mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-03 05:59:43 +02:00
The mobile accessory bar was built around coding-agent commands, so a shell session had no way to send Ctrl chords at all. A shell-mode session now gets its own bar automatically: Ctrl, Esc, Tab, four arrows, paste, dismiss. Agent sessions (claude, codex, opencode, gemini, antigravity) keep the existing bar unchanged. Ctrl is a one-shot modifier: tap it and it lights up, the next character typed on the system keyboard is sent as its control byte, and Ctrl disarms. Tapping it again cancels. That puts Ctrl+C/D/Z/R/L/A/E/W/U/K on a nine-button bar without a button per chord. Implementation notes: * The interception lives in terminal.onData, not a keydown handler: a virtual keyboard reports no usable key events, so the character only exists as onData text. It sits after shouldSuppressTerminalQueryResponse (xterm answers DA/CPR queries through onData too, and letting one of those spend the modifier would silently eat the user's Ctrl) and before every send path, so the control byte follows the normal control-char route. * ctrlByteFor() maps `code & 0x1f` over @A-Z[\]^_ and a-z, plus Ctrl+Space = NUL and Ctrl+? = DEL. Characters with no control equivalent pass through unchanged, like a hardware keyboard. * The bar now separates the base layout (the extendedKeyboardBar setting) from the effective one, resolved per session by refreshForActiveSession(). A settings save during a shell session cannot yank the bar away, and switching back to an agent tab restores the user's choice. * Ctrl disarms on use, a second tap, any other accessory key, a session switch, keyboard dismissal and a layout swap. * Ctrl joins the refocus set, so tapping it keeps the terminal focused and the keyboard open. * The armed style needs three classes to outrank mobile.css's light-skin .accessory-btn rule at (0,2,1). Verified end to end against a real shell session on an isolated instance: tapping Ctrl then typing c interrupted a running `sleep 300` (^C in the pane), the modifier disarmed, plain typing stayed literal, Ctrl+L cleared, and a cancelled Ctrl typed a literal c. Tests: test/mobile-shell-keyboard.test.ts (new, runs in CI) covers the mapping table, layout selection per session mode, base-mode memory and every disarm path; test/mobile/keyboard.test.ts adds nine browser regressions that drive the real xterm with page.keyboard.type() and assert on the bytes that would go out. Closes #262 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3113 lines
78 KiB
CSS
3113 lines
78 KiB
CSS
/* ============================================================================
|
|
Mobile & Touch Styles
|
|
Extracted from styles.css for cleaner separation.
|
|
Loaded only on small/medium screens via media attribute in index.html.
|
|
============================================================================ */
|
|
|
|
/* ============================================================================
|
|
Mobile Init Flash Prevention
|
|
Hides elements that are off by default on mobile BEFORE JS runs.
|
|
The inline <script> in <head> adds .mobile-init to <html> synchronously.
|
|
app.init() removes .mobile-init after applying visibility settings.
|
|
============================================================================ */
|
|
html.mobile-init .header-font-controls,
|
|
html.mobile-init .header-system-stats,
|
|
html.mobile-init .header-tokens,
|
|
html.mobile-init .monitor-panel,
|
|
html.mobile-init .subagents-panel,
|
|
html.mobile-init .project-insights-panel,
|
|
html.mobile-init .file-browser-panel {
|
|
display: none !important;
|
|
}
|
|
|
|
/* ============================================================================
|
|
iOS Zoom Prevention (all mobile/tablet sizes)
|
|
iOS Safari ignores user-scalable=no since iOS 10.
|
|
font-size >= 16px on inputs prevents auto-zoom on focus.
|
|
touch-action: manipulation prevents double-tap zoom.
|
|
============================================================================ */
|
|
@media (max-width: 768px) {
|
|
/* Prevent iOS auto-zoom on input focus (triggers at font-size < 16px) */
|
|
input, textarea, select {
|
|
font-size: 16px !important;
|
|
}
|
|
|
|
/* Prevent double-tap zoom globally */
|
|
html {
|
|
touch-action: manipulation;
|
|
}
|
|
|
|
/* No "open in new window" (detach) on phones/tablets — popped-out browser
|
|
windows aren't usable there. !important beats the hover/detached reveal
|
|
rules in styles.css */
|
|
.session-tab .tab-detach {
|
|
display: none !important;
|
|
}
|
|
}
|
|
|
|
/* ============================================================================
|
|
Tablet Breakpoint (430px - 768px)
|
|
============================================================================ */
|
|
@media (max-width: 768px) and (min-width: 430px) {
|
|
/* Compact header for tablet - fixed at top, includes safe area padding */
|
|
.header {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
min-height: 48px;
|
|
max-height: 48px;
|
|
padding: 0.35rem 0.5rem;
|
|
padding-left: calc(0.5rem + var(--safe-area-left));
|
|
padding-right: calc(0.5rem + var(--safe-area-right));
|
|
background: #0a0a0a;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
contain: none;
|
|
overflow: visible;
|
|
z-index: 1200;
|
|
}
|
|
|
|
/* iOS safe area adjustment for fixed header - header extends into notch area */
|
|
.ios-device .header {
|
|
padding-top: calc(0.35rem + var(--safe-area-top));
|
|
min-height: calc(48px + var(--safe-area-top));
|
|
max-height: calc(48px + var(--safe-area-top));
|
|
}
|
|
|
|
/* Push ALL content below fixed header (not just .main) so banners
|
|
(respawn, timer, orchestrator) between header and main are visible */
|
|
.app {
|
|
padding-top: 54px;
|
|
}
|
|
|
|
.ios-device .app {
|
|
padding-top: calc(54px + var(--safe-area-top));
|
|
}
|
|
|
|
/* Font controls - smaller on tablet, visibility controlled by JS */
|
|
.header-font-controls {
|
|
gap: 0.15rem;
|
|
padding: 0.15rem 0.25rem;
|
|
}
|
|
|
|
.header-font-controls .btn-icon-sm {
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
|
|
/* System stats - compact on tablet, visibility controlled by JS */
|
|
.header-system-stats {
|
|
font-size: 0.6rem;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
/* Inline, always-visible header utilities (eye / multimonitor). The
|
|
position:fixed collapsible tray (02fa3f3) had its expand toggle reverted on
|
|
master but left this container permanently `mobile-collapsed` → the response
|
|
viewer eye became unreachable on phones. Restored to the simple inline flow. */
|
|
.header-right {
|
|
gap: 0.35rem;
|
|
}
|
|
|
|
.btn-icon-header:not(.btn-sm) {
|
|
width: 44px;
|
|
height: 44px;
|
|
}
|
|
|
|
/* Compact session tabs — .tabs-two-rows override needed to match
|
|
specificity of .session-tabs.tabs-two-rows in styles.css (0,2,0) */
|
|
.session-tabs,
|
|
.session-tabs.tabs-two-rows {
|
|
flex-wrap: nowrap;
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
-webkit-overflow-scrolling: touch;
|
|
scrollbar-width: none;
|
|
max-height: 52px;
|
|
gap: 3px;
|
|
}
|
|
|
|
.session-tabs::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.session-tab {
|
|
padding: 0.4rem 0.6rem;
|
|
font-size: 0.75rem;
|
|
min-height: 40px;
|
|
}
|
|
|
|
.session-tab .tab-name {
|
|
max-width: 80px;
|
|
}
|
|
|
|
/* Compact toolbar - includes safe area padding */
|
|
.toolbar {
|
|
padding: 0.4rem 0.5rem;
|
|
padding-left: calc(0.5rem + var(--safe-area-left));
|
|
padding-right: calc(0.5rem + var(--safe-area-right));
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
/* Hide instance count buttons on tablet too */
|
|
.tab-count-group {
|
|
display: none;
|
|
}
|
|
|
|
/* Compact toolbar buttons */
|
|
.btn-toolbar {
|
|
padding: 0.4rem 0.8rem;
|
|
font-size: 0.8rem;
|
|
min-height: 40px;
|
|
}
|
|
|
|
.case-select-group {
|
|
max-width: none;
|
|
}
|
|
|
|
.case-combobox {
|
|
width: 160px;
|
|
}
|
|
|
|
.toolbar-select {
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
/* Stack panels vertically */
|
|
.monitor-panel,
|
|
.subagents-panel {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
border-radius: 8px 8px 0 0;
|
|
max-height: 40vh;
|
|
}
|
|
|
|
/* Project insights - compact on tablet, visibility controlled by JS */
|
|
.project-insights-panel {
|
|
max-width: 280px;
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
/* ---- Settings Modal: Tablet Optimizations ---- */
|
|
|
|
/* Modals must stack above the fixed tablet header (z-index 1200) so the
|
|
modal header with the close button stays visible */
|
|
.modal {
|
|
z-index: 1300;
|
|
}
|
|
|
|
.command-palette-modal {
|
|
padding: 10vh 0.75rem 0;
|
|
}
|
|
|
|
.command-palette-shell {
|
|
width: 100%;
|
|
max-height: 74vh;
|
|
}
|
|
|
|
.command-palette-input-row {
|
|
grid-template-columns: 20px minmax(0, 1fr);
|
|
}
|
|
|
|
.command-palette-input-row kbd {
|
|
display: none;
|
|
}
|
|
|
|
.command-palette-item {
|
|
min-height: 56px;
|
|
}
|
|
|
|
.modal-tabs {
|
|
overflow-x: auto;
|
|
-webkit-overflow-scrolling: touch;
|
|
scrollbar-width: none;
|
|
flex-wrap: nowrap;
|
|
}
|
|
|
|
.modal-tabs::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.modal-tab-btn {
|
|
padding: 0.4rem 0.75rem;
|
|
font-size: 0.7rem;
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Settings grid stays 2-col on tablet but tighter */
|
|
.settings-grid {
|
|
gap: 0.4rem 0.75rem;
|
|
}
|
|
|
|
.settings-item {
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
/* Event type grid - slightly tighter */
|
|
.event-type-grid {
|
|
grid-template-columns: 1fr 40px 40px 40px;
|
|
gap: 5px 6px;
|
|
}
|
|
|
|
.event-label {
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
/* ---- Subagent Windows: Tablet Optimizations ---- */
|
|
|
|
.subagent-window {
|
|
width: 320px;
|
|
height: 280px;
|
|
min-width: 240px;
|
|
min-height: 160px;
|
|
}
|
|
|
|
.subagent-window-header {
|
|
padding: 0.35rem 0.5rem;
|
|
}
|
|
|
|
.subagent-window-title .id {
|
|
font-size: 0.7rem;
|
|
max-width: 180px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.subagent-window-title .status {
|
|
font-size: 0.55rem;
|
|
}
|
|
|
|
.subagent-window-body {
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
/* ---- Respawn Banner: Tablet Optimizations ---- */
|
|
|
|
.respawn-banner {
|
|
padding: 0.25rem 0.5rem;
|
|
padding-left: calc(0.5rem + var(--safe-area-left));
|
|
padding-right: calc(0.5rem + var(--safe-area-right));
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
.respawn-compact-layout {
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.respawn-status-row1 {
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.respawn-action-log {
|
|
max-height: 2.6em;
|
|
font-size: 0.6rem;
|
|
}
|
|
|
|
.respawn-countdown-timer {
|
|
font-size: 0.55rem;
|
|
}
|
|
|
|
/* Respawn timer bar — slimmer on tablet */
|
|
.respawn-timer-bar {
|
|
width: 24px;
|
|
}
|
|
|
|
/* Show desktop voice button on tablet (hidden by max-width:1023px in styles.css,
|
|
mobile .btn-voice-mobile only shows at <430px) */
|
|
.toolbar-center .btn-toolbar.btn-voice {
|
|
display: flex !important;
|
|
}
|
|
|
|
/* Toolbar — use desktop-style sizing on tablet (plenty of room at 430-768px) */
|
|
.toolbar {
|
|
padding: 0 0.5rem;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.toolbar-left,
|
|
.toolbar-right {
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
/* Instance count controls are hidden on tablet, so toolbar-group needs gap
|
|
to space out Run / Stop / Run Shell (desktop uses gap:0 because -1+ separates them) */
|
|
.toolbar-group {
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.btn-toolbar {
|
|
padding: 0.4rem 0.75rem;
|
|
font-size: 0.75rem;
|
|
min-height: unset;
|
|
}
|
|
}
|
|
|
|
/* ============================================================================
|
|
Phone Breakpoint (<430px)
|
|
============================================================================ */
|
|
@media (max-width: 430px) {
|
|
/* Phones get a 44px header, up from 36px. Every header control is a touch
|
|
target and 44px is the floor for one; the brand "C" that gets you home is
|
|
the one that matters most. Redefined as the TOKEN rather than a literal so
|
|
the panels positioned off `var(--header-height)` (file browser, insights,
|
|
plan overlays in styles.css) follow it instead of drifting 8px under the
|
|
header. Costs 8px of terminal height on a phone. */
|
|
:root {
|
|
--header-height: 44px;
|
|
}
|
|
|
|
/* Phone brand collapses to a single "C" home button: hide the wordmark,
|
|
keep the tap target */
|
|
.header-brand {
|
|
padding-right: 0;
|
|
margin-right: 0.1rem;
|
|
border-right: none;
|
|
/* styles.css sizes this to the FULL header height, which is taller than the
|
|
header's padding box; centred by the header's `align-items: center` above,
|
|
that overflow is symmetric and the button lands flush with both edges.
|
|
Do not "fix" it with `height: 100%`: the header sets min/max-height and no
|
|
height, so the percentage has no definite containing block to resolve
|
|
against and silently falls back to auto. */
|
|
}
|
|
|
|
/* The "C" was a 0.85rem inline span — about a 12x13px hit area, far under the
|
|
44px minimum, on the one control that gets you back to the home screen.
|
|
It is now a real 44x44 button: 44px wide, and the full height of the phone
|
|
header, which is itself 44px for exactly this reason. The negative margin
|
|
spends the header's OWN left padding on the target instead of pushing the
|
|
tab strip right. */
|
|
.header-brand .logo {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 44px;
|
|
/* Taller than its parent on purpose: centred in the padded brand box, this
|
|
makes the button fill all 36 header pixels edge to edge. */
|
|
height: var(--header-height);
|
|
margin-left: -0.3rem;
|
|
font-size: 1.15rem;
|
|
line-height: 1;
|
|
border-radius: 8px;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
.header-brand .logo:active {
|
|
background: rgba(96, 165, 250, 0.16);
|
|
}
|
|
|
|
.header-brand .logo .logo-text {
|
|
display: none;
|
|
}
|
|
|
|
.header-brand .logo .logo-compact {
|
|
display: inline;
|
|
}
|
|
|
|
/* Font controls - compact on phones, visibility controlled by JS */
|
|
.header-font-controls {
|
|
gap: 0.1rem;
|
|
padding: 0.1rem 0.2rem;
|
|
}
|
|
|
|
.header-font-controls .btn-icon-sm {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
|
|
.header-font-controls .font-size-display {
|
|
font-size: 0.6rem;
|
|
min-width: 14px;
|
|
}
|
|
|
|
/* System stats - very compact on phones, visibility controlled by JS */
|
|
.header-system-stats {
|
|
font-size: 0.55rem;
|
|
gap: 0.15rem;
|
|
}
|
|
|
|
/* Token count - compact on phones, visibility controlled by JS */
|
|
.header-tokens {
|
|
font-size: 0.6rem;
|
|
padding: 0.1rem 0.3rem;
|
|
}
|
|
|
|
/* Ultra-compact header for mobile - fixed at top */
|
|
.header {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
min-height: var(--header-height);
|
|
max-height: var(--header-height);
|
|
/* styles.css top-aligns header children. That read as centred while the bar
|
|
was 36px and its contents ~31px; in a 44px bar it leaves a visible gap
|
|
under everything. */
|
|
align-items: center;
|
|
padding: 0.15rem 0.3rem;
|
|
padding-left: calc(0.3rem + var(--safe-area-left));
|
|
padding-right: calc(0.3rem + var(--safe-area-right));
|
|
gap: 0.15rem;
|
|
overflow: hidden;
|
|
background: #0a0a0a;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
contain: none;
|
|
z-index: 1200;
|
|
}
|
|
|
|
/* iOS safe area adjustment for fixed header - header extends into notch area */
|
|
.ios-device .header {
|
|
padding-top: calc(0.15rem + var(--safe-area-top));
|
|
min-height: calc(var(--header-height) + var(--safe-area-top));
|
|
max-height: calc(var(--header-height) + var(--safe-area-top));
|
|
}
|
|
|
|
/* Push ALL content below fixed header (not just .main) so banners
|
|
(respawn, timer, orchestrator) between header and main are visible.
|
|
Bottom padding for the fixed toolbar (40px) so terminal content
|
|
doesn't extend behind it. JS overrides paddingBottom on .main
|
|
when keyboard is visible, and resetLayout() clears the inline
|
|
style to re-expose this CSS value. */
|
|
.app {
|
|
/* Header height plus its 1px border and a little slack. Derived from the
|
|
token so the offset cannot fall out of step with the bar it clears. */
|
|
padding-top: calc(var(--header-height) + 6px);
|
|
}
|
|
|
|
.ios-device .app {
|
|
padding-top: calc(var(--header-height) + 6px + var(--safe-area-top));
|
|
}
|
|
|
|
.main {
|
|
padding-bottom: calc(40px + var(--safe-area-bottom));
|
|
}
|
|
|
|
/* iOS Safari: toolbar is pushed up by (100vh - --app-height) to clear the
|
|
browser's bottom bar. Match that offset in main's padding so the terminal
|
|
doesn't extend behind the toolbar. */
|
|
.ios-device.safari-browser .main {
|
|
padding-bottom: calc(40px + var(--safe-area-bottom) + (100vh - var(--app-height, 100vh)));
|
|
}
|
|
|
|
.header-right {
|
|
padding-left: 0.2rem;
|
|
gap: 0.1rem;
|
|
flex-shrink: 0;
|
|
border-left: none;
|
|
}
|
|
|
|
/* Smaller header buttons on mobile */
|
|
.btn-icon-header {
|
|
width: 26px;
|
|
height: 26px;
|
|
padding: 0;
|
|
}
|
|
|
|
.btn-icon-header svg {
|
|
width: 12px;
|
|
height: 12px;
|
|
}
|
|
|
|
/* Hide header settings gear, lifecycle log, away digest, session manager, and
|
|
file viewer on mobile - settings moved to toolbar; the others are secondary /
|
|
desktop-oriented controls that don't belong on the cramped phone header (the
|
|
session manager stays reachable via the Ctrl+K palette's "Browse all sessions"
|
|
item; the file viewer button is opt-in but its panel is desktop-sized).
|
|
(The attachments button is opt-in / default-hidden everywhere via its own
|
|
--hidden marker, so it needs no mobile-specific rule here.) */
|
|
.btn-icon-header.btn-settings,
|
|
.btn-icon-header.btn-lifecycle-log,
|
|
.btn-icon-header.btn-away-digest,
|
|
.btn-icon-header.btn-session-manager,
|
|
.btn-icon-header.btn-file-viewer,
|
|
/* Approvals bell: phones answer from the overview's NEEDS YOU rows instead
|
|
(inline approve/deny in mobile-overview.js); the bell would only crowd the
|
|
header it was designed to stay out of. */
|
|
.btn-icon-header.btn-approvals {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Read My Mind 🧠 button: desktop header only in phase 2; the phone surface
|
|
is a planned keyboard-accessory key (docs/readmymind-plan.md phase 3). */
|
|
.btn-icon-header.btn-readmymind {
|
|
display: none !important;
|
|
}
|
|
|
|
/* The big labeled Admin Panel button is desktop-only (admin-gated, revealed by
|
|
admin-ui.js). On phones admins still reach user management via App Settings →
|
|
Users, so the cramped header stays minimal. */
|
|
.btn-admin-panel {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Mobile voice input button in toolbar — initially hidden via inline style,
|
|
shown by VoiceInput._showButtons() clearing the inline display */
|
|
.btn-voice-mobile {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 26px;
|
|
height: 26px;
|
|
background: #1a2a3f;
|
|
border: 1px solid rgba(59, 130, 246, 0.3);
|
|
border-radius: 4px;
|
|
color: #93c5fd;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
order: 5; /* Between Run Shell and settings gear */
|
|
}
|
|
|
|
.btn-voice-mobile svg {
|
|
width: 13px;
|
|
height: 13px;
|
|
}
|
|
|
|
.btn-voice-mobile:active {
|
|
background: #2a3a5f;
|
|
}
|
|
|
|
.btn-voice-mobile.recording {
|
|
background: rgba(239, 68, 68, 0.25);
|
|
border-color: rgba(239, 68, 68, 0.6);
|
|
color: #ef4444;
|
|
animation: mobile-voice-pulse 1.2s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes mobile-voice-pulse {
|
|
0%, 100% {
|
|
box-shadow: inset 0 0 0 1px rgba(239, 68, 68, 0.35);
|
|
background: rgba(239, 68, 68, 0.2);
|
|
}
|
|
50% {
|
|
box-shadow: inset 0 0 0 2px rgba(239, 68, 68, 0.75);
|
|
background: rgba(239, 68, 68, 0.35);
|
|
}
|
|
}
|
|
|
|
/* Mobile app settings gear in toolbar - far right */
|
|
.btn-settings-mobile {
|
|
display: inline-flex !important;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 26px;
|
|
height: 26px;
|
|
background: transparent;
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
border-radius: 4px;
|
|
color: #9ca3af;
|
|
font-size: 0.85rem;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
order: 6; /* Far right */
|
|
position: relative;
|
|
}
|
|
|
|
.btn-settings-mobile svg {
|
|
width: 13px;
|
|
height: 13px;
|
|
}
|
|
|
|
.btn-settings-mobile:active {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
color: #fff;
|
|
}
|
|
|
|
/* App container - prevent scrolling, strict layout */
|
|
.app {
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
/* When keyboard is visible, pin the app container to the viewport.
|
|
iOS Safari scrolls the page to bring the focused input (xterm's hidden
|
|
textarea) into view, even with overflow:hidden. position:fixed prevents
|
|
the browser from scrolling the document under the app. */
|
|
.keyboard-visible .app {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: auto;
|
|
height: var(--app-height, 100dvh);
|
|
}
|
|
|
|
/* Ultra-compact session tabs — .tabs-two-rows override needed to match
|
|
specificity of .session-tabs.tabs-two-rows in styles.css (0,2,0) */
|
|
.session-tabs,
|
|
.session-tabs.tabs-two-rows {
|
|
flex: 1;
|
|
flex-wrap: nowrap;
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
-webkit-overflow-scrolling: touch;
|
|
scrollbar-width: none;
|
|
max-height: 36px;
|
|
gap: 2px;
|
|
padding: 0;
|
|
}
|
|
|
|
.session-tabs::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
/* Smaller tabs for mobile */
|
|
.session-tab {
|
|
flex-shrink: 0;
|
|
min-height: 32px;
|
|
max-height: 32px;
|
|
padding: 0.35rem 0.5rem;
|
|
font-size: 0.7rem;
|
|
gap: 0.25rem;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
/* Smaller status indicator on mobile */
|
|
.session-tab .tab-status {
|
|
width: 4px;
|
|
height: 4px;
|
|
}
|
|
|
|
/* The working dot is the one glance-state a phone needs: keep idle tiny, but
|
|
let the pulsing green "working" dot read from arm's length. !important on
|
|
the glow because the skin block's no-halo rule (styles.css, nested under
|
|
html:not([data-skin="og"])) outranks any plain class rule here. */
|
|
.session-tab .tab-status.busy {
|
|
width: 9px;
|
|
height: 9px;
|
|
box-shadow: 0 0 8px 2px color-mix(in srgb, var(--green) 55%, transparent) !important;
|
|
}
|
|
|
|
/* Truncate tab names more aggressively on mobile */
|
|
.session-tab .tab-name {
|
|
max-width: 50px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
/* Hide close/gear buttons on non-active tabs on mobile */
|
|
.session-tab .tab-close,
|
|
.session-tab .tab-gear {
|
|
display: none;
|
|
}
|
|
|
|
/* Gear icon on active tab - tiny, subtle */
|
|
.session-tab.active .tab-gear {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.65rem;
|
|
line-height: 1;
|
|
width: 32px;
|
|
height: 32px;
|
|
margin-left: auto;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
/* Close button on active tab - large for easy tapping */
|
|
.session-tab.active .tab-close {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.1rem;
|
|
line-height: 1;
|
|
width: 20px;
|
|
height: 18px;
|
|
margin-left: -2px;
|
|
}
|
|
|
|
/* Clean dark toolbar - positioned near bottom */
|
|
.toolbar {
|
|
position: fixed;
|
|
bottom: var(--safe-area-bottom);
|
|
left: 0;
|
|
right: 0;
|
|
height: 40px;
|
|
min-height: 40px;
|
|
max-height: 40px;
|
|
padding: 0.3rem 0.4rem;
|
|
padding-left: calc(0.4rem + var(--safe-area-left));
|
|
padding-right: calc(0.4rem + var(--safe-area-right));
|
|
flex-wrap: nowrap;
|
|
gap: 0.3rem;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
background: #111111;
|
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
z-index: 50;
|
|
/* Smooth transition for keyboard show/hide */
|
|
transition: transform 0.15s ease-out;
|
|
/* Ensure transform works properly */
|
|
will-change: transform;
|
|
}
|
|
|
|
/* iOS Safari with tab bar: position: fixed uses the layout viewport which
|
|
extends behind the browser chrome. Offset the toolbar upward by the delta
|
|
between 100vh (layout) and --app-height (visual). */
|
|
.ios-device.safari-browser .toolbar {
|
|
bottom: calc(var(--safe-area-bottom) + (100vh - var(--app-height, 100vh)));
|
|
}
|
|
|
|
/* When keyboard is visible the JS translateY already accounts for the full
|
|
distance from the visual-viewport bottom to the layout-viewport bottom
|
|
(keyboard + Safari bar). Remove the CSS Safari-bar offset to avoid
|
|
double-counting, which otherwise creates a visible gap above the keyboard. */
|
|
.keyboard-visible.ios-device.safari-browser .toolbar {
|
|
bottom: var(--safe-area-bottom);
|
|
}
|
|
.keyboard-visible.ios-device.safari-browser .keyboard-accessory-bar {
|
|
bottom: calc(var(--safe-area-bottom) + 40px);
|
|
}
|
|
|
|
/* Show case selector in center */
|
|
.toolbar-center {
|
|
display: flex !important;
|
|
align-items: center;
|
|
}
|
|
|
|
.toolbar-center .btn-toolbar.btn-voice {
|
|
display: none !important;
|
|
}
|
|
|
|
.toolbar-right {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Hide version display on mobile */
|
|
.version-display {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Toolbar layout */
|
|
.toolbar-left {
|
|
flex: 1 1 0;
|
|
min-width: 0;
|
|
align-items: center;
|
|
gap: 0.3rem;
|
|
}
|
|
|
|
.toolbar-left .toolbar-group {
|
|
display: flex;
|
|
flex: 1 1 0;
|
|
min-width: 0;
|
|
align-items: center;
|
|
gap: 0.3rem;
|
|
}
|
|
|
|
.toolbar-left .toolbar-group:first-child {
|
|
flex: 1 1 0;
|
|
min-width: 0;
|
|
justify-content: space-between;
|
|
gap: 0.3rem;
|
|
flex-wrap: nowrap;
|
|
align-items: center;
|
|
}
|
|
|
|
/* Hide instance count buttons (±) on mobile - cleaner UI */
|
|
.tab-count-group {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Compact toolbar buttons for mobile - white text on dark */
|
|
.btn-toolbar {
|
|
min-height: 26px !important;
|
|
max-height: 26px !important;
|
|
height: 26px !important;
|
|
padding: 0 0.5rem !important;
|
|
font-size: 0.65rem !important;
|
|
border-radius: 4px;
|
|
line-height: 26px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: 500;
|
|
letter-spacing: 0.01em;
|
|
color: #fff;
|
|
}
|
|
|
|
/* Run button group — split button with dropdown */
|
|
.run-btn-group {
|
|
display: flex;
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.btn-toolbar.btn-run {
|
|
flex: 0 0 auto;
|
|
padding: 0 0.6rem !important;
|
|
font-weight: 500;
|
|
border-radius: 4px 0 0 4px !important;
|
|
border-right: none !important;
|
|
}
|
|
|
|
.btn-toolbar.btn-run svg {
|
|
width: 10px;
|
|
height: 10px;
|
|
margin-right: 3px;
|
|
}
|
|
|
|
.btn-toolbar.btn-run-gear {
|
|
flex: 0 0 auto;
|
|
padding: 0 0.35rem !important;
|
|
min-width: unset !important;
|
|
border-radius: 0 4px 4px 0 !important;
|
|
border-left: 1px solid rgba(255,255,255,0.15) !important;
|
|
}
|
|
|
|
.btn-toolbar.btn-run-gear svg {
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
|
|
/* Claude mode colors on mobile */
|
|
.btn-toolbar.btn-run.mode-claude,
|
|
.btn-toolbar.btn-run-gear.mode-claude {
|
|
background: #1e3a5f;
|
|
border-color: rgba(59, 130, 246, 0.3);
|
|
color: #93c5fd;
|
|
}
|
|
|
|
.btn-toolbar.btn-run.mode-claude:active,
|
|
.btn-toolbar.btn-run-gear.mode-claude:active {
|
|
background: #2563eb;
|
|
border-color: rgba(59, 130, 246, 0.5);
|
|
}
|
|
|
|
/* OpenCode mode colors on mobile */
|
|
.btn-toolbar.btn-run.mode-opencode,
|
|
.btn-toolbar.btn-run-gear.mode-opencode {
|
|
background: #0a2e2a;
|
|
border-color: rgba(16, 185, 129, 0.3);
|
|
color: #6ee7b7;
|
|
}
|
|
|
|
.btn-toolbar.btn-run.mode-opencode:active,
|
|
.btn-toolbar.btn-run-gear.mode-opencode:active {
|
|
background: #0d4a40;
|
|
border-color: rgba(16, 185, 129, 0.5);
|
|
}
|
|
|
|
/* Gemini mode colors on mobile */
|
|
.btn-toolbar.btn-run.mode-gemini,
|
|
.btn-toolbar.btn-run-gear.mode-gemini {
|
|
background: #10243f;
|
|
border-color: rgba(96, 165, 250, 0.3);
|
|
color: #dbeafe;
|
|
}
|
|
|
|
.btn-toolbar.btn-run.mode-gemini:active,
|
|
.btn-toolbar.btn-run-gear.mode-gemini:active {
|
|
background: #174ea6;
|
|
border-color: rgba(96, 165, 250, 0.5);
|
|
}
|
|
|
|
/* Antigravity mode colors on mobile */
|
|
.btn-toolbar.btn-run.mode-antigravity,
|
|
.btn-toolbar.btn-run-gear.mode-antigravity {
|
|
background: #0b2b33;
|
|
border-color: rgba(34, 211, 238, 0.3);
|
|
color: #cffafe;
|
|
}
|
|
|
|
.btn-toolbar.btn-run.mode-antigravity:active,
|
|
.btn-toolbar.btn-run-gear.mode-antigravity:active {
|
|
background: #0e7490;
|
|
border-color: rgba(34, 211, 238, 0.5);
|
|
}
|
|
|
|
/* Run mode dropdown menu — positioned above toolbar on mobile */
|
|
.run-mode-menu {
|
|
bottom: 100%;
|
|
left: 0;
|
|
margin-bottom: 6px;
|
|
min-width: 160px;
|
|
max-width: 80vw;
|
|
}
|
|
|
|
.run-mode-option {
|
|
padding: 10px 12px;
|
|
font-size: 0.8rem;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
/* Per-URL edit/delete in the Web / URL list need a real touch target, and they
|
|
sit next to the row's own tap area, so they get sized up rather than relying
|
|
on the 24px desktop hit box. */
|
|
.run-mode-row-btn {
|
|
width: 34px;
|
|
height: 34px;
|
|
font-size: 1rem;
|
|
-webkit-tap-highlight-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
.run-mode-webview-delete { font-size: 1.2rem; }
|
|
|
|
.run-mode-history {
|
|
-webkit-overflow-scrolling: touch;
|
|
touch-action: manipulation;
|
|
}
|
|
|
|
/* Stop button - visible on mobile, icon-only */
|
|
.btn-toolbar.btn-stop {
|
|
display: flex !important;
|
|
flex: 0 0 auto;
|
|
padding: 0 8px !important;
|
|
min-width: unset;
|
|
order: 3; /* Left of Run Shell */
|
|
}
|
|
|
|
.btn-toolbar.btn-stop svg {
|
|
width: 10px;
|
|
height: 10px;
|
|
margin-right: 0;
|
|
}
|
|
|
|
/* Shell is NOT a toolbar button on phones — it moved into the Run dropdown
|
|
(Terminal / Shell), freeing this slot for Enter. Starting a shell is a rare,
|
|
deliberate act; sending Enter is a constant one, so the scarce phone real
|
|
estate goes to Enter. */
|
|
.btn-toolbar.btn-shell {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Secondary action - Enter - right side. Takes the slot (and the order) the
|
|
Shell button used to hold, so the toolbar rhythm is unchanged. */
|
|
.btn-toolbar.btn-enter {
|
|
display: flex !important;
|
|
flex: 0 0 auto;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 54px;
|
|
width: 54px;
|
|
white-space: nowrap;
|
|
padding: 0 8px !important;
|
|
overflow: hidden;
|
|
font-size: 0.65rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.01em;
|
|
/* !important is REQUIRED here, not defensive habit: styles.css nests its skin
|
|
overrides inside `html:not([data-skin="og"]) { … }`, so a plain `.btn-toolbar`
|
|
in that block resolves to (0,2,1) and outranks this (0,2,0) rule. Without
|
|
!important the button silently renders in generic toolbar grey. */
|
|
background: rgba(30, 58, 95, 0.85) !important;
|
|
border: 1px solid rgba(59, 130, 246, 0.45) !important;
|
|
color: #dbeafe !important;
|
|
order: 4; /* Right position — same slot Shell used to occupy */
|
|
}
|
|
|
|
.btn-toolbar.btn-enter:hover,
|
|
.btn-toolbar.btn-enter:active {
|
|
background: rgba(37, 74, 122, 0.95) !important;
|
|
border-color: rgba(59, 130, 246, 0.7) !important;
|
|
color: #fff !important;
|
|
}
|
|
|
|
/* Hide case selector on mobile - simplified toolbar */
|
|
.case-select-group {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Simplified toolbar layout — Run, Enter, and Case */
|
|
.toolbar-left .toolbar-group:first-child {
|
|
width: 100%;
|
|
gap: 8px;
|
|
}
|
|
|
|
/* Mobile case button - visible on mobile */
|
|
.btn-toolbar.btn-case-mobile {
|
|
display: flex !important;
|
|
flex: 1 1 0 !important;
|
|
min-width: 0;
|
|
padding: 0 8px !important;
|
|
order: 2; /* Between Run and Run Shell */
|
|
overflow: hidden;
|
|
justify-content: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.btn-toolbar.btn-case-mobile #mobileCaseName {
|
|
max-width: none;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Mobile case settings gear button - next to Run */
|
|
.btn-case-settings-mobile {
|
|
display: inline-flex !important;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 26px;
|
|
height: 26px;
|
|
background: transparent;
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
border-radius: 4px;
|
|
color: #9ca3af;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
order: 1; /* Next to Run */
|
|
}
|
|
|
|
.btn-case-settings-mobile:active {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
color: #fff;
|
|
}
|
|
|
|
@media (max-width: 374px) {
|
|
.toolbar {
|
|
padding: 0 2px;
|
|
}
|
|
|
|
.toolbar-left,
|
|
.toolbar-left .toolbar-group,
|
|
.toolbar-left .toolbar-group:first-child {
|
|
gap: 2px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 430px) {
|
|
.btn-case-settings-mobile {
|
|
display: none !important;
|
|
}
|
|
}
|
|
|
|
/* Mobile case settings popover */
|
|
.case-settings-popover-mobile {
|
|
position: fixed;
|
|
bottom: calc(var(--safe-area-bottom) + 46px);
|
|
left: 8px;
|
|
right: 8px;
|
|
background: #1e1e1e;
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
border-radius: 8px;
|
|
padding: 0.6rem 0.75rem;
|
|
z-index: 1000;
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
|
|
display: block;
|
|
}
|
|
|
|
.case-settings-popover-mobile.hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
.case-settings-popover-mobile .checkbox-inline {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 0.75rem;
|
|
color: #e5e7eb;
|
|
}
|
|
|
|
.case-settings-popover-mobile .form-hint {
|
|
display: block;
|
|
margin-top: 0.2rem;
|
|
font-size: 0.6rem;
|
|
color: #6b7280;
|
|
}
|
|
|
|
/* Keyboard Accessory Bar - quick actions above keyboard */
|
|
.keyboard-accessory-bar {
|
|
display: none;
|
|
position: fixed;
|
|
bottom: calc(var(--safe-area-bottom) + 40px); /* Above toolbar */
|
|
left: 0;
|
|
right: 0;
|
|
height: 44px;
|
|
background: #1a1a1a;
|
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
padding: 6px 8px;
|
|
padding-left: calc(8px + var(--safe-area-left));
|
|
padding-right: calc(8px + var(--safe-area-right));
|
|
gap: 8px;
|
|
align-items: center;
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
-webkit-overflow-scrolling: touch;
|
|
z-index: 51;
|
|
transition: transform 0.15s ease-out;
|
|
will-change: transform;
|
|
}
|
|
|
|
.keyboard-accessory-bar.visible {
|
|
display: flex;
|
|
}
|
|
|
|
/* 隐藏滚动条但保留滑动能力 */
|
|
.keyboard-accessory-bar::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.accessory-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
gap: 4px;
|
|
padding: 6px 12px;
|
|
background: #2a2a2a;
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
border-radius: 6px;
|
|
color: #e5e5e5;
|
|
font-size: 0.65rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: background 0.15s, border-color 0.15s;
|
|
}
|
|
|
|
/* Confirm state — button turns amber and shows "Tap again" */
|
|
.accessory-btn.confirming {
|
|
background: #6b4f00;
|
|
border-color: #b8860b;
|
|
color: #ffd54f;
|
|
}
|
|
|
|
/* Armed one-shot Ctrl (shell bar, issue #262). Phone palette is hardcoded in
|
|
this block, so the state needs its own entry here; three classes to outrank
|
|
the light-skin .accessory-btn rule at the bottom of this file. */
|
|
.accessory-btn.accessory-btn-ctrl.armed {
|
|
background: #2563eb;
|
|
border-color: rgba(59, 130, 246, 0.9);
|
|
color: #fff;
|
|
font-weight: 700;
|
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.45);
|
|
}
|
|
|
|
.accessory-btn:active {
|
|
background: #3a3a3a;
|
|
}
|
|
|
|
.accessory-btn svg {
|
|
width: 14px;
|
|
height: 14px;
|
|
}
|
|
|
|
.accessory-btn-arrow {
|
|
padding: 6px 10px;
|
|
background: #2563eb;
|
|
border-color: rgba(59, 130, 246, 0.5);
|
|
color: #fff;
|
|
}
|
|
|
|
.accessory-btn-arrow:active {
|
|
background: #1d4ed8;
|
|
}
|
|
|
|
.accessory-btn-dismiss {
|
|
margin-left: auto;
|
|
flex: 1 1 0;
|
|
max-width: 100px;
|
|
padding: 10px 8px;
|
|
background: #2563eb;
|
|
border-color: rgba(59, 130, 246, 0.5);
|
|
color: #fff;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.accessory-btn-dismiss svg {
|
|
width: 22px;
|
|
height: 22px;
|
|
}
|
|
|
|
.accessory-btn-dismiss:active {
|
|
background: #1d4ed8;
|
|
}
|
|
|
|
/* Voice preview — positioned above accessory bar on mobile */
|
|
.voice-preview {
|
|
bottom: calc(var(--safe-area-bottom) + 94px);
|
|
font-size: 0.8rem;
|
|
max-width: 90%;
|
|
padding: 6px 14px;
|
|
}
|
|
|
|
/* Add case button - compact (keeping for when shown via menu) */
|
|
.btn-case-add {
|
|
display: none !important;
|
|
}
|
|
|
|
.btn-case-settings {
|
|
display: none !important;
|
|
}
|
|
|
|
/* When keyboard is visible, also move accessory bar up */
|
|
.keyboard-visible .keyboard-accessory-bar.visible {
|
|
/* Position is handled by JS transform along with toolbar */
|
|
}
|
|
|
|
/* Paste overlay for iOS clipboard access */
|
|
/* Paste overlay styles extracted to universal section below (line ~2293+) */
|
|
|
|
/* LEGACY: Hide old toolbar select (no longer used on mobile) */
|
|
.toolbar-select {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Keep btn-case-add styling for tablet/future use */
|
|
.toolbar .btn-case-add {
|
|
min-width: 26px !important;
|
|
max-width: 26px !important;
|
|
width: 26px !important;
|
|
min-height: 26px !important;
|
|
max-height: 26px !important;
|
|
height: 26px !important;
|
|
padding: 0 !important;
|
|
font-size: 0.9rem;
|
|
font-weight: bold;
|
|
display: inline-flex !important;
|
|
align-items: center;
|
|
justify-content: center;
|
|
line-height: 1;
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
color: #9ca3af;
|
|
}
|
|
|
|
.btn-case-add:hover,
|
|
.btn-case-add:active {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
color: #fff;
|
|
}
|
|
|
|
/* Panels on mobile - full width, positioned above toolbar, visibility controlled by JS */
|
|
.monitor-panel,
|
|
.subagents-panel {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
border-radius: 8px 8px 0 0;
|
|
bottom: calc(44px + 2rem + var(--safe-area-bottom));
|
|
max-height: 35vh;
|
|
}
|
|
|
|
/* Modals must stack above the fixed mobile header (z-index 1200), or the
|
|
modal header with the close button is buried underneath it and the
|
|
full-screen modal cannot be dismissed */
|
|
.modal {
|
|
z-index: 1300;
|
|
}
|
|
|
|
/* Full-screen modals on phones */
|
|
.modal-content {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
height: 100%;
|
|
max-height: 100%;
|
|
border-radius: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* Small modals (confirmations, dialogs) — auto-height, not full-screen */
|
|
.modal-content.modal-sm {
|
|
height: auto;
|
|
max-height: 85vh;
|
|
border-radius: 12px;
|
|
margin: 1rem;
|
|
width: calc(100% - 2rem);
|
|
}
|
|
|
|
/* Modal safe area padding - all sides for full-screen modals */
|
|
.ios-device .modal-content {
|
|
padding-top: var(--safe-area-top);
|
|
padding-bottom: var(--safe-area-bottom);
|
|
padding-left: var(--safe-area-left);
|
|
padding-right: var(--safe-area-right);
|
|
}
|
|
|
|
.modal-header {
|
|
padding: 0.75rem 1rem;
|
|
}
|
|
|
|
.modal-header h3 {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.modal-body {
|
|
padding: 0.75rem 1rem;
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
.away-digest-modal {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
height: 100%;
|
|
max-height: 100%;
|
|
}
|
|
|
|
.away-digest-ranges {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.away-digest-ranges .filter-btn {
|
|
min-height: 38px;
|
|
padding: 0.4rem 0.5rem;
|
|
}
|
|
|
|
.away-digest-custom-range,
|
|
.away-digest-custom-range.active {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.away-digest-summary {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.away-digest-item {
|
|
grid-template-columns: 1fr;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.away-digest-action {
|
|
width: 100%;
|
|
min-height: 38px;
|
|
}
|
|
|
|
.modal-footer,
|
|
.form-actions {
|
|
padding: 0.75rem 1rem;
|
|
padding-bottom: calc(0.75rem + var(--safe-area-bottom));
|
|
}
|
|
|
|
/* Terminal container safe area */
|
|
.ios-device .terminal-container {
|
|
padding-bottom: var(--safe-area-bottom);
|
|
}
|
|
|
|
/* Main area must fill available space */
|
|
.main {
|
|
flex: 1;
|
|
min-height: 0; /* Allow flex item to shrink below content size */
|
|
}
|
|
|
|
/* Show CLI info bar on mobile (Claude version/model) */
|
|
.cli-info-bar {
|
|
display: block;
|
|
}
|
|
|
|
/* Mobile terminal — JS touch handler (terminal.scrollLines()) drives
|
|
scrollback because xterm.js DOM renderer doesn't populate xterm-viewport's
|
|
scroll area. touch-action:none lets our JS handler own the gesture. */
|
|
.terminal-container {
|
|
height: 100%;
|
|
min-height: 0;
|
|
position: relative;
|
|
overflow: visible;
|
|
touch-action: none;
|
|
}
|
|
|
|
.terminal-container .xterm,
|
|
.terminal-container .xterm-viewport,
|
|
.terminal-container .xterm-screen {
|
|
touch-action: none;
|
|
}
|
|
|
|
.response-viewer {
|
|
padding-bottom: var(--safe-area-bottom, 0px);
|
|
/* dvh tracks the visible viewport on iOS Safari (vh = large viewport and would clip
|
|
the header/close button off-screen); the vh line is the old-engine fallback */
|
|
max-height: 88vh;
|
|
max-height: 92dvh;
|
|
}
|
|
|
|
.response-viewer-body {
|
|
font-size: 14.5px;
|
|
line-height: 1.65;
|
|
padding: 16px 16px 24px;
|
|
--rv-content-max: 100%;
|
|
}
|
|
|
|
.response-viewer-body .rv-text pre,
|
|
.response-viewer-body pre {
|
|
/* Slightly smaller on mobile so diagrams fit better before scrolling */
|
|
padding: 12px 14px;
|
|
margin-left: -4px;
|
|
margin-right: -4px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.response-viewer-body .rv-text pre code,
|
|
.response-viewer-body pre code {
|
|
font-size: 11.5px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.response-viewer-body .rv-text h1,
|
|
.response-viewer-body > h1 { font-size: 1.35em; }
|
|
.response-viewer-body .rv-text h2,
|
|
.response-viewer-body > h2 { font-size: 1.2em; }
|
|
.response-viewer-body .rv-text h3,
|
|
.response-viewer-body > h3 { font-size: 1.08em; }
|
|
|
|
/* Compact welcome overlay for mobile */
|
|
.welcome-content {
|
|
max-width: calc(100vw - 1.5rem);
|
|
padding: 1rem 0.75rem;
|
|
}
|
|
|
|
.welcome-title {
|
|
font-size: 1.2rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.welcome-desc {
|
|
font-size: 0.8rem;
|
|
margin-bottom: 0.4rem;
|
|
}
|
|
|
|
.welcome-actions {
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.welcome-btn {
|
|
width: 100%;
|
|
justify-content: center;
|
|
min-height: 44px;
|
|
padding: 0.75rem 1rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.history-show-more {
|
|
display: block;
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.welcome-ralph-link {
|
|
display: block;
|
|
margin: 0.75rem auto 0;
|
|
}
|
|
|
|
.welcome-hint {
|
|
font-size: 0.7rem;
|
|
margin-top: 0.75rem;
|
|
}
|
|
|
|
/* Ralph wizard responsive */
|
|
.modal-wizard {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.wizard-progress {
|
|
padding: 0.5rem;
|
|
}
|
|
|
|
.wizard-step {
|
|
padding: 0.3rem;
|
|
}
|
|
|
|
.wizard-step-number {
|
|
width: 24px;
|
|
height: 24px;
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
.wizard-step-label {
|
|
display: none;
|
|
}
|
|
|
|
/* Timer banner - includes safe area padding */
|
|
.timer-banner {
|
|
padding-left: calc(0.5rem + var(--safe-area-left));
|
|
padding-right: calc(0.5rem + var(--safe-area-right));
|
|
}
|
|
|
|
/* ---- Respawn Banner: Mobile Optimizations ---- */
|
|
|
|
.respawn-banner {
|
|
padding: 0.3rem 0.5rem;
|
|
padding-left: calc(0.5rem + var(--safe-area-left));
|
|
padding-right: calc(0.5rem + var(--safe-area-right));
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
/* Stack banner vertically on mobile — status on top, action log below */
|
|
.respawn-compact-layout {
|
|
flex-direction: column;
|
|
gap: 0.15rem;
|
|
}
|
|
|
|
/* Let status column shrink to fit */
|
|
.respawn-status-col {
|
|
flex-shrink: 1;
|
|
min-width: 0;
|
|
gap: 0.1rem;
|
|
}
|
|
|
|
/* Row1: wrap allowed, key info prioritized via order */
|
|
.respawn-status-row1 {
|
|
flex-wrap: wrap;
|
|
gap: 0.25rem;
|
|
row-gap: 0.1rem;
|
|
}
|
|
|
|
/* State label — smaller on phone, stays first */
|
|
.respawn-state {
|
|
font-size: 0.6rem;
|
|
padding: 0.05rem 0.3rem;
|
|
}
|
|
|
|
/* Confidence badge — compact */
|
|
.respawn-banner .detection-confidence {
|
|
font-size: 0.5rem;
|
|
padding: 0.02rem 0.2rem;
|
|
}
|
|
|
|
/* Cycles — compact */
|
|
.respawn-cycles {
|
|
font-size: 0.55rem;
|
|
}
|
|
|
|
/* Run timer — compact */
|
|
.respawn-timer {
|
|
font-size: 0.55rem;
|
|
padding: 0 0.25rem;
|
|
}
|
|
|
|
/* Tokens — compact, truncate if long */
|
|
.respawn-tokens {
|
|
font-size: 0.55rem;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
max-width: 120px;
|
|
}
|
|
|
|
/* Spinner icon — smaller on phone */
|
|
.respawn-indicator {
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
/* Stop button — 36px touch target, pushed right */
|
|
.respawn-banner .btn-icon-only {
|
|
min-width: 36px;
|
|
min-height: 36px;
|
|
font-size: 1rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-left: auto;
|
|
border-radius: 6px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Row2: tighter spacing, smaller text */
|
|
.respawn-status-row2 {
|
|
font-size: 0.55rem;
|
|
gap: 0.3rem;
|
|
min-height: 0;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
/* Detection badges — smaller on phone */
|
|
.respawn-banner .detection-hook,
|
|
.respawn-banner .detection-ai-check,
|
|
.respawn-banner .detection-status {
|
|
font-size: 0.55rem;
|
|
padding: 0.02rem 0.2rem;
|
|
}
|
|
|
|
/* Countdown timers — inline, no progress bars */
|
|
.respawn-countdown-timers {
|
|
gap: 0.2rem;
|
|
}
|
|
|
|
.respawn-countdown-timer {
|
|
font-size: 0.5rem;
|
|
padding: 0.02rem 0.2rem;
|
|
gap: 0.15rem;
|
|
}
|
|
|
|
/* Hide progress bars on phone — values are enough */
|
|
.respawn-countdown-timer .respawn-timer-bar {
|
|
display: none;
|
|
}
|
|
|
|
/* Action log — single-line strip below status, show latest entry only */
|
|
.respawn-action-log {
|
|
border-left: none;
|
|
border-top: 1px solid rgba(34, 197, 94, 0.15);
|
|
padding-left: 0;
|
|
padding-top: 0.15rem;
|
|
max-height: 1.4em;
|
|
font-size: 0.55rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* ---- Session Options Modal: Respawn Tab Mobile Optimizations ---- */
|
|
|
|
/* Respawn header — keep horizontal, compact */
|
|
.respawn-header {
|
|
flex-direction: row;
|
|
gap: 0.4rem;
|
|
align-items: center;
|
|
}
|
|
|
|
/* Enable/Stop buttons — compact */
|
|
.respawn-header .respawn-actions {
|
|
display: flex;
|
|
gap: 0.3rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.respawn-header .respawn-actions .btn-toolbar {
|
|
min-height: 28px !important;
|
|
font-size: 0.7rem !important;
|
|
padding: 0.2rem 0.5rem !important;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
/* Duration preset buttons — one compact row */
|
|
.duration-presets {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.2rem;
|
|
}
|
|
|
|
.duration-preset-btn {
|
|
min-height: 24px;
|
|
padding: 0.1rem 0.4rem;
|
|
font-size: 0.6rem;
|
|
border-radius: 4px;
|
|
text-align: center;
|
|
}
|
|
|
|
.duration-custom {
|
|
display: flex;
|
|
gap: 0.2rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.duration-custom-input.visible {
|
|
flex: 0 1 auto;
|
|
}
|
|
|
|
.duration-custom-input input {
|
|
width: 64px;
|
|
min-height: 24px;
|
|
font-size: 16px; /* Prevents iOS zoom */
|
|
padding: 0.1rem 0.3rem;
|
|
}
|
|
|
|
/* Preset selector row — full-width dropdown, buttons below */
|
|
.preset-selector {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.preset-selector select {
|
|
grid-column: 1 / -1;
|
|
min-height: 32px;
|
|
font-size: 16px; /* Prevents iOS zoom */
|
|
border-radius: 5px;
|
|
padding: 0.2rem 0.4rem;
|
|
}
|
|
|
|
.preset-selector .btn {
|
|
min-height: 32px;
|
|
font-size: 0.65rem;
|
|
border-radius: 5px;
|
|
padding: 0.2rem 0.4rem;
|
|
}
|
|
|
|
/* Textareas — prevent iOS zoom with 16px font, compact height */
|
|
#sessionOptionsModal textarea {
|
|
font-size: 16px;
|
|
min-height: 32px !important;
|
|
max-height: 56px;
|
|
height: auto !important;
|
|
border-radius: 6px;
|
|
padding: 0.3rem 0.5rem;
|
|
}
|
|
|
|
/* Override inline min-height styles on respawn textareas */
|
|
#modalRespawnPrompt {
|
|
min-height: 32px !important;
|
|
max-height: 56px;
|
|
}
|
|
|
|
#modalRespawnKickstart {
|
|
min-height: 32px !important;
|
|
max-height: 48px;
|
|
}
|
|
|
|
/* Checkboxes — compact touch area */
|
|
#sessionOptionsModal .checkbox-inline {
|
|
min-height: 30px;
|
|
font-size: 0.75rem;
|
|
padding: 0.15rem 0;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
#sessionOptionsModal .checkbox-inline input[type="checkbox"] {
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
|
|
/* Respawn-loop box title matches the auto-resume title; its cycle-step
|
|
checkboxes match the step labels */
|
|
#sessionOptionsModal .respawn-loop-title {
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
#sessionOptionsModal .respawn-loop-box .checkbox-inline {
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
/* Respawn options row — stack if needed */
|
|
#sessionOptionsModal .respawn-options-row {
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
/* Form section headers — compact on mobile */
|
|
#sessionOptionsModal .form-section-header {
|
|
margin-top: 0.75rem;
|
|
padding-top: 0.5rem;
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
/* Form rows — tighter spacing */
|
|
#sessionOptionsModal .form-row {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
#sessionOptionsModal .form-row label {
|
|
font-size: 0.65rem;
|
|
margin-bottom: 0.2rem;
|
|
}
|
|
|
|
/* Form hints — compact */
|
|
#sessionOptionsModal .form-hint {
|
|
font-size: 0.6rem;
|
|
margin-top: 0.2rem;
|
|
}
|
|
|
|
/* ---- Context Tab: Mobile Optimizations ---- */
|
|
|
|
/* Context settings cards — tighter padding */
|
|
.context-setting {
|
|
padding: 0.625rem;
|
|
}
|
|
|
|
/* Stack context setting header vertically on narrow screens */
|
|
.context-setting-header {
|
|
flex-wrap: wrap;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.context-setting-header .input-suffix-sm input {
|
|
width: 60px;
|
|
font-size: 16px; /* Prevents iOS zoom */
|
|
min-height: 36px;
|
|
}
|
|
|
|
.context-setting input[type="text"] {
|
|
font-size: 16px; /* Prevents iOS zoom */
|
|
min-height: 36px;
|
|
padding: 0.4rem 0.6rem;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
/* Color picker — larger swatches for touch */
|
|
.color-picker {
|
|
gap: 8px;
|
|
}
|
|
|
|
.color-swatch {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
/* Toggle switches — larger touch area */
|
|
.form-row-switch {
|
|
min-height: 40px;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
/* Number inputs in context tab — prevent iOS zoom */
|
|
.context-settings-grid input[type="number"] {
|
|
font-size: 16px;
|
|
min-height: 36px;
|
|
}
|
|
|
|
/* ---- Ralph Tab: Mobile Optimizations ---- */
|
|
|
|
/* Ralph limits grid — 2 columns on mobile */
|
|
.ralph-limits-grid {
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.ralph-limits-grid .form-col input[type="number"] {
|
|
font-size: 16px; /* Prevents iOS zoom */
|
|
min-height: 36px;
|
|
padding: 0.4rem 0.5rem;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
/* Ralph phrase input — prevent iOS zoom */
|
|
#modalRalphPhrase {
|
|
font-size: 16px;
|
|
min-height: 40px;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
/* Save button — full width, proper touch target */
|
|
.ralph-config-actions {
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.ralph-config-actions .btn-toolbar {
|
|
width: 100%;
|
|
min-height: 44px;
|
|
font-size: 0.85rem;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
/* ---- Summary Tab: Mobile Optimizations ---- */
|
|
|
|
/* Filter buttons — larger touch targets */
|
|
.run-summary-filters {
|
|
gap: 0.3rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.filter-btn {
|
|
padding: 0.35rem 0.75rem;
|
|
font-size: 0.7rem;
|
|
min-height: 32px;
|
|
}
|
|
|
|
/* Timeline events — tighter on mobile */
|
|
.timeline-event {
|
|
padding: 0.4rem 0.5rem;
|
|
margin-bottom: 0.25rem;
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
/* Summary footer — stack vertically */
|
|
.run-summary-footer {
|
|
flex-direction: column;
|
|
gap: 0.4rem;
|
|
padding: 0.5rem 0;
|
|
}
|
|
|
|
.run-summary-actions {
|
|
flex-wrap: wrap;
|
|
gap: 0.3rem;
|
|
}
|
|
|
|
.run-summary-actions .btn-toolbar {
|
|
flex: 1;
|
|
min-height: 36px;
|
|
font-size: 0.7rem;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.auto-refresh-label {
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
.ralph-panel {
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.ralph-summary {
|
|
padding: 0.4rem 0.5rem;
|
|
padding-left: calc(0.5rem + var(--safe-area-left));
|
|
padding-right: calc(0.5rem + var(--safe-area-right));
|
|
}
|
|
|
|
/* Project insights panel - compact on mobile, visibility controlled by JS */
|
|
.project-insights-panel {
|
|
max-width: 100%;
|
|
font-size: 0.65rem;
|
|
bottom: calc(44px + 2rem + var(--safe-area-bottom));
|
|
}
|
|
|
|
/* File browser panel - compact on mobile, visibility controlled by JS */
|
|
.file-browser-panel {
|
|
max-width: 100%;
|
|
max-height: 50vh;
|
|
bottom: calc(44px + 2rem + var(--safe-area-bottom));
|
|
}
|
|
|
|
/* File preview window: full screen on phones. --app-height tracks the visual
|
|
viewport (KeyboardHandler), so the edit textarea + Save bar stay above the
|
|
OS keyboard instead of hiding behind it. Footer/edit bar pad for the home
|
|
indicator. */
|
|
.file-preview-window {
|
|
width: 100vw;
|
|
max-width: 100vw;
|
|
height: var(--app-height, 100vh);
|
|
max-height: var(--app-height, 100vh);
|
|
border-radius: 0;
|
|
border-left: none;
|
|
border-right: none;
|
|
}
|
|
|
|
.file-preview-editbar {
|
|
padding-bottom: calc(0.4rem + var(--safe-area-bottom));
|
|
}
|
|
|
|
.file-preview-overlay .file-preview-footer {
|
|
padding-bottom: calc(0.35rem + var(--safe-area-bottom));
|
|
}
|
|
|
|
/* >=16px or iOS Safari auto-zooms the page on focus */
|
|
.file-preview-body textarea.file-preview-editor {
|
|
font-size: 16px;
|
|
}
|
|
|
|
/* Notification drawer - full width on mobile, includes safe area padding */
|
|
.notification-drawer {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
right: 0;
|
|
border-radius: 0;
|
|
padding-left: var(--safe-area-left);
|
|
padding-right: var(--safe-area-right);
|
|
padding-bottom: var(--safe-area-bottom);
|
|
}
|
|
|
|
/* Scroll margin for inputs - helps with keyboard visibility */
|
|
input, textarea, [contenteditable] {
|
|
scroll-margin-bottom: 200px;
|
|
scroll-margin-top: 80px;
|
|
}
|
|
|
|
/* When keyboard is visible, the JS paddingBottom on .main already reserves
|
|
space for toolbar + accessory bar. No extra padding needed here — it was
|
|
double-counting and creating dead space between terminal and the bars. */
|
|
|
|
/* Ensure modals scroll properly when keyboard is visible */
|
|
.keyboard-visible .modal-body {
|
|
max-height: 40vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* When keyboard opens in case modal, let body grow taller */
|
|
.keyboard-visible #createCaseModal .modal-body {
|
|
max-height: 60vh;
|
|
}
|
|
|
|
/* Mobile Case Picker - Mobile-specific overrides */
|
|
.mobile-case-picker .modal-backdrop {
|
|
background: rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
.mobile-case-picker-sheet {
|
|
max-height: 60vh;
|
|
padding-bottom: var(--safe-area-bottom);
|
|
animation: slideUp 0.2s ease-out;
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
transform: translateY(100%);
|
|
}
|
|
to {
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.mobile-case-picker-header .modal-close {
|
|
width: 32px;
|
|
height: 32px;
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
.mobile-case-picker-footer {
|
|
padding-bottom: calc(12px + var(--safe-area-bottom));
|
|
}
|
|
|
|
/* ---- Settings Modal: Mobile Optimizations ---- */
|
|
|
|
/* Scrollable tabs row - prevent overflow on small screens */
|
|
.modal-tabs {
|
|
overflow-x: auto;
|
|
-webkit-overflow-scrolling: touch;
|
|
scrollbar-width: none;
|
|
gap: 0.25rem;
|
|
padding: 0 0.75rem 0.5rem 0.75rem;
|
|
flex-wrap: nowrap;
|
|
}
|
|
|
|
.modal-tabs::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.modal-tab-btn {
|
|
padding: 0.35rem 0.6rem;
|
|
font-size: 0.65rem;
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* ---- Case Modal: Mobile Touch Optimizations ---- */
|
|
|
|
/* Larger tab buttons for case modal - easy to tap */
|
|
#createCaseModal .modal-tabs {
|
|
gap: 0.5rem;
|
|
padding: 0.5rem 1rem 0.75rem;
|
|
}
|
|
|
|
#createCaseModal .modal-tab-btn {
|
|
flex: 1;
|
|
min-height: 44px;
|
|
padding: 0.6rem 1rem;
|
|
font-size: 0.8rem;
|
|
font-weight: 500;
|
|
border-radius: 8px;
|
|
justify-content: center;
|
|
text-align: center;
|
|
}
|
|
|
|
/* Touch-friendly form inputs in case modal */
|
|
#createCaseModal .form-row {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
#createCaseModal .form-row label {
|
|
font-size: 0.8rem;
|
|
margin-bottom: 0.4rem;
|
|
font-weight: 500;
|
|
color: #e5e7eb;
|
|
}
|
|
|
|
#createCaseModal .form-row input[type="text"] {
|
|
min-height: 44px;
|
|
font-size: 16px; /* Prevents iOS zoom on focus */
|
|
padding: 0.5rem 0.75rem;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
#createCaseModal .form-row .form-hint {
|
|
font-size: 0.65rem;
|
|
margin-top: 0.35rem;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* Touch-friendly action buttons in case modal */
|
|
#createCaseModal .form-actions {
|
|
padding: 0.75rem 1rem;
|
|
padding-bottom: calc(0.75rem + var(--safe-area-bottom));
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
#createCaseModal .form-actions .btn-toolbar {
|
|
min-height: 44px !important;
|
|
max-height: 44px !important;
|
|
height: 44px !important;
|
|
font-size: 0.85rem !important;
|
|
font-weight: 500;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
/* Loading state for submit button */
|
|
#caseModalSubmit.loading {
|
|
opacity: 0.6;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* Smooth open animation for case modal from mobile picker */
|
|
#createCaseModal.from-mobile .modal-content {
|
|
animation: caseModalSlideUp 0.25s ease-out;
|
|
}
|
|
|
|
@keyframes caseModalSlideUp {
|
|
from {
|
|
transform: translateY(30px);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* Mobile case picker - larger create button (override btn-toolbar !important) */
|
|
.btn-case-create-mobile {
|
|
min-height: 48px !important;
|
|
max-height: 48px !important;
|
|
height: 48px !important;
|
|
font-size: 0.85rem !important;
|
|
font-weight: 500;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
/* Settings grid - single column on phones */
|
|
.settings-grid {
|
|
grid-template-columns: 1fr;
|
|
gap: 0.35rem;
|
|
}
|
|
|
|
.settings-grid-3col {
|
|
grid-template-columns: 1fr 1fr 1fr;
|
|
}
|
|
|
|
.settings-item {
|
|
padding: 0.35rem 0.5rem;
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
.settings-item-label {
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
.settings-section-header {
|
|
font-size: 0.6rem;
|
|
padding: 0.35rem 0 0.2rem 0;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
/* Form rows - compact on mobile */
|
|
.form-row {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.form-row label {
|
|
font-size: 0.7rem;
|
|
margin-bottom: 0.2rem;
|
|
}
|
|
|
|
.form-row .form-hint {
|
|
font-size: 0.6rem;
|
|
}
|
|
|
|
.form-row input[type="text"],
|
|
.form-row input[type="number"],
|
|
.form-row textarea,
|
|
.form-row .form-select,
|
|
.form-row select {
|
|
font-size: 0.75rem;
|
|
padding: 0.35rem 0.5rem;
|
|
min-height: 32px;
|
|
}
|
|
|
|
.form-row-switch {
|
|
gap: 0.3rem;
|
|
}
|
|
|
|
.form-row-switch > label:first-child {
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
.form-section-header {
|
|
font-size: 0.6rem;
|
|
margin-top: 0.75rem;
|
|
}
|
|
|
|
/* Event type grid - tighter on mobile */
|
|
.event-type-grid {
|
|
grid-template-columns: 1fr 36px 36px 36px;
|
|
gap: 4px 4px;
|
|
padding: 6px;
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.event-header {
|
|
font-size: 0.55rem;
|
|
}
|
|
|
|
.event-label {
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
.event-type-grid input[type="checkbox"] {
|
|
width: 12px;
|
|
height: 12px;
|
|
}
|
|
|
|
/* Modal footer buttons - full width on mobile */
|
|
.form-actions {
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.form-actions .btn-toolbar {
|
|
flex: 1;
|
|
min-height: 36px !important;
|
|
max-height: 36px !important;
|
|
height: 36px !important;
|
|
font-size: 0.75rem !important;
|
|
}
|
|
|
|
/* ---- Subagent Windows: Mobile Optimizations ---- */
|
|
|
|
/* Compact stacked cards — multiple visible at once, draggable via touch */
|
|
.subagent-window {
|
|
position: fixed;
|
|
width: calc(100% - 8px);
|
|
max-width: calc(100% - 8px);
|
|
height: 110px;
|
|
min-height: 80px;
|
|
max-height: 110px;
|
|
min-width: 200px;
|
|
border-radius: 6px;
|
|
resize: none;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
/* Hide resize handle on mobile */
|
|
.subagent-window::after {
|
|
display: none;
|
|
}
|
|
|
|
/* Compact subagent window header */
|
|
.subagent-window-header {
|
|
padding: 0.2rem 0.4rem;
|
|
min-height: 24px;
|
|
}
|
|
|
|
.subagent-window-title {
|
|
gap: 0.2rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.subagent-window-title .icon {
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
.subagent-window-title .id {
|
|
font-size: 0.6rem;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
max-width: 45vw;
|
|
}
|
|
|
|
.subagent-window-title .status {
|
|
font-size: 0.45rem;
|
|
padding: 0.05rem 0.15rem;
|
|
}
|
|
|
|
.subagent-model-badge {
|
|
font-size: 0.45rem !important;
|
|
padding: 0.05rem 0.15rem !important;
|
|
}
|
|
|
|
.subagent-window-actions button {
|
|
font-size: 0.7rem;
|
|
padding: 0.15rem 0.35rem;
|
|
min-width: 26px;
|
|
min-height: 26px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.subagent-window-body {
|
|
font-size: 0.6rem;
|
|
padding: 0.2rem 0.35rem;
|
|
}
|
|
|
|
.subagent-window-body .activity-line {
|
|
gap: 0.15rem;
|
|
padding: 0.05rem 0;
|
|
}
|
|
|
|
.subagent-window-body .activity-line .time {
|
|
font-size: 0.5rem;
|
|
}
|
|
|
|
.subagent-window-body .activity-line .tool-name {
|
|
font-size: 0.55rem;
|
|
}
|
|
|
|
.subagent-window-body .activity-line .tool-detail {
|
|
font-size: 0.55rem;
|
|
}
|
|
|
|
/* Hide parent label on mobile to save space */
|
|
.subagent-window-parent {
|
|
display: none;
|
|
}
|
|
|
|
/* ---- Tab Subagent Badges: Smaller on Mobile ---- */
|
|
|
|
.session-tab .tab-subagent-badge {
|
|
height: 14px;
|
|
padding: 0 3px;
|
|
border-radius: 7px;
|
|
margin-left: 2px;
|
|
}
|
|
|
|
.session-tab .tab-subagent-badge .subagent-label {
|
|
font-size: 0.45rem;
|
|
}
|
|
|
|
/* Subagent dropdown - full width near badge on mobile (JS sets top from badge position) */
|
|
.subagent-dropdown {
|
|
position: fixed !important;
|
|
left: 8px !important;
|
|
right: 8px !important;
|
|
bottom: auto !important;
|
|
min-width: auto !important;
|
|
max-width: none !important;
|
|
border-radius: 8px;
|
|
transform: none !important;
|
|
}
|
|
|
|
/* ---- Mobile Overview (phone home screen) ----
|
|
Shown in place of the welcome overlay when the C logo is tapped. Styled only
|
|
with :root tokens so every skin, including the four light ones, works with
|
|
no override block. Never give .mobile-overview itself a display value: the
|
|
element ships with [hidden] and only .visible may turn it on. */
|
|
|
|
.mobile-overview.visible {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 10;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
padding: 0.75rem 0.6rem calc(1rem + var(--safe-area-bottom));
|
|
background: var(--bg-dark);
|
|
overflow-y: auto;
|
|
-webkit-overflow-scrolling: touch;
|
|
overscroll-behavior: contain;
|
|
}
|
|
|
|
/* No side padding: the wordmark's left edge lines up with the session cards
|
|
below it, and the Run button's right edge with theirs. */
|
|
.mobile-overview-top {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.5rem;
|
|
padding: 0;
|
|
}
|
|
|
|
/* Same accent as the "C" in the header (`.logo` uses --accent-hover), and the
|
|
same 48px box as the Run button opposite it, so the two are centered on one
|
|
line by construction rather than by eye. */
|
|
.mobile-overview-brand {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-height: 48px;
|
|
font-size: 1.5rem;
|
|
font-weight: 800;
|
|
line-height: 1;
|
|
color: var(--accent-hover);
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
/* Split Run button. Colors come from the toolbar's own
|
|
`.btn-toolbar.btn-run.mode-<backend>` rules in styles.css (the element
|
|
carries those classes), so this button and the one in the toolbar are the
|
|
same control in two places. Only size/shape is set here, and NOTHING that
|
|
would override the mode gradient. */
|
|
.mobile-overview-header {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.mobile-overview-run-group {
|
|
display: flex;
|
|
align-items: stretch;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Sized well past the 44px touch minimum: this is the primary action on the
|
|
home screen, and the caret is a separate target right next to it.
|
|
⚠️ The !important is required, not decorative: the phone block clamps every
|
|
.btn-toolbar to `height/min-height/max-height: 26px !important`, and this
|
|
button deliberately carries .btn-toolbar to inherit the run-mode gradient.
|
|
Without matching !important (max-height included) it renders 26px tall. */
|
|
.mobile-overview-run-group .mobile-overview-run {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
min-height: 48px !important;
|
|
height: 48px !important;
|
|
max-height: 48px !important;
|
|
padding: 0 1.15rem !important;
|
|
font-size: 0.95rem !important;
|
|
line-height: 1;
|
|
font-weight: 700;
|
|
font-family: inherit;
|
|
border-radius: 10px 0 0 10px !important;
|
|
}
|
|
|
|
.mobile-overview-run-mode {
|
|
font-weight: 600;
|
|
font-size: 0.85rem;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.mobile-overview-run-group .mobile-overview-run-caret {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 48px !important;
|
|
height: 48px !important;
|
|
max-height: 48px !important;
|
|
min-width: 48px !important;
|
|
width: 48px !important;
|
|
padding: 0 !important;
|
|
line-height: 1;
|
|
font-family: inherit;
|
|
border-radius: 0 10px 10px 0 !important;
|
|
}
|
|
|
|
/* The phone block shrinks every run-gear glyph to 10px; this one is a 48px
|
|
tap target, so it gets a proportionate chevron. `display:block` drops the
|
|
inline-baseline gap that would otherwise push it a pixel low. */
|
|
.mobile-overview-run-group .mobile-overview-run-caret svg {
|
|
display: block;
|
|
width: 20px !important;
|
|
height: 20px !important;
|
|
margin: 0 !important;
|
|
}
|
|
|
|
.mobile-overview-run-menu {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.15rem;
|
|
margin: 0.5rem 0 0;
|
|
padding: 0.35rem;
|
|
border: 1px solid var(--border);
|
|
border-radius: 12px;
|
|
background: var(--floating-bg);
|
|
box-shadow: var(--elevated-shadow);
|
|
}
|
|
|
|
.mobile-overview-run-option {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
min-height: 44px;
|
|
padding: 0 0.6rem;
|
|
border: none;
|
|
border-radius: 8px;
|
|
background: transparent;
|
|
color: var(--text);
|
|
font-family: inherit;
|
|
font-size: 0.82rem;
|
|
text-align: left;
|
|
}
|
|
|
|
.mobile-overview-run-option.selected {
|
|
background: var(--control-bg-hover);
|
|
}
|
|
|
|
.mobile-overview-run-option:active {
|
|
background: var(--bg-hover);
|
|
}
|
|
|
|
.mobile-overview-run-option--add {
|
|
color: var(--accent);
|
|
}
|
|
|
|
.mobile-overview-run-header {
|
|
padding: 0.4rem 0.6rem 0.2rem;
|
|
font-size: 0.6rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.09em;
|
|
text-transform: uppercase;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.mobile-overview-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
/* Flush left with the wordmark and the cards: one left edge down the screen. */
|
|
.mobile-overview-heading {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
margin: 0.35rem 0 0;
|
|
padding: 0;
|
|
font-size: 0.65rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.09em;
|
|
text-transform: uppercase;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.mobile-overview-heading-count {
|
|
color: var(--text-dim);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.mobile-overview-empty {
|
|
margin: 0;
|
|
padding: 0.5rem 0;
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* Row: 56px tap target, dot + two-line body + pill + chevron */
|
|
.mobile-overview-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.6rem;
|
|
width: 100%;
|
|
min-height: 56px;
|
|
padding: 0.5rem 0.7rem;
|
|
border: 1px solid var(--border);
|
|
border-radius: 12px;
|
|
background: var(--bg-card);
|
|
color: var(--text);
|
|
font-family: inherit;
|
|
text-align: left;
|
|
}
|
|
|
|
.mobile-overview-row:active {
|
|
background: var(--bg-hover);
|
|
}
|
|
|
|
/* Approvals Inbox answer strip: sits under a NEEDS YOU row (sibling of the
|
|
row <button>, see _buildMobileOverviewApprovalStrip). Buttons inherit no
|
|
toolbar styling on purpose; they are one-tap dialog answers, not runs. */
|
|
.mobile-overview-row-wrap {
|
|
width: 100%;
|
|
}
|
|
.mobile-overview-approval-strip {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.4rem;
|
|
padding: 0.4rem 0.2rem 0.1rem;
|
|
}
|
|
.mobile-overview-approval-btn {
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
background: var(--bg-card);
|
|
color: var(--text);
|
|
font-family: inherit;
|
|
font-size: 0.72rem;
|
|
padding: 0.35rem 0.6rem;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.mobile-overview-approval-btn.primary {
|
|
background: var(--accent);
|
|
border-color: var(--accent);
|
|
color: white;
|
|
}
|
|
.mobile-overview-approval-btn.danger {
|
|
border-color: var(--error);
|
|
color: var(--error);
|
|
}
|
|
|
|
/* Attention states mirror the session tabs exactly: red blink when the agent
|
|
asked something (permission / question), yellow blink when it is waiting for
|
|
a prompt. Same hues and same cadence as tab-blink-red / tab-blink-yellow in
|
|
styles.css; the keyframes are re-declared here only because a tab's resting
|
|
background is transparent while a row's is the card color. */
|
|
.mobile-overview-row--needs {
|
|
border-color: var(--red);
|
|
animation: mobile-overview-blink-red 2.5s ease-in-out infinite;
|
|
}
|
|
|
|
.mobile-overview-row--waiting {
|
|
border-color: var(--yellow);
|
|
animation: mobile-overview-blink-yellow 3.5s ease-in-out infinite;
|
|
}
|
|
|
|
.mobile-overview-row--error {
|
|
border-color: var(--red);
|
|
}
|
|
|
|
/* Working is not an alert, so it gets a calm green breathing edge rather than a
|
|
blink: at a glance the row reads "this one is moving", without competing with
|
|
the two states that actually want you. Slower than both of them on purpose. */
|
|
.mobile-overview-row--working {
|
|
border-color: var(--green);
|
|
animation: mobile-overview-breathe-green 2.2s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes mobile-overview-breathe-green {
|
|
0%,
|
|
100% {
|
|
background: var(--bg-card);
|
|
border-color: var(--border);
|
|
}
|
|
50% {
|
|
background: rgba(34, 197, 94, 0.1);
|
|
border-color: var(--green);
|
|
}
|
|
}
|
|
|
|
/* The pill picks up a three-dot ellipsis that fills in and empties, so the row
|
|
still reads as active on a skin where the border tint is subtle. */
|
|
.mobile-overview-pill--working::after {
|
|
content: '';
|
|
display: inline-block;
|
|
width: 0.75em;
|
|
text-align: left;
|
|
animation: mobile-overview-pill-dots 1.5s steps(1, end) infinite;
|
|
}
|
|
|
|
@keyframes mobile-overview-pill-dots {
|
|
0% {
|
|
content: '';
|
|
}
|
|
25% {
|
|
content: '.';
|
|
}
|
|
50% {
|
|
content: '..';
|
|
}
|
|
75% {
|
|
content: '...';
|
|
}
|
|
}
|
|
|
|
@keyframes mobile-overview-blink-red {
|
|
0%,
|
|
100% {
|
|
background: var(--bg-card);
|
|
border-color: var(--border);
|
|
}
|
|
50% {
|
|
background: rgba(239, 68, 68, 0.14);
|
|
border-color: var(--red);
|
|
}
|
|
}
|
|
|
|
@keyframes mobile-overview-blink-yellow {
|
|
0%,
|
|
100% {
|
|
background: var(--bg-card);
|
|
border-color: var(--border);
|
|
}
|
|
50% {
|
|
background: rgba(234, 179, 8, 0.12);
|
|
border-color: var(--yellow);
|
|
}
|
|
}
|
|
|
|
.mobile-overview-row-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.15rem;
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.mobile-overview-row-title {
|
|
display: block;
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.mobile-overview-row-case {
|
|
color: var(--text-dim);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.mobile-overview-row-sub {
|
|
display: block;
|
|
font-size: 0.68rem;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.mobile-overview-dot {
|
|
flex-shrink: 0;
|
|
width: 9px;
|
|
height: 9px;
|
|
border-radius: 50%;
|
|
background: var(--text-muted);
|
|
}
|
|
|
|
.mobile-overview-dot--needs,
|
|
.mobile-overview-dot--error {
|
|
background: var(--red);
|
|
}
|
|
|
|
.mobile-overview-dot--waiting {
|
|
background: var(--yellow);
|
|
}
|
|
|
|
/* Same as .session-tab .tab-status: green when the session is fine, and the
|
|
shared `pulse` keyframes while it is working. The halo matches the busy tab
|
|
dot and the desktop home column (.home-sessions-dot--working, styles.css):
|
|
working reads identically on every surface or it reads as three features. */
|
|
.mobile-overview-dot--working {
|
|
background: var(--green);
|
|
animation: pulse 1.5s infinite;
|
|
box-shadow: 0 0 8px 2px color-mix(in srgb, var(--green) 55%, transparent);
|
|
will-change: opacity;
|
|
}
|
|
|
|
/* Ring the pulsing dot with the SAME spinner a tab shows while it loads: same
|
|
2px ring, same bright leading edge, same `tab-load-spin` keyframes from
|
|
styles.css (reused, not re-declared, so the two can never drift). Green
|
|
rather than the tab's blue because here it means "running", not "loading":
|
|
the motion is the shared part, the color still belongs to the state. */
|
|
.mobile-overview-dot {
|
|
position: relative;
|
|
}
|
|
|
|
.mobile-overview-dot--working::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset: -4px;
|
|
border: 2px solid rgba(34, 197, 94, 0.25);
|
|
border-top-color: var(--green);
|
|
border-radius: 50%;
|
|
animation: tab-load-spin 0.7s linear infinite;
|
|
}
|
|
|
|
.mobile-overview-dot--idle {
|
|
background: var(--green);
|
|
}
|
|
|
|
.mobile-overview-dot--done {
|
|
background: var(--text-muted);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.mobile-overview-pill {
|
|
flex-shrink: 0;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
padding: 0.2rem 0.45rem;
|
|
border: 1px solid var(--border-light);
|
|
border-radius: 999px;
|
|
font-size: 0.62rem;
|
|
font-weight: 600;
|
|
color: var(--text-dim);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.mobile-overview-pill--needs,
|
|
.mobile-overview-pill--error {
|
|
border-color: var(--red);
|
|
color: var(--red);
|
|
}
|
|
|
|
.mobile-overview-pill--waiting {
|
|
border-color: var(--yellow);
|
|
color: var(--yellow);
|
|
}
|
|
|
|
.mobile-overview-pill--idle,
|
|
.mobile-overview-pill--working {
|
|
border-color: var(--green);
|
|
color: var(--green);
|
|
}
|
|
|
|
.mobile-overview-chevron {
|
|
flex-shrink: 0;
|
|
color: var(--text-muted);
|
|
font-size: 1rem;
|
|
line-height: 1;
|
|
}
|
|
|
|
/* Past conversations: quieter than a live row, since tapping one starts work */
|
|
.mobile-overview-row--past {
|
|
background: transparent;
|
|
border-style: dashed;
|
|
}
|
|
|
|
.mobile-overview-row--past .mobile-overview-row-title {
|
|
font-weight: 500;
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
.mobile-overview-dot--past {
|
|
background: transparent;
|
|
border: 1px solid var(--border-light);
|
|
}
|
|
|
|
.mobile-overview-pill--past {
|
|
border-color: var(--border-light);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.mobile-overview-more {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.35rem;
|
|
min-height: 40px;
|
|
border: none;
|
|
border-radius: 10px;
|
|
background: var(--control-bg);
|
|
color: var(--text-dim);
|
|
font-family: inherit;
|
|
font-size: 0.72rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.mobile-overview-more-count {
|
|
padding: 0.05rem 0.35rem;
|
|
border-radius: 999px;
|
|
background: var(--control-bg-hover);
|
|
color: var(--text-muted);
|
|
}
|
|
}
|
|
|
|
/* The overview's attention blink is an alert, so it stays visible without
|
|
motion: hold the alert color instead of animating to it. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.mobile-overview-row--needs,
|
|
.mobile-overview-row--waiting {
|
|
animation: none;
|
|
}
|
|
|
|
.mobile-overview-row--needs {
|
|
background: rgba(239, 68, 68, 0.14);
|
|
}
|
|
|
|
.mobile-overview-row--waiting {
|
|
background: rgba(234, 179, 8, 0.12);
|
|
}
|
|
|
|
.mobile-overview-dot--working {
|
|
animation: none;
|
|
}
|
|
|
|
/* The ring stays as a static full circle: it still marks the row, it just
|
|
stops turning. */
|
|
.mobile-overview-dot--working::after {
|
|
border-color: var(--green);
|
|
animation: none;
|
|
}
|
|
|
|
/* Working is only informational, so it drops to a static green edge and a
|
|
static ellipsis rather than holding a tint the way the alerts do. */
|
|
.mobile-overview-row--working {
|
|
animation: none;
|
|
}
|
|
|
|
.mobile-overview-pill--working::after {
|
|
content: '...';
|
|
animation: none;
|
|
}
|
|
}
|
|
|
|
/* Light-skin compatibility for mobile-only chrome. These components predate
|
|
the shared skin system and intentionally retain their original dark values
|
|
for the three dark skins above. */
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(.header, .toolbar, .keyboard-accessory-bar) {
|
|
background: var(--glass-bg);
|
|
border-color: var(--glass-border);
|
|
color: var(--text);
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(.btn-voice-mobile, .btn-settings-mobile, .btn-toolbar.btn-shell, .toolbar .btn-case-add, .accessory-btn) {
|
|
background: var(--control-bg);
|
|
border-color: var(--control-border);
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(.btn-voice-mobile:active, .btn-settings-mobile:active, .btn-toolbar.btn-shell:hover, .btn-toolbar.btn-shell:active, .btn-case-add:hover, .btn-case-add:active, .accessory-btn:active) {
|
|
background: var(--control-bg-hover);
|
|
border-color: var(--control-border-hover);
|
|
color: var(--text);
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(.btn-toolbar.btn-run.mode-claude, .btn-toolbar.btn-run-gear.mode-claude) {
|
|
background: linear-gradient(135deg, var(--accent-grad-a), var(--accent-grad-b));
|
|
border-color: var(--accent);
|
|
color: var(--accent-ink);
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(.btn-toolbar.btn-run.mode-opencode, .btn-toolbar.btn-run-gear.mode-opencode) {
|
|
background: linear-gradient(135deg, var(--accent-d), var(--accent-grad-b));
|
|
border-color: var(--accent);
|
|
color: var(--accent-ink);
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(.btn-toolbar.btn-run.mode-gemini, .btn-toolbar.btn-run-gear.mode-gemini) {
|
|
background: linear-gradient(135deg, #174ea6, #4f46e5);
|
|
border-color: #315fc3;
|
|
color: #ffffff;
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(.btn-toolbar.btn-run.mode-antigravity, .btn-toolbar.btn-run-gear.mode-antigravity) {
|
|
background: linear-gradient(135deg, #0e7490, #0891b2);
|
|
border-color: #155e75;
|
|
color: #ffffff;
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) .btn-toolbar.btn-run-gear {
|
|
border-left-color: var(--control-border-hover) !important;
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(.case-settings-popover-mobile, .mobile-case-picker-sheet) {
|
|
background: var(--floating-bg);
|
|
border-color: var(--control-border);
|
|
color: var(--text);
|
|
box-shadow: var(--elevated-shadow);
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) :is(.case-settings-popover-mobile .checkbox-inline, #createCaseModal .form-row label) {
|
|
color: var(--text);
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) .case-settings-popover-mobile .form-hint {
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="catppuccin-latte"], [data-skin="rose-pine-dawn"]) .mobile-case-picker .modal-backdrop {
|
|
background: var(--modal-backdrop);
|
|
}
|
|
|
|
|
|
/* Keyboard accessory bar + paste overlay base styles moved to styles.css
|
|
(always loaded — covers iPad landscape where mobile.css doesn't load).
|
|
Phone-specific overrides remain in @media (max-width: 430px) above. */
|
|
|
|
/* ============================================================================
|
|
iOS Safari Specific Fixes
|
|
============================================================================ */
|
|
.ios-device.safari-browser {
|
|
/* Prevent pull-to-refresh */
|
|
overscroll-behavior: none;
|
|
}
|
|
|
|
/* JS touch handler now drives terminal scrollback — no native scroll needed.
|
|
-webkit-overflow-scrolling and overflow-y:scroll on xterm-viewport removed. */
|
|
|
|
/* ============================================================================
|
|
Hover State Fallbacks for Touch
|
|
============================================================================ */
|
|
@media (hover: none) and (pointer: coarse) {
|
|
/* Remove hover-dependent UI patterns */
|
|
.session-tab .tab-close,
|
|
.session-tab .tab-gear {
|
|
opacity: 1;
|
|
width: auto;
|
|
padding: 0.15rem 0.25rem;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
/* Simplify button hover states */
|
|
.btn-toolbar:hover {
|
|
transform: none;
|
|
}
|
|
|
|
.btn-icon-header:hover {
|
|
transform: none;
|
|
}
|
|
|
|
/* Show dropdown on tap instead of hover */
|
|
.subagent-dropdown-trigger:active + .subagent-dropdown-menu,
|
|
.subagent-dropdown-trigger:focus + .subagent-dropdown-menu {
|
|
display: block;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 430px) {
|
|
/* Attachment history (COD-18): full-screen sheet on phones */
|
|
.attachment-history-drawer {
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 100%;
|
|
max-width: 100%;
|
|
height: 100vh;
|
|
height: 100dvh;
|
|
border-left: none;
|
|
border-radius: 0;
|
|
padding-top: var(--safe-area-top);
|
|
padding-left: var(--safe-area-left);
|
|
padding-right: var(--safe-area-right);
|
|
padding-bottom: var(--safe-area-bottom);
|
|
}
|
|
|
|
.attachment-history-header {
|
|
padding: 12px;
|
|
}
|
|
|
|
.attachment-history-list {
|
|
padding: 6px;
|
|
}
|
|
|
|
.attachment-history-item {
|
|
grid-template-columns: 104px minmax(0, 1fr);
|
|
gap: 8px;
|
|
padding: 8px 6px;
|
|
}
|
|
|
|
.attachment-history-thumb {
|
|
width: 104px;
|
|
}
|
|
|
|
.attachment-history-actions {
|
|
gap: 4px;
|
|
}
|
|
|
|
.attachment-history-actions button {
|
|
padding: 4px 7px;
|
|
}
|
|
}
|
|
|
|
/* ============================================================================
|
|
Connection loss: phone sizing
|
|
The banner sits in normal flow directly under the fixed header (the container
|
|
already reserves that space), so it needs the same safe-area padding as the
|
|
other banners. The overlay is fixed and handles its own insets.
|
|
============================================================================ */
|
|
@media (max-width: 430px) {
|
|
.offline-banner {
|
|
padding: 0.4rem 0.5rem;
|
|
padding-left: calc(0.5rem + var(--safe-area-left));
|
|
padding-right: calc(0.5rem + var(--safe-area-right));
|
|
font-size: 0.7rem;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
/* The countdown is the first thing to go when the bar gets tight. The
|
|
wording plus the Retry button carry the message on their own. */
|
|
.offline-banner-detail {
|
|
display: none;
|
|
}
|
|
|
|
.offline-banner-retry {
|
|
padding: 0.25rem 0.5rem;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.offline-overlay-card {
|
|
padding: 22px 18px 18px;
|
|
}
|
|
|
|
.offline-overlay-title {
|
|
font-size: 1.05rem;
|
|
}
|
|
|
|
.offline-overlay-actions {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.offline-overlay-btn {
|
|
width: 100%;
|
|
padding: 0.65rem 1rem;
|
|
}
|
|
}
|