Files
openclaw/ui/src/styles/components.css
T
Peter Steinberger ab2bd4faa8 feat(ui): consolidate activity filters into toolbar with people popover (#125917)
* test(ui): seed mock activity sessions across owners

* feat(ui): consolidate activity filters into toolbar

Also restores the shared .input styling contract for standalone form controls.

* test(ui): update activity feed e2e for toolbar people popover
2026-08-18 11:41:06 -07:00

6119 lines
127 KiB
CSS

/* ===========================================
Connect Splash (first connect with stored credentials)
=========================================== */
.connect-splash {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
height: 100dvh;
max-height: 100%;
background: var(--bg);
/* Stay invisible past fast handshakes so a quick connect paints nothing. */
opacity: 0;
animation: fade-in 0.3s var(--ease-out) 0.2s forwards;
}
.connect-splash__status {
margin-top: 16px;
color: var(--muted);
font-size: 14px;
}
openclaw-session-owner-chip {
display: inline-flex;
flex: 0 0 auto;
}
.session-owner-chip {
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid hsl(var(--owner-hue) 52% 18% / 0.9);
border-radius: 999px;
background: hsl(var(--owner-hue) 58% 26%);
box-shadow: 0 1px 2px rgb(0 0 0 / 0.24);
color: white;
font-weight: 700;
line-height: 1;
transition:
filter var(--duration-fast) ease,
opacity var(--duration-fast) ease;
}
.session-owner-chip--away {
filter: grayscale(0.8);
opacity: 0.45;
}
.session-owner-chip--row {
width: 20px;
height: 20px;
font-size: 10px;
}
.session-owner-chip--header {
width: 20px;
height: 20px;
font-size: 10px;
}
.session-owner-chip .viewer-avatar {
width: 100%;
height: 100%;
border: 0;
font-size: inherit;
}
.session-owner-stack {
position: relative;
display: inline-flex;
width: 24px;
height: 20px;
align-items: center;
justify-content: flex-end;
}
.session-owner-stack__back {
position: absolute;
z-index: 0;
left: 0;
display: inline-flex;
width: 14px;
height: 14px;
}
.session-owner-stack__back .viewer-avatar,
.session-owner-stack__overflow {
width: 14px;
height: 14px;
border: 1px solid var(--sidebar-bg, var(--bg));
border-radius: var(--radius-full);
font-size: 7px;
}
.session-owner-stack__overflow {
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--bg-elevated);
color: var(--muted);
font-weight: 700;
line-height: 1;
}
.session-owner-stack__front {
position: relative;
z-index: 1;
}
/* Leading slot shared by every sidebar row: the row's own artwork plus a run
ring and corner badge, so run state always reads at the row's left edge. */
.session-glyph {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
flex: 0 0 20px;
}
.session-glyph--circular .session-glyph__content {
width: 20px;
height: 20px;
}
.session-glyph__content {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
transition: transform var(--duration-fast) ease;
}
.channel-avatar {
display: block;
width: 20px;
height: 20px;
border-radius: var(--radius-full);
object-fit: cover;
}
.session-glyph__emoji {
font-size: 15px;
line-height: 1;
}
.session-glyph__icon,
.session-glyph__icon svg {
width: 16px;
height: 16px;
}
.session-glyph__icon {
display: inline-flex;
color: inherit;
}
/* The ring is a circle, so square artwork (page icons, thumbnails, arbitrary
SVGs) must shrink to keep its corners inside it. A 16px square scaled below
0.75 clears its 21px inner circle; 20px circular avatars fill their lead. */
.session-glyph--running .session-glyph__content {
transform: scale(0.72);
}
.session-glyph--circular.session-glyph--running .session-glyph__content {
transform: none;
}
.session-glyph__ring {
position: absolute;
inset: -2px;
box-sizing: border-box;
border: 1.5px solid color-mix(in srgb, var(--run) 28%, transparent);
border-top-color: var(--run);
border-radius: var(--radius-full);
pointer-events: none;
}
.session-glyph__badge {
position: absolute;
z-index: 1;
top: -2px;
right: -2px;
width: 7px;
height: 7px;
border-radius: var(--radius-full);
background: currentColor;
box-shadow: 0 0 0 1.5px var(--bg);
}
.session-glyph__badge--unread {
color: var(--accent);
}
.connect-splash__logo {
width: 44px;
height: 44px;
animation: connect-splash-wiggle 1.8s ease-in-out infinite;
}
@keyframes connect-splash-wiggle {
0%,
100% {
transform: rotate(-6deg);
}
50% {
transform: rotate(6deg);
}
}
@media (prefers-reduced-motion: reduce) {
.connect-splash {
opacity: 1;
animation: none;
}
.connect-splash__logo {
animation: none;
}
}
/* Shared by the login gate and the Sessions, Tasks, and Cron routes. */
.session-link {
color: var(--accent);
font-weight: 500;
text-decoration: none;
}
.session-link:hover {
text-decoration: underline;
}
/* ===========================================
Login Gate
=========================================== */
.login-gate {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
height: 100dvh;
max-height: 100%;
overflow-y: auto;
background: var(--bg);
padding: 24px;
}
.login-gate__card {
width: min(520px, 100%);
margin-block: auto;
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 32px;
animation: scale-in 0.25s var(--ease-out);
}
.login-gate__header {
text-align: center;
margin-bottom: 24px;
}
.login-gate__logo {
width: 48px;
height: 48px;
margin-bottom: 12px;
}
.login-gate__title {
font-size: 22px;
font-weight: 700;
letter-spacing: -0.02em;
}
.login-gate__sub {
color: var(--muted);
font-size: 14px;
margin-top: 4px;
}
.login-gate__form {
display: flex;
flex-direction: column;
gap: 12px;
}
.login-gate__connect {
margin-top: 4px;
width: 100%;
justify-content: center;
padding: 10px 16px;
font-size: 15px;
font-weight: 600;
}
.login-gate__failure {
margin-top: 14px;
}
.login-gate__failure-title {
font-size: 14px;
font-weight: 700;
color: var(--text);
}
.login-gate__failure-summary {
margin-top: 6px;
font-size: 13px;
line-height: 1.45;
}
.login-gate__failure-steps {
margin: 10px 0 0;
padding-left: 18px;
font-size: 12px;
line-height: 1.55;
}
.login-gate__failure-steps li + li {
margin-top: 4px;
}
.login-gate__failure-detail {
margin-top: 10px;
font-size: 12px;
}
.login-gate__failure-detail summary {
color: var(--muted);
}
.login-gate__failure-raw {
margin-top: 6px;
overflow-wrap: anywhere;
color: var(--muted);
}
.login-gate__failure-docs {
display: inline-flex;
margin-top: 10px;
font-size: 12px;
}
.login-gate__help {
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid var(--border);
}
.login-gate__help-title {
font-weight: 600;
font-size: 12px;
color: var(--text);
}
.login-gate__steps {
margin: 10px 0 0;
padding-left: 20px;
font-size: 12px;
line-height: 1.6;
color: var(--muted);
}
.login-gate__steps li {
margin-bottom: 6px;
}
.login-gate__steps li:last-child {
margin-bottom: 0;
}
.login-gate__steps code {
display: block;
margin: 4px 0 2px;
padding: 5px 10px;
font-family: var(--mono);
font-size: 12px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text);
user-select: all;
}
.login-gate__command {
display: flex;
align-items: center;
gap: 8px;
margin: 4px 0 2px;
padding: 5px 8px 5px 10px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text);
cursor: copy;
}
.login-gate__command:hover {
border-color: var(--accent);
}
.login-gate__command:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.login-gate__command code {
flex: 1;
margin: 0;
padding: 0;
background: transparent;
border: 0;
}
.login-gate__docs {
margin-top: 10px;
font-size: 12px;
}
/* Phones (matches the layout.mobile.css breakpoint). The gate renders outside
the shell, so it needs its own safe-area padding for notch/home-bar overlap
with viewport-fit=cover. */
@media (max-width: 768px), (max-width: 932px) and (max-height: 500px) and (orientation: landscape) {
.login-gate {
padding: max(16px, var(--safe-area-top)) max(12px, var(--safe-area-right))
max(16px, var(--safe-area-bottom)) max(12px, var(--safe-area-left));
}
.login-gate__card {
padding: 24px 20px;
}
/* 44px touch targets per platform guidelines. */
.login-gate__form .field input {
min-height: 44px;
}
/* Inset reveal eye keeps a comfortable coarse-pointer hit area. */
.login-gate__form .settings-secret__toggle {
width: 32px;
height: 32px;
right: 6px;
}
.login-gate__connect {
min-height: 44px;
}
}
/* The standalone body already owns safe-area insets. Reusing them inside the
gate would double notch/home-bar spacing and shrink its scroll viewport. */
@media (display-mode: standalone) and (max-width: 768px),
(display-mode: standalone) and (max-width: 932px) and (max-height: 500px) and (orientation: landscape) {
.login-gate {
padding: 16px 12px;
}
}
/* ===========================================
Cards - Refined with depth
=========================================== */
.card {
border: 1px solid var(--border);
background: var(--card);
border-radius: var(--radius-lg);
padding: 18px;
animation: rise 0.25s var(--ease-out) backwards;
transition:
border-color var(--duration-normal) var(--ease-out),
box-shadow var(--duration-normal) var(--ease-out);
}
.card:hover {
border-color: var(--border-strong);
box-shadow: var(--shadow-sm);
}
.card-title {
font-size: 15px;
font-weight: 600;
letter-spacing: -0.02em;
color: var(--text-strong);
}
.card-sub {
color: var(--muted);
font-size: 13px;
margin-top: 6px;
line-height: 1.5;
}
/* ===========================================
Stats - Bold values, subtle labels
=========================================== */
.stat {
background: var(--card);
border-radius: var(--radius-md);
padding: 14px 16px;
border: 1px solid var(--border);
transition:
border-color var(--duration-normal) var(--ease-out),
box-shadow var(--duration-normal) var(--ease-out);
}
.stat:hover {
border-color: var(--border-strong);
}
.stat-value {
font-size: 24px;
font-weight: 700;
margin-top: 6px;
letter-spacing: -0.03em;
line-height: 1.1;
}
/* ===========================================
Labels & Pills
=========================================== */
.label {
color: var(--muted);
font-size: 12px;
font-weight: 500;
}
.pill {
display: inline-flex;
align-items: center;
gap: 5px;
border: 1px solid var(--border);
padding: 5px 11px;
border-radius: var(--radius-full);
background: var(--secondary);
font-size: 12px;
font-weight: 500;
transition: border-color var(--duration-fast) ease;
}
.pill:hover {
border-color: var(--border-strong);
}
.pill.danger {
border-color: var(--danger-subtle);
background: var(--danger-subtle);
color: var(--danger);
}
/* ===========================================
Status Dot - With glow for emphasis
=========================================== */
.statusDot {
width: 8px;
height: 8px;
border-radius: var(--radius-full);
background: var(--danger);
box-shadow: 0 0 8px rgba(239, 68, 68, 0.5);
animation: pulse-subtle 2s ease-in-out infinite;
}
.statusDot.ok {
background: var(--ok);
box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
animation: none;
}
.statusDot.warn {
background: var(--warn);
box-shadow: 0 0 8px rgba(245, 158, 11, 0.5);
animation: none;
}
.statusDot.muted {
background: var(--muted);
box-shadow: none;
animation: none;
opacity: 0.5;
}
/* ===========================================
Buttons - Tactile with personality
=========================================== */
/* A disabled button reads as "unavailable"; the spinner says "working" so a
multi-minute update does not look like a dead control. */
.btn__spinner {
width: 12px;
height: 12px;
flex: 0 0 auto;
border: 2px solid color-mix(in srgb, currentColor 35%, transparent);
border-top-color: currentColor;
border-radius: var(--radius-full);
animation: btn-spinner-spin 0.7s linear infinite;
}
@keyframes btn-spinner-spin {
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: reduce) {
.btn__spinner {
animation: none;
}
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
border: 1px solid var(--border);
background: var(--bg-elevated);
padding: 8px 14px;
border-radius: var(--radius-md);
font-size: 13px;
font-weight: 500;
letter-spacing: -0.01em;
/* Labels never break mid-text; tight clusters wrap whole buttons instead
(see .settings-row--actions). */
white-space: nowrap;
transition:
border-color var(--duration-fast) var(--ease-out),
background var(--duration-fast) var(--ease-out),
box-shadow var(--duration-fast) var(--ease-out);
}
.btn:hover {
background: var(--bg-hover);
border-color: var(--border-strong);
}
.btn:active {
background: var(--secondary);
}
.btn svg {
width: 16px;
height: 16px;
stroke: currentColor;
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
stroke-linejoin: round;
flex-shrink: 0;
}
.btn.primary {
border-color: var(--accent);
background: var(--primary);
color: var(--primary-foreground);
box-shadow: 0 1px 3px var(--accent-subtle);
}
.btn.primary:hover {
background: var(--accent-hover);
border-color: var(--accent-hover);
box-shadow: 0 2px 12px var(--accent-glow);
}
:root[data-theme="dark"] .btn.primary:hover {
background: var(--primary-hover);
}
:root[data-theme="openknot"] .btn.primary:hover {
background: var(--primary-hover);
}
.btn.active {
border-color: var(--accent);
background: var(--accent-subtle);
color: var(--accent);
}
.btn.danger {
border-color: transparent;
background: var(--danger-subtle);
color: var(--danger);
}
.btn.danger:hover {
background: rgba(239, 68, 68, 0.15);
}
.btn--sm {
padding: 6px 10px;
font-size: 12px;
}
.btn--xs {
padding: 4px 6px;
font-size: 12px;
line-height: 1;
}
/* Passive notifications stay in the trailing top corner, away from the chat
* composer and other bottom-docked controls. The host shows one toast at a time
* (`show()` replaces the active one), so there is no stack to offset against. */
.app-toast {
position: fixed;
right: calc(20px + var(--safe-area-right));
top: calc(20px + var(--safe-area-top));
z-index: var(--z-toast);
display: flex;
align-items: center;
gap: var(--space-3);
max-width: min(520px, calc(100vw - 32px));
padding: 10px 12px 10px 16px;
border: 1px solid var(--border-strong);
border-radius: var(--radius-md);
background: var(--popover);
color: var(--popover-foreground);
box-shadow: var(--shadow-lg);
}
/* A corner toast degenerates at phone widths, where it would span nearly the
* whole viewport anyway. Use the full-width top idiom instead. */
@media (max-width: 768px), (max-width: 932px) and (max-height: 500px) and (orientation: landscape) {
.app-toast {
left: max(12px, var(--safe-area-left));
right: max(12px, var(--safe-area-right));
max-width: none;
}
}
/* Toasts carry recovery instructions ("Undo", where to re-enable), so a message
* that outgrows one line wraps instead of ellipsing the instruction away, with
* even line lengths because the wrapped text is a sentence to read rather than a
* label to scan. The clamp still bounds a pathological string. */
.app-toast__message {
min-width: 0;
overflow: hidden;
font-size: var(--control-ui-text-md);
text-wrap: balance;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
}
.app-toast__action,
.app-toast__dismiss {
flex: 0 0 auto;
border: 0;
background: transparent;
color: inherit;
}
.app-toast__action {
color: var(--accent);
font-size: var(--control-ui-text-sm);
font-weight: 700;
}
.app-toast__dismiss {
display: grid;
width: 28px;
height: 28px;
padding: 0;
place-items: center;
border-radius: var(--radius-sm);
color: var(--muted);
font-size: 20px;
line-height: 1;
}
.app-toast__action:hover,
.app-toast__action:focus-visible,
.app-toast__dismiss:hover,
.app-toast__dismiss:focus-visible {
color: var(--text-strong);
}
.btn--icon {
padding: 8px;
min-width: 36px;
height: 36px;
border: 1px solid var(--border);
background: rgba(255, 255, 255, 0.06);
}
.btn--icon:hover {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.2);
}
.btn--icon svg {
display: block;
width: 18px;
height: 18px;
stroke: currentColor;
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
stroke-linejoin: round;
}
openclaw-github-link-hovercard-provider {
display: contents;
}
openclaw-session-progress-hovercard-provider {
display: contents;
}
.session-progress-hovercard {
position: fixed;
z-index: 1990;
width: min(380px, calc(100vw - 24px));
max-height: min(520px, calc(100vh - 24px));
padding: 14px 16px;
overflow: auto;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: var(--card);
box-shadow: var(--shadow-lg);
color: var(--text);
pointer-events: none;
}
@media (hover: hover) {
.session-progress-hovercard {
pointer-events: auto;
}
}
.github-link-hovercard {
position: fixed;
z-index: 1990;
width: min(440px, calc(100vw - 24px));
min-height: 112px;
padding: 16px;
border: 1px solid color-mix(in srgb, var(--border-strong) 88%, transparent);
border-radius: var(--radius-lg);
background: color-mix(in srgb, var(--card) 96%, var(--bg) 4%);
box-shadow: var(--shadow-lg);
color: var(--text);
font-family: var(--font-body);
/* Interactive by design: hover intent and text selection both need the card to
receive pointer events (see github-link-hovercard.runtime.ts). */
pointer-events: auto;
opacity: 0;
transform: translateY(-5px) scale(0.99);
transform-origin: bottom left;
transition:
opacity var(--duration-normal) var(--ease-out),
transform var(--duration-normal) var(--ease-out);
}
.github-link-hovercard[data-side="bottom"] {
transform: translateY(5px) scale(0.99);
transform-origin: top left;
}
.github-link-hovercard[data-open="true"] {
opacity: 1;
transform: translateY(0) scale(1);
}
.github-link-hovercard__header,
.github-link-hovercard__footer {
display: flex;
align-items: center;
min-width: 0;
}
.github-link-hovercard__header {
gap: 9px;
color: var(--muted);
font-size: var(--control-ui-text-sm);
}
.github-link-hovercard__state {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
gap: 6px;
padding: 4px 9px;
border-radius: var(--radius-full);
background: color-mix(in srgb, currentColor 12%, transparent);
font-weight: 600;
}
.github-link-hovercard__state[data-tone="open"] {
color: var(--ok);
}
.github-link-hovercard__state[data-tone="danger"] {
color: var(--danger);
}
.github-link-hovercard__state[data-tone="purple"] {
color: var(--pr-merged);
}
.github-link-hovercard__state-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: currentColor;
box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 14%, transparent);
}
/* The repo reference, author and files chip are links too, but the title owns
the card's only underline; the rest stay text until hovered or focused. */
.github-link-hovercard__repo {
min-width: 0;
color: inherit;
overflow: hidden;
text-decoration: none;
text-overflow: ellipsis;
white-space: nowrap;
}
.github-link-hovercard__repo:hover,
.github-link-hovercard__repo:focus-visible,
.github-link-hovercard__author:hover,
.github-link-hovercard__author:focus-visible {
color: var(--text-strong);
text-decoration: none;
}
.github-link-hovercard__time {
flex: 0 0 auto;
margin-left: auto;
white-space: nowrap;
}
.github-link-hovercard__title {
display: block;
margin: 15px 0 17px;
color: var(--text-strong);
font-size: var(--control-ui-text-lg);
font-weight: 650;
letter-spacing: -0.012em;
line-height: 1.35;
overflow-wrap: anywhere;
text-decoration: none;
}
.github-link-hovercard__title:hover,
.github-link-hovercard__title:focus-visible {
text-decoration: underline;
}
.github-link-hovercard__footer {
justify-content: space-between;
gap: 16px;
}
.github-link-hovercard__author,
.github-link-hovercard__metrics {
display: inline-flex;
align-items: center;
}
.github-link-hovercard__author {
min-width: 0;
gap: 8px;
color: var(--muted);
font-size: var(--control-ui-text-md);
overflow: hidden;
text-decoration: none;
text-overflow: ellipsis;
white-space: nowrap;
}
.github-link-hovercard__avatar {
width: 25px;
height: 25px;
flex: 0 0 auto;
border: 1px solid color-mix(in srgb, var(--border-strong) 82%, transparent);
border-radius: 50%;
background: var(--bg-muted);
object-fit: cover;
}
.github-link-hovercard__metrics {
flex: 0 0 auto;
gap: 6px;
}
.github-link-hovercard__metric {
padding: 4px 8px;
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--bg-muted) 82%, transparent);
color: var(--muted);
font-family: var(--mono);
font-size: var(--control-ui-text-sm);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.github-link-hovercard__metric--files {
text-decoration: none;
}
.github-link-hovercard__metric--files:hover,
.github-link-hovercard__metric--files:focus-visible {
background: var(--bg-muted);
color: var(--text-strong);
text-decoration: none;
}
.github-link-hovercard__metric--additions {
color: var(--ok);
}
.github-link-hovercard__metric--deletions {
color: var(--danger);
}
.github-link-hovercard__loading,
.github-link-hovercard__unavailable {
display: flex;
align-items: center;
justify-content: center;
min-height: 80px;
color: var(--muted);
font-size: var(--control-ui-text-sm);
}
.github-link-hovercard__loading::before {
width: 18px;
height: 18px;
margin-right: 9px;
border: 2px solid color-mix(in srgb, var(--muted) 25%, transparent);
border-top-color: var(--muted);
border-radius: 50%;
content: "";
animation: github-hovercard-spin 0.8s linear infinite;
}
@keyframes github-hovercard-spin {
to {
transform: rotate(360deg);
}
}
@media (max-width: 560px) {
.github-link-hovercard {
padding: 14px;
}
.github-link-hovercard__header {
flex-wrap: wrap;
}
.github-link-hovercard__time {
margin-left: 0;
}
.github-link-hovercard__title {
margin: 13px 0 15px;
}
.github-link-hovercard__footer {
align-items: flex-start;
flex-direction: column;
gap: 10px;
}
}
@media (prefers-reduced-motion: reduce) {
.github-link-hovercard {
transition: none;
}
.github-link-hovercard__loading::before {
animation: none;
}
}
.btn--ghost {
border-color: transparent;
background: transparent;
color: var(--muted);
}
.btn--ghost:hover {
background: var(--bg-hover);
color: var(--text);
border-color: transparent;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* ===========================================
Form Fields
=========================================== */
.field {
display: grid;
gap: 6px;
}
.field.full {
grid-column: 1 / -1;
}
.field span {
color: var(--muted);
font-size: 13px;
font-weight: 500;
}
.field input,
.field textarea,
.field select,
.input {
border: 1px solid var(--input);
background: var(--card);
border-radius: var(--radius-md);
padding: 8px 12px;
outline: none;
box-shadow: inset 0 1px 0 var(--card-highlight);
transition:
border-color var(--duration-fast) ease,
box-shadow var(--duration-fast) ease;
}
.field input:not([type="checkbox"]):not([type="radio"]),
.field select,
input.input:not([type="checkbox"]):not([type="radio"]),
select.input {
height: 38px;
}
@media (hover: none) and (pointer: coarse) {
.field input,
.field textarea,
.field select,
.input {
font-size: 16px;
}
}
.field input:focus-visible,
.field textarea:focus-visible,
.field select:focus-visible,
.input:focus-visible {
border-color: var(--ring);
box-shadow: var(--focus-ring);
}
/* Carapace Sensitive Input adapted to the Control UI token contract. */
.oc-sensitive-input {
position: relative;
display: flex;
width: 100%;
min-width: 0;
min-height: 44px;
align-items: stretch;
border: 1px solid var(--input);
border-radius: var(--radius-md);
background: var(--card);
box-shadow: inset 0 1px 0 var(--card-highlight);
transition:
border-color var(--duration-fast) var(--ease-out),
box-shadow var(--duration-fast) var(--ease-out);
}
.oc-sensitive-input:hover:not(:has(input:disabled)) {
border-color: var(--border-strong);
}
.oc-sensitive-input:focus-within {
border-color: var(--ring);
box-shadow: var(--focus-ring);
}
.oc-sensitive-input > input {
width: 100%;
min-width: 0;
padding: 8px 12px;
border: 0;
border-radius: var(--radius-md) 0 0 var(--radius-md);
outline: none;
background: transparent;
color: var(--text);
box-shadow: none;
}
.oc-sensitive-input > input:disabled {
cursor: not-allowed;
opacity: 0.55;
}
.oc-sensitive-input > input::placeholder {
color: var(--muted);
}
.oc-sensitive-mask {
position: absolute;
z-index: 1;
inset: 0 44px 0 0;
display: flex;
overflow: hidden;
align-items: center;
padding: 0 12px;
color: var(--text);
font-family: var(--mono);
font-size: 13px;
letter-spacing: 0.08em;
line-height: 1;
pointer-events: none;
white-space: nowrap;
}
.oc-sensitive-mask[hidden] {
display: none;
}
.oc-sensitive-mask > span {
display: block;
will-change: transform;
}
.oc-sensitive-input[data-sensitive-mask-ready="true"][data-revealed="false"] > input {
color: transparent;
caret-color: var(--text);
}
.oc-sensitive-input[data-sensitive-mask-ready="true"][data-revealed="false"] > input::selection {
color: transparent;
}
.oc-sensitive-toggle {
position: relative;
z-index: 2;
display: inline-grid;
width: 44px;
min-width: 44px;
height: 100%;
min-height: 44px;
place-items: center;
padding: 0;
border: 0;
border-left: 1px solid var(--input);
border-radius: 0 var(--radius-md) var(--radius-md) 0;
background: transparent;
color: var(--muted);
cursor: var(--cursor-action);
}
.oc-sensitive-toggle:hover:not(:disabled) {
color: var(--text);
}
.oc-sensitive-toggle:disabled {
cursor: not-allowed;
opacity: 0.55;
}
.oc-sensitive-toggle:focus-visible {
outline: 2px solid var(--ring);
outline-offset: -3px;
}
.oc-sensitive-toggle svg {
width: 16px;
height: 16px;
}
@media (forced-colors: active) {
.oc-sensitive-mask {
display: none;
}
.oc-sensitive-input[data-sensitive-mask-ready="true"][data-revealed="false"] > input {
color: CanvasText;
}
}
.field select {
appearance: none;
padding-right: 36px;
background-image: var(--select-chevron);
background-repeat: no-repeat;
background-position: right 10px center;
}
.field textarea {
font-family: var(--mono);
min-height: 160px;
resize: vertical;
white-space: pre;
line-height: 1.5;
}
.field.checkbox {
grid-template-columns: auto 1fr;
align-items: center;
}
.config-form .field.checkbox {
grid-template-columns: 18px minmax(0, 1fr);
column-gap: 10px;
}
.config-form .field.checkbox input[type="checkbox"] {
margin: 0;
width: 16px;
height: 16px;
}
/* ===========================================
Cron Form
=========================================== */
/* Stat strip used by the tasks page. */
.summary-strip {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px 18px;
padding: 14px 16px;
}
.summary-strip__stats {
display: flex;
flex-wrap: wrap;
gap: 10px 28px;
flex: 1 1 auto;
min-width: 0;
}
.summary-stat {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
}
.summary-stat__icon {
width: 30px;
height: 30px;
color: var(--accent);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-md);
background: var(--accent-subtle);
padding: 6px;
flex-shrink: 0;
}
.summary-stat__icon svg {
width: 100%;
height: 100%;
stroke: currentColor;
fill: none;
}
.summary-stat--danger .summary-stat__icon {
color: var(--danger);
background: color-mix(in srgb, var(--danger) 12%, transparent);
}
.summary-stat__copy {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
.summary-stat__label {
color: var(--muted);
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.summary-stat__value {
color: var(--text-strong);
font-size: 14px;
font-weight: 600;
line-height: 1.3;
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
white-space: nowrap;
}
.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"],
.field select[aria-invalid="true"] {
border-color: var(--danger);
}
@media (max-width: 1100px) {
.summary-strip {
flex-direction: column;
align-items: flex-start;
}
.summary-strip__stats {
width: 100%;
gap: 8px 18px;
}
}
:root[data-theme-mode="light"] .field input,
:root[data-theme-mode="light"] .field textarea,
:root[data-theme-mode="light"] .field select {
background-color: var(--card);
border-color: var(--input);
}
:root[data-theme-mode="light"] .btn {
background: var(--bg);
border-color: var(--input);
}
:root[data-theme-mode="light"] .btn:hover {
background: var(--bg-hover);
}
:root[data-theme-mode="light"] .btn.active {
border-color: var(--accent);
background: var(--accent-subtle);
color: var(--accent);
}
:root[data-theme-mode="light"] .btn.primary {
background: var(--accent);
border-color: var(--accent);
}
:root[data-theme-mode="light"] .btn--icon {
background: var(--button-icon-bg);
border-color: var(--border);
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
color: var(--muted);
}
:root[data-theme-mode="light"] .btn--icon:hover {
background: var(--button-icon-bg);
border-color: var(--border-strong);
color: var(--text);
}
:root[data-theme-mode="light"] .chat-controls .btn--icon.active {
border-color: var(--accent);
background: var(--accent-subtle);
color: var(--accent);
box-shadow: 0 0 0 1px var(--accent-subtle);
}
/* Ghost stays chromeless in light mode too: without the border-color reset,
the higher-specificity light .btn--icon override above re-adds a 1px border
that dark mode never shows. Chrome appears on hover only. */
:root[data-theme-mode="light"] .btn--ghost {
background: transparent;
border-color: transparent;
box-shadow: none;
}
:root[data-theme-mode="light"] .btn--ghost:hover {
background: var(--bg-hover);
border-color: transparent;
}
/* ===========================================
Utilities
=========================================== */
.muted {
color: var(--muted);
}
.mono {
font-family: var(--mono);
}
/* ===========================================
Callouts - Informative with subtle depth
=========================================== */
.callout {
padding: 14px 16px;
border-radius: var(--radius-md);
background: var(--secondary);
border: 1px solid var(--border);
font-size: 13px;
line-height: 1.5;
position: relative;
}
.callout--dismissible,
.callout--action {
display: flex;
align-items: center;
gap: 10px;
}
.callout__content {
min-width: 0;
flex: 1;
}
.callout__dismiss {
width: 28px;
height: 28px;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
border: 0;
border-radius: var(--radius-sm);
background: transparent;
color: currentColor;
}
.callout__dismiss:hover {
background: rgba(255, 255, 255, 0.08);
}
.callout__dismiss svg {
display: block;
width: 16px;
height: 16px;
fill: none;
stroke: currentColor;
stroke-width: 1.8;
stroke-linecap: round;
stroke-linejoin: round;
}
.scope-upgrade-chip-row {
display: flex;
min-height: 30px;
justify-content: flex-end;
align-items: center;
}
.scope-upgrade-chip {
display: inline-flex;
align-items: center;
gap: 7px;
min-height: 30px;
padding: 5px 11px;
border: 1px solid color-mix(in srgb, var(--warn) 32%, var(--border));
border-radius: 999px;
background: color-mix(in srgb, var(--warn) 9%, var(--bg-elevated));
color: color-mix(in srgb, var(--warn) 78%, var(--text-strong));
font-size: 12px;
font-weight: 600;
}
.scope-upgrade-chip:hover {
background: color-mix(in srgb, var(--warn) 14%, var(--bg-elevated));
}
.scope-upgrade-chip:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.scope-upgrade-chip__dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: currentColor;
}
.callout.danger {
border-color: color-mix(in srgb, var(--danger) 34%, var(--border));
background: linear-gradient(
135deg,
color-mix(in srgb, var(--danger) 14%, var(--bg-elevated)) 0%,
color-mix(in srgb, var(--danger) 8%, var(--bg-elevated)) 100%
);
color: color-mix(in srgb, var(--danger) 82%, var(--text-strong));
}
.callout.warn {
border-color: color-mix(in srgb, var(--warn) 34%, var(--border));
background: linear-gradient(
135deg,
color-mix(in srgb, var(--warn) 14%, var(--bg-elevated)) 0%,
color-mix(in srgb, var(--warn) 8%, var(--bg-elevated)) 100%
);
color: color-mix(in srgb, var(--warn) 82%, var(--text-strong));
}
.callout.info {
border-color: rgba(59, 130, 246, 0.25);
background: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(59, 130, 246, 0.04) 100%);
color: var(--info);
}
.callout.success {
border-color: rgba(34, 197, 94, 0.25);
background: linear-gradient(135deg, rgba(34, 197, 94, 0.08) 0%, rgba(34, 197, 94, 0.04) 100%);
color: var(--ok);
}
/* Pending routes share the native mascot without exposing transient panel text. */
.lazy-view-state--loading {
display: grid;
flex: 1 1 auto;
place-items: center;
min-width: 0;
min-height: min(50dvh, 360px);
width: 100%;
margin: auto;
}
/* Lazy route load failure — a centered panel state, not a stretched callout. */
.lazy-view-error {
margin: auto;
display: grid;
gap: 8px;
justify-items: center;
text-align: center;
max-width: 440px;
padding: 48px 24px;
}
.lazy-view-error--inline {
margin: 16px auto;
padding: 24px;
}
.lazy-view-error__icon {
width: 44px;
height: 44px;
display: grid;
place-items: center;
border-radius: 50%;
background: var(--danger-subtle);
color: var(--danger);
margin-bottom: 4px;
}
/* A stale-chunk reload prompt is routine after an update, not a failure. */
.lazy-view-error--stale .lazy-view-error__icon {
background: var(--accent-subtle);
color: var(--accent);
}
.lazy-view-error__icon svg {
width: 22px;
height: 22px;
fill: none;
stroke: currentColor;
stroke-width: 1.8;
stroke-linecap: round;
stroke-linejoin: round;
}
.lazy-view-error__title {
font-size: 15px;
font-weight: 650;
color: var(--text-strong);
}
.lazy-view-error__subtitle {
font-size: 13px;
line-height: 1.5;
color: var(--muted);
}
.lazy-view-error__action {
margin-top: 8px;
}
.lazy-view-error__detail {
margin-top: 8px;
max-width: 100%;
color: var(--muted);
font-family: var(--mono);
font-size: 11px;
line-height: 1.5;
overflow-wrap: anywhere;
}
/* Compaction indicator */
.compaction-indicator {
align-self: center;
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
line-height: 1.2;
padding: 6px 14px;
margin-bottom: 8px;
border-radius: var(--radius-full);
border: 1px solid var(--border);
background: var(--panel-strong);
color: var(--text);
white-space: nowrap;
user-select: none;
animation: fade-in 0.2s var(--ease-out);
}
.compaction-indicator svg {
width: 16px;
height: 16px;
stroke: currentColor;
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
stroke-linejoin: round;
flex-shrink: 0;
}
.compaction-indicator--active {
color: var(--info);
border-color: rgba(59, 130, 246, 0.35);
}
.compaction-indicator--active svg {
animation: compaction-spin 1s linear infinite;
}
.compaction-indicator--complete {
color: var(--ok);
border-color: rgba(34, 197, 94, 0.35);
}
.compaction-indicator--fallback {
color: var(--warn-strong);
border-color: rgba(217, 119, 6, 0.35);
}
.compaction-indicator--fallback-cleared {
color: var(--ok);
border-color: rgba(34, 197, 94, 0.35);
}
@keyframes compaction-spin {
to {
transform: rotate(360deg);
}
}
openclaw-chat-session-rail {
display: contents;
}
.chat-session-rail {
position: absolute;
top: 12px;
right: 12px;
z-index: 30;
border: 1px solid var(--border);
background: color-mix(in srgb, var(--card) 96%, transparent);
box-shadow: var(--shadow-lg);
animation: fade-in 0.2s var(--ease-out);
backdrop-filter: blur(14px);
}
.chat-session-rail--pill {
display: flex;
align-items: center;
gap: 5px;
width: min(390px, calc(100% - 24px));
min-height: 40px;
padding: 4px 6px 4px 10px;
border-radius: 999px;
color: var(--text);
}
.chat-session-rail--pill:hover {
border-color: var(--border-strong);
background: var(--card);
}
/* Narrow panes cannot hold two columns, so the companion becomes a full-height
sheet on the pane's trailing edge. It still covers the thread, but it reads as
a surface that took the pane over rather than a card floating above it — the
docked column below is the shape this degrades from. The shadow and blur the
base rule gives the pill are card cues, so the sheet drops them too. */
.chat-session-rail--expanded {
display: flex;
top: 0;
right: 0;
bottom: 0;
flex-direction: column;
width: min(400px, 100%);
overflow: hidden;
border-width: 0 0 0 1px;
border-radius: 0;
box-shadow: none;
backdrop-filter: none;
animation: none;
}
.chat-session-rail--embedded {
position: relative;
inset: auto;
z-index: auto;
width: 100%;
height: 100%;
border: 0;
background: transparent;
}
.chat-session-rail__header {
display: flex;
flex: 0 0 auto;
}
.chat-session-rail__header-copy {
min-width: 0;
flex: 1 1 auto;
}
.chat-session-rail__status-row,
.chat-session-rail__status,
.chat-session-rail__actions,
.chat-session-rail__prs,
.chat-session-rail__pr {
display: flex;
align-items: center;
}
.chat-session-rail__status-row {
gap: 8px;
min-width: 0;
min-height: 18px;
color: var(--muted);
font-size: 11px;
}
.chat-session-rail__status {
--rail-health-color: var(--muted);
gap: 5px;
/* Never shrink: the chip has no overflow clipping, so a squeezed box paints
its label over the pill headline next to it. */
flex: 0 0 auto;
color: var(--rail-health-color);
font-size: 11px;
font-weight: 650;
line-height: 1;
}
.chat-session-rail__status--critical {
padding: 4px 7px;
border-radius: 999px;
background: color-mix(in srgb, var(--rail-health-color) 14%, transparent);
font-size: 10px;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.chat-session-rail__status-dot {
width: 7px;
height: 7px;
flex: 0 0 auto;
border-radius: 50%;
background: var(--rail-health-color);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--rail-health-color) 13%, transparent);
}
.chat-session-rail__status-icon {
display: inline-flex;
width: 13px;
height: 13px;
flex: 0 0 auto;
}
.chat-session-rail__status-icon svg {
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 2;
}
.chat-session-rail__status[data-health="on-track"],
.chat-session-rail__status[data-health="done"],
.chat-session-rail__status[data-health="wrapping-up"] {
--rail-health-color: var(--ok);
}
.chat-session-rail__status[data-health="grinding"] {
--rail-health-color: var(--accent);
}
.chat-session-rail__status[data-health="stuck"],
.chat-session-rail__status[data-health="waiting-on-user"] {
--rail-health-color: var(--warn);
}
.chat-session-rail__status[data-health="failed"] {
--rail-health-color: var(--danger);
}
.chat-session-rail__timing {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-session-rail__headline {
/* Block, not inline: ellipsis needs a block box inside the pill's expand
button, or the text hard-clips at the button edge. */
display: block;
min-width: 0;
overflow: hidden;
color: var(--text);
font-size: 12.5px;
font-weight: 650;
line-height: 1.35;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-session-rail__subtitle {
color: var(--muted);
font-size: 11.5px;
}
.chat-session-rail__expand {
min-width: 0;
flex: 1 1 auto;
padding: 4px;
overflow: hidden;
border: 0;
background: transparent;
color: inherit;
font: inherit;
text-align: left;
cursor: var(--cursor-action);
}
.chat-session-rail__hide,
.chat-session-rail__toggle {
color: var(--muted);
}
.chat-session-rail__actions {
gap: 2px;
flex: 0 0 auto;
}
.chat-session-rail__digest {
flex: 0 0 auto;
padding: 0 14px;
}
.chat-session-rail__assessment {
margin: 11px 0 0;
color: var(--text);
font-size: 12.5px;
line-height: 1.45;
}
.chat-session-rail__prs {
flex-wrap: wrap;
gap: 6px;
margin-top: 11px;
}
.chat-session-rail__pr {
gap: 5px;
padding: 4px 7px;
border: 1px solid var(--border);
border-radius: 999px;
color: var(--text);
font-size: 10.5px;
line-height: 1;
text-decoration: none;
}
.chat-session-rail__pr:hover {
border-color: var(--border-strong);
color: var(--text);
}
.chat-session-rail__pr-checks,
.chat-session-rail__timestamp,
.chat-session-rail__hint {
color: var(--muted);
}
.chat-session-rail__thread {
display: flex;
flex: 1 1 auto;
min-height: 96px;
flex-direction: column;
gap: 14px;
margin-top: 12px;
padding: 12px 14px 16px;
overflow-y: auto;
border-top: 1px solid var(--border);
overscroll-behavior-y: contain;
}
/* No digest means no digest block, so the thread would otherwise stack its own
border directly under the header's and open with a hanging 12px gap. */
.chat-session-rail__header + .chat-session-rail__thread {
margin-top: 0;
border-top: 0;
}
/* Openers stack against the composer they feed, so the eye travels chips ->
input without crossing the thread. Left-aligned: they are actions, not
content, and centring them would read as a second empty state. */
.chat-session-rail__starters {
display: flex;
flex: 0 0 auto;
flex-direction: column;
align-items: flex-start;
gap: 6px;
padding: 0 14px 10px;
}
.chat-session-rail__starter {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 10px;
color: var(--text);
}
/* strokeIcon already inlines fill/stroke/stroke-width on the svg. */
.chat-session-rail__starter svg {
width: 12px;
height: 12px;
flex: 0 0 auto;
color: var(--muted);
}
.chat-session-rail__starter:disabled {
cursor: not-allowed;
opacity: 0.5;
}
.chat-session-rail__starter:disabled:hover {
border-color: var(--border);
transform: none;
}
.chat-session-rail__exchange {
display: grid;
gap: 6px;
font-size: 12.5px;
line-height: 1.5;
}
.chat-session-rail__exchange + .chat-session-rail__exchange {
padding-top: 13px;
border-top: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
}
.chat-session-rail__question {
justify-self: end;
max-width: 90%;
padding: 6px 9px;
border-radius: var(--radius-md) var(--radius-md) 3px var(--radius-md);
background: color-mix(in srgb, var(--accent) 12%, transparent);
color: var(--text);
font-weight: 600;
overflow-wrap: anywhere;
}
.chat-session-rail__answer {
color: var(--text);
}
.chat-session-rail__answer > :first-child {
margin-top: 0;
}
.chat-session-rail__answer > :last-child {
margin-bottom: 0;
}
.chat-session-rail__timestamp {
font-size: 10.5px;
}
.chat-session-rail__hint {
font-size: 12px;
}
.chat-session-rail__exchange--error .chat-session-rail__hint {
color: var(--danger);
}
.chat-session-rail__retry {
justify-self: start;
}
.chat-session-rail__exchange--pending .chat-session-rail__hint {
animation: chat-session-rail-pending-pulse 1.4s ease-in-out infinite;
}
@keyframes chat-session-rail-pending-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.45;
}
}
.chat-session-rail__composer {
display: flex;
align-items: center;
gap: 6px;
flex: 0 0 auto;
padding: 10px 10px 11px 14px;
border-top: 1px solid var(--border);
background: color-mix(in srgb, var(--secondary) 40%, transparent);
}
.chat-session-rail__prompt {
min-width: 0;
flex: 1 1 auto;
}
.chat-session-rail__input {
width: 100%;
min-height: 34px;
padding: 7px 10px;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: var(--card);
color: var(--text);
font: inherit;
font-size: 12.5px;
}
.chat-session-rail__input:focus {
border-color: var(--border-strong);
outline: none;
}
.chat-session-rail__input::placeholder {
color: var(--muted);
}
/* The docked form: a real column in the pane's flex row. It claims layout space
instead of painting over the thread, so nothing here may float, shadow, or
blur — those cues would read as an overlay again. */
.chat-main--rail-docked .chat-session-rail--expanded {
position: static;
width: 100%;
height: 100%;
border-left-width: 0;
}
@media (max-width: 768px) {
/* The ambient pill stays a small chip above the composer. */
.chat-session-rail--pill {
position: fixed;
top: auto;
left: max(8px, var(--safe-area-left));
right: max(8px, var(--safe-area-right));
bottom: calc(108px + var(--safe-area-bottom));
z-index: 35;
width: auto;
}
/* No pane can hold a column at this width, so the companion takes the whole
surface rather than hovering over part of it. */
.chat-session-rail--expanded:not(.chat-session-rail--embedded) {
position: fixed;
z-index: 35;
inset: 0;
width: 100%;
padding-bottom: var(--safe-area-bottom);
border-width: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.chat-session-rail,
.chat-session-rail__exchange--pending .chat-session-rail__hint {
animation: none;
}
}
/* ===========================================
Code Blocks
=========================================== */
.code-block {
font-family: var(--mono);
font-size: 13px;
line-height: 1.5;
background: var(--secondary);
padding: 12px;
border-radius: var(--radius-md);
border: 1px solid var(--border);
max-height: 360px;
overflow: auto;
max-width: 100%;
}
:root[data-theme-mode="light"] .code-block,
:root[data-theme-mode="light"] .list-item,
:root[data-theme-mode="light"] .chip {
background: var(--bg);
}
:is(.code-block .hljs, .code-block-wrapper pre code.hljs) {
color: var(--text);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-comment,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-quote {
color: var(--muted);
font-style: italic;
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-keyword,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-selector-tag,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-subst {
color: var(--hljs-keyword);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-number,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-literal,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-variable,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-template-variable,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-tag .hljs-attr {
color: var(--hljs-number);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-string,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-doctag {
color: var(--hljs-string);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-title,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-section,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-selector-id,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-function .hljs-title {
color: var(--hljs-title);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-type,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-class .hljs-title,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-built_in,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-builtin-name {
color: var(--hljs-type);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-attr,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-attribute,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-name,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-selector-class,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-selector-attr,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-selector-pseudo {
color: var(--hljs-attribute);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-symbol,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-bullet,
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-link {
color: var(--accent-2);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-meta {
color: var(--hljs-meta);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-deletion {
color: var(--hljs-deletion);
background: rgba(248, 81, 73, 0.12);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-addition {
color: var(--hljs-addition);
background: rgba(46, 160, 67, 0.12);
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-emphasis {
font-style: italic;
}
:is(.code-block, .code-block-wrapper pre code.hljs) .hljs-strong {
font-weight: 600;
}
.markdown-plain-text-fallback {
display: block;
white-space: pre-wrap;
overflow-wrap: anywhere;
word-break: break-word;
font: inherit;
}
/* Code block wrapper chrome (generated by markdown renderer) */
.code-block-wrapper {
position: relative;
box-sizing: border-box;
width: 100%;
margin-block: 1em;
overflow: hidden;
border: 1px solid color-mix(in srgb, var(--text) 22%, transparent);
border-radius: var(--radius-md);
background: transparent;
}
.code-block-wrapper pre {
margin: 0 6px 6px;
padding: 4px 8px 8px;
border: 0;
border-radius: 0;
background: transparent;
overflow-x: auto;
}
.code-block-wrapper :where(pre code.markdown-block-art) {
display: block;
min-width: 100%;
width: max-content;
color: var(--text);
font-family: var(--mono);
font-size: 12px;
font-variant-ligatures: none;
letter-spacing: 0;
line-height: 0.86;
overflow-wrap: normal;
white-space: pre;
word-break: normal;
}
.code-block-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
min-height: 36px;
padding: 8px 8px 4px 14px;
background: transparent;
font-size: 11px;
line-height: 1;
}
.code-block-actions {
display: flex;
align-items: center;
justify-content: flex-end;
min-width: 32px;
min-height: 32px;
}
.code-block-lang {
color: var(--muted);
font-family: var(--mono);
font-size: 11px;
text-transform: lowercase;
user-select: none;
}
.code-block-copy {
appearance: none;
border: none;
background: transparent;
color: var(--muted);
width: 32px;
height: 32px;
padding: 7px;
border-radius: var(--radius-sm);
transition:
color 150ms ease,
background 150ms ease;
}
.code-block-copy__idle {
display: block;
width: 16px;
height: 16px;
background: currentColor;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='14' height='14' x='8' y='8' rx='2'/%3E%3Cpath d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2'/%3E%3C/svg%3E")
center / contain no-repeat;
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='14' height='14' x='8' y='8' rx='2'/%3E%3Cpath d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2'/%3E%3C/svg%3E")
center / contain no-repeat;
}
.code-block-copy__done {
background: currentColor;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m20 6-11 11-5-5'/%3E%3C/svg%3E")
center / contain no-repeat;
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m20 6-11 11-5-5'/%3E%3C/svg%3E")
center / contain no-repeat;
}
.code-block-copy:hover {
color: var(--text);
background: rgba(255, 255, 255, 0.1);
}
:root[data-theme-mode="light"] .code-block-copy:hover {
background: rgba(0, 0, 0, 0.08);
}
.code-block-copy__done,
.code-block-copy__failed {
display: none;
}
.code-block-copy:is(.copied, .copy-failed) .code-block-copy__idle {
display: none;
}
.code-block-copy.copied .code-block-copy__done,
.code-block-copy.copy-failed .code-block-copy__failed {
display: grid;
place-items: center;
width: 16px;
height: 16px;
font-size: 13px;
}
.code-block-copy.copied .code-block-copy__done {
color: inherit;
}
.code-block-copy.copy-failed .code-block-copy__failed {
color: var(--danger);
}
/* ===========================================
Lists
=========================================== */
.list {
display: grid;
gap: 8px;
container-type: inline-size;
}
.list-item {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(200px, 260px);
gap: 16px;
align-items: start;
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 12px;
background: var(--card);
transition: border-color var(--duration-fast) ease;
}
.list-item-clickable {
text-decoration: none;
}
.list-item-clickable:hover {
border-color: var(--border-strong);
}
.list-main {
display: grid;
gap: 4px;
min-width: 0;
}
.list-title {
font-weight: 500;
}
.list-sub {
color: var(--muted);
font-size: 12px;
}
.list-meta {
text-align: right;
color: var(--muted);
font-size: 12px;
display: grid;
gap: 4px;
min-width: 200px;
}
.list-meta .btn {
padding: 6px 10px;
}
.list-meta .field input,
.list-meta .field textarea,
.list-meta .field select {
width: 100%;
}
@container (max-width: 560px) {
.list-item {
grid-template-columns: 1fr;
}
.list-meta {
min-width: 0;
text-align: left;
}
}
/* ===========================================
Chips - Compact and punchy
=========================================== */
.chip-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.chip {
font-size: 12px;
font-weight: 500;
border: 1px solid var(--border);
border-radius: var(--radius-full);
padding: 5px 12px;
color: var(--muted);
background: var(--secondary);
transition:
border-color var(--duration-fast) var(--ease-out),
background var(--duration-fast) var(--ease-out),
transform var(--duration-fast) var(--ease-out);
}
.chip:hover {
border-color: var(--border-strong);
transform: translateY(-1px);
}
.chip input {
margin-right: 6px;
}
.chip-ok {
color: var(--ok);
border-color: rgba(34, 197, 94, 0.3);
background: var(--ok-subtle);
}
.chip-warn {
color: var(--warn);
border-color: rgba(245, 158, 11, 0.3);
background: var(--warn-subtle);
}
.chip-danger {
color: var(--danger);
border-color: rgba(239, 68, 68, 0.3);
background: var(--danger-subtle);
}
/* ===========================================
Data Table
=========================================== */
.data-table-search {
flex: 1;
min-width: 0;
}
.data-table-search input {
width: 100%;
padding: 6px 10px;
font-size: 13px;
color: var(--text);
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
outline: none;
transition: border-color var(--duration-fast) ease;
}
.data-table-search input:focus-visible {
border-color: var(--border-strong);
box-shadow: var(--focus-ring);
}
.data-table-search input::placeholder {
color: var(--muted);
}
.data-table-container {
overflow: auto;
max-height: min(70vh, 860px);
overscroll-behavior: contain;
scrollbar-gutter: stable both-edges;
-webkit-overflow-scrolling: touch;
}
.data-table {
--data-table-checkbox-col-width: 36px;
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: 13px;
}
.data-table th {
position: sticky;
top: 0;
z-index: 3;
padding: 10px 12px;
text-align: left;
font-weight: 600;
font-size: 12px;
color: var(--muted);
background: var(--bg-elevated);
border-bottom: 1px solid var(--border);
white-space: nowrap;
user-select: none;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.data-table th[data-sortable] {
transition: color var(--duration-fast) ease;
}
.data-table th[data-sortable]:hover {
color: var(--text);
}
.data-table-sort-button {
padding: 0;
border: 0;
background: transparent;
letter-spacing: inherit;
text-transform: inherit;
}
.data-table-sort-icon {
display: inline-flex;
vertical-align: middle;
margin-left: 4px;
opacity: 0.4;
transition: opacity var(--duration-fast) ease;
}
.data-table-sort-icon svg {
width: 14px;
height: 14px;
stroke: currentColor;
fill: none;
stroke-width: 1.5px;
}
.data-table th[data-sortable]:hover .data-table-sort-icon {
opacity: 0.7;
}
.data-table th[data-sort-dir="asc"] .data-table-sort-icon,
.data-table th[data-sort-dir="desc"] .data-table-sort-icon {
opacity: 1;
color: var(--text);
}
.data-table th[data-sort-dir="desc"] .data-table-sort-icon svg {
transform: rotate(180deg);
}
.data-table td {
padding: 10px 12px;
border-bottom: 1px solid var(--border);
color: var(--text);
vertical-align: middle;
}
.data-table-empty-cell {
padding: 46px 16px !important;
color: var(--muted);
text-align: center;
}
.data-table-empty-state {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
}
.data-table tbody tr {
transition: background var(--duration-fast) ease;
}
.data-table tbody tr:hover {
background: var(--bg-hover);
}
.data-table tbody tr:last-child td {
border-bottom: none;
}
/* Checkbox column */
.data-table-checkbox-col {
width: var(--data-table-checkbox-col-width);
min-width: var(--data-table-checkbox-col-width);
max-width: var(--data-table-checkbox-col-width);
box-sizing: border-box;
padding-left: 12px !important;
padding-right: 4px !important;
text-align: center;
vertical-align: middle;
position: sticky;
left: 0;
z-index: 2;
}
.data-table th.data-table-checkbox-col {
z-index: 4;
background: var(--bg-elevated);
}
td.data-table-checkbox-col {
background: var(--card);
}
.data-table tbody tr:hover td.data-table-checkbox-col {
background: var(--bg-hover);
}
.data-table-checkbox-col input[type="checkbox"] {
width: 16px;
height: 16px;
margin: 0;
vertical-align: middle;
}
/* Sticky Key column (second column, after checkbox) */
.data-table .data-table-key-col {
position: sticky;
left: var(--data-table-checkbox-col-width);
z-index: 2;
min-width: 180px;
}
.data-table th.data-table-key-col {
z-index: 4;
background: var(--bg-elevated);
}
td.data-table-key-col {
background: var(--card);
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.08);
}
.data-table tbody tr:hover td.data-table-key-col {
background: var(--bg-hover);
}
/* Bulk action bar */
.data-table-bulk-bar {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 12px;
border-bottom: 1px solid var(--border);
background: var(--accent-subtle);
font-size: 13px;
font-weight: 500;
color: var(--text);
}
.data-table-bulk-bar .btn svg {
width: 14px;
height: 14px;
}
/* Pagination */
.data-table-pagination {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
border-top: 1px solid var(--border);
background: var(--bg-elevated);
font-size: 13px;
color: var(--muted);
}
.data-table-pagination__controls {
display: flex;
align-items: center;
gap: 8px;
}
.data-table-pagination__controls button {
padding: 4px 12px;
font-size: 13px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--card);
color: var(--text);
transition:
background var(--duration-fast) ease,
border-color var(--duration-fast) ease;
}
.data-table-pagination__controls button:hover:not(:disabled) {
background: var(--bg-hover);
border-color: var(--border-strong);
}
.data-table-pagination__controls button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
/* ===========================================
Log Stream
=========================================== */
.log-stream {
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--card);
max-height: calc(100vh - 280px);
min-height: 200px;
overflow: auto;
container-type: inline-size;
}
.log-row {
display: grid;
grid-template-columns: 90px 70px minmax(140px, 200px) minmax(0, 1fr);
gap: 12px;
align-items: start;
padding: 8px 12px;
border-bottom: 1px solid var(--border);
font-size: 12px;
transition: background var(--duration-fast) ease;
}
.log-row:hover {
background: var(--bg-hover);
}
.log-row:last-child {
border-bottom: none;
}
.log-time {
color: var(--muted);
font-family: var(--mono);
}
.log-level {
font-size: 12px; /* was 11px */
font-weight: 500;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 2px 6px;
width: fit-content;
}
.log-level.trace,
.log-level.debug {
color: var(--muted);
}
.log-level.info {
color: var(--info);
border-color: rgba(59, 130, 246, 0.3);
}
.log-level.warn {
color: var(--warn);
border-color: var(--warn-subtle);
}
.log-level.error,
.log-level.fatal {
color: var(--danger);
border-color: var(--danger-subtle);
}
.log-chip.trace,
.log-chip.debug {
color: var(--muted);
}
.log-chip.info {
color: var(--info);
border-color: rgba(59, 130, 246, 0.3);
}
.log-chip.warn {
color: var(--warn);
border-color: var(--warn-subtle);
}
.log-chip.error,
.log-chip.fatal {
color: var(--danger);
border-color: var(--danger-subtle);
}
.log-subsystem {
color: var(--muted);
font-family: var(--mono);
}
.log-message {
white-space: pre-wrap;
word-break: break-word;
font-family: var(--mono);
}
@container (max-width: 620px) {
.log-row {
grid-template-columns: 70px 60px minmax(0, 1fr);
}
.log-subsystem {
display: none;
}
}
/* ===========================================
Chat
=========================================== */
.shell--chat .chat {
flex: 1;
}
/* Chat queue: compact pending rows aligned with the composer shell width. */
.chat-queue {
display: flex;
flex-direction: column;
gap: 4px;
width: calc(100% - 36px);
max-width: 48rem;
margin: 8px auto 0;
}
.chat-queue__item {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 2px 8px;
min-height: 30px;
padding: 3px 5px 3px 10px;
border-radius: var(--radius-md);
background: color-mix(in srgb, var(--text) 5%, transparent);
color: var(--muted);
}
/* The edited row keeps its slot so the operator can see where the corrected
message lands; the outline says the composer owns it right now.
Editing is an active state, not a failure, so it uses the informational
family: --accent is red in every shipped theme and sits on top of --danger
(see the --run token note in base.css), which reads as an error at a glance. */
.chat-queue__item--editing {
background: var(--info-subtle);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--info) 34%, transparent);
}
/* Drop target reads as an insertion edge rather than a selected row, so the
dragged message clearly lands at this position. */
.chat-queue__item--drop-target {
box-shadow: inset 0 2px 0 0 var(--accent);
}
/* The handle owns reordering for both input methods: drag it, or focus it and
press the arrow keys. It stays a real button so keyboards can reach it. */
.chat-queue__grip {
display: inline-flex;
align-items: center;
flex-shrink: 0;
width: 12px;
height: 16px;
margin-left: -5px;
padding: 0;
border: none;
border-radius: var(--radius-sm);
background: none;
color: color-mix(in srgb, var(--muted) 55%, transparent);
cursor: grab;
}
.chat-queue__item:hover .chat-queue__grip:not(:disabled),
.chat-queue__grip:focus-visible {
color: var(--muted);
}
.chat-queue__grip:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 1px;
}
/* Still occupies the handle column so every row's pill and text share one x,
and stays visible but dimmer than a live handle: this row is holding its
place, not offering a drag. Hover leaves it alone for the same reason. */
.chat-queue__grip:disabled {
color: color-mix(in srgb, var(--muted) 34%, transparent);
cursor: default;
}
.chat-queue__grip svg {
width: 12px;
height: 14px;
}
.chat-queue__item--failed {
background: color-mix(in srgb, var(--danger) 8%, transparent);
}
.chat-queue__icon {
display: inline-flex;
flex-shrink: 0;
width: 14px;
height: 14px;
color: var(--muted);
}
.chat-queue__icon svg {
width: 14px;
height: 14px;
fill: none;
stroke: currentColor;
stroke-width: 1.75px;
stroke-linecap: round;
stroke-linejoin: round;
}
.chat-queue__item--failed .chat-queue__icon {
color: var(--danger);
}
/* Reconnect rows mirror the connection pill's amber status dot so the queue
reads as quiet auto-retry status, not a failure needing action. */
.chat-queue__dot {
flex-shrink: 0;
width: 7px;
height: 7px;
margin: 0 3.5px;
border-radius: var(--radius-full);
background: var(--warn);
box-shadow: 0 0 8px color-mix(in srgb, var(--warn) 55%, transparent);
animation: pulse-subtle 2s ease-in-out infinite;
}
.chat-queue__item--reconnect .chat-queue__badge {
background: color-mix(in srgb, var(--warn) 14%, transparent);
color: color-mix(in srgb, var(--warn) 78%, var(--text-strong));
}
.chat-queue__badge {
flex-shrink: 0;
padding: 1px 7px;
border-radius: 999px;
background: color-mix(in srgb, var(--text) 12%, transparent);
color: var(--muted);
font-size: 11px;
font-weight: 600;
line-height: 1.5;
white-space: nowrap;
}
.chat-queue__item--failed .chat-queue__badge {
background: color-mix(in srgb, var(--danger) 12%, transparent);
color: var(--danger);
}
/* Last of the state badges on purpose: editing outranks the send state it
pauses, so a reconnecting or failed row still reads as informational blue. */
.chat-queue__item--editing .chat-queue__badge {
background: color-mix(in srgb, var(--info) 16%, transparent);
color: var(--info);
}
.chat-queue__text {
flex: 1 1 0;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--chat-text);
font-size: 13px;
line-height: 1.4;
}
.chat-queue__edit-input {
flex: 1 1 0;
min-width: 0;
min-height: 26px;
margin: -1px 0;
padding: 4px 6px;
border: 1px solid color-mix(in srgb, var(--info) 42%, transparent);
border-radius: var(--radius-sm);
background: var(--bg);
color: var(--chat-text);
font: inherit;
font-size: 13px;
line-height: 1.4;
resize: vertical;
}
.chat-queue__edit-input:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 1px;
}
/* Wraps onto its own flex line below the row (see renderChatQueueItem). */
.chat-queue__error {
flex-basis: 100%;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 0 3px 22px;
color: var(--danger);
font-size: 11.5px;
line-height: 1.35;
}
.chat-queue__actions {
display: inline-flex;
align-items: center;
gap: 2px;
flex-shrink: 0;
margin-left: auto;
}
.chat-queue__action {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
border: none;
border-radius: var(--radius-sm);
background: none;
color: var(--accent);
font-size: 11.5px;
font-weight: 600;
line-height: 1;
cursor: var(--cursor-action);
}
.chat-queue__action svg {
width: 12px;
height: 12px;
fill: none;
stroke: currentColor;
stroke-width: 1.75px;
stroke-linecap: round;
stroke-linejoin: round;
}
.chat-queue__action:hover:not(:disabled) {
background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.chat-queue__edit,
.chat-queue__remove,
.chat-queue__edit-submit,
.chat-queue__edit-cancel {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
padding: 0;
border: none;
border-radius: var(--radius-sm);
background: none;
color: var(--muted);
cursor: var(--cursor-action);
}
/* Inert instead of removed: an open edit keeps every action slot in place so the
rows below it never shift while the operator retypes. */
.chat-queue__edit:disabled,
.chat-queue__remove:disabled,
.chat-queue__edit-submit:disabled,
.chat-queue__edit-cancel:disabled {
color: color-mix(in srgb, var(--muted) 34%, transparent);
cursor: default;
}
.chat-queue__edit:hover:not(:disabled),
.chat-queue__remove:hover:not(:disabled),
.chat-queue__edit-submit:hover:not(:disabled),
.chat-queue__edit-cancel:hover:not(:disabled) {
color: var(--text);
background: color-mix(in srgb, var(--text) 10%, transparent);
}
.chat-queue__edit svg,
.chat-queue__remove svg,
.chat-queue__edit-submit svg,
.chat-queue__edit-cancel svg {
width: 13px;
height: 13px;
fill: none;
stroke: currentColor;
stroke-width: 2px;
stroke-linecap: round;
stroke-linejoin: round;
}
/* Chat lines */
.chat-line {
display: flex;
}
.chat-line.user {
justify-content: flex-end;
}
.chat-line.assistant,
.chat-line.other {
justify-content: flex-start;
}
.chat-msg {
display: grid;
gap: 6px;
max-width: min(700px, 82%);
}
.chat-line.user .chat-msg {
justify-items: end;
}
/* Chat bubbles: canonical styles live in chat/grouped.css; the .chat-line
variants below only skin the loading-skeleton preview bubbles. */
.chat-line.user .chat-bubble {
border-color: transparent;
background: var(--accent-subtle);
}
:root[data-theme-mode="light"] .chat-line.user .chat-bubble {
border-color: transparent;
background: var(--accent-subtle);
box-shadow: none;
}
.chat-line.assistant .chat-bubble {
border-color: transparent;
background: var(--secondary);
}
:root[data-theme-mode="light"] .chat-line.assistant .chat-bubble {
border-color: var(--border);
background: var(--bg-muted);
}
@keyframes chatStreamPulse {
0%,
100% {
border-color: var(--border);
}
50% {
border-color: var(--accent);
}
}
.chat-bubble.streaming {
animation: chatStreamPulse 1.5s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
.chat-bubble.streaming {
animation: none;
border-color: var(--accent);
}
}
/* Reading indicator bubble sizing; dot styles live in chat/tool-cards.css. */
.chat-bubble.chat-reading-indicator {
width: fit-content;
padding: 10px 16px;
}
/* ===========================================
QR Code
=========================================== */
.qr-wrap {
margin-top: 16px;
border-radius: var(--radius-md);
background: var(--card);
border: 1px dashed var(--border-strong);
padding: 16px;
display: inline-flex;
}
.qr-wrap img {
width: 160px;
height: 160px;
border-radius: var(--radius-sm);
image-rendering: pixelated;
}
/* ===========================================
Exec Approval Modal
=========================================== */
.exec-approval-card {
--exec-approval-accent: var(--border);
width: 100%;
max-height: inherit;
display: flex;
flex-direction: column;
background: color-mix(in srgb, var(--exec-approval-accent) 5%, var(--card));
border: 1px solid color-mix(in srgb, var(--exec-approval-accent) 45%, var(--border));
border-radius: var(--radius-lg);
padding: 20px;
animation: scale-in 0.2s var(--ease-out);
}
.exec-approval-modal-stack {
display: flex;
flex-direction: column;
gap: 10px;
max-height: calc(100dvh - 48px);
overflow-y: auto;
}
.exec-approval-card--inline {
padding: 14px 16px;
animation: none;
}
.exec-approval-card--severity-info {
--exec-approval-accent: var(--border);
}
.exec-approval-card--severity-warning {
--exec-approval-accent: var(--warn);
}
.exec-approval-card--severity-danger {
--exec-approval-accent: var(--danger);
}
.exec-approval-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.exec-approval-title {
font-size: 14px;
font-weight: 600;
}
.exec-approval-sub {
color: var(--muted);
font-size: 13px;
margin-top: 4px;
}
.exec-approval-queue,
.exec-approval-chip {
font-size: 12px; /* was 11px */
font-weight: 500;
color: var(--muted);
border: 1px solid var(--border);
border-radius: var(--radius-full);
padding: 4px 10px;
}
.exec-approval-chips {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 6px;
}
.exec-approval-chip {
padding: 2px 8px;
}
.exec-approval-command {
margin-top: 12px;
max-height: min(40dvh, 320px);
padding: 10px 12px;
background: var(--secondary);
border: 1px solid var(--border);
border-radius: var(--radius-md);
overflow-y: auto;
word-break: break-word;
white-space: pre-wrap;
font-family: var(--mono);
font-size: 13px;
}
.exec-approval-command-span {
border-radius: 3px;
padding: 0 2px;
background: color-mix(in srgb, var(--exec-highlight) 34%, transparent);
color: var(--text);
}
.exec-approval-meta {
margin-top: 12px;
display: grid;
gap: 6px;
font-size: 13px;
color: var(--muted);
}
.exec-approval-meta-row {
display: flex;
justify-content: space-between;
gap: 12px;
}
.exec-approval-meta-row span:last-child {
min-width: 0;
color: var(--text);
font-family: var(--mono);
overflow-wrap: anywhere;
text-align: right;
}
.exec-approval-details {
margin-top: 10px;
color: var(--muted);
font-size: 12px;
}
.exec-approval-details summary {
width: fit-content;
cursor: var(--cursor-action);
}
.exec-approval-details .exec-approval-meta {
margin-top: 8px;
}
.exec-approval-error {
margin-top: 10px;
font-size: 13px;
color: var(--danger);
}
.input-dialog__field {
margin-top: 14px;
}
.exec-approval-warning {
margin-top: 10px;
font-size: 13px;
color: var(--warn);
}
/* The confirm opt-out is a control, not a continuation of the message copy, so it
takes the same 12px separation the details block uses and keeps the 16px action
gap below it. Without the margin it reads as a third line of the paragraph. */
.exec-approval-skip {
margin-top: 12px;
column-gap: 10px;
font-size: 13px;
cursor: var(--cursor-action);
}
.exec-approval-skip input[type="checkbox"] {
margin: 0;
width: 16px;
height: 16px;
}
.exec-approval-actions {
margin-top: 16px;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.exec-approval-actions .btn {
display: inline-flex;
align-items: center;
gap: 8px;
}
.exec-approval-list {
display: grid;
gap: 4px;
padding: 10px;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: var(--card);
}
.exec-approval-list__heading {
padding: 2px 4px 6px;
color: var(--muted);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.exec-approval-list__item {
display: grid;
grid-template-columns: minmax(64px, 0.25fr) minmax(0, 1fr) auto;
align-items: center;
gap: 10px;
width: 100%;
padding: 7px 8px;
border: 1px solid transparent;
border-radius: var(--radius-md);
background: transparent;
color: var(--text);
text-align: left;
cursor: var(--cursor-action);
}
.exec-approval-list__item:hover,
.exec-approval-list__item:focus-visible {
border-color: var(--border);
background: var(--secondary);
}
.exec-approval-list__item:focus-visible {
outline: 2px solid color-mix(in srgb, var(--accent) 45%, transparent);
outline-offset: 1px;
}
.exec-approval-list__agent,
.exec-approval-list__command {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.exec-approval-list__agent {
color: var(--muted);
font-size: 12px;
}
.exec-approval-list__command {
font-size: 12px;
}
.exec-approval-list__expiry {
color: var(--warn);
font: 11px/1.2 var(--mono);
font-variant-numeric: tabular-nums;
}
/* ===========================================
Secret Reveal Modal
=========================================== */
.secret-reveal__value {
display: flex;
align-items: flex-start;
gap: 8px;
margin-top: 12px;
padding: 10px 12px;
background: var(--secondary);
border: 1px solid var(--border);
border-radius: var(--radius-md);
}
/* The value is shown once, so it wraps in full instead of truncating, and a single
tap or click selects all of it for operators without a working clipboard button. */
.secret-reveal__code {
flex: 1;
min-width: 0;
font-family: var(--mono);
font-size: 13px;
line-height: 1.5;
overflow-wrap: anywhere;
user-select: all;
}
.secret-reveal__hint {
margin: 10px 0 0;
color: var(--warn);
font-size: 12px;
}
.secret-reveal__header {
display: flex;
align-items: center;
gap: 12px;
}
/* Same tile geometry as .summary-stat__icon, in the ok tone: the outcome is settled,
and the mark says so before any sentence is read. */
.secret-reveal__status {
width: 28px;
height: 28px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 6px;
border-radius: var(--radius-md);
color: var(--ok);
background: color-mix(in srgb, var(--ok) 12%, transparent);
}
.secret-reveal__status svg {
width: 100%;
height: 100%;
}
/* The reassurance carries full text weight, unlike the muted .exec-approval-sub it
replaced; this is the zone that should read as settled rather than incidental. */
.secret-reveal__body {
margin-top: 16px;
color: var(--text);
font-size: 13px;
line-height: 1.6;
}
.secret-reveal__body p {
margin: 0;
}
.secret-reveal__callout {
margin-top: 12px;
text-wrap: balance;
}
/* .btn's resting border (--border) sits within ~4/255 of --card in dark
(#1e2028 on #161920), so a neutral button reads as unpainted on this surface.
--border-strong is the token that separates from a card in both themes; the
inherited .btn:hover still supplies the hover surface. */
.secret-reveal__dismiss {
border-color: var(--border-strong);
}
/* Trailing rationale: readable, but never competing with the outcome above it. */
.secret-reveal__note {
margin: 16px 0 0;
color: var(--muted);
font-size: 12px;
line-height: 1.5;
}
/* ===========================================
Agents
=========================================== */
.agents-layout {
display: grid;
grid-template-columns: 1fr;
gap: 14px;
}
.agents-toolbar {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.agents-toolbar-row {
display: flex;
align-items: center;
gap: 10px;
flex: 1;
min-width: 0;
}
.agents-control-select {
flex: 1;
min-width: 0;
max-width: 280px;
}
.agent-scope-control {
display: inline-flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.agent-scope-control__label {
color: var(--muted);
font-size: 11px;
font-weight: 650;
letter-spacing: 0.06em;
text-transform: uppercase;
white-space: nowrap;
}
.agent-scope-control openclaw-agent-select {
min-width: 150px;
max-width: min(240px, 40vw);
}
.agent-scope-control .agent-select__trigger {
min-height: 34px;
border-color: var(--border);
border-radius: var(--radius-full);
background: var(--bg-elevated);
font-size: 12px;
font-weight: 600;
}
@media (max-width: 640px) {
.agent-scope-control__label {
display: none;
}
.agent-scope-control openclaw-agent-select {
min-width: 128px;
max-width: 44vw;
}
}
.agent-select {
display: block;
width: 100%;
}
.agent-select::part(menu) {
width: min(320px, calc(100vw - 24px));
max-height: 320px;
overflow-y: auto;
padding: var(--menu-padding);
border: 1px solid var(--border-strong);
border-radius: var(--menu-radius);
background: var(--bg-elevated);
box-shadow: var(--shadow-md);
color: var(--text);
}
.agent-select__trigger {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 6px 10px;
border: 1px solid var(--border-strong);
border-radius: var(--radius-md);
background: var(--bg-accent);
color: var(--text);
font: inherit;
font-size: 13px;
font-weight: 500;
outline: none;
text-align: left;
transition:
border-color var(--duration-fast) ease,
box-shadow var(--duration-fast) ease;
}
:root[data-theme-mode="light"] .agent-select__trigger {
background: white;
}
.agent-select__trigger:focus-visible {
border-color: var(--accent);
box-shadow: var(--focus-ring);
}
.agent-select__trigger:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.agent-select__label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.agent-select__chevron {
display: inline-flex;
flex: 0 0 auto;
color: var(--muted);
}
.agent-select__chevron svg {
width: 14px;
height: 14px;
fill: none;
stroke: currentColor;
stroke-width: 1.7px;
stroke-linecap: round;
stroke-linejoin: round;
}
.agent-select__avatar {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 50%;
flex: 0 0 auto;
object-fit: cover;
}
.agent-select__avatar--text,
.agent-select__avatar--icon {
background: var(--secondary);
color: var(--muted);
font-size: 11px;
font-weight: 600;
}
.agent-select__avatar--text::before {
content: attr(data-avatar);
}
.agent-select__avatar--icon svg,
.agent-select__footer-icon svg {
width: 14px;
height: 14px;
fill: none;
stroke: currentColor;
stroke-width: 1.7px;
stroke-linecap: round;
stroke-linejoin: round;
}
.agent-select__badge {
flex: 0 0 auto;
padding: 1px 6px;
border: 1px solid var(--border);
border-radius: var(--radius-full);
color: var(--muted);
font-size: 10px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.agent-select__option {
color: var(--text);
font: inherit;
font-size: 13px;
}
.agent-select__separator {
height: 1px;
margin: 6px 4px;
background: var(--border);
}
.agent-select__option-copy {
display: grid;
flex: 1;
min-width: 0;
gap: 1px;
}
.agent-select__option-label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.agent-select__option-description {
overflow: hidden;
color: var(--muted);
font-size: 11px;
font-weight: 400;
line-height: 1.3;
text-overflow: ellipsis;
white-space: nowrap;
}
.agent-select__footer-icon {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--muted);
}
.agent-select__option .agent-select__avatar {
width: 24px;
height: 24px;
}
.agents-toolbar-actions {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
margin-left: auto;
}
.agents-refresh-btn {
white-space: nowrap;
}
.btn--ghost:hover:not(:disabled) {
background: var(--bg-hover);
color: var(--text);
}
.btn--ghost:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.agents-main {
display: grid;
gap: 14px;
}
.agent-file-tabs {
display: flex;
align-items: flex-end;
gap: var(--space-2);
}
.agent-tab-add {
border: 1px dashed var(--border);
border-radius: var(--radius-sm);
padding: 6px 10px;
font-size: 12px;
font-weight: 600;
color: var(--muted);
background: transparent;
}
.agent-tab-add:hover {
color: var(--text);
border-color: var(--accent);
}
.agent-identity-editor {
display: flex;
align-items: flex-start;
gap: 16px;
width: 100%;
}
.agent-identity-editor__actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.agent-identity-editor__avatar {
flex: 0 0 auto;
width: 56px;
height: 56px;
border-radius: 50%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-elevated);
border: 1px solid var(--border);
font-size: 24px;
}
.agent-identity-editor__avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.agent-identity-editor__fields {
display: flex;
flex-wrap: wrap;
gap: 12px;
flex: 1;
min-width: 0;
}
.agent-identity-editor__fields .field {
flex: 1 1 200px;
min-width: 0;
}
.agent-identity-editor__fields .agent-identity-editor__emoji {
flex: 0 1 96px;
}
.agent-identity-editor__hint {
font-size: 12px;
}
.workspace-link {
display: inline-flex;
align-items: center;
gap: 4px;
border: none;
background: transparent;
color: var(--accent);
font-family: var(--mono);
font-size: 12px;
padding: 2px 0;
word-break: break-all;
text-align: left;
transition: opacity var(--duration-fast) ease;
}
.workspace-link:hover {
opacity: 0.75;
text-decoration: underline;
}
.agent-chip-input {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
padding: 6px 10px;
border: 1px solid var(--border-strong);
border-radius: var(--radius-md);
background: var(--bg-accent);
min-height: 38px;
cursor: text;
transition: border-color var(--duration-fast) ease;
}
.agent-chip-input:focus-within {
border-color: var(--accent);
}
.agent-chip-input input,
.agent-chip-input input:focus-visible {
flex: 1;
min-width: 120px;
border: none;
background: transparent;
outline: none;
box-shadow: none;
font-size: 13px;
padding: 0;
}
.agent-chip-input .chip {
display: inline-flex;
align-items: center;
gap: 6px;
max-width: 100%;
}
.agent-chip-input .chip-remove {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 18px;
height: 18px;
padding: 0;
margin: 0;
border: none;
border-radius: var(--radius-full);
background: transparent;
color: var(--muted);
line-height: 1;
transition:
background var(--duration-fast) var(--ease-out),
color var(--duration-fast) var(--ease-out),
opacity var(--duration-fast) var(--ease-out);
}
.agent-chip-input .chip-remove:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.08);
color: var(--text);
}
.agent-chip-input .chip-remove:focus-visible:not(:disabled) {
background: rgba(255, 255, 255, 0.08);
color: var(--text);
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.agent-chip-input .chip-remove:disabled {
opacity: 0.45;
cursor: default;
}
.agent-file-field {
min-height: clamp(320px, 56vh, 720px);
}
.field textarea.agent-file-textarea {
min-height: clamp(320px, 56vh, 720px);
}
.agent-file-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.agent-file-sub {
color: var(--muted);
font-size: 12px;
margin-top: 4px;
}
.agent-file-actions {
display: flex;
gap: 8px;
}
.md-preview-dialog__panel {
position: relative;
width: 100%;
min-height: min(76vh, 820px);
max-height: calc(100vh - 32px);
margin: 0;
display: flex;
flex-direction: column;
border: 1px solid color-mix(in srgb, var(--border-strong) 84%, white 8%);
border-radius: calc(var(--radius-xl) + 6px);
background: linear-gradient(180deg, color-mix(in srgb, var(--panel) 94%, black 6%), var(--panel));
box-shadow:
0 32px 96px rgba(0, 0, 0, 0.48),
0 1px 0 rgba(255, 255, 255, 0.04) inset;
overflow: hidden;
transition:
width var(--duration-normal) var(--ease-out),
max-height var(--duration-normal) var(--ease-out),
margin var(--duration-normal) var(--ease-out);
}
/* A refusal or empty-state message is a few lines; without this the reader height reserved for
long skill documents leaves the dialog looking broken rather than deliberate. */
.md-preview-dialog__panel--message-only {
min-height: 0;
}
.md-preview-dialog__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 18px;
padding: 20px 22px 16px;
border-bottom: 1px solid var(--border);
background: linear-gradient(
180deg,
color-mix(in srgb, var(--panel) 90%, black 10%),
color-mix(in srgb, var(--panel) 96%, transparent 4%)
);
}
.md-preview-dialog__header-main {
min-width: 0;
display: grid;
gap: 10px;
}
.md-preview-dialog__eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
min-width: 0;
font-size: 12px; /* was 11px */
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--muted);
}
.md-preview-dialog__eyebrow svg {
width: 14px;
height: 14px;
}
.md-preview-dialog__title-wrap {
min-width: 0;
display: grid;
gap: 6px;
}
.md-preview-dialog__title {
min-width: 0;
font-size: clamp(1.2rem, 1rem + 0.6vw, 1.75rem);
line-height: 1.08;
font-weight: 650;
letter-spacing: -0.03em;
color: var(--text-strong);
text-wrap: balance;
overflow-wrap: anywhere;
}
.md-preview-dialog__path {
min-width: 0;
font-size: 12px;
color: var(--muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.md-preview-dialog__actions {
display: flex;
align-items: center;
gap: 7px;
flex-shrink: 0;
}
.md-preview-icon-btn {
width: 36px;
height: 36px;
padding: 0;
border-radius: 12px;
border-color: color-mix(in srgb, var(--border) 78%, white 10%);
background: linear-gradient(
180deg,
color-mix(in srgb, var(--bg-elevated) 92%, white 8%),
color-mix(in srgb, var(--bg-elevated) 88%, black 12%)
);
color: color-mix(in srgb, var(--text) 86%, var(--muted));
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.045),
0 8px 20px rgba(0, 0, 0, 0.18);
}
.md-preview-icon-btn:hover:not(:disabled) {
border-color: color-mix(in srgb, var(--border-strong) 78%, white 14%);
background: linear-gradient(
180deg,
color-mix(in srgb, var(--bg-hover) 88%, white 10%),
color-mix(in srgb, var(--bg-elevated) 84%, black 16%)
);
color: var(--text-strong);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.06),
0 10px 24px rgba(0, 0, 0, 0.24);
}
.md-preview-icon-btn[aria-pressed="true"] {
border-color: color-mix(in srgb, var(--accent) 52%, var(--border));
color: var(--text-strong);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.07),
0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent),
0 12px 28px rgba(0, 0, 0, 0.28);
}
.md-preview-icon-btn > span {
display: inline-flex;
align-items: center;
justify-content: center;
}
.md-preview-icon-btn svg {
width: 16px;
height: 16px;
}
.md-preview-dialog__body {
flex: 1;
min-height: 0;
overflow: auto;
overscroll-behavior: contain;
padding: clamp(18px, 3vw, 28px);
background:
radial-gradient(circle at top right, rgba(255, 92, 92, 0.055), transparent 32%),
radial-gradient(circle at bottom left, rgba(20, 184, 166, 0.045), transparent 28%),
linear-gradient(
180deg,
color-mix(in srgb, var(--panel) 84%, black 16%),
color-mix(in srgb, var(--bg) 88%, black 12%)
);
}
.md-preview-dialog__meta {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 0 22px 16px;
border-bottom: 1px solid color-mix(in srgb, var(--border) 88%, white 6%);
background: linear-gradient(
180deg,
color-mix(in srgb, var(--panel) 96%, transparent 4%),
color-mix(in srgb, var(--panel) 92%, black 8%)
);
}
.md-preview-dialog__chip {
display: inline-flex;
align-items: center;
gap: 8px;
max-width: 100%;
min-height: 32px;
padding: 6px 12px;
border-radius: var(--radius-full);
border: 1px solid color-mix(in srgb, var(--border) 78%, white 10%);
background: color-mix(in srgb, var(--secondary) 72%, transparent);
color: var(--muted);
font-size: 12px;
line-height: 1;
font-variant-numeric: tabular-nums;
}
.md-preview-dialog__chip strong {
font-weight: 600;
color: var(--text-strong);
}
.md-preview-dialog__chip.is-dirty {
border-color: color-mix(in srgb, var(--accent) 42%, var(--border));
background: color-mix(in srgb, var(--accent-subtle) 72%, var(--secondary));
}
.md-preview-dialog__chip.is-missing {
border-color: color-mix(in srgb, var(--warn) 42%, var(--border));
background: color-mix(in srgb, var(--warn-subtle) 78%, var(--secondary));
}
.md-preview-dialog__chip.is-synced {
border-color: color-mix(in srgb, var(--accent-2) 36%, var(--border));
background: color-mix(in srgb, var(--accent-2-subtle) 72%, var(--secondary));
}
.md-preview-dialog__reader {
--md-preview-serif: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
--md-preview-document-bg: color-mix(in srgb, var(--bg) 92%, var(--panel) 8%);
position: relative;
width: min(100%, 82ch);
margin: 0 auto;
padding: clamp(26px, 4vw, 56px);
border: 1px solid color-mix(in srgb, var(--border-strong) 70%, white 10%);
border-radius: calc(var(--radius-xl) + 2px);
background: linear-gradient(
180deg,
color-mix(in srgb, var(--md-preview-document-bg) 94%, white 6%),
var(--md-preview-document-bg)
);
box-shadow:
0 22px 54px rgba(0, 0, 0, 0.32),
inset 0 1px 0 rgba(255, 255, 255, 0.035);
}
.md-preview-dialog__reader::before {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background:
radial-gradient(circle at top left, rgba(255, 255, 255, 0.04), transparent 24%),
linear-gradient(180deg, rgba(255, 255, 255, 0.01), transparent 18%);
}
.md-preview-dialog__reader > * {
position: relative;
z-index: 1;
}
.md-preview-dialog__reader.sidebar-markdown {
font-size: calc(15px * var(--control-ui-text-scale));
line-height: 1.74;
color: var(--text);
}
.md-preview-dialog__reader.sidebar-markdown > :first-child {
margin-top: 0;
}
.md-preview-dialog__reader.sidebar-markdown > :last-child {
margin-bottom: 0;
}
.md-preview-dialog__reader.sidebar-markdown :is(h1, h2, h3, h4, h5, h6) {
margin: 0 0 0.9em;
line-height: 1.08;
font-weight: 650;
letter-spacing: -0.03em;
color: var(--text-strong);
text-wrap: balance;
}
.md-preview-dialog__reader.sidebar-markdown h1 {
font-family: var(--md-preview-serif);
font-size: clamp(
calc(2.2rem * var(--control-ui-text-scale)),
calc(4.4vw * var(--control-ui-text-scale)),
calc(3.35rem * var(--control-ui-text-scale))
);
line-height: 0.98;
}
.md-preview-dialog__reader.sidebar-markdown h2 {
margin-top: 2.4rem;
padding-top: 1.3rem;
border-top: 1px solid color-mix(in srgb, var(--border) 82%, white 12%);
font-family: var(--md-preview-serif);
font-size: clamp(
calc(1.45rem * var(--control-ui-text-scale)),
calc(2.1vw * var(--control-ui-text-scale)),
calc(2rem * var(--control-ui-text-scale))
);
}
.md-preview-dialog__reader.sidebar-markdown h3 {
margin-top: 1.9rem;
font-size: clamp(
calc(1.18rem * var(--control-ui-text-scale)),
calc(1.4vw * var(--control-ui-text-scale)),
calc(1.42rem * var(--control-ui-text-scale))
);
}
.md-preview-dialog__reader.sidebar-markdown :is(p, ul, ol, blockquote, pre, table, hr, details) {
margin-top: 0;
margin-bottom: 1.05rem;
}
.md-preview-dialog__reader.sidebar-markdown :is(ul, ol) {
padding-left: 1.2rem;
}
.md-preview-dialog__reader.sidebar-markdown :is(ul ul, ul ol, ol ul, ol ol) {
margin-top: 0.4rem;
margin-bottom: 0.4rem;
}
.md-preview-dialog__reader.sidebar-markdown li + li {
margin-top: 0.32rem;
}
.md-preview-dialog__reader.sidebar-markdown blockquote {
padding: 0.95rem 1.1rem;
border-left: 3px solid color-mix(in srgb, var(--accent) 85%, white 15%);
border-radius: 0 var(--radius-md) var(--radius-md) 0;
background: color-mix(in srgb, var(--secondary) 64%, transparent);
color: color-mix(in srgb, var(--text) 82%, var(--muted));
}
.md-preview-dialog__reader.sidebar-markdown pre {
padding: 16px 18px;
border: 1px solid color-mix(in srgb, var(--border) 88%, white 8%);
border-radius: var(--radius-lg);
background: color-mix(in srgb, var(--bg-elevated) 84%, black 16%);
}
.md-preview-dialog__reader.sidebar-markdown :not(pre) > code {
padding: 0.16rem 0.42rem;
border: 1px solid color-mix(in srgb, var(--border) 82%, white 8%);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--secondary) 62%, transparent);
font-size: 0.9em;
}
.md-preview-dialog__reader.sidebar-markdown hr {
border: 0;
border-top: 1px solid color-mix(in srgb, var(--border) 82%, white 10%);
}
.md-preview-dialog__reader.sidebar-markdown table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
overflow: hidden;
border: 1px solid color-mix(in srgb, var(--border) 82%, white 10%);
border-radius: var(--radius-lg);
}
.md-preview-dialog__reader.sidebar-markdown :is(th, td) {
padding: 10px 12px;
border-bottom: 1px solid color-mix(in srgb, var(--border) 82%, white 8%);
text-align: left;
vertical-align: top;
}
.md-preview-dialog__reader.sidebar-markdown th {
background: color-mix(in srgb, var(--secondary) 56%, transparent);
font-weight: 600;
}
.md-preview-dialog__reader.sidebar-markdown tr:last-child td {
border-bottom: none;
}
.md-preview-dialog__panel.fullscreen {
width: 100%;
max-height: calc(100vh - 20px);
margin: 0;
}
.md-preview-dialog__panel.fullscreen .md-preview-dialog__header {
align-items: center;
justify-content: flex-end;
gap: 0;
padding: 10px 12px;
}
.md-preview-dialog__panel.fullscreen .md-preview-dialog__header-main {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.md-preview-dialog__panel.fullscreen .md-preview-dialog__actions {
margin-left: auto;
}
.md-preview-dialog__panel.fullscreen .md-preview-dialog__meta {
display: none;
}
.md-preview-dialog__panel.fullscreen .md-preview-dialog__body {
padding: clamp(10px, 1.5vw, 18px);
}
.md-preview-dialog__panel.fullscreen .md-preview-dialog__reader {
width: min(100%, 96ch);
padding: clamp(24px, 3vw, 52px);
}
@media (max-width: 768px) {
.md-preview-dialog__panel {
width: 100%;
min-height: calc(100vh - 12px);
max-height: calc(100vh - 12px);
margin: 0;
border-radius: var(--radius-xl);
}
.md-preview-dialog__header {
flex-direction: column;
align-items: stretch;
padding: 16px 16px 14px;
}
.md-preview-dialog__actions {
justify-content: flex-end;
flex-wrap: wrap;
}
.md-preview-dialog__meta {
padding: 0 16px 14px;
}
.md-preview-dialog__body {
padding: 14px;
}
.md-preview-dialog__reader {
width: 100%;
padding: 20px 16px 22px;
}
}
@media (prefers-reduced-motion: reduce) {
.md-preview-dialog__panel {
animation: none;
transition: none;
}
}
.agent-tools-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.agent-tools-grid {
display: grid;
gap: 16px;
margin-top: 20px;
}
.agent-tools-runtime {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.agent-tools-runtime-chip {
display: inline-flex;
align-items: center;
gap: 8px;
min-width: 0;
max-width: 100%;
padding: 7px 10px;
border: 1px solid var(--border);
border-radius: var(--radius-full);
background: color-mix(in srgb, var(--card) 86%, transparent);
color: inherit;
text-decoration: none;
transition:
background-color var(--duration-fast) var(--ease-in-out),
border-color var(--duration-fast) var(--ease-in-out),
color var(--duration-fast) var(--ease-in-out);
}
.agent-tools-runtime-chip:hover {
background: color-mix(in srgb, var(--card) 92%, transparent);
border-color: color-mix(in srgb, var(--border-strong) 58%, var(--border));
}
.agent-tools-runtime-chip:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.agent-tools-runtime-chip--more {
color: var(--muted);
cursor: default;
background: color-mix(in srgb, var(--bg-elevated) 84%, transparent);
}
.agent-tools-runtime-chip--more:hover {
background: color-mix(in srgb, var(--bg-elevated) 88%, transparent);
border-color: var(--border);
}
.agent-tools-runtime-chip__meta {
color: var(--muted);
font-size: 12px; /* was 11px */
white-space: nowrap;
}
.agent-tools-group {
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: var(--bg-elevated);
overflow: hidden;
}
.agent-tools-group summary::-webkit-details-marker,
.agent-tool-summary::-webkit-details-marker {
display: none;
}
.agent-tools-group summary::marker,
.agent-tool-summary::marker {
content: "";
}
.agent-tools-group__summary {
display: flex;
align-items: flex-start;
gap: 12px;
justify-content: space-between;
padding: 12px 14px;
list-style: none;
transition:
background-color var(--duration-fast) var(--ease-in-out),
color var(--duration-fast) var(--ease-in-out);
}
.agent-tools-group__summary::before {
content: "▸";
color: var(--muted);
font-size: 12px; /* was 11px */
line-height: 20px;
transition: transform var(--duration-fast) var(--ease-in-out);
}
.agent-tools-group[open] .agent-tools-group__summary::before {
transform: rotate(90deg);
}
.agent-tools-group__summary:hover {
background: color-mix(in srgb, var(--bg-elevated) 96%, var(--text) 4%);
}
.agent-tools-group__summary:hover::before {
color: color-mix(in srgb, var(--text) 78%, var(--muted));
}
.agent-tools-group__summary:focus-visible {
outline: none;
box-shadow: inset var(--focus-ring);
}
.agent-tools-group__title {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
font-weight: 600;
}
.agent-tools-group__summary-main {
display: grid;
gap: 6px;
min-width: 0;
flex: 1;
}
.agent-tools-group__preview {
display: flex;
gap: 6px;
flex-wrap: wrap;
min-width: 0;
color: var(--muted);
font-size: 12px; /* was 11px */
}
.agent-tools-group__preview > span {
min-width: 0;
max-width: min(180px, 100%);
padding: 2px 6px;
border: 1px solid color-mix(in srgb, var(--border) 74%, transparent);
border-radius: var(--radius-full);
background: color-mix(in srgb, var(--card) 70%, transparent);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.agent-tools-group__counts {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
flex-wrap: wrap;
color: var(--muted);
font-size: 12px; /* was 11px */
font-variant-numeric: tabular-nums;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.agent-tools-group__counts > span {
white-space: nowrap;
}
.agent-tools-list {
display: grid;
gap: 10px;
padding: 0 12px 12px;
}
.agent-tools-list--stacked {
grid-template-columns: 1fr;
}
.agent-tool-card {
position: relative;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--card);
overflow: hidden;
scroll-margin-top: 16px;
}
.agent-tool-card[open] {
border-color: color-mix(in srgb, var(--accent) 22%, var(--border));
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent);
}
.agent-tool-summary {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1.7fr) minmax(220px, 0.9fr) auto;
gap: 12px 16px;
align-items: center;
min-width: 0;
padding: 12px 92px 12px 12px;
list-style: none;
transition:
background-color var(--duration-fast) var(--ease-in-out),
color var(--duration-fast) var(--ease-in-out);
}
.agent-tool-summary::after {
content: "▸";
position: absolute;
top: 18px;
right: 64px;
color: var(--muted);
font-size: 12px; /* was 11px */
transition: transform var(--duration-fast) var(--ease-in-out);
}
.agent-tool-card[open] .agent-tool-summary::after {
transform: rotate(90deg);
}
.agent-tool-summary:hover {
background: color-mix(in srgb, var(--card) 97%, var(--text) 3%);
}
.agent-tool-summary:hover::after {
color: color-mix(in srgb, var(--text) 78%, var(--muted));
}
.agent-tool-summary:focus-visible {
outline: none;
box-shadow: inset var(--focus-ring);
}
.agent-tool-summary__main {
min-width: 0;
}
.agent-tool-summary__title-row {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.agent-tool-summary__badges {
min-width: 0;
}
.agent-tool-summary__badges .agent-tool-badges {
margin-top: 0;
justify-content: flex-end;
}
.agent-tool-summary__facts {
display: grid;
gap: 8px 12px;
grid-template-columns: repeat(2, minmax(0, 1fr));
margin: 0;
min-width: 0;
}
.agent-tool-summary__fact {
min-width: 0;
}
.agent-tool-summary__fact dd {
margin: 2px 0 0;
font-size: 12px;
}
.agent-tool-toggle {
position: absolute;
top: 12px;
right: 12px;
margin: 0;
z-index: 1;
}
.agent-tool-badges {
display: flex;
gap: 6px;
flex-wrap: wrap;
margin-top: 8px;
}
.agent-tool-title {
font-weight: 600;
font-size: 13px;
min-width: 0;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.agent-tool-sub {
color: var(--muted);
font-size: 12px; /* was 11px */
margin-top: 3px;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.agent-tool-details {
padding: 0 12px 12px;
border-top: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
}
.agent-tool-card:not([open]) .agent-tool-details {
display: none;
}
.agent-tool-details-strip {
display: flex;
flex-wrap: wrap;
gap: 10px 20px;
align-items: flex-start;
padding-top: 10px;
}
.agent-tool-detail {
min-width: 0;
}
.agent-tool-detail--inline {
max-width: min(100%, 260px);
}
.agent-tool-detail .label {
margin-bottom: 4px;
}
.agent-tool-card[open] .agent-tool-sub {
overflow: visible;
text-overflow: clip;
white-space: normal;
}
.agent-tool-jump {
color: var(--accent);
text-decoration: none;
align-self: end;
margin-left: auto;
}
.agent-tool-jump:hover {
color: var(--accent-hover);
text-decoration: underline;
}
.agent-tool-jump:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
border-radius: var(--radius-sm);
}
@media (prefers-reduced-motion: reduce) {
.agent-tools-runtime-chip,
.agent-tools-group__summary,
.agent-tool-summary,
.agent-tools-group__summary::before,
.agent-tool-summary::after {
transition: none;
}
}
@media (max-width: 1320px) {
.agent-tool-summary {
grid-template-columns: minmax(0, 1fr) auto;
}
.agent-tool-summary__facts {
grid-column: 1 / -1;
}
.agent-tool-summary__badges .agent-tool-badges {
justify-content: flex-start;
}
}
@media (max-width: 768px) {
.agent-tools-group__summary {
flex-wrap: wrap;
align-items: flex-start;
}
.agent-tools-group__summary::before {
line-height: 18px;
}
.agent-tools-group__counts {
justify-content: flex-start;
width: 100%;
padding-left: 24px;
}
.agent-tool-summary {
grid-template-columns: 1fr;
padding-right: 92px;
}
.agent-tool-summary__facts {
grid-template-columns: 1fr;
}
.agent-tool-jump {
margin-left: 0;
}
}
.agent-skills-groups {
display: grid;
gap: 16px;
}
.agent-skills-group {
display: grid;
gap: 10px;
}
.agent-skills-group summary {
list-style: none;
}
.agent-skills-header {
display: flex;
align-items: center;
font-weight: 600;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--muted);
gap: 8px;
}
.agent-skills-header > span:last-child {
margin-left: auto;
}
.agent-skills-group summary::-webkit-details-marker {
display: none;
}
.agent-skills-group summary::marker {
content: "";
}
.agent-skills-header::after {
content: "▸";
font-size: 12px;
color: var(--muted);
transition: transform var(--duration-fast) ease;
margin-left: 8px;
}
.agent-skills-group[open] .agent-skills-header::after {
transform: rotate(90deg);
}
.agent-skill-row {
align-items: flex-start;
gap: 18px;
}
.agent-skill-row .list-meta {
display: flex;
align-items: flex-start;
justify-content: flex-end;
min-width: auto;
}
.skills-grid {
grid-template-columns: 1fr;
}
@container (min-width: 900px) {
.skills-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 1100px) {
.agent-tools-list {
grid-template-columns: 1fr;
}
}
@media (max-width: 640px) {
.agents-toolbar-row {
flex-direction: column;
align-items: stretch;
gap: 6px;
}
.agents-control-select {
max-width: none;
}
.agents-toolbar-actions {
margin-left: 0;
justify-content: flex-end;
}
}
.cmd-palette {
width: 100%;
overflow: hidden;
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
animation: scale-in 0.15s ease-out;
}
.cmd-palette__label {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
}
.cmd-palette__input {
width: 100%;
padding: 14px 18px;
background: transparent;
border: none;
border-bottom: 1px solid var(--border);
color: var(--text);
font-size: 15px;
outline: none;
}
.cmd-palette__input::placeholder {
color: var(--muted);
}
.cmd-palette__results {
max-height: 320px;
overflow-y: auto;
padding: 6px 0;
}
.cmd-palette__group-label {
padding: 8px 18px 4px;
color: var(--muted);
font-size: 12px; /* was 11px */
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.cmd-palette__item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 18px;
font-size: 14px;
transition: background var(--duration-fast) ease;
}
.cmd-palette__item:hover,
.cmd-palette__item--active {
background: var(--bg-hover);
}
.cmd-palette__item .nav-item__icon {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.cmd-palette__item .nav-item__icon svg {
width: 100%;
height: 100%;
}
.cmd-palette__item-desc {
margin-left: auto;
font-size: 12px;
}
.cmd-palette__empty {
display: flex;
align-items: center;
gap: 8px;
padding: 16px 18px;
color: var(--muted);
font-size: 13px;
}
.cmd-palette__footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
padding: 8px 18px;
border-top: 1px solid var(--border);
font-size: 12px; /* was 11px */
color: var(--muted);
}
.cmd-palette__footer kbd {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 1px 5px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg);
font-family: var(--mono);
font-size: 12px; /* was 10px */
line-height: 1.4;
}
@media (max-width: 768px) {
.exec-approval-card {
padding: 16px;
display: flex;
flex-direction: column;
}
.exec-approval-actions {
flex-direction: column;
flex-shrink: 0;
}
.exec-approval-actions .btn {
width: 100%;
}
.exec-approval-command {
font-size: 12px;
padding: 8px 10px;
max-height: 40dvh;
overflow-y: auto;
}
}
/* Mobile pairing */
.device-pair-setup {
max-height: calc(100dvh - 48px);
overflow-y: auto;
border: 1px solid var(--border-strong);
border-radius: var(--radius-xl);
background: color-mix(in srgb, var(--card) 96%, black 4%);
box-shadow: var(--shadow-xl);
}
.device-pair-setup__header {
position: relative;
display: grid;
justify-items: center;
gap: 12px;
padding: 28px 52px 18px;
text-align: center;
}
.device-pair-setup__header h2 {
margin: 0;
color: var(--text-strong);
font-size: 24px;
font-weight: 600;
letter-spacing: -0.025em;
}
.device-pair-setup__header p {
max-width: 390px;
margin: 7px 0 0;
color: var(--muted);
font-size: 14px;
line-height: 1.5;
}
.device-pair-setup__header p.device-pair-setup__get-apps {
margin-top: 2px;
font-size: 13px;
}
.device-pair-setup__get-apps button {
padding: 0;
border: 0;
background: none;
color: var(--text);
font: inherit;
cursor: var(--cursor-action);
text-decoration: underline;
text-underline-offset: 2px;
}
.device-pair-setup__get-apps button:hover {
color: var(--text-strong);
}
.device-pair-setup__get-apps button:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
border-radius: 4px;
}
.device-pair-setup__phone {
display: grid;
width: 44px;
height: 44px;
place-items: center;
border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
border-radius: 14px;
background: var(--accent-subtle);
color: var(--accent);
}
.device-pair-setup__phone svg {
width: 22px;
height: 22px;
fill: none;
stroke: currentColor;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 1.5px;
}
.device-pair-setup__close {
position: absolute;
top: 16px;
right: 16px;
}
.device-pair-setup__body {
display: grid;
justify-items: center;
gap: 15px;
padding: 0 28px 24px;
}
.device-pair-setup__access {
display: grid;
width: min(440px, 100%);
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
margin: 0;
padding: 0;
border: 0;
}
.device-pair-setup__access legend {
margin: 0 auto 8px;
color: var(--muted);
font-size: 12px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.device-pair-setup__access label {
display: grid;
grid-template-columns: auto 1fr;
gap: 8px;
padding: 10px;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--bg-elevated);
cursor: var(--cursor-action);
}
.device-pair-setup__access label:has(input:checked) {
border-color: color-mix(in srgb, var(--accent) 65%, var(--border));
background: var(--accent-subtle);
}
.device-pair-setup__access:disabled label {
cursor: default;
opacity: 0.7;
}
.device-pair-setup__access input {
margin-top: 2px;
}
.device-pair-setup__access span {
display: grid;
gap: 3px;
}
.device-pair-setup__access strong {
color: var(--text-strong);
font-size: 13px;
}
.device-pair-setup__access small {
color: var(--muted);
font-size: 11px;
line-height: 1.35;
}
.device-pair-setup__loading {
display: flex;
min-height: 260px;
align-items: center;
justify-content: center;
gap: 10px;
color: var(--muted);
font-size: 14px;
}
.device-pair-setup__spinner {
width: 18px;
height: 18px;
border: 2px solid var(--border-strong);
border-top-color: var(--accent);
border-radius: 50%;
animation: device-pair-setup-spin 0.8s linear infinite;
}
@keyframes device-pair-setup-spin {
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: reduce) {
.device-pair-setup__spinner {
animation: none;
}
}
.device-pair-setup__error {
width: 100%;
}
.device-pair-setup__error span {
display: block;
margin-top: 4px;
overflow-wrap: anywhere;
}
.device-pair-setup__access-warning {
width: min(440px, 100%);
}
.device-pair-setup__access-warning span {
display: block;
margin-top: 4px;
}
.device-pair-setup__qr-frame {
display: grid;
width: min(320px, 100%);
aspect-ratio: 1;
place-items: center;
box-sizing: border-box;
padding: 18px;
border: 1px solid var(--border);
border-radius: 22px;
background: var(--qr-bg);
box-shadow: 0 14px 40px rgba(0, 0, 0, 0.28);
}
.device-pair-setup__qr {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
user-select: none;
}
.device-pair-setup__qr-unavailable {
max-width: 220px;
color: var(--qr-text);
font-size: 14px;
line-height: 1.5;
text-align: center;
}
.device-pair-setup__command {
display: grid;
width: min(540px, 100%);
gap: 10px;
}
.device-pair-setup__meta {
display: flex;
max-width: 100%;
align-items: center;
gap: 9px;
}
.device-pair-setup__gateway {
min-width: 0;
overflow: hidden;
color: var(--muted);
font-family: var(--mono);
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
.device-pair-setup__gateways {
display: flex;
min-width: 0;
flex-direction: column;
gap: 2px;
}
.device-pair-setup__actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
}
.device-pair-setup__fallback {
width: min(420px, 100%);
color: var(--muted);
font-size: 12px;
}
.device-pair-setup__fallback summary {
width: fit-content;
margin: 0 auto;
}
.device-pair-setup__fallback code {
display: block;
max-height: 110px;
margin-top: 10px;
padding: 10px;
overflow: auto;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--bg);
color: var(--text);
overflow-wrap: anywhere;
white-space: normal;
}
.device-pair-setup__pending {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.device-pair-setup__waiting {
margin: 0;
color: var(--muted);
font-size: 13px;
text-align: center;
}
.device-pair-setup__state {
display: grid;
min-height: 250px;
align-content: center;
justify-items: center;
gap: 12px;
text-align: center;
}
.device-pair-setup__state-icon {
display: grid;
width: 48px;
height: 48px;
place-items: center;
border: 1px solid var(--border-strong);
border-radius: 16px;
background: var(--bg-elevated);
color: var(--muted);
}
.device-pair-setup__state-icon--success {
border-color: color-mix(in srgb, var(--ok) 32%, var(--border));
background: color-mix(in srgb, var(--ok) 10%, var(--bg-elevated));
color: var(--ok);
}
.device-pair-setup__state-icon svg {
width: 24px;
height: 24px;
}
.device-pair-setup__state h3 {
margin: 2px 0 0;
color: var(--text-strong);
font-size: 18px;
font-weight: 600;
letter-spacing: -0.015em;
line-height: 1.3;
text-wrap: balance;
}
.device-pair-setup__state p {
margin: -6px 0 4px;
color: var(--muted);
font-size: 13px;
}
.device-pair-setup__footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 20px;
border-top: 1px solid var(--border);
background: color-mix(in srgb, var(--bg-elevated) 45%, transparent);
font-size: 13px;
}
.device-pair-setup__footer a {
display: inline-flex;
min-height: 40px;
align-items: center;
gap: 4px;
border-radius: 4px;
color: var(--muted);
text-underline-offset: 2px;
}
.device-pair-setup__footer a:hover {
color: var(--text);
}
.device-pair-setup__footer a:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
color: var(--text);
}
.device-pair-setup__external-icon {
display: inline-flex;
}
.device-pair-setup__external-icon svg {
width: 14px;
height: 14px;
}
@media (max-width: 640px) {
.device-pair-setup {
max-height: 90dvh;
}
.device-pair-setup__header {
padding: 22px 46px 16px;
}
.device-pair-setup__body {
padding: 0 18px 20px;
}
.device-pair-setup__qr-frame {
width: min(300px, 100%);
padding: 14px;
}
.device-pair-setup__access {
grid-template-columns: 1fr;
}
.device-pair-setup__footer a {
min-height: 44px;
}
}
.md-preview-expand-btn .when-fullscreen {
display: none;
}
.md-preview-expand-btn.is-fullscreen .when-normal {
display: none;
}
.md-preview-expand-btn.is-fullscreen .when-fullscreen {
display: inline;
}
/* ── Session row primitives ──
Shared by the sidebar recents list and the chat session picker. Rows read
as plain text lines: attribute badges follow the title, and the trailing
aside stacks transient state, relative time, and management actions in one
grid cell, so hovering swaps state for actions without any layout shift. */
.session-row-host {
position: relative;
}
.session-row-host--draft {
--draft-row-opacity: 1;
opacity: var(--draft-row-opacity);
}
.session-row-host--draft-owner .session-row-draft-indicator {
opacity: 0.55;
}
.session-row-host--draft-other {
--draft-row-opacity: 0.58;
background: color-mix(in srgb, var(--bg-muted) 44%, transparent);
}
.session-row-host--draft-other:hover,
.session-row-host--draft-other:focus-within,
.session-row-host--draft-other.sidebar-recent-session--selected {
--draft-row-opacity: 0.88;
}
.session-row-draft-indicator {
display: inline-grid;
width: 18px;
height: 18px;
flex: 0 0 auto;
place-items: center;
border: 1px solid var(--border);
border-radius: var(--radius-full);
background: color-mix(in srgb, var(--bg-elevated) 72%, transparent);
font-size: 12px;
}
/* Unread indicator shared by the sidebar recents list, the chat session
picker, and the sessions page key cell. */
.session-unread-dot {
display: inline-block;
width: 7px;
height: 7px;
flex: 0 0 auto;
border-radius: var(--radius-full);
background: var(--accent);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 14%, transparent);
}
.session-row-aside {
display: grid;
align-items: center;
justify-items: end;
flex: 0 0 auto;
}
.session-row-aside > * {
grid-area: 1 / 1;
}
.session-row-trail {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--muted);
font-size: var(--control-ui-text-xs);
font-variant-numeric: tabular-nums;
white-space: nowrap;
transition: opacity var(--duration-fast) ease;
/* Display-only, and its opacity transition forms a stacking context that
would otherwise sit above the action buttons and swallow their clicks. */
pointer-events: none;
}
.session-row-state {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
min-width: 24px;
min-height: 24px;
transition: opacity var(--duration-fast) ease;
pointer-events: none;
}
.sidebar-session-fork-indicator {
display: inline-flex;
margin-right: 4px;
color: var(--muted);
vertical-align: -2px;
}
.sidebar-session-fork-indicator svg {
width: 14px;
height: 14px;
fill: none;
stroke: currentColor;
stroke-width: 1.6px;
}
/* Attribute badges (attached automation, approval, cloud placement):
muted metadata grouped in the row's second-line endcap. */
.session-row-badges {
display: inline-flex;
align-items: center;
gap: 6px;
flex: 0 0 auto;
}
.session-row-badge {
display: inline-flex;
align-items: center;
color: var(--muted);
}
.session-row-badge--attention {
gap: 2px;
color: var(--warn);
font-size: 10px;
font-variant-numeric: tabular-nums;
}
.session-row-badge--pull-request[data-pull-request-state="open"],
.session-row-badge--cloud[data-placement-state="active"] {
color: var(--ok);
}
.session-row-badge--pull-request[data-pull-request-state="merged"] {
color: var(--accent);
}
.session-row-badge--approval {
display: inline-flex;
align-items: center;
color: var(--warn);
}
.session-row-badge--cloud[data-placement-state="failed"] {
color: var(--danger);
}
.session-row-badge--cloud:is(
[data-placement-state="requested"],
[data-placement-state="provisioning"],
[data-placement-state="syncing"],
[data-placement-state="starting"],
[data-placement-state="draining"],
[data-placement-state="reconciling"]
) {
color: var(--accent);
}
.session-row-badge--cloud[data-workspace-conflicts] {
color: var(--warn);
}
.session-row-badge--cloud[data-disk-space-status="warning"] {
color: var(--warn);
}
.session-row-badge--cloud[data-disk-space-status="critical"] {
color: var(--danger);
}
.session-row-badge svg {
width: 12px;
height: 12px;
stroke: currentColor;
fill: none;
stroke-width: 1.6px;
stroke-linecap: round;
stroke-linejoin: round;
}
.session-row-actions {
display: inline-flex;
align-items: center;
gap: 1px;
}
.session-action {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
padding: 0;
border: none;
border-radius: var(--radius-sm);
background: transparent;
color: var(--muted);
opacity: 0;
pointer-events: none;
transition:
opacity var(--duration-fast) ease,
background var(--duration-fast) ease,
color var(--duration-fast) ease;
}
.session-action svg {
width: 14px;
height: 14px;
stroke: currentColor;
fill: none;
stroke-width: 1.6px;
stroke-linecap: round;
stroke-linejoin: round;
}
.session-action:hover:not(:disabled) {
background: color-mix(in srgb, var(--bg-hover) 92%, transparent);
color: var(--text);
}
.session-action:disabled {
cursor: not-allowed;
}
.session-row-host:hover .session-action,
.session-row-host:focus-within .session-action {
opacity: 1;
pointer-events: auto;
}
/* Disabled actions still surface on hover (dimmed) so the tooltip can
explain why archiving is unavailable instead of the button vanishing. */
.session-row-host:hover .session-action:disabled,
.session-row-host:focus-within .session-action:disabled {
opacity: 0.35;
pointer-events: auto;
}
.session-row-host:is(:hover, :focus-within) :is(.session-row-trail, .session-row-state) {
opacity: 0;
}
/* Sidebar rows are now the direct management surface after removing the
duplicate picker; touch users need pin/menu controls without hover. */
@media (hover: none), (pointer: coarse) {
.sidebar-recent-session .session-action {
opacity: 1;
pointer-events: auto;
}
.sidebar-recent-session .session-action:disabled {
opacity: 0.35;
}
.sidebar-recent-session .session-row-trail {
opacity: 0;
}
.sidebar-recent-session .session-row-aside:has(> .session-row-state) {
column-gap: 1px;
}
.sidebar-recent-session .session-row-state {
grid-area: 1 / 1;
}
.sidebar-recent-session .session-row-actions {
grid-area: 1 / 2;
}
}
.session-row-host--pinned .session-action--pin {
color: var(--accent);
}
.session-run-spinner {
box-sizing: border-box;
display: inline-block;
width: 12px;
height: 12px;
flex: 0 0 auto;
border: 1.5px solid color-mix(in srgb, var(--run) 28%, transparent);
border-top-color: var(--run);
border-radius: var(--radius-full);
}
.session-run-spinner,
.session-glyph__ring {
animation: session-run-spin 1.6s linear infinite;
}
@keyframes session-run-spin {
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: reduce) {
.session-run-spinner,
.session-glyph__ring {
animation: none;
border-color: var(--run);
}
}
/* ===========================================
Hover Marquee
=========================================== */
/* Truncated single-line labels that slide on hover to reveal the clipped
tail. lib/hover-marquee.ts measures the overflow and sets the custom
properties; without it the label stays a plain ellipsized span. Animates
text-indent so the resting ellipsis keeps rendering (text-overflow shows
no "…" for atomic inline children, ruling out a transformed wrapper). */
.hover-marquee {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: text-indent 180ms ease-out;
}
.hover-marquee--scrolling {
text-overflow: clip;
text-indent: var(--hover-marquee-shift, 0);
transition-duration: var(--hover-marquee-duration, 600ms);
transition-timing-function: linear;
}
@media (prefers-reduced-motion: reduce) {
.hover-marquee {
transition: none;
}
.hover-marquee--scrolling {
text-overflow: ellipsis;
text-indent: 0;
}
}
/* ---- Plugin tab embeds (plugin-served panels) ---- */
.plugin-tab-embed {
display: flex;
min-height: 0;
height: 100%;
}
.plugin-tab-embed__frame {
flex: 1;
min-height: 480px;
width: 100%;
border: none;
border-radius: 10px;
background: var(--card);
}
/* ── Provider brand icons (shared: chat picker, model providers page) ── */
.provider-brand-icon {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--muted);
}
.provider-brand-icon:not(.provider-brand-icon--fallback) {
background: currentColor;
mask: var(--provider-icon-url) center / contain no-repeat;
-webkit-mask: var(--provider-icon-url) center / contain no-repeat;
}
.provider-brand-icon--fallback {
border: 1px solid color-mix(in srgb, currentColor 48%, transparent);
border-radius: var(--radius-sm);
font-weight: 700;
line-height: 1;
}
.provider-brand-icon[data-provider-icon="codex"] {
color: var(--brand-codex);
}
.provider-brand-icon[data-provider-icon="claude"] {
color: var(--brand-claude);
}
.provider-brand-icon[data-provider-icon="gemini"] {
color: var(--brand-google);
}
.provider-brand-icon[data-provider-icon="llamacpp"],
.provider-brand-icon[data-provider-icon="lmstudio"],
.provider-brand-icon[data-provider-icon="ollama"] {
color: var(--text-strong);
}
.provider-brand-icon[data-provider-icon="opencode"],
.provider-brand-icon[data-provider-icon="opencodego"],
.provider-brand-icon[data-provider-icon="pi"] {
color: var(--text-strong);
}
/* Shared working-claw motion: chat and settings use the same body flex and
jaw snip timing so the brand activity signal stays consistent. */
@keyframes chatWorkingClawFlex {
0%,
6% {
transform: rotate(0deg);
}
10% {
transform: rotate(-4deg);
}
16% {
transform: rotate(3deg);
}
22%,
26% {
transform: rotate(-4deg);
}
32% {
transform: rotate(3deg);
}
42%,
100% {
transform: rotate(0deg);
}
}
@keyframes chatWorkingClawSnip {
0%,
6% {
transform: rotate(-10deg);
}
10% {
transform: rotate(-26deg);
}
16% {
transform: rotate(4deg);
}
22%,
26% {
transform: rotate(-24deg);
}
32% {
transform: rotate(4deg);
}
42%,
100% {
transform: rotate(-10deg);
}
}