diff --git a/apps/web/src/app.css b/apps/web/src/app.css index 13a8ae723..2fa9093c9 100644 --- a/apps/web/src/app.css +++ b/apps/web/src/app.css @@ -10,20 +10,6 @@ @import "./settings/styles.css"; @import "./button-roles.css"; -/* react-ui's `.corbits-toast` ships a hardcoded horizontal-center transform - (`left: 50%; transform: translateX(-50%) !important`) regardless of the - `position` prop passed to `` — main.tsx now mounts it - bottom-right, and without this the toast would still visually sit - centered while claiming to be on the right. Sonner itself already - right-aligns the toast via its own `[data-x-position="right"]` rule - (injected at runtime, unrelated to this stylesheet); this just stops - react-ui's override from fighting it. Higher specificity than that rule - wins the cascade on its own, so this is safe regardless of load order. */ -[data-sonner-toaster][data-x-position="right"] [data-sonner-toast].corbits-toast { - left: auto !important; - transform: none !important; -} - /* Mock chrome: 15px root so rem-based density matches the interactive mock. */ html { font-size: 15px; @@ -344,81 +330,8 @@ select:disabled, background: var(--background); } -/* Mock .section labels in sidebar lists — always 0.65rem / 0.07em. */ -.shell-sidebar .panel-band-heading { - font-size: 0.65rem; - font-weight: 650; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--muted-foreground); - padding: 0.65rem 0.45rem 0.3rem; - margin: 0; -} - -.panel-band { - display: flex; - flex-direction: column; - gap: 0.5rem; - padding: 0.75rem; - border-bottom: 1px solid var(--border); - min-height: 0; -} - -/* Kill heavy band chrome inside the sidebar — lists sit flush in the scroll body. */ -.shell-sidebar .panel-band { - gap: 0.15rem; - padding: 0; - border-bottom: none; -} - -.panel-band-inset { - display: flex; - flex-wrap: wrap; - gap: 0.375rem; - padding: 0 0.7rem 0.55rem; - border-bottom: none; -} - -.panel-band-page-specific { - flex: 1; - overflow-y: auto; - border-bottom: none; -} - -/* Header already prints the page title — never re-title the body band. */ -.shell-sidebar .panel-band-page-specific > .panel-band-heading { - display: none; -} - -.panel-page-header { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 0.5rem; -} - -.panel-page-identity { - min-width: 0; -} - -.panel-page-title { - margin: 0; - font-size: 0.9375rem; - font-weight: 600; - letter-spacing: -0.01em; - color: var(--foreground); -} - -.panel-page-tools { - display: flex; - flex-shrink: 0; - align-items: center; - gap: 0.125rem; -} - /* Mock .icon-sm: compact muted header tools (settings, canvas). */ -.shell-sidebar [data-slot="sidebar-panel-header"] [data-slot="button"], -.shell-sidebar .panel-page-tools [data-slot="button"] { +.shell-sidebar [data-slot="sidebar-panel-header"] [data-slot="button"] { width: 1.8rem; height: 1.8rem; min-height: 0; @@ -428,59 +341,16 @@ select:disabled, background: transparent; } -.shell-sidebar [data-slot="sidebar-panel-header"] [data-slot="button"]:hover, -.shell-sidebar .panel-page-tools [data-slot="button"]:hover { +.shell-sidebar [data-slot="sidebar-panel-header"] [data-slot="button"]:hover { background: color-mix(in srgb, var(--foreground) 5%, transparent); color: var(--foreground); } -.shell-sidebar [data-slot="sidebar-panel-header"] [data-slot="button"] svg, -.shell-sidebar .panel-page-tools [data-slot="button"] svg { +.shell-sidebar [data-slot="sidebar-panel-header"] [data-slot="button"] svg { width: 0.95rem; height: 0.95rem; } -.panel-page-actions { - display: flex; - flex-wrap: wrap; - gap: 0.3rem; -} - -/* Mock .qa-chip: quiet bordered chip on the ground, faint until hover. */ -.panel-page-actions [data-slot="button"] { - height: 1.7rem; - min-height: 0; - padding: 0 0.55rem; - border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - background: var(--background); - color: var(--muted-foreground); - font-size: 0.72rem; - font-weight: 550; - box-shadow: none; -} - -.panel-page-actions [data-slot="button"]:hover { - background: var(--background); - border-color: color-mix(in srgb, var(--foreground) 16%, transparent); - color: var(--foreground); -} - -.panel-band-heading { - margin: 0; - font-size: 0.65rem; - font-weight: 650; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.panel-muted { - margin: 0; - font-size: 0.75rem; - line-height: 1.4; - color: var(--muted-foreground); -} - .panel-stack { display: flex; flex-direction: column; @@ -493,112 +363,6 @@ select:disabled, gap: 0.125rem; } -.panel-filter-row { - display: flex; - flex-wrap: wrap; - align-content: flex-start; - row-gap: 0.375rem; - column-gap: 0.375rem; - padding: 0 0.15rem 0.55rem; -} - -/* Mock .qa-chip for FilterChip pills: square, bordered, faint; the pressed - chip carries the accent wash instead of react-ui's card treatment. */ -.shell-sidebar .panel-filter-row > button { - height: 1.7rem; - padding: 0 0.55rem; - border-radius: 0; - border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - background: var(--background); - color: var(--muted-foreground); - font-size: 0.72rem; - font-weight: 550; - box-shadow: none; -} - -.shell-sidebar .panel-filter-row > button:hover { - background: var(--background); - border-color: color-mix(in srgb, var(--foreground) 16%, transparent); - color: var(--foreground); -} - -.shell-sidebar .panel-filter-row > button[aria-pressed="true"] { - background: color-mix(in srgb, var(--shell-accent) 12%, transparent); - border-color: transparent; - color: var(--shell-accent); -} - -.panel-filter-row > button { - white-space: nowrap; -} - -/* A recent-run row that deep-links to its routine's delivery workbench. */ -.routine-run-row-linked { - cursor: pointer; - transition: background 120ms ease; -} - -.routine-run-row-linked:hover, -.routine-run-row-linked:focus-visible { - background: color-mix(in srgb, var(--primary) 8%, transparent); -} - -/* Workbench rows in the panel band. One-line title + ellipsis; dense to match - the mock chrome. Classnames kept under the historical chat-sidebar- prefix - so package-level row helpers stay reusable without owning layout CSS. */ -.chat-sidebar-row { - display: flex; - align-items: center; - gap: 0.125rem; - min-width: 0; -} - -.chat-sidebar-item { - flex: 1; - min-width: 0; -} - -.chat-sidebar-row-menu-trigger { - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.6rem; - height: 1.6rem; - flex-shrink: 0; - border: 0; - border-radius: var(--ui-radius-sm, 0.25rem); - background: transparent; - color: var(--muted-foreground); - opacity: 0; - transition: - opacity var(--duration-micro, 150ms) var(--ease-out, ease), - background var(--duration-micro, 150ms) var(--ease-out, ease), - color var(--duration-micro, 150ms) var(--ease-out, ease); -} - -.chat-sidebar-row-menu-trigger::after { - content: ""; - position: absolute; - inset: -8px; -} - -.chat-sidebar-row-menu-trigger:hover { - background: color-mix(in srgb, var(--foreground) 8%, transparent); - color: var(--foreground); -} - -.chat-sidebar-row-menu-trigger:focus-visible { - outline: 2px solid var(--ring, var(--primary)); - outline-offset: 2px; -} - -.chat-sidebar-row:hover .chat-sidebar-row-menu-trigger, -.chat-sidebar-row:focus-within .chat-sidebar-row-menu-trigger, -.chat-sidebar-row-menu-trigger[aria-expanded="true"] { - opacity: 1; -} - /* Shared stage top bar — mock `.top`: a fixed 3rem row on every stage surface. Title · dot · subtitle, then per-page actions right-align. Matches `.chat-workbench-header` geometry so conversations and pages read @@ -843,445 +607,81 @@ select:disabled, transform: translateX(0); } -/* Routine canvas pane: peer of the sidebar — soft dividers, list density, - quiet status, no admin-form chrome. */ -.shell-routine-pane { - background: var(--background); +.shell-profile-pane, +.shell-artifact-pane { + display: flex; + height: 100%; + flex-direction: column; + gap: 0.75rem; + padding: 0.75rem; + overflow: auto; + justify-content: flex-start; } -.shell-routine-toolbar { +/* Shared by every canvas pane (profile, artifact, routine): an optional + back control and title on the left, pane-specific actions on the + right — the focus-cycle/close controls for profile and artifact, a + "Runs" shortcut or save-state label for the routine pane. A hairline + divider closes off the header the same way every other row boundary + in this column does. */ +.shell-canvas-pane-header { display: flex; flex-shrink: 0; align-items: center; + justify-content: space-between; gap: 0.5rem; - padding: 0.45rem 0.7rem; + padding-bottom: 0.55rem; border-bottom: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); } -.shell-routine-new { +.shell-canvas-pane-heading { display: flex; + min-width: 0; align-items: center; - gap: 0.45rem; - width: 100%; - padding: 0.55rem 0.75rem; - border: none; - border-bottom: 1px solid color-mix(in srgb, var(--foreground) 8%, transparent); - background: transparent; - color: var(--foreground); - font-size: 0.8125rem; - font-weight: 550; - text-align: left; - cursor: pointer; -} - -.shell-routine-new:hover { - background: color-mix(in srgb, var(--foreground) 4%, transparent); -} - -.shell-routine-row, -.shell-routine-task-row { - border-bottom: 1px solid color-mix(in srgb, var(--foreground) 8%, transparent); -} - -.shell-routine-row-main { - display: flex; - align-items: flex-start; - gap: 0.5rem; - padding: 0.55rem 0.75rem; + gap: 0.25rem; } -.shell-routine-row-open { - display: flex; +.shell-canvas-pane-title { min-width: 0; - flex: 1; - flex-direction: column; - align-items: flex-start; - gap: 0.15rem; - border: none; - background: transparent; - color: inherit; - text-align: left; - cursor: pointer; - padding: 0; -} - -.shell-routine-row-name { - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.8125rem; - font-weight: 550; - letter-spacing: -0.01em; -} - -.shell-routine-row-meta { - max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - font-size: 0.6875rem; - color: var(--muted-foreground); + font-size: 0.85rem; + font-weight: 600; } -.shell-routine-row-actions { +.shell-canvas-pane-actions { display: flex; flex-shrink: 0; align-items: center; gap: 0.25rem; - padding-top: 0.1rem; -} - -.shell-routine-status { - display: inline-flex; - align-items: center; - gap: 0.35rem; - margin-top: 0.1rem; } -.shell-routine-status-label { - font-size: 0.6875rem; - color: var(--muted-foreground); - letter-spacing: 0; - text-transform: none; - font-weight: 450; -} - -.shell-routine-status-label.is-danger { - color: var(--destructive); +.shell-artifact-pane-body { + position: relative; + min-height: 0; + flex: 1 1 auto; + overflow: auto; } -.shell-routine-outcome { +/* Reduced motion needs no rule here: the component library collapses every + animation and transition to a single frame at the foundation (see + `prefers-reduced-motion` in its theme.css), and these transitions inherit + that like any other. */ +/* Brand row: the mark left, "+ New workbench" right — the sidebar's first + line, above the search box and the Workbenches label. */ +.shell-sidebar-brand-row { display: flex; align-items: center; justify-content: space-between; - gap: 0.5rem; - padding: 0.35rem 0.75rem 0.5rem; - font-size: 0.75rem; - color: var(--muted-foreground); - background: color-mix(in srgb, var(--foreground) 3%, transparent); -} - -.shell-routine-section { - margin-top: 0.35rem; - border-top: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); + padding-right: 0.35rem; } - -.shell-routine-section-head { - padding: 0.55rem 0.75rem 0.25rem; -} - -.shell-routine-section-label { - margin: 0; - font-size: 0.65rem; - font-weight: 650; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.shell-routine-quiet-empty { - margin: 0; - padding: 0.15rem 0.75rem 0.85rem; - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.shell-routine-run-row { - display: flex; - align-items: center; - gap: 0.5rem; - width: 100%; - padding: 0.55rem 0.75rem; - border: none; - border-bottom: 1px solid color-mix(in srgb, var(--foreground) 8%, transparent); - background: transparent; - color: inherit; - text-align: left; - cursor: pointer; -} - -.shell-routine-run-row:hover { - background: color-mix(in srgb, var(--foreground) 4%, transparent); -} - -.shell-profile-pane, -.shell-artifact-pane { - display: flex; - height: 100%; - flex-direction: column; - gap: 0.75rem; - padding: 0.75rem; - overflow: auto; - justify-content: flex-start; -} - -/* Shared by every canvas pane (profile, artifact, routine): an optional - back control and title on the left, pane-specific actions on the - right — the focus-cycle/close controls for profile and artifact, a - "Runs" shortcut or save-state label for the routine pane. A hairline - divider closes off the header the same way every other row boundary - in this column does. */ -.shell-canvas-pane-header { - display: flex; - flex-shrink: 0; - align-items: center; - justify-content: space-between; - gap: 0.5rem; - padding-bottom: 0.55rem; - border-bottom: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); -} - -.shell-canvas-pane-heading { - display: flex; - min-width: 0; - align-items: center; - gap: 0.25rem; -} - -.shell-canvas-pane-title { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.85rem; - font-weight: 600; -} - -.shell-canvas-pane-actions { - display: flex; - flex-shrink: 0; - align-items: center; - gap: 0.25rem; -} - -.shell-artifact-presence-status { - padding: 0 0.35rem; - font-size: 0.75rem; - color: var(--muted-foreground); - white-space: nowrap; -} - -.shell-profile-pane-hint { - margin: 0; - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.shell-artifact-pane-body { - position: relative; - min-height: 0; - flex: 1 1 auto; - overflow: auto; -} - -/* Co-viewer cursors: positioned in the pane's own fractional - coordinate space (see `PresenceCursor` in canvas-column.tsx), never - intercepting pointer events — they are a read-only presence signal, not - an interactive layer. */ -.shell-artifact-cursor-layer { - position: absolute; - inset: 0; - pointer-events: none; - overflow: hidden; -} - -.shell-artifact-cursor { - position: absolute; - display: flex; - align-items: center; - gap: 0.3rem; - transform: translate(-2px, -2px); - transition: - left 0.12s ease-out, - top 0.12s ease-out; -} - -.shell-artifact-cursor-dot { - width: 0.55rem; - height: 0.55rem; - flex-shrink: 0; - border-radius: 50%; - background: currentColor; - box-shadow: 0 0 0 1.5px var(--background); -} - -.shell-artifact-cursor-label { - padding: 0.05rem 0.4rem; - border-radius: var(--ui-radius-sm, 0.25rem); - background: currentColor; - color: var(--background); - font-size: 0.6875rem; - font-weight: 600; - white-space: nowrap; -} - -/* Reduced motion needs no rule here: the component library collapses every - animation and transition to a single frame at the foundation (see - `prefers-reduced-motion` in its theme.css), and these transitions inherit - that like any other. */ - -/* Sidebar header: the brand mark, small and quiet — the mark is the brand - moment now (dropped the orange-rail era), not the switcher this - dock replaced. */ -.shell-sidebar-brand-mark { - display: flex; - align-items: center; - width: 100%; - height: 2.25rem; - padding: 0 0.75rem; - margin: 0.3rem 0 0.1rem; - box-sizing: border-box; -} - -.shell-sidebar-brand-mark-icon { - width: 1.75rem; - height: 1.75rem; - color: var(--primary); -} - -/* Footer (reference shape): a Tools row, then the account row that - anchors the pop-up menu. Both are full-width quiet rows; the account - row has no top margin of its own so the two never read as separate - stacked bands. */ -.shell-sidebar-footer-row { - display: flex; - align-items: center; - gap: 0.55rem; - width: 100%; - padding: 0.45rem 0.4rem; - border: 0; - border-radius: 0; - background: transparent; - color: var(--foreground); - font: inherit; - cursor: pointer; - text-align: left; -} -.shell-sidebar-footer-row svg { - width: 1.05rem; - height: 1.05rem; - color: var(--muted-foreground); -} -.shell-sidebar-footer-row:hover, -.shell-sidebar-footer-row[data-active="true"] { - background: color-mix(in srgb, var(--foreground) 8%, transparent); -} -.shell-sidebar-footer-row[data-active="true"] { - box-shadow: inset 2px 0 0 0 var(--shell-accent); -} -.shell-sidebar-footer-row:focus-visible { - outline: 1px solid var(--foreground); - outline-offset: -1px; -} -/* Brand row: the mark left, "+ New workbench" right — the sidebar's first - line, above the search box and the Workbenches label. */ -.shell-sidebar-brand-row { - display: flex; - align-items: center; - justify-content: space-between; - padding-right: 0.35rem; -} -.shell-sidebar-wordmark { - flex: 1; - padding-left: 0.5rem; - font-weight: 800; - font-size: 15px; - letter-spacing: -0.01em; -} - -.shell-sidebar-account-btn { - display: flex; - align-items: center; - gap: 0.55rem; - width: 100%; - padding: 0.45rem 0.4rem; - border: 0; - border-radius: 0; - background: transparent; - cursor: pointer; - color: var(--foreground); - text-align: left; -} - -.shell-sidebar-account-btn:hover, -.shell-sidebar-account-btn[data-state="open"] { - background: color-mix(in srgb, var(--foreground) 8%, transparent); -} - -.shell-sidebar-account-btn:focus-visible { - outline: 1px solid var(--foreground); - outline-offset: -1px; -} - -/* `Avatar`'s own root span carries `role="img"`, not a `data-slot`. */ -.shell-sidebar-account-btn [role="img"] { - flex-shrink: 0; - width: 2.1rem; - height: 2.1rem; - border-radius: 50%; - font-size: 0.7rem; - font-weight: 700; -} - -.shell-sidebar-account-row { - display: flex; - align-items: center; - gap: 0.3rem; - width: 100%; -} - -.shell-sidebar-account-row .shell-sidebar-account-btn { - width: auto; - flex: 1; - min-width: 0; -} - -.shell-sidebar-settings-btn { - flex-shrink: 0; - min-width: 2.5rem; - min-height: 2.5rem; - color: var(--muted-foreground); -} - -.shell-sidebar-settings-btn:hover, -.shell-sidebar-settings-btn[data-active="true"] { - color: var(--foreground); - background: color-mix(in srgb, var(--foreground) 8%, transparent); -} - -.shell-sidebar-account-name { - min-width: 0; - flex: 1; - overflow: hidden; - font-size: 0.85rem; - font-weight: 600; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* The "Weekly usage" menu item: label on the left, the honest summary (or - nothing, when there is no cheap number yet) plus a chevron on the right. */ -.shell-sidebar-account-menu-usage { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; -} - -.shell-sidebar-account-menu-usage-value { - display: flex; - flex-shrink: 0; - align-items: center; - gap: 0.15rem; - color: var(--muted-foreground); - font-size: 0.75rem; - white-space: nowrap; -} - -.shell-sidebar-account-menu-usage-value svg { - width: 0.85rem; - height: 0.85rem; +.shell-sidebar-wordmark { + flex: 1; + padding-left: 0.5rem; + font-weight: 800; + font-size: 15px; + letter-spacing: -0.01em; } /* Settings master-detail inside the stage: fixed-width section nav beside @@ -1464,25 +864,6 @@ select:disabled, } } -.shell-ch-completion { - position: absolute; - right: -0.05rem; - bottom: -0.05rem; - display: grid; - place-items: center; - width: 0.85rem; - height: 0.85rem; - border: 2px solid var(--background); - border-radius: 50%; - background: var(--ok); - color: var(--background); -} - -.shell-ch-completion svg { - width: 0.6rem; - height: 0.6rem; -} - @media (prefers-reduced-motion: reduce) { .wb-av-orbit { animation: none; @@ -1492,49 +873,15 @@ select:disabled, } } -/* Agent-DM identity avatar: zero radius per brand rule, distinct from the - round workbench-participant stack above — a small, deterministic, - brand-compatible palette hashed off the agent's own name (see - `identityColorClass` in `./shell/sidebar-rows.ts`), never a per-agent - custom color. */ -.shell-agent-avatar { - border-radius: 0; -} - -.shell-agent-color-0 { - background: color-mix(in srgb, var(--shell-accent) 22%, transparent); - color: var(--shell-accent); +.shell-ch-meta { + min-width: 0; } -.shell-agent-color-1 { - background: color-mix(in srgb, var(--ok) 22%, transparent); - color: var(--ok); -} - -.shell-agent-color-2 { - background: color-mix(in srgb, var(--warn) 22%, transparent); - color: var(--warn); -} - -.shell-agent-color-3 { - background: color-mix(in srgb, var(--foreground) 16%, transparent); - color: var(--foreground); -} - -.shell-agent-color-4 { - background: color-mix(in srgb, var(--primary) 22%, transparent); - color: var(--primary); -} - -.shell-ch-meta { - min-width: 0; -} - -.shell-ch-name-row { - display: flex; - align-items: center; - gap: 0.35rem; - min-width: 0; +.shell-ch-name-row { + display: flex; + align-items: center; + gap: 0.35rem; + min-width: 0; } .shell-ch-name { @@ -1545,96 +892,6 @@ select:disabled, white-space: nowrap; } -/* Persistent pin glyph on pinned sidebar rows — pinning must be - visible in the list itself, not only as a toast / settings bit. */ -.shell-ch-pin { - flex-shrink: 0; - width: 0.75rem; - height: 0.75rem; - color: var(--muted-foreground, var(--ui-fg-muted)); -} - -.shell-ch-right { - display: flex; - min-width: 1.6rem; - flex-direction: row; - align-items: center; - gap: 0.35rem; - flex-shrink: 0; -} - -.shell-ch-time { - line-height: 1.3rem; - white-space: nowrap; - font-size: 0.66rem; - color: var(--muted-foreground, var(--ui-fg-muted)); -} - -/* Bounded last-message/last-known-status line under the row name — a - workbench's message preview, or an unreachable agent's join caption. */ -.shell-ch-preview { - display: block; - margin-top: 0.15rem; - font-size: 0.72rem; - color: var(--muted-foreground, var(--ui-fg-muted)); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.shell-ch-row-wrap { - position: relative; - display: flex; - align-items: stretch; - width: 100%; -} - -.shell-ch-row-wrap .shell-ch-row { - flex: 1; - min-width: 0; -} - -.shell-ch-row-menu { - position: absolute; - top: 0.5rem; - right: 0.6rem; - opacity: 0; - transition: opacity 120ms ease; -} - -.shell-ch-row-wrap:hover .shell-ch-row-menu, -.shell-ch-row-wrap:focus-within .shell-ch-row-menu, -.shell-ch-row-wrap:has([aria-expanded="true"]) .shell-ch-row-menu, -.shell-ch-row-menu .chat-sidebar-row-menu-trigger { - opacity: 1; -} - -.shell-ch-row-wrap:hover .shell-ch-time, -.shell-ch-row-wrap:focus-within .shell-ch-time, -.shell-ch-row-wrap:has([aria-expanded="true"]) .shell-ch-time { - visibility: hidden; -} - -/* Mock v2 .search: 2rem, square, on the ground — shared by every band. */ -.shell-panel-search { - display: flex; - align-items: center; - gap: 0.45rem; - margin: 0 0.15rem 0.55rem; - height: 2rem; - padding: 0 0.65rem; - border-radius: 0; - border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - background: var(--background); - color: var(--shell-panel-faint); -} - -.shell-panel-search > svg { - width: 0.85rem; - height: 0.85rem; - flex-shrink: 0; -} - /* Bare inputs in the sidebar body (inline rename fields). */ .shell-sidebar [data-slot="sidebar-panel-body"] [data-slot="input"] { height: 2rem; @@ -1648,35 +905,10 @@ select:disabled, box-shadow: none; } -.shell-sidebar .shell-panel-search [data-slot="input"] { - flex: 1; - min-width: 0; - height: 100%; - min-height: 0; - border: none; - background: transparent; - padding: 0; - font-size: 1rem; - font-weight: 400; -} - -.shell-sidebar .shell-panel-search [data-slot="input"]::placeholder, .shell-sidebar [data-slot="sidebar-panel-body"] [data-slot="input"]::placeholder { color: var(--shell-panel-faint); } -.shell-panel-search:focus-within { - outline: none; - border-color: var(--foreground); - box-shadow: 0 0 0 1px var(--foreground); -} - -.shell-sidebar .shell-panel-search [data-slot="input"]:focus-visible { - outline: none; - border: none; - box-shadow: none; -} - .shell-sidebar [data-slot="sidebar-panel-body"] [data-slot="input"]:focus-visible { outline: none; border-color: var(--foreground); @@ -1707,14 +939,12 @@ select:disabled, /* Brand rule: no grey band on the selected row — ground plus a 2px orange left edge. Selector doubles as the kill for react-ui's bg-primary/10 selected wash. */ -.shell-sidebar [data-slot="sidebar-item-row"] > div.bg-primary\/10, .shell-sidebar [data-slot="sidebar-item-row"] > div:has(button[aria-current="true"]) { background-color: var(--background); color: var(--shell-accent); box-shadow: inset 2px 0 0 0 var(--shell-accent); } -.shell-sidebar [data-slot="sidebar-item-row"] > div.bg-primary\/10 > button, .shell-sidebar [data-slot="sidebar-item-row"] > div:has(button[aria-current="true"]) > button { font-weight: 600; } @@ -2070,99 +1300,23 @@ select:disabled, gap: 0.875rem; } -/* The one-click paths sit above the key forms, side by side in a - two-column row so both fit without stacking the page tall — each card - stays square-cornered like the rest of the wizard, with the shell - accent marking the row as the primary suggestion rather than - button-shouting. */ -.onboarding-connect-row { - border: 1px solid var(--border); -} - -.onboarding-connect-card { - display: flex; - align-items: center; - gap: 0.75rem; - padding: 0.875rem; - border: 0; - border-bottom: 1px solid var(--border); - border-left: 3px solid var(--shell-accent); - border-radius: 0; -} - -.onboarding-connect-card:last-child { - border-bottom: 0; -} - -.onboarding-connect-card-text { - flex: 1; - min-width: 0; -} - -.onboarding-connect-card h2 { - margin: 0 0 0.125rem; - font-size: 0.9375rem; - font-weight: 600; -} - -.onboarding-connect-card p { - margin: 0; - font-size: 0.75rem; - line-height: 1.4; - color: var(--muted-foreground); -} - -.onboarding-connect-divider { - display: flex; - align-items: center; - gap: 0.75rem; - font-size: 0.6875rem; - font-weight: 600; - letter-spacing: 0.06em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.onboarding-connect-divider::before, -.onboarding-connect-divider::after { - content: ""; - height: 1px; - flex: 1; - background: var(--border); -} - -.onboarding-name-form, .onboarding-credential-form { display: flex; flex-direction: column; gap: 0.625rem; } -.onboarding-name-form label, .onboarding-credential-form label { font-size: 0.8125rem; font-weight: 600; } -.onboarding-name-form p, .onboarding-credential-form p { margin: 0; font-size: 0.8125rem; color: var(--muted-foreground); } -.onboarding-provider-picker { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; -} - -.onboarding-provider-description { - margin: -0.25rem 0 0; - font-size: 0.8125rem; - color: var(--muted-foreground); -} - /* A quoteable reference id under a consumer-language failure message — small and muted, never the focal point of the card. */ .onboarding-error-refid { @@ -2178,45 +1332,6 @@ select:disabled, color: var(--destructive); } -/* The secondary three providers, tucked behind a subtle expander so the - primary six-card row is what a person sees first — same square-cornered - chrome as the rest of the wizard, not a popover. Full-width so it reads - as part of the picker rather than a separate control. */ -.onboarding-provider-more { - width: 100%; - margin-top: 0.25rem; - border-radius: 0; -} - -.onboarding-provider-more-trigger { - display: inline-flex; - align-items: center; - gap: 0.375rem; - border: none; - background: none; - padding: 0; - font-size: 0.8125rem; - font-weight: 600; - color: var(--muted-foreground); - cursor: pointer; - list-style: none; -} - -.onboarding-provider-more-trigger::-webkit-details-marker { - display: none; -} - -.onboarding-provider-more-trigger:hover { - color: var(--foreground); -} - -.onboarding-provider-more-panel { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - margin-top: 0.75rem; -} - /* Tasteful motion only: a soft entrance on phase change, no looping or attention-seeking animation, fully off under reduced motion. DESIGN.md's motion ceiling is 300ms — stays at 280ms. */ @@ -2251,27 +1366,6 @@ select:disabled, } } -.onboarding-spinner { - height: 1.75rem; - width: 1.75rem; - border: 2px solid var(--border); - border-top-color: var(--shell-accent); - border-radius: 50%; - animation: onboarding-spin 0.8s linear infinite; -} - -@keyframes onboarding-spin { - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: reduce) { - .onboarding-spinner { - animation: none; - } -} - .page-fill { height: auto; flex: 1; @@ -2302,68 +1396,6 @@ select:disabled, /* The Address column is the widest and least essential — drop it before the table would have to squeeze the name columns away. */ -@media (max-width: 56rem) { - .wf-col-address { - display: none; - } -} - -.page-body { - display: flex; - flex: 1; - min-height: 0; - flex-direction: column; -} - -.chat-column { - display: flex; - flex: 1; - min-height: 0; - width: 100%; - max-width: 48rem; - margin: 0 auto; - flex-direction: column; - gap: 0.75rem; - padding: 1rem; -} - -.card-grid { - display: grid; - gap: 1rem; - grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); -} - -.card-link { - display: block; - color: inherit; - text-decoration: none; -} - -.card-footer-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.detail-list { - display: grid; - grid-template-columns: max-content 1fr; - column-gap: 2rem; - row-gap: 0.5rem; - margin: 0; - font-size: 0.875rem; -} - -.detail-list dt { - color: var(--muted-foreground); -} - -.detail-list dd { - margin: 0; -} .query-skeleton { height: 5rem; @@ -2403,102 +1435,28 @@ select:disabled, width: 60%; } -.stat-skeleton { - height: 1.75rem; - width: 3rem; -} +/* Chat surface styling ships with @corbits/chat-ui (see + packages/chat-ui/src/styles.css) — imported in main.tsx alongside + this file, mirroring how @corbits/react-ui/styles.css is consumed. */ -.approvals-list { +/* Insights stage — mock hierarchy: KPI row, panel grid, recent runs. */ +.insights-layout { display: flex; flex-direction: column; gap: 1rem; + width: 100%; } -.flex-col-gap { - display: flex; - flex-direction: column; - gap: 0.375rem; +.insights-note { + margin: 0; + font-size: 0.75rem; + color: var(--muted-foreground); } -.form-label { - font-size: 0.875rem; - font-weight: 500; -} - -.form-row { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.form-hint { - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.form-error { - font-size: 0.875rem; - color: var(--destructive); -} - -.link-button { - background: none; - border: none; - padding: 0; - font: inherit; - color: var(--primary); - cursor: pointer; - text-decoration: underline; -} - -/* Chat surface styling ships with @corbits/chat-ui (see - packages/chat-ui/src/styles.css) — imported in main.tsx alongside - this file, mirroring how @corbits/react-ui/styles.css is consumed. */ - -/* Insights stage — mock hierarchy: KPI row, panel grid, recent runs. */ -.insights-layout { - display: flex; - flex-direction: column; - gap: 1rem; - width: 100%; -} - -.insights-note { - margin: 0; - font-size: 0.75rem; - color: var(--muted-foreground); -} - -/* Insights landing scope switcher: "All workbenches" vs one pill per - sibling workbench. Hidden entirely (renders nothing) when the current - workbench has no parent — see InsightsScopeSwitcher. */ -.insights-scope-switcher { - display: flex; - flex-wrap: wrap; - gap: 0.35rem; -} - -.insights-scope-switcher-option { - padding: 0.3rem 0.65rem; - font-size: 0.75rem; - font-weight: 600; - border: 1px solid color-mix(in srgb, var(--foreground) 14%, transparent); - border-radius: 0; - background: var(--card, var(--background)); - color: var(--foreground); - cursor: pointer; -} - -.insights-scope-switcher-option[data-active="true"] { - border-color: var(--primary); - background: color-mix(in srgb, var(--primary) 14%, transparent); - color: var(--foreground); -} - -.insights-grid { - display: flex; - flex-direction: column; - border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); +.insights-grid { + display: flex; + flex-direction: column; + border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); } .insights-grid > .insights-panel { @@ -2557,10 +1515,6 @@ tr.insights-row-clickable:hover { background-color: color-mix(in srgb, var(--primary) 8%, transparent); } -.insights-table-inert tr:hover { - background-color: transparent; -} - /* Dashboard-density header/cell treatment for the insights data tables — scoped to this wrapper class so react-ui's more spacious Table defaults stand everywhere else. */ @@ -2574,22 +1528,6 @@ tr.insights-row-clickable:hover { padding: 0.55rem 0.7rem; } -/* Global landing's "activity by workbench" mini bars — zero radius and a - hairline track match the rest of this stage; the fill uses the shell - accent sparingly (one thin bar per row), not as a wash. */ -.insights-workbench-bar-track { - height: 4px; - width: 100%; - background: color-mix(in srgb, var(--foreground) 10%, transparent); - border-radius: 0; -} - -.insights-workbench-bar-fill { - height: 100%; - background: var(--shell-accent); - border-radius: 0; -} - .insights-data-table td { padding: 0.55rem 0.7rem; } @@ -2682,129 +1620,6 @@ tr.insights-row-clickable:hover { color: var(--muted-foreground); } -.insights-chain-strip { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - margin: 0; - padding: 0; - list-style: none; -} - -.insights-chain-step { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.5rem 0.75rem; - border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - border-radius: 0; - background: var(--card, var(--background)); - font: inherit; - text-align: left; - color: inherit; -} - -.insights-chain-step-clickable { - cursor: pointer; - transition: background 120ms ease; -} - -.insights-chain-step-clickable:hover { - background: color-mix(in srgb, var(--primary) 8%, transparent); -} - -/* The current step is the one deliberate orange accent in the strip — - every other step stays the neutral card border. */ -.insights-chain-step[data-current="true"] { - border-color: var(--primary); - box-shadow: 0 0 0 1px var(--primary); -} - -.insights-chain-step-title { - display: flex; - flex-direction: column; - gap: 0.1rem; - font-size: 0.8rem; - font-weight: 600; -} - -.insights-chain-step-agent { - font-size: 0.7rem; - font-weight: 500; - color: var(--muted-foreground); -} - -.insights-chain-step-duration { - font-size: 0.75rem; - color: var(--muted-foreground); - font-variant-numeric: tabular-nums; -} - -/* Legacy I2 day-bucket strip (still used by insights-timeline helpers). */ -.insights-timeline { - display: flex; - align-items: flex-end; - gap: 0.35rem; - min-height: 6rem; - padding: 0.5rem 0; -} -.insights-timeline-bar { - flex: 1 1 0; - display: flex; - flex-direction: column; - align-items: center; - justify-content: flex-end; - min-width: 0; - height: 5.5rem; -} -.insights-timeline-fill { - width: 70%; - min-height: 2px; - background: var(--shell-accent); - border-radius: 0; -} -.insights-timeline-label { - margin-top: 0.25rem; - font-size: 0.65rem; - color: var(--muted-foreground); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; -} - -/* Mock v2 .list-item .meta: two-line row copy — name over a faint detail. */ -.panel-row-copy { - display: flex; - min-width: 0; - flex-direction: column; - gap: 0.05rem; - line-height: 1.2; -} - -.panel-row-copy strong { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.8125rem; - font-weight: 500; - color: var(--foreground); -} - -.panel-row-copy > span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.6875rem; - font-weight: 500; - color: var(--shell-panel-faint); -} - -/* The current row's copy follows the row's accent ink. */ -.shell-sidebar [data-slot="sidebar-item-row"] button[aria-current="true"] .panel-row-copy strong { - color: inherit; -} - /* Mock's .chip: a quiet status pill for the stage top bar. Ambient machine activity (`working`) and a resolved state (`ok`) stay outlined and grey/ green; only a human-blocking ask (`needs-you`) gets a filled, high-weight @@ -2838,32 +1653,6 @@ tr.insights-row-clickable:hover { font-weight: 700; } -/* Mock v2 .status: bordered uppercase state chip (on / warn / off). */ -.panel-status { - display: inline-flex; - align-items: center; - flex-shrink: 0; - padding: 0.15rem 0.35rem; - border-radius: 0; - border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - background: transparent; - font-size: 0.625rem; - font-weight: 600; - letter-spacing: 0.02em; - text-transform: uppercase; - line-height: 1; -} - -.panel-status.is-ok { - color: var(--ok); - border-color: color-mix(in srgb, var(--ok) 35%, transparent); - background: color-mix(in srgb, var(--ok) 8%, transparent); -} - -.panel-status.is-muted { - color: var(--shell-panel-faint); -} - /* Global context menu (`shell/context-menu`): the keyboard shortcut hint right-aligned in a menu row. Everything else about the menu's look is react-ui's Menu component. */ @@ -2884,8 +1673,7 @@ tr.insights-row-clickable:hover { scrollbar-gutter: stable; } -.create-agent-panel [data-slot="dialog-body"] > *, -.create-agent-panel .create-agent-advanced-body > * { +.create-agent-panel [data-slot="dialog-body"] > * { min-width: 0; } @@ -2911,123 +1699,9 @@ tr.insights-row-clickable:hover { color: var(--muted-foreground); } -/* The Suggestions row: template cards that prefill name+purpose and run - the same drafting flow as a typed name — see the component's own - `SUGGESTIONS` doc comment on why each one is real. */ -.create-agent-suggestions { - margin-top: 1.25rem; - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.create-agent-suggestions-label { - font-size: 0.65rem; - font-weight: 650; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.create-agent-suggestions-grid { - border: 1px solid var(--border); -} - -.create-agent-suggestion-card { - display: flex; - width: 100%; - flex-direction: column; - gap: 0.2rem; - border: 0; - border-bottom: 1px solid var(--border); - background: var(--background); - padding: 0.6rem 0.7rem; - text-align: left; - font: inherit; - cursor: pointer; -} - -.create-agent-suggestion-card:last-child { - border-bottom: 0; -} - -.create-agent-suggestion-card:hover { - background: var(--muted); -} - -.create-agent-suggestion-title { - font-size: 0.8125rem; - font-weight: 600; -} - -.create-agent-suggestion-desc { - font-size: 0.75rem; - color: var(--muted-foreground); - line-height: 1.35; -} - -/* Create-agent panel's "Advanced" disclosure (`create-agent-panel.tsx`): - handle/model/skills, collapsed by default — same `
` escape- - hatch convention `.settings-connections-advanced` established - (`packages/settings-ui/src/styles.css`). */ -.create-agent-advanced { - border-top: 1px solid var(--border); - padding-top: 0.75rem; - margin-top: 1rem; -} - -.create-agent-advanced > summary { - cursor: pointer; - font-size: 0.875rem; - font-weight: 600; -} - -.create-agent-advanced-body { - margin-top: 0.75rem; - display: flex; - flex-direction: column; - gap: 1rem; -} - -/* the in-shell first-workbench screen — a chat, not a form. One - prompt box, centered both ways in the stage; the stage owns the full - width, no decorative panel. */ -.describe-first-workbench { - display: flex; - align-items: center; - justify-content: center; - padding: 4rem 2rem; - overflow-y: auto; - height: 100%; -} - /* The list's own section label, below the search box (owner's order: logo · search · Workbenches · rows). Quiet, matching the panel-band subheading rhythm. */ -/* The bench-wide "something needs you" line above the row list - (`workbench-list.tsx`'s `NeedsYouSignal`) — the copy carries the - emphasis, the chip only underlines it. */ -.shell-panel-needs-you { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; - margin: 0.55rem 0 0.2rem; - padding: 0.3rem 0.4rem; - font-size: 0.78rem; - font-weight: 600; - color: var(--primary-emphasis); -} - -.shell-panel-list-label { - margin: 0.55rem 0 0.2rem; - padding: 0 0.4rem; - font-size: 0.72rem; - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--muted-foreground); -} .shell-panel-list-empty { margin: 0 0 0.35rem; @@ -3048,218 +1722,6 @@ tr.insights-row-clickable:hover { width: 100%; } -.workbench-timeline-filters { - display: flex; - flex-wrap: wrap; - gap: 0.35rem; -} - -.workbench-timeline-filter-option { - padding: 0.3rem 0.65rem; - font-size: 0.75rem; - font-weight: 600; - border: 1px solid color-mix(in srgb, var(--foreground) 14%, transparent); - border-radius: 0; - background: var(--card, var(--background)); - color: var(--foreground); - cursor: pointer; -} - -.workbench-timeline-filter-option[data-active="true"] { - border-color: var(--primary); - background: color-mix(in srgb, var(--primary) 14%, transparent); -} - -.workbench-timeline-body { - display: grid; - grid-template-columns: minmax(0, 1fr) 16rem; - gap: 1rem; - align-items: start; -} - -.workbench-timeline-day + .workbench-timeline-day { - margin-top: 0.75rem; -} - -.workbench-timeline-day-divider { - display: flex; - align-items: center; - gap: 0.5rem; - margin-bottom: 0.4rem; -} - -.workbench-timeline-day-divider span { - font-size: 0.65rem; - font-weight: 650; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--muted-foreground); - white-space: nowrap; -} - -.workbench-timeline-day-divider::after { - content: ""; - flex: 1; - border-top: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); -} - -.workbench-timeline-rail { - position: relative; - margin: 0; - padding: 0 0 0 1.1rem; - list-style: none; -} - -.workbench-timeline-rail::before { - content: ""; - position: absolute; - left: 3.5px; - top: 0.5rem; - bottom: 0.5rem; - width: 1px; - background: color-mix(in srgb, var(--foreground) 10%, transparent); -} - -.workbench-timeline-entry { - position: relative; - display: flex; - align-items: baseline; - gap: 0.6rem; - padding: 0.4rem 0; - border-bottom: 1px solid color-mix(in srgb, var(--foreground) 6%, transparent); -} - -.workbench-timeline-entry[data-indented="true"] { - margin-left: 1.4rem; -} - -.workbench-timeline-marker { - position: absolute; - left: -1.1rem; - top: 0.65rem; - width: 9px; - height: 9px; - border-radius: 50%; - background: var(--muted-foreground); -} - -.workbench-timeline-entry[data-indented="true"] .workbench-timeline-marker { - border-radius: 0; - transform: rotate(45deg); - width: 7px; - height: 7px; -} - -.workbench-timeline-marker-neutral { - background: var(--muted-foreground); -} - -.workbench-timeline-marker-primary { - background: var(--shell-accent); -} - -.workbench-timeline-marker-success { - background: var(--ok); -} - -.workbench-timeline-marker-danger { - background: var(--destructive); -} - -.workbench-timeline-marker-warn { - background: var(--warn); -} - -.workbench-timeline-marker-info { - background: var(--chart-1); -} - -.workbench-timeline-entry-time { - flex: 0 0 auto; - min-width: 3.5rem; - font-size: 0.72rem; - font-variant-numeric: tabular-nums; - color: var(--muted-foreground); -} - -.workbench-timeline-entry-body { - display: flex; - min-width: 0; - flex-wrap: wrap; - align-items: center; - gap: 0.45rem; - font-size: 0.82rem; -} - -.workbench-timeline-entry-body strong { - font-weight: 650; -} - -.workbench-timeline-entry-excerpt { - min-width: 0; - color: var(--muted-foreground); - overflow-wrap: anywhere; -} - -.workbench-timeline-kpis { - display: flex; - flex-direction: column; - gap: 0.65rem; - padding: 0.85rem 0.9rem; - border: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - background: var(--card, var(--background)); -} - -.workbench-timeline-kpis h3 { - margin: 0; - font-size: 0.65rem; - font-weight: 650; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.workbench-timeline-kpi-day { - padding-top: 0.5rem; - border-top: 1px solid color-mix(in srgb, var(--foreground) 8%, transparent); -} - -.workbench-timeline-kpi-day:first-of-type { - border-top: none; - padding-top: 0; -} - -.workbench-timeline-kpi-day h4 { - margin: 0 0 0.3rem; - font-size: 0.72rem; - font-weight: 600; - color: var(--foreground); -} - -.workbench-timeline-kpi-day dl { - margin: 0; - display: flex; - flex-direction: column; - gap: 0.15rem; -} - -.workbench-timeline-kpi-day dl > div { - display: flex; - justify-content: space-between; - gap: 0.5rem; - font-size: 0.75rem; -} - -.workbench-timeline-kpi-day dt { - color: var(--muted-foreground); -} - -.workbench-timeline-kpi-day dd { - margin: 0; - font-variant-numeric: tabular-nums; - font-weight: 550; -} - /* ---------------- new-workbench picker ---------------- */ /* Screen 1 of the approved mock: one row list, no card grid. The selection rail (`box-shadow: inset 2px 0 0 0 var(--shell-accent)`) @@ -3278,343 +1740,6 @@ tr.insights-row-clickable:hover { line-height: 1.15; } -.new-workbench-picker-sub { - margin: 0.5rem 0 1.75rem; - font-size: 1rem; - line-height: 1.5; - color: var(--muted-foreground); -} - -.new-workbench-picker-not-ready { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 0.75rem; -} - -.new-workbench-picker-not-ready .new-workbench-picker-sub { - margin: 0; -} - -/* The prompt box: the primary act on this screen, so it gets - the heaviest visual weight — a bordered, focus-ringed field with the - send button inline, sized to read as "type here first". */ -.new-workbench-prompt { - display: flex; - flex-direction: column; - align-items: stretch; - border: 1px solid var(--border-strong, var(--border)); - border-radius: 0.875rem; - margin-bottom: 2.5rem; - background: var(--background); - box-shadow: - 0 1px 2px rgb(43 38 39 / 5%), - 0 20px 40px -24px rgb(43 38 39 / 22%); -} - -.new-workbench-prompt:has(.new-workbench-prompt-input:focus) { - border-color: var(--primary); - box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 20%, transparent); -} - -.new-workbench-prompt-input { - width: 100%; - min-width: 0; - resize: none; - border: 0; - background: transparent; - caret-color: var(--shell-accent); - font: inherit; - font-size: 1.25rem; - line-height: 1.45; - padding: 0; -} - -.new-workbench-prompt-input:focus { - outline: none; -} - -.new-workbench-prompt-main { - display: flex; - flex-direction: column; - gap: 1.25rem; - min-height: 10.5rem; - padding: 1.375rem 1.5rem 1rem; -} - -.new-workbench-prompt-examples, -.new-workbench-agent-selection, -.new-workbench-prompt-actions { - display: flex; - align-items: center; - flex-wrap: wrap; -} - -.new-workbench-prompt-examples { - gap: 0.5rem; - font-size: 0.8125rem; - color: var(--muted-foreground); -} - -.new-workbench-prompt-examples button, -.new-workbench-add-agent { - border: 1px solid var(--border); - border-radius: 999px; - background: var(--muted); - color: var(--foreground); - font: inherit; - font-size: 0.8125rem; - line-height: 1rem; - padding: 0.375rem 0.75rem; - cursor: pointer; -} - -.new-workbench-prompt-examples button:hover { - border-color: var(--border-strong, var(--border)); - background: color-mix(in srgb, var(--muted) 72%, var(--foreground)); -} - -.new-workbench-add-agent:hover, -.new-workbench-add-agent[aria-expanded="true"] { - border-color: var(--border-strong, var(--border)); - background: var(--background); -} - -.new-workbench-prompt-examples button.is-selected, -.new-workbench-prompt-examples button.is-selected:hover { - border-color: var(--foreground); - background: var(--foreground); - color: var(--background); -} - -.new-workbench-prompt-actions { - justify-content: space-between; - gap: 1rem; - border-top: 1px solid var(--border); - padding: 0.75rem; -} - -.new-workbench-agent-selection { - position: relative; - flex: 1 1 30rem; - gap: 0.5rem; - min-width: 0; - font-size: 0.8125rem; - font-weight: 600; - color: var(--muted-foreground); -} - -.new-workbench-agent-chip { - display: inline-flex; - align-items: center; - gap: 0.375rem; - border-radius: 999px; - background: var(--foreground); - color: var(--background); - padding: 0.3rem 0.45rem 0.3rem 0.625rem; -} - -.new-workbench-agent-chip button { - width: 1rem; - height: 1rem; - border: 0; - border-radius: 50%; - background: transparent; - color: inherit; - font: inherit; - font-size: 1rem; - line-height: 1; - cursor: pointer; -} - -.new-workbench-agent-status { - font-weight: 400; -} - -.new-workbench-agent-picker { - position: relative; -} - -.new-workbench-add-agent { - border-style: dashed; - background: var(--background); - font-weight: 600; -} - -.new-workbench-agent-popover { - position: absolute; - z-index: 2; - top: calc(100% + 0.5rem); - left: 0; - width: min(21.25rem, calc(100vw - 3rem)); - border: 1px solid var(--border); - border-radius: 0.75rem; - background: var(--background); - box-shadow: 0 20px 40px -16px rgb(43 38 39 / 28%); - padding: 0.5rem; -} - -.new-workbench-agent-search { - width: 100%; - border: 0; - border-bottom: 1px solid var(--border); - background: transparent; - color: var(--foreground); - font: inherit; - font-size: 0.875rem; - padding: 0.5rem; -} - -.new-workbench-agent-search:focus { - outline: none; -} - -.new-workbench-agent-option { - display: grid; - grid-template-columns: minmax(0, 1fr) auto auto; - align-items: center; - width: 100%; - gap: 0.5rem; - border: 0; - border-radius: 0.5rem; - background: transparent; - color: var(--foreground); - font: inherit; - text-align: left; - padding: 0.625rem; - cursor: pointer; -} - -.new-workbench-agent-option.is-active, -.new-workbench-agent-option:hover { - background: var(--muted); -} - -.new-workbench-agent-option-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.875rem; - font-weight: 600; -} - -.new-workbench-agent-option-handle, -.new-workbench-agent-empty { - margin: 0; - color: var(--muted-foreground); - font-size: 0.75rem; - font-weight: 400; -} - -.new-workbench-agent-empty { - padding: 0.75rem 0.5rem; -} - -.new-workbench-prefab-grid { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.new-workbench-template-heading { - display: flex; - justify-content: space-between; - gap: 1rem; - margin-bottom: 0.625rem; - padding: 0 0.25rem; - color: var(--muted-foreground); - font-size: 0.8125rem; -} - -.new-workbench-template-heading span:first-child { - color: var(--foreground); - font-weight: 650; - letter-spacing: 0.08em; - text-transform: uppercase; -} - -.new-workbench-prefab-card { - display: flex; - align-items: center; - gap: 0.875rem; - text-align: left; - border: 1px solid var(--border); - border-radius: 0.625rem; - background: var(--background); - padding: 0.75rem 0.875rem; - font: inherit; - cursor: pointer; -} - -.new-workbench-prefab-card:hover:not([aria-disabled="true"]) { - background: var(--muted); - border-color: var(--border-strong, var(--border)); -} - -.new-workbench-prefab-card[aria-disabled="true"] { - opacity: 0.6; - cursor: not-allowed; -} - -.new-workbench-prefab-card:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.new-workbench-pick-glyph { - width: 2.125rem; - height: 2.125rem; - flex-shrink: 0; - display: grid; - place-items: center; - border: 1px solid var(--border-strong, var(--border)); - color: var(--muted-foreground); -} - -.new-workbench-pick-text { - flex: 1; - min-width: 0; -} - -.new-workbench-pick-title { - display: block; - font-size: 0.9375rem; - font-weight: 640; - letter-spacing: -0.01em; -} - -.new-workbench-pick-promise { - display: block; - font-size: 0.875rem; - color: var(--muted-foreground); - margin-top: 0.1rem; -} - -.new-workbench-empty-channel { - border: 0; - background: transparent; - color: var(--muted-foreground); - font: inherit; - font-size: 0.875rem; - margin-top: 1rem; - padding: 0.25rem; - text-align: left; - cursor: pointer; -} - -.new-workbench-empty-channel span { - color: var(--foreground); - font-weight: 600; - text-decoration: underline; - text-underline-offset: 0.2rem; -} - -.new-workbench-empty-channel:disabled, -.new-workbench-add-agent:disabled { - cursor: not-allowed; - opacity: 0.6; -} - @media (max-width: 640px) { .new-workbench-picker { padding-top: 2rem; @@ -3623,16 +1748,6 @@ tr.insights-row-clickable:hover { .new-workbench-picker h3 { font-size: 2rem; } - - .new-workbench-prompt-actions, - .new-workbench-template-heading { - align-items: flex-start; - flex-direction: column; - } - - .new-workbench-agent-selection { - flex-basis: auto; - } } /* A workbench workbench's left info column: activity, artifacts, approvals and @@ -3676,8 +1791,7 @@ tr.insights-row-clickable:hover { gap: 0.4rem; } .workbench-info-approval-list, -.workbench-info-schedule-list, -.workbench-info-artifact-list { +.workbench-info-schedule-list { display: flex; flex-direction: column; gap: 0.6rem; diff --git a/apps/web/src/chat/styles.css b/apps/web/src/chat/styles.css index ceafb4a7c..a3aaa4284 100644 --- a/apps/web/src/chat/styles.css +++ b/apps/web/src/chat/styles.css @@ -28,3890 +28,787 @@ --avatar-4: #f2b277; } -.chat-workspace { - /* Agent badge uses the theme chart-1 series so light/dark and preset - overlays stay consistent without a package-local hex. */ - --chat-agent-accent: var(--chart-1); +/* Workbench settings — full stage surface (mock § Workbench settings): + workbenches are tenants, so settings replace the whole stage. Breadcrumb top + bar, grouped left nav, panel on the right. Never a dialog. */ - display: flex; - flex: 1; - min-height: 0; - overflow: hidden; +@media (prefers-reduced-motion: no-preference) { } -.chat-workspace-frame { +/* The async-mint setup state: three staggered orange squares (the + brand's zero-radius language) pulsing while the launches finish. + `flex: 1` claims the rest of `.chat-main`'s column instead of sitting + flush under `.chat-workbench-header` with no gap — without it the dots + render right on top of the header's bottom rule. Centering + in that claimed space, plus its own padding, keeps a real gap from the + header at any viewport height; `overflow: auto` is the same + containment `.chat-workspace-frame` needs for the pre-workbench + loading screen, so a very short viewport scrolls this in place rather + than bleeding past its box. */ +.chat-workbench-loading { display: flex; flex: 1; min-height: 0; + flex-direction: column; align-items: center; justify-content: center; - /* Without this, a short viewport lets the centered loader overflow past - this frame's own box and render on top of the page header above it - — `overflow` makes the frame clip/scroll its own content - instead of bleeding into chrome it doesn't own. */ + gap: 0.65rem; + padding: 1.5rem 1rem; + text-align: center; overflow: auto; } -/* Stage top bar — mock `.top`: fixed 3rem row, tight title, trailing actions. */ -.chat-workbench-header { - display: flex; - flex-shrink: 0; - align-items: center; - gap: 0.55rem; - min-height: 3rem; - height: 3rem; - border-bottom: 1px solid var(--border); - padding: 0 0.9rem; - background: var(--background, transparent); +.chat-workbench-loading-brand { + height: 2rem; + width: 2rem; + color: var(--shell-accent); } -.chat-workbench-identity { - display: flex; - min-width: 0; - align-items: center; - gap: 0.55rem; +@media (prefers-reduced-motion: no-preference) { + .chat-workbench-loading-brand { + animation: chat-workbench-loading-brand-breathe 2.4s ease-in-out infinite; + } } -.chat-workbench-title { - margin: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.95rem; - font-weight: 640; +@keyframes chat-workbench-loading-brand-breathe { + 0%, + 100% { + opacity: 0.7; + } + 50% { + opacity: 1; + } } -.chat-workbench-actions { +.chat-workbench-loading-mark { display: flex; - flex-shrink: 0; - align-items: center; - gap: 0.25rem; - margin-left: auto; + gap: 0.3rem; } -/* Workbench settings — full stage surface (mock § Workbench settings): - workbenches are tenants, so settings replace the whole stage. Breadcrumb top - bar, grouped left nav, panel on the right. Never a dialog. */ +.chat-workbench-loading-mark > span { + width: 0.6rem; + height: 0.6rem; + background: var(--primary); +} -.workbench-settings-stage { - display: flex; - flex: 1; - min-width: 0; - min-height: 0; - flex-direction: column; - overflow: hidden; +@media (prefers-reduced-motion: no-preference) { + .chat-workbench-loading-mark > span { + animation: chat-workbench-loading-pulse 1.2s ease-in-out infinite; + } + .chat-workbench-loading-mark > span:nth-child(2) { + animation-delay: 0.15s; + } + .chat-workbench-loading-mark > span:nth-child(3) { + animation-delay: 0.3s; + } } -.workbench-settings-topbar { - display: flex; - flex-shrink: 0; - align-items: center; - justify-content: space-between; - gap: 0.55rem; - min-height: 3rem; - height: 3rem; - border-bottom: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - padding: 0 0.9rem; +@keyframes chat-workbench-loading-pulse { + 0%, + 100% { + opacity: 0.25; + transform: translateY(0); + } + 40% { + opacity: 1; + transform: translateY(-0.3rem); + } } -.workbench-settings-topbar-identity { - display: flex; - align-items: center; - gap: 0.35rem; - min-width: 0; +.chat-workbench-loading-title { + font-size: 0.9375rem; + font-weight: 640; + color: var(--foreground); } -.workbench-settings-breadcrumb { - display: flex; - align-items: center; - gap: 0.35rem; - min-width: 0; - font-size: 0.875rem; +.chat-workbench-loading-tip { + font-size: 0.75rem; + color: var(--muted-foreground); } -.workbench-settings-section-dot { - width: 3px; - height: 3px; - flex-shrink: 0; - background: var(--muted-foreground); +@media (prefers-reduced-motion: no-preference) { + .chat-workbench-loading-tip { + /* DESIGN.md's motion ceiling is 300ms — this was 400ms. */ + animation: chat-workbench-loading-tip-fade 0.28s ease; + } } -.workbench-settings-section-label { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.8125rem; - color: var(--muted-foreground); +@keyframes chat-workbench-loading-tip-fade { + from { + opacity: 0; + } + to { + opacity: 1; + } } -.workbench-settings-breadcrumb button { - appearance: none; - border: 0; - background: transparent; - padding: 0; - color: var(--accent, var(--foreground)); - font: inherit; - font-weight: 640; - white-space: nowrap; - cursor: pointer; +/* Long agent replies are documents, not one-liners: give block-level + markdown a vertical rhythm so bold run-in headers and lists read as + structure instead of a single dense wall. */ +.chat-md-paragraph + .chat-md-paragraph, +.chat-md-paragraph + .chat-md-heading, +.chat-md-heading + .chat-md-paragraph { + margin-top: 0.5rem; } -.workbench-settings-breadcrumb button:hover { - text-decoration: underline; +/* The safe markdown subset a message bubble renders. */ +.chat-md-paragraph, +.chat-md-heading { + display: block; } -.workbench-settings-breadcrumb-sep { - color: var(--muted-foreground); - opacity: 0.6; +.chat-md-heading { + margin: 0; } -.workbench-settings-breadcrumb-current { - color: var(--muted-foreground); - white-space: nowrap; +.chat-md-inline-code { + background: var(--muted, color-mix(in srgb, var(--foreground) 8%, transparent)); + border-radius: var(--ui-radius-sm, 0.25rem); + padding: 0.05rem 0.3rem; + font-size: 0.85em; + /* An unbroken token (a long path, hash, URL) wraps inside the message + column instead of pushing it wider. */ + overflow-wrap: anywhere; } -/* One primary-action language for the whole surface: the - top-bar Save is now a react-ui primary Button, the same visual language - as the agent-detail Save below rather than a bespoke black pill. */ -.workbench-settings-topbar [data-slot="button"]:disabled, -.chat-settings-field-actions [data-slot="button"]:disabled { - /* A disabled primary action reads as neutral-gray, never a dimmed - orange — dimmed orange still reads as "another orange" on a page - where orange means "act here". Scoped to this - surface's own buttons rather than react-ui's shared disabled state. */ - opacity: 1; - border-color: transparent; - background: color-mix(in srgb, var(--foreground) 12%, transparent); - color: var(--muted-foreground); +.chat-md-code-block { + margin: 0.35rem 0; + padding: 0.5rem 0.6rem; + background: var(--muted, color-mix(in srgb, var(--foreground) 8%, transparent)); + border-radius: var(--ui-radius-sm, 0.25rem); + overflow-x: auto; + /* The block itself never grows past its flex column ancestors; a long + unbroken line scrolls inside this box instead. */ + max-width: 100%; + font-size: 0.85em; } -.workbench-settings-shell { - display: flex; - flex: 1; - min-height: 0; - overflow: hidden; +.chat-md-list { + margin: 0.25rem 0; + padding-left: 1.25rem; } -.workbench-settings-nav { - display: flex; - flex-direction: column; - flex-shrink: 0; - gap: 0.1rem; - width: 14rem; - border-right: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - padding: 1rem 0.75rem; - overflow-y: auto; +.chat-md-link { + color: var(--primary, inherit); } -.workbench-settings-nav-group { +.chat-fallback-block { display: flex; flex-direction: column; - gap: 0.125rem; - padding-top: 0.7rem; -} - -.workbench-settings-nav-group-danger { - margin-top: 0.5rem; - border-top: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); + gap: 0.2rem; + margin: 0.15rem 0 0.15rem 2.9rem; + border: 1px solid var(--border); + border-radius: var(--ui-radius-sm, 0.25rem); + padding: 0.4rem 0.55rem; + font-size: 0.75rem; } -.workbench-settings-nav-group-label { - padding: 0.25rem 0.5rem; - font-size: 0.65rem; - font-weight: 650; - letter-spacing: 0.07em; - text-transform: uppercase; +.chat-fallback-label { + font-weight: 600; color: var(--muted-foreground); } -.workbench-settings-nav-item { - display: block; - width: 100%; - border: 0; - background: transparent; - padding: 0.45rem 0.6rem; - text-align: left; - font: inherit; - font-size: 0.875rem; - color: var(--foreground); - cursor: pointer; -} - -.workbench-settings-nav-item:hover { - background: var(--muted); -} - -.workbench-settings-nav-item[aria-current="page"] { - background: var(--muted); - font-weight: 640; +.chat-fallback-body { + margin: 0; + color: var(--muted-foreground); } -.workbench-settings-nav-item:focus-visible, -.workbench-settings-breadcrumb button:focus-visible { - outline: 2px solid var(--ring, var(--primary)); - outline-offset: -2px; +/* Generative UI blocks — mock `.gen`: a bordered card under the sender's + text, square-cornered, with a pulse-dot header. Colors come from theme + tokens so light and dark both hold. */ +.chat-block { + margin: 0.35rem 0 0.35rem 2.9rem; + border: 1px solid var(--border); + border-radius: 0; + background: var(--card, var(--background)); + overflow: hidden; } -.workbench-settings-panel-area { - flex: 1; - min-width: 0; - overflow-y: auto; - padding: 1.25rem 1.5rem; - display: flex; - flex-direction: column; - gap: 0.75rem; +.chat-block-attention { + box-shadow: + 0 0 0 1px color-mix(in srgb, var(--action) 55%, transparent), + 0 4px 16px -4px color-mix(in srgb, var(--action) 30%, transparent); } -.chat-settings-field { +.chat-block-approve-who { display: flex; - flex-direction: column; - gap: 0.375rem; -} - -.chat-settings-field-inline { - flex-direction: row; align-items: center; - justify-content: space-between; + gap: 0.6rem; } -.chat-settings-field-hint { - margin: 0; +.chat-block-approve-tool { + margin: 0.4rem 0 0; + font-family: var(--font-mono); font-size: 0.8125rem; - color: var(--muted-foreground); } -.chat-context-window-control { - display: flex; - flex-direction: column; - gap: 0.375rem; +.chat-block-approve-sep { + flex: 1; } -.chat-context-window-option { +.chat-block-head { display: flex; align-items: center; gap: 0.5rem; + padding: 0.65rem 0.9rem; + border-bottom: 1px solid var(--border); + font-size: 0.75rem; + font-weight: 650; + letter-spacing: 0.02em; + color: var(--chat-agent-accent, var(--primary)); } -.chat-settings-participants-list { - margin: 0; - padding: 0; - list-style: none; - display: flex; - flex-direction: column; - gap: 0.25rem; +.chat-block-pulse { + width: 0.4rem; + height: 0.4rem; + border-radius: 0; + background: currentColor; } -.chat-settings-participant-row { - display: flex; - flex-direction: column; - gap: 0.125rem; - padding: 0.375rem 0; +@media (prefers-reduced-motion: no-preference) { + .chat-block-pulse { + animation: chat-block-pulse 1.6s ease infinite; + } } -.chat-settings-participant-row-main { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; +@keyframes chat-block-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.35; + } } -/* Sidebar-row pattern: a row's "you can act here" signal - is the 2px inset accent edge, never a background wash — the same rule - the shell's own workbench list follows (`apps/web/src/app.css`'s - `.shell-ch-row` / sidebar-item-row overrides). A persistent trailing - chevron marks the row as a drill-in, not a toggle. */ -.chat-settings-agent-picker-row { - display: flex; - width: 100%; - min-height: 2.5rem; - align-items: center; - justify-content: space-between; - gap: 0.5rem; - border: 0; - border-bottom: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - background: transparent; - padding: 0.5rem 0.125rem; - font: inherit; - font-size: 0.875rem; - color: var(--foreground); - text-align: left; - cursor: pointer; - transition: - background 150ms var(--chat-ease), - box-shadow 150ms var(--chat-ease), - color 150ms var(--chat-ease); +.chat-block-body { + padding: 0.9rem 1rem 1rem; } -.chat-settings-agent-picker-row-chevron { - flex-shrink: 0; - width: 1rem; - height: 1rem; +.chat-block-text { + margin: 0 0 0.55rem; + font-size: 0.9rem; + line-height: 1.5; color: var(--muted-foreground); } -.chat-settings-agent-picker-row:hover { - background: color-mix(in srgb, var(--foreground) 5%, transparent); - box-shadow: inset 2px 0 0 0 var(--shell-accent, var(--primary)); -} - -.chat-settings-agent-picker-row:hover .chat-settings-agent-picker-row-chevron { - color: var(--shell-accent, var(--primary)); +.chat-block-text:last-child { + margin-bottom: 0; } -.chat-settings-agent-picker-row:focus-visible { - outline: 2px solid var(--ring, var(--primary)); - outline-offset: -2px; +/* The platform's own account of an approval request -- deliberately the + most visually prominent thing in the card, ahead of the agent's own + framing, so a human never decides against unverified text alone. */ +.chat-block-approve-platform { + margin: 0 0 0.65rem; + padding: 0.6rem 0.7rem; + border: 1px solid var(--border); + background: var(--muted, transparent); } -.chat-settings-agent-back { - display: inline-flex; - align-items: center; - gap: 0.375rem; - align-self: flex-start; - border: 0; - background: transparent; - padding: 0; - font: inherit; - font-size: 0.8125rem; +.chat-block-approve-platform-requester { + margin: 0 0 0.15rem; + font-size: 0.72rem; + font-weight: 650; color: var(--muted-foreground); - cursor: pointer; } -.chat-settings-agent-back:hover { +.chat-block-approve-platform-headline { + margin: 0; + font-size: 0.95rem; + font-weight: 650; color: var(--foreground); } -.chat-settings-agent-back:focus-visible { - outline: 2px solid var(--ring, var(--primary)); - outline-offset: 2px; -} - -.workbench-settings-pane { +.chat-block-approve-args { + margin: 0.5rem 0 0; display: flex; flex-direction: column; - gap: 0.75rem; + gap: 0.3rem; } -/* DRAPER: no radius, no filled box for a callout — a hairline top border - and an uppercase caption label read as "structural note", not a card. */ -.chat-settings-callout { - border: 0; - border-top: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); - border-radius: 0; - padding: 0.75rem 0 0; - background: transparent; +.chat-block-approve-arg { display: flex; - flex-direction: column; - gap: 0.35rem; + gap: 0.4rem; + font-size: 0.8125rem; } -.chat-settings-callout-label { - font-size: 0.65rem; +.chat-block-approve-arg dt { font-weight: 650; - letter-spacing: 0.07em; - text-transform: uppercase; color: var(--muted-foreground); } -.chat-settings-callout p { +.chat-block-approve-arg dd { margin: 0; - font-size: 0.8125rem; - color: var(--muted-foreground); -} - -.chat-settings-callout-danger { - border-top-color: color-mix(in oklab, var(--destructive) 40%, var(--border)); + color: var(--foreground); + overflow-wrap: anywhere; } -.chat-settings-field-actions { - display: flex; - justify-content: flex-end; - gap: 0.5rem; +/* The agent's own framing, demoted -- never the sole description next to + live buttons (see `PlatformDetail` above, which always renders first + when available). */ +.chat-block-agent-note { + font-style: italic; } -.chat-settings-agent-block { - border: 1px solid var(--border); - border-radius: var(--ui-radius-md, 0.375rem); - padding: 0.75rem; +.chat-block-actions { display: flex; - flex-direction: column; - gap: 0.75rem; + flex-wrap: wrap; + gap: 0.4rem; + margin-top: 0.75rem; } -.chat-settings-agent-block-title { - margin: 0; - font-size: 0.875rem; - font-weight: 640; +.chat-block-approve-status { + margin: 0 0 0.4rem; + font-size: 0.8125rem; + font-weight: 650; + color: var(--muted-foreground); } -.chat-settings-agent-block-section { - display: flex; - flex-direction: column; - gap: 0.5rem; - padding-top: 0.75rem; - border-top: 1px solid color-mix(in srgb, var(--foreground) 10%, transparent); +.chat-block-approve-status[data-status="approved"] { + color: var(--foreground); } -.chat-settings-agent-block-section h4 { - margin: 0; - font-size: 0.8125rem; - font-weight: 640; +.chat-block-approve-status[data-status="rejected"] { + color: var(--destructive); } -.chat-settings-capability-list { +.chat-block-steps { display: flex; - flex-wrap: wrap; - gap: 0.375rem; - list-style: none; - margin: 0; - padding: 0; + flex-direction: column; + gap: 0.35rem; } -/* A static fact ("this agent has @corbits/example-tools") is not a button - and should not wear button chrome — no border, a muted tint fill only, - never the bordered-and-transparent look `Button variant="outline"` and - `Badge tone="neutral"` share. */ -.chat-settings-capability-chip { - display: inline-flex; +.chat-block-step { + display: grid; + grid-template-columns: 1.2rem 1fr auto; + gap: 0.45rem; align-items: center; - gap: 0.3rem; - border: 0; - border-radius: 0; - background: color-mix(in srgb, var(--foreground) 8%, transparent); - color: var(--foreground); - padding: 0.2rem 0.5rem; - font-size: 0.75rem; - font-weight: 600; + font-size: 0.8rem; + color: var(--muted-foreground); } -/* Invite agent: a quiet, small, self-start control — without an explicit - `align-self`, flex's default `align-items: stretch` would fill the - whole row. */ -.chat-settings-invite-agent-action { - display: inline-flex; - align-self: flex-start; - align-items: center; - gap: 0.375rem; - border: 0; - background: transparent; - padding: 0.2rem 0; - font: inherit; - font-size: 0.8125rem; - font-weight: 600; - color: var(--muted-foreground); - cursor: pointer; +.chat-block-step-dot { + width: 0.55rem; + height: 0.55rem; + border-radius: 0; + background: var(--border); } -.chat-settings-invite-agent-action svg { - width: 1rem; - height: 1rem; +.chat-block-step[data-state="done"] .chat-block-step-dot { + background: var(--ok, #37904a); } -.chat-settings-invite-agent-action:hover { - color: var(--shell-accent, var(--primary)); +.chat-block-step[data-state="running"] .chat-block-step-dot { + background: var(--chat-agent-accent, var(--primary)); + animation: chat-block-pulse 1.2s ease infinite; } -.chat-settings-invite-agent-action:focus-visible { - outline: 2px solid var(--ring, var(--primary)); - outline-offset: 2px; +.chat-block-step[data-state="error"] .chat-block-step-dot { + background: var(--destructive); } -.chat-settings-capability-add { - display: flex; - flex-wrap: wrap; - align-items: flex-end; - gap: 0.5rem; +.chat-block-step-note { + font-size: 0.68rem; + color: var(--muted-foreground); } -.chat-settings-capability-add .chat-settings-field { - flex: 1 1 10rem; - min-width: 8rem; +.chat-block-metrics { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.4rem; } -/* History: the current version reads through a - hairline inset accent on the row, the same signal every selected/active - row in this surface uses — never a colored Badge. */ -.chat-settings-history-row-current { - box-shadow: inset 2px 0 0 0 var(--shell-accent, var(--primary)); +.chat-block-metric { + padding: 0.5rem 0.55rem; + border: 1px solid var(--border); + border-radius: 0; + background: var(--background); } -.chat-settings-history-current-label { - margin-left: 0.5rem; +.chat-block-metric-label { font-size: 0.65rem; - font-weight: 650; - letter-spacing: 0.07em; + font-weight: 600; text-transform: uppercase; color: var(--muted-foreground); } -.chat-settings-history-repeat-count { - margin-left: 0.35rem; +.chat-block-metric-value { + margin-top: 0.1rem; + font-size: 1.05rem; + font-weight: 680; + color: var(--foreground); +} + +/* Detail text is agent-authored, so it renders neutral unless the block + declares a trend — color is a semantic the schema carries, not a default. */ +.chat-block-metric-detail { + font-size: 0.68rem; color: var(--muted-foreground); } -.chat-settings-choice-row { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); - gap: 0.5rem; +.chat-block-metric-detail[data-trend="up"] { + color: var(--ok, #37904a); } -.chat-textarea { - width: 100%; - min-height: 3.5rem; - resize: vertical; - border: 1px solid var(--border); - border-radius: var(--ui-radius-md, 0.375rem); - background: var(--background); - color: var(--foreground); - font: inherit; - padding: 0.5rem 0.625rem; +.chat-block-metric-detail[data-trend="down"] { + color: var(--destructive); } -.sender-avatar-button, -.chat-bubble-sender-button { - appearance: none; - border: 0; - background: transparent; - padding: 0; - margin: 0; - cursor: pointer; - text-align: left; - color: inherit; - font: inherit; +.chat-block-bars { + display: flex; + flex-direction: column; + gap: 0.4rem; + margin-top: 0.55rem; } -.sender-avatar-button:disabled, -.chat-bubble-sender-button:disabled { - cursor: default; +.chat-block-bar-row { + display: grid; + grid-template-columns: 5.5rem 1fr 2.25rem; + gap: 0.4rem; + align-items: center; + font-size: 0.74rem; + color: var(--muted-foreground); } -.chat-main { - display: flex; - flex: 1; - min-width: 0; - flex-direction: column; +.chat-block-bar { + display: block; + height: 0.45rem; + border-radius: 0; + background: var(--muted, color-mix(in srgb, var(--foreground) 8%, transparent)); overflow: hidden; } -/* Mock `.messages`: tight column, modest horizontal pad. - `gap` is the visible breathing space between groups; `align-items` - keeps every group at full width so its own left gutter (2.9rem) reads - consistently. `flex-shrink: 0` on the group (below) is what stops a - flex column from collapsing two groups onto the same line when content - overflows into the sibling group. */ -.chat-timeline { - display: flex; - flex: 1; - min-width: 0; - min-height: 0; - flex-direction: column; - align-items: stretch; - gap: 0.35rem; - overflow-y: auto; - padding: 0.75rem clamp(0.85rem, 3vw, 1.75rem) 1rem; +.chat-block-bar-fill { + display: block; + height: 100%; + background: var(--chat-agent-accent, var(--primary)); } -/* Positions jump-to-latest over the live tail without putting it in the - scroll flow. Claims the same flex:1 slot `.chat-timeline` used to as - `.chat-main`'s column child. */ -.chat-timeline-shell { - position: relative; - display: flex; - flex: 1; - min-width: 0; - min-height: 0; - flex-direction: column; -} - -.chat-jump-to-latest { - position: absolute; - z-index: 2; - left: 50%; - bottom: 0.75rem; - display: inline-flex; - min-height: 2.5rem; - align-items: center; - justify-content: center; - gap: 0.35rem; - border: 1px solid var(--border); - border-radius: 999px; - background: var(--background); - padding: 0 0.85rem; - color: var(--foreground); - font: inherit; - font-size: 0.8125rem; - font-weight: 600; - box-shadow: 0 2px 10px color-mix(in srgb, black 14%, transparent); - cursor: pointer; - transform: translateX(-50%); -} - -.chat-jump-to-latest svg { - width: 0.9rem; - height: 0.9rem; -} - -.chat-jump-to-latest:hover { - background: color-mix(in srgb, var(--foreground) 4%, var(--background)); -} - -.chat-jump-to-latest:focus-visible { - outline: 2px solid var(--primary); - outline-offset: 2px; -} - -.chat-jump-to-latest:active { - transform: translateX(-50%) scale(0.97); -} - -@media (prefers-reduced-motion: no-preference) { - @keyframes chat-jump-to-latest-enter { - from { - opacity: 0; - transform: translateX(-50%) translateY(4px); - } - to { - opacity: 1; - transform: translateX(-50%) translateY(0); - } - } - - .chat-jump-to-latest { - animation: chat-jump-to-latest-enter var(--duration-standard, 200ms) var(--chat-ease); - } -} - -/* The async-mint setup state: three staggered orange squares (the - brand's zero-radius language) pulsing while the launches finish. - `flex: 1` claims the rest of `.chat-main`'s column instead of sitting - flush under `.chat-workbench-header` with no gap — without it the dots - render right on top of the header's bottom rule. Centering - in that claimed space, plus its own padding, keeps a real gap from the - header at any viewport height; `overflow: auto` is the same - containment `.chat-workspace-frame` needs for the pre-workbench - loading screen, so a very short viewport scrolls this in place rather - than bleeding past its box. */ -.chat-workbench-loading { - display: flex; - flex: 1; - min-height: 0; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 0.65rem; - padding: 1.5rem 1rem; - text-align: center; - overflow: auto; -} - -.chat-workbench-loading-brand { - height: 2rem; - width: 2rem; - color: var(--shell-accent); -} - -@media (prefers-reduced-motion: no-preference) { - .chat-workbench-loading-brand { - animation: chat-workbench-loading-brand-breathe 2.4s ease-in-out infinite; - } -} - -@keyframes chat-workbench-loading-brand-breathe { - 0%, - 100% { - opacity: 0.7; - } - 50% { - opacity: 1; - } -} - -.chat-workbench-loading-mark { - display: flex; - gap: 0.3rem; -} - -.chat-workbench-loading-mark > span { - width: 0.6rem; - height: 0.6rem; - background: var(--primary); -} - -@media (prefers-reduced-motion: no-preference) { - .chat-workbench-loading-mark > span { - animation: chat-workbench-loading-pulse 1.2s ease-in-out infinite; - } - .chat-workbench-loading-mark > span:nth-child(2) { - animation-delay: 0.15s; - } - .chat-workbench-loading-mark > span:nth-child(3) { - animation-delay: 0.3s; - } -} - -@keyframes chat-workbench-loading-pulse { - 0%, - 100% { - opacity: 0.25; - transform: translateY(0); - } - 40% { - opacity: 1; - transform: translateY(-0.3rem); - } -} - -.chat-workbench-loading-title { - font-size: 0.9375rem; - font-weight: 640; - color: var(--foreground); -} - -.chat-workbench-loading-tip { - font-size: 0.75rem; - color: var(--muted-foreground); -} - -@media (prefers-reduced-motion: no-preference) { - .chat-workbench-loading-tip { - /* DESIGN.md's motion ceiling is 300ms — this was 400ms. */ - animation: chat-workbench-loading-tip-fade 0.28s ease; - } -} - -@keyframes chat-workbench-loading-tip-fade { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -.chat-timeline-empty { - display: flex; - flex: 1; - align-items: center; - justify-content: center; -} - -.chat-agent-badge { - display: inline-flex; - align-items: center; - gap: 0.2rem; - border-radius: var(--ui-radius-sm, 0.25rem); - background: color-mix(in srgb, var(--chat-agent-accent) 14%, transparent); - padding: 0.05rem 0.35rem; - font-size: 0.625rem; - font-weight: 650; - letter-spacing: 0.02em; - text-transform: uppercase; - color: var(--chat-agent-accent); -} - -/* Mock `.msg`: flat av | body row — not fat chat bubbles. */ -.chat-bubble-row { - display: flex; - align-items: flex-start; - gap: 0.65rem; - max-width: 100%; - padding: 0.3rem 0.45rem; - border-radius: var(--ui-radius-md, 0.375rem); - transition: background 120ms var(--chat-ease); -} - -.chat-bubble-row:hover { - background: color-mix(in srgb, var(--foreground) 4%, transparent); -} - -/* The message header's avatar chip — sizing, tone color, and initials - come from react-ui's `Avatar`; this only positions it within the row. */ -.sender-avatar { - margin-top: 0.1rem; - border-radius: 50%; -} - -/* A SenderAvatar rendered without the profile-button wrapper (the - streaming bubble) still occupies the same avatar column, so text - never shifts when the streamed reply finalizes into a persisted - message. */ -.chat-bubble-row > .sender-avatar-wrap { - width: 2.15rem; - display: inline-grid; - place-items: start center; - flex-shrink: 0; -} - -/* Wraps a message sender's avatar with workbench for the shared-workbench - tenant-monogram badge, positioned bottom-right — only - rendered when the server annotates the sender with a - `tenantMonogram` (see `resolveMessageSenderTenant` in - `packages/chat/src/routes.ts`). */ -.sender-avatar-wrap { - position: relative; - display: inline-flex; -} - -.chat-sender-tenant-badge { - position: absolute; - right: -0.2rem; - bottom: -0.05rem; - display: grid; - place-items: center; - min-width: 0.95rem; - height: 0.95rem; - padding: 0 0.1rem; - border-radius: 0; - border: 1.5px solid var(--background); - background: var(--chat-agent-accent, var(--primary)); - color: var(--primary-foreground); - font-size: 0.5rem; - font-weight: 700; - line-height: 1; -} - -.sender-avatar-button { - position: relative; - display: grid; - place-items: start center; - width: 2.15rem; -} - -.sender-avatar-button::after { - content: ""; - position: absolute; - inset: -8px; -} - -.chat-bubble { - display: flex; - flex: 1; - min-width: 0; - max-width: 100%; - flex-direction: column; - gap: 0.15rem; - border: 0; - border-radius: 0; - background: transparent; - padding: 0; -} - -.chat-bubble-head { - display: flex; - min-width: 0; - flex-wrap: wrap; - align-items: baseline; - gap: 0.45rem; - margin-bottom: 0.1rem; -} - -.chat-bubble-sender { - display: inline-flex; - align-items: center; - gap: 0.35rem; - font-size: 0.875rem; - font-weight: 640; - letter-spacing: -0.01em; - color: var(--foreground); -} - -.chat-bubble-text { - margin: 0; - font-size: max(var(--text-base), 16px); - font-weight: var(--font-weight-medium); - line-height: 1.55; - white-space: pre-wrap; - word-break: break-word; - color: var(--foreground); - /* Reading width, not viewport width: on a wide window an unbounded - line runs 200+ characters and long agent replies become a wall. - ~72ch is the ceiling comfortable prose tolerates. */ - max-width: 76ch; -} - -/* Long agent replies are documents, not one-liners: give block-level - markdown a vertical rhythm so bold run-in headers and lists read as - structure instead of a single dense wall. */ -.chat-md-paragraph + .chat-md-paragraph, -.chat-md-paragraph + .chat-md-heading, -.chat-md-heading + .chat-md-paragraph { - margin-top: 0.5rem; -} - -.chat-bubble-text strong { - font-weight: 640; -} - -.chat-bubble-time { - font-size: 0.6875rem; - color: var(--muted-foreground); - align-self: baseline; -} - -/* The safe markdown subset a message bubble renders. */ -.chat-md-paragraph, -.chat-md-heading { - display: block; -} - -.chat-md-heading { - margin: 0; -} - -.chat-md-inline-code { - background: var(--muted, color-mix(in srgb, var(--foreground) 8%, transparent)); - border-radius: var(--ui-radius-sm, 0.25rem); - padding: 0.05rem 0.3rem; - font-size: 0.85em; - /* An unbroken token (a long path, hash, URL) wraps inside the message - column instead of pushing it wider. */ - overflow-wrap: anywhere; -} - -.chat-md-code-block { - margin: 0.35rem 0; - padding: 0.5rem 0.6rem; - background: var(--muted, color-mix(in srgb, var(--foreground) 8%, transparent)); - border-radius: var(--ui-radius-sm, 0.25rem); - overflow-x: auto; - /* The block itself never grows past its flex column ancestors; a long - unbroken line scrolls inside this box instead. */ - max-width: 100%; - font-size: 0.85em; -} - -.chat-md-list { - margin: 0.25rem 0; - padding-left: 1.25rem; -} - -.chat-md-link { - color: var(--primary, inherit); -} - -/* Grouped continuation: the run's second-and-later messages from - the same author drop the avatar/name header entirely — indented to sit - under the name above, with the timestamp tucked into the avatar gutter - and only revealed on hover/focus rather than shown on every line. */ -/* Base row padding (0.45rem) + avatar column (2.15rem) + row gap - (0.65rem) — all three, so continuation text sits exactly under the - header message's text, not 0.45rem left of it. */ -.chat-bubble-row[data-grouped="true"] { - padding-left: calc(0.45rem + 2.15rem + 0.65rem); - position: relative; -} - -.chat-bubble-time-grouped { - position: absolute; - left: 0.45rem; - top: 0.35rem; - width: 2.15rem; - text-align: right; - padding-right: 0.3rem; - font-size: 0.625rem; - color: var(--muted-foreground); - opacity: 0; - transition: opacity 120ms var(--chat-ease); -} - -.chat-bubble-row[data-grouped="true"]:hover .chat-bubble-time-grouped, -.chat-bubble-row[data-grouped="true"]:focus-within .chat-bubble-time-grouped { - opacity: 1; -} - -/* A classified inference failure's reply already says what went - wrong; this is a low-key next step, not a second alert — a small - outline button rather than a bare underlined link, matching the - pending-bubble's failed-state buttons. */ -.chat-bubble-fix-connection { - margin-top: 0.375rem; - align-self: flex-start; -} - -.chat-bubble-row[data-own="true"] .chat-bubble-fix-connection { - align-self: flex-end; -} - -/* The owner asked for own messages back on the right, evaluated - per viewer (`isOwn` in timeline.tsx already keys off `currentUser`, never - a fixed account) — reversing flat, no-alignment timeline. Still - the row mirrors, so the avatar sits on the right and the header/text shrink to their own content - width against that same edge instead of stretching the full row. `.chat- - block` (approve/connect-service/connect-github/poll/form/steps) and - `.chat-trace` rows render as siblings of the bubble, not inside - it, and deliberately stay anchored under the left avatar gutter for every - author — mirroring a stack of tool chips or an approval card to the right - read as clutter next to the composer and doesn't match any other - approval-flow surface in the app. */ -.chat-bubble-row[data-own="true"] { - flex-direction: row-reverse; -} - -.chat-bubble-row[data-own="true"] .chat-bubble { - align-items: flex-end; -} - -.chat-bubble-row[data-own="true"] .chat-bubble-head { - justify-content: flex-end; -} - -.chat-bubble-row[data-own="true"][data-grouped="true"] { - padding-left: 0.45rem; - padding-right: calc(0.45rem + 2.15rem + 0.65rem); -} - -.chat-bubble-row[data-own="true"] .chat-bubble-time-grouped { - left: auto; - right: 0.45rem; - padding-right: 0; - padding-left: 0.3rem; - text-align: left; -} - -/* An own message's file/artifact attachment mirrors alongside its bubble — - `inline-flex` alone won't respond to `margin-left:auto` (it's an atomic - inline-level box, not a block one), so own attachments switch to `flex` - only for the own case, matching the bubble's own mirrored edge. */ -.chat-message-group[data-own="true"] .chat-artifact-chip { - display: flex; - margin-left: auto; -} - -/* Optimistic sends: a message the reader just sent renders - exactly like a normal message immediately — full opacity, no restyling — - with only the small clock glyph next to its header while in flight, and - an inline "Not sent — retry" row appended below it (never replacing or - dimming the bubble itself) once it fails. */ - -.chat-pending-glyph { - display: inline-flex; - align-items: center; - margin-left: auto; - color: var(--muted-foreground); -} - -.chat-pending-glyph svg { - width: 0.8rem; - height: 0.8rem; -} - -.chat-pending-failed-row { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 0.4rem; - margin-top: 0.45rem; - font-size: 0.75rem; -} - -.chat-pending-failed-label { - font-size: 0.6875rem; - font-weight: 650; - letter-spacing: 0.01em; - text-transform: uppercase; - color: var(--destructive); -} - -.chat-pending-retry, -.chat-pending-discard { - height: auto; - padding: 0.15rem 0.5rem; - font-size: 0.75rem; -} - -.chat-pending-discard { - color: var(--muted-foreground); -} - -.chat-day-divider { - display: flex; - align-items: center; - gap: 0.75rem; - margin: 0.65rem 0 0.35rem; - font-size: 0.6875rem; - font-weight: 600; - color: var(--muted-foreground); -} - -.chat-day-divider::before, -.chat-day-divider::after { - content: ""; - flex: 1; - height: 1px; - background: var(--border); -} - -.chat-event-line { - display: flex; - align-items: center; - justify-content: flex-start; - gap: 0.5rem; - /* Same left gutter as failed-turn / gen-ui blocks — system - notices sit under the message column, never centered and never on - the signed-in user's right edge. */ - margin: 0.15rem 0 0.15rem 2.9rem; - padding: 0.2rem 0.5rem; - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.chat-event-line a { - color: inherit; - text-decoration: underline; - text-underline-offset: 0.12em; -} - -.chat-event-time { - font-size: 0.6875rem; - opacity: 0.85; -} - -.chat-fallback-block { - display: flex; - flex-direction: column; - gap: 0.2rem; - margin: 0.15rem 0 0.15rem 2.9rem; - border: 1px solid var(--border); - border-radius: var(--ui-radius-sm, 0.25rem); - padding: 0.4rem 0.55rem; - font-size: 0.75rem; -} - -.chat-fallback-label { - font-weight: 600; - color: var(--muted-foreground); -} - -.chat-fallback-body { - margin: 0; - color: var(--muted-foreground); -} - -/* Generative UI blocks — mock `.gen`: a bordered card under the sender's - text, square-cornered, with a pulse-dot header. Colors come from theme - tokens so light and dark both hold. */ -.chat-block { - margin: 0.35rem 0 0.35rem 2.9rem; - border: 1px solid var(--border); - border-radius: 0; - background: var(--card, var(--background)); - overflow: hidden; -} - -.chat-block-attention { - box-shadow: - 0 0 0 1px color-mix(in srgb, var(--action) 55%, transparent), - 0 4px 16px -4px color-mix(in srgb, var(--action) 30%, transparent); -} - -.chat-block-approve-who { - display: flex; - align-items: center; - gap: 0.6rem; -} - -.chat-block-approve-tool { - margin: 0.4rem 0 0; - font-family: var(--font-mono); - font-size: 0.8125rem; -} - -.chat-block-approve-sep { - flex: 1; -} - -.chat-block-head { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.65rem 0.9rem; - border-bottom: 1px solid var(--border); - font-size: 0.75rem; - font-weight: 650; - letter-spacing: 0.02em; - color: var(--chat-agent-accent, var(--primary)); -} - -.chat-block-pulse { - width: 0.4rem; - height: 0.4rem; - border-radius: 0; - background: currentColor; -} - -@media (prefers-reduced-motion: no-preference) { - .chat-block-pulse { - animation: chat-block-pulse 1.6s ease infinite; - } -} - -@keyframes chat-block-pulse { - 0%, - 100% { - opacity: 1; - } - 50% { - opacity: 0.35; - } -} - -.chat-block-body { - padding: 0.9rem 1rem 1rem; -} - -.chat-block-text { - margin: 0 0 0.55rem; - font-size: 0.9rem; - line-height: 1.5; - color: var(--muted-foreground); -} - -.chat-block-text:last-child { - margin-bottom: 0; -} - -/* The platform's own account of an approval request -- deliberately the - most visually prominent thing in the card, ahead of the agent's own - framing, so a human never decides against unverified text alone. */ -.chat-block-approve-platform { - margin: 0 0 0.65rem; - padding: 0.6rem 0.7rem; - border: 1px solid var(--border); - background: var(--muted, transparent); -} - -.chat-block-approve-platform-requester { - margin: 0 0 0.15rem; - font-size: 0.72rem; - font-weight: 650; - color: var(--muted-foreground); -} - -.chat-block-approve-platform-headline { - margin: 0; - font-size: 0.95rem; - font-weight: 650; - color: var(--foreground); -} - -.chat-block-approve-args { - margin: 0.5rem 0 0; - display: flex; - flex-direction: column; - gap: 0.3rem; -} - -.chat-block-approve-arg { - display: flex; - gap: 0.4rem; - font-size: 0.8125rem; -} - -.chat-block-approve-arg dt { - font-weight: 650; - color: var(--muted-foreground); -} - -.chat-block-approve-arg dd { - margin: 0; - color: var(--foreground); - overflow-wrap: anywhere; -} - -/* The agent's own framing, demoted -- never the sole description next to - live buttons (see `PlatformDetail` above, which always renders first - when available). */ -.chat-block-agent-note { - font-style: italic; -} - -.chat-block-actions { - display: flex; - flex-wrap: wrap; - gap: 0.4rem; - margin-top: 0.75rem; -} - -.chat-block-risk { - display: inline-flex; - align-items: center; - gap: 0.3rem; - margin-bottom: 0.45rem; - font-size: 0.72rem; - font-weight: 650; -} - -.chat-block-risk svg { - width: 0.85rem; - height: 0.85rem; -} - -.chat-block-risk[data-risk="low"] { - color: var(--muted-foreground); -} - -.chat-block-risk[data-risk="medium"] { - color: var(--warn, #b7791f); -} - -.chat-block-risk[data-risk="high"] { - color: var(--destructive); -} - -.chat-block-risk-note { - font-weight: 500; - color: var(--muted-foreground); -} - -.chat-block-approve-status { - margin: 0 0 0.4rem; - font-size: 0.8125rem; - font-weight: 650; - color: var(--muted-foreground); -} - -.chat-block-approve-status[data-status="approved"] { - color: var(--foreground); -} - -.chat-block-approve-status[data-status="rejected"] { - color: var(--destructive); -} - -.chat-block-steps { - display: flex; - flex-direction: column; - gap: 0.35rem; -} - -.chat-block-step { - display: grid; - grid-template-columns: 1.2rem 1fr auto; - gap: 0.45rem; - align-items: center; - font-size: 0.8rem; - color: var(--muted-foreground); -} - -.chat-block-step-dot { - width: 0.55rem; - height: 0.55rem; - border-radius: 0; - background: var(--border); -} - -.chat-block-step[data-state="done"] .chat-block-step-dot { - background: var(--ok, #37904a); -} - -.chat-block-step[data-state="running"] .chat-block-step-dot { - background: var(--chat-agent-accent, var(--primary)); - animation: chat-block-pulse 1.2s ease infinite; -} - -.chat-block-step[data-state="error"] .chat-block-step-dot { - background: var(--destructive); -} - -.chat-block-step-note { - font-size: 0.68rem; - color: var(--muted-foreground); -} - -.chat-block-metrics { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 0.4rem; -} - -.chat-block-metric { - padding: 0.5rem 0.55rem; - border: 1px solid var(--border); - border-radius: 0; - background: var(--background); -} - -.chat-block-metric-label { - font-size: 0.65rem; - font-weight: 600; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.chat-block-metric-value { - margin-top: 0.1rem; - font-size: 1.05rem; - font-weight: 680; - color: var(--foreground); -} - -/* Detail text is agent-authored, so it renders neutral unless the block - declares a trend — color is a semantic the schema carries, not a default. */ -.chat-block-metric-detail { - font-size: 0.68rem; - color: var(--muted-foreground); -} - -.chat-block-metric-detail[data-trend="up"] { - color: var(--ok, #37904a); -} - -.chat-block-metric-detail[data-trend="down"] { - color: var(--destructive); -} - -.chat-block-bars { - display: flex; - flex-direction: column; - gap: 0.4rem; - margin-top: 0.55rem; -} - -.chat-block-bar-row { - display: grid; - grid-template-columns: 5.5rem 1fr 2.25rem; - gap: 0.4rem; - align-items: center; - font-size: 0.74rem; - color: var(--muted-foreground); -} - -.chat-block-bar { - display: block; - height: 0.45rem; - border-radius: 0; - background: var(--muted, color-mix(in srgb, var(--foreground) 8%, transparent)); - overflow: hidden; -} - -.chat-block-bar-fill { - display: block; - height: 100%; - background: var(--chat-agent-accent, var(--primary)); -} - -.chat-block-choices { - display: flex; - flex-direction: column; - gap: 0.35rem; -} - -.chat-block-choice { - display: flex; - align-items: center; - gap: 0.5rem; - width: 100%; - padding: 0.5rem 0.6rem; - border: 1px solid var(--border); - border-radius: 0; - background: var(--background); - text-align: left; - font: inherit; - font-size: 0.8125rem; - color: var(--muted-foreground); -} - -.chat-block-choice:disabled { - cursor: default; -} - -.chat-block-poll-choice { - flex-wrap: wrap; - row-gap: 0.3rem; -} - -.chat-block-poll-choice:not(:disabled) { - cursor: pointer; -} - -.chat-block-poll-choice[data-selected="true"] { - border-color: var(--chat-agent-accent, var(--primary)); - color: var(--foreground); -} - -.chat-block-poll-choice-label { - flex: 1 1 auto; - display: flex; - align-items: center; - gap: 0.4rem; -} - -.chat-block-poll-your-vote { - font-size: 0.68rem; - font-weight: 650; - letter-spacing: 0.02em; - color: var(--chat-agent-accent, var(--primary)); -} - -.chat-block-poll-choice .chat-block-bar { - flex: 1 1 6rem; - min-width: 4rem; -} - -.chat-block-poll-choice-count { - flex: 0 0 auto; - font-size: 0.72rem; - color: var(--muted-foreground); -} - -.chat-block-question-subtitle { - margin: -0.15rem 0 0.3rem; - color: var(--muted-foreground); -} - -.chat-block-question-choice:not(:disabled) { - cursor: pointer; -} - -.chat-block-question-letter { - flex: 0 0 auto; - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.4rem; - height: 1.4rem; - border: 1px solid var(--border); - font-size: 0.7rem; - font-weight: 650; - color: var(--muted-foreground); -} - -.chat-block-question-freetext { - display: flex; - flex-direction: column; - gap: 0.35rem; - margin-top: 0.5rem; -} - -.chat-block-question-freetext label { - font-size: 0.68rem; - font-weight: 650; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.chat-block-question-freetext input[type="text"] { - width: 100%; - padding: 0.45rem 0.55rem; - border: 1px solid var(--border); - border-radius: 0; - background: var(--background); - color: var(--foreground); - font: inherit; - font-size: 0.84rem; -} - -.chat-block-question-freetext-submit { - align-self: flex-start; - padding: 0.4rem 0.75rem; - border: 1px solid var(--border); - border-radius: 0; - background: var(--background); - font: inherit; - font-size: 0.78rem; - font-weight: 650; - color: var(--foreground); -} - -.chat-block-question-freetext-submit:not(:disabled) { - cursor: pointer; -} - -.chat-block-question-answered { - display: flex; - align-items: flex-start; - gap: 0.5rem; -} - -.chat-block-question-check { - flex: 0 0 auto; - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.4rem; - height: 1.4rem; - border: 1px solid var(--chat-agent-accent, var(--primary)); - color: var(--chat-agent-accent, var(--primary)); - font-size: 0.8rem; - font-weight: 650; -} - -.chat-block-question-answered-label { - margin: 0 0 0.15rem; - font-size: 0.68rem; - font-weight: 650; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.chat-block-question-answered-value { - margin: 0; - font-size: 0.84rem; - color: var(--foreground); -} - -.chat-block-field-error { - color: var(--destructive); - font-size: 0.72rem; - margin: 0.2rem 0 0; -} - -.chat-block-form-submitted { - font-size: 0.76rem; - font-weight: 650; - color: var(--chat-agent-accent, var(--primary)); -} - -.chat-block-field { - margin-bottom: 0.55rem; -} - -.chat-block-field label { - display: block; - margin-bottom: 0.25rem; - font-size: 0.68rem; - font-weight: 650; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.chat-block-field input[type="text"], -.chat-block-field textarea, -.chat-block-field select { - width: 100%; - padding: 0.45rem 0.55rem; - border: 1px solid var(--border); - border-radius: 0; - background: var(--background); - color: var(--foreground); - font: inherit; - font-size: 0.84rem; -} - -.chat-block-required { - color: var(--destructive); -} - -/* The workbench's onboarding scene card: one constant header (what this workbench - is for, the promise under it, where you are in the walkthrough) above a - body that flips between connect, pick, and reviewing. Step state is - carried in words inside each row, never by color alone — DESIGN.md - State Pills. */ -.chat-block-scene-promise { - margin: 0 0 0.6rem; - font-size: 0.9rem; - line-height: 1.45; - color: var(--foreground); -} - -.chat-block-scene-steps { - margin: 0 0 0.55rem; - padding: 0; - list-style: none; - border: 1px solid var(--border); - background: var(--background); -} - -.chat-block-scene-step { - display: flex; - align-items: baseline; - gap: 0.5rem; - padding: 0.4rem 0.7rem; - border-bottom: 1px solid var(--border); - font-size: 0.8125rem; - color: var(--muted-foreground); -} - -.chat-block-scene-step:last-child { - border-bottom: 0; -} - -.chat-block-scene-step[data-state="current"] { - color: var(--foreground); - font-weight: 650; -} - -.chat-block-scene-step-mark { - margin-left: auto; - font-size: 0.6875rem; - font-weight: 650; - letter-spacing: 0.02em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.chat-block-scene-step[data-state="current"] .chat-block-scene-step-mark { - color: var(--chat-agent-accent, var(--primary)); -} - -.chat-block-scene-step[data-state="done"] .chat-block-scene-step-mark { - color: var(--ok, #37904a); -} - -.chat-block-scene-why { - margin: 0 0 0.65rem; -} - -.chat-block-scene-status { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -.chat-block-scene-pick-heading { - margin: 0 0 0.7rem; - font-size: 0.8125rem; - font-weight: 650; - line-height: 1.45; -} - -.chat-block-scene-repo-names { - margin: 0 0 0.55rem; - padding: 0; - list-style: none; - display: flex; - flex-wrap: wrap; - gap: 0.35rem 0.6rem; - font-size: 0.875rem; - font-weight: 600; - color: var(--foreground); -} - -/* GitHub connect card. The row's checkbox control is - `@corbits/react-ui`'s own `Checkbox`; only the row layout around it - (name left, open-PR count right) is workbench-specific. */ -.chat-block-connect-steps { - margin: 0; - padding-left: 1.25rem; - display: flex; - flex-direction: column; - gap: 0.25rem; - list-style: decimal; -} - -.chat-block-connect-helper { - margin-top: 0.55rem; -} - -.chat-block-connect-line { - display: flex; - align-items: center; - gap: 0.45rem; - margin: 0 0 0.7rem; - font-size: 0.8125rem; - color: var(--muted-foreground); -} - -.chat-block-connect-tick { - color: var(--ok, #37904a); - flex-shrink: 0; -} - -.chat-block-connect-count-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; - font-size: 0.75rem; - color: var(--muted-foreground); - margin-bottom: 0.4rem; -} - -.chat-block-connect-repo-list { - border: 1px solid var(--border); - border-radius: 0; - background: var(--background); -} - -.chat-block-connect-repo-row { - display: flex; - align-items: center; - gap: 0.65rem; - padding: 0.5rem 0.7rem; - border-bottom: 1px solid var(--border); - cursor: pointer; -} - -.chat-block-connect-repo-row:last-child { - border-bottom: 0; -} - -.chat-block-connect-repo-row:hover { - background: var(--muted); -} - -.chat-block-connect-repo-label { - display: flex; - align-items: baseline; - flex: 1; - min-width: 0; - gap: 0.5rem; - cursor: pointer; -} - -.chat-block-connect-repo-name { - font-size: 0.875rem; - font-weight: 600; - color: var(--foreground); -} - -.chat-block-connect-repo-meta { - margin-left: auto; - font-size: 0.6875rem; - color: var(--muted-foreground); -} - -.chat-block-stream { - margin: 0; - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.78rem; - line-height: 1.55; - white-space: pre-wrap; - color: var(--muted-foreground); -} - -.chat-block-cursor { - display: inline-block; - width: 0.4rem; - height: 0.9em; - margin-left: 1px; - vertical-align: text-bottom; - background: var(--chat-agent-accent, var(--primary)); - animation: chat-block-blink 0.9s step-end infinite; -} - -@keyframes chat-block-blink { - 50% { - opacity: 0; - } -} - -/* Composer chrome — a single bordered input row with space to breathe. - Docked at the chat column's full working width, independent of the - 72ch prose measure the bubble text uses; the @ / slash popover then - matches that same full-width box. */ -.chat-composer-stack { - display: flex; - flex-direction: column; - align-items: stretch; - flex-shrink: 0; - width: 100%; - padding: 0.9rem clamp(0.9rem, 3vw, 1.75rem); - box-sizing: border-box; -} - -/* pre-send banner — a tenant with no usable model sees this - above the composer, before typing a single character, rather than - discovering it only after a reply never arrives. Never disables the - composer beneath it. */ -.chat-no-model-banner { - display: flex; - align-items: center; - gap: 0.5rem; - margin: 0 0 0.5rem; - padding: 0.4rem 0.6rem; - border-radius: var(--ui-radius-md, 0.375rem); - background: color-mix(in srgb, var(--destructive) 8%, transparent); - color: color-mix(in srgb, var(--destructive) 78%, var(--muted-foreground)); - font-size: 0.8125rem; -} - -.chat-no-model-banner svg { - flex-shrink: 0; - width: 1rem; - height: 1rem; -} - -.chat-no-model-banner-text { - flex: 1; -} - -/* Catch-up fetch for a still-running turn failed — soft alert - above the activity strip so a mid-turn reopen never looks idle. Same - visual weight as the no-model banner; role=alert is set in markup. */ -.chat-resume-failed-banner { - display: flex; - align-items: center; - gap: 0.5rem; - margin: 0 0 0.5rem; - padding: 0.4rem 0.6rem; - border-radius: var(--ui-radius-md, 0.375rem); - background: color-mix(in srgb, var(--destructive) 8%, transparent); - color: color-mix(in srgb, var(--destructive) 78%, var(--muted-foreground)); - font-size: 0.8125rem; -} - -.chat-resume-failed-banner svg { - flex-shrink: 0; - width: 1rem; - height: 1rem; -} - -.chat-resume-failed-banner-text { - flex: 1; -} - -.chat-composer { - position: relative; - flex-shrink: 0; - width: 100%; - padding: 0; - box-sizing: border-box; -} - -.chat-composer-attachments { - display: flex; - flex-wrap: wrap; - gap: 0.35rem; - list-style: none; - margin: 0; - padding: 0; -} - -.chat-composer-attachment { - display: inline-flex; - align-items: center; - gap: 0.25rem; - max-width: 100%; - border: 1px solid var(--border); - border-radius: var(--ui-radius-md, 0.5rem); - background: color-mix(in srgb, var(--foreground) 3%, var(--card, var(--background))); - padding: 0.2rem 0.35rem 0.2rem 0.5rem; - font-size: 0.75rem; - line-height: 1.3; -} - -.chat-composer-attachment-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 12rem; -} - -.chat-composer-attachment-remove { - display: inline-flex; - align-items: center; - justify-content: center; - border: none; - border-radius: var(--ui-radius-sm, 0.25rem); - background: transparent; - color: var(--muted-foreground); - padding: 0.1rem; - cursor: pointer; -} - -.chat-composer-attachment-remove:hover { - color: var(--foreground); - background: color-mix(in srgb, var(--foreground) 8%, transparent); -} - -.chat-composer-attachment-remove svg { - width: 0.85rem; - height: 0.85rem; -} - -.chat-composer-file-input { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -/* Mock `.artifact-chip`: a file part rendered as an open-able reference - rather than a plain label — accent-tinted so it reads as a link into the - Library, square-cornered like every other new surface in this pass. */ -.chat-artifact-chip { - display: inline-flex; - align-items: stretch; - margin: 0.15rem 0; - max-width: 100%; - min-width: 16rem; - border: 1px solid color-mix(in srgb, var(--primary) 32%, var(--border)); - border-radius: 0; - background: color-mix(in srgb, var(--primary) 8%, transparent); - color: var(--foreground); - font-size: 0.8125rem; - transition: background 120ms var(--chat-ease); -} - -.chat-artifact-chip-open { - display: inline-flex; - align-items: center; - gap: 0.55rem; - flex: 1; - min-width: 0; - padding: 0.5rem 0.7rem; - border: none; - background: transparent; - color: inherit; - font: inherit; - text-align: left; -} - -.chat-artifact-chip-open:not(:disabled) { - cursor: pointer; -} - -.chat-artifact-chip-open:not(:disabled):hover { - background: color-mix(in srgb, var(--primary) 14%, transparent); -} - -.chat-artifact-chip-open:disabled { - opacity: 0.8; -} - -.chat-artifact-chip-library { - display: grid; - place-items: center; - flex-shrink: 0; - width: 2.1rem; - border: none; - border-left: 1px solid color-mix(in srgb, var(--primary) 32%, var(--border)); - background: transparent; - color: var(--primary); - cursor: pointer; -} - -.chat-artifact-chip-library:hover { - background: color-mix(in srgb, var(--primary) 14%, transparent); -} - -.chat-artifact-chip-library svg { - width: 0.9rem; - height: 0.9rem; -} - -.chat-artifact-chip-icon { - display: grid; - place-items: center; - flex-shrink: 0; - width: 1.65rem; - height: 1.65rem; - border: 1px solid var(--border); - background: var(--card, var(--background)); - color: var(--primary); -} - -.chat-artifact-chip-icon svg { - width: 0.9rem; - height: 0.9rem; -} - -.chat-artifact-chip-meta { - min-width: 0; - display: flex; - flex-direction: column; -} - -.chat-artifact-chip-meta strong { - font-size: 0.82rem; - font-weight: 600; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.chat-artifact-chip-meta span { - font-size: 0.7rem; - color: var(--muted-foreground); -} - -/* Trace: one collapsed row per turn, expanding to each step (DESIGN.md - "Messages and cards"). */ -.chat-trace { - margin-top: 10px; - max-width: 640px; - overflow: hidden; - border-radius: var(--r-lg); - box-shadow: inset 0 0 0 1px var(--line); - font-size: 13px; -} - -.chat-trace-sum { - display: flex; - align-items: center; - gap: 8px; - width: 100%; - height: 36px; - padding: 0 12px; - border: 0; - background: transparent; - color: var(--ink-2); - font: inherit; - text-align: left; - cursor: pointer; - transition: background-color 120ms ease; -} - -.chat-trace-sum:hover { - background: var(--hover); -} - -.chat-trace-sum:focus-visible { - outline: 2px solid var(--action); - outline-offset: -2px; -} - -.chat-trace-chev { - flex: none; - width: 1em; - height: 1em; - color: var(--ink-3); - transition: transform 200ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1)); -} - -.chat-trace[data-open="true"] .chat-trace-chev { - transform: rotate(90deg); -} - -.chat-trace-signal { - flex: none; - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--action); - animation: chat-trace-pulse 1.2s ease-in-out infinite; -} - -@keyframes chat-trace-pulse { - 50% { - opacity: 0.35; - } -} - -.chat-trace-steps { - padding: 6px 0; - border-top: 1px solid var(--line); - background: var(--surface-sunk); -} - -.chat-trace-step { - display: grid; - grid-template-columns: 28px 1fr auto auto; - align-items: center; - gap: 4px; - width: 100%; - padding: 5px 12px 5px 8px; - border: 0; - background: transparent; - color: var(--ink-2); - font: inherit; - text-align: left; -} - -.chat-trace-step-button { - cursor: pointer; -} - -.chat-trace-step-button:hover { - background: var(--hover); -} - -.chat-trace-step-text { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.chat-trace-step code { - font-family: var(--font-mono); - font-size: 12px; - color: var(--ink); -} - -.chat-trace-glyph { - display: inline-flex; - justify-self: center; - color: var(--ink-3); -} - -.chat-trace-time { - font-size: 12px; - color: var(--ink-3); - font-variant-numeric: tabular-nums; -} - -.chat-trace-marker { - display: inline-flex; - width: 12px; - height: 12px; - color: var(--ink-3); -} - -.chat-trace-marker svg { - width: 12px; - height: 12px; -} - -.chat-trace-marker[data-status="running"], -.chat-trace-marker[data-status="pending"] { - color: var(--action); - animation: chat-trace-spin 0.8s linear infinite; -} - -.chat-trace-marker[data-status="failed"] { - color: var(--destructive, #b42318); -} - -@keyframes chat-trace-spin { - to { - transform: rotate(360deg); - } -} - -.chat-trace-detail { - margin: 0; - padding: 2px 12px 6px 40px; - max-height: 12rem; - overflow: auto; - white-space: pre-wrap; - overflow-wrap: anywhere; - color: var(--ink-3); - font-size: 12px; -} - -.chat-trace-note { - padding: 5px 12px 5px 40px; - color: var(--ink-3); -} - -.chat-trace-thinking { - font-style: italic; -} - -.chat-trace-retry { - color: var(--warn, #b7791f); -} - -.chat-trace-sr { - position: absolute; - width: 1px; - height: 1px; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); -} - -@media (prefers-reduced-motion: reduce) { - .chat-trace-chev, - .chat-trace-sum { - transition: none; - } - - .chat-trace-signal, - .chat-trace-marker[data-status="running"], - .chat-trace-marker[data-status="pending"] { - animation: none; - } -} - -/* iMessage-style pulse: a small grey bubble of three bouncing dots in - the incoming-message slot (same indent as a grouped reply). Who-is-typing - copy is visually hidden. */ -.chat-typing-row { - padding-left: calc(0.45rem + 2.15rem + 0.65rem); -} - -.chat-typing-row:hover { - background: transparent; -} - -.chat-typing-indicator { - position: relative; - display: inline-flex; - flex: 0 0 auto; - align-items: center; - justify-content: center; - min-width: 3.25rem; - min-height: 1.85rem; - padding: 0.4rem 0.8rem; - border-radius: 1.05rem 1.05rem 1.05rem 0.3rem; - background: color-mix(in srgb, var(--foreground) 10%, var(--background)); -} - -.chat-typing-indicator-label { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -.chat-typing-indicator-dots { - display: inline-flex; - align-items: center; - gap: 0.28rem; - height: 0.9rem; -} - -.chat-typing-indicator-dots span { - width: 0.45rem; - height: 0.45rem; - border-radius: 50%; - background: color-mix(in srgb, var(--foreground) 55%, transparent); -} - -@media (prefers-reduced-motion: no-preference) { - .chat-typing-indicator-dots span { - animation: chat-typing-dot 1.2s ease-in-out infinite; - } - - .chat-typing-indicator-dots span:nth-child(2) { - animation-delay: 0.16s; - } - - .chat-typing-indicator-dots span:nth-child(3) { - animation-delay: 0.32s; - } -} - -@keyframes chat-typing-dot { - 0%, - 60%, - 100% { - opacity: 0.45; - transform: translateY(0); - } - 30% { - opacity: 1; - transform: translateY(-0.22rem); - } -} - -.chat-composer-row { - display: flex; - min-height: 6.75rem; - flex-direction: column; - align-items: stretch; - gap: 0.55rem; - border: 1.5px solid var(--border); - border-radius: var(--ui-radius-lg, 0.75rem); - background: var(--card, var(--background)); - padding: 0.8rem 0.85rem 0.75rem; - transition: - border-color 120ms var(--chat-ease), - background-color 120ms var(--chat-ease); -} - -.chat-composer-row:focus-within { - border-color: var(--primary); -} - -.chat-composer-actions { - display: grid; - grid-template-columns: 1fr auto 1fr; - align-items: center; - margin-top: auto; - padding: 0; -} - -.chat-composer-actions > :first-child { - justify-self: start; -} - -.chat-composer-actions > :last-child { - justify-self: end; -} - -.chat-composer-submit-actions { - display: flex; - align-items: center; - gap: 0.25rem; -} - -.chat-composer-mic[data-listening="true"] { - color: var(--shell-accent, var(--primary)); - background: color-mix(in srgb, var(--shell-accent, var(--primary)) 12%, transparent); -} - -.chat-composer-keyboard-hint { - color: var(--muted-foreground); - font-size: 0.7rem; - font-weight: 600; - letter-spacing: 0.01em; - visibility: hidden; - opacity: 0; - pointer-events: none; - transition: opacity 120ms var(--chat-ease); -} - -.chat-composer-keyboard-hint[data-visible="true"] { - visibility: visible; - opacity: 1; -} - -.chat-composer-status { - min-height: 0; - padding: 0.3rem 0.15rem 0; - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.chat-composer-status:empty { - display: none; - padding: 0; -} - -.chat-composer-error { - padding: 0.3rem 0.15rem 0; - font-size: 0.75rem; - color: var(--destructive); -} - -/* Send button states: empty stays quietly available as a neutral circular - affordance; ready and sending keep the primary fill, with the spinner - communicating the in-progress state. */ -[data-send-state="empty"] { - background: color-mix(in srgb, var(--foreground) 4%, transparent); -} - -[data-send-state="empty"] svg { - color: var(--muted-foreground); -} - -/* Extends the attach/send hit area to >=40px without visually bloating the - 36px icon button: a borderless ::after grows the clickable box, since - it's still inside the button's own hit-test region. */ -.chat-composer-icon-button { - position: relative; - display: inline-flex; - width: 2.25rem; - height: 2.25rem; - min-width: 2.25rem; - min-height: 2.25rem; - align-items: center; - justify-content: center; - border-radius: 999px; - padding: 0; -} - -.chat-composer-actions > :first-child { - background: color-mix(in srgb, var(--foreground) 4%, transparent); -} - -.chat-composer-actions > :first-child:hover:not(:disabled) { - background: color-mix(in srgb, var(--foreground) 8%, transparent); -} - -.chat-composer-icon-button::after { - content: ""; - position: absolute; - inset: -3px; -} - -.chat-composer-send-spinner { - animation: chat-composer-spin 0.8s linear infinite; -} - -@keyframes chat-composer-spin { - to { - transform: rotate(360deg); - } -} - -/* The first-run prompt box for a zero-workbench bench — one big - ChatGPT-style box with corner affordances instead of the conversation - composer's slim inline row, since there is no workbench here to carry - mentions, slash commands, or a pending-bubble hand-off. */ -.first-run-composer { - width: 100%; - max-width: 40rem; -} - -.first-run-composer-box { - border: 1px solid var(--border); - border-radius: var(--ui-radius-md, 0.375rem); - background: var(--card, var(--background)); - padding: 0.9rem 0.9rem 0.6rem; - transition: border-color 120ms var(--chat-ease); -} - -.first-run-composer-box:focus-within { - border-color: var(--primary); -} - -.first-run-composer-input { - width: 100%; - min-height: 3.5rem; - max-height: 16rem; - overflow-y: auto; - resize: none; - border: 0; - background: transparent; - padding: 0.2rem 0.2rem 0.6rem; - font: inherit; - font-size: 0.95rem; - line-height: 1.5; - color: var(--foreground); -} - -.first-run-composer-input:focus { - outline: none; -} - -.first-run-composer-input::placeholder { - color: var(--muted-foreground); - opacity: 1; -} - -.first-run-composer-corners { - display: flex; - align-items: center; - justify-content: space-between; -} - -.first-run-composer-corners-right { - display: flex; - align-items: center; - gap: 0.3rem; -} - -/* Toggled on while `SpeechRecognition` is actively listening — the same - accent used for the active sidebar row, so "recording" reads through - the one house color for "live". */ -.first-run-composer-mic[data-listening="true"] { - color: var(--shell-accent, var(--primary)); - background: color-mix(in srgb, var(--shell-accent, var(--primary)) 12%, transparent); -} - -.chat-mention-popover { - position: absolute; - bottom: 100%; - left: 0; - right: 0; - z-index: 10; - display: flex; - max-height: 18rem; - width: auto; - flex-direction: column; - overflow: hidden; - border: 1px solid var(--border); - border-radius: 0.75rem; - margin-bottom: 0.35rem; - background: var(--popover, var(--card, var(--background))); - box-shadow: 0 8px 24px color-mix(in srgb, var(--foreground) 12%, transparent); -} - -/* Entrance for the mention/slash/help popovers: all three anchor - `bottom: 100%` (they open above the caret line), so a single - caret-side-aware direction — rising up from the input — covers every - case. */ -@keyframes chat-popover-enter { - from { - opacity: 0; - transform: translateY(4px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@media (prefers-reduced-motion: no-preference) { - .chat-popover-enter { - animation: chat-popover-enter 110ms var(--chat-ease); - } -} - -.chat-mention-list { - display: flex; - max-height: 18rem; - flex-direction: column; - overflow-y: auto; - padding: 0.25rem; -} - -.chat-mention-group-label { - padding: 0.4rem 0.5rem 0.15rem; - font-size: 0.66rem; - font-weight: 650; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--muted-foreground); -} - -.chat-mention-option { - display: flex; - align-items: center; - gap: 0.55rem; - width: 100%; - border: none; - border-radius: 0.45rem; - background: none; - padding: 0.45rem 0.5rem; - text-align: left; - font: inherit; - color: var(--foreground); - cursor: pointer; -} - -.chat-mention-option:focus-visible { - outline: 2px solid var(--ring, var(--primary)); - outline-offset: -2px; -} - -.mention-avatar { - flex-shrink: 0; -} - -.chat-mention-meta { - display: flex; - min-width: 0; - flex: 1; - flex-direction: column; - gap: 0.05rem; -} - -.chat-mention-name { - overflow: hidden; - font-size: 0.8125rem; - font-weight: 600; - text-overflow: ellipsis; - white-space: nowrap; -} - -.chat-mention-handle { - font-weight: 600; - font-size: 0.8125rem; -} - -.chat-mention-meta .chat-mention-handle { - overflow: hidden; - font-size: 0.68rem; - font-weight: 400; - color: var(--muted-foreground); - text-overflow: ellipsis; - white-space: nowrap; -} - -.chat-mention-label { - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.chat-mention-option[data-highlighted="true"], -.chat-mention-option:hover { - background: var(--muted, color-mix(in srgb, var(--foreground) 6%, transparent)); -} - -.chat-mention-empty { - padding: 0.75rem 0.7rem; - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.chat-slash-help { - max-height: none; -} - -.chat-slash-help-title { - padding: 0.45rem 0.65rem 0.15rem; - font-size: 0.7rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.02em; - color: var(--muted-foreground); -} - -.chat-slash-help-footer { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; - padding: 0.45rem 0.65rem; - border-top: 1px solid var(--border); -} - -.chat-slash-help-note { - font-size: 0.7rem; - font-style: italic; - color: var(--muted-foreground); -} - -.chat-slash-help-close { - border: none; - background: none; - padding: 0; - font: inherit; - font-size: 0.75rem; - color: var(--primary); - cursor: pointer; -} - -.chat-thread-breadcrumb { - display: flex; - min-width: 0; - align-items: center; - gap: 0.375rem; - font-size: 0.875rem; -} - -.chat-thread-breadcrumb-link { - flex-shrink: 0; - border: 0; - background: transparent; - padding: 0; - color: var(--muted-foreground); - cursor: pointer; - font: inherit; - text-decoration: underline; - text-underline-offset: 2px; -} - -.chat-thread-breadcrumb-link:hover { - color: var(--foreground); -} - -.chat-thread-breadcrumb-sep { - color: var(--muted-foreground); -} - -.chat-thread-breadcrumb-current { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-weight: 600; -} - -.chat-threads-menu { - position: relative; -} - -.chat-threads-menu-trigger { - display: inline-flex; - align-items: center; - gap: 0.25rem; - border: 1px solid var(--border); - border-radius: var(--ui-radius-md, 0.375rem); - background: transparent; - padding: 0.25rem 0.5rem; - font-size: 0.8125rem; - color: var(--foreground); - cursor: pointer; - list-style: none; -} - -.chat-threads-menu-trigger::-webkit-details-marker { - display: none; -} - -.chat-threads-menu-panel { - position: absolute; - top: calc(100% + 0.25rem); - right: 0; - z-index: 20; - min-width: 12rem; - max-height: 16rem; - overflow-y: auto; - border: 1px solid var(--border); - border-radius: var(--ui-radius-lg, 0.5rem); - background: var(--card, var(--background)); - box-shadow: 0 8px 24px color-mix(in srgb, var(--foreground) 12%, transparent); - padding: 0.25rem; -} - -.chat-threads-menu-empty { - padding: 0.5rem 0.75rem; - font-size: 0.8125rem; - color: var(--muted-foreground); -} - -.chat-threads-menu-item { - display: block; - width: 100%; - border: 0; - border-radius: var(--ui-radius-md, 0.375rem); - background: transparent; - padding: 0.5rem 0.75rem; - text-align: left; - font-size: 0.8125rem; - color: var(--foreground); - cursor: pointer; -} - -.chat-threads-menu-item:hover { - background: var(--muted, color-mix(in srgb, var(--foreground) 6%, transparent)); -} - -/* Sub-threads render indented under their depth-1 parent — the two-level - cap made visible in the menu, not just enforced server-side. */ -.chat-threads-menu-group + .chat-threads-menu-group { - margin-top: 0.125rem; -} - -.chat-threads-menu-item-nested { - padding-left: 1.75rem; - font-size: 0.75rem; - color: var(--muted-foreground); -} - -/* The fork's visible back-reference to its origin message — - shown atop a sub-thread's timeline, distinct from the breadcrumb's - "walk back out" affordance directly above it. */ -.chat-thread-origin-banner { - margin: 0 0 0.5rem; - padding: 0.375rem 0.75rem; - border-radius: var(--ui-radius-md, 0.375rem); - background: var(--muted, color-mix(in srgb, var(--foreground) 5%, transparent)); - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.chat-thread-origin-banner-link { - border: 0; - background: transparent; - padding: 0; - font: inherit; - font-weight: 600; - color: var(--primary); - cursor: pointer; - text-decoration: underline; - text-underline-offset: 2px; -} - -.chat-member-stack { - display: flex; - align-items: center; -} - -.member-avatar, -.chat-member-stack-overflow { - display: grid; - place-items: center; - width: 1.5rem; - height: 1.5rem; - flex-shrink: 0; - border-radius: 0; - border: 1.5px solid var(--background); - margin-left: -0.35rem; - font-size: 0.625rem; - font-weight: 700; - line-height: 1; -} - -.member-avatar:first-child { - margin-left: 0; -} - -.chat-member-stack-overflow { - background: var(--muted); - color: var(--muted-foreground); -} - -/* Settings sits apart from the quiet New routine / Insights pair — a hairline - divider keeps it reading as the header's one persistent, always-available - control rather than a third peer in that group. */ -.chat-workbench-settings-slot { - display: flex; - align-items: center; - margin-left: 0.35rem; - padding-left: 0.6rem; - border-left: 1px solid var(--border); -} - -/* Who's live in this workbench right now, beyond the static member stack - above — round (vs. the member stack's square avatars) and colored per - principal (server-assigned, see @corbits/presence) so "live" reads at a - glance without any extra chrome or a status dot. */ -.chat-presence-stack { - display: flex; - align-items: center; - margin-left: 0.35rem; -} - -.chat-presence-avatar, -.chat-presence-stack-overflow { - display: grid; - place-items: center; - width: 1.5rem; - height: 1.5rem; - flex-shrink: 0; - border-radius: 50%; - border: 1.5px solid var(--background); - margin-left: -0.35rem; - font-size: 0.625rem; - font-weight: 700; - line-height: 1; -} - -.chat-presence-avatar:first-child { - margin-left: 0; -} - -.chat-presence-stack-overflow { - background: var(--muted); - color: var(--muted-foreground); -} - -/* Message body column: bubble/parts + reaction/pin row share one flex - column so chips sit under the bubble instead of as a loose sibling of - the whole header+bubble cluster. Each direct child is a - block that must keep its own height — no shrinking that would let - the tool-activity stack or a failed-turn strip collapse onto the row - below. */ -.chat-message-body { - display: flex; - flex-direction: column; - align-items: stretch; - gap: 0.2rem; - min-width: 0; - max-width: 100%; - width: 100%; -} - -.chat-message-body > * { - flex-shrink: 0; - min-width: 0; -} - -/* Existing reaction chips + pin toggle, indented to align under the bubble - text the same way `.chat-thread-affordance` does. Chips are content — - they always show once a reaction exists. The pin toggle fades in on - hover/focus so a quiet workbench doesn't carry a permanent row of controls - under every line. Adding a *new* reaction, replying, and the ellipsis - menu live in `.chat-hover-toolbar` instead — see below. */ -.chat-message-actions { - display: flex; - align-items: center; - gap: 0.35rem; - margin: 0.05rem 0 0.15rem 2.9rem; - max-width: 100%; -} - -.chat-reaction-row { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 0.3rem; -} - -.chat-reaction-chip { - display: inline-flex; - align-items: center; - gap: 0.3rem; - border: 1px solid var(--border, transparent); - border-radius: 0; - background: color-mix(in srgb, var(--foreground) 4%, transparent); - padding: 0.1rem 0.5rem; - font-size: 0.75rem; - line-height: 1.4; - color: var(--muted-foreground); - cursor: pointer; - transition: - background 120ms var(--chat-ease), - border-color 120ms var(--chat-ease); -} - -.chat-reaction-chip:hover { - background: color-mix(in srgb, var(--foreground) 8%, transparent); -} - -.chat-reaction-chip[data-reacted="true"] { - border-color: var(--primary); - background: color-mix(in srgb, var(--primary) 12%, transparent); - color: var(--foreground); -} - -.chat-reaction-chip-count { - font-weight: 600; - font-variant-numeric: tabular-nums; -} - -.chat-reaction-picker-anchor { - position: relative; - display: inline-flex; -} - -.chat-pin-toggle { - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.6rem; - height: 1.6rem; - border: 0; - border-radius: var(--ui-radius-sm, 0.25rem); - background: transparent; - color: var(--muted-foreground); - cursor: pointer; - opacity: 0; - transition: - opacity 120ms var(--chat-ease), - background 120ms var(--chat-ease), - color 120ms var(--chat-ease); -} - -.chat-pin-toggle::after { - content: ""; - position: absolute; - inset: -8px; -} - -.chat-pin-toggle svg { - width: 0.9rem; - height: 0.9rem; -} - -.chat-message-group:hover .chat-pin-toggle, -.chat-message-group:focus-within .chat-pin-toggle, -.chat-pin-toggle[data-pinned="true"] { - opacity: 1; -} - -.chat-pin-toggle:hover { - background: color-mix(in srgb, var(--foreground) 8%, transparent); - color: var(--foreground); -} - -.chat-pin-toggle[data-pinned="true"] { - color: var(--accent, var(--primary)); -} - -/* Mock's hover toolbar: the compact trailing-edge cluster (add-reaction, - reply-in-thread, ellipsis) that replaces the old persistent inline - "Reply in thread" link and the standalone add-reaction trigger. Floats - over the inboard (trailing) corner — top-right on others' left-aligned - rows, top-left on own right-aligned rows so Edit does not sit on the - prompt — fading in on hover/focus-within like `.chat-pin-toggle` above, - plus `data-open` so it stays put while its own picker or menu is open - even if focus moves into a portaled menu. - Positioned relative to `.chat-message-row` — the wrapper - around this one message's own content, siblings-with but excluding its - optional `.chat-day-divider` — rather than `.chat-message-group` as a - whole: anchoring to the outer group let the toolbar float up into the - day divider's own space on the first message of a new day, detached - from the row a reader was actually hovering. */ -.chat-message-group { - position: relative; - display: block; - width: 100%; - min-width: 0; - flex-shrink: 0; - contain: layout; -} - -.chat-message-row { - position: relative; - display: block; - width: 100%; - min-width: 0; - isolation: isolate; -} - -/* Entrance for new message groups and the failed-send card: a subtle - fade + lift so appended rows don't just pop into the DOM. `@keyframes` - only plays when a node first matches (mount, or first time its - `data-pending="failed"` selector applies) — established rows sitting in - a re-rendered list never replay it. */ -@media (prefers-reduced-motion: no-preference) { - @keyframes chat-message-enter { - from { - opacity: 0; - transform: translateY(4px); - } - to { - opacity: 1; - transform: translateY(0); - } - } - - .chat-message-group { - animation: chat-message-enter 160ms var(--chat-ease); - } - - .chat-bubble[data-pending="failed"] { - animation: chat-message-enter 160ms var(--chat-ease); - } -} - -.chat-hover-toolbar { - position: absolute; - top: -0.25rem; - right: 0.4rem; - z-index: 5; - display: inline-flex; - align-items: center; - gap: 0.1rem; - border: 1px solid var(--border, transparent); - border-radius: var(--ui-radius-sm, 0.25rem); - background: var(--background); - padding: 0.15rem; - opacity: 0; - pointer-events: none; - box-shadow: 0 2px 10px color-mix(in srgb, black 14%, transparent); - transition: opacity 120ms var(--chat-ease); -} - -/* Own (right-aligned) rows: inboard edge is the left of the row, matching - how `[data-own="true"] .chat-artifact-chip` already mirrors. */ -.chat-message-group[data-own="true"] .chat-hover-toolbar { - right: auto; - left: 0.4rem; -} - -.chat-message-group:hover .chat-hover-toolbar, -.chat-message-group:focus-within .chat-hover-toolbar, -.chat-hover-toolbar[data-open="true"] { - opacity: 1; - pointer-events: auto; -} - -.chat-hover-toolbar .chat-reaction-add, -.chat-hover-reply, -.chat-hover-edit, -.chat-hover-ellipsis { - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.6rem; - height: 1.6rem; - border: 0; - border-radius: var(--ui-radius-sm, 0.25rem); - background: transparent; - color: var(--muted-foreground); - cursor: pointer; - opacity: 1; - transition: - background 120ms var(--chat-ease), - color 120ms var(--chat-ease); -} - -.chat-hover-toolbar .chat-reaction-add::after, -.chat-hover-reply::after, -.chat-hover-edit::after, -.chat-hover-ellipsis::after { - content: ""; - position: absolute; - inset: -8px; -} - -.chat-hover-toolbar .chat-reaction-add svg, -.chat-hover-reply svg, -.chat-hover-edit svg, -.chat-hover-ellipsis svg { - width: 0.9rem; - height: 0.9rem; -} - -.chat-hover-toolbar .chat-reaction-add:hover, -.chat-hover-reply:hover, -.chat-hover-edit:hover, -.chat-hover-ellipsis:hover, -.chat-hover-toolbar .chat-reaction-add[aria-expanded="true"], -.chat-hover-ellipsis[aria-expanded="true"] { - background: color-mix(in srgb, var(--foreground) 8%, transparent); - color: var(--foreground); -} - -.chat-reaction-picker { - position: absolute; - top: 100%; - left: 0; - z-index: 20; - display: flex; - gap: 0.15rem; - margin-top: 0.25rem; - border: 1px solid var(--border, transparent); - border-radius: var(--ui-radius-md, 0.375rem); - background: var(--background); - padding: 0.3rem; - box-shadow: 0 4px 16px color-mix(in srgb, black 18%, transparent); -} - -.chat-reaction-picker-option { - display: inline-flex; - align-items: center; - justify-content: center; - width: 1.9rem; - height: 1.9rem; - border: 0; - border-radius: var(--ui-radius-sm, 0.25rem); - background: transparent; - font-size: 1.05rem; - line-height: 1; - cursor: pointer; -} - -.chat-reaction-picker-option:hover, -.chat-reaction-picker-option:focus-visible { - background: color-mix(in srgb, var(--foreground) 8%, transparent); -} - -/* Pinned strip: a single row of jump chips at the workbench top — mock - geometry mirrors the thread-open row's flat, borderless affordance - rather than a card. */ -.chat-pinned-strip { +.chat-block-connect-helper { + margin-top: 0.55rem; +} + +.chat-block-connect-line { display: flex; align-items: center; - gap: 0.5rem; - border-bottom: 1px solid var(--border, transparent); - padding: 0.45rem clamp(0.85rem, 3vw, 1.75rem); - overflow-x: auto; -} - -.chat-pinned-strip-icon { - flex-shrink: 0; - width: 0.9rem; - height: 0.9rem; + gap: 0.45rem; + margin: 0 0 0.7rem; + font-size: 0.8125rem; color: var(--muted-foreground); } -.chat-pinned-strip-row { - display: flex; - align-items: center; - gap: 0.4rem; - overflow-x: auto; -} - -.chat-pinned-strip-item { +.chat-block-connect-tick { + color: var(--ok, #37904a); flex-shrink: 0; - max-width: 16rem; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - border: 1px solid var(--border, transparent); - border-radius: 999px; - background: color-mix(in srgb, var(--foreground) 4%, transparent); - padding: 0.2rem 0.65rem; - font-size: 0.75rem; - color: var(--muted-foreground); - cursor: pointer; } -.chat-pinned-strip-item:hover { - background: color-mix(in srgb, var(--foreground) 8%, transparent); - color: var(--foreground); -} - -.chat-pinned-strip-error { - color: var(--muted-foreground); -} - -.chat-pinned-strip-error-message { +.chat-block-stream { margin: 0; - font-size: 0.75rem; - color: var(--muted-foreground); -} - -.chat-thread-affordance { - display: flex; - align-items: center; - gap: 0.5rem; - margin: -0.1rem 0 0.35rem 2.9rem; - max-width: 100%; - font-size: 0.75rem; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 0.78rem; + line-height: 1.55; + white-space: pre-wrap; color: var(--muted-foreground); } -.chat-thread-avatar-stack { - display: inline-flex; - align-items: center; -} - -/* The reply-thread affordance's small overlapping initials stack — kept - as its own bespoke chip (not the header's react-ui `Avatar`) since it - only ever shows a stack of bare initials, no tone or tooltip. */ -.thread-avatar-chip { - display: grid; - place-items: center; - flex-shrink: 0; - font-weight: 700; - line-height: 1; - margin-left: -0.3rem; - border: 1.5px solid var(--background); - border-radius: var(--ui-radius-sm, 0.25rem); - width: 1.25rem; - height: 1.25rem; - font-size: 0.5625rem; +.chat-block-cursor { + display: inline-block; + width: 0.4rem; + height: 0.9em; + margin-left: 1px; + vertical-align: text-bottom; + background: var(--chat-agent-accent, var(--primary)); + animation: chat-block-blink 0.9s step-end infinite; } -.thread-avatar-chip:first-child { - margin-left: 0; +@keyframes chat-block-blink { + 50% { + opacity: 0; + } } -.chat-thread-affordance-meta { - display: inline-flex; +/* pre-send banner — a tenant with no usable model sees this + above the composer, before typing a single character, rather than + discovering it only after a reply never arrives. Never disables the + composer beneath it. */ +.chat-no-model-banner { + display: flex; align-items: center; - gap: 0.375rem; - min-width: 0; -} - -.chat-thread-reply-count { - font-weight: 600; - color: var(--foreground); -} - -.chat-thread-open { - margin-left: auto; - border: 0; - background: transparent; - padding: 0.125rem 0.375rem; - font: inherit; - font-weight: 600; - color: var(--primary); - cursor: pointer; -} - -.chat-thread-open:hover { - text-decoration: underline; - text-underline-offset: 2px; -} - -.chat-kind-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem; -} - -.chat-kind-card { - display: flex; - flex-direction: column; - gap: 0.2rem; - border: 1px solid var(--border); - border-radius: 0; - background: var(--background); - padding: 0.65rem 0.75rem; - text-align: left; - font: inherit; - cursor: pointer; -} - -.chat-kind-card[aria-pressed="true"] { - border-color: var(--primary); - background: color-mix(in srgb, var(--primary) 5%, var(--background)); - box-shadow: 0 0 0 1px var(--primary) inset; -} - -.chat-kind-card-title { + margin: 0 0 0.5rem; + padding: 0.4rem 0.6rem; + border-radius: var(--ui-radius-md, 0.375rem); + background: color-mix(in srgb, var(--destructive) 8%, transparent); + color: color-mix(in srgb, var(--destructive) 78%, var(--muted-foreground)); font-size: 0.8125rem; - font-weight: 600; -} - -.chat-kind-card-desc { - font-size: 0.75rem; - color: var(--muted-foreground); - line-height: 1.35; -} - -.chat-textarea { - border-radius: var(--radius, 0.375rem); - border: 1px solid var(--input); - background: var(--card); - padding: 0.5rem 0.75rem; - font: inherit; - font-size: 0.875rem; - resize: vertical; -} - -.chat-textarea:focus-visible { - outline: 2px solid var(--ring, var(--primary)); - outline-offset: -2px; -} - -.chat-new-workbench-form { - display: flex; - flex-direction: column; - gap: 0.85rem; -} - -.chat-form-field { - display: flex; - flex-direction: column; - gap: 0.375rem; -} - -/* Matches the app's established uppercase-label convention - (.panel-band-heading in apps/web/src/app.css). react-ui ships an - equivalent DialogFieldLabel, but it renders a