diff --git a/AGENTS.md b/AGENTS.md index f991690b0..d48346baf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -143,6 +143,15 @@ root `pnpm tools-pr` script without a new explicit maintainer decision. - `AskUserQuestionCard` (in `ToolCard.tsx`) prefers the live `onAnswerToolUse(toolUseId, content)` route (POSTs to `/api/runs/:id/tool-result`) and falls back to the legacy `onSubmitForm(text)` path when the run has already terminated. Selected chips persist across reloads by parsing the stored `tool_result.content` back into the selections shape. - Tool group rendering uses `dedupeSnapshotToolRetries` to collapse identical `AskUserQuestion` retries (one card per unique input, keeping the latest tool_use_id) and `TodoWrite` snapshots (only the most recent call, since each call is a state replace). +## Web CSS ownership + +- `apps/web/src/index.css` is an import-only cascade entrypoint. Do not add selectors or declarations there; add imports only when a truly global stylesheet is needed, and keep import order intentional. +- Shared global styles belong in `apps/web/src/styles/`: design tokens, base/reset rules, primitives, app-shell layout, and legacy cross-component selectors that cannot safely be scoped yet. Keep domain-level global files grouped by owner (for example `styles/viewer/` and `styles/workspace/`) instead of adding more large files directly under `styles/`. +- New component-owned UI styles should default to CSS Modules next to the component (`Component.module.css`) instead of expanding global stylesheets. This is preferred for isolated components, panels, menus, drawers, toolbars, cards, and form sections. +- When touching an existing component with nearby global styles, prefer migrating that component's local selectors to a CSS Module as part of the change if it is small and testable. Do not mix a large mechanical move with behavior/styling changes in the same patch. +- Keep global class names only for deliberate shared contracts: reusable primitives, theme hooks, third-party/content styling, cross-component layout, or selectors that rely on global cascade/specificity. Document any new global selector group with its owning feature. +- CSS refactors must preserve cascade semantics. For mechanical splits, verify expanded import content/order matches the previous stylesheet; for CSS Module migrations, validate the affected UI path with `pnpm --filter @open-design/web typecheck` and a focused build/test or visual check when practical. + ## i18n keys - `apps/web/src/i18n/types.ts` is the typed `Dict`; every key must be defined in all 18 locale files under `apps/web/src/i18n/locales/*.ts` (`ar`, `de`, `en`, `es-ES`, `fa`, `fr`, `hu`, `id`, `ja`, `ko`, `pl`, `pt-BR`, `ru`, `th`, `tr`, `uk`, `zh-CN`, `zh-TW`). Add the key to `types.ts` first; missing translations produce a typecheck error. diff --git a/apps/web/src/components/DesignSystemsSection.tsx b/apps/web/src/components/DesignSystemsSection.tsx index 63bdd795e..3bfbb5152 100644 --- a/apps/web/src/components/DesignSystemsSection.tsx +++ b/apps/web/src/components/DesignSystemsSection.tsx @@ -109,10 +109,13 @@ export function DesignSystemsSection({ cfg, setCfg }: Props) { useEffect(() => { if (!highlightedDesignSystemId) return; const raf = window.requestAnimationFrame(() => { - cardRefs.current.get(highlightedDesignSystemId)?.scrollIntoView({ - behavior: 'smooth', - block: 'center', - }); + const card = cardRefs.current.get(highlightedDesignSystemId); + if (typeof card?.scrollIntoView === 'function') { + card.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + } }); const timeout = window.setTimeout(() => { setHighlightedDesignSystemId((current) => diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 0018c3319..875e9638c 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -1,27797 +1,24 @@ @import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;500;600;700&display=swap'); @import './styles/design-system-flow.css'; - -/* ============================================================ - Open Design — neutral product workspace - ============================================================ */ -:root { - /* Surface palette — neutral app chrome that does not bias generated artifacts. - Keep warm/brand colors out of preview backgrounds; generated product UI - must choose its own palette through the active skill/design brief. */ - --bg: #faf9f7; - --bg-app: #faf9f7; - --bg-panel: #ffffff; - --bg-subtle: #eef1f5; - --bg-muted: #e4e8ef; - --bg-elevated: #ffffff; - --border: #e1e5eb; - --border-strong: #c9d0da; - --border-soft: #edf0f4; - - --text: #1a1916; - --text-strong: #0d0c0a; - --text-muted: #74716b; - --text-soft: #989590; - --text-faint: #b3b0a8; - - /* Accent — Open Design action color for app chrome only. */ - --accent: #c96442; - --accent-strong: #b45a3b; - --accent-soft: #f5d8cb; - --accent-tint: #fbeee5; - --accent-hover: #b45a3b; - - /* Semantic accent tints used by tool / status pills. */ - --green: #1f7a3a; - --green-bg: #e8f7ee; - --green-border: #c6ead2; - --blue: #2348b8; - --blue-bg: #e8efff; - --blue-border: #c8d6ff; - --purple: #6c3aa6; - --purple-bg: #f3ecf9; - --purple-border: #e4d4f1; - --red: #9c2a25; - --red-bg: #fdecea; - --red-border: #f5c6c2; - --amber: #b26200; - --amber-bg: #fff3e0; - - --shadow-xs: 0 1px 0 rgba(28, 27, 26, 0.04); - --shadow-sm: 0 1px 2px rgba(28, 27, 26, 0.05), 0 1px 3px rgba(28, 27, 26, 0.04); - --shadow-md: 0 6px 24px rgba(28, 27, 26, 0.07), 0 2px 6px rgba(28, 27, 26, 0.04); - --shadow-lg: 0 24px 60px rgba(28, 27, 26, 0.16), 0 8px 16px rgba(28, 27, 26, 0.07); - - --radius-sm: 8px; - --radius: 12px; - --radius-lg: 16px; - --radius-pill: 999px; - - /* "Active option" indicator color, intentionally separate from --accent - (brand orange used by primary CTAs like Create / Save). Use --selected - for "this option is the one currently picked" affordances — selected - fidelity card, focused input ring, active filter pill — so a primary - CTA and a selected state can coexist on the same screen without - competing. --selected-soft is the 16% tint for outer rings / fills. */ - --selected: #2563eb; - --selected-soft: rgba(37, 99, 235, 0.16); - - --serif: 'Source Serif Pro', 'Source Serif 4', 'Iowan Old Style', 'Apple Garamond', Georgia, 'Times New Roman', serif; - --sans: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - --mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace; -} - -/* Dark theme variables — shared between explicit [data-theme="dark"] and the - OS-level prefers-color-scheme media query (system mode = no data-theme attr). */ -[data-theme="dark"] { - color-scheme: dark; - --bg: #1a1917; - --bg-app: #1a1917; - --bg-panel: #222120; - --bg-subtle: #272523; - --bg-muted: #2e2c29; - --bg-elevated: #2a2825; - --border: #333128; - --border-strong: #46433c; - --border-soft: #2a2825; - - --text: #e8e4dc; - --text-strong: #f2ede4; - --text-muted: #9a9690; - --text-soft: #6e6b65; - --text-faint: #4e4b46; - - --accent: #d97a56; - --accent-strong: #e8896a; - --accent-soft: #3d2318; - --accent-tint: #2e1a12; - --accent-hover: #e8896a; - - --green: #4caf72; - --green-bg: #0f2a18; - --green-border: #1a4028; - --blue: #6b8fe8; - --blue-bg: #0f1a38; - --blue-border: #1a2c58; - --purple: #a87dd4; - --purple-bg: #1e1030; - --purple-border: #321a50; - --red: #e06b65; - --red-bg: #2a0e0c; - --red-border: #451714; - --amber: #e09a40; - --amber-bg: #2a1a04; - - --shadow-xs: 0 1px 0 rgba(0, 0, 0, 0.2); - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2); - --shadow-md: 0 6px 24px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.25); - --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.6), 0 8px 16px rgba(0, 0, 0, 0.3); -} - -/* System mode: follow OS preference when no explicit data-theme is set. */ -@media (prefers-color-scheme: dark) { - html:not([data-theme]) { - color-scheme: dark; - --bg: #1a1917; - --bg-app: #1a1917; - --bg-panel: #222120; - --bg-subtle: #272523; - --bg-muted: #2e2c29; - --bg-elevated: #2a2825; - --border: #333128; - --border-strong: #46433c; - --border-soft: #2a2825; - - --text: #e8e4dc; - --text-strong: #f2ede4; - --text-muted: #9a9690; - --text-soft: #6e6b65; - --text-faint: #4e4b46; - - --accent: #d97a56; - --accent-strong: #e8896a; - --accent-soft: #3d2318; - --accent-tint: #2e1a12; - --accent-hover: #e8896a; - - --green: #4caf72; - --green-bg: #0f2a18; - --green-border: #1a4028; - --blue: #6b8fe8; - --blue-bg: #0f1a38; - --blue-border: #1a2c58; - --purple: #a87dd4; - --purple-bg: #1e1030; - --purple-border: #321a50; - --red: #e06b65; - --red-bg: #2a0e0c; - --red-border: #451714; - --amber: #e09a40; - --amber-bg: #2a1a04; - - --shadow-xs: 0 1px 0 rgba(0, 0, 0, 0.2); - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2); - --shadow-md: 0 6px 24px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.25); - --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.6), 0 8px 16px rgba(0, 0, 0, 0.3); - } -} - -* { box-sizing: border-box; } - -html, body, #root { height: 100%; margin: 0; } - -body { - font-family: var(--sans); - color: var(--text); - background: var(--bg-app); - font-size: 13.5px; - line-height: 1.5; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.od-loading-shell { - min-height: 100vh; - display: grid; - place-items: center; - color: var(--text-muted); - background: var(--bg-app); - font: 500 13px/1.4 var(--sans); -} - -/* -------- Buttons --------------------------------------------------- */ -button { - font: inherit; - color: var(--text); - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - padding: 6px 12px; - cursor: pointer; - transition: background 120ms ease, border-color 120ms ease, color 120ms ease, box-shadow 120ms ease; -} -button:hover:not(:disabled) { background: var(--bg-subtle); border-color: var(--border-strong); } -button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } - -button.primary { - background: var(--accent); - border-color: var(--accent); - color: white; - font-weight: 500; - box-shadow: 0 1px 0 rgba(180, 90, 59, 0.18) inset, var(--shadow-xs); -} -button.primary:hover:not(:disabled) { - background: var(--accent-hover); - border-color: var(--accent-hover); -} -button.primary-ghost { - background: var(--bg-panel); - border-color: var(--accent); - color: var(--accent); - font-weight: 500; -} -button.primary-ghost:hover:not(:disabled) { background: var(--accent-tint); } - -button.ghost { - background: transparent; - border-color: var(--border); - color: var(--text); -} -button.ghost:hover:not(:disabled) { background: var(--bg-subtle); border-color: var(--border-strong); } -/* - * Transient success state for ghost buttons. Used by the Media - * Providers "Reload from daemon" button after a successful reload — - * the button briefly turns green with a check icon for ~2s then snaps - * back to its idle treatment, replacing what used to be a permanent - * success paragraph under the section header. Reusable on any - * ghost button that wants the same "did it" pulse. - */ -button.ghost.is-success-flash { - border-color: var(--green-border, color-mix(in srgb, #1f9d55 32%, var(--border))); - background: var(--green-bg, color-mix(in srgb, #1f9d55 8%, transparent)); - color: var(--green, #137a3d); -} -button.ghost.is-success-flash:hover:not(:disabled) { - background: var(--green-bg, color-mix(in srgb, #1f9d55 14%, transparent)); -} -button.ghost.is-success-flash svg { - color: currentColor; -} - -/* - * Visually-hidden but assistive-tech accessible. We use it to keep - * a `role="status"` live-region for actions whose visible feedback - * is too transient (e.g. a 2s button-label flash) for a screen - * reader to catch reliably. Borrowed from Tailwind's `sr-only` so - * it reads as the same primitive everyone already knows. - */ -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -button.subtle { - background: var(--bg-subtle); - border-color: transparent; - color: var(--text); -} -button.subtle:hover:not(:disabled) { background: var(--bg-muted); } - -button.icon-btn { padding: 6px 10px; font-size: 13px; } -button:disabled { opacity: 0.5; cursor: not-allowed; } - -/* -------- Inputs ---------------------------------------------------- */ -input, textarea, select { - font: inherit; - color: var(--text); - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - padding: 7px 10px; - width: 100%; - transition: border-color 120ms ease, box-shadow 120ms ease; -} -input::placeholder, textarea::placeholder { color: var(--text-faint); } -input:focus, textarea:focus, select:focus { - outline: none; - border-color: var(--selected); - box-shadow: 0 0 0 3px var(--selected-soft); -} -/* Entry sidebar form fields keep a quieter, neutral focus so the orange - "Create" CTA stays the loudest thing in the panel. A low-opacity black - halo reads as "focused" without re-introducing the global blue/accent - ring. Using rgba directly because the Next CSS pipeline collapses - `color-mix(in srgb, var(--text) 8%, transparent)` to a solid var(--text) - ring (would render a 3px black band — too loud). */ -.entry-side input:focus, -.entry-side textarea:focus, -.entry-side select:focus { - border-color: var(--text); - box-shadow: 0 0 0 3px rgba(28, 27, 26, 0.08); -} -/* Native at the same `padding: 7px 10px` rule above, so any - form that flex-aligns an input next to a select (e.g. the memory editor's - Title + Type row) renders with mismatched heights. Stripping the native - chrome lets the shared padding and inherited line-height compute the - same box dimensions as the input, then we paint our own chevron via a - background SVG. The chevron color is a per-theme override so it stays - readable against the panel in both light and dark. */ -select { - padding-right: 32px; - appearance: none; - -webkit-appearance: none; - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%2374716b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 10px center; - background-size: 12px 12px; -} -select::-ms-expand { display: none; } -[data-theme='dark'] select { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%239a9690' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 10px center; - background-size: 12px 12px; -} -@media (prefers-color-scheme: dark) { - html:not([data-theme]) select { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%239a9690' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 10px center; - background-size: 12px 12px; - } -} -textarea { resize: vertical; font-family: inherit; } - -.od-select { - position: relative; - width: 100%; - min-width: 0; -} -.od-select-trigger { - width: 100%; - min-width: 0; - min-height: 36px; - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: center; - gap: 8px; - padding: 7px 10px; - font: inherit; - color: var(--text); - text-align: left; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; - transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease; -} -.od-select-trigger:hover:not(:disabled), -.od-select-trigger[aria-expanded='true'] { - border-color: var(--border-strong); - background: var(--bg-subtle); -} -.od-select-trigger:focus-visible, -.od-select-trigger[aria-expanded='true'] { - outline: none; - border-color: var(--selected); - box-shadow: 0 0 0 3px var(--selected-soft); -} -.od-select-trigger:disabled { - opacity: 0.55; - cursor: not-allowed; -} -.od-select-trigger svg { - color: var(--text-muted); - transition: transform 120ms ease; -} -.od-select-trigger[aria-expanded='true'] svg { - transform: rotate(180deg); -} -.od-select-value, -.od-select-option-label { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.od-select-menu { - z-index: 9000; - padding: 4px; - overflow-y: auto; - overflow-x: hidden; - overscroll-behavior: contain; - max-width: calc(100vw - 24px); - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-lg); -} -.od-select-menu.portal { - position: fixed; -} -.od-select-menu.inline { - position: absolute; - top: calc(100% + 4px); - left: 0; - right: 0; - max-height: min(280px, 48vh); -} -.od-select-option { - width: 100%; - min-width: 0; - min-height: 30px; - display: grid; - grid-template-columns: minmax(0, 1fr) 16px; - align-items: center; - gap: 8px; - padding: 6px 8px; - color: var(--text); - background: transparent; - border: 0; - border-radius: 6px; - font: inherit; - font-size: 12.5px; - text-align: left; - cursor: pointer; -} -.od-select-option:hover:not(:disabled), -.od-select-option.active:not(:disabled) { - background: var(--bg-subtle); -} -.od-select-option.selected { - color: var(--text); - font-weight: 600; -} -.od-select-option:disabled { - opacity: 0.45; - cursor: not-allowed; -} -.od-select-option-check { - display: inline-flex; - justify-content: center; - color: var(--selected); - opacity: 0; -} -.od-select-option.selected .od-select-option-check { - opacity: 1; -} -.od-select-group + .od-select-group { - margin-top: 4px; - padding-top: 4px; - border-top: 1px solid var(--border-soft); -} -.od-select-group-label { - padding: 6px 8px 4px; - color: var(--text-faint); - font-size: 11px; - font-weight: 600; -} - -code { - font-family: var(--mono); - background: var(--bg-subtle); - padding: 1px 5px; - border-radius: 4px; - font-size: 0.92em; - color: var(--text); -} - -/* -------- App shell ------------------------------------------------- */ -.app { - display: grid; - /* Three rows: chrome header (auto, fixed at 40px), optional - * project-actions toolbar (auto — collapses to 0 when hidden), - * and the chat/workspace split (1fr — fills the remaining - * viewport). The split gets explicit `grid-row: 3` below so the - * layout works whether the toolbar is rendered or returns null - * (workspace-focused mode). */ - grid-template-columns: minmax(0, 1fr); - grid-template-rows: auto auto 1fr; - height: 100vh; - background: var(--bg-app); - overflow: hidden; -} -.app > .split { - grid-row: 3; -} - -.workspace-shell { - width: 100vw; - max-width: 100vw; - height: 100vh; - min-width: 0; - min-height: 0; - display: grid; - grid-template-columns: minmax(0, 1fr); - grid-template-rows: 36px minmax(0, 1fr); - background: var(--bg-app); - overflow: hidden; -} -.workspace-shell__body { - min-width: 0; - min-height: 0; - overflow: hidden; -} -.workspace-shell__body > .app, -.workspace-shell__body > .entry-shell { - height: 100%; - min-height: 0; -} - -.workspace-tabs-chrome.app-chrome-header { - min-width: 0; - max-width: 100%; - min-height: 38px; - height: 38px; - padding: 0 8px 0 12px; - gap: 0; - background: color-mix(in srgb, var(--bg-panel) 88%, var(--bg-subtle)); - border-bottom: 1px solid color-mix(in srgb, var(--border) 74%, transparent); - box-shadow: 0 1px 0 color-mix(in srgb, var(--bg-panel) 80%, transparent) inset; - user-select: none; - -webkit-app-region: drag; - overflow: hidden; - position: relative; - z-index: 120; -} -.workspace-tabs-chrome .workspace-tabs-traffic { - margin-right: calc(var(--app-chrome-traffic-margin) + 8px); -} -.workspace-tabs-new-btn { - width: 24px; - height: 24px; - padding: 0; - border: 0; - border-radius: 5px; - background: transparent; - color: var(--text-faint); - display: inline-flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; - -webkit-app-region: no-drag; - transition: background 100ms ease, color 100ms ease; -} -.workspace-tabs-new-btn:hover { - background: color-mix(in srgb, var(--bg-panel) 72%, var(--bg-subtle)); - color: var(--text); -} -.workspace-tabs-icon-btn { - width: 28px; - height: 28px; - padding: 0; - border-radius: 6px; - border-color: transparent; - background: transparent; - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; - -webkit-app-region: no-drag; -} -.workspace-tabs-icon-btn:hover:not(:disabled), -.workspace-tabs-icon-btn.is-active { - color: var(--text-strong); - background: color-mix(in srgb, var(--bg-panel) 78%, var(--bg-subtle)); - border-color: var(--border); -} -.workspace-tabs-strip { - align-self: stretch; - min-width: 0; - flex: 1 1 0; - display: flex; - align-items: center; - gap: 0; - padding-left: 0; - /* Horizontal scroll inside the strip — tabs keep their natural width - and the strip itself scrolls when it overflows the chrome. This - replaces the previous "slice to N visible + overflow chip" model - which let lots of tabs squish-shrink and could push the rest of - the chrome out of view. */ - overflow-x: auto; - overflow-y: hidden; - scrollbar-width: none; - contain: layout style; - -webkit-app-region: drag; - /* Allow the user's trackpad / shift-wheel to scroll horizontally - without the OS turning it into a back-gesture. */ - overscroll-behavior-x: contain; -} -.workspace-tabs-strip::-webkit-scrollbar { - height: 0; - display: none; -} -.workspace-tab { - position: relative; - height: 24px; - /* Fixed width — never shrink. The strip scrolls when the tabs - don't fit anymore. */ - width: 156px; - flex: 0 0 156px; - padding: 0; - display: grid; - grid-template-columns: minmax(0, 1fr) 22px; - align-items: center; - border: 1px solid var(--border); - border-radius: 6px; - background: transparent; - color: var(--text-muted); - overflow: hidden; - contain: layout style; - transition: background 100ms ease, border-color 100ms ease, color 100ms ease; - -webkit-app-region: no-drag; -} -.workspace-tab + .workspace-tab::before { - content: ''; - position: absolute; - left: 0; - top: 50%; - width: 1px; - height: 14px; - background: color-mix(in srgb, var(--border) 70%, transparent); - transform: translateY(-50%); - pointer-events: none; -} -.workspace-tab:hover { - background: color-mix(in srgb, var(--bg-panel) 64%, transparent); - border-color: color-mix(in srgb, var(--border) 60%, transparent); - color: var(--text); -} -.workspace-tab.is-active { - background: color-mix(in srgb, var(--bg-panel) 92%, var(--bg-subtle)); - border-color: color-mix(in srgb, var(--border-strong) 52%, transparent); - color: var(--text-strong); - box-shadow: 0 1px 1px color-mix(in srgb, var(--text) 6%, transparent); -} -.workspace-tab:hover::before, -.workspace-tab.is-active::before { - opacity: 0; -} -.workspace-tab__main { - min-width: 0; - height: 100%; - padding: 0 6px 0 9px; - border: 0; - border-radius: 0; - background: transparent; - color: inherit; - display: inline-flex; - align-items: center; - justify-content: flex-start; - gap: 6px; - text-align: left; -} -.workspace-tab__main:hover:not(:disabled) { - background: transparent; - border-color: transparent; -} -.workspace-tab__icon { - width: 14px; - height: 14px; - display: inline-flex; - align-items: center; - justify-content: center; - color: currentColor; - opacity: 0.74; - flex: 0 0 auto; - line-height: 0; -} -.workspace-tab__icon svg, -.workspace-tab__close svg, -.workspace-tabs-icon-btn svg { - display: block; - flex: none; -} -.workspace-tab__label { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 11.5px; - font-weight: 520; - letter-spacing: 0; -} -.workspace-tab__close { - width: 18px; - height: 18px; - margin-right: 4px; - padding: 0; - border: 0; - border-radius: 5px; - background: transparent; - color: var(--text-faint); - display: inline-flex; - align-items: center; - justify-content: center; - opacity: 0; - flex: 0 0 auto; -} -.workspace-tab:hover .workspace-tab__close, -.workspace-tab.is-active .workspace-tab__close { - opacity: 1; -} -.workspace-tab__close:hover { - color: var(--text); - background: var(--bg-subtle); -} -.workspace-tabs-actions { - position: relative; - align-self: stretch; - display: inline-flex; - align-items: center; - gap: 4px; - flex: 0 0 auto; - margin-left: 8px; - padding-left: 4px; - z-index: 35; - -webkit-app-region: no-drag; -} -.workspace-tabs-popover { - /* Anchored to the viewport, not to .workspace-tabs-actions, because the - actions group sits next to the tab strip on the left side of the chrome - header — not the right. `position: absolute; right: 0` against that - narrow inline-flex container made the 380px popover hang off the left - edge of the window. Fixed positioning keeps it inside the viewport - regardless of where the actions group lands as tabs accumulate. */ - position: fixed; - top: calc(var(--workspace-tabs-chrome-height, 38px) + 7px); - right: max(10px, env(safe-area-inset-right)); - width: min(380px, calc(100vw - 24px)); - max-height: min(620px, calc(100vh - var(--workspace-tabs-chrome-height, 38px) - 24px)); - padding: 8px; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--bg-panel); - box-shadow: var(--shadow-lg); - display: flex; - flex-direction: column; - gap: 8px; - z-index: 130; - -webkit-app-region: no-drag; -} -.workspace-tabs-popover, -.workspace-tabs-popover * { - -webkit-app-region: no-drag; -} -.workspace-tabs-search { - height: 34px; - display: flex; - align-items: center; - gap: 7px; - padding: 0 9px; - border: 1px solid var(--selected); - border-radius: 7px; - background: var(--bg); - color: var(--text-muted); - box-shadow: 0 0 0 3px var(--selected-soft); -} -.workspace-tabs-search input { - height: 30px; - padding: 0; - border: 0; - background: transparent; - box-shadow: none; - color: var(--text); - font-size: 13px; -} -.workspace-tabs-search input:focus { - border: 0; - box-shadow: none; -} -.workspace-tabs-popover__section { - display: flex; - align-items: center; - justify-content: space-between; - padding: 4px 5px 0; - color: var(--text-muted); - font-size: 11.5px; - font-weight: 650; - letter-spacing: 0; -} -.workspace-tabs-list { - min-height: 0; - max-height: 500px; - overflow-y: auto; - display: flex; - flex-direction: column; - gap: 2px; - scrollbar-width: thin; - contain: layout style paint; -} -.workspace-tabs-list__item { - min-height: 34px; - border-radius: 7px; - display: grid; - grid-template-columns: minmax(0, 1fr) 26px; - align-items: center; - contain: layout style; -} -.workspace-tabs-list__item:hover, -.workspace-tabs-list__item.is-active { - background: color-mix(in srgb, var(--selected) 9%, var(--bg-subtle)); -} -.workspace-tabs-list__main { - min-width: 0; - height: 34px; - padding: 0 7px; - border: 0; - border-radius: 7px; - background: transparent; - color: var(--text); - display: flex; - align-items: center; - gap: 9px; - text-align: left; -} -.workspace-tabs-list__main:hover:not(:disabled) { - background: transparent; - border-color: transparent; -} -.workspace-tabs-list__icon { - width: 17px; - height: 17px; - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; - line-height: 0; -} -.workspace-tabs-list__icon svg, -.workspace-tabs-list__close svg, -.workspace-tabs-search svg { - display: block; - flex: none; -} -.workspace-tabs-list__text { - min-width: 0; - display: flex; - flex-direction: column; - gap: 1px; -} -.workspace-tabs-list__title, -.workspace-tabs-list__meta { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.workspace-tabs-list__title { - color: var(--text-strong); - font-size: 12.5px; - font-weight: 600; -} -.workspace-tabs-list__meta { - color: var(--text-muted); - font-size: 11px; -} -.workspace-tabs-list__close { - width: 22px; - height: 22px; - padding: 0; - border: 0; - border-radius: 6px; - background: transparent; - color: var(--text-faint); - display: inline-flex; - align-items: center; - justify-content: center; -} -.workspace-tabs-list__close:hover:not(:disabled) { - color: var(--text); - background: var(--bg-muted); -} -.workspace-tabs-empty { - padding: 18px 8px; - text-align: center; - color: var(--text-muted); - font-size: 12.5px; -} -.workspace-tab-preview { - position: fixed; - z-index: 140; - display: flex; - align-items: flex-start; - gap: 9px; - padding: 9px 11px; - border: 1px solid var(--border); - border-radius: 9px; - background: var(--bg-panel); - box-shadow: var(--shadow-lg); - pointer-events: none; - animation: workspace-tab-preview-in 130ms cubic-bezier(0.23, 1, 0.32, 1); -} -.workspace-tab-preview__icon { - width: 22px; - height: 22px; - border-radius: 6px; - background: var(--bg-subtle); - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; -} -.workspace-tab-preview__text { - min-width: 0; - display: flex; - flex-direction: column; - gap: 2px; -} -.workspace-tab-preview__title { - color: var(--text-strong); - font-size: 12.5px; - font-weight: 600; - line-height: 1.25; - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - word-break: break-word; -} -.workspace-tab-preview__meta { - color: var(--text-muted); - font-size: 11px; - line-height: 1.3; -} -.workspace-tab-preview__detail { - margin-top: 3px; - color: var(--text-muted); - font-size: 11.5px; - line-height: 1.4; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; - overflow: hidden; - word-break: break-word; -} -@keyframes workspace-tab-preview-in { - from { opacity: 0; transform: translateY(-3px); } - to { opacity: 1; transform: translateY(0); } -} -@media (max-width: 720px) { - .workspace-tabs-chrome.app-chrome-header { - padding-right: 6px; - } - .workspace-tab { - min-width: 40px; - max-width: 44px; - flex-basis: 44px; - grid-template-columns: 1fr; - padding: 0; - } - .workspace-tab__main { - justify-content: center; - padding: 0; - } - .workspace-tab__label, - .workspace-tab__close { - display: none; - } -} - -.app-chrome-header { - --app-chrome-traffic-space: 0px; - --app-chrome-traffic-margin: 0px; /* Set to 8px on macOS */ - display: flex; - align-items: center; - min-height: 48px; - padding: 5px 14px; - border-bottom: 1px solid var(--border); - background: var(--bg); - gap: 12px; - flex-shrink: 0; -} -.app-chrome-traffic-space { - width: var(--app-chrome-traffic-space); - flex: 0 0 var(--app-chrome-traffic-space); - /* Add extra spacing after the traffic light controls on macOS so project - navigation starts after the native window controls. */ - margin-right: var(--app-chrome-traffic-margin); -} -.app-chrome-back, -.settings-icon-btn { - width: 32px; - height: 32px; - padding: 0; - border-color: transparent; - background: transparent; - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} -.app-chrome-back:hover:not(:disabled), -.settings-icon-btn:hover:not(:disabled) { - background: var(--bg-subtle); - border-color: var(--border); - color: var(--text); -} -.app-chrome-content { - min-width: 0; - display: flex; - align-items: center; - gap: 10px; - overflow: hidden; - align-self: stretch; - flex: 1 1 auto; -} -.app-chrome-drag { - min-width: 24px; - flex: 1 1 auto; - align-self: stretch; -} -.app-chrome-actions { - display: inline-flex; - align-items: center; - gap: 6px; - flex-shrink: 0; -} -.app-chrome-file-actions { - display: inline-flex; - align-items: center; - gap: 6px; - flex-shrink: 0; -} -.app-chrome-file-actions:not(:empty) + .app-chrome-actions { - margin-left: 4px; - padding-left: 8px; - border-left: 1px solid var(--border); -} -.chrome-action { - display: inline-flex; - align-items: center; - gap: 6px; - height: 30px; - padding: 0 12px; - border-radius: 7px; - font-size: 12.5px; - font-weight: 500; - line-height: 1; - white-space: nowrap; - cursor: pointer; - border: 1px solid var(--border); - background: transparent; - color: var(--text); - transition: background 120ms ease, border-color 120ms ease, color 120ms ease; -} -.chrome-action svg { - display: block; -} -.chrome-action > svg:first-child { - transform: translateY(1px); -} -.chrome-action:hover:not(:disabled) { - background: var(--bg-subtle); -} -.chrome-action-secondary { - border-color: var(--border); - background: var(--bg); - color: var(--text); -} -.chrome-action-secondary:hover:not(:disabled) { - background: var(--bg-subtle); - border-color: var(--text-muted); -} -.chrome-action-primary { - background: var(--text-strong); - border-color: var(--text-strong); - color: var(--bg); -} -.chrome-action-primary:hover:not(:disabled) { - background: var(--text); - border-color: var(--text); - color: var(--bg); -} -.chrome-action-export { - background: var(--accent); - border-color: var(--accent); - color: white; - box-shadow: 0 6px 14px color-mix(in srgb, var(--accent) 24%, transparent); -} -.chrome-action-export:hover:not(:disabled) { - background: var(--accent-hover); - border-color: var(--accent-hover); - color: white; -} -.chrome-action-export:focus-visible { - outline-color: var(--accent); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); -} -.chrome-action-export.export-ready-nudge { - animation: export-ready-nudge 1600ms cubic-bezier(0.23, 1, 0.32, 1) 1; -} -.chrome-share-menu .share-menu-popover, -.chrome-present-wrap .present-menu { - top: calc(100% + 6px); - right: 0; -} -.zoom-menu { position: relative; display: inline-block; } -.zoom-menu .zoom-trigger { - display: inline-flex; - align-items: center; - gap: 4px; -} -.zoom-menu-popover { - position: absolute; - top: calc(100% + 6px); - left: 50%; - transform: translateX(-50%); - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius-md); - box-shadow: var(--shadow-md); - padding: 4px; - min-width: 110px; - z-index: 40; - display: flex; - flex-direction: column; - gap: 1px; -} -.zoom-menu-item { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 6px 10px; - background: transparent; - border: none; - border-radius: var(--radius-sm); - color: var(--text); - font-size: 12.5px; - cursor: pointer; - text-align: left; -} -.zoom-menu-item:hover { background: var(--bg-subtle); } -.zoom-menu-item.active { color: var(--accent-strong); font-weight: 600; } -@media (max-width: 880px) { - .chrome-action-secondary span { display: none; } - .chrome-action-secondary { padding: 0 10px; } -} -@media (max-width: 720px) { - .app-chrome-content { display: none; } -} -.viewer.is-tab-present .viewer-toolbar, -.viewer.is-tab-present .live-artifact-refresh-notice { - display: none; -} -.viewer.is-tab-present .viewer-body { - inset: 0; -} -.viewer .present-exit-btn { - position: absolute; - top: 12px; - right: 12px; - z-index: 50; - width: 30px; - height: 30px; - border-radius: 50%; - background: var(--bg); - border: 1px solid var(--border); - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - box-shadow: var(--shadow-sm); - color: var(--text-muted); -} -.viewer .present-exit-btn:hover { - background: var(--bg-subtle); - color: var(--text); -} -.live-artifact-preview-frame-host { - width: 100%; - height: 100%; - background: var(--bg); -} -.live-artifact-preview-frame-host:fullscreen { - background: var(--bg); -} -.app-project-title { - display: flex; - flex-direction: column; - justify-content: center; - gap: 4px; - min-width: 0; - width: 100%; - overflow: hidden; -} -.app-project-title-line { - display: flex; - /* Center on the cross-axis so the contentEditable title text, the - muted meta string, the design-system picker chip, and the edit - icon button all sit on a shared vertical mid-line. Baseline - alignment makes the icon-only edit button appear to "float" above - the text (the button has no text baseline of its own). */ - align-items: center; - gap: 8px; - min-width: 0; - max-width: 100%; - overflow: hidden; - padding-left: 1px; -} -.app-project-title .title { - color: var(--text-strong); - font-size: 13.5px; - line-height: 18px; - font-weight: 600; - letter-spacing: -0.01em; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - flex: 0 1 auto; - min-width: 0; -} -.app-project-title .meta { - color: var(--text-muted); - font-size: 11.5px; - line-height: 16px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - min-width: 0; - flex: 0 1 auto; -} -.project-target-platforms, -.project-feature-chips { - display: inline-flex; - align-items: center; - gap: 4px; - min-width: 0; - max-width: min(100%, 280px); - height: 22px; - overflow: hidden; - white-space: nowrap; - flex: 0 1 auto; -} -.project-feature-chips { - max-width: min(100%, 260px); -} -.project-target-platforms-label, -.project-feature-chips-label { - color: var(--text-muted); - font-size: 10px; - line-height: 18px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.06em; - flex-shrink: 0; -} -.project-target-platform-chip, -.project-feature-chip { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 0; - max-width: 92px; - height: 20px; - padding: 0 8px; - border: 1px solid color-mix(in srgb, var(--border) 78%, transparent); - border-radius: 999px; - color: var(--text-muted); - background: color-mix(in srgb, var(--bg-subtle) 88%, transparent); - font-size: 11px; - line-height: 18px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex: 0 1 auto; -} -.project-feature-chip.is-landing { - color: color-mix(in srgb, var(--accent) 72%, var(--text-strong)); - border-color: color-mix(in srgb, var(--accent) 26%, transparent); - background: color-mix(in srgb, var(--accent) 10%, var(--bg-subtle)); -} -.project-feature-chip.is-widgets { - color: color-mix(in srgb, #0891b2 72%, var(--text-strong)); - border-color: color-mix(in srgb, #0891b2 26%, transparent); - background: color-mix(in srgb, #0891b2 10%, var(--bg-subtle)); -} -.project-target-platform-chip.is-count { - min-width: 28px; - max-width: 36px; - flex: 0 0 auto; - color: var(--text-strong); - background: color-mix(in srgb, var(--accent, #7c5cff) 12%, var(--bg-subtle)); -} - -/* Project instructions toggle & editor bar */ -.project-instructions-toggle { - background: none; - border: none; - color: var(--text-muted); - cursor: pointer; - padding: 2px 4px; - margin-left: 4px; - border-radius: 4px; - opacity: 0.6; - transition: opacity 0.15s; - flex-shrink: 0; -} -.project-instructions-toggle:hover { - opacity: 1; - background: var(--bg-hover, rgba(128, 128, 128, 0.1)); -} -.project-instructions-bar { - padding: 8px 16px 10px; - border-bottom: 1px solid var(--border); - background: var(--bg-secondary, var(--bg)); - display: flex; - flex-direction: column; - gap: 6px; -} -.project-instructions-label { - font-size: 11px; - font-weight: 600; - color: var(--text-muted); - letter-spacing: 0.3px; -} -.project-instructions-input { - width: 100%; - min-height: 48px; - max-height: 120px; - padding: 8px 10px; - font-size: 13px; - line-height: 1.45; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--bg); - color: var(--text); - resize: vertical; - font-family: inherit; -} -.project-instructions-input:focus { - outline: none; - border-color: var(--accent, #646cff); -} -.project-instructions-actions { - display: flex; - gap: 6px; - justify-content: flex-end; -} - -/* Saved-state entry point: a persistent header chip that reveals the - review panel so saved Project instructions stay discoverable (#1822). */ -.project-instructions-chip { - display: inline-flex; - align-items: center; - gap: 4px; - margin-left: 6px; - padding: 2px 8px; - font-size: 11px; - font-weight: 600; - line-height: 1.6; - color: var(--text-muted); - background: var(--bg-hover, rgba(128, 128, 128, 0.1)); - border: 1px solid var(--border); - border-radius: 999px; - cursor: pointer; - transition: color 0.15s, border-color 0.15s; - flex-shrink: 0; - max-width: 220px; -} -.project-instructions-chip span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.project-instructions-chip:hover, -.project-instructions-chip.is-open { - color: var(--text); - border-color: var(--accent, #646cff); -} -.project-instructions-bar-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; -} -.project-instructions-status { - display: inline-flex; - align-items: center; - gap: 3px; - font-size: 11px; - font-weight: 500; - color: var(--accent, #646cff); - flex-shrink: 0; -} -.project-instructions-preview { - width: 100%; - max-height: 160px; - overflow-y: auto; - padding: 8px 10px; - font-size: 13px; - line-height: 1.5; - white-space: pre-wrap; - word-break: break-word; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--bg); - color: var(--text); -} - -/* Settings: custom instructions textarea */ -.custom-instructions-input { - width: 100%; - min-height: 80px; - max-height: 200px; - padding: 10px 12px; - font-size: 13px; - line-height: 1.5; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--bg); - color: var(--text); - resize: vertical; - font-family: inherit; -} -.custom-instructions-input:focus { - outline: none; - border-color: var(--accent, #646cff); -} - -.topbar { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 16px; - border-bottom: 1px solid var(--border); - background: var(--bg); - gap: 16px; - flex-wrap: wrap; -} -.topbar-left { display: flex; flex-direction: row; align-items: center; gap: 12px; min-width: 0; } -.topbar-title { display: flex; flex-direction: column; gap: 1px; min-width: 0; } -.topbar .title { font-weight: 600; font-size: 14px; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.topbar .meta { color: var(--text-muted); font-size: 11.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.topbar-right { display: flex; gap: 6px; align-items: center; flex-wrap: nowrap; } - -.topbar .brand-mark { - display: inline-flex; - width: 30px; - height: 30px; - border-radius: 50%; - align-items: center; - justify-content: center; - background: linear-gradient(135deg, var(--accent-tint) 0%, var(--accent-soft) 100%); - color: var(--accent); - flex-shrink: 0; - overflow: hidden; -} -.topbar .brand-mark .brand-mark-img { - width: 100%; - height: 100%; - object-fit: contain; - display: block; - padding: 1px; - user-select: none; - -webkit-user-drag: none; -} - -.topbar-left .back-btn { - align-self: center; - padding: 4px 10px; - font-size: 12px; - background: transparent; - border-color: transparent; - color: var(--text-muted); -} -.topbar-left .back-btn:hover { background: var(--bg-subtle); color: var(--text); border-color: var(--border); } - -/* -------- Avatar menu (replaces in-topbar agent picker) ------------- */ -.avatar-menu { position: relative; } -.avatar-btn { - width: 32px; - height: 32px; - padding: 0; - border-radius: 50%; - background: linear-gradient(135deg, var(--accent-tint) 0%, var(--accent-soft) 100%); - border: 1px solid var(--border); - display: inline-flex; - align-items: center; - justify-content: center; - overflow: hidden; - transition: box-shadow 120ms ease, transform 120ms ease; -} -.avatar-btn:hover:not(:disabled) { - box-shadow: 0 0 0 3px rgba(194, 83, 45, 0.18); - border-color: transparent; -} -.avatar-btn:focus-visible { - outline: none; - box-shadow: 0 0 0 3px rgba(194, 83, 45, 0.32); - border-color: transparent; -} -.avatar-btn:active:not(:disabled) { transform: scale(0.96); } -.avatar-btn-photo { - width: 100%; - height: 100%; - object-fit: cover; - object-position: 50% 22%; - display: block; - user-select: none; - -webkit-user-drag: none; - pointer-events: none; -} -.avatar-popover { - position: absolute; - top: calc(100% + 8px); - right: 0; - z-index: 80; - min-width: 280px; - padding: 6px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-lg); - display: flex; - flex-direction: column; - gap: 2px; -} -[dir="rtl"] .avatar-popover { - right: auto; - left: 0; -} -.avatar-popover-head { - padding: 10px 10px 8px; - display: flex; - flex-direction: column; - gap: 2px; - border-bottom: 1px solid var(--border-soft); - margin-bottom: 4px; -} -.avatar-popover-head .who { font-weight: 600; font-size: 13px; } -.avatar-popover-head .where { font-size: 11.5px; color: var(--text-muted); } -.avatar-item { - display: flex; - align-items: center; - gap: 10px; - width: 100%; - padding: 8px 10px; - font-size: 12.5px; - text-align: left; - background: transparent; - border: none; - border-radius: var(--radius-sm); - cursor: pointer; - color: var(--text); - text-decoration: none; -} -.avatar-item:hover { background: var(--bg-subtle); } -/* `` items inside the popover need explicit color + decoration - resets — the user-agent stylesheet otherwise paints them blue - and underlined, which reads as misalignment next to the button - rows that share the same `.avatar-item` class. */ -a.avatar-item, -a.avatar-item:visited { - color: var(--text); -} -.avatar-item .avatar-item-icon { - width: 18px; - height: 18px; - display: inline-flex; - align-items: center; - justify-content: center; - line-height: 0; - color: var(--text-muted); - flex-shrink: 0; -} -.avatar-item .avatar-item-meta { - margin-left: auto; - font-size: 11px; - color: var(--text-muted); - font-variant-numeric: tabular-nums; - white-space: nowrap; -} -.avatar-section-label { - font-size: 10.5px; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-faint); - font-weight: 600; - padding: 8px 10px 4px; -} -.avatar-model-section { - padding: 2px 10px 6px; - display: flex; - flex-direction: column; - gap: 6px; - border-top: 1px dashed var(--border-soft); - margin-top: 4px; -} -.avatar-select-row { - display: flex; - align-items: center; - gap: 8px; - font-size: 12px; - color: var(--text-muted); -} -.avatar-select-label { - flex-shrink: 0; - min-width: 64px; -} -.avatar-select { - flex: 1; - min-width: 0; - font-size: 12px; - padding: 4px 6px; - border-radius: var(--radius-sm); - border: 1px solid var(--border); - background-color: var(--bg-panel); - color: var(--text); - cursor: pointer; -} -.avatar-select:focus { outline: 2px solid var(--accent-soft, var(--border-strong)); } - -/* Environment pill — only used in entry view header now */ -.env-pill { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 4px 12px 4px 4px; - background: var(--bg-subtle); - border: 1px solid var(--border); - border-radius: var(--radius-pill); - cursor: pointer; - max-width: 360px; - min-width: 0; - font: inherit; - color: inherit; -} -.env-pill:hover { background: var(--bg-panel); border-color: var(--border-strong); } -.env-pill-dot { - width: 22px; height: 22px; border-radius: 50%; - background: linear-gradient(135deg, #d97757 0%, #b85a3b 100%); - flex-shrink: 0; -} -.env-pill-dot[data-mode="api"] { - background: linear-gradient(135deg, #1c1b1a 0%, #4b4948 100%); -} -.env-pill-label { font-weight: 500; font-size: 12px; } -.env-pill-meta { - font-size: 12px; color: var(--text-muted); - white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; -} - -/* -------- Split / panes -------------------------------------------- */ -.split { - display: grid; - grid-template-columns: 460px 8px minmax(400px, 1fr); - min-height: 0; - min-width: 0; - max-width: 100%; - overflow: hidden; -} -.split.is-resizing-chat { - cursor: col-resize; - user-select: none; -} -.split.is-resizing-chat iframe { - pointer-events: none; -} -.split.split-focus { - grid-template-columns: minmax(0, 1fr); -} -.split-chat-slot { - display: flex; - min-height: 0; - min-width: 0; - overflow: hidden; -} -.split-chat-slot[hidden] { - display: none; -} -.split-chat-slot > .pane { - flex: 1 1 auto; - min-width: 0; -} -.pane { - display: flex; - flex-direction: column; - min-height: 0; - min-width: 0; - background: var(--bg-panel); - overflow: hidden; -} -.split-resize-handle { - width: 8px; - min-width: 8px; - height: 100%; - cursor: col-resize; - background: - linear-gradient( - 90deg, - transparent 0, - transparent 3px, - var(--border) 3px, - var(--border) 5px, - transparent 5px - ); - transition: background 120ms ease; - touch-action: none; -} -.split-resize-handle:hover, -.split-resize-handle:focus-visible, -.split.is-resizing-chat .split-resize-handle { - background: - linear-gradient( - 90deg, - color-mix(in srgb, var(--accent) 16%, transparent) 0, - color-mix(in srgb, var(--accent) 16%, transparent) 3px, - var(--accent) 3px, - var(--accent) 5px, - color-mix(in srgb, var(--accent) 16%, transparent) 5px - ); -} -.split-resize-handle:focus-visible { - outline: 2px solid var(--accent); - outline-offset: -2px; -} - -/* -------- Chat sticky header --------------------------------------- */ -.chat-header { - display: flex; - align-items: center; - gap: 4px; - padding: 8px 12px; - border-bottom: 1px solid var(--border); - background: var(--bg-panel); - position: sticky; - top: 0; - z-index: 4; - height: 44px; -} -.chat-header-tabs { display: inline-flex; gap: 16px; flex: 1; } -.chat-header-tab { - background: transparent; - border: none; - border-bottom: 2px solid transparent; - border-radius: 0; - padding: 8px 0; - font-size: 13px; - color: var(--text-muted); - font-weight: 500; -} -.chat-header-tab:hover { color: var(--text); background: transparent; border-color: transparent; } -.chat-header-tab.active { - color: var(--text); - border-bottom-color: var(--text); -} -.chat-active-conversation { - min-width: 0; - flex: 1; - display: inline-flex; - align-items: center; - gap: 6px; -} -.chat-active-conversation-title { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 13px; - font-weight: 600; - color: var(--text); -} -.chat-active-conversation-rename { - width: 24px; - height: 24px; - padding: 0; - border: none; - background: transparent; - color: var(--text-faint); - border-radius: 5px; - display: inline-flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; -} -.chat-active-conversation-rename:hover { - background: var(--bg-subtle); - color: var(--text); -} -.chat-active-conversation-rename:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} -.chat-active-conversation-input { - width: 100%; - min-width: 0; - height: 28px; - padding: 3px 8px; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--bg); - color: var(--text); - font-size: 13px; - font-weight: 600; -} -.chat-active-conversation-input:focus { - outline: none; - border-color: var(--accent); - box-shadow: 0 0 0 2px var(--accent-soft); -} -.chat-header-actions { display: inline-flex; gap: 4px; align-items: center; } -.chat-header-actions .icon-only { - width: 28px; height: 28px; - padding: 0; - background: transparent; - border: none; - border-radius: 6px; - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; -} -.chat-header-actions .icon-only:hover { background: var(--bg-subtle); color: var(--text); } - -.chat-log { - flex: 1; - overflow-y: auto; - overflow-x: hidden; - padding: 16px; - display: flex; - flex-direction: column; - gap: 14px; - min-width: 0; -} - -/* -------- Messages -------------------------------------------------- */ -.msg { - padding: 0; - background: transparent; - border: none; - white-space: normal; - word-wrap: break-word; - min-width: 0; - max-width: 100%; -} -.msg.user { - align-self: flex-end; - display: flex; - flex-direction: column; - align-items: flex-end; - max-width: min(78%, 560px); -} -.msg .role { - display: flex; - align-items: baseline; - gap: 8px; - font-size: 12.5px; - text-transform: none; - color: var(--text-strong); - margin-bottom: 4px; - letter-spacing: 0; - font-weight: 600; -} -.msg-time { - color: var(--text-faint); - font-size: 11px; - font-weight: 500; - font-variant-numeric: tabular-nums; - white-space: nowrap; -} -.chat-day-separator { - display: flex; - align-items: center; - gap: 10px; - color: var(--text-faint); - font-size: 11px; - font-weight: 600; - margin: 4px 0 0; -} -.chat-day-separator::before, -.chat-day-separator::after { - content: ''; - height: 1px; - background: var(--border); - flex: 1; -} -.msg.user .role::before { content: ''; } -.msg.user .role { - justify-content: flex-end; - color: var(--text-muted); -} -.msg.user .user-text { - white-space: pre-wrap; - color: #fff; - background: var(--selected); - border: 1px solid color-mix(in srgb, var(--selected) 88%, #000); - border-radius: 14px 14px 4px 14px; - padding: 8px 11px; - line-height: 1.45; - box-shadow: var(--shadow-xs); -} -.msg-plugin-chip { - display: inline-flex; - align-items: center; - gap: 8px; - margin: 4px 0 6px; - padding: 4px 10px 4px 8px; - border-radius: 999px; - background: var(--bg-subtle); - border: 1px solid var(--border); - font-size: 11.5px; - line-height: 1; - color: var(--text-muted); - max-width: 100%; -} -.msg-plugin-chip__dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--accent); - flex: 0 0 auto; -} -.msg-plugin-chip--design-system .msg-plugin-chip__dot { - background: #d76445; -} -.msg-plugin-chip__label { - display: inline-flex; - align-items: baseline; - gap: 6px; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.msg-plugin-chip__kind { - text-transform: uppercase; - letter-spacing: 0.04em; - font-weight: 600; - color: var(--text-faint); -} -.msg-plugin-chip__title { - color: var(--text); - font-weight: 500; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.msg-plugin-chip__version, -.msg-plugin-chip__task { - color: var(--text-faint); - font-variant-numeric: tabular-nums; -} -.msg-plugin-chip__task { - border-left: 1px solid var(--border); - padding-left: 8px; -} - -/* Wrapper for user bubble + copy button */ -.msg.user .user-text-wrap { - position: relative; - display: inline-block; - max-width: 100%; -} -.msg.user .user-text-wrap .user-text { - padding-inline-end: 28px; -} -.msg.user .user-status-wrap { - display: flex; - justify-content: flex-end; -} -.user-status-card { - display: inline-flex; - align-items: center; - gap: 10px; - max-width: min(320px, 100%); - padding: 9px 11px; - border-radius: 14px 14px 4px 14px; - background: var(--bg-subtle); - border: 1px solid var(--border); - box-shadow: var(--shadow-xs); - color: var(--text); - text-align: start; -} -.user-status-card__icon { - width: 28px; - height: 28px; - border-radius: 8px; - display: inline-flex; - align-items: center; - justify-content: center; - flex: 0 0 auto; - color: var(--accent); - background: color-mix(in srgb, var(--accent) 12%, transparent); -} -.user-status-card__copy { - min-width: 0; - display: grid; - gap: 2px; -} -.user-status-card__copy strong { - font-size: 12.5px; - line-height: 1.2; - font-weight: 650; -} -.user-status-card__copy span { - font-size: 11.5px; - line-height: 1.35; - color: var(--text-muted); -} - -.msg.user .user-copy-btn { - position: absolute; - inset-block-start: 6px; - inset-inline-end: 6px; - padding: 4px; - border: none; - background: none; - border-radius: 4px; - opacity: 0; - transition: opacity 120ms ease; - color: rgba(255, 255, 255, 0.85); - cursor: pointer; - z-index: 1; -} -.msg.user .user-copy-btn:hover { - color: #fff; - background: rgba(255, 255, 255, 0.12); -} -.msg.user .user-text-wrap:hover .user-copy-btn, -.msg.user .user-text-wrap:focus-within .user-copy-btn, -.msg.user .user-copy-btn:focus-visible { - opacity: 1; -} -.msg.user .user-copy-btn:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 1px; -} -@media (hover: none) { - .msg.user .user-copy-btn { - opacity: 1; - } -} -.msg.assistant .prose { margin-top: 4px; } -.msg .artifact-badge { - display: inline-block; - margin-top: 8px; - padding: 2px 8px; - font-size: 11px; - background: var(--accent); - color: white; - border-radius: 4px; -} -.msg.error { - border: 1px solid var(--red-border); - background: var(--red-bg); - color: var(--red); - padding: 10px 12px; - border-radius: var(--radius-sm); - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; -} -.chat-error-text { min-width: 0; } -.chat-error-retry { - flex: 0 0 auto; - border-color: var(--red-border); - color: var(--red); - padding: 4px 10px; -} - -/* -------- Composer -------------------------------------------------- */ -.composer { - border-top: 1px solid var(--border); - padding: 10px; - display: flex; - flex-direction: column; - gap: 8px; - background: var(--bg-panel); - position: relative; - z-index: 1; -} -/* Elevation kicks in only when a pinned todo card is sitting directly - * above the composer. Without the card the chat scroll already provides - * its own visual edge; the shadow would just darken empty whitespace. */ -.chat-pinned-todo + .composer { - box-shadow: 0 -10px 22px -10px rgba(0, 0, 0, 0.22); -} -/* Dark mode: the same alpha disappears against the dark panel, so push - * it harder and let it spread a bit further. */ -[data-theme="dark"] .chat-pinned-todo + .composer { - box-shadow: 0 -14px 28px -12px rgba(0, 0, 0, 0.65); -} -@media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]) .chat-pinned-todo + .composer { - box-shadow: 0 -14px 28px -12px rgba(0, 0, 0, 0.65); - } -} -.composer-shell { - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--bg-panel); - box-shadow: var(--shadow-xs); - padding: 8px 10px 6px; - display: flex; - flex-direction: column; - gap: 6px; - transition: border-color 120ms ease, box-shadow 120ms ease; -} -.composer-shell:focus-within { - border-color: var(--border-strong); - box-shadow: var(--shadow-sm); -} -.composer.drag-active .composer-shell { - border-color: var(--accent); - box-shadow: 0 0 0 3px var(--accent-soft); -} -.composer textarea { - min-height: 88px; - max-height: min(184px, 34vh); - border: none; - background: transparent; - padding: 4px 4px; - resize: none; - overflow-y: hidden; -} -.composer textarea:focus { outline: none; box-shadow: none; } -.composer-input-wrap { - position: relative; - display: flex; - flex-direction: column; -} -.composer-textarea-layer { - position: relative; - min-height: 88px; -} -.composer-input-overlay { - position: absolute; - inset: 0; - z-index: 2; - min-height: 88px; - padding: 0; - color: var(--text); - font: inherit; - line-height: normal; - pointer-events: none; - white-space: pre-wrap; - overflow: hidden; - overflow-wrap: anywhere; -} -.composer-input-overlay-inner { - min-height: 100%; - padding: 4px; - transform: translateY(calc(-1 * var(--composer-input-scroll, 0px))); -} -.composer-input-wrap.has-mention-overlay textarea { - color: transparent; - caret-color: var(--text); -} -.composer-input-wrap.has-mention-overlay textarea::selection { - background: color-mix(in srgb, var(--accent) 24%, transparent); - color: transparent; -} -.composer-inline-mention { - display: inline; - max-width: none; - margin: 0; - padding: 0; - border: 0; - border-radius: 5px; - background: color-mix(in srgb, var(--accent) 8%, transparent); - box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 12%, transparent); - box-decoration-break: clone; - -webkit-box-decoration-break: clone; - color: color-mix(in srgb, var(--accent) 62%, var(--text)); - font: inherit; - font-size: inherit; - font-weight: inherit; - line-height: inherit; - letter-spacing: inherit; - vertical-align: baseline; - overflow: visible; - text-overflow: clip; - white-space: inherit; -} -.composer-row { - display: flex; - align-items: center; - gap: 6px; - padding-top: 4px; -} -.composer-row .icon-btn { - width: 28px; height: 28px; - padding: 0; - background: transparent; - border: none; - border-radius: 6px; - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 14px; -} -.composer-row .icon-btn:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text); } -.composer-spacer { flex: 1; } -.composer-import { - background: transparent; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - padding: 4px 12px; - font-size: 12px; - color: var(--text-muted); -} -.composer-import:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text); } -.composer-research { - display: inline-flex; - align-items: center; - gap: 6px; - background: transparent; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - padding: 4px 10px; - font-size: 12px; - color: var(--text-muted); - cursor: pointer; -} -.composer-research:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text); } -.composer-research.on { - background: color-mix(in oklab, var(--accent) 12%, transparent); - border-color: var(--accent); - color: var(--accent); -} -.composer-research-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--accent); -} -.composer-send { - display: inline-flex; - align-items: center; - gap: 6px; - background: var(--accent); - border-color: var(--accent); - color: white; - font-weight: 500; - padding: 4px 14px; - font-size: 12.5px; -} -.composer-send:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); } -.composer-send.stop { background: var(--text); border-color: var(--text); color: var(--bg); } -.composer-send.stop:hover { background: var(--text-strong); border-color: var(--text-strong); color: var(--bg); } -.composer-hint { - font-size: 11px; - color: var(--text-faint); - margin: 0 8px; -} - -.preview-draw-note-input::placeholder { - color: rgba(255, 225, 210, 0.86); -} - -.preview-draw-note-input:focus { - background: rgba(218, 97, 56, 0.26) !important; - border-color: rgba(255, 184, 140, 0.95) !important; - box-shadow: 0 0 0 3px rgba(218, 97, 56, 0.34), 0 0 18px rgba(218, 97, 56, 0.24) !important; -} - -/* -------- Staged attachments -------------------------------------- */ -.staged-row { - display: flex; - flex-wrap: wrap; - gap: 6px; - padding: 4px 4px 0; -} -.staged-chip { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 8px 4px 4px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius); - max-width: 220px; - font-size: 11.5px; - box-shadow: var(--shadow-xs); -} -.staged-chip img { - width: 28px; - height: 28px; - border-radius: 6px; - object-fit: cover; -} -.staged-preview-trigger { - display: inline-flex; - align-items: center; - gap: 6px; - min-width: 0; - max-width: 100%; - flex: 1 1 auto; - padding: 0; - border: 0; - background: transparent; - color: inherit; - text-align: left; -} -.staged-preview-trigger:hover:not(:disabled) { - background: transparent; - border-color: transparent; -} -.staged-preview-trigger img { - flex: 0 0 28px; - width: 28px; - height: 28px; -} -.staged-preview-trigger .staged-name { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.staged-preview-modal { - position: fixed; - inset: 0; - z-index: 1200; - display: flex; - align-items: center; - justify-content: center; - padding: 24px; - background: rgba(17, 24, 39, 0.44); -} -.staged-preview-card { - display: flex; - flex-direction: column; - gap: 10px; - width: min(720px, calc(100vw - 48px)); - max-height: calc(100vh - 48px); - padding: 12px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-lg); -} -.staged-preview-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - min-width: 0; -} -.staged-preview-head span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--text); - font-size: 13px; - font-weight: 600; -} -.staged-preview-card > img { - display: block; - max-width: 100%; - max-height: min(70vh, 640px); - object-fit: contain; - border-radius: var(--radius); - background: var(--bg); -} -.staged-comment { - border-radius: var(--radius-pill); - padding: 4px 6px 4px 10px; -} -.user-attachment.staged-comment { - max-width: 100%; - max-height: 200px; - overflow-y: auto; -} -.staged-comment .staged-name { - display: inline-flex; - align-items: center; - gap: 6px; - min-width: 0; - flex-wrap: wrap; -} -.staged-comment .staged-name strong { - color: var(--accent-strong); - font-weight: 650; - flex: 0 0 auto; -} -.staged-comment .staged-name span { - white-space: normal; - word-break: break-word; -} -.staged-comment button { - width: 20px; - height: 20px; - padding: 0; - border: 0; - border-radius: var(--radius-pill); - background: transparent; - color: var(--text-faint); - font-size: 14px; - line-height: 1; -} -.staged-comment button:hover { - background: var(--bg-subtle); - color: var(--text); -} -.staged-icon { - width: 28px; height: 28px; - border-radius: 6px; - background: var(--bg-subtle); - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 14px; -} -.staged-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 140px; - color: var(--text); -} -.staged-remove { - background: transparent; - border: none; - padding: 0 2px; - color: var(--text-faint); - cursor: pointer; - font-size: 13px; - line-height: 1; - border-radius: 4px; -} -.staged-remove:hover { color: var(--red); background: var(--red-bg); } - -.linked-dirs-row { - display: flex; - gap: 6px; - flex-wrap: wrap; -} -.linked-dir-chip { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 8px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - font-size: 11.5px; - color: var(--text-muted); - max-width: 220px; -} -.linked-dir-chip svg { flex-shrink: 0; color: var(--text-muted); } -.linked-dir-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.user-attachments { - display: flex; - gap: 6px; - flex-wrap: wrap; - justify-content: flex-end; - margin-bottom: 8px; -} -.user-attachment { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 4px 10px 4px 4px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius); - font-size: 11.5px; - max-width: 240px; - color: var(--text); - cursor: default; - text-align: left; - font: inherit; - font-size: 11.5px; -} -.user-attachment.openable { cursor: pointer; } -.user-attachment.openable:hover { - background: var(--bg-subtle); - border-color: var(--accent); -} -.user-attachment img { - width: 28px; - height: 28px; - object-fit: cover; - border-radius: 6px; - display: block; -} -.user-attachment .staged-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* -------- Mention popover ------------------------------------------- */ -.mention-popover { - order: -1; - flex: 0 0 clamp(300px, 52vh, 480px); - min-height: 248px; - max-height: min(480px, 72vh); - margin: 0 0 6px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-md); - overflow: hidden; - display: flex; - flex-direction: column; -} -.mention-tabs { - flex: 0 0 auto; - display: flex; - align-items: center; - gap: 4px; - min-height: 42px; - padding: 6px; - background: var(--bg-subtle); - border-bottom: 1px solid var(--border-soft); - overflow-x: auto; - scrollbar-width: none; -} -.mention-tabs::-webkit-scrollbar { - display: none; -} -.mention-tab { - flex: 0 0 auto; - display: inline-flex; - align-items: center; - justify-content: center; - min-width: max-content; - min-height: 30px; - padding: 5px 10px; - border: 1px solid transparent; - border-radius: var(--radius-sm); - background: transparent; - color: var(--text-muted); - font: inherit; - font-size: 11.5px; - line-height: 18px; - cursor: pointer; -} -.mention-tab.active { - background: var(--bg-panel); - color: var(--text); - border-color: var(--border); - box-shadow: var(--shadow-xs); -} -.mention-results { - flex: 1 1 auto; - min-height: 0; - overflow-y: auto; - padding: 3px 0; -} -.mention-empty { - padding: 14px 10px; - color: var(--text-muted); - font-size: 12px; - text-align: center; - line-height: 1.45; -} -.mention-item { - display: flex; - align-items: center; - width: 100%; - background: transparent; - border: none; - padding: 7px 10px; - font-size: 12px; - text-align: left; - gap: 8px; - color: var(--text); - min-height: 32px; -} -.mention-item:hover { background: var(--bg-subtle); border-color: transparent; } -.mention-section-label { - font-size: 10px; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--text-muted); - padding: 4px 8px 2px; -} -.mention-section-label + .mention-section-label, -.mention-item + .mention-section-label { - margin-top: 4px; - border-top: 1px solid var(--border); -} -.mention-item--plugin { - align-items: flex-start; -} -.mention-item-body { - display: flex; - flex-direction: column; - gap: 2px; - flex: 1; - min-width: 0; -} -.mention-item-body strong { - font-weight: 500; - font-size: 12px; -} -.mention-meta--desc { - white-space: normal; - font-size: 10.5px; - line-height: 1.3; - color: var(--text-muted); - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; -} -.mention-item code { - flex: 1; - font-size: 11px; - background: transparent; - padding: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.mention-meta { color: var(--text-muted); font-size: 10px; flex-shrink: 0; } - -/* Section header inside the @-popover when both skills and files appear. */ -.mention-section-head { - padding: 6px 10px 2px; - font-size: 10px; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-muted); -} -.mention-skill-item { flex-direction: column; align-items: flex-start; gap: 2px; } -.mention-skill-row { - display: inline-flex; - align-items: center; - gap: 6px; - width: 100%; -} -.mention-skill-row code { flex: none; font-size: 11px; } -.mention-skill-badge { - display: inline-flex; - align-items: center; - height: 16px; - padding: 0 6px; - border-radius: 999px; - background: var(--bg-subtle); - color: var(--text-muted); - font-size: 9px; - text-transform: uppercase; - letter-spacing: 0.04em; -} -.mention-skill-desc { - color: var(--text-muted); - font-size: 11px; - line-height: 1.4; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -/* Staged skill chips above the textarea. Reuse the staged-chip baseline - so they line up with attachment chips. */ -.staged-skills-row { gap: 6px; flex-wrap: wrap; } -.staged-chip.staged-skill { - background: color-mix(in oklab, var(--accent) 8%, var(--bg-subtle)); - border-color: color-mix(in oklab, var(--accent) 28%, var(--border)); -} -.staged-chip.staged-skill .staged-icon { color: var(--accent); } -.staged-chip.staged-skill-user { - background: color-mix(in oklab, var(--accent) 14%, var(--bg-subtle)); -} - -/* =========================================================== - Modal / Settings - =========================================================== */ -.modal-backdrop { - position: fixed; inset: 0; - background: rgba(28, 27, 26, 0.42); - backdrop-filter: blur(4px); - -webkit-backdrop-filter: blur(4px); - -webkit-app-region: no-drag; - display: flex; - align-items: center; - justify-content: center; - z-index: 100; - animation: fade-in 160ms ease-out; -} -.modal { - background: var(--bg-elevated); - -webkit-app-region: no-drag; - border-radius: var(--radius-lg); - padding: 22px; - width: 520px; - max-width: calc(100vw - 32px); - display: flex; - flex-direction: column; - gap: 12px; - box-shadow: var(--shadow-lg); - animation: pop-in 220ms cubic-bezier(0.21, 1.02, 0.73, 1); -} -.modal h2 { margin: 0; font-size: 18px; letter-spacing: -0.01em; font-weight: 600; } -.modal label { - display: flex; - flex-direction: column; - gap: 4px; - font-size: 12px; - color: var(--text-muted); -} -.modal .hint { - font-size: 12px; - color: var(--text-muted); - line-height: 1.55; - margin: 0; -} -.modal .row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; } - -.updater-popup { - position: absolute; - top: 0; - left: calc(100% + 12px); - z-index: 80; - width: min(360px, calc(100vw - var(--entry-rail-width, 56px) - 24px)); - display: grid; - grid-template-columns: 44px minmax(0, 1fr); - gap: 14px; - padding: 18px; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--bg-elevated); - color: var(--text); - box-shadow: var(--shadow-lg); - -webkit-app-region: no-drag; -} - -[dir='rtl'] .entry-updater-menu .updater-popup { - left: auto; - right: calc(100% + 12px); -} - -.entry-main__topbar .entry-updater-menu .updater-popup { - top: calc(100% + 10px); - right: 0; - left: auto; -} - -[dir='rtl'] .entry-main__topbar .entry-updater-menu .updater-popup { - right: auto; - left: 0; -} - -.updater-popup__icon { - width: 44px; - height: 44px; - display: grid; - place-items: center; - border-radius: 8px; - background: color-mix(in oklab, var(--accent) 12%, var(--bg-subtle)); - color: var(--accent); -} - -.updater-popup__body { - min-width: 0; -} - -.updater-popup__body h2 { - margin: 0; - font-size: 16px; - line-height: 1.25; - font-weight: 650; -} - -.updater-popup__body p { - margin: 6px 0 0; - color: var(--text-muted); - font-size: 13px; - line-height: 1.45; -} - -.updater-popup__error { - color: var(--red) !important; -} - -.updater-popup__actions { - grid-column: 1 / -1; - display: flex; - flex-wrap: wrap; - justify-content: flex-end; - gap: 8px; - min-height: 34px; -} - -.updater-popup__button { - min-height: 34px; - padding: 0 12px; - border: 1px solid var(--border-strong); - border-radius: 7px; - background: var(--bg-panel); - color: var(--text); - font: inherit; - font-size: 13px; - cursor: pointer; -} - -.updater-popup__button:hover { - border-color: var(--text-muted); -} - -.updater-popup__button:disabled { - cursor: default; - opacity: 0.55; -} - -.updater-popup__button--primary { - border-color: var(--accent); - background: var(--accent); - color: #ffffff; -} - -@media (max-width: 560px) { - .updater-popup { - width: calc(100vw - var(--entry-rail-width, 56px) - 18px); - } -} - -/* Compact rename modal */ -.modal-rename { - width: 420px; - gap: 14px; -} -.modal-rename input[type="text"] { - width: 100%; - padding: 9px 12px; - border-radius: 8px; - border: 1px solid var(--border-strong); - background: var(--bg-panel); - color: var(--text-strong); - font-size: 13px; - outline: none; -} -.modal-rename input[type="text"]:focus { - border-color: var(--accent); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent); -} -.modal-rename .row button { - padding: 8px 18px; - border-radius: 999px; - font-size: 13px; - font-weight: 500; - cursor: pointer; - border: 1px solid var(--border); - background: var(--bg-subtle); - color: var(--text-strong); -} -.modal-rename .row button:hover { border-color: var(--border-strong); } -.modal-rename .row button.primary { - background: var(--accent); - border-color: var(--accent); - color: #fff; -} -.modal-rename .row button.primary:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -/* Confirm modal — same shell, danger primary */ -.modal-confirm { - width: 420px; - gap: 12px; -} -.modal-confirm-message { - margin: 0; - font-size: 13px; - color: var(--text-strong); - line-height: 1.55; -} -.modal-confirm-error { - margin: 0; - font-size: 13px; - color: var(--red); - line-height: 1.55; -} -.modal-confirm .row { margin-top: 8px; } -.modal-confirm .row button { - padding: 8px 18px; - border-radius: 999px; - font-size: 13px; - font-weight: 500; - cursor: pointer; - border: 1px solid var(--border); - background: var(--bg-subtle); - color: var(--text-strong); -} -.modal-confirm .row button:hover { border-color: var(--border-strong); } -.modal-confirm .row button.primary.danger { - background: var(--red); - border-color: var(--red); - color: #fff; -} -.modal-confirm .row button.primary.danger:hover { filter: brightness(0.95); } - -/* Project category tags */ -.design-card-tag { - display: inline-flex; - align-items: center; - align-self: flex-start; - padding: 1px 7px; - border-radius: 999px; - font-size: 10.5px; - font-weight: 500; - line-height: 1.5; - letter-spacing: 0.01em; - background: var(--bg-subtle); - color: var(--text-muted); - border: 1px solid var(--border); - margin-bottom: 2px; -} -.design-card-tag-row { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 5px; - min-width: 0; -} -.design-card-tag.tag-prototype { - color: #2348b8; - background: #e8efff; - border-color: rgba(35, 72, 184, 0.18); -} -.design-card-tag.tag-live-artifact { - color: #6d4ff5; - background: rgba(116, 92, 255, 0.12); - border-color: rgba(116, 92, 255, 0.28); -} -.design-card-tag.tag-slide { - color: #b15e00; - background: rgba(255, 159, 64, 0.14); - border-color: rgba(255, 159, 64, 0.32); -} -.design-card-tag.tag-media { - color: #1c8a73; - background: rgba(28, 138, 115, 0.12); - border-color: rgba(28, 138, 115, 0.28); -} -.design-card-tag.tag-design-system { - color: var(--red); - background: color-mix(in srgb, var(--red) 9%, var(--bg-panel)); - border-color: color-mix(in srgb, var(--red) 34%, transparent); -} - -@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } -@keyframes pop-in { - from { opacity: 0; transform: translateY(6px) scale(0.98); } - to { opacity: 1; transform: translateY(0) scale(1); } -} -@keyframes pulse { - 0%, 100% { opacity: 1; transform: scale(1); } - 50% { opacity: 0.6; transform: scale(0.85); } -} - -.modal-settings { - --modal-padding: 24px; - width: min(920px, calc(100vw - 48px)); - /* Fixed height regardless of which section is active — short sections - pad out with empty space inside `.settings-content`, long sections - scroll internally. Keeps the dialog from jumping in size as the user - switches sidebar items. */ - height: min(720px, calc(100vh - 64px)); - padding: 0; - gap: 0; - /* Anchor for the absolutely-positioned `.settings-chrome` strip - (close button + autosave indicator). Without this the chrome - would escape to the viewport on long-scrolling sections. */ - position: relative; -} -@media (max-height: 600px) { - .modal-settings { height: 90vh; } -} -.modal-settings .modal-body { - overflow: hidden; - flex: 1; - min-height: 0; - display: grid; - grid-template-columns: 240px minmax(0, 1fr); - gap: 0; - margin: 0; - padding: 0; -} -.modal-head { - display: flex; - flex-direction: column; - gap: 4px; - flex-shrink: 0; - padding: var(--modal-padding); -} -.modal-head .kicker { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.08em; - color: var(--text-muted); - font-weight: 600; -} -.modal-head h2 { - font-size: 22px; - font-weight: 600; - letter-spacing: -0.015em; - color: var(--text); -} -.modal-head .subtitle { - margin: 4px 0 0; - font-size: 13px; - color: var(--text-muted); - line-height: 1.55; - /* 72ch lets typical one-sentence English subtitles fit on a single - * line inside the 920px settings modal while still wrapping cleanly - * on narrow viewports (the modal width clamps to 100vw - 48px) and - * for the longest locales (German, French). 50ch was forcing even - * the English subtitle onto two lines. See nexu-io/open-design#743. */ - max-width: 72ch; -} -.modal-foot { - display: flex; - justify-content: flex-end; - gap: 8px; - padding: 16px var(--modal-padding, 22px) 12px; - border-top: 1px solid var(--border); - margin-top: 0; - flex-shrink: 0; -} - -/* Top-right chrome strip for the Settings dialog. Floats above the - sidebar/content rhythm so the close affordance and the autosave - indicator never compete with the title or sidebar nav. The strip - is a flex row right-anchored to the modal corner; the autosave - pill comes first so the close button keeps a stable optical - position and the user's eye returns to the same place after a - save settles. */ -.settings-chrome { - position: absolute; - top: 14px; - right: 14px; - z-index: 10; - display: flex; - align-items: center; - gap: 8px; -} -.settings-chrome > * { - pointer-events: auto; -} - -/* Close button. Minimal circular icon button with a hairline ring - that warms on hover and snaps to the accent on focus-visible. - Sized to read as a passive corner control rather than a primary - CTA — the autosave indicator next to it carries any system - feedback the user might need. */ -.settings-close { - display: inline-flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - padding: 0; - border-radius: 999px; - border: 1px solid var(--border); - background: color-mix(in srgb, var(--bg-panel) 90%, transparent); - color: var(--text-muted); - cursor: pointer; - transition: color 140ms ease, border-color 140ms ease, background-color 140ms ease, transform 140ms ease; - backdrop-filter: blur(6px); - -webkit-backdrop-filter: blur(6px); -} -.settings-close:hover { - color: var(--text); - border-color: color-mix(in srgb, var(--text) 22%, var(--border)); - background: var(--bg-panel); - transform: scale(1.04); -} -.settings-close:active { - transform: scale(0.96); -} -.settings-close:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - color: var(--text); -} -@media (prefers-reduced-motion: reduce) { - .settings-close { transition: color 80ms linear, border-color 80ms linear; transform: none !important; } -} - -/* Autosave status pill. Lives in the chrome strip now (next to the - close button) instead of a footer. Renders nothing while idle so - the chrome reads as a single close button until something is - actually saving; settles to a green check on success and red on - failure. The pill never takes focus and never blocks input — it - is a passive system message announced to assistive tech via - aria-live on the wrapper. */ -.settings-autosave { - display: inline-flex; - align-items: center; - gap: 6px; - min-height: 24px; - padding: 4px 10px; - border-radius: 999px; - font-size: 11.5px; - font-weight: 500; - letter-spacing: 0.005em; - color: var(--text-muted); - background: color-mix(in srgb, var(--bg-panel) 90%, transparent); - border: 1px solid transparent; - backdrop-filter: blur(6px); - -webkit-backdrop-filter: blur(6px); - transition: opacity 160ms ease, color 160ms ease, background-color 160ms ease, border-color 160ms ease, transform 160ms ease; - pointer-events: none; - user-select: none; - white-space: nowrap; -} -.settings-autosave.is-idle { - opacity: 0; - transform: translateY(-2px); - /* Collapse the visual weight so the chrome strip is just the - close button when nothing is happening. */ - padding: 0; - border-color: transparent; - background: transparent; - min-height: 0; -} -.settings-autosave.is-pending, -.settings-autosave.is-saving { - opacity: 1; - color: var(--text-muted); - background: color-mix(in srgb, var(--text-muted) 12%, var(--bg-panel)); - border-color: color-mix(in srgb, var(--text-muted) 18%, transparent); -} -.settings-autosave.is-saved { - opacity: 1; - color: var(--green, #1f9d55); - background: color-mix(in srgb, var(--green, #1f9d55) 14%, var(--bg-panel)); - border-color: color-mix(in srgb, var(--green, #1f9d55) 30%, transparent); - animation: settingsAutosavePop 220ms ease-out; -} -.settings-autosave.is-error { - opacity: 1; - color: var(--red, #d23434); - background: color-mix(in srgb, var(--red, #d23434) 14%, var(--bg-panel)); - border-color: color-mix(in srgb, var(--red, #d23434) 32%, transparent); -} -@keyframes settingsAutosavePop { - from { transform: translateY(-2px) scale(0.98); } - to { transform: translateY(0) scale(1); } -} -@media (prefers-reduced-motion: reduce) { - .settings-autosave { transition: opacity 80ms linear; animation: none !important; } -} - -/* Hide the verbose error copy on narrow viewports so the chrome - strip doesn't crowd the close button. The icon + tooltip carry - the meaning, and the screen-reader announcement still fires. */ -@media (max-width: 720px) { - .settings-autosave.is-error span, - .settings-autosave.is-saved span, - .settings-autosave.is-pending span, - .settings-autosave.is-saving span { - display: none; - } - .settings-autosave.is-idle { padding: 0; } -} - -/* Make sure header copy doesn't crash into the close button on - narrow viewports. The kicker/title/subtitle should keep their - normal width but reserve right-side gutter for the chrome. */ -.modal-settings .modal-head { - padding-right: calc(var(--modal-padding) + 56px); -} - -/* Compact settings header: place subtitle on the same line as the h2 - so opening the dialog doesn't burn 2-3 rows of header height. The - row uses baseline alignment so the smaller subtitle sits at the - h2 baseline and wraps to its own line on narrow viewports. The - welcome hero variant is unaffected — it keeps the stacked layout. */ -.modal-settings .modal-head-line { - display: flex; - align-items: baseline; - flex-wrap: wrap; - column-gap: 16px; - row-gap: 4px; -} -.modal-settings .modal-head-line > h2 { - flex: 0 0 auto; - margin: 0; -} -.modal-settings .modal-head-line > .subtitle { - margin: 0; - flex: 1 1 240px; - font-size: 12.5px; - /* Override the 72ch cap from the stacked variant so the subtitle - can take the full remaining inline width before wrapping. */ - max-width: none; -} - -/* Section-local Save key button for the Composio API key field. We do - NOT autosave secrets, so this is the explicit gesture. Styled as a - primary button to stand out next to the password input + ghost - Clear, with a tighter vertical rhythm so it sits flush in the - field-row alongside the input. */ -.settings-connectors-save { - display: inline-flex; - align-items: center; - gap: 6px; - white-space: nowrap; -} -.settings-connectors-save.is-busy { - opacity: 0.85; - cursor: progress; -} -.settings-section-connectors .field-row { - /* Allow the input + Save key + Clear triplet to wrap on narrow widths - instead of crushing the input. */ - flex-wrap: wrap; -} -@media (max-width: 540px) { - .settings-section-connectors .field-row > input, - .settings-section-connectors .field-row > .field-input-skeleton-wrap { - flex: 1 1 100%; - } -} - -/* Two-stage destructive confirmation for clearing the saved Composio - API key. Step 1 ("confirm") is a soft amber-ish warning rooted in - the same red palette as other destructive surfaces so it reads as - "this will undo something" without screaming. Step 2 ("final") leans - into red with a brief arming animation on the commit button so a - reflex double-click cannot blow through both stages. The whole panel - collapses inline beneath the credentials field so the destructive - action stays visually anchored to the row that started it. */ -.settings-connectors-clear.is-arming { - border-color: var(--red-border); - color: var(--red); - background: color-mix(in srgb, var(--red-bg) 65%, transparent); -} -.settings-connectors-clear-confirm { - margin-top: 10px; - display: grid; - grid-template-columns: 26px minmax(0, 1fr) auto; - align-items: start; - gap: 12px; - padding: 12px 14px; - border-radius: 12px; - border: 1px solid var(--red-border); - background: color-mix(in srgb, var(--red-bg) 78%, transparent); - color: var(--text); - font-size: 12.5px; - line-height: 1.45; - animation: settings-connectors-clear-confirm-in 180ms ease-out; -} -.settings-connectors-clear-confirm.is-final { - background: var(--red-bg); - border-color: color-mix(in srgb, var(--red) 55%, var(--red-border)); - box-shadow: - 0 0 0 1px color-mix(in srgb, var(--red) 18%, transparent), - 0 8px 24px -16px color-mix(in srgb, var(--red) 80%, transparent); -} -@keyframes settings-connectors-clear-confirm-in { - from { opacity: 0; transform: translateY(-3px); } - to { opacity: 1; transform: translateY(0); } -} -.settings-connectors-clear-confirm-icon { - width: 24px; - height: 24px; - border-radius: 999px; - border: 1px solid color-mix(in srgb, var(--red) 35%, var(--red-border)); - background: var(--bg-panel); - color: var(--red); - display: inline-flex; - align-items: center; - justify-content: center; - font-weight: 700; - font-size: 13px; - line-height: 1; - margin-top: 2px; - flex-shrink: 0; -} -.settings-connectors-clear-confirm.is-final .settings-connectors-clear-confirm-icon { - background: var(--red); - color: var(--bg-panel); - border-color: var(--red); -} -.settings-connectors-clear-confirm-glyph { - display: inline-block; - transform: translateY(-0.5px); -} -.settings-connectors-clear-confirm-copy { - display: flex; - flex-direction: column; - gap: 3px; - min-width: 0; -} -.settings-connectors-clear-confirm-copy strong { - font-size: 13px; - font-weight: 650; - color: var(--text); - line-height: 1.3; -} -.settings-connectors-clear-confirm-copy span { - color: var(--text-muted); - overflow-wrap: anywhere; -} -.settings-connectors-clear-confirm.is-final .settings-connectors-clear-confirm-copy strong { - color: var(--red); -} -.settings-connectors-clear-confirm-actions { - display: flex; - gap: 6px; - flex-shrink: 0; - align-self: center; -} -/* "Continue" — moves to stage 2. Styled as a quiet outlined button - tinted with the red palette so it reads as "destructive but not - yet committed". */ -.settings-connectors-clear-step { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 7px 12px; - border-radius: 999px; - border: 1px solid var(--red-border); - background: var(--bg-panel); - color: var(--red); - font-size: 12px; - font-weight: 600; - cursor: pointer; - transition: - background 120ms ease-out, - border-color 120ms ease-out, - transform 120ms ease-out; -} -.settings-connectors-clear-step:hover { - background: color-mix(in srgb, var(--red-bg) 60%, var(--bg-panel)); - border-color: var(--red); -} -.settings-connectors-clear-step:active { - transform: translateY(1px); -} -/* Final commit button. Solid red with an arming sweep that fills the - left edge for ~700ms before the click is honored. While "arming", - the button is visually hot but disabled so an Enter-spam can't get - ahead of the user's intent; once armed, the label swaps to the - destructive verb and the click commits. */ -.settings-connectors-clear-commit { - position: relative; - overflow: hidden; - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 200px; - padding: 8px 14px; - border-radius: 999px; - border: 1px solid var(--red); - background: var(--red); - color: var(--bg-panel); - font-size: 12px; - font-weight: 650; - letter-spacing: 0.01em; - cursor: pointer; - transition: - transform 120ms ease-out, - box-shadow 160ms ease-out, - background 160ms ease-out; -} -.settings-connectors-clear-commit:disabled, -.settings-connectors-clear-commit[aria-disabled='true'] { - cursor: progress; - background: color-mix(in srgb, var(--red) 70%, var(--bg-panel)); - border-color: color-mix(in srgb, var(--red) 60%, var(--red-border)); -} -.settings-connectors-clear-commit.is-armed { - box-shadow: - 0 0 0 3px color-mix(in srgb, var(--red) 22%, transparent), - 0 10px 20px -12px color-mix(in srgb, var(--red) 75%, transparent); -} -.settings-connectors-clear-commit.is-armed:hover { - background: color-mix(in srgb, var(--red) 88%, #000); -} -.settings-connectors-clear-commit.is-armed:active { - transform: translateY(1px); -} -/* The arming sweep — a translucent fill that races from left to right - over the disabled window, signaling "almost ready". CSS-only so it - renders the same in every browser without a second timer. */ -.settings-connectors-clear-commit-arm { - position: absolute; - inset: 0; - pointer-events: none; - background: linear-gradient( - 90deg, - color-mix(in srgb, #fff 28%, transparent) 0%, - color-mix(in srgb, #fff 12%, transparent) 100% - ); - transform: translateX(-100%); - opacity: 0.85; - animation: settings-connectors-clear-commit-arm 700ms ease-out forwards; -} -.settings-connectors-clear-commit.is-armed .settings-connectors-clear-commit-arm { - display: none; -} -@keyframes settings-connectors-clear-commit-arm { - from { transform: translateX(-100%); } - to { transform: translateX(0%); } -} -.settings-connectors-clear-commit-label { - position: relative; - display: inline-flex; - align-items: center; - gap: 6px; - z-index: 1; -} -@media (max-width: 540px) { - .settings-connectors-clear-confirm { - grid-template-columns: 26px minmax(0, 1fr); - } - .settings-connectors-clear-confirm-actions { - grid-column: 1 / -1; - justify-content: flex-end; - flex-wrap: wrap; - } - .settings-connectors-clear-commit { - min-width: 0; - flex: 1 1 auto; - } -} -@media (prefers-reduced-motion: reduce) { - .settings-connectors-clear-confirm { - animation: none; - } - .settings-connectors-clear-commit-arm { - animation: none; - transform: translateX(0%); - opacity: 0.4; - } -} -.settings-sidebar { - display: flex; - flex-direction: column; - gap: 2px; - padding: 16px 10px; - background: var(--bg-panel); - border-right: 1px solid var(--border); - min-height: 0; - overflow-y: auto; - overscroll-behavior: contain; -} -/* Sidebar items follow the ChatGPT-style settings rhythm: single line, - icon + label only, gray pill fill on active. The `` description - that ships in the JSX is hidden — the label alone is the navigation - handle; the section's own subtitle in the content area carries any - intro copy. */ -.settings-nav-item { - width: 100%; - border: 1px solid transparent; - border-radius: 8px; - background: transparent; - color: var(--text); - display: grid; - grid-template-columns: 18px minmax(0, 1fr); - gap: 10px; - align-items: center; - padding: 7px 10px; - text-align: left; - cursor: pointer; -} -.settings-nav-item:hover { - background: rgba(0, 0, 0, 0.04); -} -.settings-nav-item.active, -.settings-nav-item.active:hover { - background: var(--bg-subtle); - border-color: transparent; - color: var(--text); - box-shadow: none; -} -.settings-nav-item svg { - justify-self: center; - opacity: 0.75; -} -.settings-nav-item span { - min-width: 0; - display: flex; - flex-direction: column; - gap: 2px; -} -.settings-nav-item strong { - color: currentColor; - font-size: 13px; - font-weight: 500; - line-height: 1.3; - overflow-wrap: anywhere; -} -.settings-nav-item.active strong { - font-weight: 600; -} -.settings-nav-item small { - display: none; -} -.settings-content { - min-width: 0; - min-height: 0; - /* Top inset matches `.settings-sidebar`'s `padding-top: 16` so the first - content row (the Local CLI / BYOK pill) sits level with the first - sidebar nav-item across the gutter. */ - padding: 16px var(--modal-padding) 22px; - overflow: auto; - overscroll-behavior: contain; - display: flex; - flex-direction: column; - gap: 18px; -} -@media (max-width: 760px) { - .modal-settings { - width: min(560px, calc(100vw - 24px)); - } - .modal-settings .modal-body { - grid-template-columns: 1fr; - } - .settings-sidebar { - flex-direction: row; - overflow-x: auto; - overscroll-behavior-x: contain; - padding: 10px 12px; - border-right: 0; - border-bottom: 1px solid var(--border); - } - .settings-nav-item { - min-width: 150px; - } -} - -/* Segmented control */ -/* Compact single-line segmented control (Local CLI / BYOK and friends). - Mirrors `.subtab-pill` and `.ds-picker-mode`: gray rounded container, - white raised tab for active, just a title — the meta sub-line that used - to render here is hidden because the section right below already prints - the same context ("Local CLI · Detected by scanning..."). */ -.seg-control { - display: grid; - grid-template-columns: repeat(var(--seg-cols, 2), minmax(0, 1fr)); - gap: 2px; - padding: 3px; - background: var(--bg-subtle); - border: 1px solid var(--border); - border-radius: var(--radius); - /* Height-match the active sidebar nav-item across the gutter so the - top of the content area lines up neatly with the picked section. */ - min-height: 42px; - min-width: 0; -} -.seg-btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - padding: 4px 12px; - border: 1px solid transparent; - border-radius: var(--radius-sm); - background: transparent; - cursor: pointer; - text-align: center; - min-width: 0; -} -.seg-btn:hover:not(:disabled):not(.active) { color: var(--text); } -.seg-btn.active { - background: var(--bg-panel); - border-color: transparent; - box-shadow: var(--shadow-xs); -} -.seg-btn .seg-title { - font-size: 12px; - font-weight: 500; - color: var(--text); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 100%; -} -.seg-btn.active .seg-title { font-weight: 600; } -.seg-btn .seg-meta { display: none; } -.seg-btn:disabled { opacity: 0.55; cursor: not-allowed; } -/* Inline variant: title and meta sit on a single row instead of - stacking, which saves a row of vertical space inside the - execution-mode tabs (Local CLI · 2 installed / BYOK · API provider). */ -.seg-btn--inline { - flex-direction: row; - align-items: baseline; - gap: 8px; - padding-block: 8px; -} -.seg-btn--inline > .seg-title { min-width: 0; flex: 0 0 auto; } -.seg-btn--inline > .seg-meta { min-width: 0; flex: 1 1 auto; } - -/* Secondary protocol selector — pill chips, wraps to multiple rows */ -.protocol-chips { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin: 4px 0 8px; - padding: 0; - min-width: 0; -} -.protocol-chip { - flex: 0 1 auto; - max-width: 100%; - min-width: 0; - background: transparent; - border: 1px solid var(--border); - border-radius: 999px; - padding: 4px 12px; - font-size: 12px; - font-weight: 500; - color: var(--text-muted); - cursor: pointer; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - transition: background 120ms ease, color 120ms ease, border-color 120ms ease, box-shadow 120ms ease; -} -.protocol-chip:hover:not(.active) { - background: var(--bg-subtle); - color: var(--text); - border-color: var(--border-strong); -} -.protocol-chip.active { - background: var(--selected); - border-color: color-mix(in srgb, var(--selected) 86%, var(--text-strong)); - color: #fff; - font-weight: 600; - box-shadow: - 0 0 0 3px var(--selected-soft), - 0 1px 2px color-mix(in srgb, var(--selected) 22%, transparent); -} -.protocol-chip.active:hover { - background: color-mix(in srgb, var(--selected) 88%, var(--text-strong)); - border-color: color-mix(in srgb, var(--selected) 80%, var(--text-strong)); - color: #fff; -} - -.settings-section { display: flex; flex-direction: column; gap: 12px; } -.settings-section-connectors { gap: 16px; } -/* Credentials sit above the catalog now; the divider lives under the field - so the eye reads "configure key → catalog unlocks below". */ -.settings-section-connectors > .settings-section-connectors-credentials { - padding-bottom: 16px; - border-bottom: 1px solid var(--border-soft); - margin-bottom: 4px; -} -.settings-section-connectors > .connectors-panel-embedded { - margin-top: 0; -} -.settings-rescan-btn { - display: inline-flex; - align-items: center; - gap: 6px; - min-width: 96px; - justify-content: center; -} -.settings-rescan-btn.loading { - border-color: var(--accent-soft); - background: var(--accent-tint); - color: var(--accent-strong); -} -.settings-rescan-status, -.settings-test-status { - margin: -4px 0 0; - padding: 7px 10px; - border: 1px solid var(--green-border); - border-radius: var(--radius-sm); - background: var(--green-bg); - color: var(--green); - font-size: 12px; - line-height: 1.4; - word-wrap: break-word; - overflow-wrap: break-word; -} -.settings-rescan-status.error, -.settings-test-status.error { - border-color: var(--red-border); - background: var(--red-bg); - color: var(--red); -} -.settings-rescan-status-inline { - margin: 0; - padding: 0; - border: 0; - background: transparent; - color: var(--green); - font-size: 11.5px; - white-space: nowrap; -} -.settings-rescan-status-inline.error { - color: var(--red); -} -.settings-test-status.warn { - border-color: var(--amber-border, var(--orange-border, var(--green-border))); - background: var(--amber-bg, var(--orange-bg, var(--green-bg))); - color: var(--amber, var(--orange, var(--green))); -} -.settings-test-status.running { - border-color: var(--accent-soft); - background: var(--accent-tint); - color: var(--accent-strong); -} -.settings-test-actions { - margin-top: 8px; - display: flex; - flex-direction: column; - gap: 8px; -} -.settings-test-actions-hint { - font-size: 12px; - color: var(--text-muted); -} -.settings-test-actions-row { - display: flex; - flex-wrap: wrap; - gap: 8px; -} -.settings-test-btn { - display: inline-flex; - align-items: center; - gap: 6px; - min-width: 64px; - justify-content: center; -} -.settings-test-btn.loading { - border-color: var(--accent-soft); - background: var(--accent-tint); - color: var(--accent-strong); -} -.section-head-actions .seg-control { - display: inline-grid; - grid-template-columns: repeat(var(--seg-cols, 2), auto); - min-height: unset; - min-width: unset; -} -.section-head-actions { - display: inline-flex; - align-items: center; - gap: 8px; -} -/* ============================================================ - Orbit settings section — redesigned layout - Hero · Automation card · Run receipt · Artifact strip - ============================================================ */ -.orbit-section { - gap: 16px; -} - -/* ---------- 1. Hero / header zone ---------- */ -.orbit-hero { - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - align-items: center; - gap: 14px; - padding: 16px 18px; - border: 1px solid var(--border); - border-radius: var(--radius); - background: - radial-gradient(140% 160% at 100% 0%, color-mix(in srgb, var(--accent-tint) 75%, transparent) 0%, transparent 60%), - var(--bg-panel); - box-shadow: var(--shadow-xs); -} -.orbit-hero-mark { - width: 44px; - height: 44px; - border-radius: 12px; - display: inline-flex; - align-items: center; - justify-content: center; - background: linear-gradient(135deg, var(--accent) 0%, color-mix(in srgb, var(--accent) 70%, #000) 100%); - color: #fff; - box-shadow: 0 6px 14px color-mix(in srgb, var(--accent) 32%, transparent); - flex-shrink: 0; -} -.orbit-hero-copy { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} -.orbit-hero-eyebrow { - font-size: 10.5px; - font-weight: 700; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--accent-strong); -} -.orbit-hero-title { - margin: 0; - font-size: 18px; - font-weight: 650; - letter-spacing: -0.015em; - color: var(--text-strong); - line-height: 1.15; -} -.orbit-hero-lede { - margin: 4px 0 0; - font-size: 12.5px; - color: var(--text-muted); - line-height: 1.5; - max-width: 52ch; -} -.orbit-hero-lede strong { - color: var(--text); - font-weight: 600; -} -.orbit-hero-actions { - display: inline-flex; - align-items: center; - gap: 10px; - flex-shrink: 0; -} - -.orbit-state-pill { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 10px 4px 8px; - border-radius: var(--radius-pill); - font-size: 11px; - font-weight: 600; - letter-spacing: 0.02em; - background: var(--bg-subtle); - border: 1px solid var(--border); - color: var(--text-muted); - white-space: nowrap; -} -.orbit-state-dot { - width: 7px; - height: 7px; - border-radius: 50%; - background: var(--border-strong); -} -.orbit-state-pill.orbit-state-active { - background: color-mix(in srgb, var(--accent-tint) 80%, var(--bg-panel)); - border-color: color-mix(in srgb, var(--accent) 36%, var(--border)); - color: var(--accent-strong); -} -.orbit-state-pill.orbit-state-active .orbit-state-dot { - background: var(--accent); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); - animation: pulse 2.4s ease-in-out infinite; -} - -.orbit-run-cta { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 7px 14px; - background: var(--accent); - color: #fff; - border: 1px solid var(--accent); - border-radius: var(--radius-sm); - font-size: 12.5px; - font-weight: 600; - letter-spacing: 0.005em; - cursor: pointer; - box-shadow: 0 1px 0 color-mix(in srgb, var(--accent-strong) 22%, transparent) inset, var(--shadow-xs); - transition: background 120ms ease, border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease; -} -.orbit-run-cta:hover:not(:disabled) { - background: var(--accent-hover); - border-color: var(--accent-hover); - transform: translateY(-1px); -} -.orbit-run-cta:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} -.orbit-run-cta:disabled { - cursor: not-allowed; - opacity: 0.7; - transform: none; -} -.orbit-run-cta.is-busy { - background: color-mix(in srgb, var(--accent) 75%, #000); - border-color: transparent; -} -.orbit-run-cta .icon-spin { - color: #fff; -} - -@media (max-width: 620px) { - .orbit-hero { - grid-template-columns: auto minmax(0, 1fr); - grid-template-rows: auto auto; - row-gap: 10px; - } - .orbit-hero-actions { - grid-column: 1 / -1; - justify-content: space-between; - } -} - -/* ---------- 2. Automation card ---------- */ -.orbit-automation { - display: flex; - flex-direction: column; - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--bg-panel); - box-shadow: var(--shadow-xs); - overflow: hidden; - transition: border-color 140ms ease, background 140ms ease; -} -.orbit-automation.is-on { - border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); - background: - linear-gradient(180deg, color-mix(in srgb, var(--accent-tint) 40%, var(--bg-panel)) 0%, var(--bg-panel) 40%); -} - -/* ---------- Locked state ---------- - When no Composio connector is wired up the automation card collapses - into a passive, gated surface. We desaturate the hue, tighten contrast, - and overlay a soft diagonal sheen so the card reads as "intentionally - off" rather than "broken styling". The lock banner above the rows - names the prerequisite plainly and points back to the Connectors gate - without competing with it. */ -.orbit-automation.is-locked { - border-color: color-mix(in srgb, var(--text-soft) 22%, var(--border)); - background: - repeating-linear-gradient( - 135deg, - color-mix(in srgb, var(--text-soft) 4%, transparent) 0 6px, - transparent 6px 14px - ), - var(--bg-subtle); - filter: saturate(0.55); -} -.orbit-automation.is-locked.is-on { - /* When the user previously had the schedule on but later removed the - last connector we still show the "on" gradient very faintly — but - the locked treatment wins so the panel reads as gated. */ - background: - repeating-linear-gradient( - 135deg, - color-mix(in srgb, var(--text-soft) 4%, transparent) 0 6px, - transparent 6px 14px - ), - var(--bg-subtle); -} -.orbit-automation.is-locked .orbit-automation-row { - /* Block any accidental click-through into the row's whitespace; real - controls retain their own pointer behavior via :disabled. */ - cursor: not-allowed; -} -.orbit-automation.is-locked .orbit-automation-title, -.orbit-automation.is-locked .orbit-automation-sub { - color: var(--text-soft); -} -.orbit-automation.is-locked .orbit-time-input, -.orbit-automation.is-locked .orbit-switch, -.orbit-automation.is-locked .orbit-template-select-input, -.orbit-automation.is-locked .orbit-automation-sub-action { - cursor: not-allowed; - opacity: 0.6; -} -.orbit-automation.is-locked .orbit-time-input:disabled, -.orbit-automation.is-locked .orbit-template-select-input:disabled { - /* The native :disabled state already dims; keep our own opacity tuned - so it matches the surrounding desaturated card. */ - background: color-mix(in srgb, var(--text-soft) 6%, var(--bg-subtle)); -} - -/* Lock banner — sits above the configuration rows and names the gate - reason in a single line. Compact, low-contrast accent so it reads as - metadata rather than a second hero. */ -.orbit-automation-lock-banner { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 18px; - font-size: 11.5px; - color: var(--text-muted); - background: color-mix(in srgb, var(--accent) 5%, var(--bg-panel)); - border-bottom: 1px solid color-mix(in srgb, var(--accent) 22%, var(--border)); -} -.orbit-automation-lock-banner svg { - color: var(--accent-strong); - flex-shrink: 0; -} -.orbit-automation-lock-badge { - font-size: 10px; - font-weight: 700; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--accent-strong); - padding: 2px 7px; - border-radius: 999px; - background: color-mix(in srgb, var(--accent) 14%, transparent); - flex-shrink: 0; -} -.orbit-automation-lock-text { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -@media (max-width: 620px) { - .orbit-automation-lock-text { white-space: normal; } -} - -.orbit-automation-row { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 16px; - align-items: center; - padding: 14px 18px; -} -.orbit-automation-schedule-row { - align-items: start; -} -.orbit-automation-divider { - height: 1px; - background: var(--border-soft); - margin: 0 18px; -} -.orbit-automation-label { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} -.orbit-automation-title { - font-size: 13px; - font-weight: 600; - color: var(--text); - letter-spacing: -0.005em; -} -.orbit-automation-sub { - font-size: 11.5px; - color: var(--text-muted); - line-height: 1.45; -} - -/* Custom switch control — rebuilt rather than reusing .toggle-row so the - Orbit section owns the pattern and can tune track/thumb proportions. */ -.orbit-switch { - display: inline-flex; - align-items: center; - gap: 10px; - padding: 4px 12px 4px 4px; - background: transparent; - border: 1px solid var(--border); - border-radius: var(--radius-pill); - cursor: pointer; - color: var(--text-muted); - font-size: 12px; - font-weight: 600; - letter-spacing: 0.02em; - transition: border-color 140ms ease, color 140ms ease, background 140ms ease; -} -.orbit-switch:hover { border-color: var(--border-strong); } -.orbit-switch.is-on { - background: color-mix(in srgb, var(--accent-tint) 70%, var(--bg-panel)); - border-color: color-mix(in srgb, var(--accent) 36%, var(--border)); - color: var(--accent-strong); -} -.orbit-switch:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} -.orbit-switch:disabled, -.orbit-switch.is-locked { - cursor: not-allowed; - opacity: 0.55; - border-color: var(--border); - background: transparent; - color: var(--text-soft); -} -.orbit-switch:disabled .orbit-switch-track, -.orbit-switch.is-locked .orbit-switch-track { - background: var(--border); -} -.orbit-switch:disabled.is-on .orbit-switch-track, -.orbit-switch.is-locked.is-on .orbit-switch-track { - background: color-mix(in srgb, var(--accent) 35%, var(--border)); -} -.orbit-switch-track { - position: relative; - width: 34px; - height: 20px; - border-radius: 999px; - background: var(--border-strong); - transition: background 160ms ease; -} -.orbit-switch-thumb { - position: absolute; - top: 2px; - left: 2px; - width: 16px; - height: 16px; - border-radius: 50%; - background: #fff; - box-shadow: 0 1px 3px rgba(28, 27, 26, 0.22); - transition: transform 180ms cubic-bezier(0.2, 0, 0.2, 1); -} -.orbit-switch.is-on .orbit-switch-track { background: var(--accent); } -.orbit-switch.is-on .orbit-switch-thumb { transform: translateX(14px); } -.orbit-switch-text { - font-variant-numeric: tabular-nums; - min-width: 22px; - text-align: right; -} - -.orbit-automation-schedule-controls { - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 6px; - min-width: 0; -} -.orbit-time-input { - width: 140px; - padding: 6px 10px; - font-variant-numeric: tabular-nums; - font-size: 13px; - letter-spacing: 0.02em; - text-align: center; - border-radius: var(--radius-sm); -} -.orbit-next-run { - display: inline-flex; - align-items: baseline; - gap: 6px; - font-size: 11.5px; - color: var(--text-muted); - max-width: 260px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.orbit-next-run-label { - text-transform: uppercase; - letter-spacing: 0.08em; - font-size: 10.5px; - font-weight: 600; - color: var(--text-soft); -} -.orbit-next-run-value { - color: var(--text); - font-weight: 600; - font-variant-numeric: tabular-nums; -} -.orbit-next-run-value.muted { - color: var(--text-muted); - font-weight: 500; -} - -@media (max-width: 620px) { - .orbit-automation-row { - grid-template-columns: minmax(0, 1fr); - row-gap: 10px; - } - .orbit-automation-schedule-controls { - align-items: stretch; - } - .orbit-time-input { - width: 100%; - } - .orbit-switch { align-self: flex-start; } -} - -/* ---------- 3. Template row (folded into Automation card) ---------- - Previously this section lived in its own paired card. We folded it into - the automation card as a third row + dedicated preview slot so users - configure schedule and prompt-steering in one place, and the section - reads as one cohesive configuration surface instead of two parallel - panels competing for attention. The class names below still use the - `orbit-template-` prefix because they continue to describe template - surface elements — they just live inside the automation card now. */ - -.orbit-automation.has-template { - border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); -} -.orbit-automation-template-row { - /* Slightly more vertical breathing room than the switch/schedule rows - because the right column hosts a wider select control. */ - align-items: start; - padding-top: 16px; - padding-bottom: 16px; -} -.orbit-automation-template-controls { - display: flex; - align-items: center; - justify-content: flex-end; - min-width: 220px; - width: clamp(220px, 36%, 320px); -} - -/* Inline warning variant of the row sub-copy. Used by the Prompt - template row when the saved skill id is no longer in the registry — - takes the place of the standard descriptive sub-line and inlines a - small Reset action that pushes the config back to the default - (`orbit-general`). The warning lives flush inside the automation - row so we do not need a separate preview panel for the missing - state. */ -.orbit-automation-sub-warning { - display: inline-flex; - align-items: center; - flex-wrap: wrap; - gap: 6px; - padding: 4px 8px; - margin-top: 2px; - border-radius: var(--radius-sm); - background: color-mix(in srgb, #c47a2c 10%, var(--bg-panel)); - color: #8a5217; - font-weight: 500; -} -.orbit-automation-sub-warning svg { - color: #c47a2c; - flex-shrink: 0; -} -.orbit-automation-sub-warning strong { - color: #6c3f0f; - font-weight: 600; -} -.orbit-automation-sub-action { - appearance: none; - -webkit-appearance: none; - display: inline-flex; - align-items: center; - padding: 2px 8px; - margin-left: auto; - font: inherit; - font-size: 11px; - font-weight: 600; - letter-spacing: 0.02em; - color: #8a5217; - background: var(--bg-panel); - border: 1px solid color-mix(in srgb, #c47a2c 32%, var(--border)); - border-radius: var(--radius-pill); - cursor: pointer; - transition: border-color 120ms ease, color 120ms ease, background 120ms ease; -} -.orbit-automation-sub-action:hover { - color: #6c3f0f; - border-color: color-mix(in srgb, #c47a2c 55%, var(--border)); - background: color-mix(in srgb, #c47a2c 8%, var(--bg-panel)); -} -.orbit-automation-sub-action:focus-visible { - outline: 2px solid color-mix(in srgb, #c47a2c 65%, var(--accent)); - outline-offset: 2px; -} - -/* Native select wrapper. Originally a labelled grid (label + select); - the row title now carries the inline label, so the select stretches - to fill its column. */ -.orbit-template-select { - display: flex; - flex: 1; - min-width: 0; -} -.orbit-template-select-wrap { - position: relative; - display: flex; - align-items: center; - width: 100%; - min-width: 0; -} -.orbit-template-select-input { - appearance: none; - -webkit-appearance: none; - width: 100%; - padding: 8px 32px 8px 12px; - font: inherit; - font-size: 13px; - font-weight: 500; - color: var(--text); - background: var(--bg-subtle); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; - transition: border-color 140ms ease, background 140ms ease, box-shadow 140ms ease; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} -.orbit-template-select-input:hover:not(:disabled) { - border-color: var(--border-strong); - background: var(--bg-panel); -} -.orbit-template-select-input:focus-visible { - outline: none; - border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); -} -.orbit-template-select-input:disabled { - /* While the skill registry is loading we show a progress cursor; the - locked variant overrides this back to not-allowed via the parent - `.orbit-automation.is-locked` rule above. */ - cursor: progress; - opacity: 0.65; -} -.orbit-template-select-chevron { - position: absolute; - right: 10px; - top: 50%; - transform: translateY(-50%); - color: var(--text-soft); - pointer-events: none; -} - -@media (max-width: 620px) { - /* On narrow viewports the template row should stack: title block on top, - select control full-width below it. */ - .orbit-automation-template-row { - grid-template-columns: minmax(0, 1fr); - row-gap: 10px; - } - .orbit-automation-template-controls { - width: 100%; - min-width: 0; - justify-content: stretch; - } -} - -/* ---------- 4. Run receipt ---------- */ -.orbit-receipt { - display: flex; - flex-direction: column; - gap: 12px; - padding: 16px 18px; - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--bg-panel); - box-shadow: var(--shadow-xs); -} -.orbit-receipt-head { - display: flex; - justify-content: space-between; - align-items: center; - gap: 12px; - flex-wrap: wrap; -} -.orbit-receipt-head-left { - display: inline-flex; - align-items: baseline; - gap: 10px; - flex-wrap: wrap; - min-width: 0; -} -.orbit-receipt-eyebrow { - display: inline-flex; - align-items: center; - gap: 5px; - font-size: 10.5px; - font-weight: 700; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--text-muted); -} -.orbit-receipt-eyebrow svg { color: var(--text-soft); } -.orbit-receipt-timestamp { - font-size: 14px; - font-weight: 650; - color: var(--text-strong); - letter-spacing: -0.005em; - font-variant-numeric: tabular-nums; -} -.orbit-receipt-timestamp.muted { - color: var(--text-muted); - font-weight: 500; -} -.orbit-trigger-pill { - padding: 2px 10px; - border-radius: var(--radius-pill); - font-size: 10.5px; - font-weight: 700; - letter-spacing: 0.06em; - text-transform: uppercase; - background: var(--bg-subtle); - border: 1px solid var(--border); - color: var(--text-muted); -} -.orbit-trigger-pill.orbit-trigger-manual { - background: var(--blue-bg); - border-color: var(--blue-border); - color: var(--blue); -} -.orbit-trigger-pill.orbit-trigger-scheduled { - background: color-mix(in srgb, var(--accent-tint) 80%, var(--bg-panel)); - border-color: color-mix(in srgb, var(--accent) 36%, var(--border)); - color: var(--accent-strong); -} - -.orbit-inline-notice { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 6px 10px; - border-radius: var(--radius-sm); - font-size: 11.5px; - line-height: 1.4; - border: 1px solid var(--border); - background: var(--bg-subtle); - color: var(--text); -} -.orbit-inline-notice.is-success { - border-color: var(--green-border); - background: var(--green-bg); - color: var(--green); -} -.orbit-inline-notice.is-error { - border-color: var(--red-border); - background: var(--red-bg); - color: var(--red); -} - -/* Proportional run meter: a single bar whose segment widths reflect the - success / skip / failure ratio. Preferred over 4 equal tiles because it - communicates "mostly succeeded" or "mostly failed" at a glance. */ -.orbit-meter { - display: flex; - width: 100%; - height: 8px; - border-radius: 999px; - overflow: hidden; - background: var(--bg-subtle); - border: 1px solid var(--border); -} -.orbit-meter-seg { - height: 100%; - transition: width 260ms ease; -} -.orbit-meter-seg.is-succeeded { background: var(--green); } -.orbit-meter-seg.is-skipped { background: var(--border-strong); } -.orbit-meter-seg.is-failed { background: var(--red); } -.orbit-meter-seg.is-empty { - width: 100%; - background: repeating-linear-gradient( - 45deg, - var(--bg-subtle) 0 6px, - var(--bg-muted) 6px 12px - ); -} - -.orbit-counts { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 0; - margin: 0; - padding: 0; - list-style: none; -} -.orbit-counts .orbit-count { - position: relative; - display: flex; - flex-direction: column; - gap: 2px; - padding: 4px 12px; -} -.orbit-counts .orbit-count + .orbit-count::before { - content: ''; - position: absolute; - left: 0; - top: 6px; - bottom: 6px; - width: 1px; - background: var(--border-soft); -} -.orbit-count dt { - font-size: 10.5px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.08em; - color: var(--text-muted); - margin: 0; -} -.orbit-count dd { - margin: 0; - font-size: 20px; - font-weight: 650; - letter-spacing: -0.015em; - color: var(--text-strong); - font-variant-numeric: tabular-nums; - line-height: 1.1; -} -.orbit-count.is-succeeded dd { color: var(--green); } -.orbit-count.is-skipped dd { color: var(--text-muted); } -.orbit-count.is-failed dd { color: var(--red); } - -@media (max-width: 620px) { - .orbit-counts { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 10px; } - .orbit-counts .orbit-count + .orbit-count::before { display: none; } -} - -/* ---------- 1b. Configuration gate --------------------------------------- - Surfaces when the user has no connected integrations. We share the - orbit-themed accent palette of the hero/firstrun panel so the gate - reads as a first-class part of the panel rather than an inline error - banner. Layout mirrors the firstrun composition (glyph · copy · action) - so the section feels rhythmically consistent regardless of which - empty-state panel is showing. The decorative ring glyph reuses the - same dashed-orbit motif as the firstrun glyph but anchors a small - "link" icon at the center to telegraph "wire up a connector". */ -.orbit-config-gate { - position: relative; - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - align-items: center; - gap: 18px; - padding: 16px 20px; - border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border)); - border-radius: var(--radius); - background: - radial-gradient(120% 160% at 0% 50%, color-mix(in srgb, var(--accent-tint) 90%, transparent) 0%, transparent 60%), - var(--bg-panel); - box-shadow: var(--shadow-xs); - overflow: hidden; - animation: orbitConfigGateIn 220ms ease-out; -} -.orbit-config-gate::after { - /* Soft outer ring decoration in the corner — pure visual, mirrors the - firstrun panel so the two empty states feel like siblings. */ - content: ''; - position: absolute; - right: -50px; - top: -50px; - width: 160px; - height: 160px; - border-radius: 50%; - border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent); - pointer-events: none; - z-index: 0; -} -@keyframes orbitConfigGateIn { - from { opacity: 0; transform: translateY(4px); } - to { opacity: 1; transform: translateY(0); } -} - -.orbit-config-gate-glyph { - position: relative; - width: 52px; - height: 52px; - flex-shrink: 0; - display: inline-flex; - align-items: center; - justify-content: center; -} -.orbit-config-gate-ring { - position: absolute; - inset: 0; - border-radius: 50%; - border: 1px dashed color-mix(in srgb, var(--accent) 42%, transparent); -} -.orbit-config-gate-ring-outer { - inset: 0; - animation: orbitFirstrunSpin 22s linear infinite; -} -.orbit-config-gate-ring-inner { - inset: 9px; - border-style: solid; - border-color: color-mix(in srgb, var(--accent) 26%, transparent); - animation: orbitFirstrunSpin 16s linear infinite reverse; -} -.orbit-config-gate-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 22px; - height: 22px; - border-radius: 50%; - background: linear-gradient(135deg, var(--accent) 0%, color-mix(in srgb, var(--accent) 70%, #000) 100%); - color: var(--btn-primary-fg, #fff); - box-shadow: 0 4px 10px color-mix(in srgb, var(--accent) 32%, transparent); -} -@media (prefers-reduced-motion: reduce) { - .orbit-config-gate-ring { animation: none !important; } -} - -.orbit-config-gate-copy { - display: flex; - flex-direction: column; - gap: 4px; - min-width: 0; -} -.orbit-config-gate-eyebrow { - font-size: 10.5px; - font-weight: 700; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--accent-strong); -} -.orbit-config-gate-title { - margin: 0; - font-size: 14px; - font-weight: 650; - letter-spacing: -0.005em; - color: var(--text-strong); - line-height: 1.3; -} -.orbit-config-gate-body { - margin: 0; - font-size: 12px; - color: var(--text-muted); - line-height: 1.5; - max-width: 56ch; -} - -.orbit-config-gate-actions { - display: flex; - align-items: center; - flex-shrink: 0; - position: relative; - z-index: 1; -} -.orbit-config-gate-action { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 8px 14px; - font-size: 12.5px; - font-weight: 600; - letter-spacing: 0.005em; - color: var(--btn-primary-fg, #fff); - background: linear-gradient(135deg, var(--accent) 0%, color-mix(in srgb, var(--accent) 70%, #000) 100%); - border: 1px solid color-mix(in srgb, var(--accent) 60%, transparent); - border-radius: 999px; - cursor: pointer; - transition: transform 140ms ease, box-shadow 140ms ease, filter 140ms ease; - box-shadow: 0 6px 14px color-mix(in srgb, var(--accent) 22%, transparent); -} -.orbit-config-gate-action:hover { - transform: translateY(-1px); - filter: brightness(1.05); - box-shadow: 0 10px 20px color-mix(in srgb, var(--accent) 28%, transparent); -} -.orbit-config-gate-action:focus-visible { - outline: 2px solid var(--accent-strong); - outline-offset: 2px; -} -.orbit-config-gate-action svg { - transition: transform 160ms ease; -} -.orbit-config-gate-action:hover svg { - transform: translateX(2px); -} - -@media (max-width: 620px) { - .orbit-config-gate { - grid-template-columns: auto minmax(0, 1fr); - grid-template-rows: auto auto; - row-gap: 12px; - } - .orbit-config-gate-actions { - grid-column: 1 / -1; - justify-content: flex-start; - } -} - -/* ---------- 5. Live artifact strip ---------- */ -.orbit-artifact-strip { - position: relative; - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - grid-template-rows: auto auto; - column-gap: 14px; - row-gap: 10px; - align-items: center; - padding: 14px 16px 14px 18px; - border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--border)); - border-radius: var(--radius); - background: - linear-gradient(135deg, color-mix(in srgb, var(--accent-tint) 60%, var(--bg-panel)) 0%, var(--bg-panel) 55%); - box-shadow: var(--shadow-xs); - overflow: hidden; -} -.orbit-artifact-strip::before { - content: ''; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 3px; - background: linear-gradient(180deg, var(--accent) 0%, color-mix(in srgb, var(--accent) 50%, transparent) 100%); -} -.orbit-artifact-strip.is-legacy { - background: var(--bg-subtle); - border-color: var(--border); - border-style: dashed; -} -.orbit-artifact-strip.is-legacy::before { display: none; } - -.orbit-artifact-strip-icon { - grid-row: 1; - width: 38px; - height: 38px; - border-radius: 10px; - display: inline-flex; - align-items: center; - justify-content: center; - background: var(--bg-panel); - border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--border)); - color: var(--accent-strong); - flex-shrink: 0; -} -.orbit-artifact-strip.is-legacy .orbit-artifact-strip-icon { - border-color: var(--border); - color: var(--text-muted); -} -.orbit-artifact-strip-copy { - grid-row: 1; - display: flex; - flex-direction: column; - gap: 1px; - min-width: 0; -} -.orbit-artifact-strip-kicker { - font-size: 10px; - font-weight: 700; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--accent-strong); -} -.orbit-artifact-strip.is-legacy .orbit-artifact-strip-kicker { - color: var(--text-muted); -} -.orbit-artifact-strip-title { - font-size: 13.5px; - font-weight: 650; - letter-spacing: -0.01em; - color: var(--text-strong); - line-height: 1.25; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.orbit-artifact-strip-meta { - font-size: 11.5px; - color: var(--text-muted); - line-height: 1.4; -} -.orbit-artifact-strip-actions { - grid-row: 1; - display: inline-flex; - align-items: center; - gap: 6px; - flex-shrink: 0; -} - -.orbit-artifact-ghost { - display: inline-flex; - align-items: center; - gap: 5px; - padding: 6px 10px; - font-size: 11.5px; - font-weight: 600; - color: var(--text); - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; - transition: background 120ms ease, border-color 120ms ease; -} -.orbit-artifact-ghost:hover { - background: var(--bg-subtle); - border-color: var(--border-strong); -} - -.orbit-artifact-open { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 7px 12px; - background: var(--accent); - color: #fff; - border: 1px solid var(--accent); - border-radius: var(--radius-sm); - font-size: 12px; - font-weight: 600; - text-decoration: none; - box-shadow: 0 1px 0 color-mix(in srgb, var(--accent-strong) 20%, transparent) inset, var(--shadow-xs); - transition: background 120ms ease, border-color 120ms ease, transform 120ms ease; -} -.orbit-artifact-open:hover { - background: var(--accent-hover); - border-color: var(--accent-hover); - transform: translateY(-1px); -} -.orbit-artifact-open:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.orbit-artifact-peek { - grid-column: 1 / -1; - grid-row: 2; - margin-top: 2px; - border-top: 1px solid color-mix(in srgb, var(--accent) 12%, var(--border-soft)); - padding-top: 8px; -} -.orbit-artifact-strip.is-legacy .orbit-artifact-peek { - border-top-color: var(--border-soft); -} -.orbit-artifact-peek summary { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 2px 0; - cursor: pointer; - list-style: none; - font-size: 11px; - font-weight: 600; - letter-spacing: 0.02em; - color: var(--text-muted); - user-select: none; -} -.orbit-artifact-peek summary::-webkit-details-marker { display: none; } -.orbit-artifact-peek summary svg { - transition: transform 160ms ease; - color: var(--text-soft); -} -.orbit-artifact-peek[open] summary svg { transform: rotate(90deg); } -.orbit-artifact-peek[open] summary { color: var(--text); } -.orbit-artifact-peek pre { - margin: 8px 0 0; - max-height: 240px; - overflow: auto; - padding: 12px 14px; - border: 1px solid var(--border-soft); - border-radius: var(--radius-sm); - background: var(--bg-panel); - color: var(--text); - font: 11.5px/1.6 var(--mono); - white-space: pre-wrap; - overflow-wrap: anywhere; -} - -@media (max-width: 620px) { - .orbit-artifact-strip { - grid-template-columns: auto minmax(0, 1fr); - } - .orbit-artifact-strip-actions { - grid-column: 1 / -1; - grid-row: auto; - justify-content: flex-end; - } - .orbit-artifact-peek { - grid-column: 1 / -1; - } -} -.settings-field-error { - color: var(--red); - font-size: 12px; - line-height: 1.4; -} - -.settings-about-list { - margin: 0; - display: flex; - flex-direction: column; - gap: 8px; -} -.mcp-client-body { - display: flex; - flex-direction: column; - gap: 12px; -} - -/* Group 1: what the MCP server does */ -.mcp-capabilities-card { - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px 14px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - line-height: 1.55; -} -.mcp-capabilities-label { - margin: 0; - font-size: 11px; - font-weight: 600; - color: var(--text-muted); - text-transform: uppercase; - letter-spacing: 0.05em; -} -.mcp-capabilities-list { - margin: 0; - padding-left: 18px; - font-size: 13px; - color: var(--text); -} - -/* Group 2: setup flow */ -.mcp-setup-card { - display: flex; - flex-direction: column; - gap: 12px; - padding: 14px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); -} -.mcp-setup-card .ds-picker { - margin-bottom: 0; -} -.mcp-running-note { - margin: 0; - font-size: 12px; - color: var(--text-muted); - line-height: 1.5; -} -.settings-about-list > div { - display: flex; - align-items: center; - gap: 12px; - padding: 12px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); -} -.settings-about-version-row { - justify-content: space-between; -} -.settings-about-list dt { - font-size: 12px; - color: var(--text-muted); - flex-shrink: 0; -} -.settings-about-list dd { - margin: 0; - color: var(--text); - font-size: 13px; - font-weight: 600; - overflow-wrap: anywhere; -} -.settings-notify-card { - padding: 12px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); -} -.settings-notify-card-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} -.settings-notify-card-header h4 { - margin: 0; -} -.settings-notify-card-hint { - margin: 16px 0 0; -} -.settings-about-version-left { - display: flex; - align-items: center; - gap: 12px; -} -.settings-about-version-num { - color: var(--text); - font-size: 13px; - font-weight: 600; -} -.settings-about-download-link { - display: inline-flex; - align-items: center; - padding: 2px 9px; - font-size: 11px; - font-weight: 500; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: transparent; - color: var(--text-muted); - text-decoration: none; - transition: background 120ms ease, color 120ms ease, border-color 120ms ease; - white-space: nowrap; -} -.settings-about-download-link:hover:not(:disabled) { - background: var(--bg-subtle); - color: var(--text); - border-color: var(--border-strong); -} -.settings-about-download-link:disabled { - opacity: 0.5; - cursor: default; -} - -.media-provider-reload-row { - display: flex; - justify-content: flex-start; - margin-bottom: 4px; -} -.media-provider-reload-btn { - font-size: 12px; - color: var(--text-muted); - padding: 4px 8px; - gap: 4px; -} -.media-provider-reload-btn:hover { - color: var(--text); -} -.settings-about-diagnostics { - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: space-between; - gap: 16px; - padding: 12px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); -} -.settings-about-diagnostics-text { - display: flex; - flex-direction: column; - gap: 4px; - flex: 1 1 auto; - min-width: 0; -} -.settings-about-diagnostics h4 { - margin: 0; - font-size: 13px; - font-weight: 600; - color: var(--text); -} -.settings-about-diagnostics-text .hint { - margin: 0; - font-size: 12px; -} -.diagnostics-export-row { - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 6px; - flex: 0 0 auto; - max-width: 220px; -} -.diagnostics-export-button { - display: inline-flex; - align-items: center; - gap: 6px; - white-space: nowrap; -} -.diagnostics-export-status { - margin: 0; - font-size: 12px; - overflow-wrap: anywhere; - text-align: right; -} -.diagnostics-export-status.success { color: var(--text); } -.diagnostics-export-status.error { color: var(--danger, #d4543b); } - -.media-provider-list { - display: flex; - flex-direction: column; - gap: 10px; -} -.media-provider-row { - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); -} -.media-provider-row.pending { - background: var(--bg-subtle); - border-style: dashed; -} -.media-provider-head { - display: flex; - justify-content: space-between; - gap: 10px; -} -.media-provider-meta { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} -.media-provider-name-row { - display: flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; -} -.media-provider-name { - font-size: 13px; - font-weight: 600; - color: var(--text); -} -.media-provider-hint { - font-size: 11px; - color: var(--text-muted); -} -.media-provider-badge { - align-self: flex-start; - font-size: 10px; - font-weight: 600; - padding: 2px 7px; - border-radius: var(--radius-pill); - border: 1px solid var(--border); -} -.media-provider-badges { - display: flex; - flex-wrap: wrap; - justify-content: flex-end; - gap: 6px; -} -.media-provider-badge.integrated { - color: #137a3d; - background: color-mix(in srgb, #1f9d55 10%, transparent); - border-color: color-mix(in srgb, #1f9d55 28%, var(--border)); -} -.media-provider-badge.unsupported { - color: var(--text-soft); - background: var(--bg-subtle); - border-color: var(--border); -} -.media-provider-badge.on { - color: #3155c9; - background: color-mix(in srgb, #4169e1 10%, transparent); - border-color: color-mix(in srgb, #4169e1 28%, var(--border)); -} -.media-provider-body { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; - gap: 6px; -} -/* - * "Coming soon" drawer under the media provider list. We render the - * roadmap providers as a denser, read-only list rather than the same - * editable cards as the main list — the user can't actually configure - * these, so giving them the same visual weight is misleading. The - *
/ is styled by the shared .memory-details-summary - * rules; only the inner list needs new styles. - */ -.media-provider-coming-soon { - margin-top: 12px; - padding: 10px 12px; - background: var(--bg-subtle); - border: 1px solid var(--border); - border-radius: var(--radius-sm); -} -.media-provider-coming-soon[open] { - padding-bottom: 12px; -} -.media-provider-coming-soon-list { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; -} -.media-provider-coming-soon-item { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 8px 0; - border-top: 1px solid var(--border-soft, var(--border)); -} -.media-provider-coming-soon-item:first-child { - border-top: 0; -} -.media-provider-coming-soon-meta { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} -.media-provider-secret-field { - position: relative; - min-width: 0; -} -.media-provider-secret-field input { - width: 100%; - padding-right: 34px; -} -.secret-visibility-button { - position: absolute; - top: 50%; - right: 8px; - display: inline-flex; - align-items: center; - justify-content: center; - width: 22px; - height: 22px; - padding: 0; - color: var(--text-muted); - background: transparent; - border: 0; - border-radius: var(--radius-pill); - box-shadow: none; - transform: translateY(-50%); -} -.secret-visibility-button:hover:not(:disabled) { - color: var(--text); - background: var(--bg-subtle); - box-shadow: none; -} -.secret-visibility-button:disabled { - color: var(--text-faint); - cursor: not-allowed; -} -.section-head { - display: flex; - justify-content: space-between; - align-items: flex-start; - gap: 12px; -} -.section-head > div { - max-width: 100%; - min-width: 0; -} -/* - Section-head action buttons (Reload, Test, Rescan, Fetch models, …) - must read on a single line. Without an explicit nowrap they wrap when - the section title + hint take most of the row, producing label - fragments like "Reload from / daemon" that look broken. flex-shrink:0 - keeps the button at its content width even when the description gets - long; long button labels are then guarded by their own copy budget. -*/ -.section-head > button, -.section-head .section-head-actions > button { - white-space: nowrap; - flex-shrink: 0; -} -.section-head h3 { margin: 0; font-size: 13px; font-weight: 600; letter-spacing: 0.01em; } -.section-head .hint { margin-top: 2px; } -.field { display: flex; flex-direction: column; gap: 4px; } -.field-label { font-size: 12px; font-weight: 500; color: var(--text-muted); } -.field-required { - margin-left: 4px; - color: var(--red); - font-weight: 700; -} -.field-error, -.field-inline-status { - font-size: 11.5px; - line-height: 1.35; -} -.field-error { - color: var(--red); -} -.field-inline-status.running { - color: var(--accent-strong); -} -.field-inline-status.success { - color: var(--green); -} -.field-row { display: flex; gap: 6px; align-items: stretch; } -.field-row input { flex: 1; } -.field-row .icon-btn { white-space: nowrap; padding: 6px 12px; } -.settings-base-url-readonly .field-row input[readonly] { - color: var(--text-muted); - cursor: default; -} -.settings-base-url-customize { - flex-shrink: 0; -} -.settings-language-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - gap: 8px; -} -.settings-language-tile { - display: grid; - grid-template-columns: 1fr auto; - align-items: center; - gap: 8px; - padding: 10px 12px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - color: var(--text); - text-align: left; - box-shadow: none; - min-width: 0; -} -.settings-language-tile-text { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} -.settings-language-tile-title { - font-size: 13px; - font-weight: 600; - color: var(--text); - overflow-wrap: anywhere; -} -.settings-language-tile-code { - font-size: 11px; - color: var(--text-muted); - font-variant-numeric: tabular-nums; - letter-spacing: 0.02em; -} -.settings-language-tile:hover { - border-color: var(--border-strong); - background: var(--bg-subtle); -} -/* Differentiate hover, selected, and keyboard-focus states so the - currently-selected language is visually distinct even when the - pointer is hovering a different tile. Issue #628 (carries over from - the previous dropdown form). */ -.settings-language-tile.active { - background: var(--accent-tint); - border-color: var(--accent); - color: var(--accent); - font-weight: 500; -} -.settings-language-tile.active .settings-language-tile-title { color: var(--accent); } -.settings-language-tile.active:hover { background: var(--accent-soft); } -.settings-language-tile:focus-visible { - outline: 2px solid var(--accent); - outline-offset: -2px; -} -.empty-card { - padding: 16px; - border: 1px dashed var(--border); - border-radius: var(--radius-sm); - color: var(--text-muted); - font-size: 12px; - background: var(--bg-subtle); -} - -.agent-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 8px; -} -.agent-grid-installed { - grid-template-columns: minmax(0, 1fr); -} -.agent-group { - display: flex; - flex-direction: column; - gap: 8px; -} -.agent-group-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} -.agent-group-head h4 { - margin: 0; - font-size: 12px; - font-weight: 650; - color: var(--text); -} -.agent-group-head-actions { - display: inline-flex; - align-items: center; - gap: 10px; - min-width: 0; -} -.agent-group-rescan-btn { - min-width: 92px; - min-height: 32px; -} -.agent-install-collapse { - margin-top: 10px; - padding: 10px 12px; - border: 1px solid var(--border-soft); - border-radius: var(--radius-sm); - background: var(--bg-subtle); -} -.agent-install-collapse-summary { - list-style: none; - cursor: pointer; - display: flex; - align-items: center; - gap: 8px; - color: var(--text-muted); - font-size: 12px; - font-weight: 600; - user-select: none; -} -.agent-install-collapse-summary::-webkit-details-marker { - display: none; -} -.agent-install-collapse-summary::before { - content: ''; - width: 0; - height: 0; - border-left: 5px solid currentColor; - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - transition: transform 0.15s ease; -} -.agent-install-collapse[open] > .agent-install-collapse-summary::before { - transform: rotate(90deg); -} -.agent-grid-unavailable { - margin-top: 10px; -} -/* Test feedback row injected into the agent grid right after the - tested/selected card. Spans both columns so the result reads as a - continuation of that card's row, instead of a top-of-section banner - that pushes the grid down on every Test/Rescan click. */ -.agent-test-result-row { - grid-column: 1 / -1; - display: flex; - flex-direction: column; - gap: 8px; -} -.agent-test-result-row > .settings-test-status { - margin: 0; -} -.agent-test-result-row > .settings-test-actions { - margin-top: 0; -} -.agent-card { - display: flex; - align-items: center; - gap: 10px; - /* Uniform height regardless of state: an installed agent only renders - name + version, while an unavailable one adds an Install/Docs row. - Without `min-height` the two variants render ~50px vs ~68px and the - grid looks ragged. Pinning to ~64px lets installed cards center their - two-line body in the same box that the action variant fills. */ - min-height: 64px; - padding: 6px 10px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; - text-align: left; - position: relative; - transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease; -} -.agent-card-installed { - padding: 0; - min-height: 72px; - overflow: hidden; -} -.agent-card-select { - flex: 1; - min-width: 0; - min-height: 70px; - display: flex; - align-items: center; - gap: 12px; - padding: 10px 14px 10px 18px; - border: 0; - background: transparent; - color: inherit; - text-align: left; - cursor: pointer; -} -.agent-card-test-btn { - align-self: stretch; - flex-shrink: 0; - min-width: 112px; - margin: 0; - padding: 0 18px; - border: 0; - border-left: 1px solid var(--border-soft); - border-radius: 0; - background: transparent; - color: var(--text); - font-weight: 600; -} -.agent-card-test-btn:hover:not(:disabled) { - background: color-mix(in srgb, var(--accent-tint) 58%, transparent); - color: var(--accent-strong); -} -.agent-card:hover:not(.disabled) { - border-color: var(--border-strong); -} -.agent-card.active { - border-color: var(--border); - background: color-mix(in srgb, var(--accent-tint) 54%, var(--bg-panel)); - box-shadow: none; -} -.agent-card.active::before { - content: ''; - position: absolute; - inset: 0 auto 0 0; - width: 3px; - background: var(--accent); -} -.agent-card.disabled { - cursor: not-allowed; - opacity: 0.55; -} -.agent-card.disabled.agent-card-unavailable { - opacity: 0.72; -} -.agent-card-actions { - display: flex; - flex-wrap: wrap; - gap: 6px 10px; - margin-top: 3px; -} -/* Inline variant: links sit to the right of the agent name as a sibling - of .agent-card-body, so unavailable cards collapse to a single row - (no "not installed" label, no version meta) and the long tail of - unavailable adapters shrinks. */ -.agent-card-actions--inline { - margin-top: 0; - flex: 0 0 auto; - flex-wrap: nowrap; - align-items: center; -} -.agent-card-link { - font-size: 11px; - font-weight: 500; - color: var(--text-muted); - text-decoration: none; - transition: color 120ms ease; -} -.agent-card-link:hover { - color: var(--text); - text-decoration: underline; - text-underline-offset: 2px; -} -/* Docs link: secondary affordance, plain muted text. Sits to the left - of the Install ghost button so the primary "do this" action keeps the - right edge. Hover toggles the underline only — no background, no - color change — to keep visual weight low. */ -.agent-card-link--muted { - color: var(--text-muted); - font-weight: 500; -} -.agent-card-link--muted:hover { - color: var(--text); -} -/* Install link: primary affordance for an unavailable card, styled as - a ghost button (transparent + outlined) so it reads as a real - actionable target rather than yet another text link. */ -.agent-card-link--ghost { - display: inline-flex; - align-items: center; - padding: 3px 10px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: transparent; - color: var(--text); -} -.agent-card-link--ghost:hover { - background: var(--bg-subtle); - border-color: var(--border-strong); - text-decoration: none; -} -.agent-install-path-hint { - margin-top: 10px; - margin-bottom: 0; -} -.agent-install-guide { - margin-top: 10px; -} -.agent-install-steps { - margin: 8px 0 0; - padding-left: 18px; - color: var(--text-muted); - font-size: 12px; - line-height: 1.45; -} -.agent-install-steps li + li { - margin-top: 4px; -} -.agent-card-body { display: flex; flex-direction: column; min-width: 0; flex: 1; } -.agent-card-name { - display: flex; - align-items: baseline; - gap: 5px; - min-width: 0; - font-size: 12.5px; - font-weight: 650; - color: var(--text); - white-space: nowrap; - overflow: hidden; -} -.agent-card-name > span:first-child, -.agent-card-tagline { - overflow: hidden; - text-overflow: ellipsis; -} -.agent-card-name > span:first-child { - flex: 0 1 auto; -} -.agent-card-name-divider, -.agent-card-tagline { - color: var(--text-muted); - font-weight: 500; -} -.agent-card-tagline { - flex: 1 1 auto; -} -.agent-card-description { - margin-top: 1px; - font-size: 11px; - color: var(--text-muted); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.agent-card-meta { - font-size: 11px; color: var(--text-muted); - font-variant-numeric: tabular-nums; - white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - line-height: 1.35; -} -.agent-card-meta .muted { color: var(--text-soft); font-style: italic; } -.agent-model-row-head { - font-size: 12px; - color: var(--text); -} -.agent-model-row-head strong { - font-weight: 600; -} -.agent-model-row { - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px; - border: 1px solid var(--border-soft); - border-radius: var(--radius-sm); - background: var(--bg-panel); -} -.agent-model-row select, -.agent-model-row input, -.agent-cli-env select, -.agent-cli-env input, -.settings-section-byok select, -.settings-section-byok input { - background-color: var(--bg-subtle); -} -/* - Generic Settings card — wraps a single panel of related controls in a - white-on-soft-border container so users get an obvious "this lump of - fields belongs together" boundary instead of a long flat scroll. Used - by the BYOK form, the Custom instructions block, the Memory list, and - any future Settings section that should read as a card. -*/ -.settings-section-card { - padding: 16px; - border: 1px solid var(--border-soft); - border-radius: var(--radius-sm); - background: var(--bg-panel); -} -.agent-model-row .field { gap: 4px; } -.agent-model-row .field-label { - display: inline-flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; - font-size: 11.5px; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--text-muted); -} -.agent-model-source-badge { - display: inline-flex; - align-items: center; - min-height: 18px; - padding: 2px 6px; - border: 1px solid var(--border); - border-radius: 999px; - background: var(--bg-subtle); - color: var(--text-muted); - font-size: 10.5px; - font-weight: 600; - line-height: 1; - letter-spacing: 0; - text-transform: none; -} -.agent-model-source-badge.live { - border-color: var(--green-border); - background: var(--green-bg); - color: var(--green); -} -.agent-model-source-badge.fallback { - min-height: auto; - padding: 0; - border: 0; - border-radius: 0; - background: transparent; - color: var(--text-muted); - font-size: 11.5px; - font-weight: 500; -} -.agent-model-source-badge.fallback::before { - content: '·'; - margin-right: 6px; - color: var(--text-muted); -} -.agent-model-row .hint { margin: 0; font-size: 11.5px; } -.agent-model-select-wrap { - position: relative; -} -.agent-model-select-wrap select { - appearance: none; - -webkit-appearance: none; - background-image: none; - padding-right: 28px; - width: 100%; -} -.agent-model-select-chevron { - position: absolute; - right: 8px; - top: 50%; - transform: translateY(-50%); - color: var(--text-soft); - pointer-events: none; -} -.agent-cli-env { - padding: 10px 12px; - border: 1px solid var(--border-soft); - border-radius: var(--radius-sm); - background: var(--bg-panel); -} -.agent-cli-env-summary { - list-style: none; - cursor: pointer; - display: flex; - align-items: center; - gap: 8px; - user-select: none; -} -.agent-cli-env-summary::-webkit-details-marker { - display: none; -} -.agent-cli-env-summary::before { - content: ''; - width: 0; - height: 0; - border-left: 5px solid currentColor; - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - color: var(--text-muted); - transition: transform 0.15s ease; -} -.agent-cli-env[open] > .agent-cli-env-summary::before { - transform: rotate(90deg); -} -.agent-cli-env-summary-title { - font-size: 13px; - font-weight: 600; - color: var(--text); -} -.agent-cli-env-body { - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 10px; -} -.agent-cli-env-body .hint { - margin: 0; - font-size: 11.5px; -} -.agent-cli-env-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 8px; -} -.status-dot { - width: 8px; height: 8px; - border-radius: 50%; - background: #cbd5e1; - flex-shrink: 0; -} -.status-dot.active { - background: var(--accent); - box-shadow: 0 0 0 3px var(--accent-soft); -} -/* Flat brand glyph rendered single-color via currentColor — no surrounding - colored block. Brands that ship with simple-icons get their official - mark; the rest fall back to .agent-icon-fallback below. */ -.agent-icon { - flex-shrink: 0; - color: var(--text); -} -.agent-icon-fallback { - display: inline-flex; - align-items: center; - justify-content: center; - background: var(--bg-subtle); - color: var(--text-muted); - border-radius: 6px; - font-weight: 600; - font-family: var(--font-mono, monospace); - letter-spacing: 0; -} -/* Theme-aware rendering for monochrome brand SVGs whose paths use - `fill="currentColor"`. Loaded through `` they'd resolve to the - SVG document's default (black) and disappear against dark surfaces. - Painting them via a CSS mask lets `background-color: currentColor` - inherit the surrounding text color, so the same mark stays legible - under light + dark themes. */ -.agent-icon-mono { - display: inline-block; - background-color: currentColor; - -webkit-mask-size: contain; - mask-size: contain; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - -webkit-mask-position: center; - mask-position: center; -} - -.error { - border-color: var(--red-border); - background: var(--red-bg); - color: var(--red); -} - -/* =========================================================== - Activity ticker (legacy — still used in some flows) - =========================================================== */ -.activity { - margin: 4px 0 8px; - padding: 8px 10px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-subtle); -} -.activity-header { display: flex; align-items: center; gap: 8px; font-size: 12px; margin-bottom: 6px; } -.activity-header .dot { - width: 8px; height: 8px; - border-radius: 50%; - background: var(--text-muted); - flex: 0 0 auto; -} -.activity-header .dot[data-active="true"] { - background: var(--accent); - animation: pulse 1.2s ease-in-out infinite; -} -.activity-title { font-weight: 500; } -.activity-stats { - margin-left: auto; - color: var(--text-muted); - font-variant-numeric: tabular-nums; - font-size: 11px; -} -.activity-list { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - gap: 4px; - max-height: 280px; - overflow-y: auto; -} -.activity-item { - display: flex; - align-items: flex-start; - gap: 8px; - font-size: 12px; - line-height: 1.4; - padding: 2px 0; -} -.activity-item .badge { - flex: 0 0 auto; - display: inline-block; - padding: 1px 6px; - border-radius: 4px; - font-size: 10px; - font-weight: 500; - letter-spacing: 0.02em; - background: var(--bg-subtle); - color: var(--text-muted); - border: 1px solid var(--border); - min-width: 56px; - text-align: center; -} -.activity-item .badge-tool { background: var(--blue-bg); border-color: var(--blue-border); color: var(--blue); } -.activity-item .badge-result { background: var(--green-bg); border-color: var(--green-border); color: var(--green); } -.activity-item .badge-error, -.activity-item .badge-result.badge-error { - background: var(--red-bg); border-color: var(--red-border); color: var(--red); -} -.activity-item .badge-thinking { background: var(--purple-bg); border-color: var(--purple-border); color: var(--purple); } -.activity-item .badge-status { background: var(--bg-panel); } -.activity-item .badge-text { background: transparent; border-color: var(--border); } -.activity-item .badge-usage { background: var(--bg-panel); color: var(--text-muted); } -.activity-item .detail { flex: 1; min-width: 0; overflow-wrap: break-word; word-break: break-word; } -.activity-item .detail.muted { color: var(--text-muted); } -.activity-item .thinking-text { font-style: italic; color: var(--text-muted); } -.activity-waiting { font-size: 11px; color: var(--text-muted); font-style: italic; } - -/* ============================================================ - Entry view — left sidebar + right tabs - ============================================================ */ -.entry-shell { - display: grid; - grid-template-rows: auto minmax(0, 1fr); - height: 100vh; - min-height: 0; - background: var(--bg); -} -/* Note: `.entry-shell--no-header` and the rest of the entry-view - redesign live in `src/styles/home/`. Keeping that bundle out of - this file makes upstream `index.css` rebases easier. */ - -.entry { - display: grid; - grid-template-columns: var(--entry-rail-width, 64px) 1fr; - height: 100%; - min-height: 0; - background: var(--bg); -} - -.entry-side { - border-right: 1px solid var(--border); - background: transparent; - display: flex; - flex-direction: column; -} - -/* ============================================================ - NEW PROJECT PANEL — design standards - This block applies to every tab inside `.newproj` (prototype, - live-artifact, slide deck, template, media, other). Pickers and - options added to new tabs should slot into these rules instead - of inventing local variants. - - Heights - text input ............ 30px (.newproj-name, .newproj-folder-input) - dropdown trigger ...... 38px (.newproj-section .ds-picker-trigger) - compact toggle ........ 36px (.compact-toggle) - popover item .......... 38px (.ds-picker-item) - - Color rules — neutral only. The orange accent is reserved for - the Create CTA. Nothing else inside `.newproj` should pull from - `var(--accent)` / `--accent-tint` / `--accent-soft`. - hover bg .............. var(--bg-subtle) - focused input ......... border var(--text) + 8% black halo - (.entry-side input:focus) - active border ......... var(--border-strong) - active mark ........... var(--text) - segmented control ..... mirror .subtab-pill (gray rounded - container + white raised active button) - ============================================================ */ -.newproj { - display: flex; - flex-direction: column; - flex: 1; - min-height: 0; - overflow: hidden; - padding-top: 24px; -} -.newproj-tabs-shell { - position: relative; - overflow: visible; - padding: 0 24px; - margin-bottom: -1px; - z-index: 2; - flex: 0 0 auto; -} -.newproj-tabs { - display: flex; - padding: 0; - gap: 2px; - overflow-x: auto; - overflow-y: hidden; - scroll-behavior: smooth; - scrollbar-width: none; -} -.newproj-tabs::-webkit-scrollbar { display: none; } -.newproj-tabs-shell::before, -.newproj-tabs-shell::after { - content: ''; - position: absolute; - top: 0; - bottom: 0; - width: 28px; - z-index: 1; - pointer-events: none; - opacity: 0; - transition: opacity 120ms ease; -} -.newproj-tabs-shell::before { - left: 0; - background: linear-gradient(90deg, var(--bg), transparent); -} -.newproj-tabs-shell::after { - right: 0; - background: linear-gradient(270deg, var(--bg), transparent); -} -.newproj-tabs-shell.can-left::before, -.newproj-tabs-shell.can-right::after { - opacity: 1; -} -.newproj-tabs-arrow { - display: none; - position: absolute; - top: 50%; - z-index: 2; - width: 28px; - height: 28px; - transform: translateY(-50%); - border: 1px solid var(--border); - border-radius: 999px; - background: var(--bg-panel); - color: var(--text-strong); - box-shadow: var(--shadow-xs); - align-items: center; - justify-content: center; - padding: 0; - transition: opacity 120ms ease, transform 120ms ease; -} -.newproj-tabs-arrow:hover { border-color: var(--border-strong); background: var(--bg-subtle); } -.newproj-tabs-arrow svg { - display: block; - flex: none; -} -.newproj-tabs-arrow.left { left: 6px; } -.newproj-tabs-arrow.right { right: 6px; } -.newproj-tabs-arrow.hidden { - opacity: 0; - pointer-events: none; - transform: translateY(-50%) scale(0.92); -} -.newproj-tab { - flex: 0 0 auto; - min-width: max-content; - padding: 7px 14px; - background: transparent; - font-size: 12px; - color: var(--text-muted); - font-weight: 500; - border: 1px solid transparent; - border-radius: 12px 12px 0 0; - position: relative; - transition: none; -} -.newproj-tab:focus, -.newproj-tab:active { - background: transparent; - border-color: transparent; - outline: none; - color: var(--text-muted); -} -.newproj-tab:focus-visible { - outline: none; - box-shadow: inset 0 0 0 2px var(--selected); - border-radius: 8px 8px 0 0; - color: var(--text); -} -.newproj-tab:hover:not(:disabled) { - background: transparent; - border-color: transparent; - outline: none; - color: var(--text); -} -.newproj-tab.active, -.newproj-tab.active:hover:not(:disabled), -.newproj-tab.active:focus, -.newproj-tab.active:focus-visible { - color: var(--text); - background: var(--bg-panel); - border-color: var(--border); - border-bottom-color: var(--bg-panel); - z-index: 3; -} -.newproj-body { - margin: 0 24px; - padding: 16px 18px 12px; - border: 1px solid var(--border); - border-radius: 0 0 12px 12px; - background: var(--bg-panel); - box-shadow: var(--shadow-xs); - display: flex; - flex-direction: column; - gap: 12px; - /* The parent `.newproj` is `overflow: hidden`; the card needs to be the - scroll container so taller form variants (image/media tabs, validation - messages, compact-height windows) stay reachable inside the sidebar. - `flex: 0 1 auto` keeps the card at its natural content height when the - form is short (so there's no big white void at the bottom of the - card), but lets `flex-shrink: 1` collapse it down to the available - space when content is long — at which point `overflow-y: auto` takes - over and the body scrolls. */ - flex: 0 1 auto; - min-height: 0; - overflow-y: auto; -} -.newproj-title { - margin: 0; - font-size: 13px; - font-weight: 550; - display: flex; - align-items: center; - gap: 8px; -} -.newproj-title-text { flex: 1 1 auto; min-width: 0; } -.newproj-title-badge { - flex: 0 0 auto; - display: inline-flex; - align-items: center; - padding: 2px 8px; - border-radius: var(--radius-pill); - border: 1px solid var(--border); - background: transparent; - color: var(--text-muted); - font-size: 10px; - font-weight: 700; - letter-spacing: 0.08em; - text-transform: uppercase; - line-height: 1.4; -} -.newproj-name { width: 100%; height: 30px; padding: 0 10px; } -.newproj-section { display: flex; flex-direction: column; gap: 6px; } -.platform-picker-hint { - margin: 0; - color: var(--text-muted); - font-size: 11px; - line-height: 1.35; -} -/* PlatformPicker reuses the DS picker visual language (`.ds-picker-*`) - declared further down, so its bespoke `.platform-dropdown-*` rules were - removed — the dropdown trigger and popover render through the shared - `.ds-picker-trigger` / `.ds-picker-popover` / `.ds-picker-item` styles - for a neutral, accent-free look that matches the rest of the panel. */ -.newproj-label { - font-size: 11.5px; - color: var(--text-muted); - font-weight: 500; -} -.newproj-media-options { - display: flex; - flex-direction: column; - gap: 14px; -} -.newproj-media-field, -.newproj-media-options .newproj-label { - display: flex; - flex-direction: column; - gap: 6px; -} -.newproj-model-groups { - display: flex; - flex-direction: column; - gap: 12px; -} -.newproj-model-group { - display: flex; - flex-direction: column; - gap: 8px; -} -.newproj-provider-row { - display: flex; - align-items: center; - gap: 8px; - color: var(--text-muted); - font-size: 11px; - font-weight: 700; - letter-spacing: 0.11em; - text-transform: uppercase; -} -.newproj-provider-badge { - border-radius: 999px; - padding: 2px 8px; - font-size: 10px; - letter-spacing: 0.08em; -} -.newproj-provider-badge.configured { - border: 1px solid color-mix(in srgb, #4169e1 24%, transparent); - background: color-mix(in srgb, #4169e1 10%, transparent); - color: #3155c9; -} -.newproj-provider-badge.integrated { - border: 1px solid color-mix(in srgb, var(--accent) 24%, transparent); - background: color-mix(in srgb, var(--accent) 10%, transparent); - color: color-mix(in srgb, var(--accent) 78%, var(--text-strong)); -} -.newproj-provider-badge.unsupported { - border: 1px solid var(--border); - background: var(--bg-subtle); - color: var(--text-muted); -} -.newproj-model-grid, -.newproj-option-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 8px; -} -.newproj-option-grid.aspect-grid { - grid-template-columns: repeat(3, minmax(0, 1fr)); -} -.newproj-option-grid.compact { - grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); -} -.newproj-card { - border: 1px solid var(--border); - border-radius: 10px; - background: var(--bg-panel); - color: var(--text-strong); - box-shadow: var(--shadow-xs); - cursor: pointer; - text-align: left; - transition: border-color 140ms ease, background 140ms ease, box-shadow 140ms ease; -} -.newproj-card:hover { - border-color: var(--border-strong); - background: var(--bg-subtle); -} -.newproj-card.active { - border-color: var(--border-strong); - background: var(--bg-subtle); - box-shadow: none; -} -.newproj-model-card { - min-height: 74px; - padding: 12px; - display: flex; - flex-direction: column; - gap: 5px; -} -.newproj-model-name { - font-family: var(--font-mono); - font-size: 14px; - font-weight: 700; - line-height: 1.2; -} -.newproj-model-hint { - color: var(--text-muted); - font-size: 12px; - line-height: 1.35; -} -.newproj-option-card { - min-height: 62px; - min-width: 0; - padding: 10px; - display: flex; - align-items: center; - justify-content: center; - gap: 7px; - text-align: center; - color: var(--text-muted); - font-size: 12px; - font-weight: 600; -} -.newproj-option-card small { - color: var(--text-muted); - font-size: 11px; -} -.aspect-grid .newproj-option-card { - flex-direction: column; - gap: 6px; - padding: 10px 6px; -} -.aspect-copy { - display: flex; - flex-direction: column; - gap: 2px; - align-items: center; - line-height: 1.15; - min-width: 0; - max-width: 100%; -} -.aspect-copy strong { - color: var(--text-muted); - font-size: 12.5px; - font-weight: 650; - letter-spacing: -0.005em; - max-width: 100%; - overflow-wrap: anywhere; -} -.aspect-copy small { - color: var(--text-muted); - font-size: 11.5px; -} -.aspect-glyph { - flex: none; - width: 34px; - height: 24px; - border-radius: 4px; - background: var(--bg-subtle); - box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border) 80%, transparent); -} -.aspect-1-1 { width: 22px; height: 22px; } -.aspect-16-9 { width: 32px; height: 18px; } -.aspect-9-16 { width: 18px; height: 32px; } -.aspect-4-3 { width: 28px; height: 21px; } -.aspect-3-4 { width: 21px; height: 28px; } -@media (max-width: 560px) { - .newproj-model-grid, - .newproj-option-grid, - .newproj-option-grid.aspect-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} -.newproj-skills { - display: flex; - flex-direction: column; - gap: 6px; - max-height: 220px; - overflow-y: auto; - padding-right: 4px; -} -.skill-radio { - display: flex; - gap: 8px; - padding: 10px 12px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; - align-items: flex-start; - background: var(--bg-panel); -} -.skill-radio:hover { background: var(--bg-subtle); border-color: var(--border-strong); } -.skill-radio.active { - border-color: var(--accent); - background: var(--accent-tint); -} -.skill-radio input { width: auto; margin-top: 2px; } -.skill-radio-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; } -.skill-radio-name { font-weight: 500; font-size: 13px; } -.skill-radio-desc { font-size: 11px; color: var(--text-muted); line-height: 1.4; } -.newproj-empty { - font-size: 12px; color: var(--text-muted); - font-style: italic; padding: 8px 0; -} -.video-body, -.audio-body { - display: flex; - align-items: center; - justify-content: center; - padding: 24px; - background: var(--bg-subtle); - flex: 1; - min-height: 0; -} -.video-body video { - max-width: 100%; - max-height: 100%; - border-radius: var(--radius-sm); - background: #000; -} -.audio-card { - display: flex; - flex-direction: column; - align-items: center; - gap: 10px; - width: min(100%, 480px); - padding: 28px 32px; - color: var(--text-muted); - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius); -} -.audio-card-name { - font-size: 13px; - font-weight: 500; - color: var(--text); - text-align: center; - word-break: break-word; -} -.audio-card audio { - width: 100%; -} -.newproj-create { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - width: 100%; - padding: 8px 11px; - margin-top: 2px; - font-size: 13px; - border-radius: var(--radius-sm); -} -.newproj-import { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - width: 100%; - padding: 6px 10px; - font-size: 12.5px; - border-radius: var(--radius-sm); -} -.newproj-create svg, -.newproj-import svg { - display: block; - flex: 0 0 auto; -} -.newproj-open-folder { - display: flex; - flex-direction: column; - gap: 6px; - margin-top: 2px; -} -.newproj-folder-input { - width: 100%; - height: 30px; - padding: 0 10px; - font-size: 12px; - font-family: var(--font-mono, monospace); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-input, var(--bg)); - color: var(--text); - box-sizing: border-box; -} -.newproj-folder-input:focus { - outline: none; - border-color: var(--accent); -} -.newproj-folder-input::placeholder { - color: var(--text-muted); - opacity: 0.7; -} -.newproj-footer { - padding: 6px 24px 16px; - font-size: 11px; - color: var(--text-muted); - text-align: center; - flex: 0 0 auto; - /* Sits a fixed, tight distance below the body card. Visible card→text - gap = padding-top (6). */ - margin-top: 0; -} - -/* -------- Fidelity cards (prototype tab) ---------------------------- */ -.fidelity-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 8px; -} -.fidelity-card { - position: relative; - display: flex; - flex-direction: column; - align-items: stretch; - gap: 4px; - padding: 6px 6px 8px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; - transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease; -} -.fidelity-card:hover { border-color: var(--border-strong); } -.fidelity-card.active { - border-color: var(--selected); - box-shadow: - 0 0 0 1px var(--selected), - 0 1px 0 rgba(37, 99, 235, 0.04); -} -.fidelity-thumb { - display: block; - width: 100%; - aspect-ratio: 16 / 5; - border-radius: 4px; - overflow: hidden; - background: var(--bg-subtle); - border: 1px solid var(--border-soft); -} -.fidelity-thumb-wireframe { background: #fbfaf6; } -.fidelity-thumb-high-fidelity { background: var(--bg-panel); } -.fidelity-card.active .fidelity-thumb { border-color: var(--selected-soft); } -.fidelity-label { - text-align: center; - font-size: 11.5px; - font-weight: 500; - color: var(--text); -} - -/* -------- Toggle row (deck "speaker notes", template "animations") -- */ -.toggle-row { - display: flex; - align-items: center; - gap: 12px; - width: 100%; - padding: 12px 14px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; - text-align: left; - transition: border-color 120ms ease, background 120ms ease; -} -.toggle-row:hover { border-color: var(--border-strong); } -.toggle-row.on { border-color: var(--border-strong); background: var(--bg-subtle); } -.toggle-row.disabled, -.toggle-row:disabled { - cursor: not-allowed; - opacity: 0.58; - background: var(--bg-subtle); -} -.toggle-row.disabled:hover, -.toggle-row:disabled:hover { border-color: var(--border); } -.toggle-row-text { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 2px; -} -.toggle-row-label { - font-size: 13px; - font-weight: 500; - color: var(--text); -} -.toggle-row-hint { - font-size: 11.5px; - color: var(--text-muted); - line-height: 1.4; -} -.toggle-row-switch { - flex: none; - position: relative; - width: 32px; - height: 18px; - border-radius: 999px; - background: var(--border-strong); - transition: background 160ms ease; -} -.toggle-row-switch::after { - content: ''; - position: absolute; - top: 2px; - left: 2px; - width: 14px; - height: 14px; - border-radius: 50%; - background: #fff; - box-shadow: 0 1px 2px rgba(28, 27, 26, 0.18); - transition: transform 160ms cubic-bezier(0.2, 0, 0.2, 1); -} -.toggle-row.on .toggle-row-switch { background: var(--text); } -.toggle-row.on .toggle-row-switch::after { transform: translateX(14px); } - -/* -------- Compact toggle (lightweight inline variant used by - SurfaceOptions; one-line, no border, hint moves to a native tooltip). -- */ -.compact-toggle-list { - display: flex; - flex-direction: column; - gap: 2px; -} -.compact-toggle { - display: flex; - align-items: center; - gap: 12px; - width: 100%; - min-height: 36px; - padding: 4px 10px; - background: transparent; - border: 1px solid transparent; - border-radius: var(--radius-sm); - cursor: pointer; - text-align: left; - transition: background 120ms ease; -} -.compact-toggle:hover { background: var(--bg-subtle); } -.compact-toggle.disabled, -.compact-toggle:disabled { - cursor: not-allowed; - opacity: 0.58; - background: transparent; -} -.compact-toggle.disabled:hover, -.compact-toggle:disabled:hover { background: transparent; } -.compact-toggle-label { - flex: 1; - min-width: 0; - font-size: 12.5px; - color: var(--text); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.compact-toggle-switch { - flex: none; - position: relative; - width: 28px; - height: 16px; - border-radius: 999px; - background: var(--border-strong); - transition: background 160ms ease; -} -.compact-toggle-switch::after { - content: ''; - position: absolute; - top: 2px; - left: 2px; - width: 12px; - height: 12px; - border-radius: 50%; - background: #fff; - box-shadow: 0 1px 2px rgba(28, 27, 26, 0.18); - transition: transform 160ms cubic-bezier(0.2, 0, 0.2, 1); -} -.compact-toggle.on .compact-toggle-switch { background: var(--text); } -.compact-toggle.on .compact-toggle-switch::after { transform: translateX(12px); } - -/* -------- Template picker (template tab) ---------------------------- */ -.template-list { - display: flex; - flex-direction: column; - gap: 6px; -} -.template-option { - display: flex; - align-items: center; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - text-align: left; - transition: border-color 120ms ease, background 120ms ease; -} -.template-option:hover { border-color: var(--border-strong); } -.template-option.active { - border-color: var(--accent); - background: var(--accent-tint); -} -.template-option-select { - display: flex; - align-items: flex-start; - gap: 10px; - flex: 1; - min-width: 0; - padding: 12px 14px; - background: none; - border: none; - cursor: pointer; - text-align: left; -} -.template-option-delete { - flex: none; - padding: 4px 10px; - margin-right: 8px; - background: none; - border: none; - cursor: pointer; - color: var(--text-muted); - font-size: 12px; - border-radius: var(--radius-sm); - opacity: 0; - transition: opacity 120ms ease, color 120ms ease; -} -.template-option:hover .template-option-delete { opacity: 1; } -.template-option-delete:hover { color: var(--danger, #e53e3e); } -.template-radio { - flex: none; - margin-top: 2px; - width: 14px; - height: 14px; - border-radius: 50%; - border: 1.5px solid var(--border-strong); - background: var(--bg-panel); - position: relative; -} -.template-radio.active { - border-color: var(--accent); - background: var(--bg-panel); -} -.template-radio.active::after { - content: ''; - position: absolute; - inset: 2px; - border-radius: 50%; - background: var(--accent); -} -.template-option-text { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 2px; -} -.template-option-name { - font-size: 13px; - font-weight: 500; - color: var(--text); -} -.template-option-desc { - font-size: 11.5px; - color: var(--text-muted); - line-height: 1.4; -} -.template-howto { - display: flex; - flex-direction: column; - gap: 4px; - padding: 12px 14px; - background: var(--bg-subtle); - border: 1px dashed var(--border-strong); - border-radius: var(--radius-sm); -} -.template-howto-title { - font-size: 12.5px; - font-weight: 500; - color: var(--text); -} -.template-howto-body { - font-size: 11.5px; - color: var(--text-muted); - line-height: 1.45; -} - -/* -------- New project · connectors section (live-artifact tab) ----- */ -.newproj-connectors { gap: 8px; } -.newproj-connectors-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; -} -.newproj-connectors-manage { - padding: 2px 8px; - font-size: 11px; - font-weight: 500; - letter-spacing: 0.02em; - color: var(--text-muted); - background: transparent; - border: 1px solid var(--border); - border-radius: 999px; - cursor: pointer; - transition: color 120ms ease, border-color 120ms ease, background 120ms ease; -} -.newproj-connectors-manage:hover { - color: var(--text); - border-color: var(--border-strong); - background: var(--bg-subtle); -} -.newproj-connectors-hint { - font-size: 11.5px; - color: var(--text-muted); - line-height: 1.45; -} -.newproj-connectors-list { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-wrap: wrap; - gap: 6px; -} -.newproj-connector-chip { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 10px 4px 8px; - font-size: 11.5px; - color: var(--text); - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: 999px; - max-width: 100%; - min-width: 0; -} -.newproj-connector-dot { - width: 6px; - height: 6px; - border-radius: 999px; - background: #3bb273; - box-shadow: 0 0 0 3px color-mix(in srgb, #3bb273 22%, transparent); - flex: none; -} -.newproj-connector-name { - font-weight: 500; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 140px; -} -.newproj-connector-account { - color: var(--text-muted); - font-size: 11px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 120px; -} -.newproj-connectors-empty { - display: flex; - align-items: flex-start; - gap: 10px; - width: 100%; - padding: 12px 14px; - background: var(--bg-subtle); - border: 1px dashed var(--border-strong); - border-radius: var(--radius-sm); - cursor: pointer; - text-align: left; - transition: border-color 140ms ease, background 140ms ease, transform 140ms ease; -} -.newproj-connectors-empty:hover { - border-color: var(--accent); - border-style: solid; - background: color-mix(in srgb, var(--accent) 4%, var(--bg-subtle)); -} -.newproj-connectors-empty:active { transform: translateY(1px); } -.newproj-connectors-empty:focus-visible { - outline: none; - border-color: var(--accent); - box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent); -} -.newproj-connectors-empty-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 26px; - height: 26px; - flex: none; - border-radius: 8px; - background: var(--bg-panel); - border: 1px solid var(--border); - color: var(--text-muted); -} -.newproj-connectors-empty:hover .newproj-connectors-empty-icon { - color: var(--accent); - border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); -} -.newproj-connectors-empty-text { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} -.newproj-connectors-empty-title { - font-size: 12.5px; - font-weight: 500; - color: var(--text); -} -.newproj-connectors-empty-body { - font-size: 11.5px; - color: var(--text-muted); - line-height: 1.45; -} -.newproj-connectors-empty-cta { - margin-top: 4px; - font-size: 11.5px; - font-weight: 500; - color: var(--accent); -} - -/* -------- Design system picker (custom popover dropdown) ------------ */ -.ds-picker { position: relative; } -.ds-picker-trigger { - display: flex; - align-items: center; - gap: 8px; - width: 100%; - padding: 6px 10px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; - text-align: left; - transition: border-color 120ms ease, box-shadow 120ms ease; -} -/* New project panel triggers run a tighter 38px row so the dropdowns sit - close to the project-name input and the Companion toggle. Other call - sites (Settings dialog, model picker) keep their own sizing. */ -.newproj-section .ds-picker-trigger { - min-height: 38px; - padding: 2px 10px; -} -.ds-picker-trigger:hover { border-color: var(--border-strong); } -.ds-picker-trigger.open { - border-color: var(--border-strong); - box-shadow: none; -} -.ds-picker-meta { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 1px; -} -.ds-picker-title { - display: inline-flex; - align-items: center; - gap: 6px; - font-size: 12.5px; - font-weight: 500; - line-height: 1.2; - color: var(--text); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.ds-picker-extra-pill { - font-size: 10px; - font-weight: 600; - letter-spacing: 0.04em; - padding: 1px 6px; - background: var(--accent-soft); - color: var(--accent); - border-radius: 999px; -} -.ds-picker-sub { - font-size: 11px; - line-height: 1.2; - color: var(--text-muted); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.ds-picker-chevron { - flex: none; - color: var(--text-muted); - transition: transform 160ms ease; -} -.ds-picker-trigger.empty .ds-picker-title { color: var(--text-muted); font-weight: 500; } - -/* Avatar — square with 2x2 swatch grid (or "none" diagonal slash). */ -.ds-avatar { - position: relative; - flex: none; - width: 24px; - height: 24px; - border-radius: 5px; - overflow: hidden; - border: 1px solid var(--border); - background: var(--bg-panel); - display: inline-flex; - align-items: center; - justify-content: center; -} -.ds-avatar-grid { - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: 1fr 1fr; - width: 100%; - height: 100%; -} -.ds-avatar-cell { display: block; } -.ds-avatar-stack { - position: absolute; - right: -2px; - bottom: -2px; - font-size: 9px; - font-weight: 600; - letter-spacing: 0.02em; - padding: 1px 5px; - background: var(--text-strong); - color: #fff; - border-radius: 999px; - border: 2px solid var(--bg-panel); -} -.ds-avatar-none { - background: var(--bg-subtle); - color: var(--text-faint); -} - -/* Popover */ -.ds-picker-popover { - position: absolute; - top: calc(100% + 6px); - left: 0; - right: 0; - z-index: 30; - display: flex; - flex-direction: column; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-lg); - overflow: hidden; - animation: ds-pop-in 140ms cubic-bezier(0.2, 0, 0.2, 1); -} -@keyframes ds-pop-in { - from { opacity: 0; transform: translateY(-4px); } - to { opacity: 1; transform: translateY(0); } -} -.ds-picker-head { - display: flex; - align-items: center; - gap: 8px; - padding: 8px; - border-bottom: 1px solid var(--border); - background: var(--bg-panel); -} -.ds-picker-search { - flex: 1; - padding: 6px 10px !important; - font-size: 12.5px; - background: var(--bg-panel); - border: 1px solid var(--border) !important; - border-radius: var(--radius-sm) !important; -} -/* Mirror the `.subtab-pill` segmented control so all left/right toggles in - the app share one visual language — gray container, white raised tab for - active. Density stays popover-tight (smaller padding than the toolbar - variant). */ -.ds-picker-mode { - flex: none; - display: inline-flex; - padding: 3px; - gap: 2px; - background: var(--bg-subtle); - border: 1px solid var(--border); - border-radius: var(--radius); -} -.ds-picker-mode-btn { - padding: 3px 12px !important; - font-size: 11px !important; - font-weight: 500; - border: none !important; - background: transparent !important; - color: var(--text-muted) !important; - border-radius: var(--radius-sm) !important; - box-shadow: none !important; -} -.ds-picker-mode-btn:hover:not(.active) { - color: var(--text) !important; -} -.ds-picker-mode-btn.active { - background: var(--bg-panel) !important; - color: var(--text) !important; - box-shadow: var(--shadow-xs) !important; -} -.ds-picker-list { - display: flex; - flex-direction: column; - max-height: 320px; - overflow-y: auto; - padding: 4px; -} -.ds-picker-list-design-systems { - min-height: 120px; -} -.ds-picker-empty { - padding: 16px 12px; - font-size: 12px; - color: var(--text-muted); - text-align: center; -} -.ds-picker-item { - display: flex; - align-items: center; - gap: 10px; - min-height: 38px; - padding: 4px 8px; - background: transparent; - border: 1px solid transparent; - border-radius: var(--radius-sm); - cursor: pointer; - text-align: left; -} -.ds-picker-item:hover { background: var(--bg-subtle); } -.ds-picker-item.active { - border-color: var(--border-strong); -} -.ds-picker-item-text { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 1px; -} -.ds-picker-item-title { - display: inline-flex; - align-items: center; - gap: 6px; - font-size: 12.5px; - font-weight: 500; - line-height: 1.2; - color: var(--text); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.ds-picker-item-badge { - font-size: 9px; - font-weight: 600; - letter-spacing: 0.06em; - padding: 2px 5px; - background: transparent; - color: var(--text-muted); - border: 1px solid var(--border); - border-radius: 4px; -} -.ds-picker-item-sub { - font-size: 11px; - line-height: 1.2; - color: var(--text-muted); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* Selection mark — a circle for single-select, a square for multi. - In multi mode the active state shows the pick order (1 = primary). */ -.ds-picker-mark { - flex: none; - display: inline-flex; - align-items: center; - justify-content: center; - width: 18px; - height: 18px; - font-size: 10px; - font-weight: 600; - color: transparent; -} -.ds-picker-mark.radio { - border-radius: 50%; - border: 1.5px solid var(--border-strong); - background: var(--bg-panel); - position: relative; -} -.ds-picker-mark.radio.active { - border-color: var(--text); -} -.ds-picker-mark.radio.active::after { - content: ''; - width: 9px; - height: 9px; - border-radius: 50%; - background: var(--text); -} -.ds-picker-mark.check { - border-radius: 4px; - border: 1.5px solid var(--border-strong); - background: var(--bg-panel); - color: var(--bg-panel); -} -.ds-picker-mark.check.active { - border-color: var(--text); - background: var(--text); - color: var(--bg-panel); -} - -.ds-picker-foot { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 10px; - border-top: 1px solid var(--border); - background: var(--bg-subtle); - font-size: 11.5px; - color: var(--text-muted); - line-height: 1.4; -} -.ds-picker-foot-text { flex: 1; min-width: 0; } -.ds-picker-foot-text strong { color: var(--text); font-weight: 600; } -.ds-picker-clear { - flex: none; - padding: 4px 10px !important; - font-size: 11px !important; - background: var(--bg-panel) !important; - color: var(--text) !important; - border: 1px solid var(--border) !important; -} -.ds-picker-clear:hover { border-color: var(--border-strong) !important; } - -.entry-side-foot { - margin-top: auto; - display: flex; - flex-direction: column; - gap: 6px; - min-width: 0; - padding: 16px 24px 20px; -} -.entry-side-foot-row { - display: flex; - align-items: center; - gap: 6px; - min-width: 0; -} -.entry-side-foot .foot-pill { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 5px; - padding: 3px 8px; - min-height: 24px; - background: var(--bg-subtle); - border: 1px solid var(--border); - border-radius: var(--radius-pill); - font-size: 10.5px; - color: var(--text-muted); - align-self: flex-start; - cursor: pointer; - text-decoration: none; -} -.entry-side-foot-row .foot-pill { - min-width: 0; -} -.entry-side-foot-row .foot-pill-follow { - flex: 0 1 auto; -} -/* Social cluster (Discord + X) — sits at the right end of the bottom row - via `margin-left: auto`, which opens flex space between the left - "your stuff" group (Language, Pet) and the right "follow us" group. */ -.entry-side-foot-social { - display: inline-flex; - align-items: center; - gap: 4px; - margin-left: auto; -} -.entry-side-foot-row .foot-pill-pet-label, -.entry-side-foot-row .foot-pill-follow-label { - flex: 1 1 auto; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.entry-side-foot .foot-pill:hover { background: var(--bg-panel); border-color: var(--border-strong); color: var(--text); } -.entry-side-foot .foot-pill .ico { font-size: 12px; opacity: 0.7; } -/* The Icon component renders inline SVG with `stroke: currentColor`, so by - default it tracks the pill's text color exactly. Override via opacity to - keep the glyph muted relative to the label — on hover the icon "lifts" - just enough to feel active without going full black, which would make - it compete with the label for attention. */ -.entry-side-foot .foot-pill svg { - width: 10px; - height: 10px; - opacity: 0.55; - transition: opacity 120ms ease; -} -.entry-side-foot .foot-pill:hover svg { - opacity: 0.75; -} - -/* Language switcher pill + popover (entry sidebar foot). */ -.lang-menu-wrap { - position: relative; - align-self: flex-start; -} -.lang-menu-wrap .lang-pill { - font-variant-numeric: tabular-nums; -} -.lang-menu-popover { - position: absolute; - bottom: calc(100% + 6px); - left: 0; - z-index: 50; - min-width: 180px; - width: max-content; - max-width: min(280px, calc(100vw - 48px)); - display: flex; - flex-direction: column; - padding: 4px; - background: var(--bg-panel); - border: 1px solid var(--border-strong); - border-radius: 10px; - box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12); -} -.lang-menu-item { - display: grid; - grid-template-columns: minmax(0, 1fr) auto auto; - align-items: center; - gap: 10px; - padding: 7px 10px; - background: transparent; - border: 0; - border-radius: 7px; - font-size: 12.5px; - color: var(--text); - text-align: left; - cursor: pointer; -} -.lang-menu-label { - min-width: 0; - overflow-wrap: anywhere; -} -.lang-menu-item:hover { background: var(--bg-subtle); } -.lang-menu-item.active { background: var(--bg-subtle); } -.lang-menu-item .lang-menu-code { - color: var(--text-faint); - font-size: 11px; - font-variant-numeric: tabular-nums; - letter-spacing: 0.02em; -} -.lang-menu-item .lang-menu-check { - color: var(--text-muted); - display: inline-flex; - align-items: center; -} - -/* Right side */ -.entry-main { - display: flex; - flex-direction: column; - min-width: 0; - min-height: 0; -} -.entry-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - padding: 24px 28px 0; - border-bottom: 1px solid var(--border); - min-width: 0; -} -.entry-header-tabs-row { - display: flex; - align-items: center; - gap: 24px; -} -.entry-tabs { - align-self: stretch; - display: flex; - align-items: center; - gap: 24px; - min-width: 0; - overflow-x: auto; - overflow-y: hidden; - scrollbar-width: none; -} -.entry-tabs::-webkit-scrollbar { display: none; } -.entry-tab { - background: transparent; - border: none; - border-bottom: 2px solid transparent; - border-radius: 0; - padding: 6px 4px 8px; - font-size: 12px; - color: var(--text-muted); - font-weight: 500; - flex: 0 0 auto; - white-space: nowrap; - transition: none; -} -.entry-tab:focus, -.entry-tab:active { - background: transparent; - border-color: transparent; - border-bottom-color: transparent; - outline: none; - color: var(--text-muted); -} -.entry-tab:focus-visible { - outline: 2px solid var(--selected); - outline-offset: 2px; - border-radius: 4px; - color: var(--text); -} -.entry-tab:hover:not(:disabled) { - background: transparent; - border-color: transparent; - border-bottom-color: transparent; - outline: none; - color: var(--text); -} -.entry-tab.active, -.entry-tab.active:hover:not(:disabled), -.entry-tab.active:focus, -.entry-tab.active:focus-visible { - color: var(--text); - border-bottom-color: var(--text); -} -.entry-header-right { - display: inline-flex; - align-items: center; - gap: 8px; - flex: 0 0 auto; -} - -.entry-tab-content { - flex: 1; - min-height: 0; - overflow-y: auto; - padding: 22px 28px 32px; - background: var(--bg); -} - -.tab-panel { - display: flex; - flex-direction: column; - gap: 18px; -} -.tab-panel-toolbar { - display: flex; - gap: 10px; - align-items: center; - justify-content: space-between; - flex-wrap: wrap; - /* Older browsers ignore row-gap on flex with wrap — explicit row-gap keeps - the wrapped row visually separated rather than flush against the pill. */ - row-gap: 8px; - position: sticky; - top: 0; - z-index: 4; - background: var(--bg-panel); -} -.tab-panel-toolbar .toolbar-left { - display: flex; - gap: 8px; - align-items: center; - flex: 0 0 auto; - min-width: 0; -} -.tab-panel-toolbar .toolbar-right { - display: flex; - gap: 8px; - align-items: center; - flex: 1 1 auto; - min-width: 0; - justify-content: flex-end; - flex-wrap: wrap; -} -.tab-panel-toolbar .toolbar-search { - position: relative; - flex: 1 1 180px; - min-width: 140px; - max-width: 280px; -} -/* Narrow columns (entry tab content sometimes lands at ~570px wide) — keep - the segmented pill on its own row above the search/view toggle so the - search input never collapses into a tiny stub squeezed between two pills. */ -@media (max-width: 720px) { - .tab-panel-toolbar { flex-direction: column; align-items: stretch; } - .tab-panel-toolbar .toolbar-left { justify-content: flex-start; } - .tab-panel-toolbar .toolbar-right { justify-content: space-between; } - .tab-panel-toolbar .toolbar-search { max-width: none; } -} -.tab-panel-toolbar .toolbar-search input { - padding-left: 30px; - background: var(--bg-panel); -} -.tab-panel-toolbar .toolbar-search input[type='search']::-webkit-search-cancel-button { - /* Hide the native clear control; we render our own styled button. */ - appearance: none; - -webkit-appearance: none; -} -.tab-panel-toolbar .toolbar-search input[disabled] { - opacity: 0.6; - cursor: not-allowed; -} -.tab-panel-toolbar.designs-toolbar { - background: transparent; -} -.tab-panel-toolbar .toolbar-search .toolbar-search-clear { - position: absolute; - right: 6px; - top: 50%; - transform: translateY(-50%); - display: inline-flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - padding: 0; - border: 0; - border-radius: 999px; - background: transparent; - color: var(--text-faint); - cursor: pointer; - transition: background-color 120ms ease, color 120ms ease; -} -.tab-panel-toolbar .toolbar-search .toolbar-search-clear:hover { - background: var(--bg-subtle); - color: var(--text); -} -.tab-panel-toolbar .toolbar-search .toolbar-search-clear:focus-visible { - outline: 2px solid var(--text); - outline-offset: 2px; -} -.tab-panel-toolbar .toolbar-search .search-icon { - position: absolute; - left: 9px; - top: 50%; - transform: translateY(-50%); - color: var(--text-faint); - font-size: 13px; - pointer-events: none; -} -.tab-empty { - padding: 48px 0; - text-align: center; - color: var(--text-muted); - font-size: 13px; -} - -.connector-inline-error { - margin: 0 0 12px; - padding: 10px 12px; - border: 1px solid color-mix(in oklab, #ff6b6b 45%, var(--line) 55%); - border-radius: 12px; - background: color-mix(in oklab, #ff6b6b 10%, var(--panel) 90%); - color: color-mix(in oklab, #ff6b6b 70%, var(--fg) 30%); - font-size: 13px; - line-height: 1.45; -} - -.connectors-heading h2 { - margin: 0; - font-size: 18px; - line-height: 1.25; -} -.connectors-heading p { - margin: 4px 0 0; - color: var(--text-muted); - font-size: 13px; -} -.tab-panel-toolbar .toolbar-search.connectors-search { - flex: 0 1 320px; - width: min(320px, 100%); -} -.tab-panel-toolbar .toolbar-search.connectors-search input { - padding-right: 32px; -} -.connectors-empty { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; - padding: 64px 16px; - text-align: center; -} -.connectors-empty-title { - margin: 0; - font-size: 14px; - color: var(--text); - font-weight: 500; - max-width: 480px; - word-break: break-word; -} -.connectors-empty-body { - margin: 0; - font-size: 13px; - color: var(--text-muted); - max-width: 480px; -} -.connectors-empty-action { - margin-top: 8px; -} -.connector-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); - gap: 14px; -} -.connector-card { - position: relative; - display: flex; - flex-direction: column; - gap: 12px; - min-height: 168px; - padding: 16px 16px 14px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-xs); - cursor: pointer; - text-align: left; - transition: - transform 140ms ease, - border-color 140ms ease, - box-shadow 180ms ease, - background 140ms ease; -} -.connector-card:hover:not(.is-locked) { - transform: translateY(-1px); - border-color: color-mix(in srgb, var(--text) 28%, var(--border)); - box-shadow: 0 8px 24px -14px rgba(0, 0, 0, 0.25), var(--shadow-xs); -} -.connector-card:focus-visible { - outline: none; - border-color: color-mix(in srgb, var(--text) 48%, var(--border)); - box-shadow: - 0 0 0 3px color-mix(in srgb, var(--text) 14%, transparent), - 0 8px 24px -14px rgba(0, 0, 0, 0.25); -} -.connector-card.status-connected { - border-color: color-mix(in srgb, var(--green) 40%, var(--border)); - background: linear-gradient( - 180deg, - color-mix(in srgb, var(--green) 5%, var(--bg-panel)) 0%, - var(--bg-panel) 60% - ); -} -.connector-card.status-disabled { background: var(--bg-subtle); } -.connector-card.status-error { - border-color: color-mix(in srgb, var(--red) 35%, var(--border)); -} -.connector-card-top { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; -} -.connector-card-head { - display: flex; - flex-direction: column; - gap: 4px; - min-width: 0; - flex: 1 1 auto; -} -.connector-card-title { - margin: 0; - font-size: 15px; - font-weight: 600; - line-height: 1.25; - letter-spacing: -0.005em; - color: var(--text); - /* The title is now a flex row so a connection-status dot can sit - inline next to the connector name without breaking the title's - truncation. The name span owns the ellipsis; the dot stays at a - fixed size on the trailing edge of the row. */ - display: flex; - align-items: center; - gap: 6px; - min-width: 0; -} -.connector-card-title-name { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -/* Inline title-anchored connection dot. Sized + offset so it visually - aligns with the title's cap-height rather than its full line-height, - and given a soft halo on the connected variant so the live state - reads as a small "online" pulse next to the connector name. The - halo collapses to nothing in non-connected variants because today - the dot is only rendered for `connected` (error/disabled use a - separate pill in the action column). */ -.connector-card-title-dot { - flex: 0 0 auto; - /* Optical alignment: pull the dot up by ~1px so it sits on the - baseline of an uppercase letter rather than the descender line. */ - margin-top: -1px; -} - -/* Connector brand mark. Used in two sizes: a compact 28px tile inside - catalog cards (`size-sm`) and a 44px mark in the detail drawer head - (`size-lg`). The wrapper also hosts the fallback initials tile so the - image fades over a stable, themed surface — there is no flash of empty - space while the network resolves and no broken-icon chrome if the - request fails. The remote logos come from `logos.composio.dev`, keyed - by the lowercased toolkit slug stripped of underscores; the URL is - built in `ConnectorsBrowser.tsx` so the daemon's friendlier ids - (`google_drive`) still resolve to the right CDN entry (`googledrive`). */ -.connector-logo { - position: relative; - flex: 0 0 auto; - display: inline-flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border-radius: 8px; - border: 1px solid var(--border); - background: var(--bg-panel); - overflow: hidden; - isolation: isolate; - /* `var(--bg-panel)` already inverts in dark mode, so the logo's - transparent corners read cleanly on either theme. The remote SVGs - ship with theme-aware fills (we request `?theme=light|dark` to - match), but the soft border still gives them a tidy frame. */ - box-shadow: var(--shadow-xs); - user-select: none; -} -.connector-logo.size-lg { - width: 44px; - height: 44px; - border-radius: 12px; -} -.connector-logo-img { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - object-fit: contain; - /* Inset the rendered image a hair so brand marks with no built-in - padding (small monograms, square photos) don't crash into the - border on the small tile. */ - padding: 4px; - /* Fade the image in when it lands so a slow connection doesn't pop - content under the user's eye. The fallback underneath provides - instant visual presence in the meantime. */ - opacity: 0; - transform: scale(0.96); - transition: opacity 160ms ease-out, transform 160ms ease-out; - z-index: 1; -} -.connector-logo.size-lg .connector-logo-img { - padding: 6px; -} -.connector-logo.state-loaded .connector-logo-img { - opacity: 1; - transform: scale(1); -} -/* Fallback initials tile. Three jobs, in priority order: - 1. While the image is pending, the tile is a *neutral skeleton* — - no color, no letters showing through. The user shouldn't see a - bright colored placeholder flash and then morph into a totally - different brand mark when the SVG lands; that mismatch reads as - "wrong logo" before it reads as "loading". - 2. Once the image has loaded, the tile is fully hidden. Composio - SVGs frequently have transparent regions, and even a faint - colored backdrop bleeds through and tints the brand — exactly - the visual mixing we want to avoid. Hiding it (not just dimming) - guarantees the real image owns the slot. - 3. Only when the network actually fails (`state-error`) — or when - no slug was derivable in the first place (`is-fallback`) — do we - promote the tile to a quiet brand mark with stable initials. - Even then it's deliberately *muted*: a single low-saturation - neutral surface, lighter weight type, and a subtle hue accent - from a hashed palette so two adjacent fallbacks don't read as - identical. The intent is "calm placeholder", never "louder than - the real logos one row up". */ -.connector-logo-fallback { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - font-size: 11px; - font-weight: 600; - letter-spacing: 0.02em; - color: transparent; - background: var(--bg-subtle); - text-transform: uppercase; - z-index: 0; - /* Default: skeleton mode. Initials are kept in the DOM (so a paint - of the error state doesn't have to re-flow text) but rendered - transparent. The only visible thing is the soft `--bg-subtle` - surface, which sits flush with the wrapper border and reads as a - quiet placeholder, not a brand. */ - transition: - color 140ms ease-out, - background-color 140ms ease-out, - opacity 120ms ease-out; -} -.connector-logo.size-lg .connector-logo-fallback { - font-size: 14px; - letter-spacing: 0.04em; -} -/* Pending: gentle shimmer over the neutral surface so the user can - tell something is loading, without a colored tile suggesting a - particular brand. The shimmer only kicks in if the image takes - long enough to matter — short loads finish before the animation - even completes one cycle. */ -.connector-logo.state-pending .connector-logo-fallback { - background: - linear-gradient( - 90deg, - var(--bg-subtle) 0%, - color-mix(in srgb, var(--bg-subtle) 60%, var(--bg-panel)) 50%, - var(--bg-subtle) 100% - ); - background-size: 200% 100%; - animation: connector-logo-shimmer 1400ms ease-in-out infinite; -} -@keyframes connector-logo-shimmer { - from { background-position: 100% 0; } - to { background-position: -100% 0; } -} -/* Loaded: yank the fallback entirely. `visibility: hidden` keeps it - out of the paint pipeline so transparent regions in the SVG can't - composite over a colored backdrop. */ -.connector-logo.state-loaded .connector-logo-fallback { - visibility: hidden; - opacity: 0; -} -/* Error / no-slug: quiet brand mark. Initials become visible, but in - a muted neutral palette by default. The hashed palette below adds - a hint of hue so a row of fallbacks isn't monotone, but every - variant stays low-saturation and low-contrast against the card - surface so the real logos always read as the focal point. */ -.connector-logo.state-error .connector-logo-fallback, -.connector-logo.is-fallback .connector-logo-fallback { - color: var(--text-muted); - background: var(--bg-subtle); - animation: none; -} -.connector-logo.state-error[data-palette='0'] .connector-logo-fallback, -.connector-logo.is-fallback[data-palette='0'] .connector-logo-fallback { - background: color-mix(in oklab, var(--accent) 6%, var(--bg-subtle)); - color: color-mix(in oklab, var(--accent) 35%, var(--text-muted)); -} -.connector-logo.state-error[data-palette='1'] .connector-logo-fallback, -.connector-logo.is-fallback[data-palette='1'] .connector-logo-fallback { - background: color-mix(in oklab, #6b8afd 7%, var(--bg-subtle)); - color: color-mix(in oklab, #6b8afd 38%, var(--text-muted)); -} -.connector-logo.state-error[data-palette='2'] .connector-logo-fallback, -.connector-logo.is-fallback[data-palette='2'] .connector-logo-fallback { - background: color-mix(in oklab, #2dbfa8 7%, var(--bg-subtle)); - color: color-mix(in oklab, #2dbfa8 38%, var(--text-muted)); -} -.connector-logo.state-error[data-palette='3'] .connector-logo-fallback, -.connector-logo.is-fallback[data-palette='3'] .connector-logo-fallback { - background: color-mix(in oklab, #d18b3a 7%, var(--bg-subtle)); - color: color-mix(in oklab, #d18b3a 40%, var(--text-muted)); -} -.connector-logo.state-error[data-palette='4'] .connector-logo-fallback, -.connector-logo.is-fallback[data-palette='4'] .connector-logo-fallback { - background: color-mix(in oklab, #c356b3 6%, var(--bg-subtle)); - color: color-mix(in oklab, #c356b3 38%, var(--text-muted)); -} -.connector-logo.state-error[data-palette='5'] .connector-logo-fallback, -.connector-logo.is-fallback[data-palette='5'] .connector-logo-fallback { - background: color-mix(in oklab, #5d6b85 9%, var(--bg-subtle)); - color: color-mix(in oklab, #5d6b85 42%, var(--text-muted)); -} -/* The wrapper border is what visually frames the tile. When the real - image is loaded we keep it; in the fallback states we soften it a - step so the tile recedes further compared to a card with a real - logo on the same row. */ -.connector-logo.state-error, -.connector-logo.is-fallback { - border-color: var(--border-soft, var(--border)); - box-shadow: none; -} -@media (prefers-reduced-motion: reduce) { - .connector-logo-img { - transition: none; - transform: none; - } - .connector-logo.state-pending .connector-logo-fallback { - animation: none; - background: var(--bg-subtle); - } -} - -/* Embedded catalog (Settings → Connectors). Cards are tighter here so - the logo shrinks to 24px and sheds a touch of border radius so it - reads as a quiet badge next to the connector name rather than a - prominent brand mark. The card-top gap (already 8px in the embedded - variant) keeps the logo close to the head copy. */ -.connectors-panel-embedded .connector-logo.size-sm { - width: 24px; - height: 24px; - border-radius: 7px; -} -.connectors-panel-embedded .connector-logo.size-sm .connector-logo-img { - padding: 3px; -} -.connectors-panel-embedded .connector-logo.size-sm .connector-logo-fallback { - font-size: 10px; -} - -.connector-meta { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 6px; - color: var(--text-muted); - font-size: 12px; - line-height: 1; -} -.connector-meta-item { - white-space: nowrap; -} -.connector-meta-dot { - color: var(--text-faint); -} -.connector-tools-badge { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 2px 7px; - border-radius: var(--radius-pill); - background: var(--bg-subtle); - border: 1px solid var(--border); - color: var(--text-muted); - font-size: 11px; - font-weight: 500; - line-height: 1.3; - white-space: nowrap; - transform-origin: left center; - will-change: transform, opacity; -} -.connector-tools-badge svg { - opacity: 0.65; -} -.connector-tools-badge.is-ready { - animation: connector-tools-badge-in 420ms cubic-bezier(0.22, 1, 0.36, 1) both; -} -.connector-tools-badge.is-ready svg { - animation: connector-tools-badge-icon-in 520ms cubic-bezier(0.22, 1, 0.36, 1) both; - animation-delay: 120ms; -} -@keyframes connector-tools-badge-in { - 0% { - opacity: 0; - transform: translateY(3px) scale(0.92); - border-color: color-mix(in srgb, var(--border) 40%, transparent); - background: color-mix(in srgb, var(--bg-subtle) 60%, transparent); - } - 55% { - opacity: 1; - border-color: color-mix(in srgb, var(--accent, var(--text-muted)) 30%, var(--border)); - } - 100% { - opacity: 1; - transform: translateY(0) scale(1); - border-color: var(--border); - background: var(--bg-subtle); - } -} -@keyframes connector-tools-badge-icon-in { - 0% { - opacity: 0; - transform: rotate(-12deg) scale(0.8); - } - 100% { - opacity: 0.65; - transform: rotate(0) scale(1); - } -} -@media (prefers-reduced-motion: reduce) { - .connector-tools-badge.is-ready { - animation: connector-tools-badge-fade 180ms ease-out both; - } - .connector-tools-badge.is-ready svg { - animation: none; - } - @keyframes connector-tools-badge-fade { - from { opacity: 0; } - to { opacity: 1; } - } -} -.connector-status { - flex: 0 0 auto; - display: inline-flex; - align-items: center; - gap: 5px; - padding: 2px 8px; - border-radius: var(--radius-pill); - background: var(--bg-subtle); - color: var(--text-muted); - border: 1px solid var(--border); - font-size: 11px; - font-weight: 600; - line-height: 1.4; - letter-spacing: 0.01em; -} -.connector-status-dot { - width: 6px; - height: 6px; - border-radius: 999px; - background: currentColor; - box-shadow: 0 0 0 2px color-mix(in srgb, currentColor 22%, transparent); -} -.connector-status.status-connected, -.connector-status-pill.status-connected { - background: color-mix(in srgb, var(--green) 12%, transparent); - color: var(--green); - border-color: color-mix(in srgb, var(--green) 32%, transparent); -} -.connector-status.status-error, -.connector-status-pill.status-error { - background: color-mix(in srgb, var(--red) 10%, transparent); - color: var(--red); - border-color: color-mix(in srgb, var(--red) 30%, transparent); -} -.connector-status.status-disabled, -.connector-status-pill.status-disabled { - opacity: 0.7; -} -.connector-status.status-pending, -.connector-status-pill.status-pending { - background: color-mix(in srgb, var(--accent) 12%, transparent); - color: var(--accent); - border-color: color-mix(in srgb, var(--accent) 32%, transparent); -} -.connector-status-pill { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 3px 10px; - border-radius: var(--radius-pill); - background: var(--bg-subtle); - border: 1px solid var(--border); - color: var(--text-muted); - font-size: 12px; - font-weight: 600; -} -.connector-description { - margin: 0; - color: var(--text-muted); - font-size: 13px; - line-height: 1.5; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; - overflow: hidden; -} -.connector-actions { - display: flex; - align-items: center; - gap: 8px; - margin-top: auto; - padding-top: 4px; -} -button.connector-action { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - padding: 5px 12px; - font-size: 12px; - font-weight: 500; - border-radius: var(--radius); - transition: transform 120ms ease, background 140ms ease, border-color 140ms ease; -} -button.connector-action.is-connect { - min-width: 92px; -} -button.connector-action.is-disconnect { - min-width: 106px; - color: var(--text-muted); -} -button.connector-action.is-disconnect:hover:not(:disabled) { - color: var(--red); - border-color: color-mix(in srgb, var(--red) 35%, var(--border)); -} -button.connector-action.is-cancel-authorization { - min-width: 74px; - color: var(--text-muted); -} -button.connector-action.is-loading { - cursor: wait; -} -.connector-authorization-hint { - margin: -2px 0 0; - color: var(--text-muted); - font-size: 12px; - line-height: 1.4; -} -.connector-panel-alerts { - display: grid; - gap: 6px; -} -.connector-panel-alert { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: center; - gap: 8px; - margin: 0; - padding: 7px 9px; - border: 1px solid color-mix(in srgb, var(--red) 28%, var(--border)); - border-radius: var(--radius); - background: color-mix(in srgb, var(--red) 8%, var(--bg-panel)); - color: var(--text-muted); - font-size: 12px; - line-height: 1.35; -} -.connector-panel-alert-copy { - display: grid; - grid-template-columns: minmax(0, max-content) minmax(0, 1fr); - align-items: center; - gap: 8px; - min-width: 0; - margin: 0; -} -.connector-panel-alert-copy strong { - min-width: 0; - max-width: min(160px, 34vw); - overflow: hidden; - color: var(--red); - font-weight: 600; - text-overflow: ellipsis; - white-space: nowrap; -} -.connector-panel-alert-copy span { - display: -webkit-box; - max-height: calc(12px * 1.35 * 2); - min-width: 0; - overflow: hidden; - overflow-wrap: anywhere; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - line-clamp: 2; -} -.connector-panel-alert-action { - width: 24px; - height: 24px; - padding: 0; - border-radius: 999px; - color: var(--red); - border-color: color-mix(in srgb, var(--red) 22%, var(--border)); - background: color-mix(in srgb, var(--red) 7%, transparent); -} -.connector-panel-alert-action:hover:not(:disabled) { - border-color: color-mix(in srgb, var(--red) 42%, var(--border)); - background: color-mix(in srgb, var(--red) 12%, transparent); -} - -.connector-authorization-block { - display: grid; - gap: 8px; - margin-top: 8px; -} - -.connector-authorization-link { - display: inline-flex; - align-items: center; - width: fit-content; - min-height: 26px; - padding: 4px 9px; - border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border)); - border-radius: 999px; - color: var(--accent); - background: color-mix(in srgb, var(--accent) 8%, transparent); - appearance: none; - cursor: pointer; - font: inherit; - font-size: 12px; - font-weight: 600; - text-decoration: none; -} - -.connector-card > .connector-authorization-link { - margin-top: -2px; -} - -/* Connector gate — masks the grid when the Composio API key is missing. */ -.connector-grid-wrap { - position: relative; -} -.connector-grid-wrap.is-masked .connector-grid { - filter: blur(2px) saturate(0.85); - opacity: 0.55; - pointer-events: none; - user-select: none; - transition: filter 160ms ease, opacity 160ms ease; -} -/* When the gate is shown, anchor it to the visible first screen of the - connectors tab instead of centering across the full (potentially long) - connector list. We lift the tab-content into a flex column, let the - connectors panel and the masked grid wrap stretch to fill the remaining - viewport height, and hide overflow so the absolutely-positioned gate - card remains fixed in the first-screen center. */ -.entry-tab-content:has(> .tab-panel.connectors-panel > .connector-grid-wrap.is-masked) { - display: flex; - flex-direction: column; - overflow: hidden; -} -.tab-panel.connectors-panel:has(> .connector-grid-wrap.is-masked) { - flex: 1 1 auto; - min-height: 0; -} -.connector-grid-wrap.is-masked { - flex: 1 1 auto; - min-height: 0; - overflow: hidden; -} -.connector-grid-wrap.is-masked .connector-grid { - /* Prevent the blurred grid from introducing its own scroll height inside - the now-clipped wrap — the wrap is our visible viewport for the gate. */ - max-height: 100%; - overflow: hidden; -} -.connector-card.is-locked { - cursor: not-allowed; -} - -/* Embedded inside Settings → Connectors. The section-head already shows the - "Connectors" heading + hint, so suppress the inner panel heading and let - the toolbar collapse to just the search input. The masked gate keeps its - absolute positioning relative to .connector-grid-wrap (already set), but - we cap its height inside the modal so the gate stays visible without - blowing the dialog out vertically. */ -.tab-panel.connectors-panel.connectors-panel-embedded { - gap: 14px; - position: relative; -} -/* Toast anchor: positions the connector error toast at the center-top - of the settings panel rather than at the bottom of the viewport. */ -.connectors-toast-anchor { - position: absolute; - top: 20px; - left: 50%; - transform: translateX(-50%); - z-index: 10; - pointer-events: none; - width: max-content; -} -.connectors-toast-anchor .od-toast { - position: static; - transform: none; - pointer-events: auto; -} -.connectors-panel-embedded .tab-panel-toolbar { - justify-content: flex-end; - margin-top: -4px; -} -.connectors-panel-embedded .toolbar-left.connectors-heading { - display: none; -} -.connectors-panel-embedded .toolbar-right { - flex: 1 1 auto; - justify-content: space-between; - gap: 10px; - align-items: center; - flex-wrap: nowrap; -} -.connectors-panel-embedded .tab-panel-toolbar .toolbar-search.connectors-search { - flex: 0 1 320px; - width: min(320px, 100%); - max-width: 320px; - min-width: 180px; -} - -/* Provider tabs sit in the toolbar's left edge (right of the hidden inner - heading). Today there is only Composio, but the segmented control is - built so additional providers slot in without re-styling. */ -.connectors-provider-tabs { - display: inline-flex; - align-items: center; - gap: 2px; - padding: 3px; - border-radius: 999px; - background: var(--bg-subtle); - border: 1px solid var(--border); - flex: 0 0 auto; -} -/* Hide the provider tabs when there is only one option — no choice = no need for a tab. */ -.connectors-provider-tabs:has(> :only-child) { - display: none; -} -.connectors-provider-tab { - appearance: none; - border: 0; - background: transparent; - color: var(--text-muted); - font: inherit; - font-size: 12px; - font-weight: 500; - letter-spacing: 0.005em; - padding: 5px 12px; - border-radius: 999px; - cursor: pointer; - white-space: nowrap; - transition: - background 140ms ease, - color 140ms ease, - box-shadow 180ms ease; -} -.connectors-provider-tab:hover:not(.is-active) { - color: var(--text); - background: color-mix(in srgb, var(--text) 6%, transparent); -} -.connectors-provider-tab.is-active { - color: var(--text); - background: var(--bg-panel); - box-shadow: - 0 1px 2px rgba(0, 0, 0, 0.06), - 0 0 0 1px color-mix(in srgb, var(--text) 12%, var(--border)); -} -.connectors-provider-tab:focus-visible { - outline: none; - box-shadow: - 0 0 0 2px color-mix(in srgb, var(--text) 14%, transparent), - 0 1px 2px rgba(0, 0, 0, 0.06); -} -.connectors-panel-embedded .connector-grid-wrap.is-masked { - /* Cap the masked grid's height so the gate's centered card stays in the - visible portion of the settings dialog without forcing the modal body - to scroll past it. The grid below is blurred and intentionally clipped. */ - min-height: clamp(320px, 45vh, 420px); - max-height: clamp(360px, 56vh, 560px); - overflow: hidden; - border-radius: var(--radius); -} -.connectors-panel-embedded .connector-grid-wrap.is-masked .connector-grid { - max-height: 100%; - overflow: hidden; -} -/* Compact catalog density inside the modal: tighter tracks, no description - row, action collapsed to an icon-only button anchored top-right. */ -.connectors-panel-embedded .connector-grid { - grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); - gap: 10px; -} -.connectors-panel-embedded .connector-card { - min-height: 0; - padding: 10px 10px 10px 12px; - gap: 6px; -} -.connectors-panel-embedded .connector-card-title { - font-size: 13px; - font-weight: 600; - letter-spacing: 0; -} -/* Two-row meta layout for the embedded catalog. The previous single - row let long category labels wrap unpredictably, leaving cards in - a 3-column grid with mismatched heights. Stacking onto its own - rows fixes the height and gives the async tools-badge a stable - anchor to animate into without resizing the card. */ -.connectors-panel-embedded .connector-meta { - flex-direction: column; - align-items: flex-start; - flex-wrap: nowrap; - font-size: 11px; - gap: 4px; - width: 100%; - min-width: 0; -} -/* Category row: single line with ellipsis. The full label is still - reachable via the `title` attribute on the span and the card's - own openDetailsAria, so we never lose information. */ -.connectors-panel-embedded .connector-meta-category { - display: block; - width: 100%; - min-width: 0; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - line-height: 1.3; -} -/* Tools-badge slot. Reserves its own row and a fixed height even - while the discovery call is in flight (`aria-hidden` is set on the - span before the badge resolves), so the card doesn't grow when - the badge animates in. The fixed height matches the embedded - badge's pill (1px + 1px borders + 10px text + 2px padding × 2 ≈ - 18px). */ -.connectors-panel-embedded .connector-meta-tools { - display: inline-flex; - align-items: center; - min-height: 18px; - /* Hairline visual placeholder while the discovery request is in - flight — a 1px-tall faint baseline so the row reads as - intentionally reserved space rather than an empty gap. The - placeholder is dropped the moment the badge appears. */ - position: relative; -} -.connectors-panel-embedded .connector-meta-tools[aria-hidden="true"]::after { - content: ''; - display: block; - width: 32px; - height: 1px; - background: color-mix(in srgb, var(--text) 10%, transparent); - border-radius: 999px; -} -/* The dot separator was only meaningful when the meta was a single - inline row; in the stacked layout it would float orphaned at the - start of the badge row. Hide it (the embedded card no longer - renders it in JSX, but keep the rule defensive in case a future - refactor reintroduces inline separators here). */ -.connectors-panel-embedded .connector-meta .connector-meta-dot { - display: none; -} -.connectors-panel-embedded .connector-tools-badge { - padding: 1px 6px; - font-size: 10px; - border-radius: 999px; -} -/* Anchor the action column to the top now that the meta block can be - one or two rows tall — center alignment used to make the action - drift down whenever the badge appeared. Keeping the action top- - aligned matches the title baseline and stops the eye from - tracking up and down across cards. */ -.connectors-panel-embedded .connector-card-top { - align-items: flex-start; - gap: 8px; -} -.connectors-panel-embedded .connector-card-actions { - /* Nudge the action button down a touch so it optically aligns with - the title's cap-height instead of its top edge. */ - margin-top: 1px; -} - -/* Icon-only connect/disconnect action: a 26px circular control anchored - at the card's top-right edge. We keep the same `connector-action` - class so loading/disabled state styling carries over from the - shared rules above, but the visual treatment is overridden here so - the catalog grid doesn't end up with a row of high-contrast filled - squares competing for attention. The default state is a subtle - ghost — almost recedes into the card — and the action picks up - accent weight only when the card or the button itself is hovered - or focused. The whole card is also clickable (it opens the - details drawer where Connect lives at full size), so this is a - secondary affordance, not the primary CTA. */ -.connectors-panel-embedded .connector-card-actions { - display: inline-flex; - align-items: center; - gap: 6px; - flex: 0 0 auto; -} -.connectors-panel-embedded button.connector-action.icon-only { - display: inline-flex; - align-items: center; - justify-content: center; - width: 26px; - height: 26px; - /* Reset the min-width from the non-embedded `.is-connect` / - `.is-disconnect` pill rules above; in the compact catalog the - action collapses to a 26px circle and any `min-width` would - force it back into a wide pill that overlaps the card head - text. */ - min-width: 0; - padding: 0; - border-radius: 999px; - border: 1px solid transparent; - background: transparent; - color: var(--text-muted); - cursor: pointer; - transition: - background 160ms ease, - border-color 160ms ease, - color 160ms ease, - transform 120ms ease, - box-shadow 160ms ease, - opacity 160ms ease; -} -/* Soft default fill so the ghost button still has a visible target on - the lightest card backgrounds. We tint with `--text` rather than a - solid color so the control reads correctly in both dark and light - themes without per-theme overrides. */ -.connectors-panel-embedded button.connector-action.icon-only { - background: color-mix(in srgb, var(--text) 5%, transparent); - border-color: color-mix(in srgb, var(--text) 10%, transparent); -} -/* When the parent card is hovered, the action gains a touch more weight - so it telegraphs interactivity without ever flashing to full white. - This applies whether the user is hovering the card or the button - directly, so reaching for the action never feels like the button - moves out from under them. */ -.connectors-panel-embedded .connector-card:hover:not(.is-locked) button.connector-action.icon-only:not(:disabled), -.connectors-panel-embedded .connector-card:focus-visible button.connector-action.icon-only:not(:disabled) { - color: var(--text); - background: color-mix(in srgb, var(--text) 9%, transparent); - border-color: color-mix(in srgb, var(--text) 18%, transparent); -} -.connectors-panel-embedded button.connector-action.icon-only:hover:not(:disabled) { - color: var(--text); - background: color-mix(in srgb, var(--text) 12%, transparent); - border-color: color-mix(in srgb, var(--text) 24%, transparent); -} -.connectors-panel-embedded button.connector-action.icon-only:active:not(:disabled) { - transform: scale(0.92); -} -.connectors-panel-embedded button.connector-action.icon-only:focus-visible { - outline: none; - border-color: color-mix(in srgb, var(--accent) 70%, transparent); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); -} -.connectors-panel-embedded button.connector-action.icon-only:disabled { - opacity: 0.4; - cursor: not-allowed; -} -@media (prefers-reduced-motion: reduce) { - .connectors-panel-embedded button.connector-action.icon-only { - transition: background 80ms linear, color 80ms linear; - } - .connectors-panel-embedded button.connector-action.icon-only:active:not(:disabled) { - transform: none; - } -} - -/* Connect (the "+" affordance). Refined into an accent-tinted ghost so - the action reads as inviting rather than competing — the previous - "fill with var(--text)" rule produced a hard off-white square in - dark theme that fought every other card. Default state borrows a - whisper of the accent so the plus is visibly the accent action of - the card; hover/card-hover both lift the tint up while keeping the - button transparent enough to feel like part of the card surface, - not a stamped-on chip. */ -.connectors-panel-embedded button.connector-action.is-connect { - color: color-mix(in srgb, var(--accent) 76%, var(--text-muted)); - background: color-mix(in srgb, var(--accent) 10%, transparent); - border-color: color-mix(in srgb, var(--accent) 22%, transparent); -} -.connectors-panel-embedded .connector-card:hover:not(.is-locked) button.connector-action.is-connect:not(:disabled), -.connectors-panel-embedded .connector-card:focus-visible button.connector-action.is-connect:not(:disabled) { - color: var(--accent-strong, var(--accent)); - background: color-mix(in srgb, var(--accent) 16%, transparent); - border-color: color-mix(in srgb, var(--accent) 38%, transparent); -} -.connectors-panel-embedded button.connector-action.is-connect:hover:not(:disabled) { - color: var(--accent-strong, var(--accent)); - background: color-mix(in srgb, var(--accent) 26%, transparent); - border-color: color-mix(in srgb, var(--accent) 56%, transparent); - box-shadow: 0 4px 14px -8px color-mix(in srgb, var(--accent) 60%, transparent); -} -.connectors-panel-embedded button.connector-action.is-connect:active:not(:disabled) { - background: color-mix(in srgb, var(--accent) 32%, transparent); -} - -/* Disconnect stays neutral until the user actually points at it, then - warms to the destructive red so the "remove" intent is unambiguous. - Slightly de-emphasized at rest compared to Connect — the connected - row already carries a green status dot to communicate state, so - this control doesn't need to advertise itself. */ -.connectors-panel-embedded button.connector-action.is-disconnect { - color: var(--text-muted); - background: color-mix(in srgb, var(--text) 4%, transparent); - border-color: color-mix(in srgb, var(--text) 10%, transparent); -} -.connectors-panel-embedded .connector-card:hover:not(.is-locked) button.connector-action.is-disconnect:not(:disabled), -.connectors-panel-embedded .connector-card:focus-visible button.connector-action.is-disconnect:not(:disabled) { - color: var(--text); - background: color-mix(in srgb, var(--text) 9%, transparent); - border-color: color-mix(in srgb, var(--text) 18%, transparent); -} -.connectors-panel-embedded button.connector-action.is-disconnect:hover:not(:disabled) { - color: var(--red, var(--text)); - background: color-mix(in srgb, var(--red, var(--text)) 14%, transparent); - border-color: color-mix(in srgb, var(--red, var(--text)) 42%, transparent); -} - -/* Connection-status pip. Lives inline next to the connector name in - the embedded catalog (anchored via `.connector-card-title-dot`), - and the same dot is reused in the drawer where the rules above - handle the larger non-embedded variant. The halo is a `box-shadow` - ring rather than a `border` so the dot's optical size stays at - 7px even with the green pulse around it. */ -.connectors-panel-embedded .connector-status-dot { - display: inline-block; - width: 7px; - height: 7px; - border-radius: 999px; - background: var(--text-muted); - flex: 0 0 auto; -} -.connectors-panel-embedded .connector-status-dot.status-connected { - background: var(--green, #22c55e); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--green, #22c55e) 22%, transparent); -} -.connectors-panel-embedded .connector-status-dot.status-error { - background: var(--red, #ef4444); -} -.connectors-panel-embedded .connector-status-dot.status-disabled { - background: var(--text-faint); -} -/* Error/disabled pills inside the compact card stay legible but small. */ -.connectors-panel-embedded .connector-card .connector-status-pill { - font-size: 10px; - padding: 1px 6px; -} - -/* On very narrow modals, keep tabs and search on one row while letting the - search input absorb the squeeze. */ -@media (max-width: 540px) { - .connectors-provider-tabs { - flex-shrink: 0; - } - .connectors-panel-embedded .tab-panel-toolbar .toolbar-search.connectors-search { - min-width: 0; - } -} -.connector-gate { - position: absolute; - inset: 0; - z-index: 2; - display: flex; - align-items: center; - justify-content: center; - padding: 24px; - background: - radial-gradient(ellipse at top, color-mix(in srgb, var(--bg-panel) 78%, transparent) 0%, color-mix(in srgb, var(--bg) 72%, transparent) 65%), - color-mix(in srgb, var(--bg) 45%, transparent); - backdrop-filter: blur(6px); - -webkit-backdrop-filter: blur(6px); - border-radius: var(--radius); - animation: connector-gate-fade 220ms ease-out; - pointer-events: auto; -} -@keyframes connector-gate-fade { - from { opacity: 0; transform: translateY(4px); } - to { opacity: 1; transform: translateY(0); } -} -.connector-gate-card { - display: flex; - flex-direction: column; - align-items: center; - gap: 12px; - max-width: 420px; - padding: 28px 28px 24px; - text-align: center; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-md, 0 10px 30px rgba(0, 0, 0, 0.18)); -} -.connector-gate-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - border-radius: 50%; - background: var(--bg-subtle); - border: 1px solid var(--border); - color: var(--text-muted); -} -.connector-gate-title { - margin: 2px 0 0; - font-size: 15px; - font-weight: 600; - color: var(--text); - letter-spacing: 0.01em; -} -.connector-gate-body { - margin: 0; - max-width: 340px; - color: var(--text-muted); - font-size: 13px; - line-height: 1.5; -} -/* ------------------------------------------------------------------ */ -/* Connector detail drawer */ -/* ------------------------------------------------------------------ */ -.connector-drawer-backdrop { - position: fixed; - inset: 0; - z-index: 100; - background: color-mix(in srgb, #111827 18%, transparent); - backdrop-filter: blur(1px); - -webkit-backdrop-filter: blur(1px); - display: flex; - justify-content: flex-end; - align-items: flex-end; - padding: 24px 24px 12px; - animation: connector-drawer-fade 180ms cubic-bezier(0.23, 1, 0.32, 1); -} -@keyframes connector-drawer-fade { - from { opacity: 0; } - to { opacity: 1; } -} -.connector-drawer { - position: relative; - display: flex; - flex-direction: column; - width: min(456px, 92vw); - height: clamp(480px, 68vh, 640px); - max-height: calc(100vh - 36px); - background: var(--bg-panel); - border: 1px solid color-mix(in srgb, var(--border) 82%, transparent); - border-radius: 12px; - box-shadow: 0 24px 64px -34px rgba(15, 23, 42, 0.5); - overflow: hidden; - animation: connector-drawer-slide 220ms cubic-bezier(0.23, 1, 0.32, 1); -} -@keyframes connector-drawer-slide { - from { transform: translateX(18px); opacity: 0; } - to { transform: translateX(0); opacity: 1; } -} -.connector-drawer-head { - display: flex; - align-items: flex-start; - gap: 14px; - padding: 22px 22px 18px; - border-bottom: 1px solid color-mix(in srgb, var(--border) 70%, transparent); - background: color-mix(in srgb, var(--bg-subtle) 36%, var(--bg-panel)); -} -.connector-drawer-titles { - flex: 1 1 auto; - min-width: 0; - display: flex; - flex-direction: column; - gap: 8px; -} -.connector-drawer-eyebrow { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 6px; - font-size: 11px; - font-weight: 500; - letter-spacing: 0.02em; - text-transform: uppercase; - color: var(--text-faint); -} -.connector-drawer-titles h2 { - margin: 0; - font-size: 21px; - font-weight: 600; - line-height: 1.2; - letter-spacing: 0; - color: var(--text); -} -.connector-drawer-status { - display: flex; - align-items: center; - gap: 7px; - flex-wrap: wrap; -} -.connector-drawer-tool-count-chip { - display: inline-flex; - align-items: center; - min-height: 24px; - max-width: 100%; - padding: 3px 9px; - border-radius: var(--radius-pill); - border: 1px solid color-mix(in srgb, var(--border) 75%, transparent); - background: color-mix(in srgb, var(--bg-panel) 72%, var(--bg-subtle)); - color: var(--text-muted); - font-size: 11.5px; - font-weight: 600; - line-height: 1.2; -} -.connector-drawer-close { - flex: 0 0 auto; - display: inline-flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - padding: 0; - border-radius: 999px; - color: var(--text-muted); -} -.connector-drawer-close:hover { - background: var(--bg-subtle); - color: var(--text); -} -.connector-drawer-body { - flex: 1 1 auto; - min-height: 0; - overflow-y: auto; - padding: 18px 22px 14px; - display: flex; - flex-direction: column; - gap: 20px; -} -.connector-drawer-section { - display: flex; - flex-direction: column; - gap: 10px; -} -.connector-drawer-section-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - min-width: 0; -} -.connector-drawer-section-title { - margin: 0; - font-size: 11px; - font-weight: 600; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--text-faint); - display: flex; - align-items: center; - gap: 8px; -} -.connector-drawer-inline-action { - display: inline-flex; - align-items: center; - gap: 6px; - min-width: 0; - min-height: 28px; - padding: 4px 9px; - border-radius: var(--radius); - font-size: 12px; - font-weight: 500; - white-space: nowrap; -} -.connector-drawer-count { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 20px; - height: 18px; - padding: 0 6px; - border-radius: 999px; - background: var(--bg-subtle); - border: 1px solid var(--border); - color: var(--text-muted); - font-size: 10px; - font-weight: 600; - letter-spacing: 0; - text-transform: none; -} -.connector-drawer-description { - margin: 0; - color: var(--text); - font-size: 14px; - line-height: 1.55; -} -.connector-drawer-details { - display: grid; - grid-template-columns: minmax(0, 1fr); - gap: 0; - margin: 0; - padding: 2px 0 0; - border-top: 1px solid color-mix(in srgb, var(--border) 55%, transparent); -} -.connector-drawer-details > div { - display: grid; - grid-template-columns: minmax(96px, 0.38fr) minmax(0, 1fr); - align-items: baseline; - gap: 12px; - padding: 8px 0; -} -.connector-drawer-details > div + div { - border-top: 1px solid color-mix(in srgb, var(--border) 42%, transparent); -} -.connector-drawer-details dt { - margin: 0; - color: var(--text-muted); - font-size: 12.5px; - font-weight: 500; -} -.connector-drawer-details dd { - margin: 0; - color: var(--text); - font-size: 13px; - word-break: break-word; -} -.connector-drawer-details-error dd { - color: var(--red); -} -.connector-drawer-empty { - margin: 0; - padding: 12px 14px; - border: 1px dashed color-mix(in srgb, var(--border) 78%, transparent); - border-radius: var(--radius); - color: var(--text-muted); - font-size: 13px; - line-height: 1.5; -} -.connector-drawer-tools { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - gap: 6px; -} -.connector-drawer-tool { - display: flex; - flex-direction: column; - gap: 5px; - padding: 9px 11px; - background: color-mix(in srgb, var(--bg-panel) 74%, var(--bg-subtle)); - border: 1px solid color-mix(in srgb, var(--border) 68%, transparent); - border-radius: var(--radius); - transition: border-color 140ms ease, background 140ms ease; -} -.connector-drawer-tool:hover { - background: var(--bg-panel); - border-color: color-mix(in srgb, var(--text) 16%, var(--border)); -} -.connector-drawer-tool-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; -} -.connector-drawer-tool-title { - min-width: 0; - font-size: 12.5px; - font-weight: 600; - color: var(--text); - line-height: 1.35; -} -.connector-drawer-tool-badge { - display: inline-flex; - align-items: center; - flex: 0 0 auto; - padding: 1px 6px; - border-radius: var(--radius-pill); - font-size: 9.5px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - background: var(--bg-subtle); - border: 1px solid var(--border); - color: var(--text-muted); -} -.connector-drawer-tool-badge.side-read { - background: color-mix(in srgb, var(--green) 10%, transparent); - border-color: color-mix(in srgb, var(--green) 28%, transparent); - color: var(--green); -} -.connector-drawer-tool-badge.side-write { - background: color-mix(in srgb, var(--amber, #d97706) 10%, transparent); - border-color: color-mix(in srgb, var(--amber, #d97706) 28%, transparent); - color: var(--amber, #d97706); -} -.connector-drawer-tool-badge.side-destructive { - background: color-mix(in srgb, var(--red) 10%, transparent); - border-color: color-mix(in srgb, var(--red) 30%, transparent); - color: var(--red); -} -.connector-drawer-tool-desc { - margin: 0; - color: var(--text-muted); - font-size: 12px; - line-height: 1.42; -} -.connector-drawer-tool-name { - font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); - font-size: 10.5px; - color: var(--text-faint); - background: color-mix(in srgb, var(--bg-subtle) 76%, transparent); - padding: 2px 5px; - border-radius: 4px; - align-self: flex-start; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.connector-drawer-load-more { - align-self: flex-start; - margin-top: 10px; - display: inline-flex; - align-items: center; - gap: 6px; - min-height: 34px; - padding: 7px 14px; -} -.connector-drawer-foot { - padding: 12px 22px 16px; - border-top: 1px solid color-mix(in srgb, var(--border) 70%, transparent); - display: flex; - justify-content: flex-end; - gap: 8px; - background: color-mix(in srgb, var(--bg-subtle) 28%, var(--bg-panel)); -} -.connector-drawer-foot button.connector-action { - padding: 7px 18px; - font-size: 13px; -} -@media (max-width: 520px) { - .connector-drawer-backdrop { - align-items: stretch; - padding: 0; - } - .connector-drawer { - width: 100%; - max-height: none; - height: 100%; - border-radius: 0; - border-right: 0; - border-left: 0; - } - .connector-drawer-head { - padding: 16px 16px 14px; - } - .connector-drawer-body { - padding: 16px 16px 20px; - } - .connector-drawer-foot { - padding: 12px 16px 16px; - } - .connector-drawer-section-head { - align-items: flex-start; - flex-direction: column; - gap: 8px; - } - .connector-drawer-details > div { - grid-template-columns: 1fr; - gap: 2px; - } -} - -/* Recent / Your designs segmented pill */ -.subtab-pill { - display: inline-flex; - padding: 3px; - background: var(--bg-subtle); - border: 1px solid var(--border); - border-radius: var(--radius); - gap: 2px; - max-width: 100%; - overflow-x: auto; - scrollbar-width: none; -} -.subtab-pill::-webkit-scrollbar { display: none; } -.subtab-pill button { - background: transparent; - border: none; - border-radius: var(--radius-sm); - padding: 5px 16px; - font-size: 12px; - color: var(--text-muted); - font-weight: 500; - white-space: nowrap; -} -.subtab-pill button:hover:not(.active) { background: var(--bg-muted); border-color: transparent; color: var(--text); } -.subtab-pill button.active { - background: var(--bg-panel); - color: var(--text); - box-shadow: var(--shadow-xs); -} -/* Icon-only variant: any pill button whose sole child is an SVG icon. - Center the glyph via inline-flex (removes text line-height drift) and - use padding that matches the text variant's overall height so both - sub-pills align on the same baseline in the toolbar. */ -.subtab-pill button:has(> svg:only-child) { - padding: 5px 8px; - display: inline-flex; - align-items: center; - justify-content: center; - line-height: 0; -} - -/* Designs grid */ -.design-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); - gap: 14px; -} -.design-card { - position: relative; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 0; - cursor: pointer; - transition: border-color 120ms ease, box-shadow 120ms ease, transform 120ms ease; - display: flex; - flex-direction: column; - overflow: hidden; - min-height: 168px; -} -.design-card.is-design-system-project { - border-color: color-mix(in srgb, var(--red) 62%, var(--border)); - box-shadow: 0 0 0 1px color-mix(in srgb, var(--red) 42%, transparent); -} -.design-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); transform: translateY(-1px); } -.design-card.is-design-system-project:hover { - border-color: var(--red); - box-shadow: - 0 0 0 1px color-mix(in srgb, var(--red) 54%, transparent), - var(--shadow-sm); -} -.design-card-thumb { - flex: 1; - min-height: 100px; - display: flex; - align-items: center; - justify-content: center; - background: var(--bg-subtle); - color: var(--text-faint); - font-size: 38px; - position: relative; -} -.design-card-thumb::before { - /* Folder-shape icon */ - content: ''; - width: 56px; - height: 44px; - background: var(--bg-panel); - border: 1px solid var(--border-strong); - border-radius: 4px 6px 6px 6px; - position: relative; - box-shadow: var(--shadow-xs); -} -.design-card-thumb::after { - /* Folder tab notch */ - content: ''; - position: absolute; - width: 22px; - height: 8px; - background: var(--bg-panel); - border: 1px solid var(--border-strong); - border-bottom: none; - border-radius: 4px 4px 0 0; - margin-top: -52px; - margin-left: -26px; -} -.design-card-meta-block { - padding: 10px 12px 12px; - display: flex; - flex-direction: column; - gap: 2px; - border-top: 1px solid var(--border-soft); - background: var(--bg-panel); -} -.design-card-name { - font-weight: 600; - font-size: 13px; - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - color: var(--text-strong); -} -.design-card-meta { - display: flex; - align-items: baseline; - gap: 6px; - font-size: 11.5px; - color: var(--text-muted); - min-width: 0; - white-space: nowrap; - overflow: hidden; -} -.design-card-meta-main { - min-width: 0; - flex: 1 1 auto; - overflow: hidden; - text-overflow: ellipsis; -} -.design-card-meta-time { - flex: 0 0 auto; - color: var(--text-muted); -} -.design-card-meta .ds { - color: var(--accent); -} -.design-card-status { - font-weight: 500; -} -.design-card-status-running { - color: var(--accent); -} -.design-card-status-awaiting_input { - color: var(--amber); -} -.design-card-status-queued, -.design-card-status-not_started, -.design-card-status-canceled { - color: var(--text-muted); -} -.design-card-status-succeeded { - color: var(--green); -} -.design-card-status-failed { - color: var(--red); -} -.design-card-status-published { - color: var(--green); -} -.design-card-close { - position: absolute; - top: 8px; - right: 8px; - width: 22px; - height: 22px; - padding: 0; - border-radius: 50%; - font-size: 14px; - line-height: 1; - background: var(--bg-panel); - opacity: 0; - transition: opacity 0.15s; - border-color: var(--border); - z-index: 2; - /* Keep button interactive for keyboard/AT users even while visually hidden. */ - display: inline-flex; - align-items: center; - justify-content: center; - color: var(--text-muted); -} -.design-card:hover .design-card-close, -.design-card:focus-within .design-card-close, -.design-kanban-card:hover .design-card-close, -.design-kanban-card:focus-within .design-card-close, -.design-card-close:focus-visible { opacity: 1; } -.design-card-close:hover { color: var(--text-strong); border-color: var(--border-strong); } - -/* Overflow menu trigger and dropdown on project cards */ -.design-card-menu-anchor { - position: absolute; - top: 8px; - right: 8px; - z-index: 3; -} -.design-card-more { - width: 26px; - height: 26px; - padding: 0; - border-radius: 6px; - background: var(--bg-panel); - border: 1px solid var(--border); - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - opacity: 0; - transition: opacity 0.15s, color 0.15s, border-color 0.15s; -} -.design-card:hover .design-card-more, -.design-card:focus-within .design-card-more, -.design-card-more[aria-expanded="true"], -.design-card-more:focus-visible { opacity: 1; } -.design-card-more:hover { color: var(--text-strong); border-color: var(--border-strong); } -@media (hover: none) { - .design-card .design-card-more { opacity: 1; } -} -.design-card-menu { - position: absolute; - top: calc(100% + 6px); - right: 0; - min-width: 144px; - background: var(--bg-panel); - border: 1px solid var(--border-strong); - border-radius: 8px; - box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12); - padding: 4px; - display: flex; - flex-direction: column; - z-index: 50; -} -.design-card-menu button { - display: flex; - align-items: center; - gap: 8px; - padding: 7px 10px; - border: 0; - background: transparent; - border-radius: 6px; - color: var(--text-strong); - font-size: 12.5px; - text-align: left; - cursor: pointer; -} -.design-card-menu button:hover { background: var(--bg-subtle); } -.design-card-menu button.danger { color: var(--red); } -.design-card-menu button.danger:hover { background: color-mix(in srgb, var(--red) 12%, transparent); } - -/* Multi-select checkbox + selected state */ -.design-card-checkbox { - position: absolute; - top: 8px; - left: 8px; - width: 22px; - height: 22px; - border-radius: 6px; - background: var(--bg-panel); - border: 1px solid var(--border-strong); - display: inline-flex; - align-items: center; - justify-content: center; - color: var(--bg-panel); - z-index: 3; -} -.design-card-checkbox.checked { - background: var(--accent); - border-color: var(--accent); - color: #fff; -} -.design-card.is-selected { - border-color: var(--accent); - box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 32%, transparent); -} -.design-card.select-mode { cursor: pointer; } - -/* Select-mode toolbar controls */ -.designs-select-toggle { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 6px 10px; - border: 1px solid var(--border); - border-radius: 999px; - background: var(--bg-panel); - color: var(--text-strong); - font-size: 12.5px; - cursor: pointer; -} -.designs-select-toggle:hover { border-color: var(--border-strong); } -.designs-select-bar { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 4px 6px 4px 12px; - border: 1px solid var(--border-strong); - border-radius: 999px; - background: var(--bg-panel); -} -.designs-select-count { - font-size: 12.5px; - color: var(--text-strong); -} -.designs-select-delete { - padding: 5px 10px; - border-radius: 999px; - border: 0; - background: var(--red); - color: #fff; - font-size: 12px; - cursor: pointer; -} -.designs-select-delete:disabled { opacity: 0.5; cursor: not-allowed; } -.designs-select-cancel { - padding: 5px 10px; - border-radius: 999px; - border: 1px solid var(--border); - background: transparent; - color: var(--text-muted); - font-size: 12px; - cursor: pointer; -} -.designs-select-cancel:hover { color: var(--text-strong); border-color: var(--border-strong); } -/* Larger comfortable touch target on coarse pointers (tablets/touch laptops). - On touch the hover reveal never fires, so keep the close button visible. */ -@media (hover: none) { - .design-card .design-card-close, - .design-kanban-card .design-card-close { opacity: 1; } -} - -/* Featured (tutorial) card variant */ -.design-card.featured .design-card-thumb { - background: linear-gradient(180deg, #e8efff 0%, #d8e3ff 100%); -} -.design-card.featured .design-card-thumb::before { - background: var(--bg-panel); - border-color: rgba(35, 72, 184, 0.18); -} -.design-card.featured .design-card-thumb::after { display: none; } - -.live-artifact-card { - border-color: color-mix(in srgb, var(--accent) 28%, var(--border)); -} -.live-artifact-card:hover { - border-color: color-mix(in srgb, var(--accent) 48%, var(--border-strong)); -} -.project-thumb::before, -.project-thumb::after { - display: none; -} -.design-card-thumb .thumb-media { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - object-fit: cover; - display: block; -} -.design-card-thumb .thumb-iframe { - position: absolute; - top: 0; - left: 0; - width: 250%; - height: 250%; - transform: scale(0.4); - transform-origin: top left; - border: 0; - background: var(--bg-panel); - pointer-events: none; -} -.project-thumb-image, -.project-thumb-video, -.project-thumb-html, -.project-thumb-logo { - background: var(--bg-subtle); -} -.project-thumb-logo .thumb-media { - inset: 22%; - width: 56%; - height: 56%; - object-fit: contain; -} -.project-thumb-image .project-thumb-glyph, -.project-thumb-video .project-thumb-glyph, -.project-thumb-html .project-thumb-glyph, -.project-thumb-logo .project-thumb-glyph { - display: none; -} -.project-thumb-glyph { - width: 46px; - height: 46px; - border-radius: 14px; - display: grid; - place-items: center; - color: var(--text-strong); - background: var(--bg-panel); - border: 1px solid var(--border-strong); - box-shadow: var(--shadow-xs); - font-size: 20px; - font-weight: 600; - line-height: 1; - text-transform: uppercase; -} -.live-artifact-thumb { - background: - radial-gradient(circle at 32% 28%, rgba(116, 92, 255, 0.18), transparent 36%), - linear-gradient(135deg, rgba(66, 153, 225, 0.16), rgba(116, 92, 255, 0.12)); -} -.live-artifact-thumb::before, -.live-artifact-thumb::after { - display: none; -} -.live-artifact-thumb-glyph { - width: 46px; - height: 46px; - border-radius: 16px; - display: grid; - place-items: center; - color: var(--accent); - background: var(--bg-panel); - border: 1px solid color-mix(in srgb, var(--accent) 24%, var(--border)); - box-shadow: var(--shadow-xs); - font-size: 18px; - line-height: 1; -} -.design-card-badges, -.live-artifact-badges { - display: flex; - flex-wrap: wrap; - gap: 5px; - min-height: 18px; -} -.live-artifact-badge, -.design-live-count { - display: inline-flex; - align-items: center; - flex: 0 0 auto; - border-radius: 999px; - border: 1px solid var(--border); - padding: 2px 7px; - font-size: 10px; - font-weight: 700; - letter-spacing: 0.03em; - text-transform: uppercase; - white-space: nowrap; - line-height: 1.2; - background: var(--bg-panel); -} -.live-artifact-badge.live { - color: var(--accent); - border-color: color-mix(in srgb, var(--accent) 32%, var(--border)); - background: color-mix(in srgb, var(--accent) 9%, var(--bg-panel)); -} -.live-artifact-badge.refreshing { - color: #1d4ed8; - border-color: rgba(29, 78, 216, 0.22); - background: rgba(29, 78, 216, 0.08); -} -.live-artifact-badge.refresh-failed { - color: #b42318; - border-color: rgba(180, 35, 24, 0.22); - background: rgba(180, 35, 24, 0.08); -} -.live-artifact-badge.archived { - color: var(--text-muted); - border-color: var(--border-strong); - background: var(--bg-subtle); -} -.live-artifact-badges.compact { - display: inline-flex; - gap: 4px; - min-height: 0; - vertical-align: middle; -} -.live-artifact-badges.compact .live-artifact-badge { - padding: 1px 5px; - font-size: 9px; - line-height: 1.25; -} -.design-live-count { - position: absolute; - right: 10px; - bottom: 10px; - color: var(--accent); - border-color: color-mix(in srgb, var(--accent) 26%, var(--border)); - box-shadow: var(--shadow-xs); -} - -/* Grid card keyboard focus (cards carry role="button" on a div). */ -.design-card:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - border-color: var(--border-strong); -} - -/* Kanban View */ -.tab-panel.design-kanban-view { - /* Fill the scrollable parent (.entry-tab-content) so columns can size to - the available viewport without a fragile 100vh calc. */ - flex: 1 1 auto; - min-height: 0; - height: 100%; -} -.design-kanban-board { - display: flex; - gap: 14px; - overflow-x: auto; - /* Let columns grow to fill the remaining vertical space inside - .tab-panel.design-kanban-view. */ - flex: 1 1 auto; - min-height: 0; - padding-bottom: 8px; - /* Hint that horizontal content may overflow on narrow viewports. */ - scroll-snap-type: x proximity; - scrollbar-gutter: stable; -} -.design-kanban-col { - width: 280px; - flex-shrink: 0; - display: flex; - flex-direction: column; - background: var(--bg-subtle); - border-radius: var(--radius); - padding: 12px; - gap: 12px; - min-height: 0; - scroll-snap-align: start; -} -.design-kanban-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - font-size: 13px; - font-weight: 500; - color: var(--text-strong); - padding-left: 2px; - /* Prevent long status labels from pushing the count chip out of the column. */ - min-width: 0; -} -.design-kanban-header > span:first-child { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - min-width: 0; -} -.design-kanban-count { - background: var(--bg-panel); - border: 1px solid var(--border-soft); - color: var(--text-muted); - font-size: 11px; - padding: 2px 8px; - border-radius: var(--radius-pill); - flex-shrink: 0; -} -.design-kanban-list { - display: flex; - flex-direction: column; - gap: 8px; - overflow-y: auto; - /* Keep the scrollbar gutter from shifting the list when content grows. */ - padding-right: 4px; - margin-right: -4px; - flex: 1 1 auto; - min-height: 0; -} -.design-kanban-empty { - color: var(--text-faint); - font-size: 13px; - text-align: center; - padding: 20px 0; -} -.design-kanban-card { - position: relative; - background: var(--bg-panel); - border: 1px solid var(--border-soft); - border-radius: var(--radius); - padding: 12px 14px; - display: flex; - flex-direction: column; - gap: 6px; - cursor: pointer; - transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease; - box-shadow: var(--shadow-xs); -} -.design-kanban-card.is-design-system-project { - border-color: color-mix(in srgb, var(--red) 62%, var(--border)); - box-shadow: - 0 0 0 1px color-mix(in srgb, var(--red) 42%, transparent), - var(--shadow-xs); -} -.design-kanban-card:hover { - border-color: var(--border-strong); - box-shadow: var(--shadow-sm); - transform: translateY(-1px); -} -.design-kanban-card.is-design-system-project:hover { - border-color: var(--red); - box-shadow: - 0 0 0 1px color-mix(in srgb, var(--red) 54%, transparent), - var(--shadow-sm); -} -.design-kanban-card:active { - transform: none; -} -.design-kanban-card:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - border-color: var(--border-strong); -} -.design-kanban-card::before { - content: ''; - position: absolute; - left: -1px; - top: -1px; - bottom: -1px; - width: 3px; - border-radius: var(--radius) 0 0 var(--radius); - background: var(--text-muted); -} -.design-kanban-card.status-running::before { background: var(--accent); } -.design-kanban-card.status-awaiting_input::before { background: var(--amber); } -.design-kanban-card.status-succeeded::before { background: var(--green); } -.design-kanban-card.status-failed::before { background: var(--red); } -.design-kanban-card.status-not_started::before, -.design-kanban-card.status-queued::before, -.design-kanban-card.status-canceled::before { background: var(--text-muted); } - -.design-kanban-card-name { - font-size: 13px; - font-weight: 500; - color: var(--text-strong); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - /* Reserve room for the absolutely-positioned close button. */ - padding-right: 20px; -} -.design-kanban-card-meta { - font-size: 12px; - color: var(--text-muted); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.design-kanban-card-meta .ds { - color: var(--text-strong); - font-weight: 500; -} - -/* Honor user motion preferences for the new hover transform / transitions - introduced alongside the kanban view. */ -@media (prefers-reduced-motion: reduce) { - .design-card, - .design-kanban-card { - transition: none; - } - .design-card:hover, - .design-kanban-card:hover { - transform: none; - } -} - -/* Examples gallery */ -.examples-panel { gap: 32px; } -.examples-list { - display: flex; - flex-direction: column; - gap: 32px; -} -.example-card { - display: grid; - grid-template-columns: 2fr 1fr; - gap: 24px; - align-items: center; -} -.example-preview { - position: relative; - background: var(--bg-subtle); - border: 1px solid var(--border); - border-radius: var(--radius); - height: 320px; - overflow: hidden; -} -.example-preview iframe { - width: 100%; height: 100%; border: none; background: white; pointer-events: none; -} -.example-preview-placeholder { - position: absolute; inset: 0; - display: flex; align-items: center; justify-content: center; - color: var(--text-muted); font-size: 12px; -} -.example-meta { display: flex; flex-direction: column; gap: 12px; } -.example-name { font-size: 18px; font-weight: 600; letter-spacing: -0.01em; } -.example-prompt { - font-size: 13px; - color: var(--text-muted); - line-height: 1.55; - font-style: italic; -} -.example-cta { align-self: flex-start; padding: 8px 18px; } -.example-tag { - font-size: 10px; - text-transform: uppercase; - letter-spacing: 0.06em; - padding: 2px 8px; - border-radius: var(--radius-pill); - background: var(--accent); - color: white; - flex-shrink: 0; -} - -/* Design systems gallery — masonry-style cards with lazy showcase iframes - serving as thumbnails. The grid mirrors the prompt-templates and example - gallery surfaces so the three browse tabs feel uniform. */ -.ds-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - gap: 16px; -} -.ds-card { - display: flex; - flex-direction: column; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: 12px; - overflow: hidden; - cursor: pointer; - transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease; -} -.ds-card:hover { - border-color: var(--border-strong); - transform: translateY(-1px); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06); -} -.ds-card:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} -.ds-card.active { - border-color: var(--accent); - box-shadow: 0 0 0 2px var(--accent-tint); -} -.ds-card-thumb { - position: relative; - width: 100%; - aspect-ratio: 4 / 3; - background: var(--bg-subtle); - overflow: hidden; - border-bottom: 1px solid var(--border); -} -.ds-card-thumb iframe { - width: 200%; - height: 200%; - border: none; - display: block; - background: white; - transform: scale(0.5); - transform-origin: top left; - pointer-events: none; -} -.ds-card-thumb-fallback { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; -} -.ds-card-thumb-swatches { - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-template-rows: repeat(2, 1fr); - width: 100%; - height: 100%; -} -.ds-card-thumb-swatches > span { display: block; } -.ds-card-thumb-overlay { - position: absolute; - right: 8px; - bottom: 8px; - background: rgba(15, 15, 18, 0.78); - color: #fff; - font-size: 10px; - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - padding: 4px 10px; - border-radius: 999px; - opacity: 0; - transition: opacity 0.15s ease; -} -.ds-card:hover .ds-card-thumb-overlay, -.ds-card-thumb:focus-visible .ds-card-thumb-overlay { - opacity: 1; -} -.ds-card-meta { - display: flex; - flex-direction: column; - gap: 6px; - padding: 12px 14px 14px; - flex: 1; -} -.ds-card-title-row { - display: flex; - align-items: center; - gap: 8px; - min-width: 0; -} -.ds-card-title { - font-size: 14px; - font-weight: 600; - color: var(--text); - letter-spacing: -0.005em; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex: 1; - min-width: 0; -} -.ds-card-badge { - font-size: 9px; - font-weight: 600; - letter-spacing: 0.06em; - padding: 2px 6px; - background: var(--accent-soft); - color: var(--accent); - border-radius: 4px; - flex-shrink: 0; -} -.ds-card-summary { - font-size: 12px; - color: var(--text-muted); - line-height: 1.45; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} -.ds-card-footer { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - margin-top: auto; - padding-top: 6px; -} -.ds-card-category { - font-size: 10px; - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--text-muted); -} -.ds-card-swatches { - display: inline-flex; - align-items: center; - border-radius: 6px; - border: 1px solid var(--border); - overflow: hidden; - flex-shrink: 0; - height: 18px; -} -.ds-card-swatches > span { - display: block; - width: 14px; - height: 100%; -} -.ds-card-swatches > span + span { - border-left: 1px solid rgba(0, 0, 0, 0.05); -} - -/* Legacy list classes kept for any consumer outside the tab — the gallery - itself no longer renders these. Safe to remove once nothing references - them. */ -.ds-list { display: flex; flex-direction: column; gap: 8px; } -.ds-row { - display: flex; - align-items: center; - gap: 12px; - padding: 14px 16px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - cursor: pointer; -} -.ds-row:hover { border-color: var(--border-strong); } -.ds-row.active { background: var(--accent-tint); border-color: var(--accent); } -.ds-row-body { flex: 1; min-width: 0; } -.ds-row-title { - font-weight: 500; - font-size: 14px; - display: flex; - align-items: center; - gap: 8px; -} -.ds-row-default { - font-size: 9px; - font-weight: 600; - letter-spacing: 0.06em; - padding: 2px 6px; - background: var(--accent-soft); - color: var(--accent); - border-radius: 4px; -} -.ds-row-summary { font-size: 12px; color: var(--text-muted); margin-top: 2px; } -.ds-row-swatches { - display: inline-flex; - align-items: center; - gap: 0; - border-radius: 6px; - border: 1px solid var(--border); - overflow: hidden; - flex-shrink: 0; - height: 24px; -} -.ds-row-swatch { - display: block; - width: 16px; - height: 100%; -} -.ds-row-swatch + .ds-row-swatch { border-left: 1px solid rgba(0, 0, 0, 0.05); } - -@media (max-width: 900px) { - .entry { - grid-template-columns: 56px 1fr !important; - height: 100%; - min-height: 0; - } - .example-card { grid-template-columns: 1fr; } - .example-preview { height: 240px; } - /* Note: home-redesign breakpoints (entry-nav-rail, entry-main__inner, - home-hero__title) live in the per-component files under - `src/styles/home/` to keep upstream `index.css` rebases narrow. */ -} - -/* ============================================================ - File workspace — tabs + viewer - ============================================================ */ -.workspace { - display: flex; - flex-direction: column; - min-height: 0; - min-width: 0; - background: var(--bg); - flex: 1; - overflow: hidden; -} -.ws-tabs-shell { - display: flex; - align-items: center; - gap: 8px; - padding: 6px 10px; - border-bottom: 1px solid var(--border); - background: var(--bg-panel); - height: 44px; - position: sticky; - top: 0; - z-index: 4; -} -.ws-tabs-bar { - display: flex; - align-items: center; - gap: 2px; - overflow-x: auto; - overflow-y: hidden; - flex-wrap: nowrap; - flex: 1 1 auto; - min-width: 0; - height: 100%; - /* thinner scrollbar to avoid overlap */ - scrollbar-width: thin; - scrollbar-color: var(--border-strong) transparent; -} - -.ws-tab { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 6px 12px; - background: transparent; - border: none; - border-radius: var(--radius-sm); - font-size: 12.5px; - cursor: pointer; - flex-shrink: 0; - max-width: 220px; - min-width: 0; - color: var(--text-muted); - transition: background 120ms ease, color 120ms ease; -} -.ws-tab.live-artifact-tab { - max-width: 320px; -} -.ws-tab:hover { background: var(--bg-subtle); color: var(--text); } -.ws-tab.draggable { cursor: grab; } -.ws-tab.dragging { - cursor: grabbing; - opacity: 0.55; -} -.ws-tab.drag-over-before { - box-shadow: inset 2px 0 0 var(--accent); - background: var(--bg-subtle); - color: var(--text); -} -.ws-tab.drag-over-after { - box-shadow: inset -2px 0 0 var(--accent); - background: var(--bg-subtle); - color: var(--text); -} -.ws-tab:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} -.ws-tab.active { - background: var(--bg-subtle); - color: var(--text); - font-weight: 500; -} -.ws-tab .tab-icon { - flex: 0 0 auto; - font-size: 13px; - color: var(--text-muted); - width: 14px; - height: 14px; - display: inline-flex; - align-items: center; - justify-content: center; - line-height: 0; - text-align: center; -} -.ws-tab .tab-icon svg, -.ws-tab-close svg { - display: block; - flex: none; -} -.ws-tab.active .tab-icon { color: var(--text); } -.ws-tab-label { - flex: 1 1 auto; - min-width: 32px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 180px; -} -.ws-tab.live-artifact-tab .ws-tab-label { - max-width: none; -} -.ws-live-artifact-badges { - flex: 0 0 auto; - flex-wrap: nowrap; - max-width: none; - overflow: visible; -} -.ws-live-artifact-badges .live-artifact-badge:not(.live):not(.refreshing):not(.refresh-failed):not(.archived) { - display: none; -} -.ws-live-artifact-badges:has(.refreshing) .live-artifact-badge.live { - display: none; -} -.ws-tab-close { - flex: 0 0 auto; - border: none; - background: transparent; - padding: 0 2px; - font-size: 14px; - line-height: 1; - color: var(--text-faint); - border-radius: 4px; - margin-left: 2px; - width: 16px; - height: 16px; - display: inline-flex; - align-items: center; - justify-content: center; -} -.ws-tab-close:hover { background: var(--border); color: var(--text); } - -.ws-tab.design-files-tab { - font-weight: 500; - color: var(--text); - /* Pin the Design Files entry to the left edge of the horizontally - scrollable tab strip so the user always has a way back to the file - list even when many tabs overflow. Without sticky positioning, - Design Files sits at flex index 0 and scrolls off the left edge - once the strip is wide enough to require horizontal scrolling. - Issue #775. */ - position: sticky; - left: 0; - z-index: 1; - /* Solid panel surface so other tabs that scroll past underneath the - sticky element do not visibly bleed through. */ - background: var(--bg-panel); - /* Subtle right-edge shadow signals the visual separation when other - tabs are scrolled beneath it. */ - box-shadow: 4px 0 6px -4px rgba(0, 0, 0, 0.12); -} -.ws-tab.design-files-tab.active { - /* Active state should still read as selected; layer the active tint - over the sticky panel surface. */ - background: var(--bg-subtle); -} - -.ws-tabs-actions { display: inline-flex; gap: 4px; align-items: center; } -.ws-focus-toggle { - display: inline-flex; - align-items: center; - gap: 6px; - height: 28px; - padding: 0 10px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg); - color: var(--text-muted); - font-size: 12px; - white-space: nowrap; - flex-shrink: 0; -} -.ws-focus-toggle:hover { - background: var(--bg-subtle); - color: var(--text); - border-color: var(--border-strong); -} -.ws-focus-toggle:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} -.ws-tab-action { - padding: 4px 12px; - font-size: 12.5px; - background: transparent; - border: 1px solid transparent; - border-radius: var(--radius-sm); - color: var(--text-muted); -} -.ws-tab-action:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text); border-color: transparent; } -.ws-tab-action.share { - background: var(--text); - color: var(--bg); - border-color: var(--text); - font-weight: 500; -} -.ws-tab-action.share:hover:not(:disabled) { - background: #000; - border-color: #000; -} - -.ws-body { - flex: 1; - min-height: 0; - display: flex; - flex-direction: column; -} - -/* -------- Design Files panel (full right pane) ---------------------- */ -.df-panel { - flex: 1; - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 380px); - min-height: 0; - background: var(--bg); -} -.df-panel.no-preview { grid-template-columns: minmax(0, 1fr); } -.df-main { - display: flex; - flex-direction: column; - min-height: 0; - background: var(--bg); - border-right: 1px solid var(--border); -} -.df-main:last-child { border-right: none; } -.df-body { - flex: 1; - min-height: 0; - overflow-y: auto; - padding: 12px 0 0; - display: flex; - flex-direction: column; -} -.df-controls-row { - margin: 0 20px 8px; - display: flex; - align-items: center; - justify-content: flex-start; - gap: 12px; - flex-wrap: wrap; -} -.df-controls-spacer { - flex: 1 1 auto; - min-width: 0; -} -.df-breadcrumbs { - display: flex; - align-items: center; - flex-wrap: wrap; - padding: 0 20px 6px; - font-size: 12px; - color: var(--text-muted); -} -.df-breadcrumb-btn { - all: unset; - cursor: pointer; - color: var(--text-muted); - padding: 2px 4px; - border-radius: 3px; - font-size: 12px; - transition: color 120ms cubic-bezier(0.23, 1, 0.32, 1), background 120ms cubic-bezier(0.23, 1, 0.32, 1); -} -.df-breadcrumb-btn:hover { color: var(--text); background: var(--bg-subtle); } -.df-breadcrumb-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; } -.df-breadcrumb-segment { display: inline-flex; align-items: center; } -.df-breadcrumb-sep { padding: 0 2px; color: var(--text-faint); font-size: 11px; user-select: none; } -.df-breadcrumb-current { padding: 2px 4px; color: var(--text); font-weight: 500; font-size: 12px; } -.df-dir-row td { cursor: pointer; } -.df-controls-row .df-actions { - margin-left: 0; - display: inline-flex; - align-items: center; - justify-content: flex-end; - gap: 4px; - flex-wrap: wrap; -} -.df-controls-row .df-actions button { - background: transparent; - border: 1px solid transparent; - color: var(--text-muted); - padding: 5px 10px; - font-size: 12.5px; - border-radius: var(--radius-sm); - display: inline-flex; - align-items: center; - gap: 6px; -} -.df-controls-row .df-actions button:hover:not(:disabled) { - background: var(--bg-subtle); - color: var(--text); -} -.df-controls-row .df-actions button.danger { color: var(--red); } -.df-controls-row .df-actions button.danger:hover:not(:disabled) { - background: var(--red-bg); - color: var(--red); -} -.df-refresh-control { - width: 28px; - height: 28px; - padding: 0; - border: 1px solid transparent; - border-radius: var(--radius-sm); - background: transparent; - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; -} -.df-refresh-control:hover:not(:disabled) { - background: var(--bg-subtle); - color: var(--text); -} -.df-group-toggle { - margin: 0; - display: inline-flex; - align-items: center; - gap: 4px; - padding: 3px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-panel); - color: var(--text-muted); - font-size: 11.5px; -} -.df-group-toggle > span { - padding: 0 6px 0 4px; - color: var(--text-faint); -} -.df-group-toggle button { - min-height: 24px; - padding: 3px 9px; - border: 1px solid transparent; - background: transparent; - color: var(--text-muted); - font-size: 11.5px; -} -.df-group-toggle button:hover { - background: var(--bg-subtle); - color: var(--text); -} -.df-group-toggle button.active { - background: var(--bg-subtle); - border-color: var(--border); - color: var(--text); -} - -/* Filter-by-kind dropdown. Lives in the .df-controls-row alongside the - group-by toggle. Trigger is a button with a count badge when active; - popover is a single-column checkbox list of available kinds. */ -.df-kind-filter { - position: relative; - display: inline-flex; -} -.df-kind-filter-trigger { - display: inline-flex; - align-items: center; - gap: 6px; - min-height: 30px; - padding: 3px 10px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-panel); - color: var(--text-muted); - font-size: 11.5px; - cursor: pointer; -} -.df-kind-filter-trigger:hover { - background: var(--bg-subtle); - color: var(--text); -} -.df-kind-filter-trigger.active { - background: var(--bg-subtle); - border-color: var(--selected, var(--border)); - color: var(--text); -} -.df-kind-filter-trigger-label { - white-space: nowrap; -} -.df-kind-filter-count { - min-width: 18px; - height: 18px; - padding: 0 5px; - display: inline-flex; - align-items: center; - justify-content: center; - border-radius: 9px; - background: var(--selected, var(--accent, var(--text))); - color: var(--bg-panel); - font-size: 10.5px; - font-weight: 600; - line-height: 1; -} -.df-kind-filter-popover { - position: absolute; - top: calc(100% + 6px); - left: 0; - width: max(220px, 100%); - max-height: min(360px, calc(100vh - 96px)); - padding: 6px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-panel); - box-shadow: var(--shadow-lg); - z-index: 130; - display: flex; - flex-direction: column; - gap: 4px; - overflow: hidden; -} -.df-kind-filter-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 4px 8px 6px; - border-bottom: 1px solid var(--border); - color: var(--text-faint); - font-size: 10.5px; - letter-spacing: 0.06em; - text-transform: uppercase; -} -.df-kind-filter-clear { - border: 0; - background: transparent; - color: var(--text-muted); - font-size: 11px; - text-transform: none; - letter-spacing: 0; - cursor: pointer; - padding: 2px 4px; - border-radius: 4px; -} -.df-kind-filter-clear:hover { - background: var(--bg-subtle); - color: var(--text); -} -.df-kind-filter-list { - list-style: none; - margin: 0; - padding: 0; - overflow-y: auto; - display: flex; - flex-direction: column; -} -.df-kind-filter-list li + li { - border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent); -} -.df-kind-filter-item { - display: grid; - grid-template-columns: 18px 22px 1fr auto; - align-items: center; - gap: 8px; - padding: 6px 8px; - cursor: pointer; - color: var(--text); - font-size: 12px; - border-radius: 4px; -} -.df-kind-filter-item:hover { - background: var(--bg-subtle); -} -.df-kind-filter-item input[type='checkbox'] { - margin: 0; - cursor: pointer; -} -.df-kind-filter-glyph { - color: var(--text-faint); - font-size: 11px; - text-align: center; - font-family: var(--mono, ui-monospace, monospace); -} -.df-kind-filter-label { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.df-kind-filter-itemcount { - color: var(--text-faint); - font-size: 11px; - font-variant-numeric: tabular-nums; -} -.df-section { display: flex; flex-direction: column; gap: 0; } -.df-section + .df-section { margin-top: 6px; } -.df-section-label { - font-size: 10.5px; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--text-faint); - font-weight: 600; - padding: 12px 20px 6px; -} -.df-section-toggle { - width: auto; - min-width: 0; - display: flex; - align-items: center; - gap: 6px; - border: none; - background: transparent; - text-align: left; - font: inherit; - cursor: pointer; -} -.df-section-toggle:hover { - background: var(--bg-subtle); - color: var(--text); -} -.df-section-toggle span:first-of-type { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.df-section-row td { - padding: 10px 20px 4px; - border-top: 1px solid var(--border); -} -.df-section-row:first-child td { - border-top: none; -} -.df-section-count { - margin-left: 8px; - color: var(--text-faint); - font-weight: 500; - letter-spacing: 0; -} -/* --- file list table --- */ -.df-table { - width: 100%; - border-collapse: collapse; - table-layout: auto; -} -.df-table thead { border-bottom: 1px solid var(--border); } -.df-th-btn { - all: unset; - cursor: pointer; - display: inline-flex; - align-items: center; - gap: 2px; - width: 100%; - font: inherit; - color: inherit; - letter-spacing: inherit; - text-transform: inherit; -} -.df-th-btn:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - border-radius: 2px; -} - -.df-th-sortable { - cursor: pointer; - user-select: none; - font-size: 10.5px; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--text-faint); - font-weight: 600; - padding: 12px 20px 6px; - text-align: left; - white-space: nowrap; - transition: color 120ms ease; -} -.df-th-sortable:hover { color: var(--text); } -.df-th-check, .df-cell-check { width: 28px; padding: 10px 0 10px 20px; } -.df-th-icon, .df-cell-icon { width: 36px; padding: 10px 0; } -.df-th-name { width: auto; } -.df-th-kind { width: 110px; } -.df-th-time { width: 100px; } -.df-th-menu, .df-cell-menu { width: 32px; padding: 10px 20px 10px 0; } -.df-sort-arrow { font-size: 10px; } - -.df-file-row { - transition: background 120ms ease; -} -.df-file-row:hover { background: var(--bg-subtle); } -.df-file-row.active:not(.selected) { background: var(--bg-subtle); } -.df-file-row.active.selected { background: var(--blue-bg); } -.df-file-row.active .df-row-name { color: var(--text-strong); } -.df-file-row.selected { background: var(--blue-bg); } - -.df-cell-check { - text-align: center; - vertical-align: middle; -} -.df-cell-icon { - text-align: center; - vertical-align: middle; -} -.df-cell-name { - padding: 10px 12px 10px 0; - vertical-align: middle; -} -.df-cell-openable { - cursor: pointer; -} -.df-row-name-btn { - all: unset; - cursor: pointer; - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} -.df-row-name-btn:hover:not(:disabled) { - background: transparent; - border-color: transparent; -} -.df-row-name-btn:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - border-radius: 2px; -} -.df-cell-kind { - padding: 10px 12px 10px 0; - vertical-align: middle; -} -.df-cell-time { - padding: 10px 12px 10px 0; - vertical-align: middle; - font-size: 11.5px; - color: var(--text-muted); - font-variant-numeric: tabular-nums; - white-space: nowrap; -} -.df-cell-menu { - text-align: center; - vertical-align: middle; -} -.df-kind-label { - font-size: 12px; - color: var(--text-muted); -} - -/* --- pagination --- */ -.df-pagination { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 20px; - font-size: 12px; - color: var(--text-muted); - flex-wrap: wrap; -} -.df-pagination-start { - justify-content: flex-start; -} -.df-pagination-center { - justify-content: center; -} -.df-pagination-end { - justify-content: flex-end; -} -.df-pagination-right { - margin-left: auto; -} -.df-pagination label { - display: inline-flex; - align-items: center; - gap: 4px; - white-space: nowrap; -} -.df-pagination select { - font-size: 12px; - padding: 2px 24px 2px 6px; - border: 1px solid var(--border); - border-radius: 4px; - appearance: none; - -webkit-appearance: none; - background-color: var(--bg); - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%2374716b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 6px center; - background-size: 12px 12px; - color: var(--text); - cursor: pointer; -} -.df-pagination select::-ms-expand { display: none; } -[data-theme='dark'] .df-pagination select { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%239a9690' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E"); -} -@media (prefers-color-scheme: dark) { - html:not([data-theme]) .df-pagination select { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%239a9690' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E"); - } -} -.df-pagination select:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 1px; -} -.df-page-btn { - background: transparent; - border: 1px solid var(--border); - color: var(--text-muted); - padding: 4px 10px; - font-size: 12px; - border-radius: 4px; - cursor: pointer; - display: inline-flex; - align-items: center; - gap: 4px; - transition: background 120ms ease, color 120ms ease; -} -.df-page-btn:hover:not(:disabled) { - background: var(--bg-subtle); - color: var(--text); -} -.df-page-btn:disabled { - opacity: 0.4; - cursor: default; -} -.df-page-btn:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 1px; -} -.df-page-info { - white-space: nowrap; - color: var(--text-faint); - font-size: 11.5px; -} - -.df-row { - display: grid; - grid-template-columns: 28px 36px 1fr auto auto; - align-items: center; - gap: 12px; - padding: 10px 20px; - background: transparent; - border: none; - border-radius: 0; - text-align: left; - cursor: pointer; - width: 100%; - font: inherit; - color: inherit; - position: relative; - transition: background 120ms ease; -} -.df-row-live-artifact { - grid-template-columns: 36px minmax(0, 1fr) auto; -} -.df-row-plugin-folder { - grid-template-columns: minmax(0, 1fr) auto minmax(0, auto); - cursor: default; -} -.df-row-folder-main { - appearance: none; - display: grid; - grid-template-columns: 36px minmax(0, 1fr); - align-items: center; - gap: 12px; - min-width: 0; - padding: 0; - border: 0; - background: transparent; - color: inherit; - cursor: pointer; - font: inherit; - text-align: left; -} -.df-plugin-install { - appearance: none; - display: inline-flex; - align-items: center; - justify-content: center; - min-height: 28px; - padding: 0 10px; - border: 1px solid var(--border); - border-radius: 999px; - background: var(--bg-panel); - color: var(--text-muted); - cursor: pointer; - font: inherit; - font-size: 11px; - font-weight: 600; -} -.df-plugin-install:hover:not(:disabled) { - border-color: var(--accent); - color: var(--accent); -} -.df-plugin-install:disabled { - cursor: not-allowed; - opacity: 0.55; -} -.df-plugin-actions { - display: flex; - justify-content: flex-end; - gap: 6px; - flex-wrap: wrap; -} -.df-inline-notice { - margin: 0 20px 6px; - padding: 8px 10px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-subtle); - color: var(--text-muted); - font-size: 12px; -} -.df-inline-notice a, -.plugin-action-card__notice a { - color: var(--accent); - text-decoration: underline; - text-underline-offset: 2px; - word-break: break-all; -} -.df-row:hover { background: var(--bg-subtle); } -.df-row.active { background: var(--blue-bg); color: var(--text); } -.df-row.active .df-row-name { color: var(--text-strong); } -.df-row.selected { background: var(--blue-bg); } - -.df-row-check { - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - font-size: 13px; - color: var(--text-muted); - user-select: none; - width: 24px; - height: 24px; - border-radius: 4px; - transition: background 120ms ease, color 120ms ease; -} -.df-row-check:hover { background: var(--border); color: var(--text); } -.df-row-check[aria-checked="true"] { color: var(--accent-strong); } - -.df-select-bar { - display: inline-flex; - align-items: center; - gap: 2px; -} -.df-select-all { - background: none; - border: none; - color: var(--text-faint); - cursor: pointer; - font: inherit; - font-size: 11px; - font-weight: 500; - padding: 2px 6px; - border-radius: 4px; - transition: background 120ms ease, color 120ms ease; -} -.df-select-all:hover { background: var(--bg-subtle); color: var(--text); } -.df-row-icon { - width: 32px; - height: 32px; - display: inline-flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - border-radius: 6px; - background: var(--bg-subtle); - color: var(--text-muted); - font-size: 14px; - position: relative; -} -.df-row-icon[data-kind="folder"] { background: var(--bg-muted); color: var(--text-soft); } -.df-row-icon[data-kind="html"] { background: var(--accent-tint); color: var(--accent-strong); } -.df-row-icon[data-kind="image"] { background: var(--green-bg); color: var(--green); } -.df-row-icon[data-kind="code"] { background: #fff7d8; color: #8c6700; } -.df-row-icon[data-kind="text"] { background: var(--bg-subtle); color: var(--text-muted); } -.df-row-icon[data-kind="sketch"] { background: var(--purple-bg); color: var(--purple); } -.df-row-name-wrap { display: flex; flex-direction: column; gap: 2px; min-width: 0; } -.df-row-name { - font-size: 13px; - font-weight: 500; - color: var(--text); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.df-rename-input { - width: 100%; - min-width: 0; - border: 1px solid var(--accent); - border-radius: 4px; - background: var(--bg-panel); - color: var(--text); - font: inherit; - font-size: 13px; - line-height: 1.3; - padding: 3px 6px; -} -.df-row-sub { - font-size: 11px; - color: var(--text-muted); - display: inline-flex; - align-items: center; - gap: 6px; - flex-wrap: wrap; -} -.df-row-time { - font-size: 11.5px; - color: var(--text-muted); - font-variant-numeric: tabular-nums; - white-space: nowrap; -} -.df-row-menu { - background: transparent; - border: none; - padding: 4px 6px; - color: var(--text-muted); - font-size: 16px; - border-radius: 4px; - opacity: 0; - transition: opacity 120ms ease; -} -.df-row:hover .df-row-menu { opacity: 1; } -.df-row-menu:focus { opacity: 1; } -.df-row-menu:hover { background: var(--border); color: var(--text); } -.df-section-more { - margin: 6px 20px 10px; - width: auto; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - background: var(--bg-subtle); - border: 1px solid var(--border); - color: var(--text-muted); - font-size: 12px; - line-height: 1; -} -.df-section-more:hover:not(:disabled) { - background: var(--bg-muted); - color: var(--text); -} -.df-row-collapse { - background: transparent; - border: none; - color: var(--text-muted); - padding: 0; - width: 18px; - text-align: center; - font-size: 12px; - cursor: pointer; -} -.df-empty { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 48px 24px; - color: var(--text-muted); - font-size: 13px; - min-height: 320px; - background-color: var(--bg); - background-image: - linear-gradient(to right, var(--border-soft) 1px, transparent 1px), - linear-gradient(to bottom, var(--border-soft) 1px, transparent 1px); - background-size: 24px 24px; - background-position: -1px -1px; -} -.df-empty-pill { - display: inline-flex; - flex-direction: column; - align-items: center; - gap: 14px; - padding: 28px 36px; - border-radius: 24px; - border: 1px solid var(--border); - background: var(--bg-panel); - box-shadow: - 0 1px 0 rgba(15, 15, 15, 0.02), - 0 8px 24px -12px rgba(15, 15, 15, 0.08); - max-width: min(420px, 92%); - text-align: center; -} -.df-empty-title { - font-size: 16px; - color: var(--text-strong); - font-weight: 500; - letter-spacing: -0.005em; - line-height: 1.3; -} -.df-empty-cta { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 7px 14px; - border: 1px solid var(--border); - border-radius: 999px; - background: var(--bg); - color: var(--text); - font: inherit; - font-size: 12.5px; - font-weight: 500; - cursor: pointer; - flex-shrink: 0; - transition: background 120ms ease, border-color 120ms ease; -} -.df-empty-cta:hover { - background: var(--bg-subtle); - border-color: var(--border-strong); -} -.df-empty-cta:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.df-drop { - margin: 18px 16px 16px; - padding: 16px; - border: 1px dashed var(--border-strong); - border-radius: var(--radius); - background: var(--bg-panel); - display: flex; - flex-direction: column; - align-items: center; - gap: 6px; - text-align: center; - color: var(--text-muted); -} -.df-drop.dragging { - border-color: var(--accent); - background: color-mix(in srgb, var(--accent) 10%, var(--bg-panel)); - color: var(--text); -} -.df-drop .label { - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.1em; - color: var(--text-faint); - display: inline-flex; - align-items: center; - gap: 6px; - font-weight: 600; -} -.df-drop .desc { - font-size: 12px; - line-height: 1.5; - max-width: 56ch; -} - -/* Right preview pane in design files */ -.df-preview { - display: flex; - flex-direction: column; - min-height: 0; - background: var(--bg-panel); - border-left: 1px solid var(--border); - position: relative; -} -.df-preview-empty { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - color: var(--text-faint); - font-size: 13px; - padding: 32px; -} -.df-preview-thumb { - margin: 16px; - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--bg); - overflow: hidden; - aspect-ratio: 16/10; - flex-shrink: 0; - position: relative; -} -.df-preview-thumb.is-openable { - cursor: pointer; -} -.df-preview-thumb-open { - position: absolute; - inset: 0; - z-index: 2; - width: 100%; - height: 100%; - padding: 0; - border: 0; - border-radius: inherit; - background: transparent; - cursor: pointer; -} -.df-preview-thumb-open:hover { - background: color-mix(in srgb, var(--accent) 6%, transparent); -} -.df-preview-thumb-open:focus-visible { - outline: 2px solid var(--accent); - outline-offset: -3px; -} -.df-preview-thumb iframe, -.df-preview-thumb img, -.df-preview-thumb video { - width: 100%; height: 100%; border: none; background: white; object-fit: cover; display: block; -} -.df-preview-thumb .sketch-preview, -.df-preview-thumb .sketch-preview svg { - width: 100%; - height: 100%; - display: block; -} -.df-preview-thumb audio { - width: calc(100% - 24px); - position: absolute; - left: 12px; - bottom: 12px; -} -.df-preview-meta { - padding: 0 16px 16px; - display: flex; - flex-direction: column; - gap: 7px; -} -.df-preview-name { - font-size: 14px; - font-weight: 600; - color: var(--text-strong); - word-break: break-word; -} -.df-preview-kind { font-size: 12px; color: var(--text-muted); } -.df-preview-stats { font-size: 11.5px; color: var(--text-muted); } -.df-preview-actions { - display: inline-flex; - align-items: center; - gap: 8px; - margin-bottom: 6px; - flex-wrap: wrap; -} -.df-preview-actions button, -.df-preview-actions .ghost-link { - font-size: 12px; - min-height: 30px; - padding: 0 11px; - background: var(--bg-panel); - border: 1px solid var(--border); - color: var(--text); - border-radius: var(--radius-pill); - display: inline-flex; - align-items: center; - gap: 6px; - text-decoration: none; -} -.df-preview-actions button:hover, -.df-preview-actions .ghost-link:hover { - background: var(--bg-subtle); - border-color: var(--border-strong); -} -.df-preview-close { - position: absolute; - top: 12px; - right: 12px; - z-index: 4; - width: 30px; - height: 30px; - padding: 0; - border: 1px solid var(--border); - border-radius: 999px; - background: color-mix(in srgb, var(--bg-panel) 92%, transparent); - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; - box-shadow: var(--shadow-xs); - transition: opacity 120ms ease, background 120ms ease, border-color 120ms ease, color 120ms ease; -} - -@media (hover: hover) and (pointer: fine) { - .df-preview-close { - opacity: 0; - pointer-events: none; - } - - .df-preview:hover .df-preview-close, - .df-preview-close:focus-visible { - opacity: 1; - pointer-events: auto; - } -} - -@media (any-pointer: coarse) { - .df-preview-close { - opacity: 1; - pointer-events: auto; - } -} - -.df-preview-close:hover { - background: var(--bg-subtle); - border-color: var(--border-strong); - color: var(--text-strong); -} - -/* Row context menu */ -.df-row-popover { - position: fixed; - z-index: 200; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - box-shadow: var(--shadow-md); - padding: 4px; - min-width: 160px; - display: flex; - flex-direction: column; -} -.df-row-popover button { - background: transparent; - border: none; - padding: 7px 10px; - font-size: 12.5px; - text-align: left; - border-radius: 4px; - color: var(--text); -} -.df-row-popover button:hover { background: var(--bg-subtle); } -.df-row-popover button.danger { color: var(--red); } -.df-row-popover button.danger:hover { background: var(--red-bg); } - -/* -------- Viewer ---------------------------------------------------- */ -.viewer { - flex: 1; - display: flex; - flex-direction: column; - min-height: 0; -} -.viewer-toolbar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 14px; - border-bottom: 1px solid var(--border); - background: var(--bg-panel); - font-size: 12.5px; - color: var(--text-muted); - gap: 8px; - min-height: 44px; - flex-shrink: 0; -} -.viewer-toolbar-left { display: inline-flex; align-items: center; gap: 8px; } -.viewer-toolbar-actions { display: inline-flex; gap: 2px; align-items: center; flex-wrap: wrap; } -.viewer-toolbar .icon-only, -.viewer-toolbar-actions .icon-only { - width: 28px; - height: 28px; - padding: 0; - background: transparent; - border: none; - border-radius: 6px; - color: var(--text-muted); - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 14px; -} -.viewer-toolbar .icon-only:hover:not(:disabled), -.viewer-toolbar-actions .icon-only:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text); } -.viewer-action { - display: inline-flex; - align-items: center; - gap: 5px; - padding: 4px 10px; - background: transparent; - border: none; - border-radius: var(--radius-sm); - color: var(--text-muted); - font-size: 12.5px; - white-space: nowrap; -} -.viewer-action:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text); } -.viewer-action.active { - background: var(--accent-tint); - color: var(--accent-strong); -} -.viewer-action.primary { - background: var(--accent); - border: 1px solid var(--accent); - color: white; -} -.viewer-action.primary:hover:not(:disabled) { - background: var(--accent-hover); - border-color: var(--accent-hover); - color: white; -} -/* Primary actions support a subtle in-flight affordance via data-running. Keeps - layout stable (unlike :disabled-only) so locale text width doesn't jump. */ -.viewer-action.primary[data-running='true'] { - opacity: 0.75; - cursor: progress; -} -/* Preview-only controls: keep layout stable across modes. - When inactive, reserve the same horizontal slot but fully disable interaction. */ -.viewer-preview-controls { - display: inline-flex; - gap: 2px; - align-items: center; - transition: opacity 140ms ease; -} -.viewer-preview-controls[data-active='false'] { - opacity: 0; - pointer-events: none; - user-select: none; -} -.viewer-zoom-level { - min-width: 60px; - justify-content: center; -} -.viewer-divider { - width: 1px; - height: 18px; - background: var(--border); - margin: 0 4px; -} -.viewer-toggle { - display: inline-flex; - align-items: center; - gap: 6px; - font-size: 12.5px; - color: var(--text-muted); - padding: 4px 10px; - border-radius: var(--radius-sm); - white-space: nowrap; -} -.viewer-toggle .switch { - position: relative; - width: 28px; - height: 16px; - background: var(--bg-muted); - border-radius: 999px; - border: 1px solid var(--border-strong); - transition: background 120ms ease; -} -.viewer-toggle .switch::after { - content: ''; - position: absolute; - top: 1px; - left: 1px; - width: 12px; height: 12px; - background: var(--bg-panel); - border-radius: 50%; - transition: transform 120ms ease; - box-shadow: var(--shadow-xs); -} -.viewer-toggle.on .switch, -.viewer-toggle.active .switch { background: var(--text); border-color: var(--text); } -.viewer-toggle.on .switch::after, -.viewer-toggle.active .switch::after { transform: translateX(12px); } -.viewer-tabs { display: inline-flex; gap: 2px; } -.viewer-mode-menu { - position: relative; - display: inline-flex; - align-items: center; - width: 96px; -} -.viewer-mode-trigger { - width: 100%; - min-height: 26px; - justify-content: space-between; - gap: 8px; - padding: 4px 8px 4px 10px; - border-color: var(--border); - background-color: var(--bg-panel); - color: var(--text); - font-size: 12.5px; - line-height: 1.2; - box-shadow: var(--shadow-xs); -} -.viewer-mode-trigger:hover:not(:disabled), -.viewer-mode-trigger[aria-expanded='true'] { - border-color: var(--border-strong); - background-color: var(--bg-subtle); -} -.viewer-mode-popover { - position: absolute; - top: calc(100% + 6px); - left: 0; - z-index: 80; - width: 100%; - display: flex; - flex-direction: column; - padding: 5px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-panel); - box-shadow: var(--shadow-md); - overflow: hidden; -} -.viewer-mode-menu-item { - width: 100%; - min-height: 28px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - padding: 5px 8px; - border: 1px solid transparent; - border-radius: var(--radius-sm); - background: transparent; - color: var(--text-muted); - font-size: 12.5px; - text-align: left; -} -.viewer-mode-menu-item:hover, -.viewer-mode-menu-item.active { - background: var(--bg-subtle); - color: var(--text); -} -.viewer-mode-menu-item.active { - background: var(--accent-tint); - color: var(--accent-strong); -} -.viewer-tab { - background: transparent; - border: none; - padding: 4px 10px; - font-size: 12px; - border-radius: var(--radius-sm); - color: var(--text-muted); - white-space: nowrap; -} -.viewer-tab:hover { background: var(--bg-subtle); color: var(--text); } -.viewer-tab.active { - background: var(--bg-subtle); - color: var(--text); - font-weight: 500; -} -.viewer-meta { font-size: 12px; color: var(--text-muted); } -.ghost-link { - font-size: 12px; - color: var(--text-muted); - text-decoration: none; - padding: 4px 10px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - display: inline-flex; - align-items: center; - gap: 4px; -} -.ghost-link:hover { background: var(--bg-subtle); color: var(--text); } -.viewer-body { - flex: 1; - min-height: 0; - position: relative; - background: var(--bg); - overflow: auto; -} -.live-artifact-refresh-notice { - position: sticky; - top: 0; - z-index: 2; - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 9px 14px; - border-bottom: 1px solid var(--border); - background: var(--bg-panel); - color: var(--text-muted); - font-size: 12.5px; -} -.live-artifact-refresh-notice-copy { - display: inline-flex; - align-items: center; - gap: 8px; - min-width: 0; -} -.live-artifact-refresh-notice strong { - color: var(--text); - font-weight: 600; -} -.live-artifact-refresh-notice .icon-only { - width: 24px; - height: 24px; - flex: 0 0 auto; - border: none; - border-radius: var(--radius-sm); - background: transparent; - color: inherit; - font-size: 16px; -} -.live-artifact-refresh-notice .icon-only:hover { - background: color-mix(in srgb, currentColor 12%, transparent); -} -.live-artifact-refresh-notice.running { - background: var(--bg-subtle); -} -.live-artifact-refresh-notice.success { - background: var(--green-bg); - border-color: var(--green-border); - color: var(--green); -} -.live-artifact-refresh-notice.error { - background: var(--red-bg); - border-color: var(--red-border); - color: var(--red); -} -.live-artifact-refresh-notice.success strong, -.live-artifact-refresh-notice.error strong { - color: inherit; -} - -.live-artifact-code-panel { - display: flex; - flex-direction: column; - height: 100%; - min-height: 0; - background: var(--bg); -} -.live-artifact-code-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - padding: 12px 16px; - border-bottom: 1px solid var(--border); - background: var(--bg-panel); -} -.live-artifact-code-copy { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} -.live-artifact-code-copy strong { - font-size: 13px; - color: var(--text); -} -.live-artifact-code-copy span { - font-size: 12px; - color: var(--text-muted); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.live-artifact-code-tabs { - flex-shrink: 0; -} -.live-artifact-code-panel pre.viewer-source { - flex: 1 1 auto; - min-height: 0; -} - -/* ============================================================ - Live artifact — Refresh history panel - ============================================================ */ -.live-artifact-refresh-panel { - display: flex; - flex-direction: column; - gap: 18px; - padding: 20px 22px 28px; - color: var(--text); - background: var(--bg); - min-height: 100%; -} -.live-artifact-refresh-hero { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 20px; - padding: 16px 18px; - border: 1px solid var(--border); - border-radius: 10px; - background: var(--bg-panel); - box-shadow: var(--shadow-xs, 0 1px 2px rgba(16, 24, 40, 0.04)); -} -.live-artifact-refresh-hero-main { - display: flex; - flex-direction: column; - gap: 8px; - min-width: 0; - flex: 1 1 auto; -} -.live-artifact-refresh-hero-desc { - margin: 0; - color: var(--text-muted); - font-size: 12.5px; - line-height: 1.5; -} -.live-artifact-refresh-hero-meta { - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 6px; - flex: 0 0 auto; -} -.live-artifact-refresh-hero-metric { - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 2px; - text-align: right; -} -.live-artifact-refresh-label { - font-size: 10.5px; - font-weight: 600; - letter-spacing: 0.06em; - text-transform: uppercase; - color: var(--text-muted); -} -.live-artifact-refresh-value { - font-size: 14px; - font-weight: 600; - color: var(--text); - font-variant-numeric: tabular-nums; -} -.live-artifact-refresh-value.muted { - color: var(--text-muted); - font-weight: 500; -} -.live-artifact-refresh-sub { - font-size: 11.5px; - color: var(--text-muted); - font-variant-numeric: tabular-nums; -} - -/* Refresh status badges reuse the .live-artifact-badge pill shape with tone mixins */ -.live-artifact-badge.refresh-status { - text-transform: uppercase; - letter-spacing: 0.04em; -} -.live-artifact-badge.refresh-status.tone-neutral { - color: var(--text-muted); - border-color: var(--border); - background: var(--bg-panel); -} -.live-artifact-badge.refresh-status.tone-running { - color: #1d4ed8; - border-color: rgba(29, 78, 216, 0.22); - background: rgba(29, 78, 216, 0.08); -} -.live-artifact-badge.refresh-status.tone-success { - color: var(--green); - border-color: var(--green-border); - background: var(--green-bg); -} -.live-artifact-badge.refresh-status.tone-warning { - color: var(--amber); - border-color: color-mix(in srgb, var(--amber) 28%, transparent); - background: var(--amber-bg); -} -.live-artifact-badge.refresh-status.tone-error { - color: var(--red); - border-color: var(--red-border); - background: var(--red-bg); -} - -.live-artifact-refresh-facts { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 10px; -} -.live-artifact-refresh-fact { - display: flex; - flex-direction: column; - gap: 4px; - padding: 12px 14px; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--bg-panel); - min-width: 0; -} -.live-artifact-refresh-fact .live-artifact-refresh-value { - font-size: 13px; - font-weight: 600; -} - -.live-artifact-refresh-section { - display: flex; - flex-direction: column; - gap: 10px; -} -.live-artifact-refresh-section-header { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 12px; -} -.live-artifact-refresh-section-header h4 { - margin: 0; - font-size: 12.5px; - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--text-muted); -} -.live-artifact-refresh-hint { - font-size: 11.5px; - color: var(--text-muted); -} -.live-artifact-refresh-empty { - padding: 16px 18px; - border: 1px dashed var(--border); - border-radius: 8px; - background: var(--bg-panel); - color: var(--text-muted); - font-size: 12.5px; - line-height: 1.5; -} -.live-artifact-refresh-empty em { - font-style: normal; - font-weight: 600; - color: var(--text); -} - -.live-artifact-refresh-timeline { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - gap: 0; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--bg-panel); - overflow: hidden; -} -.live-artifact-refresh-event { - display: grid; - grid-template-columns: 20px 1fr; - column-gap: 12px; - padding: 12px 14px; - border-bottom: 1px solid var(--border-soft, var(--border)); - position: relative; -} -.live-artifact-refresh-event:last-child { - border-bottom: none; -} -.live-artifact-refresh-event-dot { - width: 10px; - height: 10px; - border-radius: 999px; - margin-top: 5px; - justify-self: center; - background: var(--border-strong); - box-shadow: 0 0 0 3px var(--bg-panel); -} -.live-artifact-refresh-event.tone-running .live-artifact-refresh-event-dot { - background: #1d4ed8; - animation: liveArtifactRefreshPulse 1.5s ease-in-out infinite; -} -.live-artifact-refresh-event.tone-success .live-artifact-refresh-event-dot { - background: var(--green); -} -.live-artifact-refresh-event.tone-error .live-artifact-refresh-event-dot { - background: var(--red); -} -@keyframes liveArtifactRefreshPulse { - 0%, 100% { opacity: 1; transform: scale(1); } - 50% { opacity: 0.55; transform: scale(0.85); } -} -.live-artifact-refresh-event-body { - display: flex; - flex-direction: column; - gap: 4px; - min-width: 0; -} -.live-artifact-refresh-event-row { - display: inline-flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; -} -.live-artifact-refresh-event-time { - font-size: 11.5px; - color: var(--text-muted); - font-variant-numeric: tabular-nums; -} -.live-artifact-refresh-event-detail { - font-size: 12.5px; - color: var(--text); - line-height: 1.5; - word-break: break-word; -} - -.live-artifact-refresh-tiles { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--bg-panel); - overflow: hidden; -} -.live-artifact-refresh-tile { - display: grid; - grid-template-columns: 1fr auto; - column-gap: 12px; - row-gap: 6px; - padding: 12px 14px; - border-bottom: 1px solid var(--border-soft, var(--border)); - align-items: center; -} -.live-artifact-refresh-tile:last-child { - border-bottom: none; -} -.live-artifact-refresh-tile-main { - display: flex; - flex-direction: column; - gap: 2px; - min-width: 0; -} -.live-artifact-refresh-tile-title { - font-size: 13px; - font-weight: 500; - color: var(--text); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.live-artifact-refresh-tile-meta code { - font-size: 11px; - font-family: var(--mono); - color: var(--text-muted); - background: var(--bg-subtle); - padding: 1px 6px; - border-radius: 4px; -} -.live-artifact-refresh-tile-side { - display: inline-flex; - justify-self: end; -} -.live-artifact-refresh-tile-error { - grid-column: 1 / -1; - font-size: 11.5px; - color: var(--red); - background: var(--red-bg); - border: 1px solid var(--red-border); - border-radius: 6px; - padding: 6px 10px; - line-height: 1.5; - word-break: break-word; -} -.live-artifact-refresh-tile-error.neutral { - color: var(--text-muted); - background: var(--bg-subtle); - border-color: var(--border); -} - -.live-artifact-refresh-kv { - margin: 0; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 10px; - padding: 14px 16px; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--bg-panel); -} -.live-artifact-refresh-kv > div { - display: flex; - flex-direction: column; - gap: 3px; - min-width: 0; -} -.live-artifact-refresh-kv dt { - font-size: 10.5px; - font-weight: 600; - letter-spacing: 0.06em; - text-transform: uppercase; - color: var(--text-muted); -} -.live-artifact-refresh-kv dd { - margin: 0; - font-size: 13px; - color: var(--text); - word-break: break-word; -} -.live-artifact-refresh-kv dd code { - font-family: var(--mono); - font-size: 12px; - background: var(--bg-subtle); - padding: 1px 6px; - border-radius: 4px; -} - -.live-artifact-refresh-raw { - border: 1px solid var(--border); - border-radius: 8px; - background: var(--bg-panel); - overflow: hidden; -} -.live-artifact-refresh-raw > summary { - cursor: pointer; - padding: 10px 14px; - font-size: 12px; - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--text-muted); - user-select: none; - list-style: none; -} -.live-artifact-refresh-raw > summary::-webkit-details-marker { display: none; } -.live-artifact-refresh-raw > summary::before { - content: '▸'; - display: inline-block; - width: 1em; - transition: transform 120ms ease; - color: var(--text-faint); -} -.live-artifact-refresh-raw[open] > summary::before { - transform: rotate(90deg); -} -.live-artifact-refresh-raw > summary:hover { - background: var(--bg-subtle); -} -.live-artifact-refresh-raw-note { - margin: 0; - padding: 0 14px 10px; - color: var(--text-muted); - font-size: 11.5px; - line-height: 1.5; -} -.live-artifact-refresh-raw pre.viewer-source { - border-top: 1px solid var(--border); - min-height: 0; - max-height: 320px; - overflow: auto; -} - -@media (max-width: 640px) { - .live-artifact-refresh-hero { - flex-direction: column; - align-items: stretch; - } - .live-artifact-refresh-hero-meta { - align-items: flex-start; - } - .live-artifact-refresh-hero-metric { - align-items: flex-start; - text-align: left; - } -} -.viewer-body iframe { - width: 100%; - height: 100%; - border: none; - background: white; -} -.artifact-preview-transport-stack { - position: absolute; - inset: 0; - width: 100%; - height: 100%; -} -.artifact-preview-transport-stack iframe { - position: absolute; - inset: 0; -} -.artifact-preview-transport-stack iframe[data-od-active='false'] { - visibility: hidden; - pointer-events: none; -} -.viewer-viewport-switcher { - position: relative; - display: inline-flex; - align-items: center; - width: 96px; -} -.viewer-viewport-trigger { - width: 100%; - min-height: 26px; - justify-content: space-between; - gap: 8px; - padding: 4px 8px 4px 10px; - border-color: var(--border); - background-color: var(--bg-panel); - color: var(--text); - font-size: 12.5px; - line-height: 1.2; - box-shadow: var(--shadow-xs); -} -.viewer-viewport-trigger:hover:not(:disabled), -.viewer-viewport-trigger[aria-expanded='true'] { - border-color: var(--border-strong); - background-color: var(--bg-subtle); -} -.viewer-viewport-menu { - position: absolute; - top: calc(100% + 6px); - right: 0; - z-index: 80; - width: 100%; - padding: 5px; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg-panel); - box-shadow: var(--shadow-md); - overflow: hidden; -} -.viewer-viewport-menu-item { - width: 100%; - min-height: 28px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - padding: 5px 8px; - border: 1px solid transparent; - border-radius: var(--radius-sm); - background: transparent; - color: var(--text-muted); - font-size: 12.5px; - text-align: left; -} -.viewer-viewport-menu-item:hover { - background: var(--bg-subtle); - color: var(--text); -} -.viewer-viewport-menu-item.active { - background: var(--accent-tint); - color: var(--accent-strong); -} -.live-artifact-preview-layer, -.comment-preview-layer { - position: relative; - width: 100%; - height: 100%; - min-height: 0; -} -.live-artifact-preview-layer.preview-viewport[data-active='false'] { - display: none; -} -.preview-viewport:not(.preview-viewport-desktop) { - overflow: auto; - display: flex; - align-items: flex-start; - justify-content: center; - min-width: 0; - padding: 24px; - background: - linear-gradient(45deg, color-mix(in srgb, var(--border) 28%, transparent) 25%, transparent 25%), - linear-gradient(-45deg, color-mix(in srgb, var(--border) 28%, transparent) 25%, transparent 25%), - linear-gradient(45deg, transparent 75%, color-mix(in srgb, var(--border) 28%, transparent) 75%), - linear-gradient(-45deg, transparent 75%, color-mix(in srgb, var(--border) 28%, transparent) 75%), - var(--bg-subtle); - background-size: 16px 16px; - background-position: 0 0, 0 8px, 8px -8px, -8px 0; -} -.preview-viewport:not(.preview-viewport-desktop) .preview-frame-clip, -.preview-viewport:not(.preview-viewport-desktop) .comment-frame-clip, -.preview-viewport:not(.preview-viewport-desktop).manual-edit-workspace .manual-edit-canvas { - width: calc(var(--preview-viewport-width) * var(--preview-scale, 1)); - height: calc(var(--preview-viewport-height) * var(--preview-scale, 1)); - max-width: none; - max-height: none; - flex: 0 0 auto; - border: 1px solid var(--border-strong); - border-radius: 18px; - overflow: hidden; - box-shadow: 0 24px 80px rgba(0, 0, 0, 0.22); - background: var(--bg-panel); -} -.preview-viewport:not(.preview-viewport-desktop) .preview-frame-clip > div, -.preview-viewport:not(.preview-viewport-desktop) .comment-frame-clip > div, -.preview-viewport:not(.preview-viewport-desktop).manual-edit-workspace .manual-edit-canvas > div { - will-change: transform; -} -.preview-viewport:not(.preview-viewport-desktop) .preview-frame-clip, -.preview-viewport:not(.preview-viewport-desktop) .comment-frame-clip { - position: relative; - inset: auto; -} -.preview-viewport-mobile .preview-frame-clip, -.preview-viewport-mobile .comment-frame-clip, -.preview-viewport-mobile.manual-edit-workspace .manual-edit-canvas { - border-radius: 28px; -} -.preview-frame-clip, -.comment-frame-clip { - position: absolute; - inset: 0; - overflow: hidden; -} -.comment-overlay-layer { - position: absolute; - inset: 0; - z-index: 3; - pointer-events: none; -} -.board-pod-stroke { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - overflow: visible; - pointer-events: none; -} -.board-pod-stroke polyline { - fill: none; - stroke: rgba(22, 119, 255, 0.95); - stroke-width: 3; - stroke-linecap: round; - stroke-linejoin: round; - filter: drop-shadow(0 0 4px rgba(22, 119, 255, 0.28)); -} -.comment-target-overlay { - position: absolute; - pointer-events: none; - border: 1px solid var(--comment-overlay-border, #1677ff); - background: var(--comment-overlay-bg, rgba(22, 119, 255, 0.24)); - box-shadow: 0 0 0 1px var(--comment-overlay-ring, rgba(22, 119, 255, 0.18)); -} -.comment-target-overlay.selected { - border-width: 2px; -} -/* Per-member overlay highlight triggered by chip hover / remove-button focus. - Only fires from the member rendering branch in CommentTargetOverlay; - single-element snapshots use the non-member fallback and never get hovered. */ -.comment-target-overlay--member.is-hover-focused { - outline: 2px solid rgb(22, 119, 255); - outline-offset: 2px; - z-index: 2; /* float above sibling overlays */ -} -.comment-target-overlay-label { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} -.comment-saved-marker { - position: absolute; - pointer-events: none; -} -.comment-saved-outline { - position: absolute; - inset: 0; - border: 1px dashed rgba(22, 119, 255, 0.72); - background: rgba(22, 119, 255, 0.08); -} -.comment-saved-pin { - position: absolute; - z-index: 2; - left: -8px; - top: -8px; - min-width: 18px; - height: 18px; - padding: 0 5px; - border: 1px solid #0958d9; - border-radius: var(--radius-pill); - background: #1677ff; - color: white; - font-size: 10px; - font-weight: 700; - line-height: 16px; - box-shadow: var(--shadow-sm); - pointer-events: auto; -} -.comment-saved-pin:hover { - background: #0958d9; - transform: translateY(-1px); -} -.comment-popover { - position: absolute; - left: 14px; - top: 14px; - z-index: 40; - width: min(320px, calc(100% - 28px)); - padding: 10px; - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--bg-panel); - box-shadow: var(--shadow-lg); -} -.comment-popover-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; - margin-bottom: 8px; -} -.comment-popover-head div { - display: grid; - gap: 1px; - min-width: 0; - overflow: hidden; -} -.comment-popover-head strong { - font-size: 13px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.comment-popover-head span { - color: var(--text-muted); - font-size: 12px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.comment-popover-head button.ghost { - flex: 0 0 auto; - white-space: nowrap; -} -.board-pod-summary { - display: grid; - gap: 6px; - margin-bottom: 8px; - padding: 8px; - border: 1px solid var(--border-soft); - border-radius: var(--radius-sm); - background: var(--bg-subtle); -} -.board-pod-summary strong { - font-size: 12px; -} -.board-pod-members { - display: flex; - flex-wrap: wrap; - gap: 6px; - max-height: 132px; - overflow-y: auto; -} -.board-pod-chip { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 3px 7px; - border: 1px solid var(--border-soft); - border-radius: var(--radius-pill); - background: var(--bg-panel); - color: var(--text-muted); - font-size: 11px; -} -.board-pod-chip-remove { - display: inline-flex; - align-items: center; - justify-content: center; - width: 14px; - height: 14px; - padding: 0; - border: 0; - border-radius: 50%; - background: transparent; - color: var(--text-muted); - cursor: pointer; - opacity: 0; - transition: opacity 200ms cubic-bezier(0.23, 1, 0.32, 1), color 200ms cubic-bezier(0.23, 1, 0.32, 1); -} -.board-pod-chip:hover .board-pod-chip-remove, -.board-pod-chip-remove:focus-visible { - opacity: 1; -} -.board-pod-chip-remove:hover, -.board-pod-chip-remove:focus-visible { - color: var(--text-default, var(--text-strong)); -} -.board-note-list { - display: grid; - gap: 6px; - margin-bottom: 8px; -} -.board-note-item { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; - padding: 8px 9px; - border: 1px solid var(--border-soft); - border-radius: var(--radius-sm); - background: var(--bg-subtle); - font-size: 12px; -} -.board-note-item span { - /* The note span sits next to a Remove button inside a fixed-width - ~300px popover. Without flex: 1 + min-width: 0 + overflow-wrap an - unbroken long string (URL, hash, base64) refuses to break and - pushes the popover layout sideways past its 320px boundary. - Issue #782. */ - flex: 1; - min-width: 0; - color: var(--text); - overflow-wrap: anywhere; -} -.comment-popover textarea { - min-height: 78px; - max-height: 120px; - resize: vertical; -} -.comment-popover-actions { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 10px; - margin-top: 8px; -} -.comment-popover-actions > * { - /* Buttons may carry labels longer than their natural fair-share of the - 320px popover width (Save comment, Send to chat, Sending..., Add note, - Remove). Without an explicit shrink ceiling and wrap, the row pushes - past the popover's right edge instead of breaking onto a new line. - Issue #779. */ - max-width: 100%; -} -.comment-popover-actions-end { - display: inline-flex; - align-items: center; - justify-content: flex-end; - flex-wrap: wrap; - gap: 10px; - min-width: 0; -} -.comment-popover-remove { - color: var(--red); - background: transparent; - border-color: transparent; -} -.comment-popover-remove:hover:not(:disabled) { - background: var(--red-bg); - border-color: var(--red-border); -} - -/* Right-side comment thread panel. Shown while board (comment) mode - is on; takes the place of the chat sidebar's removed Comments tab. - Floats over the artifact preview at the right edge. */ -.comment-side-panel { - position: absolute; - top: 8px; - right: 8px; - bottom: 8px; - width: 320px; - max-width: calc(100% - 16px); - background: var(--bg-panel, #fff); - border: 1px solid var(--border); - border-radius: 12px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); - display: flex; - flex-direction: column; - z-index: 30; - overflow: hidden; -} -.comment-side-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - padding: 10px 12px; - border-bottom: 1px solid var(--border); - background: var(--bg-panel, #fff); -} -.comment-side-title { - display: inline-flex; - align-items: center; - min-width: 0; - gap: 7px; - color: var(--text); - font-size: 12px; - font-weight: 700; -} -.comment-side-title span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.comment-side-close { - width: 26px; - height: 26px; - flex: 0 0 auto; - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0; - border: 1px solid transparent; - border-radius: 6px; - background: transparent; - color: var(--text-muted); - cursor: pointer; -} -.comment-side-close:hover { - background: var(--bg-subtle); - border-color: var(--border); - color: var(--text); -} -.comment-side-rail { - position: absolute; - top: 8px; - right: 8px; - bottom: 8px; - z-index: 30; - width: 42px; - display: flex; - flex-direction: column; - align-items: center; - gap: 10px; - padding: 12px 0; - border: 1px solid var(--border); - border-radius: 12px; - background: var(--bg-panel, #fff); - color: var(--text-muted); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); - cursor: pointer; -} -.comment-side-rail:hover { - color: var(--text); - border-color: var(--border-strong); - background: var(--bg-subtle); -} -.comment-side-rail span { - writing-mode: vertical-rl; - text-orientation: mixed; - font-size: 11px; - font-weight: 700; - letter-spacing: 0.04em; -} -.comment-side-rail strong { - min-width: 18px; - height: 18px; - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0 4px; - border-radius: 999px; - background: #ff5a3c; - color: #fff; - font-size: 10px; - font-weight: 700; - line-height: 1; -} -.comment-side-list { - flex: 1; - overflow-y: auto; - padding: 12px; - display: flex; - flex-direction: column; - gap: 8px; -} -.comment-side-empty { - padding: 24px 8px; - text-align: center; - color: var(--text-muted); - font-size: 12.5px; -} -.comment-side-item { - border: 1px solid transparent; - border-radius: 8px; - padding: 10px 12px; - background: transparent; - display: flex; - flex-direction: column; - gap: 4px; -} -.comment-side-item.selected { - background: #fff1ec; - border-color: #ff8c75; -} -.comment-side-item-head { - display: flex; - align-items: center; - gap: 8px; -} -.comment-side-author { - display: inline-flex; - align-items: center; - gap: 6px; - flex: 1; - min-width: 0; -} -.comment-side-author strong { - font-size: 13px; - color: var(--text); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.comment-side-avatar { - width: 20px; - height: 20px; - border-radius: 50%; - background: #0a0a0a; - color: #fff; - font-size: 11px; - display: inline-flex; - align-items: center; - justify-content: center; - font-weight: 600; - flex-shrink: 0; -} -.comment-side-time { - font-size: 11px; - color: var(--text-muted); - white-space: nowrap; -} -.comment-side-check { - width: 16px; - height: 16px; - border-radius: 4px; - border: 1.5px solid var(--border); - background: #fff; - cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0; - color: #fff; - flex-shrink: 0; -} -.comment-side-check.checked { - background: #ff5a3c; - border-color: #ff5a3c; -} -.comment-side-body { - font-size: 13px; - color: var(--text); - line-height: 1.45; - word-break: break-word; - white-space: pre-wrap; -} -.comment-side-reply { - align-self: flex-start; - background: transparent; - border: none; - padding: 0; - font-size: 12px; - color: var(--text-muted); - cursor: pointer; -} -.comment-side-reply:hover { - color: var(--text); - text-decoration: underline; -} -.comment-side-selectbar { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - border-top: 1px solid var(--border); - background: #fff1ec; -} -.comment-side-selectcount { - flex: 1; - font-size: 12px; - color: #cc3a20; -} -.comment-side-selectbar .ghost { - background: transparent; - border: none; - font-size: 12px; - color: var(--text); - cursor: pointer; - padding: 4px 6px; -} -.comment-side-selectbar .ghost:hover { - color: var(--text); - text-decoration: underline; -} -.comment-side-selectbar .primary { - display: inline-flex; - align-items: center; - gap: 4px; - background: #ff5a3c; - color: #fff; - border: 1px solid #ff5a3c; - border-radius: 6px; - padding: 5px 10px; - font-size: 12px; - cursor: pointer; -} -.comment-side-selectbar .primary:disabled { - background: #ffb9aa; - border-color: #ffb9aa; - cursor: not-allowed; -} -.comment-side-selectbar .primary:hover:not(:disabled) { - background: #e94a2d; - border-color: #e94a2d; -} - -/* Anchor for the transient comment-save toast. Centered at the top - of the artifact preview so the confirmation is visible without - covering the popover's former position. */ -.comment-toast-anchor { - position: absolute; - top: 16px; - left: 50%; - transform: translateX(-50%); - z-index: 60; - pointer-events: auto; -} - -/* Inspect panel — sibling of the comment popover. Anchored to the - right side of the preview surface. Width is fixed so layout doesn't - reflow as the user scrubs slider values; controls reserve space for - their numeric readouts. */ -.inspect-panel { - position: absolute; - top: 14px; - right: 14px; - z-index: 5; - width: 296px; - max-height: calc(100% - 28px); - overflow-y: auto; - display: flex; - flex-direction: column; - gap: 14px; - padding: 12px 14px 14px; - border: 1px solid var(--border); - border-radius: var(--radius); - background: var(--bg-panel); - box-shadow: var(--shadow-lg); - font-size: 12px; -} -.inspect-panel-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 10px; -} -.inspect-panel-head > button { - /* Without an explicit shrink-floor, a long selected-component label - in the title can squeeze the Close button so narrowly that the - "x" glyph stacks into a vertical layout on some font/zoom - combinations. Pinning flex-shrink: 0 keeps the close affordance - on a stable single-line size regardless of label length. Issue - #785. */ - flex-shrink: 0; -} -.inspect-panel-title { - display: grid; - gap: 2px; - min-width: 0; -} -.inspect-panel-title strong { - /* Long target.label values (deeply-nested component selectors) used - to spill past the title container and out of the panel because - this rule had no overflow constraint. Truncate with ellipsis; - the carries title={label} so the full string is still - visible on hover. Issue #780. */ - font-size: 13px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.inspect-panel-title code { - color: var(--text-muted); - font-size: 11px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.inspect-ancestor-notice { - display: flex; - align-items: flex-start; - gap: 8px; - padding: 10px; - color: var(--text); - border: 1px solid color-mix(in srgb, var(--accent) 28%, var(--border)); - border-radius: var(--radius-sm); - background: color-mix(in srgb, var(--accent) 8%, var(--bg-panel)); - font-size: 11px; - line-height: 1.4; -} -.inspect-ancestor-notice-icon { - display: grid; - place-items: center; - flex: 0 0 16px; - width: 16px; - height: 16px; - color: var(--accent); - border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); - border-radius: 999px; - font-size: 10px; - font-weight: 700; - line-height: 1; -} -.inspect-ancestor-notice-text { - min-width: 0; -} -.inspect-ancestor-notice-text strong { - font-weight: 600; -} -.inspect-ancestor-notice-text code { - padding: 1px 3px; - border-radius: 3px; - background: color-mix(in srgb, var(--text) 8%, transparent); - font-family: var(--font-mono); - font-size: 10px; -} - -/* MCP Copy button floats above a syntax-highlighted
; .ghost's
-   transparent background let the dark code surface bleed through and
-   made the affordance effectively invisible. Pin a solid panel
-   background here (not inline) so button.ghost:hover:not(:disabled)
-   still wins the cascade. Issue #742. */
-button.ghost.mcp-copy-btn {
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
-}
-button.ghost.mcp-copy-btn:hover:not(:disabled) {
-  background: var(--bg-subtle);
-  border-color: var(--border-strong);
-}
-.inspect-section {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-}
-.inspect-section-label {
-  color: var(--text-muted);
-  font-size: 10px;
-  font-weight: 700;
-  letter-spacing: 0.08em;
-  text-transform: uppercase;
-}
-.inspect-row {
-  display: grid;
-  grid-template-columns: 64px 1fr auto;
-  align-items: center;
-  gap: 8px;
-}
-.inspect-row > label {
-  color: var(--text-muted);
-  font-size: 11px;
-}
-.inspect-row input[type='color'] {
-  width: 28px;
-  height: 22px;
-  padding: 0;
-  border: 1px solid var(--border);
-  border-radius: 4px;
-  background: transparent;
-}
-.inspect-row input[type='text'],
-.inspect-row select {
-  min-width: 0;
-  padding: 3px 6px;
-  border: 1px solid var(--border);
-  border-radius: 4px;
-  background: var(--bg);
-  font-size: 11px;
-  font-family: inherit;
-}
-.inspect-row input[type='range'] {
-  width: 100%;
-}
-.inspect-row-value {
-  font-variant-numeric: tabular-nums;
-  color: var(--text-muted);
-  font-size: 11px;
-  min-width: 42px;
-  text-align: right;
-}
-.inspect-panel-footer {
-  display: flex;
-  justify-content: space-between;
-  gap: 8px;
-}
-.inspect-panel-error {
-  margin: 0;
-  padding: 6px 8px;
-  border: 1px solid var(--red-border);
-  border-radius: 4px;
-  background: var(--red-bg);
-  color: var(--red);
-  font-size: 11px;
-}
-
-.inspect-empty-hint-container {
-  position: absolute;
-  top: 14px;
-  right: 14px;
-  z-index: 5;
-  padding: 8px 12px;
-  border: 1px dashed var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  color: var(--text-muted);
-  font-size: 12px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 5px;
-  pointer-events: none;
-}
-
-.inspect-empty-hint-container.comment-side-panel-open {
-  right: 340px;
-  max-width: min(480px, calc(100% - 368px));
-}
-
-@media (max-width: 760px) {
-  .inspect-empty-hint-container.comment-side-panel-open {
-    left: 14px;
-    right: 14px;
-    top: 54px;
-    max-width: none;
-  }
-}
-
-.inspect-empty-hint-container button,
-.inspect-empty-hint-container .close-button {
-  pointer-events: auto;
-}
-
-.inspect-empty-hint code {
-  padding: 1px 5px;
-  border-radius: 3px;
-  background: var(--bg);
-  font-size: 11px;
-}
-
-.comments-panel {
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-  padding: 14px;
-  overflow: auto;
-}
-.comments-section {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-}
-.comments-section h3 {
-  margin: 0;
-  color: var(--text-muted);
-  font-size: 11px;
-  font-weight: 700;
-  letter-spacing: 0.08em;
-  text-transform: uppercase;
-}
-.comments-empty {
-  margin: 0;
-  padding: 10px 12px;
-  border: 1px dashed var(--border);
-  border-radius: var(--radius);
-  color: var(--text-faint);
-  font-size: 12px;
-}
-.comment-card {
-  display: grid;
-  gap: 5px;
-  padding: 9px 10px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  box-shadow: var(--shadow-xs);
-}
-.comment-card.attached {
-  border-color: var(--accent-soft);
-  background: var(--accent-tint);
-}
-.comment-card-top {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 10px;
-  min-width: 0;
-}
-.comment-card-top strong {
-  min-width: 0;
-  overflow: hidden;
-  color: var(--accent-strong);
-  font-size: 13px;
-  font-weight: 700;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.comment-card-actions {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  flex: 0 0 auto;
-}
-.comment-card p {
-  margin: 0;
-  color: var(--text);
-  font-size: 12.5px;
-  line-height: 1.35;
-  overflow-wrap: anywhere;
-}
-.comment-card-action {
-  flex: 0 0 auto;
-  padding: 3px 8px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-pill);
-  background: var(--bg);
-  color: var(--text-muted);
-  font-size: 11.5px;
-}
-.comment-card-action:hover {
-  border-color: var(--accent-soft);
-  color: var(--accent-strong);
-}
-.comment-card-action.danger {
-  color: var(--red);
-}
-.comment-card-action.danger:hover {
-  border-color: var(--red-border);
-  background: var(--red-bg);
-}
-.comment-card-meta {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 4px;
-  color: var(--text-faint);
-  font-size: 11px;
-}
-.comment-card-meta span {
-  max-width: 140px;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.comment-card-meta span:not(:last-child)::after {
-  content: '·';
-  margin-left: 4px;
-  color: var(--text-faint);
-}
-.comments-footer {
-  display: flex;
-  justify-content: flex-start;
-  padding-top: 2px;
-}
-.comments-footer .primary {
-  padding: 6px 12px;
-  font-size: 12px;
-}
-.comment-history-attachments {
-  gap: 6px;
-}
-.viewer-source {
-  margin: 0;
-  padding: 16px;
-  font-family: var(--mono);
-  font-size: 12px;
-  line-height: 1.55;
-  white-space: pre-wrap;
-  word-break: break-word;
-  color: var(--text);
-  background: var(--bg-panel);
-  min-height: 100%;
-}
-
-/* Code viewer with line numbers */
-.code-viewer {
-  background: var(--bg-panel);
-  margin: 0;
-  padding: 0;
-  display: grid;
-  grid-template-columns: auto 1fr;
-  font-family: var(--mono);
-  font-size: 12px;
-  line-height: 1.6;
-  min-height: 100%;
-}
-.code-viewer .gutter {
-  background: var(--bg);
-  color: var(--text-faint);
-  text-align: right;
-  padding: 16px 12px 16px 16px;
-  user-select: none;
-  border-right: 1px solid var(--border-soft);
-  white-space: pre;
-  font-variant-numeric: tabular-nums;
-}
-.code-viewer .lines {
-  padding: 16px 16px 16px 18px;
-  white-space: pre;
-  overflow-x: auto;
-  color: var(--text);
-}
-
-.viewer-empty {
-  padding: 48px 24px;
-  text-align: center;
-  color: var(--text-muted);
-  font-size: 13px;
-}
-
-/* Shown when a .jsx/.tsx module (loaded by a sibling HTML entry) is opened on
-   its own — it has no standalone preview, so we point at the page(s) that
-   render it. Issue #2744. */
-.viewer-module-pointer {
-  max-width: 460px;
-  margin: 64px auto;
-  padding: 0 24px;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 10px;
-  text-align: center;
-  color: var(--text-muted);
-}
-.viewer-module-pointer__title {
-  margin: 0;
-  font-size: 15px;
-  font-weight: 600;
-  color: var(--text);
-}
-.viewer-module-pointer__body {
-  margin: 0;
-  font-size: 13px;
-  line-height: 1.5;
-}
-.viewer-module-pointer__cta {
-  margin: 4px 0 0;
-  font-size: 13px;
-  font-weight: 500;
-  color: var(--text);
-}
-.viewer-module-pointer__entries {
-  list-style: none;
-  margin: 6px 0 0;
-  padding: 0;
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-  width: 100%;
-  align-items: center;
-}
-.viewer-module-pointer__link {
-  display: inline-flex;
-  align-items: center;
-  gap: 7px;
-  padding: 7px 12px;
-  border: 1px solid var(--border);
-  border-radius: 8px;
-  background: var(--bg-subtle);
-  color: var(--text);
-  font-size: 13px;
-  cursor: pointer;
-  transition:
-    background 200ms cubic-bezier(0.23, 1, 0.32, 1),
-    border-color 200ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-.viewer-module-pointer__link:hover {
-  background: var(--bg-muted);
-  border-color: var(--border-strong);
-}
-.viewer-module-pointer__link:disabled {
-  cursor: default;
-  opacity: 0.6;
-}
-/* Compact horizontal variant for the upload-failure banner: the message
-   sits inline next to a dismiss button so the user can clear the stale
-   notice without changing tabs. Issue #786. */
-.viewer-empty.viewer-empty-dismissible {
-  padding: 12px 16px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 12px;
-  text-align: left;
-}
-.document-preview {
-  max-width: 860px;
-  margin: 0 auto;
-  padding: 32px 40px 56px;
-  color: var(--text);
-}
-.document-preview h2 {
-  margin: 0 0 24px;
-  font-size: 20px;
-  line-height: 1.25;
-}
-.document-preview section {
-  border-top: 1px solid var(--border-soft);
-  padding-top: 18px;
-  margin-top: 18px;
-}
-.document-preview h3 {
-  margin: 0 0 12px;
-  font-size: 13px;
-  color: var(--text-muted);
-  font-weight: 600;
-}
-.document-preview p {
-  margin: 0 0 8px;
-  font-size: 14px;
-  line-height: 1.65;
-  white-space: pre-wrap;
-}
-.markdown-rendered {
-  max-width: 900px;
-  margin: 0 auto;
-  padding: 24px 28px 40px;
-  color: var(--text);
-  line-height: 1.65;
-  white-space: normal;
-}
-.markdown-status {
-  margin: 12px auto 0;
-  max-width: 900px;
-  padding: 8px 10px;
-  border: 1px solid var(--border-soft);
-  border-radius: 8px;
-  background: var(--bg-panel);
-  color: var(--text-muted);
-  font-size: 12px;
-}
-.markdown-status-error {
-  border-color: color-mix(in oklab, var(--danger, #d04b4b) 45%, var(--border-soft));
-  color: var(--danger, #d04b4b);
-}
-.markdown-rendered h1,
-.markdown-rendered h2,
-.markdown-rendered h3,
-.markdown-rendered h4,
-.markdown-rendered h5,
-.markdown-rendered h6 {
-  margin: 20px 0 10px;
-  line-height: 1.25;
-}
-.markdown-rendered p { margin: 10px 0; }
-.markdown-rendered ul,
-.markdown-rendered ol {
-  margin: 10px 0;
-  padding-left: 24px;
-}
-.markdown-rendered blockquote {
-  margin: 12px 0;
-  padding: 8px 12px;
-  border-left: 3px solid var(--border);
-  color: var(--text-muted);
-  background: var(--bg-panel);
-}
-.markdown-code-block {
-  position: relative;
-}
-.markdown-code-copy {
-  position: absolute;
-  top: 18px;
-  right: 18px;
-  z-index: 1;
-  border: 1px solid var(--border-soft);
-  border-radius: 999px;
-  background: color-mix(in oklab, var(--bg-panel) 92%, black 8%);
-  color: var(--text-muted);
-  font: inherit;
-  font-size: 11px;
-  font-weight: 600;
-  line-height: 1;
-  padding: 7px 10px;
-  cursor: pointer;
-  opacity: 0;
-  transform: translateY(-2px);
-  transition:
-    opacity 120ms ease,
-    transform 120ms ease,
-    color 120ms ease,
-    border-color 120ms ease,
-    background 120ms ease;
-}
-.markdown-code-block:hover .markdown-code-copy,
-.markdown-code-block:focus-within .markdown-code-copy {
-  opacity: 1;
-  transform: translateY(0);
-}
-@media (hover: none) {
-  .markdown-code-copy {
-    opacity: 1;
-    transform: translateY(0);
-  }
-}
-.markdown-code-copy:hover,
-.markdown-code-copy:focus-visible {
-  color: var(--text);
-  border-color: var(--border);
-  background: var(--bg-elevated, var(--bg));
-}
-.markdown-code-toast {
-  position: absolute;
-  top: 18px;
-  right: 82px;
-  z-index: 1;
-  border-radius: 999px;
-  background: color-mix(in oklab, var(--accent) 18%, var(--bg-panel));
-  color: var(--text);
-  font-size: 11px;
-  font-weight: 600;
-  line-height: 1;
-  padding: 7px 10px;
-  box-shadow: 0 10px 26px color-mix(in oklab, var(--accent) 18%, transparent);
-}
-.markdown-rendered pre {
-  margin: 12px 0;
-  background: var(--bg-panel);
-  border: 1px solid var(--border-soft);
-  border-radius: 8px;
-  padding: 40px 12px 12px;
-  overflow: auto;
-}
-.markdown-rendered code {
-  font-family: var(--mono);
-  font-size: 12px;
-}
-.markdown-rendered a { color: var(--accent); }
-.image-body {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 24px;
-}
-.image-body img {
-  max-width: 100%;
-  max-height: 100%;
-  object-fit: contain;
-  background: white;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-}
-.sketch-preview {
-  width: 100%;
-  height: 100%;
-  min-height: 0;
-}
-.sketch-preview svg {
-  width: 100%;
-  height: 100%;
-  display: block;
-  background: white;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-}
-.sketch-preview.loading {
-  background:
-    radial-gradient(circle at 8px 8px, #d7d4ce 1px, transparent 1px),
-    #f7f5f1;
-  background-size: 16px 16px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-}
-.sketch-preview-empty-mark path {
-  stroke: #bfbcb6;
-  stroke-width: 6;
-  stroke-linecap: round;
-  fill: none;
-}
-
-/* Sketch editor */
-.sketch-editor {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  min-height: 0;
-  background: var(--bg);
-}
-.sketch-toolbar {
-  display: flex;
-  align-items: center;
-  gap: 4px;
-  padding: 8px 12px;
-  border-bottom: 1px solid var(--border);
-  background: var(--bg-panel);
-  flex-wrap: wrap;
-}
-.sketch-tool {
-  padding: 6px 10px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: 6px;
-  font-size: 14px;
-  line-height: 1;
-  min-width: 32px;
-}
-.sketch-tool:hover { background: var(--bg-subtle); }
-.sketch-tool.active { background: var(--accent); color: white; border-color: var(--accent); }
-.sketch-divider { width: 1px; height: 20px; background: var(--border); margin: 0 4px; }
-.sketch-color { width: 32px; height: 28px; padding: 0; border: 1px solid var(--border); border-radius: 6px; cursor: pointer; }
-.sketch-size { width: 80px; background: transparent; border: none; }
-.sketch-spacer { flex: 1; }
-.sketch-canvas-wrap { flex: 1; min-height: 0; position: relative; background: var(--bg); }
-.sketch-canvas-wrap canvas { display: block; cursor: crosshair; }
-
-/* ===========================================================
-   Streaming chat: assistant message header, prose, thinking,
-   tool cards, status pills, grouped action card.
-   =========================================================== */
-.chat-empty-wrap {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  gap: 18px;
-  padding: 24px 8px;
-  min-height: 100%;
-}
-.chat-empty {
-  color: var(--text-muted);
-  font-size: 13px;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  text-align: center;
-  gap: 6px;
-  max-width: 44ch;
-}
-.chat-empty-title { font-weight: 600; color: var(--text-strong); font-size: 15px; }
-.chat-empty-hint { line-height: 1.6; }
-
-.chat-examples {
-  display: flex;
-  flex-direction: column;
-  gap: 10px;
-  width: 100%;
-  max-width: 520px;
-}
-
-.chat-example {
-  position: relative;
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  width: 100%;
-  padding: 14px 16px;
-  text-align: left;
-  cursor: pointer;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  box-shadow: var(--shadow-xs);
-  color: var(--text);
-  font: inherit;
-  overflow: hidden;
-  transition:
-    transform 160ms ease,
-    border-color 160ms ease,
-    box-shadow 160ms ease,
-    background-color 160ms ease;
-  opacity: 0;
-  animation: chat-example-in 380ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
-}
-.chat-example::before {
-  content: '';
-  position: absolute;
-  inset: 0;
-  background: linear-gradient(
-    135deg,
-    var(--accent-tint) 0%,
-    transparent 55%
-  );
-  opacity: 0;
-  transition: opacity 200ms ease;
-  pointer-events: none;
-}
-.chat-example:hover {
-  transform: translateY(-2px);
-  border-color: var(--accent);
-  box-shadow: var(--shadow-md);
-  background: var(--bg-panel);
-}
-.chat-example:hover::before {
-  opacity: 1;
-}
-.chat-example:active {
-  transform: translateY(0);
-}
-.chat-example:focus-visible {
-  outline: 2px solid var(--accent);
-  outline-offset: 2px;
-}
-
-.chat-example-icon {
-  position: relative;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  width: 36px;
-  height: 36px;
-  flex-shrink: 0;
-  border-radius: var(--radius-sm);
-  background: var(--accent);
-  color: #fff;
-  font-size: 18px;
-  line-height: 1;
-  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04), inset 0 -1px 0 rgba(0, 0, 0, 0.08);
-}
-.chat-example-body {
-  position: relative;
-  display: flex;
-  flex-direction: column;
-  gap: 3px;
-  min-width: 0;
-  flex: 1;
-}
-.chat-example-head {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  flex-wrap: wrap;
-}
-.chat-example-title {
-  font-weight: 600;
-  color: var(--text-strong);
-  font-size: 13.5px;
-}
-.chat-example-tag {
-  font-family: var(--mono);
-  font-size: 10.5px;
-  letter-spacing: 0.04em;
-  text-transform: uppercase;
-  color: var(--accent);
-  background: var(--accent-tint);
-  border: 1px solid var(--accent-soft);
-  padding: 1px 7px;
-  border-radius: var(--radius-pill);
-  line-height: 1.5;
-  white-space: nowrap;
-}
-.chat-example-prompt {
-  color: var(--text-muted);
-  font-size: 12.5px;
-  line-height: 1.5;
-  display: -webkit-box;
-  -webkit-line-clamp: 2;
-  -webkit-box-orient: vertical;
-  overflow: hidden;
-}
-.chat-example-cta {
-  position: relative;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  width: 24px;
-  height: 24px;
-  flex-shrink: 0;
-  border-radius: 50%;
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-  font-size: 13px;
-  transition: background-color 160ms ease, color 160ms ease, transform 160ms ease;
-}
-.chat-example:hover .chat-example-cta {
-  background: var(--accent);
-  color: #fff;
-  transform: translateX(2px);
-}
-
-@keyframes chat-example-in {
-  from {
-    opacity: 0;
-    transform: translateY(8px);
-  }
-  to {
-    opacity: 1;
-    transform: translateY(0);
-  }
-}
-
-/* Connect-your-repo CTA in the empty chat state. Shares the example cards'
-   sizing and accent system but stands apart with a tinted fill so it reads as
-   guidance rather than another starter prompt. */
-.chat-connect-repo {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  width: 100%;
-  max-width: 520px;
-  padding: 14px 16px;
-  background: var(--accent-tint);
-  border: 1px solid var(--accent-soft);
-  border-radius: var(--radius);
-  box-shadow: var(--shadow-xs);
-  opacity: 0;
-  animation: chat-example-in 380ms cubic-bezier(0.22, 1, 0.36, 1) 220ms forwards;
-}
-.chat-connect-repo-icon {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  width: 36px;
-  height: 36px;
-  flex-shrink: 0;
-  border-radius: var(--radius-sm);
-  background: var(--accent);
-  color: #fff;
-}
-.chat-connect-repo-body {
-  display: flex;
-  flex-direction: column;
-  gap: 3px;
-  min-width: 0;
-  flex: 1;
-}
-.chat-connect-repo-title {
-  font-weight: 600;
-  color: var(--text-strong);
-  font-size: 13.5px;
-}
-.chat-connect-repo-text {
-  color: var(--text-muted);
-  font-size: 12.5px;
-  line-height: 1.5;
-}
-.chat-connect-repo button {
-  flex-shrink: 0;
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  white-space: nowrap;
-}
-
-.assistant-header {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  font-size: 11.5px;
-  color: var(--text-muted);
-  margin-bottom: 8px;
-}
-.assistant-header .dot {
-  width: 7px;
-  height: 7px;
-  border-radius: 50%;
-  background: var(--text-muted);
-}
-.assistant-header .dot[data-active="true"] {
-  background: var(--accent);
-  animation: pulse 1.2s ease-in-out infinite;
-}
-.assistant-label { font-weight: 600; color: var(--text-strong); font-size: 12.5px; }
-.assistant-stats { font-variant-numeric: tabular-nums; margin-left: auto; }
-
-.assistant-flow {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-  min-width: 0;
-}
-.prose-block { line-height: 1.6; color: var(--text); }
-.prose-block .md-p { margin: 0; }
-.prose-block .md-p + .md-p { margin-top: 8px; }
-.prose-block .md-h { margin: 10px 0 4px; line-height: 1.3; font-weight: 600; }
-.prose-block .md-h1 { font-size: 18px; }
-.prose-block .md-h2 { font-size: 16px; }
-.prose-block .md-h3 { font-size: 14px; }
-.prose-block .md-h4 { font-size: 13px; }
-.prose-block .md-ul, .prose-block .md-ol { margin: 4px 0; padding-left: 20px; }
-.prose-block .md-ul li, .prose-block .md-ol li { margin: 2px 0; }
-.prose-block .md-inline-code {
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: 4px;
-  padding: 1px 5px;
-  font-family: var(--mono);
-  font-size: 0.92em;
-}
-.prose-block .md-code {
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-  padding: 10px 12px;
-  margin: 6px 0;
-  overflow-x: auto;
-  font-size: 12px;
-  line-height: 1.5;
-}
-.prose-block .md-code code { font-family: var(--mono); }
-.prose-block .md-link {
-  color: var(--accent);
-  text-decoration: underline;
-  word-break: break-word;
-  overflow-wrap: anywhere;
-}
-.prose-block .md-link-bare {
-  /* Long bare URLs (OAuth flows etc.) need to wrap mid-string so they
-     don't escape the chat column. break-word is enough for most agents,
-     but `anywhere` covers query strings with no spaces. */
-  word-break: break-all;
-  overflow-wrap: anywhere;
-}
-.prose-block .md-hr {
-  border: none;
-  border-top: 1px solid var(--border);
-  margin: 10px 0;
-}
-/* GFM pipe tables. Rules are intentionally unscoped so both the chat path
-   (`.prose-block`) and the artifact preview (`.markdown-rendered`) get
-   the same treatment. Horizontal scroll lives on the wrapper so the
-   inner  can keep `display: table; width: 100%` and distribute
-   column widths across the container instead of collapsing to content. */
-.md-table-wrap {
-  display: block;
-  overflow-x: auto;
-  max-width: 100%;
-  margin: 12px 0;
-  border: 1px solid var(--border);
-  border-radius: 6px;
-}
-.md-table {
-  width: 100%;
-  border-collapse: collapse;
-  font-size: 0.95em;
-}
-.md-table th,
-.md-table td {
-  padding: 8px 12px;
-  text-align: left;
-  vertical-align: top;
-  border-bottom: 1px solid var(--border);
-}
-.md-table th + th,
-.md-table td + td {
-  border-left: 1px solid var(--border);
-}
-.md-table thead th {
-  background: var(--bg-panel);
-  color: var(--text);
-  font-weight: 600;
-  border-bottom-color: var(--border);
-}
-.md-table tbody tr:last-child td {
-  border-bottom: none;
-}
-.md-table tbody tr:nth-child(even) td {
-  background: color-mix(in oklab, var(--bg-panel) 40%, transparent);
-}
-.op-waiting {
-  font-size: 12px;
-  color: var(--text-muted);
-  font-style: italic;
-}
-
-/* Thinking blocks */
-.thinking-block {
-  background: rgba(108, 58, 166, 0.04);
-  border: 1px solid rgba(108, 58, 166, 0.16);
-  border-radius: var(--radius-sm);
-}
-.thinking-toggle {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  width: 100%;
-  padding: 7px 12px;
-  background: transparent;
-  border: none;
-  text-align: left;
-  font-size: 12px;
-  color: var(--text);
-  border-radius: var(--radius-sm);
-}
-.thinking-toggle:hover { background: rgba(108, 58, 166, 0.05); border-color: transparent; }
-.thinking-icon { color: var(--purple); }
-.thinking-label { font-weight: 500; }
-.thinking-preview {
-  flex: 1;
-  color: var(--text-muted);
-  font-style: italic;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.thinking-chev { color: var(--text-muted); font-size: 10px; }
-.thinking-body {
-  margin: 0;
-  padding: 0 14px 12px 14px;
-  font-family: var(--mono);
-  font-size: 11px;
-  line-height: 1.55;
-  color: var(--text-muted);
-  white-space: pre-wrap;
-  overflow-wrap: anywhere;
-}
-
-/* Status pills */
-.status-pill {
-  display: inline-flex;
-  align-self: flex-start;
-  align-items: flex-start;
-  flex-wrap: wrap;
-  gap: 6px;
-  max-width: 100%;
-  padding: 3px 12px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-pill);
-  font-size: 11.5px;
-  color: var(--text-muted);
-}
-.status-label { letter-spacing: 0.02em; }
-.status-detail {
-  min-width: 0;
-  color: var(--text);
-  overflow-wrap: anywhere;
-}
-
-/* Grouped tool / action card — the collapsible pill from screenshot 9 */
-.action-card {
-  display: flex;
-  flex-direction: column;
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  overflow: hidden;
-}
-.action-card-toggle {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  width: 100%;
-  padding: 8px 12px;
-  background: transparent;
-  border: none;
-  text-align: left;
-  font-size: 12.5px;
-  color: var(--text);
-  cursor: pointer;
-  border-radius: var(--radius);
-}
-.action-card-toggle:hover { background: var(--bg-subtle); border-color: transparent; }
-.action-card-toggle .ico {
-  width: 20px; height: 20px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  border-radius: 4px;
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-  font-size: 12px;
-  font-family: var(--mono);
-  flex-shrink: 0;
-}
-.action-card-toggle .summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.action-card-toggle .summary strong { font-weight: 500; }
-.action-card-toggle .chev { color: var(--text-faint); font-size: 10px; flex-shrink: 0; }
-.action-card-toggle.running .ico { animation: pulse 1.6s ease-in-out infinite; background: var(--purple-bg); color: var(--purple); }
-.action-card-body {
-  padding: 0 12px 10px;
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-  border-top: 1px solid var(--border-soft);
-}
-.action-card-body > .op-card { border-color: transparent; box-shadow: none; padding: 4px 0; }
-.action-card-body > .op-card .op-card-head { padding: 6px 0; }
-
-/* Tool / operation cards — single, ungrouped */
-.op-card {
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  overflow: hidden;
-  box-shadow: var(--shadow-xs);
-  min-width: 0;
-  max-width: 100%;
-}
-.op-card-head {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  padding: 8px 12px;
-  font-size: 12.5px;
-  flex-wrap: wrap;
-}
-.op-icon {
-  width: 22px;
-  height: 22px;
-  border-radius: 6px;
-  background: var(--bg-subtle);
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  color: var(--text-muted);
-  font-size: 12px;
-  font-family: var(--mono);
-  flex-shrink: 0;
-}
-.op-icon-write { background: var(--green-bg); color: var(--green); }
-.op-icon-edit  { background: var(--amber-bg); color: var(--amber); }
-.op-icon-read  { background: var(--blue-bg); color: var(--blue); }
-.op-title { font-weight: 500; }
-
-/*
- * Inline rendering of Claude's AskUserQuestion tool. Question text reads
- * like a normal assistant message, options live as a row of chip buttons
- * the user picks from. Once an answer ships (or the user has moved past
- * the turn) the card locks into a read only state: chips remain visible
- * but unclickable so the user can see what was asked.
- */
-.op-ask-question .op-card-head { padding-bottom: 4px; }
-.op-ask-question-body {
-  display: flex;
-  flex-direction: column;
-  gap: 14px;
-  padding: 4px 12px 12px;
-}
-.op-ask-question-field {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-}
-.op-ask-question-header {
-  font-size: 10.5px;
-  font-weight: 600;
-  letter-spacing: 0.06em;
-  text-transform: uppercase;
-  color: var(--text-muted);
-}
-.op-ask-question-prompt {
-  font-size: 13px;
-  line-height: 1.45;
-  color: var(--text);
-}
-.op-ask-question-options {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-}
-.op-ask-question-option {
-  display: inline-flex;
-  flex-direction: column;
-  align-items: flex-start;
-  gap: 2px;
-  padding: 7px 11px;
-  border: 1px solid var(--border);
-  border-radius: 8px;
-  background: var(--bg-panel);
-  color: var(--text);
-  font-size: 12.5px;
-  font-family: inherit;
-  cursor: pointer;
-  text-align: left;
-  transition: border-color 120ms ease, background 120ms ease;
-}
-.op-ask-question-option:hover { border-color: var(--accent); }
-.op-ask-question-option.on {
-  border-color: var(--accent);
-  background: var(--accent-bg, var(--bg-subtle));
-}
-.op-ask-question-option:disabled {
-  cursor: default;
-  opacity: 0.85;
-}
-.op-ask-question-option-label { font-weight: 500; }
-.op-ask-question-option-desc {
-  font-size: 11px;
-  color: var(--text-muted);
-  white-space: normal;
-}
-.op-ask-question-foot {
-  display: flex;
-  justify-content: flex-end;
-  padding: 4px 12px 12px;
-}
-/* Match the composer's Send button styling so the two actions read as
- * the same control family. The composer-send is bordered with the
- * accent color and uses the same padding/typography; we drop the icon
- * by skipping the gap/svg children entirely. */
-.op-ask-question-submit {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  background: var(--accent);
-  border: 1px solid var(--accent);
-  border-radius: var(--radius);
-  color: white;
-  font-weight: 500;
-  padding: 4px 14px;
-  font-size: 12.5px;
-  cursor: pointer;
-}
-.op-ask-question-submit:hover:not(:disabled) {
-  background: var(--accent-hover);
-  border-color: var(--accent-hover);
-}
-.op-ask-question-submit:disabled {
-  opacity: 0.5;
-  cursor: default;
-}
-/*
- * Locked card state: unchosen options fade back into the card surface
- * while the user's pick keeps its accent outline so the answer stays
- * legible after submission. Without the `:not(.on)` qualifier the lock
- * would wipe the selected state and the card would look unanswered.
- */
-.op-ask-question-locked .op-ask-question-option:not(.on) {
-  border-color: var(--border-soft);
-  background: transparent;
-  color: var(--text-muted);
-}
-.op-ask-question-locked .op-ask-question-option.on {
-  border-color: var(--accent);
-  background: var(--accent-bg, var(--bg-subtle));
-  cursor: default;
-}
-.op-ask-question-locked .op-ask-question-option.on .op-ask-question-option-desc {
-  color: var(--text-muted);
-}
-.op-meta { color: var(--text-muted); font-size: 11.5px; }
-.op-desc { font-style: italic; }
-.op-path {
-  background: var(--bg-subtle);
-  padding: 1px 6px;
-  border-radius: 4px;
-  font-size: 11px;
-  font-family: var(--mono);
-  color: var(--text);
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  max-width: min(220px, 100%);
-}
-.op-status {
-  margin-left: auto;
-  font-size: 10.5px;
-  padding: 2px 9px;
-  border-radius: var(--radius-pill);
-  letter-spacing: 0.02em;
-  border: 1px solid var(--border);
-  color: var(--text-muted);
-  background: var(--bg-subtle);
-}
-.op-status-running {
-  border-color: var(--purple-border);
-  background: var(--purple-bg);
-  color: var(--purple);
-  animation: pulse 1.6s ease-in-out infinite;
-}
-/*
- * Static waiting-for-input variant. Same purple chrome as `op-status-running`
- * but no pulse: an AskUserQuestion card is parked until the user clicks, not
- * actively working, so the animation is misleading.
- */
-.op-status-awaiting {
-  border-color: var(--purple-border);
-  background: var(--purple-bg);
-  color: var(--purple);
-}
-.op-status-ok { border-color: var(--green-border); background: var(--green-bg); color: var(--green); }
-.op-status-error { border-color: var(--red-border); background: var(--red-bg); color: var(--red); }
-.op-toggle {
-  font-size: 10.5px;
-  padding: 2px 8px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-pill);
-  background: transparent;
-  color: var(--text-muted);
-}
-.op-open {
-  font-size: 10.5px;
-  padding: 2px 9px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-pill);
-  background: transparent;
-  color: var(--text);
-  cursor: pointer;
-}
-.op-open:hover {
-  background: var(--bg-subtle);
-  border-color: var(--accent);
-  color: var(--accent);
-}
-
-.produced-files {
-  margin-top: 4px;
-  padding: 12px 14px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-}
-.produced-files-label {
-  font-size: 11px;
-  text-transform: uppercase;
-  letter-spacing: 0.06em;
-  color: var(--text-muted);
-  margin-bottom: 8px;
-  font-weight: 600;
-}
-.produced-files-list { display: flex; flex-direction: column; gap: 4px; }
-.produced-file {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  padding: 6px 8px;
-  border-radius: 6px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  font-size: 12px;
-}
-.produced-file-icon { width: 22px; text-align: center; color: var(--text-muted); }
-.produced-file-name {
-  flex: 1;
-  min-width: 0;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  font-family: var(--mono);
-}
-.produced-file-size {
-  font-size: 10.5px;
-  color: var(--text-muted);
-  font-variant-numeric: tabular-nums;
-}
-.produced-file-actions { display: flex; gap: 6px; }
-.produced-file-actions .ghost,
-.produced-file-actions .ghost-link {
-  font-size: 11px;
-  padding: 3px 9px;
-}
-
-.plugin-action-panel {
-  margin-top: 4px;
-  padding: 12px 14px;
-  border: 1px solid var(--accent-soft);
-  border-radius: var(--radius);
-  background: color-mix(in srgb, var(--accent) 5%, var(--bg-panel));
-  box-shadow: var(--shadow-xs);
-}
-.plugin-action-panel__head {
-  display: flex;
-  align-items: flex-start;
-  gap: 9px;
-  margin-bottom: 10px;
-}
-.plugin-action-panel__icon {
-  width: 25px;
-  height: 25px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  border-radius: 7px;
-  background: var(--accent-soft);
-  color: var(--accent);
-  flex-shrink: 0;
-}
-.plugin-action-panel__title {
-  font-weight: 650;
-  font-size: 13px;
-  color: var(--text);
-}
-.plugin-action-panel__subtitle {
-  margin-top: 2px;
-  font-size: 11.5px;
-  line-height: 1.35;
-  color: var(--text-muted);
-}
-.plugin-action-panel__list {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-}
-.plugin-action-card {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-  min-width: 0;
-  padding-top: 8px;
-  border-top: 1px solid var(--border-soft);
-}
-.plugin-action-panel__list .plugin-action-card:first-child {
-  padding-top: 0;
-  border-top: none;
-}
-.plugin-action-card__main {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  min-width: 0;
-}
-.plugin-action-card__folder-icon {
-  width: 23px;
-  height: 23px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  border-radius: 6px;
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-  flex-shrink: 0;
-}
-.plugin-action-card__copy {
-  display: flex;
-  flex-wrap: wrap;
-  align-items: baseline;
-  gap: 5px 8px;
-  min-width: 0;
-  font-size: 11.5px;
-  color: var(--text-muted);
-}
-.plugin-action-card__path {
-  max-width: 220px;
-  padding: 1px 6px;
-  border-radius: 4px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border-soft);
-  color: var(--text);
-  font-family: var(--mono);
-  font-size: 11px;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.plugin-action-card__actions {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-}
-.plugin-action-button {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  gap: 5px;
-  min-height: 28px;
-  max-width: 100%;
-  padding: 5px 10px;
-  border: 1px solid var(--border);
-  border-radius: 7px;
-  background: var(--bg-panel);
-  color: var(--text);
-  font-size: 11.5px;
-  font-weight: 550;
-  cursor: pointer;
-}
-.plugin-action-button:hover:not(:disabled) {
-  border-color: var(--accent);
-  color: var(--accent);
-}
-.plugin-action-button:disabled {
-  opacity: 0.58;
-  cursor: default;
-}
-.plugin-action-button--primary {
-  border-color: var(--accent);
-  background: var(--accent);
-  color: #fff;
-}
-.plugin-action-button--primary:hover:not(:disabled) {
-  color: #fff;
-  filter: brightness(0.98);
-}
-.plugin-action-card__notice {
-  padding: 6px 8px;
-  border-radius: 6px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border-soft);
-  color: var(--text-muted);
-  font-size: 11.5px;
-  line-height: 1.35;
-}
-
-/* "Files this turn" summary — derived from Read/Write/Edit tool_use events
-   so users can scan every file the agent touched without expanding tool-
-   group disclosures. Compact pill while streaming, full list once done. */
-.file-ops {
-  display: flex;
-  flex-direction: column;
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  overflow: hidden;
-}
-.file-ops.is-streaming {
-  border-style: dashed;
-  background: var(--bg-subtle);
-}
-.file-ops-toggle {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  width: 100%;
-  padding: 7px 10px;
-  background: transparent;
-  border: 0;
-  cursor: pointer;
-  text-align: left;
-  font-size: 12px;
-  color: var(--text);
-  min-width: 0;
-}
-.file-ops-toggle:hover { background: var(--bg-subtle); }
-.file-ops-icon { display: inline-flex; color: var(--text-muted); }
-.file-ops-label {
-  font-size: 10.5px;
-  text-transform: uppercase;
-  letter-spacing: 0.06em;
-  color: var(--text-muted);
-  font-weight: 600;
-}
-.file-ops-summary-line {
-  flex: 1;
-  min-width: 0;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  font-variant-numeric: tabular-nums;
-  color: var(--text-muted);
-}
-.file-ops-count {
-  font-size: 10.5px;
-  font-weight: 600;
-  font-variant-numeric: tabular-nums;
-  color: var(--text-muted);
-  padding: 1px 6px;
-  border-radius: 8px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-}
-.file-ops.is-streaming .file-ops-count {
-  color: var(--accent);
-  border-color: color-mix(in oklab, var(--accent) 30%, var(--border));
-}
-.file-ops-chev { display: inline-flex; color: var(--text-muted); }
-.file-ops-list {
-  list-style: none;
-  margin: 0;
-  padding: 4px 6px 8px;
-  display: flex;
-  flex-direction: column;
-  gap: 3px;
-  border-top: 1px solid var(--border-soft, var(--border));
-}
-.file-ops-row {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  padding: 5px 8px;
-  border-radius: 6px;
-  font-size: 12px;
-  min-width: 0;
-}
-.file-ops-row:hover { background: var(--bg-subtle); }
-.file-ops-row--running { color: var(--text); }
-.file-ops-row--error { color: var(--text); }
-.file-ops-row-badges { display: inline-flex; gap: 3px; flex-shrink: 0; }
-.file-ops-badge {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  gap: 2px;
-  min-width: 18px;
-  height: 18px;
-  padding: 0 4px;
-  border-radius: 4px;
-  font-size: 10px;
-  font-weight: 700;
-  font-family: var(--mono);
-  letter-spacing: 0;
-  border: 1px solid var(--border);
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-}
-.file-ops-badge-count {
-  font-size: 9px;
-  font-weight: 600;
-  font-variant-numeric: tabular-nums;
-  opacity: 0.85;
-}
-.file-ops-badge--read {
-  color: color-mix(in oklab, var(--accent) 80%, var(--text));
-  border-color: color-mix(in oklab, var(--accent) 30%, var(--border));
-}
-.file-ops-badge--write {
-  color: #2e7d32;
-  border-color: color-mix(in oklab, #2e7d32 35%, var(--border));
-  background: color-mix(in oklab, #2e7d32 8%, var(--bg-subtle));
-}
-.file-ops-badge--edit {
-  color: #b26500;
-  border-color: color-mix(in oklab, #b26500 35%, var(--border));
-  background: color-mix(in oklab, #b26500 8%, var(--bg-subtle));
-}
-.file-ops-row-path {
-  flex: 1;
-  min-width: 0;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  font-family: var(--mono);
-  font-size: 11.5px;
-  background: transparent;
-  padding: 0;
-}
-.file-ops-row-count {
-  font-size: 10.5px;
-  color: var(--text-muted);
-  font-variant-numeric: tabular-nums;
-  flex-shrink: 0;
-}
-.file-ops-row-status {
-  font-size: 10.5px;
-  text-transform: uppercase;
-  letter-spacing: 0.04em;
-  font-weight: 600;
-  padding: 1px 6px;
-  border-radius: 8px;
-  flex-shrink: 0;
-}
-.file-ops-row-status--running {
-  color: var(--accent);
-  background: color-mix(in oklab, var(--accent) 10%, transparent);
-}
-.file-ops-row-status--error {
-  color: #c0392b;
-  background: color-mix(in oklab, #c0392b 12%, transparent);
-}
-.file-ops-row-open {
-  font-size: 11px;
-  padding: 3px 9px;
-  background: transparent;
-  border: 1px solid var(--border);
-  border-radius: 6px;
-  color: var(--text);
-  cursor: pointer;
-  flex-shrink: 0;
-}
-.file-ops-row-open:hover {
-  border-color: var(--accent);
-  color: var(--accent);
-}
-
-.op-bash .op-command,
-.op-bash .op-output {
-  margin: 0;
-  padding: 8px 12px;
-  background: #1c1b1a;
-  color: #f0eee9;
-  font-family: var(--mono);
-  font-size: 11px;
-  line-height: 1.55;
-  white-space: pre-wrap;
-  overflow-wrap: anywhere;
-  max-height: 220px;
-  overflow-y: auto;
-}
-.op-bash .op-output { background: #2a2926; }
-
-.op-todo .todo-list {
-  list-style: none;
-  margin: 0;
-  padding: 6px 8px 10px;
-  display: flex;
-  flex-direction: column;
-  gap: 1px;
-  font-size: 12px;
-}
-.todo-item {
-  display: flex;
-  align-items: flex-start;
-  gap: 10px;
-  padding: 5px 8px;
-  line-height: 1.45;
-  border-radius: var(--radius-sm);
-  border: 1px solid transparent;
-  transition: background-color 120ms ease, border-color 120ms ease;
-}
-.todo-check {
-  width: 16px;
-  flex-shrink: 0;
-  color: var(--text-soft);
-  font-family: var(--mono);
-  text-align: center;
-  line-height: 1.45;
-}
-.todo-text { color: var(--text); }
-
-/* Pending — quietly waiting */
-.todo-pending .todo-check { color: var(--text-faint); }
-.todo-pending .todo-text { color: var(--text-muted); }
-
-/* In progress — the only row that should pop */
-.todo-in_progress {
-  background: color-mix(in srgb, var(--accent) 10%, var(--bg-panel));
-  border-color: color-mix(in srgb, var(--accent) 28%, transparent);
-}
-.todo-in_progress .todo-check { color: var(--accent); }
-.todo-in_progress .todo-text { color: var(--text-strong); font-weight: 600; }
-
-/* Completed — settled, but still readable */
-.todo-completed .todo-check { color: var(--green); }
-.todo-completed .todo-text {
-  text-decoration: line-through;
-  text-decoration-color: color-mix(in srgb, var(--text-muted) 60%, transparent);
-  color: var(--text-muted);
-}
-
-/* Stopped — terminal fallback for a run that ended mid-task */
-.todo-stopped {
-  background: color-mix(in srgb, var(--red) 8%, var(--bg-panel));
-  border-color: color-mix(in srgb, var(--red) 24%, transparent);
-}
-.todo-stopped .todo-check { color: var(--red); font-weight: 700; }
-.todo-stopped .todo-text { color: var(--text); }
-
-/* Composer extras */
-.composer.drag-active {
-  outline: 2px dashed var(--accent);
-  outline-offset: -4px;
-}
-
-/* Present / Share menus */
-.present-wrap { position: relative; display: inline-block; }
-.present-trigger .caret {
-  margin-left: 4px;
-  font-size: 10px;
-  opacity: 0.7;
-}
-.present-menu {
-  position: absolute;
-  top: calc(100% + 6px);
-  right: 0;
-  z-index: 60;
-  min-width: 168px;
-  padding: 4px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  box-shadow: var(--shadow-md);
-  display: flex;
-  flex-direction: column;
-}
-.present-menu button {
-  background: transparent;
-  border: none;
-  padding: 8px 10px;
-  font-size: 12px;
-  text-align: left;
-  border-radius: 6px;
-  cursor: pointer;
-  display: flex;
-  align-items: center;
-  gap: 10px;
-  color: var(--text);
-}
-.present-menu button:hover { background: var(--bg-subtle); border-color: transparent; }
-.present-icon {
-  display: inline-flex;
-  width: 14px;
-  justify-content: center;
-  color: var(--text-muted);
-  font-size: 12px;
-}
-
-.share-menu { position: relative; display: inline-block; }
-.viewer-action-export {
-  gap: 6px;
-  background: var(--accent);
-  border-color: var(--accent);
-  color: white;
-  box-shadow: 0 6px 14px color-mix(in srgb, var(--accent) 20%, transparent);
-}
-.viewer-action-export:hover:not(:disabled) {
-  background: var(--accent-hover);
-  border-color: var(--accent-hover);
-  color: white;
-}
-@keyframes export-ready-nudge {
-  0% {
-    transform: translateY(0);
-    box-shadow: 0 6px 14px color-mix(in srgb, var(--accent) 24%, transparent);
-  }
-  18% {
-    transform: translateY(-1px);
-    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent),
-      0 8px 18px color-mix(in srgb, var(--accent) 30%, transparent);
-  }
-  36% {
-    transform: translateY(0);
-  }
-  54% {
-    transform: translateY(-1px);
-    box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 12%, transparent),
-      0 8px 18px color-mix(in srgb, var(--accent) 28%, transparent);
-  }
-  100% {
-    transform: translateY(0);
-    box-shadow: 0 6px 14px color-mix(in srgb, var(--accent) 24%, transparent);
-  }
-}
-
-@media (prefers-reduced-motion: reduce) {
-  .chrome-action-export.export-ready-nudge {
-    animation: export-ready-nudge-reduced 1200ms ease-out 1;
-  }
-}
-
-@keyframes export-ready-nudge-reduced {
-  0%, 100% {
-    box-shadow: 0 6px 14px color-mix(in srgb, var(--accent) 24%, transparent);
-  }
-  35% {
-    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
-  }
-}
-.share-menu-popover {
-  position: absolute;
-  top: calc(100% + 6px);
-  right: 0;
-  z-index: 50;
-  min-width: 240px;
-  padding: 4px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  box-shadow: var(--shadow-md);
-  display: flex;
-  flex-direction: column;
-}
-.share-menu-item {
-  display: flex;
-  align-items: center;
-  gap: 10px;
-  padding: 8px 10px;
-  font-size: 12.5px;
-  text-align: left;
-  background: transparent;
-  border: none;
-  border-radius: 6px;
-  cursor: pointer;
-  color: var(--text);
-}
-.share-menu-item:hover:not(:disabled) {
-  background: var(--bg-subtle);
-  border-color: transparent;
-}
-.share-menu-item:disabled { opacity: 0.45; cursor: not-allowed; }
-.share-menu-icon { flex: 0 0 auto; width: 18px; text-align: center; font-size: 13px; }
-.share-menu-divider { height: 1px; background: var(--border); margin: 4px 6px; }
-
-.button-like {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  min-height: 36px;
-  padding: 0 14px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  color: var(--text);
-  text-decoration: none;
-  cursor: pointer;
-}
-.button-like:disabled {
-  opacity: 0.5;
-  cursor: not-allowed;
-}
-.deploy-modal {
-  width: min(760px, calc(100vw - 32px));
-  max-height: calc(100vh - 32px);
-  overflow: auto;
-}
-/* Add a few extra pixels above the deploy dialog's primary action so
-   it does not crowd the divider. The shared .modal-foot uses 12px
-   vertical padding which is fine for shorter dialogs but reads as
-   cramped here because the deploy form ends in dense token / domain
-   config rows that push content right up to the border. Scoped to the
-   deploy dialog only (the template-save dialog also carries the
-   .deploy-modal class on its root, which is why we key off the
-   purpose-specific .deploy-flow-modal hook instead). 16px matches the
-   bump proposed for the global rule in #957 so the rhythm stays close
-   to the rest of the app. Issue #913. */
-.deploy-flow-modal .modal-foot {
-  padding-top: 16px;
-}
-.deploy-form {
-  display: flex;
-  flex-direction: column;
-  gap: 14px;
-  margin-top: 18px;
-}
-.deploy-provider-field {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-}
-.field-label-row {
-  display: flex;
-  align-items: baseline;
-  justify-content: space-between;
-  gap: 16px;
-}
-.field-label-note {
-  display: flex;
-  flex-direction: column;
-  align-items: flex-end;
-  gap: 4px;
-  max-width: min(420px, 70%);
-  text-align: right;
-}
-.field-label-note .hint {
-  margin: 0;
-  font-size: 12px;
-  line-height: 1.35;
-}
-.field-label-row a:not(.field-label-link) {
-  color: var(--accent);
-  font-size: 13px;
-  white-space: nowrap;
-}
-.field-label-group {
-  display: inline-flex;
-  align-items: center;
-  gap: 8px;
-  flex-wrap: wrap;
-}
-.field-label-link {
-  display: inline-flex;
-  align-items: center;
-  gap: 3px;
-  font-size: 10.5px;
-  color: var(--text-muted);
-  text-decoration: none;
-  white-space: nowrap;
-  transition: color 120ms ease;
-}
-.field-label-link:hover {
-  color: var(--accent);
-}
-.field-label-link svg {
-  opacity: 0.75;
-  transition: transform 120ms ease, opacity 120ms ease;
-}
-.field-label-link:hover svg {
-  opacity: 1;
-  transform: translate(1px, -1px);
-}
-.field-status-badge {
-  display: inline-flex;
-  align-items: center;
-  font-size: 10.5px;
-  font-weight: 600;
-  letter-spacing: 0.02em;
-  padding: 2px 7px;
-  border-radius: var(--radius-pill);
-  color: #137a3d;
-  background: color-mix(in srgb, #1f9d55 10%, transparent);
-  border: 1px solid color-mix(in srgb, #1f9d55 28%, var(--border));
-  font-variant-numeric: tabular-nums;
-}
-/*
- * Inline variant lives next to a label on the same row instead of taking
- * its own line. We also drop the green "success" treatment because the
- * media-provider row already has a green "Integrated" badge — two green
- * pills in one row read as duplicate state. Neutral muted gray keeps it
- * legible as metadata without competing for attention.
- */
-.field-status-badge--inline {
-  color: var(--text-muted);
-  background: var(--bg-subtle);
-  border-color: var(--border);
-  font-weight: 500;
-}
-/* ---------- Composio API key skeleton ----------
-   The Composio config is daemon-backed, so on first paint after a
-   restart there is a window where the section renders empty even
-   though a saved key exists. Rather than show a misleading "no key
-   saved" state, we overlay a skeleton on the input + chip + buttons
-   so the user understands the field is still resolving.
-   We intentionally keep the real input mounted underneath the shimmer
-   so focus, autofill, and accessibility nodes are not torn down on
-   resolve — the parent label gets aria-busy and the disabled flags
-   on the buttons are the structural safety net. */
-.field-status-badge-skeleton {
-  /* Same footprint as the saved-state chip so the row geometry
-     doesn't shift when hydration completes. The width is a
-     calibrated guess for a "Saved · ••••XXXX" string; close enough
-     that the swap-in feels stable without depending on the actual
-     tail length. */
-  width: 86px;
-  height: 18px;
-  border: 1px solid var(--border);
-  background:
-    linear-gradient(
-      90deg,
-      color-mix(in srgb, var(--text-soft) 8%, var(--bg-subtle)) 0%,
-      color-mix(in srgb, var(--text-soft) 18%, var(--bg-subtle)) 50%,
-      color-mix(in srgb, var(--text-soft) 8%, var(--bg-subtle)) 100%
-    );
-  background-size: 200% 100%;
-  animation: settingsSkeletonShimmer 1.4s ease-in-out infinite;
-  border-radius: var(--radius-pill);
-  padding: 0;
-  color: transparent;
-}
-
-.field-input-skeleton-wrap {
-  position: relative;
-  display: flex;
-  flex: 1;
-  min-width: 0;
-}
-.field-input-skeleton-wrap > input {
-  flex: 1;
-  min-width: 0;
-}
-.field-input-skeleton-shimmer {
-  position: absolute;
-  inset: 1px;
-  border-radius: var(--radius);
-  pointer-events: none;
-  background:
-    linear-gradient(
-      90deg,
-      transparent 0%,
-      color-mix(in srgb, var(--text-soft) 14%, transparent) 50%,
-      transparent 100%
-    );
-  background-size: 220% 100%;
-  animation: settingsSkeletonShimmer 1.6s ease-in-out infinite;
-}
-
-/* The whole credentials field gets a softened, "we're checking" feel
-   while loading: muted label, slightly desaturated input chrome.
-   Matches the broader Settings shimmer language. */
-.settings-section-connectors-credentials.is-loading .field-label {
-  color: var(--text-muted);
-}
-.settings-section-connectors-credentials.is-loading input:disabled {
-  cursor: progress;
-  background: color-mix(in srgb, var(--text-soft) 4%, var(--bg-subtle));
-  /* Preserve readable placeholder color even when disabled — the
-     placeholder doubles as the "Checking for a saved key…" cue. */
-  color: var(--text-muted);
-  -webkit-text-fill-color: var(--text-muted);
-}
-.settings-section-connectors-credentials.is-loading input:disabled::placeholder {
-  color: var(--text-muted);
-  opacity: 1;
-}
-
-/* Inline status hint variant — the help line below the input becomes
-   a small spinner + status string while loading. Sits in the same
-   slot as the regular hint so layout doesn't jump. */
-.field-hint-loading {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  color: var(--text-muted);
-}
-.field-hint-loading svg {
-  color: var(--accent-strong);
-  flex-shrink: 0;
-}
-
-@keyframes settingsSkeletonShimmer {
-  0%   { background-position: 100% 0; }
-  100% { background-position: -100% 0; }
-}
-@media (prefers-reduced-motion: reduce) {
-  .field-status-badge-skeleton,
-  .field-input-skeleton-shimmer {
-    animation: none;
-  }
-  .field-input-skeleton-shimmer {
-    background: color-mix(in srgb, var(--text-soft) 8%, transparent);
-  }
-}
-.deploy-form input,
-.deploy-form select {
-  width: 100%;
-  min-height: 44px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  padding: 0 12px;
-  background: var(--bg-panel);
-  color: var(--text);
-  font: inherit;
-}
-.deploy-config-actions {
-  display: flex;
-  justify-content: flex-end;
-}
-.deploy-field-grid {
-  display: grid;
-  grid-template-columns: repeat(2, minmax(0, 1fr));
-  gap: 12px;
-}
-.cloudflare-domain-grid {
-  grid-template-columns: minmax(180px, 0.85fr) minmax(220px, 1.15fr);
-}
-.deploy-field-grid.single-field {
-  grid-template-columns: minmax(0, 1fr);
-}
-.deploy-field-grid label {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-}
-.field-hint {
-  color: var(--text-muted);
-  font-size: 12px;
-  line-height: 1.35;
-}
-.deploy-error {
-  margin: 0;
-  color: var(--red);
-}
-.deploy-result-block {
-  overflow: hidden;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-lg);
-  background: var(--bg-panel);
-  box-shadow: var(--shadow-xs);
-}
-.deploy-result-block.ready {
-  border-color: color-mix(in srgb, var(--green) 26%, var(--border));
-}
-.deploy-result-block.delayed {
-  border-color: color-mix(in srgb, #b7791f 34%, var(--border));
-}
-.deploy-result-block.protected,
-.deploy-result-block.failed {
-  border-color: color-mix(in srgb, #c96442 38%, var(--border));
-}
-.deploy-result-summary {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-  padding: 14px 16px 12px;
-  background: color-mix(in srgb, var(--bg-subtle) 64%, var(--bg-panel));
-}
-.deploy-result-summary-head {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 12px;
-  flex-wrap: wrap;
-}
-.deploy-result-link-head {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  flex-wrap: wrap;
-}
-.deploy-result-badge,
-.deploy-result-link-state {
-  display: inline-flex;
-  align-items: center;
-  min-height: 24px;
-  padding: 0 9px;
-  border-radius: var(--radius-pill);
-  font-size: 11px;
-  font-weight: 700;
-  letter-spacing: 0.02em;
-  white-space: nowrap;
-}
-.deploy-result-badge.ready,
-.deploy-result-link-state.ready {
-  color: #137a3d;
-  background: color-mix(in srgb, var(--green) 14%, transparent);
-  border: 1px solid color-mix(in srgb, var(--green) 28%, var(--border));
-}
-.deploy-result-badge.delayed,
-.deploy-result-link-state.delayed {
-  color: #9a5b12;
-  background: color-mix(in srgb, #b7791f 14%, transparent);
-  border: 1px solid color-mix(in srgb, #b7791f 28%, var(--border));
-}
-.deploy-result-badge.protected,
-.deploy-result-badge.failed,
-.deploy-result-link-state.protected,
-.deploy-result-link-state.failed {
-  color: #a34828;
-  background: color-mix(in srgb, #c96442 14%, transparent);
-  border: 1px solid color-mix(in srgb, #c96442 30%, var(--border));
-}
-.deploy-result-label {
-  font-size: 12px;
-  font-weight: 700;
-  letter-spacing: 0.08em;
-  text-transform: uppercase;
-  color: var(--text);
-}
-.deploy-result-message {
-  margin: 0;
-  color: var(--text-muted);
-}
-.deploy-result-links {
-  display: grid;
-  border-top: 1px solid var(--border);
-}
-.deploy-result-link {
-  display: grid;
-  grid-template-columns: minmax(0, 1fr) auto;
-  align-items: center;
-  gap: 12px 16px;
-  padding: 14px 16px;
-  background: var(--bg-panel);
-}
-.deploy-result-link + .deploy-result-link {
-  border-top: 1px solid var(--border);
-}
-.deploy-result-link-main {
-  display: grid;
-  min-width: 0;
-  gap: 6px;
-}
-.deploy-result-link-label {
-  color: var(--text);
-  font-size: 13px;
-  font-weight: 700;
-  letter-spacing: 0.01em;
-}
-.deploy-result-link-message {
-  margin: 0;
-  color: var(--text-muted);
-}
-.deploy-result-url {
-  min-width: 0;
-  font-weight: 500;
-  overflow-wrap: anywhere;
-}
-.deploy-result-actions {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  gap: 2px;
-  padding: 3px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-subtle);
-}
-.deploy-result-actions .viewer-action,
-.deploy-result-actions .ghost-link {
-  min-height: 28px;
-  border: 0;
-  background: transparent;
-}
-.deploy-result-actions .viewer-action:hover:not(:disabled),
-.deploy-result-actions .ghost-link:hover {
-  background: var(--bg-panel);
-}
-@media (max-width: 640px) {
-  .deploy-form .field-label-row {
-    align-items: flex-start;
-    flex-direction: column;
-    gap: 6px;
-  }
-  .deploy-form .field-label-note {
-    align-items: flex-start;
-    max-width: none;
-    text-align: left;
-  }
-  .deploy-field-grid,
-  .cloudflare-domain-grid {
-    grid-template-columns: minmax(0, 1fr);
-  }
-  .deploy-result-link {
-    grid-template-columns: minmax(0, 1fr);
-  }
-  .deploy-result-actions {
-    justify-content: flex-start;
-    flex-wrap: wrap;
-    width: fit-content;
-  }
-}
-.ghost-link.disabled,
-.ghost-link[aria-disabled='true'] {
-  opacity: 0.45;
-  pointer-events: none;
-  cursor: not-allowed;
-}
-
-.present-overlay {
-  position: fixed;
-  inset: 0;
-  z-index: 1000;
-  background: black;
-  display: flex;
-}
-.present-overlay iframe {
-  flex: 1;
-  width: 100%;
-  height: 100%;
-  border: none;
-  background: white;
-}
-.present-exit {
-  position: absolute;
-  top: calc(env(safe-area-inset-top, 0px) + 20px);
-  right: calc(env(safe-area-inset-right, 0px) + 20px);
-  z-index: 1001;
-  display: inline-flex;
-  align-items: center;
-  padding: 6px 12px;
-  font-size: 12px;
-  background: rgba(255, 255, 255, 0.92);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-  cursor: pointer;
-}
-.present-exit:hover { background: white; }
-
-/* Picker (legacy in some surfaces) */
-.picker {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  padding: 2px 6px 2px 10px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-}
-.picker-label {
-  font-size: 11px;
-  text-transform: uppercase;
-  letter-spacing: 0.05em;
-  color: var(--text-muted);
-}
-.picker select {
-  border: none;
-  background: transparent;
-  padding: 4px 6px;
-  width: auto;
-  min-width: 120px;
-  box-shadow: none;
-}
-.picker select:focus { outline: none; box-shadow: none; }
-.picker.agent-picker select { min-width: 140px; }
-.picker.agent-picker .icon-btn {
-  padding: 2px 8px;
-  background: transparent;
-  border: none;
-}
-
-/* Preview pane (legacy single-iframe path; kept for fallbacks) */
-.preview {
-  display: flex;
-  flex-direction: column;
-  background: var(--bg-subtle);
-  min-height: 0;
-}
-.preview-header {
-  padding: 8px 12px;
-  border-bottom: 1px solid var(--border);
-  background: var(--bg-panel);
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  font-size: 12px;
-  color: var(--text-muted);
-}
-.preview-title {
-  display: flex; align-items: center; gap: 10px; min-width: 0;
-}
-.preview-actions { display: flex; gap: 8px; align-items: center; }
-.preview-actions .muted { font-size: 11px; }
-.preview-actions button { padding: 4px 10px; font-size: 12px; }
-.preview-body { flex: 1; min-height: 0; position: relative; }
-.preview-body iframe {
-  width: 100%; height: 100%; border: none; background: white;
-}
-.preview-empty {
-  display: flex; align-items: center; justify-content: center;
-  height: 100%;
-  color: var(--text-muted);
-  padding: 24px;
-  text-align: center;
-  font-size: 13px;
-}
-
-/* ============================================================
-   Merged from main — persistence-era additions: editable project
-   title, conversations dropdown, examples filters + preview modal,
-   question form, design-system modal, system reminder collapse,
-   operations todo card.
-   ============================================================ */
-
-/* Editable project title (inline rename in topbar) */
-.topbar-title .title.editable,
-.app-project-title .title.editable {
-  outline: none;
-  border-radius: 4px;
-  padding: 1px 4px;
-  margin: 0px;
-  max-width: min(100%, 420px);
-}
-.topbar-title .title.editable:focus,
-.app-project-title .title.editable:focus {
-  background: var(--bg-subtle);
-  box-shadow: 0 0 0 1px var(--accent);
-}
-
-/* Conversations dropdown — pill in the project topbar */
-.conv-pill {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  padding: 6px 10px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  font: inherit;
-  font-size: 12px;
-  color: inherit;
-  cursor: pointer;
-  user-select: none;
-  max-width: 220px;
-}
-.conv-pill:hover { background: var(--bg-subtle); }
-.conv-pill.open { background: var(--bg-subtle); border-color: var(--accent); }
-.conv-pill-icon { font-size: 13px; line-height: 1; }
-.conv-pill-label {
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.conv-pill-count {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  min-width: 18px;
-  height: 18px;
-  padding: 0 6px;
-  border-radius: 9px;
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-  font-size: 10px;
-}
-.conv-menu {
-  position: fixed;
-  width: 320px;
-  max-height: 420px;
-  overflow-y: auto;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
-  z-index: 200;
-  padding: 8px;
-}
-.conv-menu-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 4px 6px 8px;
-  border-bottom: 1px solid var(--border);
-  margin-bottom: 6px;
-  font-size: 11px;
-  color: var(--text-muted);
-  text-transform: uppercase;
-  letter-spacing: 0.05em;
-}
-.conv-add-btn { padding: 2px 8px; font-size: 11px; }
-.conv-menu-empty {
-  padding: 16px 8px;
-  font-size: 12px;
-  color: var(--text-muted);
-  text-align: center;
-}
-.conv-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
-.conv-item {
-  display: flex;
-  align-items: stretch;
-  gap: 4px;
-  border-radius: 6px;
-}
-.conv-item.active { background: var(--bg-subtle); }
-.conv-item-button {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  gap: 2px;
-  padding: 6px 8px;
-  border: none;
-  background: transparent;
-  text-align: left;
-  border-radius: 6px;
-  cursor: pointer;
-}
-.conv-item-button:hover { background: var(--bg-subtle); }
-.conv-item-name {
-  font-size: 12px;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.conv-item-meta {
-  font-size: 10px;
-  color: var(--text-muted);
-}
-.conv-item-del {
-  border: none;
-  background: transparent;
-  color: var(--text-muted);
-  font-size: 16px;
-  width: 24px;
-  cursor: pointer;
-  border-radius: 6px;
-}
-.conv-item-del:hover { background: var(--bg-subtle); color: var(--text); }
-.conv-rename-input {
-  flex: 1;
-  border: 1px solid var(--accent);
-  border-radius: 6px;
-  background: var(--bg-panel);
-  padding: 6px 8px;
-  font: inherit;
-  font-size: 12px;
-}
-
-/* Collapsible system-reminder block in chat */
-.system-reminder-block {
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  margin: 6px 0;
-}
-.system-reminder-toggle {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  width: 100%;
-  padding: 6px 10px;
-  background: transparent;
-  border: none;
-  text-align: left;
-  font-size: 12px;
-  color: var(--text-muted);
-}
-.system-reminder-toggle:hover { background: rgba(0, 0, 0, 0.03); }
-.system-reminder-icon { color: var(--text-muted); }
-.system-reminder-label { font-weight: 500; color: var(--text); }
-.system-reminder-preview {
-  flex: 1;
-  color: var(--text-muted);
-  font-style: italic;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.system-reminder-chev { color: var(--text-muted); font-size: 10px; }
-.system-reminder-body {
-  margin: 0;
-  padding: 0 12px 10px 12px;
-  font-family: var(--mono);
-  font-size: 11px;
-  line-height: 1.5;
-  color: var(--text-muted);
-  white-space: pre-wrap;
-  overflow-wrap: anywhere;
-}
-
-/* Waiting-on-input pill (richer than the simple italic version above). */
-.op-waiting {
-  display: flex;
-  flex-wrap: wrap;
-  align-items: flex-start;
-  gap: 8px;
-  max-width: 100%;
-  padding: 8px 12px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-subtle);
-  font-size: 12px;
-  color: var(--text-muted);
-  font-style: normal;
-}
-.op-waiting-dot {
-  width: 8px;
-  height: 8px;
-  border-radius: 999px;
-  background: var(--accent);
-  flex-shrink: 0;
-  animation: pulse 1.4s ease-in-out infinite;
-}
-.op-waiting-label {
-  font-weight: 500;
-  color: var(--text);
-}
-.op-waiting-detail {
-  min-width: 0;
-  max-width: 100%;
-  font-family: var(--mono);
-  font-size: 11px;
-  background: var(--bg-panel);
-  padding: 1px 6px;
-  border-radius: 4px;
-  color: var(--text-muted);
-  overflow-wrap: anywhere;
-  white-space: normal;
-}
-.op-waiting-hint {
-  flex-basis: 100%;
-  font-size: 11px;
-  color: var(--text-soft);
-  font-style: italic;
-}
-
-/* op-todo card variant (the .op-todo .todo-list rule already exists above) */
-.op-todo {
-  border-color: var(--accent-soft);
-  background: linear-gradient(180deg, var(--accent-tint) 0%, var(--bg-panel) 60%);
-}
-.op-todo .op-card-head {
-  border-bottom: 1px solid color-mix(in srgb, var(--accent-soft) 70%, transparent);
-  background: transparent;
-}
-.op-todo .op-icon {
-  background: var(--accent-soft);
-  color: var(--accent);
-}
-.op-todo .op-title {
-  letter-spacing: 0.02em;
-  text-transform: uppercase;
-  font-size: 11px;
-  color: var(--text-strong);
-}
-.op-todo .op-meta {
-  margin-left: auto;
-  font-variant-numeric: tabular-nums;
-  font-size: 11px;
-  color: var(--text-strong);
-}
-
-/*
- * Pinned task list above the chat composer. The card sits just above the
- * prompt input, persistent for the whole conversation rather than buried
- * in a single assistant message. It mirrors the Cursor / Codex / Claude
- * Code TTY pattern where the todo list is always one click away while you
- * are typing the next prompt. The card is collapsible via its own
- * header — see `.op-todo-toggle` below.
- */
-.chat-pinned-todo {
-  position: relative;
-  padding: 0 10px;
-  transition:
-    transform 220ms cubic-bezier(0.23, 1, 0.32, 1),
-    opacity 200ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-/* Slide-down dismiss: matches the global animation philosophy
- * (strong ease-out, ~200ms). The wrapper animates away while the
- * Done click still resolves the snapshot key. */
-.chat-pinned-todo-exit {
-  transform: translateY(24px);
-  opacity: 0;
-  pointer-events: none;
-}
-/* Soft fade above the pinned card so chat text scrolling beneath it
- * dissolves into the panel background instead of getting hard-clipped
- * at the card's top edge. Sits in the gutter directly above the card,
- * pulled up by negative bottom so it overlaps the chat scroll area. */
-.chat-pinned-todo::before {
-  content: '';
-  position: absolute;
-  left: 0;
-  right: 0;
-  bottom: 100%;
-  height: 28px;
-  background: linear-gradient(to top, var(--bg-panel), transparent);
-  pointer-events: none;
-}
-.chat-pinned-todo .op-card.op-todo {
-  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
-  /* Square the bottom corners so the card reads as tucked under the
-   * composer below it, rather than as a floating pill with a gap. */
-  border-bottom-left-radius: 0;
-  border-bottom-right-radius: 0;
-  border-bottom: 0;
-}
-
-/*
- * Shared accordion expand/collapse motion. `grid-template-rows: 0fr -> 1fr`
- * is the modern way to animate auto-height content; pair it with a
- * fade so the body doesn't pop in flat. Strong ease-out curve, with
- * asymmetric timing (enter 200ms, exit 140ms) per Emil Kowalski's
- * framework — entrance reads as considered, exit is decisive.
- */
-.accordion-collapsible {
-  display: grid;
-  grid-template-rows: 0fr;
-  opacity: 0;
-  transition:
-    grid-template-rows 140ms cubic-bezier(0.23, 1, 0.32, 1),
-    opacity 100ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-.accordion-collapsible.open {
-  grid-template-rows: 1fr;
-  opacity: 1;
-  transition:
-    grid-template-rows 200ms cubic-bezier(0.23, 1, 0.32, 1),
-    opacity 200ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-.accordion-collapsible-inner {
-  min-height: 0;
-  overflow: hidden;
-}
-
-/*
- * Make the todo card header a clickable expand/collapse affordance.
- * Default is expanded while work is in flight; the user can flip it via
- * the chevron at the right. Removing the bottom border in the collapsed
- * state keeps the card looking like a single pill rather than an empty
- * disclosure with a hanging line.
- */
-.op-todo .op-todo-head {
-  display: flex;
-  align-items: stretch;
-  padding: 0;
-}
-.op-todo .op-todo-toggle {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  flex: 1;
-  min-width: 0;
-  padding: 8px 12px;
-  background: transparent;
-  border: 0;
-  cursor: pointer;
-  text-align: left;
-  font: inherit;
-  color: inherit;
-}
-.op-todo .op-todo-done {
-  display: inline-flex;
-  align-items: center;
-  margin: 6px 10px 6px 0;
-  padding: 2px 12px;
-  font-size: 11px;
-  font-weight: 500;
-  border: 1px solid var(--green-border);
-  border-radius: var(--radius-pill);
-  background: var(--green-bg);
-  color: var(--green);
-  cursor: pointer;
-}
-.op-todo .op-todo-done:hover {
-  filter: brightness(1.05);
-}
-.op-todo-collapsed .op-card-head {
-  border-bottom-color: transparent;
-}
-.op-todo-current {
-  margin-left: 8px;
-  font-size: 12px;
-  color: var(--text-muted);
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  min-width: 0;
-  flex: 1;
-}
-.op-todo-chev {
-  margin-left: 4px;
-  font-size: 11px;
-  color: var(--text-muted);
-  transition: transform 120ms ease;
-}
-
-/* Question form — interactive form a planning agent can post into chat */
-.question-form {
-  margin: 8px 0;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-lg);
-  background: var(--bg-panel);
-  box-shadow: var(--shadow-md);
-  overflow: hidden;
-}
-.question-form-locked {
-  background: var(--bg-subtle);
-  box-shadow: none;
-  opacity: 0.92;
-}
-.question-form-head {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  padding: 12px 14px;
-  border-bottom: 1px solid var(--border);
-  background: linear-gradient(180deg, var(--accent-tint) 0%, var(--bg-panel) 100%);
-}
-.question-form-locked .question-form-head {
-  background: var(--bg-subtle);
-}
-.question-form-icon {
-  width: 28px;
-  height: 28px;
-  border-radius: 999px;
-  background: var(--accent);
-  color: white;
-  font-weight: 600;
-  font-size: 14px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  flex-shrink: 0;
-}
-.question-form-titles {
-  flex: 1;
-  min-width: 0;
-}
-.question-form-title {
-  font-size: 14px;
-  font-weight: 600;
-  letter-spacing: -0.01em;
-  color: var(--text);
-}
-.question-form-desc {
-  margin-top: 2px;
-  font-size: 12px;
-  color: var(--text-muted);
-  line-height: 1.45;
-}
-.question-form-pill {
-  font-size: 10px;
-  letter-spacing: 0.04em;
-  text-transform: uppercase;
-  padding: 3px 8px;
-  border-radius: 999px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  color: var(--text-muted);
-}
-.question-form-body {
-  display: flex;
-  flex-direction: column;
-  gap: 14px;
-  padding: 14px;
-}
-.qf-field { display: flex; flex-direction: column; gap: 6px; }
-.qf-label {
-  font-size: 12px;
-  font-weight: 500;
-  color: var(--text);
-  display: flex;
-  align-items: center;
-  gap: 4px;
-}
-.qf-required { color: var(--accent); }
-.qf-help {
-  font-size: 11px;
-  color: var(--text-muted);
-  margin-top: -2px;
-  word-wrap: break-word;
-  overflow-wrap: break-word;
-  line-height: 1.4;
-}
-.qf-options {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-}
-.qf-chip {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  padding: 6px 12px;
-  border: 1px solid var(--border);
-  border-radius: 999px;
-  background: var(--bg-panel);
-  font-size: 12px;
-  cursor: pointer;
-  transition: border-color 120ms ease, background 120ms ease, color 120ms ease;
-}
-.qf-chip input { width: auto; margin: 0; display: none; }
-.qf-chip-copy {
-  display: flex;
-  flex-direction: column;
-  gap: 2px;
-  line-height: 1.2;
-}
-.qf-chip-desc {
-  color: var(--text-muted);
-  font-size: 11px;
-  font-weight: 400;
-}
-.qf-chip-on .qf-chip-desc {
-  color: inherit;
-  opacity: 0.72;
-}
-.qf-chip:hover { border-color: var(--border-strong); }
-.qf-chip-disabled {
-  cursor: not-allowed;
-  opacity: 0.48;
-}
-.qf-chip-disabled:hover { border-color: var(--border); }
-.qf-chip-on {
-  border-color: var(--accent);
-  background: var(--accent-soft);
-  color: var(--accent-hover);
-  font-weight: 500;
-}
-.question-form-locked .qf-chip { cursor: not-allowed; }
-.qf-input,
-.qf-select,
-.qf-textarea {
-  font-size: 13px;
-}
-.question-form-foot {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  padding: 10px 14px;
-  border-top: 1px solid var(--border);
-  background: var(--bg-subtle);
-}
-.qf-hint,
-.qf-locked-note {
-  flex: 1;
-  font-size: 11px;
-  color: var(--text-muted);
-  line-height: 1.4;
-}
-
-/* Direction-cards picker — used by the second discovery form when the user
-   selects "Pick a direction for me". Each card carries a palette swatch
-   row, a serif/sans type sample, a mood blurb, and a real-world refs
-   line so the user can scan visually instead of squinting at radio labels. */
-.qf-direction-cards {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
-  gap: 10px;
-  margin-top: 4px;
-}
-.qf-card {
-  position: relative;
-  display: flex;
-  flex-direction: column;
-  gap: 10px;
-  padding: 14px;
-  background: var(--bg);
-  border: 1px solid var(--border);
-  border-radius: 10px;
-  cursor: pointer;
-  transition:
-    border-color 0.12s ease,
-    background 0.12s ease,
-    transform 0.06s ease;
-}
-.qf-card input { display: none; }
-.qf-card:hover { border-color: var(--border-strong); }
-.qf-card:active { transform: translateY(1px); }
-.qf-card-on {
-  border-color: var(--accent, #c96442);
-  background: color-mix(in oklch, var(--accent, #c96442) 4%, var(--bg));
-  box-shadow: 0 0 0 1px var(--accent, #c96442) inset;
-}
-.qf-card-disabled { cursor: not-allowed; opacity: 0.6; }
-.qf-card-head {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 8px;
-}
-.qf-card-title {
-  font-size: 13px;
-  font-weight: 600;
-  letter-spacing: -0.005em;
-  color: var(--text);
-  line-height: 1.3;
-}
-.qf-card-pill {
-  font-family: ui-monospace, 'JetBrains Mono', monospace;
-  font-size: 9px;
-  letter-spacing: 0.06em;
-  text-transform: uppercase;
-  padding: 2px 6px;
-  background: var(--accent, #c96442);
-  color: #fff;
-  border-radius: 999px;
-  flex-shrink: 0;
-}
-.qf-card-swatches {
-  display: flex;
-  gap: 4px;
-  height: 18px;
-}
-.qf-card-swatch {
-  flex: 1;
-  border-radius: 3px;
-  border: 1px solid rgba(0, 0, 0, 0.08);
-  min-width: 0;
-}
-.qf-card-types {
-  display: flex;
-  align-items: baseline;
-  gap: 12px;
-  padding: 6px 0;
-  border-top: 1px solid var(--border);
-  border-bottom: 1px solid var(--border);
-}
-.qf-card-type-display {
-  font-size: 28px;
-  line-height: 1;
-  letter-spacing: -0.02em;
-  color: var(--text);
-  flex-shrink: 0;
-}
-.qf-card-type-body {
-  font-size: 12px;
-  color: var(--text-muted);
-  line-height: 1.3;
-  letter-spacing: -0.005em;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-.qf-card-mood {
-  margin: 0;
-  font-size: 12px;
-  line-height: 1.45;
-  color: var(--text-muted);
-}
-.qf-card-refs {
-  margin: 0;
-  font-size: 11px;
-  color: var(--text-muted);
-  line-height: 1.4;
-}
-.qf-card-refs-label {
-  font-family: ui-monospace, 'JetBrains Mono', monospace;
-  font-size: 10px;
-  letter-spacing: 0.06em;
-  text-transform: uppercase;
-  color: var(--text-muted);
-  opacity: 0.7;
-}
-
-/* Design-system preview modal */
-.ds-modal-backdrop {
-  position: fixed;
-  inset: 0;
-  z-index: 900;
-  background: rgba(28, 27, 26, 0.42);
-  backdrop-filter: blur(2px);
-  -webkit-app-region: no-drag;
-  display: flex;
-  align-items: stretch;
-  justify-content: center;
-  padding: 32px;
-}
-.ds-modal {
-  width: 100%;
-  max-width: 1320px;
-  background: var(--bg);
-  -webkit-app-region: no-drag;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-lg);
-  box-shadow: var(--shadow-lg);
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-  min-height: 0;
-}
-.ds-modal-fullscreen {
-  max-width: none;
-  border-radius: 0;
-}
-/* Two-row header: title row keeps the title/subtitle + close together at
-   the top; toolbar row hosts tabs (left) and the action cluster (right).
-   This split lets the title breathe even when the toolbar carries a busy
-   set of controls (Use plugin + sidebar + Fullscreen + Share + plugin
-   share + ...), and keeps the close button anchored in its expected
-   top-right corner. */
-.ds-modal-header {
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-  padding: 14px 20px;
-  border-bottom: 1px solid var(--border);
-  background: var(--bg-panel);
-}
-.ds-modal-header-top {
-  display: flex;
-  align-items: flex-start;
-  gap: 12px;
-}
-.ds-modal-header-toolbar {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  flex-wrap: wrap;
-}
-.ds-modal-title-block {
-  flex: 1;
-  min-width: 0;
-  display: flex;
-  flex-direction: column;
-  gap: 3px;
-}
-.ds-modal-title {
-  font-size: 16px;
-  font-weight: 600;
-  letter-spacing: -0.01em;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  color: var(--text-strong, var(--text));
-}
-.ds-modal-subtitle {
-  font-size: 12px;
-  color: var(--text-muted);
-  line-height: 1.5;
-  /* Allow up to two lines for long descriptions (e.g. Chinese plugin
-     subtitles) instead of clipping at the first word — keeps the
-     vertical footprint bounded but readable. */
-  display: -webkit-box;
-  -webkit-line-clamp: 2;
-  -webkit-box-orient: vertical;
-  overflow: hidden;
-  word-break: break-word;
-}
-.ds-modal-tabs {
-  display: inline-flex;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: 999px;
-  padding: 3px;
-  gap: 2px;
-  flex-shrink: 0;
-}
-.ds-modal-tab {
-  background: transparent;
-  border: none;
-  border-radius: 999px;
-  padding: 6px 16px;
-  font-size: 12.5px;
-  font-weight: 500;
-  color: var(--text-muted);
-  cursor: pointer;
-}
-.ds-modal-tab:hover { color: var(--text); }
-.ds-modal-tab.active {
-  background: var(--bg-panel);
-  color: var(--text);
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
-}
-.ds-modal-actions {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  justify-content: flex-end;
-  flex-wrap: wrap;
-  margin-left: auto;
-}
-/* Keep individual action labels (Use plugin / DESIGN.md / Fullscreen /
-   Share / etc.) on a single line; the toolbar row itself can wrap to a
-   second visual line only at very narrow widths via flex-wrap above. */
-.ds-modal-actions > button,
-.ds-modal-actions .share-menu > button {
-  white-space: nowrap;
-  flex-shrink: 0;
-}
-.ds-modal-stage {
-  flex: 1;
-  min-height: 0;
-  background: white;
-  position: relative;
-  display: flex;
-  align-items: stretch;
-}
-.ds-modal-stage-iframe {
-  flex: 1;
-  min-width: 0;
-  position: relative;
-  overflow: hidden;
-  background: white;
-}
-.ds-modal-stage-iframe-scaler {
-  position: absolute;
-  top: 0;
-  left: 0;
-  transform-origin: top left;
-  background: white;
-  /* Prevent the GPU layer from blurring the scaled iframe on Retina. */
-  will-change: transform;
-}
-.ds-modal-stage-iframe-scaler iframe {
-  width: 100%;
-  height: 100%;
-  border: none;
-  display: block;
-  background: white;
-}
-.ds-modal-stage.has-sidebar .ds-modal-stage-iframe {
-  flex: 1 1 60%;
-}
-.ds-modal-sidebar {
-  position: relative;
-  flex: 1 1 40%;
-  min-width: 320px;
-  max-width: 560px;
-  border-left: 1px solid var(--border);
-  background: var(--bg-panel);
-  overflow: auto;
-  scrollbar-gutter: stable;
-  display: flex;
-  flex-direction: column;
-}
-.ds-modal-stage-handle {
-  position: absolute;
-  top: 50%;
-  width: 18px;
-  height: 56px;
-  transform: translateY(-50%);
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  color: var(--text-muted);
-  cursor: pointer;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 14px;
-  line-height: 1;
-  padding: 0;
-  z-index: 3;
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
-  transition: color 120ms ease, background 120ms ease;
-}
-.ds-modal-stage-handle:hover { color: var(--text); background: var(--bg-subtle); }
-.ds-modal-stage-handle.is-expand {
-  right: 0;
-  border-right: none;
-  border-radius: 8px 0 0 8px;
-}
-.ds-modal-stage-handle.is-collapse {
-  position: sticky;
-  left: 0;
-  border-left: none;
-  border-radius: 0 8px 8px 0;
-}
-.ds-modal-fullscreen .ds-modal-stage:fullscreen .ds-modal-stage-iframe-scaler,
-.ds-modal-stage:fullscreen .ds-modal-stage-iframe-scaler {
-  height: 100vh;
-}
-
-/* Custom (non-iframe) stage container — used by the plugin media
-   detail variant so image / video / audio plugins share the
-   same modal chrome (header, sidebar toggle, fullscreen, share)
-   as HTML and design-system plugins. The container fills the
-   stage frame and centers its child media element. */
-.ds-modal-stage-custom {
-  position: absolute;
-  inset: 0;
-  display: flex;
-  background: var(--bg-subtle, #f6f5f1);
-}
-.plugin-media-stage {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 24px;
-  overflow: auto;
-  min-height: 0;
-}
-.plugin-media-stage__empty {
-  color: var(--text-muted);
-  font-size: 13px;
-}
-.plugin-media-stage__image,
-.plugin-media-stage__video,
-.plugin-media-stage__audio-poster {
-  max-width: 100%;
-  max-height: 100%;
-  object-fit: contain;
-  border-radius: 8px;
-  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.16);
-  background: white;
-}
-.plugin-media-stage__video {
-  width: auto;
-  height: auto;
-}
-.plugin-media-stage__audio {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  gap: 18px;
-  width: 100%;
-  max-width: 480px;
-}
-.plugin-media-stage__audio-poster {
-  width: 320px;
-  height: 320px;
-  object-fit: cover;
-}
-.plugin-media-stage__audio-glyph {
-  width: 200px;
-  height: 200px;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: linear-gradient(
-    135deg,
-    color-mix(in srgb, var(--accent) 18%, transparent),
-    color-mix(in srgb, var(--accent) 4%, transparent)
-  );
-  color: var(--accent);
-  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.12);
-}
-.plugin-media-stage__audio-player {
-  width: 100%;
-}
-
-/* Sidebar prompt block — top of the right pane, mirrors the
-   PROMPT BODY card in the previous bespoke media modal so users
-   still get the prompt + copy affordance, just inside the
-   unified PreviewModal chrome. */
-.plugin-media-sidebar {
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
-}
-.plugin-media-sidebar__prompt {
-  border: 1px solid var(--border);
-  border-radius: 10px;
-  padding: 12px 14px;
-  background: var(--surface-1, var(--bg-panel));
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-}
-.plugin-media-sidebar__prompt-head {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 10px;
-}
-.plugin-media-sidebar__prompt-label {
-  font-size: 11px;
-  font-weight: 600;
-  letter-spacing: 0.08em;
-  text-transform: uppercase;
-  color: var(--text-muted);
-}
-.plugin-media-sidebar__prompt-copy {
-  appearance: none;
-  background: transparent;
-  border: 1px solid transparent;
-  color: var(--text-muted);
-  font-size: 11.5px;
-  display: inline-flex;
-  align-items: center;
-  gap: 4px;
-  padding: 3px 8px;
-  border-radius: 6px;
-  cursor: pointer;
-  transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
-}
-.plugin-media-sidebar__prompt-copy:hover {
-  color: var(--text);
-  background: var(--bg-subtle);
-  border-color: var(--border);
-}
-.plugin-media-sidebar__prompt-body {
-  margin: 0;
-  font-family:
-    ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
-  font-size: 12px;
-  line-height: 1.55;
-  color: var(--text);
-  white-space: pre-wrap;
-  word-break: break-word;
-  max-height: 260px;
-  overflow: auto;
-}
-.ds-modal-empty {
-  position: absolute;
-  inset: 0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: var(--text-muted);
-  font-size: 13px;
-}
-/* Error state extends .ds-modal-empty: stacks a title, body, and Retry
-   button instead of a single line, so a fetch failure no longer leaves
-   the modal stuck at "Loading…" with disabled toolbar buttons and no
-   recovery path. Issue #860. */
-.ds-modal-error {
-  flex-direction: column;
-  gap: 10px;
-  padding: 0 24px;
-  text-align: center;
-}
-.ds-modal-error-title {
-  color: var(--text);
-  font-size: 14px;
-  font-weight: 600;
-}
-.ds-modal-error-body {
-  color: var(--text-muted);
-  font-size: 13px;
-  max-width: 48ch;
-  line-height: 1.5;
-}
-.ds-modal-actions .ghost.is-active {
-  background: var(--accent-tint);
-  color: var(--accent);
-  border-color: var(--accent);
-}
-/* Accent CTA used by plugin detail wrappers to surface a "Use plugin"
-   action without forking the whole modal layout. Sits before the
-   ghost actions, mirroring the accent button used in NewProjectModal
-   so the affordance reads consistently across surfaces. */
-.ds-modal-primary-action {
-  appearance: none;
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  padding: 7px 16px;
-  border-radius: 6px;
-  border: 1px solid var(--accent);
-  background: var(--accent);
-  color: white;
-  font-size: 12.5px;
-  font-weight: 600;
-  white-space: nowrap;
-  cursor: pointer;
-  transition: background-color 120ms ease, border-color 120ms ease,
-    transform 120ms ease;
-}
-.ds-modal-primary-action:hover:not(:disabled) {
-  background: var(--accent-strong, var(--accent));
-  border-color: var(--accent-strong, var(--accent));
-}
-.ds-modal-primary-action:active:not(:disabled) {
-  transform: translateY(0.5px);
-}
-.ds-modal-primary-action:disabled {
-  cursor: progress;
-  opacity: 0.6;
-}
-/* Dedicated icon-button shape for the modal close affordance. Matches the
-   visual language used by plugin-media-detail__close and
-   new-project-modal__close so every modal closes with the same control. */
-.ds-modal-close {
-  appearance: none;
-  width: 32px;
-  height: 32px;
-  border-radius: 999px;
-  background: transparent;
-  border: 1px solid transparent;
-  color: var(--text-muted);
-  cursor: pointer;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  padding: 0;
-  flex-shrink: 0;
-  transition: background-color 120ms ease, color 120ms ease,
-    border-color 120ms ease;
-}
-.ds-modal-close svg {
-  display: block;
-  width: 14px !important;
-  height: 14px !important;
-  stroke-width: 1.9;
-}
-.ds-modal-close:hover,
-.ds-modal-close:focus-visible {
-  background: var(--bg-subtle);
-  border-color: var(--border);
-  color: var(--text);
-  outline: none;
-}
-
-/* DESIGN.md side panel — monospace source view with light syntax tints,
-   echoing the styles.refero.design "compact" markdown source pane. */
-.design-spec-empty {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 24px;
-  color: var(--text-muted);
-  font-size: 12px;
-}
-.design-spec-pre {
-  margin: 0;
-  padding: 16px 18px;
-  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
-  font-size: 12px;
-  line-height: 1.6;
-  /* Wrap long lines instead of forcing the side pane to scroll horizontally —
-     DESIGN.md prose can have 200+ char paragraphs that otherwise produce a
-     scrollbar inside the modal. `overflow-wrap: anywhere` keeps long
-     hyphenated tokens (URLs, file paths) from blowing out the column. */
-  white-space: pre-wrap;
-  overflow-wrap: anywhere;
-  word-break: break-word;
-  overflow-y: auto;
-  overflow-x: hidden;
-  color: var(--text);
-  background: var(--bg-panel);
-  flex: 1;
-}
-.design-spec-pre code { font: inherit; color: inherit; background: transparent; }
-.design-spec-line { display: inline; }
-.design-spec-line.is-h1 { color: #2563eb; font-weight: 700; }
-.design-spec-line.is-h2 { color: #0891b2; font-weight: 700; }
-.design-spec-line.is-h3 { color: #0d9488; font-weight: 600; }
-.design-spec-line.is-h4 { color: #16a34a; font-weight: 600; }
-.design-spec-line.is-quote { color: #6b7280; font-style: italic; }
-.design-spec-line.is-list { color: var(--text); }
-.design-spec-line.is-table { color: #7c3aed; }
-.design-spec-line.is-fence { color: #dc2626; }
-.design-spec-line.is-blank { color: var(--text-muted); }
-.md-tk-bold { font-weight: 700; color: var(--text); }
-.md-tk-em { font-style: italic; color: var(--text); }
-.md-tk-code {
-  background: var(--bg-subtle);
-  padding: 0 4px;
-  border-radius: 3px;
-  color: #0f766e;
-}
-.md-tk-color {
-  display: inline-flex;
-  align-items: center;
-  gap: 4px;
-  color: #be185d;
-}
-.md-tk-color-swatch {
-  display: inline-block;
-  width: 8px;
-  height: 8px;
-  border-radius: 2px;
-  border: 1px solid rgba(0, 0, 0, 0.12);
-  vertical-align: middle;
-}
-@media (max-width: 760px) {
-  .ds-modal-backdrop { padding: 0; }
-  .ds-modal { border-radius: 0; }
-  .ds-modal-header { gap: 10px; padding: 12px 14px; }
-  .ds-modal-actions { justify-content: flex-start; }
-  .ds-modal-stage { flex-direction: column; }
-  .ds-modal-stage.has-sidebar .ds-modal-stage-iframe { flex: 1 1 50%; }
-  .ds-modal-sidebar {
-    border-left: none;
-    border-top: 1px solid var(--border);
-    flex: 1 1 50%;
-    min-width: 0;
-    max-width: none;
-  }
-  /* On stacked layout the side handles (which assume horizontal split)
-     would float over content awkwardly — fall back to the header toggle. */
-  .ds-modal-stage-handle { display: none; }
-}
-
-/* Examples gallery toolbar — filter pills + richer card metadata */
-.examples-toolbar {
-  display: flex;
-  flex-direction: column;
-  gap: 10px;
-  position: sticky;
-  top: 0;
-  /* Sit above example preview iframes while scrolling (iframes get their own
-     compositor layer; z-index: 2 was not always enough for a clean mask). */
-  z-index: 20;
-  isolation: isolate;
-  margin: 0;
-  padding: 0 0 14px;
-  background: var(--bg);
-  border-bottom: 1px solid var(--border-soft);
-  box-shadow: var(--shadow-sm);
-}
-.examples-search {
-  position: relative;
-  /* Full-width band so scrolling previews cannot show through beside a narrow
-     search field on wide layouts (toolbar stays sticky; iframes layer high). */
-  width: 100%;
-  max-width: 100%;
-  align-self: stretch;
-  background: var(--bg);
-}
-.examples-search input {
-  display: block;
-  width: 100%;
-  max-width: 360px;
-  padding: 7px 12px 7px 32px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: 8px;
-  font-size: 13px;
-  color: var(--text);
-}
-.examples-search input::placeholder { color: var(--text-faint); }
-.examples-search input:focus {
-  outline: none;
-  border-color: var(--accent);
-  box-shadow: 0 0 0 3px var(--accent-soft);
-}
-.examples-search .search-icon {
-  position: absolute;
-  left: 10px;
-  top: 50%;
-  transform: translateY(-50%);
-  color: var(--text-faint);
-  pointer-events: none;
-  display: inline-flex;
-  align-items: center;
-}
-.examples-filter-row {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-  align-items: center;
-}
-.examples-filter-label {
-  font-size: 11px;
-  text-transform: uppercase;
-  letter-spacing: 0.06em;
-  color: var(--text-muted);
-  margin-right: 6px;
-  font-weight: 500;
-}
-.filter-pill {
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: 999px;
-  padding: 5px 12px;
-  font-size: 12px;
-  color: var(--text-muted);
-  cursor: pointer;
-  display: inline-flex;
-  gap: 6px;
-  align-items: center;
-}
-button.filter-pill:hover:not(:disabled) {
-  background: var(--bg-muted);
-  border-color: var(--border-strong);
-  color: var(--text);
-}
-/*
-  Selected filter pill — quiet "I am the chosen one" treatment, NOT a CTA.
-  Earlier the active pill borrowed the full `--accent` fill, which read as
-  a primary button competing with the real action ("+ New memory") next to
-  it. Users would mis-read the count (e.g. "All 0") as a clickable big
-  orange button. We now keep the selection bordered + slightly weighted so
-  it still looks "on" but stops shouting; the lone primary button on the
-  right keeps its monopoly on the accent color.
-*/
-.filter-pill.active {
-  background: var(--bg-subtle);
-  border-color: var(--border-strong);
-  color: var(--text);
-  font-weight: 600;
-}
-button.filter-pill.active:hover:not(:disabled) {
-  background: var(--bg-muted);
-  border-color: var(--border-strong);
-  color: var(--text);
-}
-.filter-pill-count {
-  font-size: 11px;
-  opacity: 0.7;
-}
-button.filter-pill:hover:not(:disabled) .filter-pill-count,
-.filter-pill.active .filter-pill-count {
-  color: currentColor;
-  opacity: 0.9;
-}
-.example-card-actions {
-  display: flex;
-  gap: 8px;
-  align-items: center;
-  flex-wrap: wrap;
-}
-.example-tags {
-  display: flex;
-  gap: 6px;
-  flex-wrap: wrap;
-  margin-top: 2px;
-}
-/* Tag variants — keep the worktree base .example-tag style; these add
-   color-coded variants for mode/platform pills. */
-.example-tag.platform-mobile { color: var(--accent); border-color: var(--accent-soft); background: var(--accent-soft); }
-.example-tag.mode-deck { color: var(--accent); border-color: var(--accent-soft); background: var(--accent-soft); }
-
-/* Example preview hover affordance + click-through overlay */
-.example-preview {
-  cursor: zoom-in;
-  transition: border-color 0.15s, box-shadow 0.15s;
-}
-.example-preview:hover {
-  border-color: var(--accent);
-  box-shadow: 0 6px 18px rgba(201, 100, 66, 0.10);
-}
-.example-preview:focus-visible {
-  outline: none;
-  border-color: var(--accent);
-  box-shadow: 0 0 0 3px var(--accent-soft);
-}
-.example-preview-overlay {
-  position: absolute;
-  bottom: 10px;
-  right: 10px;
-  padding: 5px 12px;
-  font-size: 11.5px;
-  font-weight: 500;
-  color: white;
-  background: rgba(28, 27, 26, 0.78);
-  border-radius: 999px;
-  opacity: 0;
-  transition: opacity 0.15s;
-  pointer-events: none;
-  letter-spacing: 0.02em;
-}
-.example-preview:hover .example-preview-overlay,
-.example-preview:focus-visible .example-preview-overlay { opacity: 1; }
-
-/* ============================================================
-   Loading primitives — spinner, skeletons, shimmer
-   ============================================================ */
-@keyframes icon-spin { to { transform: rotate(360deg); } }
-@keyframes shimmer {
-  0% { background-position: -400px 0; }
-  100% { background-position: 400px 0; }
-}
-.icon-spin { animation: icon-spin 0.9s linear infinite; transform-origin: center; }
-
-.loading-spinner {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  color: var(--text-muted);
-}
-.loading-spinner-label { font-size: 12px; }
-
-.skeleton-block,
-.skeleton-shimmer {
-  display: inline-block;
-  background-color: var(--bg-subtle);
-  background-image: linear-gradient(
-    90deg,
-    var(--bg-subtle) 0%,
-    var(--bg-muted) 50%,
-    var(--bg-subtle) 100%
-  );
-  background-size: 800px 100%;
-  animation: shimmer 1.4s linear infinite;
-}
-.skeleton-block + .skeleton-block { margin-top: 6px; }
-
-.design-card-skeleton { cursor: default; pointer-events: none; }
-.design-card-skeleton .design-card-thumb { background: none; }
-.design-card-skeleton .design-card-thumb::before,
-.design-card-skeleton .design-card-thumb::after { display: none; }
-.design-card-skeleton .design-card-thumb {
-  background-image: linear-gradient(
-    90deg,
-    var(--bg-subtle) 0%,
-    var(--bg-muted) 50%,
-    var(--bg-subtle) 100%
-  );
-  background-size: 800px 100%;
-  animation: shimmer 1.4s linear infinite;
-}
-
-.centered-loader {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  gap: 10px;
-  padding: 48px 12px;
-  color: var(--text-muted);
-}
-.centered-loader-label { font-size: 12.5px; letter-spacing: 0.01em; }
-
-/* ============================================================
-   Resizable entry sidebar
-   ============================================================ */
-.entry {
-  /* The sidebar width is driven by an inline style fed from local state,
-     with sensible bounds enforced in JS. */
-}
-.entry-side {
-  position: relative;
-  min-width: 280px;
-  max-width: 560px;
-  min-height: 0;
-}
-.entry-side-resizer {
-  position: absolute;
-  top: 0;
-  right: -3px;
-  width: 6px;
-  height: 100%;
-  cursor: col-resize;
-  z-index: 5;
-  background: transparent;
-  border: 0;
-  padding: 0;
-  transition: background-color 120ms ease;
-}
-.entry-side-resizer:hover,
-.entry-side-resizer.dragging { background: var(--accent-soft); }
-body.entry-resizing { cursor: col-resize; user-select: none; }
-
-/* ============================================================
-   Composer Import popover (coming-soon menu)
-   ============================================================ */
-.composer-import-wrap { position: relative; }
-.composer-import-menu {
-  position: absolute;
-  bottom: calc(100% + 6px);
-  left: 0;
-  min-width: 240px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  box-shadow: var(--shadow-md);
-  padding: 6px;
-  display: flex;
-  flex-direction: column;
-  gap: 2px;
-  z-index: 30;
-  animation: pop-in 180ms cubic-bezier(0.21, 1.02, 0.73, 1);
-}
-.composer-import-item {
-  display: flex;
-  align-items: center;
-  gap: 10px;
-  padding: 8px 10px;
-  background: transparent;
-  border: none;
-  border-radius: var(--radius-sm);
-  color: var(--text-muted);
-  cursor: not-allowed;
-  font-size: 12.5px;
-  text-align: left;
-  width: 100%;
-}
-.composer-import-item:hover {
-  background: var(--bg-subtle);
-  color: var(--text);
-}
-.composer-import-item-enabled {
-  cursor: pointer;
-  color: var(--text);
-}
-.composer-import-item-enabled:hover {
-  background: var(--bg-subtle);
-  color: var(--text);
-}
-.composer-import-item-label { flex: 1; }
-.composer-import-item-soon {
-  font-size: 10px;
-  letter-spacing: 0.05em;
-  text-transform: uppercase;
-  color: var(--text-faint);
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-pill);
-  padding: 2px 6px;
-}
-.composer-import-item .ico {
-  display: inline-flex;
-  width: 16px;
-  align-items: center;
-  justify-content: center;
-  color: var(--text-muted);
-}
-
-/* ============================================================
-   Chat header: conversation history dropdown
-   ============================================================ */
-.chat-history-wrap { position: static; display: inline-flex; align-items: center; }
-.chat-history-wrap .icon-only { position: relative; }
-.chat-history-wrap.open .icon-only { background: var(--bg-subtle); color: var(--text); }
-.chat-history-badge {
-  position: absolute;
-  top: 2px;
-  right: 2px;
-  min-width: 14px;
-  height: 14px;
-  padding: 0 3px;
-  font-size: 9.5px;
-  font-weight: 600;
-  letter-spacing: 0;
-  background: var(--accent);
-  color: white;
-  border-radius: 999px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  line-height: 1;
-}
-.chat-history-menu {
-  position: absolute;
-  top: calc(100% + 6px);
-  left: 12px;
-  right: 12px;
-  width: auto;
-  max-height: min(360px, calc(100vh - 160px));
-  display: flex;
-  flex-direction: column;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  box-shadow: var(--shadow-md);
-  z-index: 30;
-  overflow: hidden;
-  animation: pop-in 180ms cubic-bezier(0.21, 1.02, 0.73, 1);
-}
-.chat-history-menu-head {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 8px 10px;
-  border-bottom: 1px solid var(--border);
-  background: var(--bg-panel);
-}
-.chat-history-menu-title {
-  font-size: 11px;
-  text-transform: uppercase;
-  letter-spacing: 0.06em;
-  color: var(--text-muted);
-  font-weight: 600;
-}
-.chat-history-new {
-  display: inline-flex;
-  align-items: center;
-  gap: 4px;
-  padding: 3px 8px;
-  font-size: 11px;
-  border-radius: var(--radius-pill);
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  color: var(--text-muted);
-}
-.chat-history-new:hover { background: var(--bg-muted); color: var(--text); }
-.chat-history-list {
-  display: flex;
-  flex-direction: column;
-  gap: 1px;
-  padding: 4px 6px 6px;
-  overflow-y: auto;
-}
-.chat-history-empty {
-  padding: 16px 8px;
-  font-size: 12px;
-  color: var(--text-muted);
-  text-align: center;
-  font-style: italic;
-}
-.chat-conv-item {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  padding: 6px 8px;
-  border-radius: var(--radius-sm);
-  background: transparent;
-  border: 1px solid transparent;
-  color: var(--text);
-  font-size: 12.5px;
-  text-align: left;
-  cursor: pointer;
-  width: 100%;
-}
-.chat-conv-item:hover { background: var(--bg-subtle); }
-.chat-conv-item.active {
-  background: var(--accent-tint);
-  border-color: var(--accent-soft);
-  color: var(--text-strong);
-}
-.chat-conv-item-name {
-  flex: 1;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-.chat-conv-item-meta {
-  font-size: 10.5px;
-  color: var(--text-faint);
-  font-variant-numeric: tabular-nums;
-}
-.chat-conv-item-del {
-  width: 22px;
-  height: 22px;
-  padding: 0;
-  border: none;
-  background: transparent;
-  color: var(--text-faint);
-  border-radius: 4px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  opacity: 0;
-  transition: opacity 120ms ease, background-color 120ms ease, color 120ms ease;
-}
-.chat-conv-item:hover .chat-conv-item-del { opacity: 1; }
-.chat-conv-item-del:hover { background: var(--red-bg); color: var(--red); }
-
-/* ============================================================
-   Scroll-to-bottom button (chat)
-   ============================================================ */
-.chat-log-wrap { position: relative; flex: 1; min-height: 0; min-width: 0; display: flex; }
-.chat-log-wrap .chat-log { flex: 1; }
-.chat-jump-btn {
-  position: absolute;
-  bottom: 12px;
-  /* Margin-based centering (instead of `transform: translateX(-50%)`)
-   * so the slide-in / slide-out transition can animate `transform`
-   * without overwriting a centering translate. */
-  left: 0;
-  right: 0;
-  margin: 0 auto;
-  width: max-content;
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  padding: 6px 12px;
-  border-radius: var(--radius-pill);
-  background: var(--bg-panel);
-  border: 1px solid var(--border-strong);
-  color: var(--text);
-  font-size: 12px;
-  box-shadow: var(--shadow-sm);
-  cursor: pointer;
-  z-index: 6;
-  /* Default = hidden. The slide tucks below and scales down slightly to
-   * read as folding into the composer. Strong ease-out curve
-   * (cubic-bezier(0.23, 1, 0.32, 1)) replaces both built-in `ease` and
-   * the previous slightly-overshoot variant — Emil Kowalski's framework:
-   * UI animations should use strong custom ease-out, and entrance pills
-   * should not bounce. Exit is faster than enter (asymmetric timing):
-   * the user has already chosen to dismiss; system response should be
-   * decisive. */
-  opacity: 0;
-  transform: translateY(8px) scale(0.9);
-  visibility: hidden;
-  pointer-events: none;
-  transition:
-    opacity 140ms cubic-bezier(0.23, 1, 0.32, 1),
-    transform 140ms cubic-bezier(0.23, 1, 0.32, 1),
-    visibility 0s linear 140ms;
-}
-.chat-jump-btn-active {
-  opacity: 1;
-  transform: translateY(0) scale(1);
-  visibility: visible;
-  pointer-events: auto;
-  transition:
-    opacity 200ms cubic-bezier(0.23, 1, 0.32, 1),
-    transform 200ms cubic-bezier(0.23, 1, 0.32, 1),
-    visibility 0s linear 0s;
-}
-.chat-jump-btn:hover { background: var(--bg-subtle); border-color: var(--border-strong); }
-
-/* ============================================================
-   Assistant message footer (bottom-of-message status pill)
-   ============================================================ */
-.assistant-completion-row {
-  display: flex;
-  flex-direction: column;
-  align-items: flex-start;
-  gap: 6px;
-  margin-top: 10px;
-}
-.assistant-footer {
-  display: inline-flex;
-  align-items: center;
-  gap: 8px;
-  padding: 4px 10px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-pill);
-  font-size: 11.5px;
-  color: var(--text-muted);
-  width: fit-content;
-}
-.assistant-footer .dot {
-  width: 7px;
-  height: 7px;
-  border-radius: 50%;
-  background: var(--text-muted);
-}
-.assistant-footer .dot[data-active="true"] {
-  background: var(--accent);
-  animation: pulse 1.2s ease-in-out infinite;
-}
-.assistant-footer .assistant-label {
-  font-weight: 600;
-  color: var(--text-strong);
-  font-size: 11.5px;
-}
-.assistant-footer .assistant-stats {
-  font-variant-numeric: tabular-nums;
-  color: var(--text-muted);
-}
-.assistant-footer[data-unfinished="true"] {
-  background: var(--amber-bg);
-  border-color: color-mix(in srgb, var(--amber) 35%, var(--border));
-}
-.assistant-footer[data-unfinished="true"] .dot {
-  background: var(--amber);
-}
-
-/* ---------------------------------------------------------------------
- * Critique Theater (Phase 8). Role-keyed tinting via `data-role` so the
- * components hold no hex literals - the lane just declares which role
- * it represents and the cascade picks the right hue from
- * existing semantic tokens. Phase 9 will add the settings toggle that
- * gates the surrounding mount points.
- * ------------------------------------------------------------------- */
-.theater-stage {
-  display: flex;
-  flex-direction: column;
-  gap: 10px;
-  margin-top: 10px;
-  padding: 12px 14px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  color: var(--text);
-}
-.theater-stage-head {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 8px;
-}
-.theater-stage-title {
-  margin: 0;
-  font-size: 13px;
-  font-weight: 600;
-  color: var(--text-strong);
-}
-.theater-stage-rounds {
-  display: flex;
-  flex-direction: column;
-  gap: 4px;
-  margin: 0;
-  padding: 0;
-  list-style: none;
-}
-.theater-stage-round { display: contents; }
-.theater-stage-lanes {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-}
-
-.theater-round-divider {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 8px;
-  padding: 4px 0;
-  font-size: 11.5px;
-  color: var(--text-muted);
-  border-bottom: 1px dashed var(--border);
-}
-.theater-round-divider[data-status="in-progress"] {
-  color: var(--text);
-}
-.theater-round-score { font-variant-numeric: tabular-nums; }
-.theater-round-score[data-meets-threshold="true"] { color: var(--text-strong); }
-.theater-round-score[data-meets-threshold="false"] { color: var(--amber); }
-.theater-round-score-value { font-weight: 600; }
-.theater-round-score-scale { opacity: 0.7; }
-
-.theater-score-ticker {
-  display: flex;
-  gap: 10px;
-  align-items: center;
-  padding: 6px 0;
-}
-.theater-score-row {
-  position: relative;
-  display: flex;
-  align-items: flex-end;
-  gap: 4px;
-  height: 40px;
-  flex: 0 0 auto;
-}
-.theater-score-tick {
-  width: 6px;
-  background: color-mix(in srgb, var(--accent) 35%, transparent);
-  border-radius: 2px;
-}
-.theater-score-tick[data-meets-threshold="true"] {
-  background: var(--accent);
-}
-.theater-score-threshold {
-  position: absolute;
-  left: 0;
-  right: 0;
-  border-top: 1px dashed color-mix(in srgb, var(--accent) 55%, transparent);
-}
-.theater-score-meta {
-  display: flex;
-  flex-direction: column;
-  gap: 2px;
-  font-size: 11.5px;
-  color: var(--text-muted);
-}
-.theater-score-value {
-  font-size: 16px;
-  font-weight: 600;
-  font-variant-numeric: tabular-nums;
-  color: var(--text-strong);
-}
-.theater-score-threshold-label { font-size: 10.5px; }
-
-.theater-lane {
-  display: flex;
-  flex-direction: column;
-  gap: 4px;
-  padding: 6px 8px;
-  border-radius: var(--radius-sm);
-  border-left: 3px solid var(--border);
-  background: var(--bg-panel);
-}
-.theater-lane[data-active="true"] {
-  background: color-mix(in srgb, var(--accent) 6%, var(--bg-panel));
-}
-.theater-lane[data-role="designer"] { border-left-color: color-mix(in srgb, var(--accent) 60%, var(--border)); }
-.theater-lane[data-role="critic"] { border-left-color: color-mix(in srgb, var(--amber) 60%, var(--border)); }
-.theater-lane[data-role="brand"] { border-left-color: color-mix(in srgb, var(--purple, var(--accent)) 60%, var(--border)); }
-.theater-lane[data-role="a11y"] { border-left-color: color-mix(in srgb, var(--green, var(--accent)) 60%, var(--border)); }
-.theater-lane[data-role="copy"] { border-left-color: color-mix(in srgb, var(--blue, var(--accent)) 60%, var(--border)); }
-.theater-lane-head {
-  display: flex;
-  align-items: baseline;
-  justify-content: space-between;
-  gap: 8px;
-}
-.theater-lane-role {
-  font-size: 13px;
-  font-weight: 600;
-  color: var(--text-strong);
-}
-.theater-lane-mustfix {
-  font-size: 11px;
-  color: var(--amber);
-  font-variant-numeric: tabular-nums;
-}
-.theater-lane-rounds {
-  display: flex;
-  gap: 8px;
-  margin: 0;
-  padding: 0;
-  list-style: none;
-}
-.theater-lane-round {
-  display: flex;
-  align-items: center;
-  gap: 4px;
-  padding: 2px 6px;
-  border-radius: var(--radius-sm);
-  font-size: 11.5px;
-  color: var(--text-muted);
-  background: var(--bg-subtle);
-  font-variant-numeric: tabular-nums;
-}
-.theater-lane-round[data-state="pending"] { opacity: 0.5; }
-.theater-lane-round[data-state="open"][data-current="true"] {
-  background: color-mix(in srgb, var(--accent) 12%, var(--bg-subtle));
-  color: var(--text);
-}
-.theater-lane-round-n { font-weight: 600; }
-.theater-lane-round-score { font-weight: 600; color: var(--text-strong); }
-.theater-lane-round-dims {
-  display: inline-flex;
-  gap: 2px;
-  margin-left: 4px;
-}
-.theater-lane-round-dim {
-  width: 5px;
-  height: 5px;
-  border-radius: 50%;
-  background: color-mix(in srgb, var(--amber) 60%, transparent);
-}
-.theater-lane-round-dim[data-meets-half="true"] {
-  background: color-mix(in srgb, var(--accent) 70%, transparent);
-}
-
-.theater-interrupt {
-  padding: 4px 12px;
-  background: transparent;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-  color: var(--text);
-  font: inherit;
-  font-size: 11.5px;
-  cursor: pointer;
-}
-.theater-interrupt:hover { background: var(--bg-subtle); }
-.theater-interrupt:disabled { opacity: 0.6; cursor: default; }
-.theater-interrupt[data-pending="true"] {
-  background: color-mix(in srgb, var(--amber) 12%, var(--bg-subtle));
-}
-
-.theater-degraded {
-  display: flex;
-  flex-direction: column;
-  gap: 4px;
-  padding: 10px 12px;
-  margin-top: 10px;
-  background: color-mix(in srgb, var(--amber) 8%, var(--bg-subtle));
-  border: 1px solid color-mix(in srgb, var(--amber) 35%, var(--border));
-  border-radius: var(--radius);
-  color: var(--text);
-}
-.theater-degraded-heading {
-  margin: 0;
-  font-size: 12.5px;
-  font-weight: 600;
-  color: var(--text-strong);
-}
-.theater-degraded-reason { margin: 0; font-size: 11.5px; color: var(--text-muted); }
-
-.theater-collapsed {
-  display: inline-flex;
-  align-items: center;
-  gap: 8px;
-  margin-top: 10px;
-  padding: 4px 10px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-pill);
-  font-size: 11.5px;
-  color: var(--text-muted);
-}
-.theater-collapsed[data-phase="shipped"][data-ship-status="shipped"] {
-  background: color-mix(in srgb, var(--accent) 10%, var(--bg-subtle));
-  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
-}
-.theater-collapsed[data-phase="interrupted"],
-.theater-collapsed[data-phase="shipped"][data-ship-status="below_threshold"],
-.theater-collapsed[data-phase="shipped"][data-ship-status="timed_out"],
-.theater-collapsed[data-phase="failed"] {
-  background: color-mix(in srgb, var(--amber) 10%, var(--bg-subtle));
-  border-color: color-mix(in srgb, var(--amber) 35%, var(--border));
-}
-.theater-collapsed-badge {
-  font-weight: 600;
-  color: var(--text-strong);
-}
-
-.theater-transcript {
-  display: flex;
-  flex-direction: column;
-  gap: 10px;
-  padding: 12px 14px;
-  background: var(--bg-panel);
-  border: 1px dashed var(--border);
-  border-radius: var(--radius);
-  color: var(--text);
-}
-.theater-transcript-head {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 8px;
-}
-.theater-transcript-readonly {
-  font-size: 10.5px;
-  text-transform: uppercase;
-  letter-spacing: 0.06em;
-  color: var(--text-muted);
-}
-.theater-transcript-speed-label {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  font-size: 11px;
-  color: var(--text-muted);
-}
-.theater-transcript-speed {
-  padding: 2px 6px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-  background: var(--bg-subtle);
-  font: inherit;
-  font-size: 11.5px;
-  color: var(--text);
-}
-.theater-transcript-loading,
-.theater-transcript-empty,
-.theater-transcript-error { margin: 0; font-size: 12px; color: var(--text-muted); }
-.theater-transcript-lanes {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-}
-
-/* prefers-reduced-motion: dampen the ticker pulse and lane transitions. */
-@media (prefers-reduced-motion: reduce) {
-  .theater-score-tick,
-  .theater-lane,
-  .theater-lane-round { transition: none !important; }
-}
-
-.assistant-feedback-wrap {
-  display: inline-flex;
-  flex-direction: column;
-  align-items: flex-start;
-  gap: 6px;
-  max-width: min(360px, 100%);
-}
-.assistant-feedback {
-  display: inline-flex;
-  align-items: center;
-  gap: 2px;
-  margin-left: 2px;
-  padding-left: 8px;
-  border-left: 1px solid var(--border);
-  color: var(--text-muted);
-}
-.assistant-feedback-button {
-  position: relative;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  width: 22px;
-  height: 22px;
-  padding: 0;
-  border: 1px solid transparent;
-  border-radius: var(--radius-pill);
-  background: transparent;
-  color: var(--text-muted);
-  cursor: pointer;
-  overflow: visible;
-}
-.assistant-feedback-button:hover {
-  background: var(--bg-subtle);
-  border-color: var(--border);
-  color: var(--text-strong);
-}
-.assistant-feedback-button[data-selected="true"] {
-  color: var(--accent);
-  background: var(--accent-tint);
-  border-color: var(--accent-soft);
-}
-.assistant-feedback-button[data-selected="true"] svg {
-  fill: currentColor;
-}
-.assistant-feedback-burst {
-  position: absolute;
-  inset: 50% auto auto 50%;
-  width: 30px;
-  height: 30px;
-  pointer-events: none;
-  transform: translate(-50%, -50%);
-}
-.assistant-feedback-burst span {
-  position: absolute;
-  left: 50%;
-  top: 50%;
-  width: 4px;
-  height: 4px;
-  border-radius: 50%;
-  background: var(--accent);
-  opacity: 0;
-  animation: assistant-feedback-burst 620ms ease-out forwards;
-}
-.assistant-feedback-burst span:nth-child(2) {
-  background: var(--amber);
-  animation-delay: 25ms;
-  --burst-angle: 58deg;
-}
-.assistant-feedback-burst span:nth-child(3) {
-  background: var(--green);
-  animation-delay: 45ms;
-  --burst-angle: 116deg;
-}
-.assistant-feedback-burst span:nth-child(4) {
-  animation-delay: 65ms;
-  --burst-angle: 174deg;
-}
-.assistant-feedback-burst span:nth-child(5) {
-  background: var(--blue);
-  animation-delay: 85ms;
-  --burst-angle: 232deg;
-}
-.assistant-feedback-burst span:nth-child(6) {
-  background: var(--accent);
-  animation-delay: 105ms;
-  --burst-angle: 290deg;
-}
-
-@keyframes assistant-feedback-burst {
-  0% {
-    opacity: 0;
-    transform: rotate(var(--burst-angle, 0deg)) translate(0, 0) scale(0.5);
-  }
-  18% {
-    opacity: 1;
-  }
-  100% {
-    opacity: 0;
-    transform: rotate(var(--burst-angle, 0deg)) translate(18px, 0) scale(0.9);
-  }
-}
-
-.assistant-feedback-reasons {
-  width: min(340px, 100%);
-  padding: 8px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-  background: var(--bg-panel);
-  box-shadow: var(--shadow-xs);
-}
-.assistant-feedback-reason-title {
-  display: inline-flex;
-  align-items: center;
-  gap: 5px;
-  margin-bottom: 7px;
-  font-size: 11px;
-  font-weight: 650;
-  color: var(--text-muted);
-}
-.assistant-feedback-reason-emoji {
-  font-size: 12px;
-  line-height: 1;
-}
-.assistant-feedback-reason-options {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-}
-.assistant-feedback-reason-option {
-  display: inline-flex;
-  align-items: center;
-  gap: 5px;
-  min-height: 26px;
-  padding: 4px 8px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-pill);
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-  font-size: 11px;
-  line-height: 1.2;
-  cursor: pointer;
-  user-select: none;
-}
-.assistant-feedback-reason-option:hover {
-  border-color: var(--border-strong);
-  color: var(--text-strong);
-}
-.assistant-feedback-reason-option[data-selected="true"] {
-  border-color: var(--accent-soft);
-  background: var(--accent-tint);
-  color: var(--accent);
-}
-.assistant-feedback-reason-option input {
-  width: 12px;
-  height: 12px;
-  margin: 0;
-  accent-color: var(--accent);
-}
-.assistant-feedback-custom {
-  width: 100%;
-  margin-top: 8px;
-  padding: 7px 8px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-  background: var(--bg-subtle);
-  color: var(--text);
-  font: inherit;
-  font-size: 11px;
-  line-height: 1.4;
-  resize: vertical;
-}
-.assistant-feedback-custom:focus {
-  outline: none;
-  border-color: var(--accent);
-  box-shadow: 0 0 0 2px var(--accent-tint);
-}
-.assistant-feedback-submit {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  min-height: 26px;
-  padding: 4px 10px;
-  border: 1px solid var(--accent-soft);
-  border-radius: var(--radius-pill);
-  background: var(--accent);
-  color: #fff;
-  font-size: 11px;
-  font-weight: 650;
-  cursor: pointer;
-}
-.assistant-feedback-submit:hover:not(:disabled) {
-  background: var(--accent-hover);
-}
-.assistant-feedback-submit:disabled {
-  opacity: 0.45;
-  cursor: default;
-}
-.assistant-feedback-actions {
-  display: flex;
-  justify-content: flex-end;
-  margin-top: 8px;
-}
-.assistant-feedback-discord-note {
-  margin: 7px 0 0;
-  color: var(--text-muted);
-  font-size: 11px;
-  line-height: 1.35;
-}
-.assistant-feedback-discord-note a {
-  color: #5865f2;
-  font-weight: 650;
-  text-decoration: underline;
-  text-decoration-thickness: 1px;
-  text-underline-offset: 2px;
-}
-.assistant-feedback-discord-note a:hover {
-  color: color-mix(in srgb, #5865f2 78%, var(--text-strong));
-}
-
-.unfinished-todos {
-  margin-top: 10px;
-  width: min(520px, 100%);
-  padding: 10px 12px;
-  border: 1px solid color-mix(in srgb, var(--amber) 35%, var(--border));
-  border-radius: var(--radius);
-  background: color-mix(in srgb, var(--amber) 8%, var(--bg-panel));
-  color: var(--text);
-}
-.unfinished-todos-head {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 10px;
-}
-.unfinished-todos-title {
-  font-size: 12px;
-  font-weight: 700;
-  color: var(--text-strong);
-}
-.unfinished-todos-continue {
-  flex: 0 0 auto;
-  border: 1px solid color-mix(in srgb, var(--amber) 45%, var(--border-strong));
-  border-radius: var(--radius-sm);
-  background: var(--bg-panel);
-  color: var(--text-strong);
-  font: inherit;
-  font-size: 12px;
-  font-weight: 650;
-  padding: 5px 9px;
-  cursor: pointer;
-}
-.unfinished-todos-continue:hover {
-  background: var(--bg-subtle);
-}
-.unfinished-todos-list {
-  margin: 8px 0 0;
-  padding-left: 18px;
-  font-size: 12.5px;
-  color: var(--text-muted);
-}
-.unfinished-todos-list li + li {
-  margin-top: 3px;
-}
-.unfinished-todos-more {
-  margin-top: 6px;
-  font-size: 12px;
-  color: var(--text-muted);
-}
-
-/* ============================================================
-   Coming-soon disabled affordance for viewer toolbar buttons
-   ============================================================ */
-.viewer-action[data-coming-soon='true'] {
-  position: relative;
-  z-index: 5;
-  opacity: 0.55;
-  cursor: not-allowed;
-}
-.viewer-action[data-coming-soon='true']:hover { background: var(--bg-subtle); color: var(--text-muted); }
-.viewer-action[data-coming-soon='true']::after {
-  content: 'Coming soon';
-  position: absolute;
-  bottom: calc(100% + 6px);
-  left: 50%;
-  transform: translateX(-50%);
-  background: rgba(17, 24, 39, 0.95);
-  color: white;
-  font-size: 10.5px;
-  padding: 4px 8px;
-  border-radius: var(--radius-sm);
-  white-space: nowrap;
-  letter-spacing: 0.03em;
-  pointer-events: none;
-  opacity: 0;
-  transition: opacity 120ms ease;
-  z-index: 10;
-  text-transform: uppercase;
-  font-weight: 600;
-}
-.viewer-action[data-coming-soon='true']:hover::after { opacity: 1; }
-.viewer-toggle[data-coming-soon='true'] {
-  position: relative;
-  z-index: 5;
-  opacity: 0.55;
-  cursor: not-allowed;
-}
-.viewer-toggle[data-coming-soon='true']:hover { background: var(--bg-subtle); }
-.viewer-toggle[data-coming-soon='true']::after {
-  content: 'Coming soon';
-  position: absolute;
-  bottom: calc(100% + 6px);
-  left: 50%;
-  transform: translateX(-50%);
-  background: rgba(17, 24, 39, 0.95);
-  color: white;
-  font-size: 10.5px;
-  padding: 4px 8px;
-  border-radius: var(--radius-sm);
-  white-space: nowrap;
-  letter-spacing: 0.03em;
-  pointer-events: none;
-  opacity: 0;
-  transition: opacity 120ms ease;
-  z-index: 10;
-  text-transform: uppercase;
-  font-weight: 600;
-}
-.viewer-toggle[data-coming-soon='true']:hover::after { opacity: 1; }
-
-/* Polished toolbar button states — coordinate with the global system.
-   Hover: subtle bg + soft border so groups look intentional. */
-.viewer-action,
-.viewer-tab,
-.viewer-toggle {
-  border: 1px solid transparent;
-  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
-}
-.viewer-action:hover:not(:disabled):not([data-coming-soon='true']),
-.viewer-tab:hover { border-color: var(--border); }
-.viewer-tab.active { border-color: var(--border); }
-
-/* ============================================================
-   Composer — settings/upload icons consistent sizing + spacing
-   ============================================================ */
-.composer-row .icon-btn svg { display: block; }
-.composer-row .icon-btn.active { background: var(--bg-subtle); color: var(--text); }
-.composer-import {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-}
-.composer-send svg { display: block; }
-
-/* Make the composer's leading icons share the same hit target. */
-.composer-row .composer-icon-divider {
-  width: 1px;
-  height: 18px;
-  background: var(--border);
-  margin: 0 2px;
-}
-
-/* ============================================================
-   Deck navigation toolbar (prev / counter / next)
-   ============================================================ */
-.deck-nav {
-  display: inline-flex;
-  align-items: center;
-  gap: 2px;
-  padding: 0 6px;
-  margin-left: 4px;
-  border-left: 1px solid var(--border);
-  height: 28px;
-}
-.deck-nav-counter {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  min-width: 50px;
-  padding: 0 6px;
-  font-size: 12px;
-  color: var(--text-muted);
-  font-variant-numeric: tabular-nums;
-  letter-spacing: 0.02em;
-}
-
-/* ============================================================
-   Composer rename input style for inline conversation rename.
-   ============================================================ */
-.chat-conv-rename-input {
-  border: 1px solid var(--accent);
-  border-radius: var(--radius-sm);
-  background: var(--bg-panel);
-  outline: none;
-}
-
-/* ============================================================
-   Prompt template gallery
-   ============================================================ */
-.prompt-templates-panel { display: flex; flex-direction: column; gap: 16px; }
-.prompt-templates-panel > .tab-panel-toolbar {
-  position: sticky;
-  top: 0;
-  z-index: 20;
-  isolation: isolate;
-  margin: 0;
-  padding: 0 0 12px;
-  background: var(--bg);
-  border-bottom: 1px solid var(--border-soft);
-  box-shadow: var(--shadow-sm);
-}
-.prompt-templates-count {
-  margin-left: auto;
-  color: var(--text-muted);
-  font-size: 12px;
-}
-.prompt-templates-grid {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
-  gap: 14px;
-}
-.prompt-template-card {
-  display: flex;
-  flex-direction: column;
-  gap: 10px;
-  padding: 0;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: 10px;
-  overflow: hidden;
-  text-align: left;
-  cursor: pointer;
-  transition: border-color 0.15s ease, transform 0.15s ease;
-}
-.prompt-template-card:hover {
-  border-color: var(--border-strong);
-  transform: translateY(-1px);
-}
-.prompt-template-thumb {
-  position: relative;
-  width: 100%;
-  aspect-ratio: 4 / 3;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: var(--bg-subtle);
-  overflow: hidden;
-}
-.prompt-template-thumb img {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-  display: block;
-}
-.prompt-template-thumb-fallback {
-  color: var(--text-faint);
-}
-.prompt-template-thumb-play {
-  position: absolute;
-  right: 8px;
-  bottom: 8px;
-  background: rgba(0, 0, 0, 0.55);
-  color: #fff;
-  font-size: 10px;
-  width: 22px;
-  height: 22px;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  letter-spacing: 0;
-}
-.prompt-template-thumb-provider {
-  position: absolute;
-  left: 8px;
-  top: 8px;
-  background: rgba(0, 0, 0, 0.62);
-  color: #fff;
-  font-size: 10px;
-  font-weight: 600;
-  padding: 3px 7px;
-  border-radius: 999px;
-  letter-spacing: 0.02em;
-  backdrop-filter: blur(6px);
-  -webkit-backdrop-filter: blur(6px);
-  max-width: calc(100% - 16px);
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-.prompt-template-thumb-provider.is-hyperframes {
-  background: linear-gradient(135deg, #ff5e3a 0%, #f0c14b 100%);
-  color: #1a1410;
-}
-.prompt-template-meta {
-  display: flex;
-  flex-direction: column;
-  gap: 4px;
-  padding: 0 12px 12px;
-}
-.prompt-template-title {
-  font-size: 13px;
-  font-weight: 600;
-  color: var(--text);
-  line-height: 1.3;
-}
-.prompt-template-summary {
-  font-size: 12px;
-  color: var(--text-muted);
-  line-height: 1.4;
-  display: -webkit-box;
-  -webkit-line-clamp: 3;
-  -webkit-box-orient: vertical;
-  overflow: hidden;
-}
-.prompt-template-tags {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-  margin-top: 4px;
-}
-.prompt-template-category {
-  font-size: 10px;
-  font-weight: 600;
-  padding: 2px 6px;
-  border-radius: 4px;
-  background: var(--accent-tint);
-  color: var(--accent);
-  text-transform: uppercase;
-  letter-spacing: 0.04em;
-}
-.prompt-template-tag,
-.prompt-template-model {
-  font-size: 10px;
-  padding: 2px 6px;
-  border-radius: 4px;
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-}
-.prompt-template-model {
-  font-weight: 500;
-}
-.prompt-template-source {
-  font-size: 10px;
-  color: var(--text-faint);
-  margin-top: 6px;
-}
-.prompt-templates-footer {
-  font-size: 11px;
-  color: var(--text-faint);
-  padding-top: 12px;
-  border-top: 1px dashed var(--border);
-  text-align: center;
-}
-.prompt-template-modal-backdrop {
-  position: fixed;
-  inset: 0;
-  background: rgba(15, 15, 18, 0.45);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  z-index: 1500;
-  padding: 24px;
-}
-.prompt-template-modal {
-  position: relative;
-  background: var(--bg-panel);
-  border-radius: 14px;
-  width: min(820px, 100%);
-  max-height: calc(100vh - 48px);
-  max-height: min(90vh, calc(100dvh - 48px));
-  display: flex;
-  flex-direction: column;
-  min-height: 0;
-  border: 1px solid var(--border);
-  overflow: hidden;
-  box-shadow: 0 32px 80px rgba(0, 0, 0, 0.25);
-}
-.prompt-template-modal-head {
-  flex: 0 0 auto;
-  display: flex;
-  gap: 12px;
-  align-items: flex-start;
-  min-height: 56px;
-  padding: 18px 66px 0 18px;
-  background: var(--bg-panel);
-}
-.prompt-template-modal-titles { flex: 1; min-width: 0; }
-.prompt-template-modal-close {
-  position: absolute;
-  top: 20px;
-  right: 20px;
-  z-index: 20;
-  width: 36px;
-  height: 36px;
-  padding: 0;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  pointer-events: auto;
-}
-.prompt-template-modal-titles h2 {
-  font-size: 17px;
-  margin: 0 0 6px 0;
-  color: var(--text);
-}
-.prompt-template-modal-titles p {
-  margin: 0;
-  font-size: 13px;
-  color: var(--text-muted);
-  line-height: 1.45;
-}
-.prompt-template-modal-tags {
-  flex: 0 0 auto;
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-  padding: 10px 18px 0;
-}
-.prompt-template-modal-body {
-  flex: 1 1 auto;
-  min-height: 0;
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
-  padding: 16px 18px;
-  overflow: auto;
-}
-.prompt-template-modal-asset {
-  position: relative;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: #000;
-  border-radius: 8px;
-  overflow: hidden;
-  max-height: 360px;
-}
-.prompt-template-modal-asset img,
-.prompt-template-modal-asset video {
-  max-width: 100%;
-  max-height: 360px;
-  display: block;
-}
-.prompt-template-modal-asset-image-trigger {
-  display: block;
-  padding: 0;
-  margin: 0;
-  border: 0;
-  background: transparent;
-  cursor: zoom-in;
-  max-height: 360px;
-  line-height: 0;
-}
-.prompt-template-modal-asset-image-trigger img {
-  transition: transform 200ms ease;
-}
-.prompt-template-modal-asset-image-trigger:hover img,
-.prompt-template-modal-asset-image-trigger:focus-visible img {
-  transform: scale(1.02);
-}
-.prompt-template-modal-asset-expand {
-  position: absolute;
-  top: 10px;
-  right: 10px;
-  z-index: 1;
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  padding: 6px 10px;
-  border: 0;
-  border-radius: 999px;
-  background: rgba(0, 0, 0, 0.55);
-  color: #fff;
-  font-size: 11px;
-  font-weight: 600;
-  letter-spacing: 0.01em;
-  cursor: pointer;
-  backdrop-filter: blur(8px);
-  -webkit-backdrop-filter: blur(8px);
-  opacity: 0.78;
-  transition: opacity 120ms ease, background 120ms ease;
-}
-.prompt-template-modal-asset:hover .prompt-template-modal-asset-expand,
-.prompt-template-modal-asset-expand:hover,
-.prompt-template-modal-asset-expand:focus-visible {
-  opacity: 1;
-  background: rgba(0, 0, 0, 0.72);
-}
-.prompt-template-lightbox-backdrop {
-  position: fixed;
-  inset: 0;
-  z-index: 1600;
-  background: rgba(8, 9, 12, 0.94);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 48px;
-  cursor: zoom-out;
-  animation: prompt-template-lightbox-fade 140ms ease-out;
-}
-@keyframes prompt-template-lightbox-fade {
-  from { opacity: 0; }
-  to { opacity: 1; }
-}
-.prompt-template-lightbox-media {
-  max-width: 100%;
-  max-height: 100%;
-  width: auto;
-  height: auto;
-  display: block;
-  border-radius: 10px;
-  box-shadow: 0 28px 80px rgba(0, 0, 0, 0.6);
-  cursor: default;
-  background: #000;
-}
-.prompt-template-lightbox-close {
-  position: fixed;
-  top: 18px;
-  right: 18px;
-  width: 40px;
-  height: 40px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  border: 0;
-  border-radius: 999px;
-  background: rgba(255, 255, 255, 0.14);
-  color: #fff;
-  cursor: pointer;
-  backdrop-filter: blur(10px);
-  -webkit-backdrop-filter: blur(10px);
-  transition: background 120ms ease, transform 120ms ease;
-}
-.prompt-template-lightbox-close:hover,
-.prompt-template-lightbox-close:focus-visible {
-  background: rgba(255, 255, 255, 0.26);
-  transform: scale(1.05);
-}
-@media (max-width: 640px) {
-  .prompt-template-lightbox-backdrop { padding: 16px; }
-  .prompt-template-lightbox-close { top: 12px; right: 12px; }
-}
-.prompt-template-modal-prompt {
-  border: 1px solid var(--border);
-  border-radius: 8px;
-  background: var(--bg-subtle);
-  display: flex;
-  flex-direction: column;
-}
-.prompt-template-modal-prompt-head {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 8px 12px;
-  border-bottom: 1px solid var(--border);
-}
-.prompt-template-modal-prompt-label {
-  font-size: 11px;
-  font-weight: 600;
-  text-transform: uppercase;
-  letter-spacing: 0.05em;
-  color: var(--text-muted);
-}
-.prompt-template-modal-prompt-body {
-  margin: 0;
-  padding: 12px;
-  white-space: pre-wrap;
-  word-break: break-word;
-  font-family: var(--font-mono, ui-monospace, monospace);
-  font-size: 12px;
-  line-height: 1.5;
-  max-height: 320px;
-  overflow: auto;
-}
-.prompt-template-modal-foot {
-  flex: 0 0 auto;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  gap: 12px;
-  padding: 12px 18px;
-  border-top: 1px solid var(--border);
-  font-size: 11px;
-  color: var(--text-faint);
-}
-.prompt-template-license {
-  padding: 1px 6px;
-  background: var(--bg-subtle);
-  border-radius: 4px;
-  color: var(--text-muted);
-}
-
-/* Prompt template picker — reuses the design-system picker shell so the
-   trigger / popover already look right. We only restyle the avatar (image
-   thumb) and add the editable prompt body block below the trigger. */
-.prompt-template-picker .prompt-template-avatar {
-  background: var(--bg-subtle);
-}
-.prompt-template-picker .prompt-template-avatar img {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-  display: block;
-}
-.prompt-template-picker .prompt-template-avatar.fallback {
-  color: var(--text-muted);
-}
-.prompt-template-edit {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-  margin-top: 10px;
-  padding: 10px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-}
-.prompt-template-edit-head {
-  display: flex;
-  flex-direction: column;
-  gap: 2px;
-}
-.prompt-template-edit-label {
-  font-size: 11.5px;
-  font-weight: 600;
-  color: var(--text);
-  letter-spacing: 0.01em;
-}
-.prompt-template-edit-hint {
-  font-size: 10.5px;
-  color: var(--text-muted);
-  line-height: 1.4;
-}
-.prompt-template-edit-textarea {
-  width: 100%;
-  min-height: 96px;
-  padding: 8px 10px;
-  font-size: 12px;
-  line-height: 1.5;
-  font-family: inherit;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-  color: var(--text);
-  resize: vertical;
-}
-.prompt-template-edit-textarea:focus {
-  outline: none;
-  border-color: var(--accent);
-  box-shadow: 0 0 0 1px var(--accent);
-}
-.prompt-template-edit-empty {
-  margin-top: 6px;
-  font-size: 11px;
-  color: var(--text-muted);
-  font-style: italic;
-}
-.prompt-template-error {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 10px;
-  margin-top: 8px;
-  padding: 8px 10px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-left: 3px solid var(--danger, #c0392b);
-  border-radius: var(--radius-sm);
-  font-size: 12px;
-  color: var(--text);
-}
-.prompt-template-error-msg {
-  flex: 1;
-  min-width: 0;
-}
-.prompt-template-error-retry {
-  flex: none;
-  font-size: 11.5px !important;
-  padding: 4px 10px !important;
-}
-
-/* ============================================================
-   Pet — Codex-style floating companion + settings cards
-   ------------------------------------------------------------
-   The overlay anchors to the bottom-right via inline right/bottom
-   styles set by PetOverlay. The accent color cascades through the
-   `--pet-accent` custom property so every pet variant gets the
-   same halo / bubble border / focus ring without a class explosion.
-   ============================================================ */
-.pet-overlay {
-  position: fixed;
-  z-index: 90;
-  display: flex;
-  flex-direction: column;
-  align-items: flex-end;
-  gap: 10px;
-  pointer-events: none;
-  --pet-accent: var(--accent);
-}
-.pet-overlay > * { pointer-events: auto; }
-
-.pet-sprite {
-  position: relative;
-  /* The overlay sprite was 56px which read as a tiny postage stamp
-     against a 1280px+ canvas — bumped to 96px so the pet feels like
-     a present companion rather than a thumbnail. The image-mode
-     children inherit width/height: 100% via .pet-image, so atlas /
-     strip / static pets all scale up automatically. */
-  width: 96px;
-  height: 96px;
-  background: transparent;
-  border: 0;
-  box-shadow: none;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  cursor: grab;
-  user-select: none;
-  touch-action: none;
-  transition: transform 160ms ease;
-}
-.pet-sprite:hover {
-  transform: translateY(-2px);
-}
-.pet-sprite:active { cursor: grabbing; }
-.pet-sprite-glyph {
-  /* Glyph font-size scales with the sprite box (~0.55 ratio) so
-     emoji-only built-ins and the avatar mark stay legible at the
-     larger overlay size. */
-  font-size: 52px;
-  line-height: 1;
-  animation: var(--pet-anim, pet-float) 3.4s ease-in-out infinite;
-  filter: drop-shadow(0 1px 0 rgba(0,0,0,0.08));
-}
-.pet-sprite-shadow {
-  position: absolute;
-  bottom: -12px;
-  left: 50%;
-  width: 64px;
-  height: 8px;
-  background: rgba(0, 0, 0, 0.18);
-  border-radius: 50%;
-  filter: blur(4px);
-  transform: translateX(-50%);
-  animation: pet-shadow 3.4s ease-in-out infinite;
-}
-.pet-sprite-status {
-  position: absolute;
-  right: 8px;
-  top: 8px;
-  min-width: 20px;
-  height: 20px;
-  padding: 0 6px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  border-radius: var(--radius-pill);
-  background: var(--pet-accent);
-  color: white;
-  border: 2px solid var(--bg);
-  font-size: 11px;
-  font-weight: 700;
-  line-height: 1;
-  box-shadow: var(--shadow-sm);
-}
-
-@keyframes pet-bounce {
-  0%, 100% { transform: translateY(0); }
-  50% { transform: translateY(-6px); }
-}
-@keyframes pet-sway {
-  0%, 100% { transform: rotate(-4deg); }
-  50% { transform: rotate(4deg); }
-}
-@keyframes pet-float {
-  0%, 100% { transform: translateY(0) rotate(0); }
-  50% { transform: translateY(-4px) rotate(2deg); }
-}
-@keyframes pet-wiggle {
-  0%, 100% { transform: rotate(0); }
-  25% { transform: rotate(-6deg); }
-  75% { transform: rotate(6deg); }
-}
-@keyframes pet-shadow {
-  0%, 100% { transform: translateX(-50%) scale(1); opacity: 0.18; }
-  50% { transform: translateX(-50%) scale(0.85); opacity: 0.12; }
-}
-
-@media (prefers-reduced-motion: reduce) {
-  .pet-sprite-glyph,
-  .pet-sprite-shadow { animation: none !important; }
-}
-
-.pet-bubble {
-  max-width: 240px;
-  background: var(--bg-panel);
-  color: var(--text);
-  border: 1px solid var(--pet-accent);
-  border-radius: 12px;
-  padding: 10px 12px 8px;
-  box-shadow: var(--shadow-md);
-  font-size: 12.5px;
-  line-height: 1.4;
-  position: relative;
-  animation: pet-bubble-in 200ms ease-out;
-}
-.pet-bubble::after {
-  content: '';
-  position: absolute;
-  right: 18px;
-  bottom: -6px;
-  width: 12px;
-  height: 12px;
-  background: var(--bg-panel);
-  border-right: 1px solid var(--pet-accent);
-  border-bottom: 1px solid var(--pet-accent);
-  transform: rotate(45deg);
-}
-.pet-bubble-name {
-  font-weight: 600;
-  font-size: 12px;
-  color: var(--pet-accent);
-  margin-bottom: 2px;
-}
-.pet-bubble-line {
-  color: var(--text);
-}
-.pet-idle-quote {
-  margin: 0;
-  display: grid;
-  gap: 5px;
-}
-.pet-idle-quote blockquote {
-  margin: 0;
-  color: var(--text);
-  font-size: 12.5px;
-  line-height: 1.45;
-}
-.pet-idle-quote figcaption {
-  color: var(--text-muted);
-  font-size: 11px;
-  text-align: right;
-}
-.pet-idle-quote figcaption::before {
-  content: '— ';
-}
-.pet-task-list {
-  margin-top: 8px;
-  display: grid;
-  gap: 8px;
-}
-.pet-task-group {
-  display: grid;
-  gap: 5px;
-}
-.pet-task-group-title {
-  color: var(--text-faint);
-  font-size: 10px;
-  font-weight: 700;
-  letter-spacing: 0.04em;
-  text-transform: uppercase;
-}
-.pet-task-item {
-  width: 100%;
-  min-width: 0;
-  display: grid;
-  grid-template-columns: auto minmax(0, 1fr) auto;
-  align-items: center;
-  gap: 6px;
-  padding: 5px 7px;
-  border-radius: 8px;
-  border: 1px solid var(--border);
-  background: var(--bg-subtle);
-  color: var(--text);
-  text-align: left;
-  cursor: pointer;
-}
-.pet-task-item:hover {
-  background: var(--bg-muted);
-  border-color: var(--border-strong);
-}
-.pet-task-item--static {
-  cursor: default;
-}
-.pet-task-item--static:hover {
-  background: var(--bg-subtle);
-  border-color: var(--border);
-}
-.pet-task-dot {
-  width: 7px;
-  height: 7px;
-  border-radius: 999px;
-  background: var(--pet-accent);
-  box-shadow: 0 0 0 3px color-mix(in srgb, var(--pet-accent) 18%, transparent);
-}
-.pet-task-dot[data-pet-task-status="queued"] {
-  opacity: 0.62;
-}
-.pet-task-dot[data-pet-task-status="succeeded"] {
-  background: var(--success);
-  box-shadow: 0 0 0 3px color-mix(in srgb, var(--success) 18%, transparent);
-}
-.pet-task-dot[data-pet-task-status="failed"] {
-  background: var(--danger);
-  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 18%, transparent);
-}
-.pet-task-dot[data-pet-task-status="canceled"] {
-  background: var(--text-faint);
-  box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-faint) 18%, transparent);
-}
-.pet-task-name {
-  min-width: 0;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  font-size: 11.5px;
-  font-weight: 600;
-}
-.pet-task-count {
-  min-width: 18px;
-  height: 18px;
-  padding: 0 5px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  border-radius: var(--radius-pill);
-  background: var(--bg);
-  color: var(--text-muted);
-  border: 1px solid var(--border);
-  font-size: 10.5px;
-  font-weight: 700;
-}
-.pet-bubble-actions {
-  margin-top: 8px;
-  display: flex;
-  gap: 6px;
-  flex-wrap: wrap;
-}
-.pet-bubble-btn {
-  display: inline-flex;
-  align-items: center;
-  gap: 4px;
-  font-size: 11px;
-  padding: 3px 8px;
-  border-radius: var(--radius-pill);
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-  border: 1px solid var(--border);
-  cursor: pointer;
-}
-.pet-bubble-btn:hover {
-  background: var(--bg-muted);
-  color: var(--text);
-  border-color: var(--border-strong);
-}
-
-body.desktop-pet-shell {
-  width: 100vw;
-  height: 100vh;
-  margin: 0;
-  overflow: hidden;
-  background: transparent;
-}
-body.desktop-pet-shell .pet-overlay {
-  right: 18px !important;
-  bottom: 18px !important;
-}
-body.desktop-pet-shell .pet-bubble,
-body.desktop-pet-shell .pet-sprite {
-  -webkit-app-region: drag;
-}
-body.desktop-pet-shell .pet-task-item {
-  -webkit-app-region: no-drag;
-}
-
-@keyframes pet-bubble-in {
-  from { opacity: 0; transform: translateY(4px) scale(0.96); }
-  to { opacity: 1; transform: translateY(0) scale(1); }
-}
-
-/* ----- Entry sidebar adopt pill ----- */
-.pet-pill {
-  position: relative;
-  height: 24px;
-  padding: 0;
-  gap: 0;
-  cursor: default;
-}
-.pet-pill .pet-pill-glyph {
-  font-size: 12px;
-  line-height: 1;
-  display: inline-flex;
-  align-items: center;
-  width: 14px;
-  height: 14px;
-  justify-content: center;
-}
-.pet-pill > .pet-pill-main,
-.pet-pill > .pet-pill-toggle {
-  display: inline-flex;
-  align-items: center;
-  align-self: stretch;
-  background: transparent;
-  border: 0;
-  color: inherit;
-  font: inherit;
-  cursor: pointer;
-  padding: 0 7px;
-}
-.pet-pill > .pet-pill-main {
-  gap: 5px;
-  border-radius: var(--radius-pill) 0 0 var(--radius-pill);
-}
-.pet-pill > .pet-pill-toggle {
-  justify-content: center;
-  padding: 0 7px;
-  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
-  color: var(--text-muted);
-}
-.pet-pill > .pet-pill-main:hover,
-.pet-pill > .pet-pill-toggle:hover {
-  background: var(--bg-panel);
-  color: var(--text);
-}
-.pet-pill-divider {
-  width: 1px;
-  align-self: stretch;
-  margin: 4px 0;
-  background: var(--border);
-}
-.pet-pill-fresh {
-  border-color: var(--accent);
-  color: var(--text);
-  background: var(--accent-tint);
-}
-.pet-pill-fresh > .pet-pill-divider {
-  background: var(--accent);
-  opacity: 0.35;
-}
-.pet-pill-fresh:hover {
-  background: var(--accent-soft);
-  border-color: var(--accent);
-}
-.pet-pill-dot {
-  width: 6px;
-  height: 6px;
-  border-radius: 50%;
-  background: var(--accent);
-  box-shadow: 0 0 0 3px var(--accent-tint);
-  margin-left: 2px;
-}
-
-/* ----- Settings → Pets ----- */
-.pet-wake-controls {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-}
-.pet-source-head {
-  margin-top: 14px;
-  display: grid;
-  gap: 4px;
-}
-.pet-source-head h4 {
-  margin: 0;
-  font-size: 13px;
-  font-weight: 600;
-  color: var(--text-strong);
-}
-.pet-source-head .hint {
-  margin: 0;
-}
-.pet-action-status {
-  margin: 10px 0 0;
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  width: fit-content;
-  max-width: 100%;
-  padding: 6px 10px;
-  border-radius: var(--radius-sm);
-  border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--border));
-  background: color-mix(in srgb, var(--accent) 9%, var(--bg-panel));
-  color: var(--accent-strong);
-  font-size: 12px;
-  line-height: 1.35;
-}
-.pet-action-status span {
-  min-width: 0;
-  overflow-wrap: anywhere;
-}
-.pet-current-summary {
-  margin-top: 10px;
-  display: grid;
-  grid-template-columns: 42px minmax(0, 1fr);
-  align-items: center;
-  gap: 10px;
-  padding: 9px 10px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-}
-.pet-current-summary__sprite {
-  width: 42px;
-  height: 42px;
-  border-radius: 10px;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  overflow: hidden;
-  background: color-mix(in srgb, var(--pet-accent, var(--accent)) 12%, transparent);
-  border: 1px solid color-mix(in srgb, var(--pet-accent, var(--accent)) 30%, var(--border));
-}
-.pet-current-summary__copy {
-  display: grid;
-  gap: 1px;
-  min-width: 0;
-}
-.pet-current-summary__label {
-  color: var(--text-faint);
-  font-size: 10px;
-  text-transform: uppercase;
-  letter-spacing: 0.04em;
-}
-.pet-current-summary__copy strong {
-  min-width: 0;
-  color: var(--text-strong);
-  font-size: 13px;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.pet-current-summary__copy span:last-child {
-  min-width: 0;
-  color: var(--text-muted);
-  font-size: 11.5px;
-  line-height: 1.35;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.seg-btn.small {
-  padding: 4px 10px !important;
-  font-size: 12px !important;
-  display: inline-flex;
-  align-items: center;
-  gap: 4px;
-  border-radius: var(--radius-pill);
-  border: 1px solid var(--border);
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-  cursor: pointer;
-}
-.seg-btn.small.active {
-  background: var(--accent-tint);
-  color: var(--accent-strong);
-  border-color: var(--accent);
-}
-.seg-btn.small[disabled] {
-  opacity: 0.5;
-  cursor: not-allowed;
-}
-
-/* Pet source tabs — pill row that splits the panel into three
-   exclusive surfaces (built-in / custom / community). Sits flush
-   between the section head and the active tab's content so the user
-   reads it as "pick a source, then pick a pet inside it". */
-.pet-tabs {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-  margin-top: 12px;
-}
-.pet-tabs-top-row {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: 12px;
-}
-.pet-tabs-top-row .subtab-pill {
-  flex: 0 0 auto;
-}
-.pet-tabs-hint {
-  margin: 0;
-}
-
-.pet-grid {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
-  gap: 10px;
-  margin-top: 4px;
-}
-
-/* The Community tab stacks the codex grid + hatch panel; flatten
-   their default top margins so the spacing reads as one column
-   rather than two ad-hoc cards. */
-.pet-community {
-  display: flex;
-  flex-direction: column;
-  gap: 14px;
-  margin-top: 4px;
-}
-.pet-community .pet-codex,
-.pet-community .pet-hatch {
-  margin-top: 0;
-}
-
-/* Custom tab dashed wrapper — strip the top margin so it tucks
-   directly under the tab pills. */
-.pet-tabs + .pet-custom,
-.pet-tabs + div + .pet-custom {
-  margin-top: 4px;
-}
-
-/* Highlight the codex card whose name + image match the user's
-   current adoption (community pets land in the custom slot, so we
-   piggy-back on the same "active" affordance used by built-ins). */
-.pet-codex-card.active {
-  border-color: var(--accent);
-  box-shadow: 0 0 0 1px var(--accent) inset;
-}
-.pet-card {
-  --pet-accent: var(--accent);
-  position: relative;
-  display: flex;
-  align-items: center;
-  gap: 10px;
-  padding: 10px 12px;
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  cursor: pointer;
-  text-align: left;
-  color: var(--text);
-  transition: border-color 120ms ease, transform 120ms ease;
-}
-.pet-card:hover {
-  border-color: var(--border-strong);
-  transform: translateY(-1px);
-}
-.pet-card.active {
-  border-color: var(--pet-accent);
-  box-shadow: 0 0 0 1px var(--pet-accent) inset;
-  background: var(--bg-panel);
-}
-.pet-card-glyph {
-  font-size: 28px;
-  line-height: 1;
-  width: 40px;
-  height: 40px;
-  border-radius: 50%;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  background: color-mix(in srgb, var(--pet-accent) 14%, transparent);
-}
-.pet-card-meta {
-  display: flex;
-  flex-direction: column;
-  min-width: 0;
-  flex: 1;
-}
-.pet-card-name {
-  font-weight: 600;
-  font-size: 13px;
-  color: var(--text-strong);
-}
-.pet-card-flavor {
-  font-size: 11.5px;
-  color: var(--text-muted);
-  line-height: 1.3;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  display: -webkit-box;
-  -webkit-line-clamp: 2;
-  -webkit-box-orient: vertical;
-}
-.pet-card-cta,
-.pet-card-badge {
-  position: absolute;
-  top: 8px;
-  right: 8px;
-  font-size: 10.5px;
-  padding: 2px 7px;
-  border-radius: var(--radius-pill);
-  background: var(--bg-subtle);
-  color: var(--text-muted);
-  border: 1px solid var(--border);
-  display: inline-flex;
-  align-items: center;
-  gap: 3px;
-}
-.pet-card-badge {
-  background: color-mix(in srgb, var(--pet-accent) 16%, transparent);
-  border-color: var(--pet-accent);
-  color: var(--pet-accent);
-}
-
-.pet-custom {
-  margin-top: 18px;
-  padding: 14px;
-  border-radius: var(--radius);
-  border: 1px dashed var(--border-strong);
-  background: var(--bg-subtle);
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-}
-.pet-custom-head {
-  display: flex;
-  align-items: flex-start;
-  justify-content: space-between;
-  gap: 12px;
-}
-.pet-custom-head h4 {
-  margin: 0 0 4px;
-  font-size: 13px;
-}
-.pet-custom-preview {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  padding: 10px 12px;
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-}
-.pet-custom-sprite {
-  font-size: 32px;
-  line-height: 1;
-  width: 48px;
-  height: 48px;
-  /* Lock the sprite to a perfect square inside the flex preview row.
-     Without `flex-shrink: 0`, a long pet name or greeting in the
-     adjacent bubble shrinks the 48px box horizontally, squashing
-     the sprite and breaking the round halo. */
-  flex: 0 0 48px;
-  border-radius: 50%;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  background: color-mix(in srgb, var(--pet-accent, var(--accent)) 14%, transparent);
-}
-.pet-custom-bubble {
-  display: flex;
-  flex-direction: column;
-  gap: 2px;
-  font-size: 12.5px;
-  color: var(--text);
-  /* Allow the text column to shrink past its intrinsic content width
-     so long greetings wrap instead of pushing the sprite. */
-  min-width: 0;
-}
-.pet-custom-bubble strong {
-  color: var(--pet-accent, var(--accent));
-}
-.pet-custom-fields {
-  display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
-  gap: 10px;
-}
-.pet-swatches {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-  align-items: center;
-}
-.pet-swatch {
-  width: 22px;
-  height: 22px;
-  border-radius: 50%;
-  border: 2px solid transparent;
-  cursor: pointer;
-  padding: 0;
-  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.12);
-}
-.pet-swatch.active {
-  border-color: var(--text-strong);
-}
-.pet-swatch-picker {
-  width: 22px;
-  height: 22px;
-  border: 1px dashed var(--border-strong);
-  border-radius: 50%;
-  background: transparent;
-  padding: 0;
-  cursor: pointer;
-}
-
-/* ============================================================
-   Pet — discovery surfaces (welcome teaser, composer)
-   ============================================================ */
-
-/* ----- Welcome dialog teaser ----- */
-.welcome-pet-teaser {
-  margin-top: 10px;
-  display: flex;
-  align-items: center;
-  gap: 10px;
-  padding: 8px 12px;
-  background: linear-gradient(135deg, var(--accent-tint) 0%, var(--accent-soft) 100%);
-  border: 1px solid var(--accent);
-  border-radius: var(--radius);
-  cursor: pointer;
-  text-align: left;
-  color: var(--text);
-  width: 100%;
-}
-.welcome-pet-teaser:hover {
-  filter: brightness(1.02);
-}
-.welcome-pet-glyph {
-  font-size: 24px;
-  line-height: 1;
-  width: 36px;
-  height: 36px;
-  border-radius: 50%;
-  background: var(--bg-panel);
-  border: 1px solid var(--accent);
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  flex: none;
-}
-.welcome-pet-copy {
-  display: flex;
-  flex-direction: column;
-  flex: 1;
-  min-width: 0;
-}
-.welcome-pet-copy strong {
-  font-size: 12.5px;
-  color: var(--text-strong);
-}
-.welcome-pet-copy span {
-  font-size: 11px;
-  color: var(--text-muted);
-  line-height: 1.35;
-}
-.welcome-pet-cta {
-  display: inline-flex;
-  align-items: center;
-  gap: 4px;
-  font-size: 11.5px;
-  color: var(--accent-strong);
-  font-weight: 600;
-  white-space: nowrap;
-}
-
-/* ----- Composer pet button + popover ----- */
-.composer-pet-wrap {
-  position: relative;
-  display: inline-flex;
-}
-.composer-pet {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  padding: 4px 10px;
-  border-radius: var(--radius-pill);
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  color: var(--text-muted);
-  cursor: pointer;
-  font-size: 12px;
-}
-.composer-pet:hover {
-  background: var(--bg-muted);
-  color: var(--text);
-  border-color: var(--border-strong);
-}
-.composer-pet.adopted {
-  border-color: var(--accent);
-  color: var(--accent-strong);
-  background: var(--accent-tint);
-}
-.composer-pet-glyph {
-  font-size: 14px;
-  line-height: 1;
-}
-.composer-pet-label {
-  font-weight: 500;
-}
-.composer-pet-menu {
-  position: absolute;
-  bottom: calc(100% + 6px);
-  right: 0;
-  z-index: 30;
-  width: 260px;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  box-shadow: var(--shadow-md);
-  padding: 10px;
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-}
-.composer-pet-menu-head {
-  display: flex;
-  flex-direction: column;
-  gap: 2px;
-  padding: 0 2px 6px;
-  border-bottom: 1px solid var(--border-soft);
-}
-.composer-pet-menu-head strong {
-  font-size: 12px;
-  color: var(--text-strong);
-}
-.composer-pet-menu-head span {
-  font-size: 10.5px;
-  color: var(--text-muted);
-  font-family: var(--mono);
-}
-.composer-pet-menu-row {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  padding: 4px 8px;
-  border-radius: var(--radius-sm);
-  background: transparent;
-  border: 1px solid transparent;
-  color: var(--text-muted);
-  cursor: pointer;
-  font-size: 12px;
-  text-align: left;
-}
-.composer-pet-menu-row:hover {
-  background: var(--bg-subtle);
-  color: var(--text);
-}
-.composer-pet-menu-row.toggle {
-  background: var(--bg-subtle);
-  border-color: var(--border);
-}
-.composer-pet-menu-grid {
-  display: grid;
-  grid-template-columns: repeat(4, 1fr);
-  gap: 4px;
-}
-.composer-pet-menu-pet {
-  --pet-accent: var(--accent);
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 2px;
-  padding: 6px 2px;
-  border-radius: var(--radius-sm);
-  background: var(--bg-subtle);
-  border: 1px solid transparent;
-  cursor: pointer;
-  font-size: 10.5px;
-  color: var(--text-muted);
-}
-.composer-pet-menu-pet:hover {
-  border-color: var(--border-strong);
-  color: var(--text);
-}
-.composer-pet-menu-pet.active {
-  border-color: var(--pet-accent);
-  background: color-mix(in srgb, var(--pet-accent) 12%, transparent);
-  color: var(--text);
-}
-.composer-pet-menu-pet span:first-child {
-  font-size: 18px;
-  line-height: 1;
-}
-.composer-pet-menu-row.settings {
-  border-top: 1px solid var(--border-soft);
-  margin-top: 2px;
-  padding-top: 8px;
-  border-radius: 0;
-  border-bottom: 1px solid transparent;
-  border-left: 1px solid transparent;
-  border-right: 1px solid transparent;
-}
-
-/* ============================================================
-   Composer — consolidated Tools popover
-   ------------------------------------------------------------
-   The leading @ glyph now opens a single tabbed popover
-   containing MCP / Import / Pet quick actions plus a shortcut
-   to the full Settings dialog. Replaces three standalone row
-   buttons that were overflowing in narrow chats.
-   ============================================================ */
-.composer-tools-wrap {
-  position: relative;
-  display: inline-flex;
-}
-.composer-tools-trigger {
-  position: relative;
-}
-.composer-tools-at {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  width: 17px;
-  height: 17px;
-  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
-  font-size: 16px;
-  font-weight: 650;
-  line-height: 1;
-  letter-spacing: 0;
-  color: currentColor;
-  transform: translateY(-0.5px);
-}
-.composer-tools-trigger.active {
-  background: var(--bg-subtle);
-  color: var(--text);
-}
-.composer-tools-menu {
-  position: absolute;
-  bottom: calc(100% + 6px);
-  left: 0;
-  width: 320px;
-  max-width: calc(100vw - 32px);
-  z-index: 30;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  box-shadow: var(--shadow-md);
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-.composer-tools-tabs {
-  display: flex;
-  align-items: stretch;
-  gap: 4px;
-  padding: 4px;
-  border-bottom: 1px solid var(--border-soft);
-  background: var(--bg-subtle);
-}
-.composer-tools-tab {
-  flex: 1;
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  gap: 5px;
-  min-height: 30px;
-  padding: 6px 6px;
-  font-size: 11.5px;
-  background: transparent;
-  border: 1px solid transparent;
-  border-radius: var(--radius-sm);
-  color: var(--text-muted);
-  cursor: pointer;
-  white-space: nowrap;
-  min-width: 0;
-}
-.composer-tools-tab:hover { color: var(--text); }
-.composer-tools-tab.active {
-  background: var(--bg-panel);
-  color: var(--text);
-  border-color: var(--border);
-  box-shadow: var(--shadow-xs);
-}
-.composer-tools-tab-glyph {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  width: 14px;
-  height: 14px;
-  font-size: 13px;
-  line-height: 1;
-  flex: 0 0 auto;
-}
-.composer-tools-tab > svg,
-[role='tab'] > svg {
-  display: block;
-  flex: none;
-  align-self: center;
-}
-.composer-tools-content {
-  display: flex;
-  flex-direction: column;
-  gap: 4px;
-  padding: 6px;
-  max-height: 360px;
-  overflow-y: auto;
-}
-.composer-tools-filter {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-  padding: 2px 2px 6px;
-}
-.composer-tools-segments {
-  display: grid;
-  grid-template-columns: repeat(2, minmax(0, 1fr));
-  gap: 2px;
-  padding: 2px;
-  border: 1px solid var(--border-soft);
-  border-radius: var(--radius-sm);
-  background: var(--bg-subtle);
-}
-.composer-tools-segment {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  min-height: 28px;
-  padding: 5px 8px;
-  border: 1px solid transparent;
-  border-radius: calc(var(--radius-sm) - 1px);
-  background: transparent;
-  color: var(--text-muted);
-  font: inherit;
-  font-size: 11.5px;
-  cursor: pointer;
-  min-width: 0;
-}
-.composer-tools-segment:hover {
-  color: var(--text);
-  border-color: var(--border);
-}
-.composer-tools-segment.active {
-  background: var(--bg-panel);
-  color: var(--text);
-  border-color: var(--border);
-  font-weight: 600;
-  box-shadow: var(--shadow-xs);
-}
-.composer-tools-segment {
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.composer-tools-search {
-  width: 100%;
-  height: 30px;
-  padding: 0 9px;
-  border: 1px solid var(--border);
-  border-radius: var(--radius-sm);
-  background: var(--bg-panel);
-  color: var(--text);
-  font-size: 12px;
-}
-.composer-tools-search:focus {
-  border-color: var(--accent);
-  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
-}
-.composer-tools-empty {
-  padding: 14px 10px;
-  font-size: 12px;
-  color: var(--text-muted);
-  text-align: center;
-  line-height: 1.5;
-}
-.composer-tools-list {
-  display: flex;
-  flex-direction: column;
-  gap: 2px;
-}
-.composer-tools-section-label {
-  padding: 4px 6px 2px;
-  color: var(--text-muted);
-  font-size: 10px;
-  letter-spacing: 0.04em;
-  text-transform: uppercase;
-}
-.composer-tools-row {
-  display: inline-flex;
-  align-items: center;
-  gap: 8px;
-  padding: 6px 8px;
-  background: transparent;
-  border: 1px solid transparent;
-  border-radius: var(--radius-sm);
-  color: var(--text);
-  cursor: pointer;
-  text-align: left;
-  font-size: 12px;
-  width: 100%;
-}
-.composer-tools-row:hover {
-  background: var(--bg-subtle);
-  color: var(--text);
-}
-.composer-tools-row.active {
-  background: var(--bg-subtle);
-  border-color: var(--border);
-}
-.composer-tools-row-body {
-  display: inline-flex;
-  align-items: baseline;
-  gap: 6px;
-  flex: 1;
-  min-width: 0;
-}
-.composer-tools-row-body strong {
-  font-weight: 500;
-  font-size: 12px;
-}
-.composer-tools-row-meta {
-  font-size: 10.5px;
-  color: var(--text-faint);
-  text-transform: lowercase;
-}
-/* Plugins panel — each row is a horizontal split: the main button
-   applies the plugin, the side button opens the details modal. */
-.composer-tools-row--plugin {
-  padding: 0;
-  gap: 0;
-}
-.composer-tools-row--plugin .composer-tools-row-body {
-  flex-direction: column;
-  align-items: flex-start;
-  gap: 1px;
-}
-.composer-tools-row--plugin .composer-tools-row-meta {
-  text-transform: none;
-  white-space: normal;
-  display: -webkit-box;
-  -webkit-line-clamp: 2;
-  -webkit-box-orient: vertical;
-  overflow: hidden;
-}
-.composer-tools-row-main {
-  flex: 1;
-  display: inline-flex;
-  align-items: center;
-  gap: 8px;
-  padding: 6px 8px;
-  background: transparent;
-  border: none;
-  color: inherit;
-  font: inherit;
-  text-align: left;
-  cursor: pointer;
-  border-radius: 4px;
-  min-width: 0;
-}
-.composer-tools-row-main:hover {
-  background: var(--bg-subtle);
-}
-.composer-tools-row-main:disabled {
-  opacity: 0.6;
-  cursor: not-allowed;
-}
-.composer-tools-row-side {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  width: 28px;
-  padding: 0;
-  background: transparent;
-  border: none;
-  color: var(--text-muted);
-  cursor: pointer;
-  border-radius: 4px;
-}
-.composer-tools-row-side:hover {
-  color: var(--text);
-  background: var(--bg-subtle);
-}
-.composer-tools-row-pending {
-  font-size: 10.5px;
-  color: var(--text-faint);
-  margin-left: auto;
-}
-.composer-tools-row-action {
-  border-top: 1px solid var(--border-soft);
-  border-radius: 0;
-  margin-top: 4px;
-  padding-top: 8px;
-  color: var(--text-muted);
-}
-.composer-tools-row-action:hover { color: var(--text); }
-.composer-tools-row-toggle {
-  background: var(--bg-subtle);
-  border-color: var(--border);
-}
-.composer-tools-pet {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-}
-.composer-tools-pet-head {
-  padding: 0 4px 4px;
-}
-.composer-tools-pet-head .hint {
-  font-size: 11px;
-  color: var(--text-muted);
-}
-.composer-tools-pet-grid {
-  display: grid;
-  grid-template-columns: repeat(4, 1fr);
-  gap: 4px;
-  padding: 0 2px;
-}
-.composer-tools-pet-item {
-  --pet-accent: var(--accent);
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 2px;
-  padding: 6px 2px;
-  background: transparent;
-  border: 1px solid var(--border-soft);
-  border-radius: var(--radius-sm);
-  color: var(--text-muted);
-  font-size: 10.5px;
-  cursor: pointer;
-}
-.composer-tools-pet-item:hover {
-  border-color: var(--border-strong);
-  color: var(--text);
-}
-.composer-tools-pet-item.active {
-  border-color: var(--pet-accent);
-  background: color-mix(in srgb, var(--pet-accent) 12%, transparent);
-  color: var(--text);
-}
-.composer-tools-pet-item span:first-child {
-  font-size: 18px;
-  line-height: 1;
-}
-.composer-tools-settings {
-  display: inline-flex;
-  align-items: center;
-  gap: 8px;
-  padding: 9px 12px;
-  background: transparent;
-  border: none;
-  border-top: 1px solid var(--border-soft);
-  border-radius: 0;
-  color: var(--text);
-  cursor: pointer;
-  font-size: 12px;
-  text-align: left;
-  width: 100%;
-}
-.composer-tools-settings:hover {
-  background: var(--bg-subtle);
-}
-
-/* ============================================================
-   Pet — uploaded image / spritesheet rendering
-   ------------------------------------------------------------
-   The face element switches between an emoji glyph and an
-   image/spritesheet via the PetSpriteFace component. Static
-   images inherit the float animation from .pet-sprite-glyph;
-   spritesheets get their own pet-frames keyframes which the
-   component drives with an inline `animation: …` string so
-   each pet can choose its own (frames, fps) combo.
-   ============================================================ */
-.pet-image {
-  display: inline-block;
-  background-position: 0 0;
-  background-repeat: no-repeat;
-  background-size: 100% 100%;
-  width: 100%;
-  height: 100%;
-  /* Pixel art-friendly upscale so 32×32 sheets stay crisp at 56 px. */
-  image-rendering: pixelated;
-  image-rendering: -moz-crisp-edges;
-}
-.pet-image.static {
-  /* Single static image — let the parent .pet-sprite-glyph float
-     animation drive subtle movement. */
-  background-size: contain;
-  background-position: center;
-}
-
-/* Spritesheet keyframe used by PetSpriteFace inline animation. The
-   from/to span 0% → 100%, which CSS resolves against
-   (container_width − background_width). With background-size set to
-   `${frames * 100}% 100%` each `steps()` tick lands on a frame edge. */
-@keyframes pet-frames {
-  from { background-position-x: 0%; }
-  to { background-position-x: 100%; }
-}
-
-/* Full-atlas mode: the sprite renders the active row from a Codex
-   8x9 grid. background-position-y is set inline to the row offset and
-   the keyframe walks frames inside that row. We use a CSS variable
-   for the endpoint so a single keyframe block can drive any row's
-   `frames`/`cols` combination — `--pet-atlas-end-x` is set per row by
-   PetSpriteFace based on `frames / (cols - 1) * 100%`. */
-.pet-image.atlas {
-  background-repeat: no-repeat;
-  /* The sprite element switches rows on the fly; smooth out the
-     row transitions so direction changes feel less jarring without
-     blurring the per-frame `steps()` cadence. */
-  transition: background-position-y 220ms ease;
-}
-@keyframes pet-atlas-frames {
-  from { background-position-x: 0%; }
-  to { background-position-x: var(--pet-atlas-end-x, 100%); }
-}
-
-@media (prefers-reduced-motion: reduce) {
-  .pet-image.frames,
-  .pet-image.atlas { animation: none !important; background-position-x: 0% !important; }
-}
-
-/* PetSettings — upload row, frames/fps grid, error caption */
-.pet-image-controls {
-  display: flex;
-  flex-direction: column;
-  gap: 6px;
-  margin-top: 4px;
-  padding: 10px;
-  border-radius: var(--radius);
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-}
-.pet-image-row {
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  flex-wrap: wrap;
-}
-.seg-btn.small.ghost {
-  background: transparent;
-  border-color: var(--border);
-  color: var(--text-muted);
-}
-.seg-btn.small.ghost:hover {
-  background: var(--bg-subtle);
-  color: var(--text);
-  border-color: var(--border-strong);
-}
-.pet-image-error {
-  color: var(--red) !important;
-}
-.pet-image-frames {
-  margin-top: 6px;
-  display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
-  gap: 8px;
-}
-.pet-image-frames .field input {
-  width: 100%;
-}
-
-/* Make the custom-pet preview / settings sprite respect the rounded
-   accent halo used for emoji glyphs even when an image is rendered. */
-.pet-custom-sprite {
-  overflow: hidden;
-}
-
-/* --- Codex hatch-pet atlas import + AI generation panels ------------ */
-
-.pet-atlas-preview {
-  margin-top: 14px;
-  padding: 14px;
-  border: 1px solid var(--border);
-  border-radius: 12px;
-  background: var(--surface);
-  display: grid;
-  gap: 12px;
-}
-
-.pet-atlas-head {
-  display: flex;
-  align-items: flex-start;
-  justify-content: space-between;
-  gap: 12px;
-}
-
-.pet-atlas-head strong {
-  font-size: 13px;
-  font-weight: 600;
-  color: var(--text);
-}
-
-.pet-atlas-thumb {
-  width: 100%;
-  aspect-ratio: 1536 / 1872;
-  max-height: 240px;
-  background-repeat: no-repeat;
-  background-position: center;
-  background-size: contain;
-  background-color: var(--surface-2, #f6f4ee);
-  border-radius: 8px;
-  border: 1px solid var(--border);
-  image-rendering: pixelated;
-}
-
-.pet-atlas-rows {
-  display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
-  gap: 6px;
-}
-
-.pet-atlas-row {
-  display: flex;
-  flex-direction: column;
-  align-items: flex-start;
-  gap: 2px;
-  padding: 8px 10px;
-  border: 1px solid var(--border);
-  border-radius: 8px;
-  background: var(--bg);
-  cursor: pointer;
-  font: inherit;
-  text-align: left;
-  transition: border-color 120ms ease, background 120ms ease;
-}
-
-.pet-atlas-row:hover {
-  border-color: var(--accent);
-}
-
-.pet-atlas-row.active {
-  border-color: var(--accent);
-  background: color-mix(in srgb, var(--accent) 10%, transparent);
-}
-
-.pet-atlas-row[disabled] {
-  opacity: 0.55;
-  cursor: progress;
-}
-
-.pet-atlas-row-name {
-  font-size: 12px;
-  font-weight: 600;
-  color: var(--text);
-}
-
-.pet-atlas-row-meta {
-  font-size: 11px;
-  color: var(--text-muted);
-}
-
-.pet-atlas-actions {
-  display: flex;
-  justify-content: flex-end;
-  gap: 8px;
-}
-
-/* "Hatch with AI" panel */
-
-.pet-hatch {
-  margin-top: 14px;
-  padding: 14px;
-  border: 1px dashed var(--border);
-  border-radius: 12px;
-  background: color-mix(in srgb, var(--accent) 4%, transparent);
-  display: grid;
-  gap: 10px;
-}
-
-.pet-hatch-head h4 {
-  margin: 0 0 4px 0;
-  font-size: 13px;
-  font-weight: 600;
-}
-
-.pet-hatch-prompt {
-  margin: 0;
-  padding: 10px 12px;
-  background: var(--bg);
-  border: 1px solid var(--border);
-  border-radius: 8px;
-  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
-  font-size: 11px;
-  line-height: 1.5;
-  color: var(--text);
-  max-height: 200px;
-  overflow: auto;
-  white-space: pre-wrap;
-  word-break: break-word;
-}
-
-.pet-hatch-actions {
-  display: flex;
-  gap: 8px;
-}
-
-.pet-hatch-foot {
-  margin: 0;
-}
-
-/* "Recently hatched" — Codex pets discovered under ~/.codex/pets/ */
-
-.pet-codex {
-  margin-top: 14px;
-  padding: 14px;
-  border: 1px solid var(--border);
-  border-radius: 12px;
-  background: var(--surface);
-  display: grid;
-  gap: 10px;
-}
-
-.pet-codex-head {
-  display: flex;
-  align-items: flex-start;
-  justify-content: space-between;
-  gap: 12px;
-}
-
-.pet-codex-head h4 {
-  margin: 0 0 4px 0;
-  font-size: 13px;
-  font-weight: 600;
-}
-
-/* Right-aligned button cluster in the codex head — keeps the
-   "Download community pets" + "Refresh" buttons on a single row even
-   when the section title wraps. */
-.pet-codex-head-actions {
-  display: flex;
-  gap: 6px;
-  flex-wrap: wrap;
-  justify-content: flex-end;
-}
-
-.pet-codex-empty {
-  margin: 4px 0 0 0;
-}
-
-/* Inline status line shown after a community sync completes. Stays in
-   the section flow so the result reads as part of the same control
-   group as the trigger button. The error variant tints toward the
-   accent without screaming red — sync errors are usually transient
-   network blips. */
-.pet-codex-sync-status {
-  margin: 6px 0 8px 0;
-  padding: 6px 10px;
-  border-radius: 6px;
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  font-size: 12px;
-  color: var(--text-muted);
-}
-
-.pet-codex-sync-status.error {
-  background: color-mix(in srgb, var(--accent) 8%, var(--bg-subtle));
-  border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
-  color: var(--text-strong);
-}
-
-.pet-codex-grid {
-  display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
-  gap: 10px;
-}
-
-.pet-codex-card {
-  position: relative;
-  display: grid;
-  grid-template-columns: 56px 1fr;
-  align-items: center;
-  gap: 10px;
-  padding: 10px;
-  border: 1px solid var(--border);
-  border-radius: 10px;
-  background: var(--bg);
-}
-
-.pet-codex-thumb {
-  position: relative;
-  width: 56px;
-  height: 56px;
-  border-radius: 8px;
-  border: 1px solid var(--border);
-  background-color: var(--surface-2, #f6f4ee);
-  background-image: var(--pet-codex-src);
-  background-repeat: no-repeat;
-  /* Show only the top-left cell of the 8x9 atlas — that is the
-     `idle` row, frame 0, and reads as a recognizable still even
-     before adoption. */
-  background-position: 0 0;
-  background-size: 800% 900%;
-  image-rendering: pixelated;
-}
-
-/* Hover preview — a larger floating overlay that plays through every
-   cell of the 8x9 Codex atlas so users can see what animations (idle,
-   running, waving, jumping, failed, waiting, …) the pet has before they
-   adopt it. The overlay is purely decorative: pointer-events: none
-   prevents it from stealing hover from the card, and aria-hidden keeps
-   it out of the accessibility tree. */
-.pet-codex-thumb-preview {
-  position: absolute;
-  bottom: calc(100% + 8px);
-  left: 50%;
-  width: 128px;
-  /* 128 * (208/192) ≈ 138.7 — matches a single atlas cell aspect. */
-  height: 139px;
-  border-radius: 10px;
-  border: 1px solid var(--border);
-  background-color: var(--surface-2, #f6f4ee);
-  background-image: var(--pet-codex-src);
-  background-repeat: no-repeat;
-  background-position: 0% 0%;
-  background-size: 800% 900%;
-  image-rendering: pixelated;
-  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.22);
-  opacity: 0;
-  pointer-events: none;
-  transform: translate(-50%, 6px) scale(0.82);
-  transform-origin: 50% 100%;
-  transition:
-    opacity 140ms ease,
-    transform 160ms ease;
-  z-index: 20;
-}
-
-.pet-codex-card:hover .pet-codex-thumb-preview,
-.pet-codex-card:focus-within .pet-codex-thumb-preview {
-  opacity: 1;
-  transform: translate(-50%, 0) scale(1);
-  /* 8 column steps (1 row) in 0.6s → ~13 fps per animation, then the row
-     index advances every 0.6s for a full 5.4s sweep through all 9 rows.
-     Animating background-position-x + -y as two independent animations
-     keeps the nested loop in pure CSS. */
-  animation:
-    pet-codex-preview-col 0.6s steps(8, jump-none) infinite,
-    pet-codex-preview-row 5.4s steps(9, jump-none) infinite;
-}
-
-@keyframes pet-codex-preview-col {
-  from { background-position-x: 0%; }
-  to { background-position-x: 100%; }
-}
-
-@keyframes pet-codex-preview-row {
-  from { background-position-y: 0%; }
-  to { background-position-y: 100%; }
-}
-
-@media (prefers-reduced-motion: reduce) {
-  .pet-codex-card:hover .pet-codex-thumb-preview,
-  .pet-codex-card:focus-within .pet-codex-thumb-preview {
-    animation: none;
-    background-position: 0% 0%;
-  }
-}
-
-.pet-codex-meta {
-  display: grid;
-  gap: 2px;
-  min-width: 0;
-  padding-right: 8px;
-}
-
-.pet-codex-title-row {
-  min-width: 0;
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-}
-
-.pet-codex-meta strong {
-  font-size: 12px;
-  font-weight: 600;
-  color: var(--text);
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
-.pet-codex-default-badge {
-  flex: none;
-  padding: 1px 6px;
-  border-radius: var(--radius-pill);
-  border: 1px solid color-mix(in srgb, var(--accent) 26%, var(--border));
-  background: color-mix(in srgb, var(--accent) 8%, transparent);
-  color: var(--accent-strong);
-  font-size: 9.5px;
-  font-weight: 600;
-  line-height: 1.4;
-}
-
-.pet-codex-description {
-  font-size: 11px;
-  color: var(--text-muted);
-  display: -webkit-box;
-  -webkit-line-clamp: 2;
-  -webkit-box-orient: vertical;
-  overflow: hidden;
-}
-
-/* Adopt button: absolutely positioned at card's right edge so the
-   meta text always fills the full remaining width. Hidden until hover;
-   adopted (active) icon-only check stays visible at all times. */
-.pet-codex-adopt-btn {
-  position: absolute;
-  right: 10px;
-  top: 50%;
-  transform: translateY(-50%);
-}
-.pet-codex-adopt-btn:not(.active) {
-  opacity: 0;
-  pointer-events: none;
-  transition: opacity 120ms ease;
-}
-.pet-codex-card:hover .pet-codex-adopt-btn:not(.active),
-.pet-codex-card:focus-within .pet-codex-adopt-btn:not(.active) {
-  opacity: 1;
-  pointer-events: auto;
-}
-
-/* Slash-command popover in the chat composer */
-
-.slash-popover {
-  position: absolute;
-  bottom: 100%;
-  left: 0;
-  right: 0;
-  margin-bottom: 4px;
-  max-height: 260px;
-  overflow-y: auto;
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  box-shadow: var(--shadow-md);
-  z-index: 10;
-  padding: 6px;
-}
-
-.slash-popover-head {
-  display: flex;
-  align-items: baseline;
-  justify-content: space-between;
-  gap: 10px;
-  padding: 4px 6px 6px 6px;
-  border-bottom: 1px solid var(--border);
-  margin-bottom: 4px;
-  font-size: 11px;
-  color: var(--text-muted);
-  text-transform: uppercase;
-  letter-spacing: 0.04em;
-}
-
-.slash-popover-hint {
-  font-size: 10px;
-  text-transform: none;
-  letter-spacing: 0;
-  color: var(--text-muted);
-}
-
-.slash-item {
-  display: flex;
-  align-items: flex-start;
-  gap: 8px;
-  width: 100%;
-  background: transparent;
-  border: 1px solid transparent;
-  border-radius: 6px;
-  padding: 7px 8px;
-  text-align: left;
-  cursor: pointer;
-  font: inherit;
-}
-
-.slash-item:hover,
-.slash-item.active {
-  background: var(--bg-subtle);
-  border-color: var(--border);
-}
-
-.slash-item-icon {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  width: 22px;
-  height: 22px;
-  border-radius: 6px;
-  background: color-mix(in srgb, var(--accent) 12%, transparent);
-  color: var(--accent);
-  flex-shrink: 0;
-  margin-top: 1px;
-}
-
-.slash-item-body {
-  display: grid;
-  gap: 2px;
-  min-width: 0;
-  flex: 1;
-}
-
-.slash-item-row {
-  display: flex;
-  align-items: baseline;
-  gap: 8px;
-}
-
-.slash-item-label {
-  font-size: 12px;
-  font-weight: 600;
-  color: var(--text);
-  background: transparent;
-  padding: 0;
-}
-
-.slash-item-arg {
-  font-size: 10px;
-  color: var(--text-muted);
-  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
-}
-
-.slash-item-desc {
-  font-size: 11px;
-  color: var(--text-muted);
-  line-height: 1.35;
-}
-
-/* --- CJK (Chinese / Japanese / Korean) Comfort Pass --- */
-:lang(zh), :lang(zh-CN), :lang(zh-TW), :lang(ja), :lang(ko) {
-  --sans: "Inter", "PingFang SC", "Microsoft YaHei", "Noto Sans SC", "Hiragino Sans GB", "Source Han Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-}
-
-/* --- Arabic & Persian Comfort Pass --- */
-[dir="rtl"] {
-  --sans: "Cairo", "Inter", "Vazirmatn", "Noto Sans Arabic", "Segoe UI Arabic", "Tahoma", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-}
-
-/* Scope line-height increase to prose and chat to avoid breaking compact UI chrome */
-[dir="rtl"] .prose,
-[dir="rtl"] .chat-message,
-[dir="rtl"] .msg .user-text {
-  line-height: 1.7;
-}
-
-[dir="rtl"] .avatar-item,
-[dir="rtl"] .settings-tab,
-[dir="rtl"] .ds-item-name {
-  text-align: right;
-}
-
-[dir="rtl"] .app-chrome-back svg,
-[dir="rtl"] .back-btn svg,
-[dir="rtl"] .icon-btn svg[data-lucide="chevron-left"],
-[dir="rtl"] .icon-btn svg[data-lucide="chevron-right"],
-[dir="rtl"] .icon-btn svg[data-lucide="arrow-left"],
-[dir="rtl"] .icon-btn svg[data-lucide="arrow-right"],
-[dir="rtl"] .newproj-tabs-arrow svg,
-[dir="rtl"] .icon-only svg[data-lucide="chevron-left"],
-[dir="rtl"] .icon-only svg[data-lucide="chevron-right"],
-[dir="rtl"] .icon-only svg[data-lucide="arrow-left"],
-[dir="rtl"] .icon-only svg[data-lucide="arrow-right"],
-[dir="rtl"] .welcome-pet-teaser svg {
-  transform: scaleX(-1);
-}
-
-[dir="rtl"] .title,
-[dir="rtl"] .meta {
-  text-align: right;
-}
-
-/* ============================================================
-   Quick Switcher (Cmd/Ctrl+P file palette)
-   ============================================================ */
-.qs-overlay {
-  position: fixed;
-  inset: 0;
-  background: rgba(13, 12, 10, 0.32);
-  /* Sits in the modal tier (alongside prompt-template-modal-overlay) so
-   * the palette renders above any open context menus or popovers. */
-  z-index: 1500;
-  display: flex;
-  align-items: flex-start;
-  justify-content: center;
-  padding-top: 12vh;
-}
-.qs-palette {
-  width: min(560px, 92vw);
-  background: var(--bg-panel);
-  border: 1px solid var(--border);
-  border-radius: var(--radius-lg);
-  box-shadow: var(--shadow-lg);
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-  color: var(--text);
-}
-.qs-input {
-  appearance: none;
-  border: 0;
-  outline: none;
-  background: transparent;
-  padding: 14px 16px;
-  font-size: 16px;
-  font-weight: 500;
-  color: var(--text);
-  border-bottom: 1px solid var(--border-soft);
-}
-.qs-input::placeholder {
-  color: var(--text-faint);
-}
-.qs-list {
-  max-height: 50vh;
-  overflow-y: auto;
-  padding: 4px 0;
-}
-.qs-empty {
-  padding: 20px 16px;
-  color: var(--text-muted);
-  text-align: center;
-  font-size: 13px;
-}
-.qs-row {
-  display: grid;
-  grid-template-columns: auto 1fr auto;
-  gap: 10px;
-  align-items: baseline;
-  padding: 7px 16px;
-  cursor: pointer;
-  border-left: 3px solid transparent;
-}
-.qs-row-active {
-  background: var(--accent-soft);
-  border-left-color: var(--accent);
-}
-.qs-name {
-  font-weight: 600;
-  font-size: 14px;
-  color: var(--text);
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-.qs-path {
-  color: var(--text-muted);
-  font-size: 12.5px;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  min-width: 0;
-}
-.qs-kind {
-  font-size: 10.5px;
-  letter-spacing: 0.06em;
-  text-transform: uppercase;
-  color: var(--text-faint);
-  font-weight: 600;
-}
-.qs-row-active .qs-kind {
-  color: var(--accent-strong);
-}
-.qs-footer {
-  display: flex;
-  gap: 14px;
-  padding: 8px 16px;
-  border-top: 1px solid var(--border-soft);
-  font-size: 11.5px;
-  color: var(--text-muted);
-}
-.qs-footer kbd {
-  background: var(--bg-subtle);
-  border: 1px solid var(--border);
-  border-radius: 3px;
-  padding: 0 4px;
-  margin-right: 2px;
-  font-family: ui-monospace, monospace;
-  font-size: 10px;
-  color: var(--text-muted);
-}
-
-/* Library section (Skills & Design Systems management) */
-
-.library-toolbar {
-  display: flex;
-  flex-direction: column;
-  gap: 10px;
-  margin-bottom: 16px;
-}
-/* Skills toolbar: two-row layout.
-   Row 1: search (grows) + New skill button (right, 12px gap).
-   Row 2: filter dropdowns with inline labels. */
-.library-toolbar.skills-toolbar {
-  flex-direction: column;
-  gap: 10px;
-}
-.skills-toolbar-top {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-}
-.skills-toolbar-top .library-search {
-  flex: 1 1 0;
-  min-width: 0;
-}
-
-/* Variant: filter pills on the left, primary action on the right.
-   Used by Memory's toolbar where there is no search input above. */
-.library-toolbar.is-row {
-  flex-direction: row;
-  align-items: center;
-  justify-content: space-between;
-  flex-wrap: wrap;
-}
-
-.library-toolbar.is-row .library-filters {
-  flex: 1;
-  min-width: 0;
-}
-
-/* Primary action button positioned at the right edge of `.library-toolbar`.
-   The default button line-height makes the icon and label vertically off
-   when sitting next to filter pills; tightening the line-height aligns the
-   plus icon with the text baseline. */
-.library-toolbar-action {
-  flex-shrink: 0;
-  display: inline-flex;
-  align-items: center;
-  gap: 6px;
-  line-height: 1;
-}
-
-.library-search {
-  width: 100%;
-  padding: 9px 12px;
-  border: 1px solid var(--border-strong);
-  border-radius: 10px;
-  font-size: 13px;
-  background: var(--bg-panel);
-  color: var(--text);
-  outline: none;
-  box-shadow: var(--shadow-xs);
-  cursor: text;
-  transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;
-}
-
-.library-search:hover {
-  background: var(--bg-elevated);
-  border-color: var(--border-strong);
-}
-
-.library-search:focus {
-  border-color: var(--selected);
-  box-shadow: 0 0 0 3px var(--selected-soft), var(--shadow-xs);
-}
-
-.library-filters {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 6px;
-}
-
-/*
- * Compact dropdown row for Skills "Type" + "Category" filters.
- * Pills don't scale once the option set crosses ~10 entries (Skills'
- * Category list has 14 today and is growing); they wrap to 2-3 rows
- * and push the actual cards far below the fold. A labelled  on macOS uses its own intrinsic min-height that ends up
+   shorter than  at the same `padding: 7px 10px` rule above, so any
+   form that flex-aligns an input next to a select (e.g. the memory editor's
+   Title + Type row) renders with mismatched heights. Stripping the native
+   chrome lets the shared padding and inherited line-height compute the
+   same box dimensions as the input, then we paint our own chevron via a
+   background SVG. The chevron color is a per-theme override so it stays
+   readable against the panel in both light and dark. */
+select {
+  padding-right: 32px;
+  appearance: none;
+  -webkit-appearance: none;
+  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%2374716b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E");
+  background-repeat: no-repeat;
+  background-position: right 10px center;
+  background-size: 12px 12px;
+}
+select::-ms-expand { display: none; }
+[data-theme='dark'] select {
+  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%239a9690' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E");
+  background-repeat: no-repeat;
+  background-position: right 10px center;
+  background-size: 12px 12px;
+}
+@media (prefers-color-scheme: dark) {
+  html:not([data-theme]) select {
+    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%239a9690' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 6 8 9 5'/%3E%3C/svg%3E");
+    background-repeat: no-repeat;
+    background-position: right 10px center;
+    background-size: 12px 12px;
+  }
+}
+textarea { resize: vertical; font-family: inherit; }
+
+.od-select {
+  position: relative;
+  width: 100%;
+  min-width: 0;
+}
+.od-select-trigger {
+  width: 100%;
+  min-width: 0;
+  min-height: 36px;
+  display: grid;
+  grid-template-columns: minmax(0, 1fr) auto;
+  align-items: center;
+  gap: 8px;
+  padding: 7px 10px;
+  font: inherit;
+  color: var(--text);
+  text-align: left;
+  background: var(--bg-panel);
+  border: 1px solid var(--border);
+  border-radius: var(--radius-sm);
+  cursor: pointer;
+  transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;
+}
+.od-select-trigger:hover:not(:disabled),
+.od-select-trigger[aria-expanded='true'] {
+  border-color: var(--border-strong);
+  background: var(--bg-subtle);
+}
+.od-select-trigger:focus-visible,
+.od-select-trigger[aria-expanded='true'] {
+  outline: none;
+  border-color: var(--selected);
+  box-shadow: 0 0 0 3px var(--selected-soft);
+}
+.od-select-trigger:disabled {
+  opacity: 0.55;
+  cursor: not-allowed;
+}
+.od-select-trigger svg {
+  color: var(--text-muted);
+  transition: transform 120ms ease;
+}
+.od-select-trigger[aria-expanded='true'] svg {
+  transform: rotate(180deg);
+}
+.od-select-value,
+.od-select-option-label {
+  min-width: 0;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.od-select-menu {
+  z-index: 9000;
+  padding: 4px;
+  overflow-y: auto;
+  overflow-x: hidden;
+  overscroll-behavior: contain;
+  max-width: calc(100vw - 24px);
+  background: var(--bg-panel);
+  border: 1px solid var(--border);
+  border-radius: var(--radius-sm);
+  box-shadow: var(--shadow-lg);
+}
+.od-select-menu.portal {
+  position: fixed;
+}
+.od-select-menu.inline {
+  position: absolute;
+  top: calc(100% + 4px);
+  left: 0;
+  right: 0;
+  max-height: min(280px, 48vh);
+}
+.od-select-option {
+  width: 100%;
+  min-width: 0;
+  min-height: 30px;
+  display: grid;
+  grid-template-columns: minmax(0, 1fr) 16px;
+  align-items: center;
+  gap: 8px;
+  padding: 6px 8px;
+  color: var(--text);
+  background: transparent;
+  border: 0;
+  border-radius: 6px;
+  font: inherit;
+  font-size: 12.5px;
+  text-align: left;
+  cursor: pointer;
+}
+.od-select-option:hover:not(:disabled),
+.od-select-option.active:not(:disabled) {
+  background: var(--bg-subtle);
+}
+.od-select-option.selected {
+  color: var(--text);
+  font-weight: 600;
+}
+.od-select-option:disabled {
+  opacity: 0.45;
+  cursor: not-allowed;
+}
+.od-select-option-check {
+  display: inline-flex;
+  justify-content: center;
+  color: var(--selected);
+  opacity: 0;
+}
+.od-select-option.selected .od-select-option-check {
+  opacity: 1;
+}
+.od-select-group + .od-select-group {
+  margin-top: 4px;
+  padding-top: 4px;
+  border-top: 1px solid var(--border-soft);
+}
+.od-select-group-label {
+  padding: 6px 8px 4px;
+  color: var(--text-faint);
+  font-size: 11px;
+  font-weight: 600;
+}
+
+code {
+  font-family: var(--mono);
+  background: var(--bg-subtle);
+  padding: 1px 5px;
+  border-radius: 4px;
+  font-size: 0.92em;
+  color: var(--text);
+}
diff --git a/apps/web/src/styles/shell.css b/apps/web/src/styles/shell.css
new file mode 100644
index 000000000..f5544a54b
--- /dev/null
+++ b/apps/web/src/styles/shell.css
@@ -0,0 +1,1254 @@
+/* -------- App shell ------------------------------------------------- */
+.app {
+  display: grid;
+  /* Three rows: chrome header (auto, fixed at 40px), optional
+   * project-actions toolbar (auto — collapses to 0 when hidden),
+   * and the chat/workspace split (1fr — fills the remaining
+   * viewport). The split gets explicit `grid-row: 3` below so the
+   * layout works whether the toolbar is rendered or returns null
+   * (workspace-focused mode). */
+  grid-template-columns: minmax(0, 1fr);
+  grid-template-rows: auto auto 1fr;
+  height: 100vh;
+  background: var(--bg-app);
+  overflow: hidden;
+}
+.app > .split {
+  grid-row: 3;
+}
+
+.workspace-shell {
+  width: 100vw;
+  max-width: 100vw;
+  height: 100vh;
+  min-width: 0;
+  min-height: 0;
+  display: grid;
+  grid-template-columns: minmax(0, 1fr);
+  grid-template-rows: 36px minmax(0, 1fr);
+  background: var(--bg-app);
+  overflow: hidden;
+}
+.workspace-shell__body {
+  min-width: 0;
+  min-height: 0;
+  overflow: hidden;
+}
+.workspace-shell__body > .app,
+.workspace-shell__body > .entry-shell {
+  height: 100%;
+  min-height: 0;
+}
+
+.workspace-tabs-chrome.app-chrome-header {
+  min-width: 0;
+  max-width: 100%;
+  min-height: 38px;
+  height: 38px;
+  padding: 0 8px 0 12px;
+  gap: 0;
+  background: color-mix(in srgb, var(--bg-panel) 88%, var(--bg-subtle));
+  border-bottom: 1px solid color-mix(in srgb, var(--border) 74%, transparent);
+  box-shadow: 0 1px 0 color-mix(in srgb, var(--bg-panel) 80%, transparent) inset;
+  user-select: none;
+  -webkit-app-region: drag;
+  overflow: hidden;
+  position: relative;
+  z-index: 120;
+}
+.workspace-tabs-chrome .workspace-tabs-traffic {
+  margin-right: calc(var(--app-chrome-traffic-margin) + 8px);
+}
+.workspace-tabs-new-btn {
+  width: 24px;
+  height: 24px;
+  padding: 0;
+  border: 0;
+  border-radius: 5px;
+  background: transparent;
+  color: var(--text-faint);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  flex: 0 0 auto;
+  -webkit-app-region: no-drag;
+  transition: background 100ms ease, color 100ms ease;
+}
+.workspace-tabs-new-btn:hover {
+  background: color-mix(in srgb, var(--bg-panel) 72%, var(--bg-subtle));
+  color: var(--text);
+}
+.workspace-tabs-icon-btn {
+  width: 28px;
+  height: 28px;
+  padding: 0;
+  border-radius: 6px;
+  border-color: transparent;
+  background: transparent;
+  color: var(--text-muted);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  flex: 0 0 auto;
+  -webkit-app-region: no-drag;
+}
+.workspace-tabs-icon-btn:hover:not(:disabled),
+.workspace-tabs-icon-btn.is-active {
+  color: var(--text-strong);
+  background: color-mix(in srgb, var(--bg-panel) 78%, var(--bg-subtle));
+  border-color: var(--border);
+}
+.workspace-tabs-strip {
+  align-self: stretch;
+  min-width: 0;
+  flex: 1 1 0;
+  display: flex;
+  align-items: center;
+  gap: 0;
+  padding-left: 0;
+  /* Horizontal scroll inside the strip — tabs keep their natural width
+     and the strip itself scrolls when it overflows the chrome. This
+     replaces the previous "slice to N visible + overflow chip" model
+     which let lots of tabs squish-shrink and could push the rest of
+     the chrome out of view. */
+  overflow-x: auto;
+  overflow-y: hidden;
+  scrollbar-width: none;
+  contain: layout style;
+  -webkit-app-region: drag;
+  /* Allow the user's trackpad / shift-wheel to scroll horizontally
+     without the OS turning it into a back-gesture. */
+  overscroll-behavior-x: contain;
+}
+.workspace-tabs-strip::-webkit-scrollbar {
+  height: 0;
+  display: none;
+}
+.workspace-tab {
+  position: relative;
+  height: 24px;
+  /* Fixed width — never shrink. The strip scrolls when the tabs
+     don't fit anymore. */
+  width: 156px;
+  flex: 0 0 156px;
+  padding: 0;
+  display: grid;
+  grid-template-columns: minmax(0, 1fr) 22px;
+  align-items: center;
+  border: 1px solid var(--border);
+  border-radius: 6px;
+  background: transparent;
+  color: var(--text-muted);
+  overflow: hidden;
+  contain: layout style;
+  transition: background 100ms ease, border-color 100ms ease, color 100ms ease;
+  -webkit-app-region: no-drag;
+}
+.workspace-tab + .workspace-tab::before {
+  content: '';
+  position: absolute;
+  left: 0;
+  top: 50%;
+  width: 1px;
+  height: 14px;
+  background: color-mix(in srgb, var(--border) 70%, transparent);
+  transform: translateY(-50%);
+  pointer-events: none;
+}
+.workspace-tab:hover {
+  background: color-mix(in srgb, var(--bg-panel) 64%, transparent);
+  border-color: color-mix(in srgb, var(--border) 60%, transparent);
+  color: var(--text);
+}
+.workspace-tab.is-active {
+  background: color-mix(in srgb, var(--bg-panel) 92%, var(--bg-subtle));
+  border-color: color-mix(in srgb, var(--border-strong) 52%, transparent);
+  color: var(--text-strong);
+  box-shadow: 0 1px 1px color-mix(in srgb, var(--text) 6%, transparent);
+}
+.workspace-tab:hover::before,
+.workspace-tab.is-active::before {
+  opacity: 0;
+}
+.workspace-tab__main {
+  min-width: 0;
+  height: 100%;
+  padding: 0 6px 0 9px;
+  border: 0;
+  border-radius: 0;
+  background: transparent;
+  color: inherit;
+  display: inline-flex;
+  align-items: center;
+  justify-content: flex-start;
+  gap: 6px;
+  text-align: left;
+}
+.workspace-tab__main:hover:not(:disabled) {
+  background: transparent;
+  border-color: transparent;
+}
+.workspace-tab__icon {
+  width: 14px;
+  height: 14px;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  color: currentColor;
+  opacity: 0.74;
+  flex: 0 0 auto;
+  line-height: 0;
+}
+.workspace-tab__icon svg,
+.workspace-tab__close svg,
+.workspace-tabs-icon-btn svg {
+  display: block;
+  flex: none;
+}
+.workspace-tab__label {
+  min-width: 0;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  font-size: 11.5px;
+  font-weight: 520;
+  letter-spacing: 0;
+}
+.workspace-tab__close {
+  width: 18px;
+  height: 18px;
+  margin-right: 4px;
+  padding: 0;
+  border: 0;
+  border-radius: 5px;
+  background: transparent;
+  color: var(--text-faint);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  opacity: 0;
+  flex: 0 0 auto;
+}
+.workspace-tab:hover .workspace-tab__close,
+.workspace-tab.is-active .workspace-tab__close {
+  opacity: 1;
+}
+.workspace-tab__close:hover {
+  color: var(--text);
+  background: var(--bg-subtle);
+}
+.workspace-tabs-actions {
+  position: relative;
+  align-self: stretch;
+  display: inline-flex;
+  align-items: center;
+  gap: 4px;
+  flex: 0 0 auto;
+  margin-left: 8px;
+  padding-left: 4px;
+  z-index: 35;
+  -webkit-app-region: no-drag;
+}
+.workspace-tabs-popover {
+  /* Anchored to the viewport, not to .workspace-tabs-actions, because the
+     actions group sits next to the tab strip on the left side of the chrome
+     header — not the right. `position: absolute; right: 0` against that
+     narrow inline-flex container made the 380px popover hang off the left
+     edge of the window. Fixed positioning keeps it inside the viewport
+     regardless of where the actions group lands as tabs accumulate. */
+  position: fixed;
+  top: calc(var(--workspace-tabs-chrome-height, 38px) + 7px);
+  right: max(10px, env(safe-area-inset-right));
+  width: min(380px, calc(100vw - 24px));
+  max-height: min(620px, calc(100vh - var(--workspace-tabs-chrome-height, 38px) - 24px));
+  padding: 8px;
+  border: 1px solid var(--border);
+  border-radius: 8px;
+  background: var(--bg-panel);
+  box-shadow: var(--shadow-lg);
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  z-index: 130;
+  -webkit-app-region: no-drag;
+}
+.workspace-tabs-popover,
+.workspace-tabs-popover * {
+  -webkit-app-region: no-drag;
+}
+.workspace-tabs-search {
+  height: 34px;
+  display: flex;
+  align-items: center;
+  gap: 7px;
+  padding: 0 9px;
+  border: 1px solid var(--selected);
+  border-radius: 7px;
+  background: var(--bg);
+  color: var(--text-muted);
+  box-shadow: 0 0 0 3px var(--selected-soft);
+}
+.workspace-tabs-search input {
+  height: 30px;
+  padding: 0;
+  border: 0;
+  background: transparent;
+  box-shadow: none;
+  color: var(--text);
+  font-size: 13px;
+}
+.workspace-tabs-search input:focus {
+  border: 0;
+  box-shadow: none;
+}
+.workspace-tabs-popover__section {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 4px 5px 0;
+  color: var(--text-muted);
+  font-size: 11.5px;
+  font-weight: 650;
+  letter-spacing: 0;
+}
+.workspace-tabs-list {
+  min-height: 0;
+  max-height: 500px;
+  overflow-y: auto;
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+  scrollbar-width: thin;
+  contain: layout style paint;
+}
+.workspace-tabs-list__item {
+  min-height: 34px;
+  border-radius: 7px;
+  display: grid;
+  grid-template-columns: minmax(0, 1fr) 26px;
+  align-items: center;
+  contain: layout style;
+}
+.workspace-tabs-list__item:hover,
+.workspace-tabs-list__item.is-active {
+  background: color-mix(in srgb, var(--selected) 9%, var(--bg-subtle));
+}
+.workspace-tabs-list__main {
+  min-width: 0;
+  height: 34px;
+  padding: 0 7px;
+  border: 0;
+  border-radius: 7px;
+  background: transparent;
+  color: var(--text);
+  display: flex;
+  align-items: center;
+  gap: 9px;
+  text-align: left;
+}
+.workspace-tabs-list__main:hover:not(:disabled) {
+  background: transparent;
+  border-color: transparent;
+}
+.workspace-tabs-list__icon {
+  width: 17px;
+  height: 17px;
+  color: var(--text-muted);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  flex: 0 0 auto;
+  line-height: 0;
+}
+.workspace-tabs-list__icon svg,
+.workspace-tabs-list__close svg,
+.workspace-tabs-search svg {
+  display: block;
+  flex: none;
+}
+.workspace-tabs-list__text {
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 1px;
+}
+.workspace-tabs-list__title,
+.workspace-tabs-list__meta {
+  min-width: 0;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.workspace-tabs-list__title {
+  color: var(--text-strong);
+  font-size: 12.5px;
+  font-weight: 600;
+}
+.workspace-tabs-list__meta {
+  color: var(--text-muted);
+  font-size: 11px;
+}
+.workspace-tabs-list__close {
+  width: 22px;
+  height: 22px;
+  padding: 0;
+  border: 0;
+  border-radius: 6px;
+  background: transparent;
+  color: var(--text-faint);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+}
+.workspace-tabs-list__close:hover:not(:disabled) {
+  color: var(--text);
+  background: var(--bg-muted);
+}
+.workspace-tabs-empty {
+  padding: 18px 8px;
+  text-align: center;
+  color: var(--text-muted);
+  font-size: 12.5px;
+}
+.workspace-tab-preview {
+  position: fixed;
+  z-index: 140;
+  display: flex;
+  align-items: flex-start;
+  gap: 9px;
+  padding: 9px 11px;
+  border: 1px solid var(--border);
+  border-radius: 9px;
+  background: var(--bg-panel);
+  box-shadow: var(--shadow-lg);
+  pointer-events: none;
+  animation: workspace-tab-preview-in 130ms cubic-bezier(0.23, 1, 0.32, 1);
+}
+.workspace-tab-preview__icon {
+  width: 22px;
+  height: 22px;
+  border-radius: 6px;
+  background: var(--bg-subtle);
+  color: var(--text-muted);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  flex: 0 0 auto;
+}
+.workspace-tab-preview__text {
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+}
+.workspace-tab-preview__title {
+  color: var(--text-strong);
+  font-size: 12.5px;
+  font-weight: 600;
+  line-height: 1.25;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  word-break: break-word;
+}
+.workspace-tab-preview__meta {
+  color: var(--text-muted);
+  font-size: 11px;
+  line-height: 1.3;
+}
+.workspace-tab-preview__detail {
+  margin-top: 3px;
+  color: var(--text-muted);
+  font-size: 11.5px;
+  line-height: 1.4;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  word-break: break-word;
+}
+@keyframes workspace-tab-preview-in {
+  from { opacity: 0; transform: translateY(-3px); }
+  to { opacity: 1; transform: translateY(0); }
+}
+@media (max-width: 720px) {
+  .workspace-tabs-chrome.app-chrome-header {
+    padding-right: 6px;
+  }
+  .workspace-tab {
+    min-width: 40px;
+    max-width: 44px;
+    flex-basis: 44px;
+    grid-template-columns: 1fr;
+    padding: 0;
+  }
+  .workspace-tab__main {
+    justify-content: center;
+    padding: 0;
+  }
+  .workspace-tab__label,
+  .workspace-tab__close {
+    display: none;
+  }
+}
+
+.app-chrome-header {
+  --app-chrome-traffic-space: 0px;
+  --app-chrome-traffic-margin: 0px; /* Set to 8px on macOS */
+  display: flex;
+  align-items: center;
+  min-height: 48px;
+  padding: 5px 14px;
+  border-bottom: 1px solid var(--border);
+  background: var(--bg);
+  gap: 12px;
+  flex-shrink: 0;
+}
+.app-chrome-traffic-space {
+  width: var(--app-chrome-traffic-space);
+  flex: 0 0 var(--app-chrome-traffic-space);
+  /* Add extra spacing after the traffic light controls on macOS so project
+     navigation starts after the native window controls. */
+  margin-right: var(--app-chrome-traffic-margin);
+}
+.app-chrome-back,
+.settings-icon-btn {
+  width: 32px;
+  height: 32px;
+  padding: 0;
+  border-color: transparent;
+  background: transparent;
+  color: var(--text-muted);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+}
+.app-chrome-back:hover:not(:disabled),
+.settings-icon-btn:hover:not(:disabled) {
+  background: var(--bg-subtle);
+  border-color: var(--border);
+  color: var(--text);
+}
+.app-chrome-content {
+  min-width: 0;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  overflow: hidden;
+  align-self: stretch;
+  flex: 1 1 auto;
+}
+.app-chrome-drag {
+  min-width: 24px;
+  flex: 1 1 auto;
+  align-self: stretch;
+}
+.app-chrome-actions {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  flex-shrink: 0;
+}
+.app-chrome-file-actions {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  flex-shrink: 0;
+}
+.app-chrome-file-actions:not(:empty) + .app-chrome-actions {
+  margin-left: 4px;
+  padding-left: 8px;
+  border-left: 1px solid var(--border);
+}
+.chrome-action {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  height: 30px;
+  padding: 0 12px;
+  border-radius: 7px;
+  font-size: 12.5px;
+  font-weight: 500;
+  line-height: 1;
+  white-space: nowrap;
+  cursor: pointer;
+  border: 1px solid var(--border);
+  background: transparent;
+  color: var(--text);
+  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
+}
+.chrome-action svg {
+  display: block;
+}
+.chrome-action > svg:first-child {
+  transform: translateY(1px);
+}
+.chrome-action:hover:not(:disabled) {
+  background: var(--bg-subtle);
+}
+.chrome-action-secondary {
+  border-color: var(--border);
+  background: var(--bg);
+  color: var(--text);
+}
+.chrome-action-secondary:hover:not(:disabled) {
+  background: var(--bg-subtle);
+  border-color: var(--text-muted);
+}
+.chrome-action-primary {
+  background: var(--text-strong);
+  border-color: var(--text-strong);
+  color: var(--bg);
+}
+.chrome-action-primary:hover:not(:disabled) {
+  background: var(--text);
+  border-color: var(--text);
+  color: var(--bg);
+}
+.chrome-action-export {
+  background: var(--accent);
+  border-color: var(--accent);
+  color: white;
+  box-shadow: 0 6px 14px color-mix(in srgb, var(--accent) 24%, transparent);
+}
+.chrome-action-export:hover:not(:disabled) {
+  background: var(--accent-hover);
+  border-color: var(--accent-hover);
+  color: white;
+}
+.chrome-action-export:focus-visible {
+  outline-color: var(--accent);
+  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
+}
+.chrome-action-export.export-ready-nudge {
+  animation: export-ready-nudge 1600ms cubic-bezier(0.23, 1, 0.32, 1) 1;
+}
+.chrome-share-menu .share-menu-popover,
+.chrome-present-wrap .present-menu {
+  top: calc(100% + 6px);
+  right: 0;
+}
+.zoom-menu { position: relative; display: inline-block; }
+.zoom-menu .zoom-trigger {
+  display: inline-flex;
+  align-items: center;
+  gap: 4px;
+}
+.zoom-menu-popover {
+  position: absolute;
+  top: calc(100% + 6px);
+  left: 50%;
+  transform: translateX(-50%);
+  background: var(--bg);
+  border: 1px solid var(--border);
+  border-radius: var(--radius-md);
+  box-shadow: var(--shadow-md);
+  padding: 4px;
+  min-width: 110px;
+  z-index: 40;
+  display: flex;
+  flex-direction: column;
+  gap: 1px;
+}
+.zoom-menu-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 8px;
+  padding: 6px 10px;
+  background: transparent;
+  border: none;
+  border-radius: var(--radius-sm);
+  color: var(--text);
+  font-size: 12.5px;
+  cursor: pointer;
+  text-align: left;
+}
+.zoom-menu-item:hover { background: var(--bg-subtle); }
+.zoom-menu-item.active { color: var(--accent-strong); font-weight: 600; }
+@media (max-width: 880px) {
+  .chrome-action-secondary span { display: none; }
+  .chrome-action-secondary { padding: 0 10px; }
+}
+@media (max-width: 720px) {
+  .app-chrome-content { display: none; }
+}
+.viewer.is-tab-present .viewer-toolbar,
+.viewer.is-tab-present .live-artifact-refresh-notice {
+  display: none;
+}
+.viewer.is-tab-present .viewer-body {
+  inset: 0;
+}
+.viewer .present-exit-btn {
+  position: absolute;
+  top: 12px;
+  right: 12px;
+  z-index: 50;
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  background: var(--bg);
+  border: 1px solid var(--border);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  box-shadow: var(--shadow-sm);
+  color: var(--text-muted);
+}
+.viewer .present-exit-btn:hover {
+  background: var(--bg-subtle);
+  color: var(--text);
+}
+.live-artifact-preview-frame-host {
+  width: 100%;
+  height: 100%;
+  background: var(--bg);
+}
+.live-artifact-preview-frame-host:fullscreen {
+  background: var(--bg);
+}
+.app-project-title {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  gap: 4px;
+  min-width: 0;
+  width: 100%;
+  overflow: hidden;
+}
+.app-project-title-line {
+  display: flex;
+  /* Center on the cross-axis so the contentEditable title text, the
+     muted meta string, the design-system picker chip, and the edit
+     icon button all sit on a shared vertical mid-line. Baseline
+     alignment makes the icon-only edit button appear to "float" above
+     the text (the button has no text baseline of its own). */
+  align-items: center;
+  gap: 8px;
+  min-width: 0;
+  max-width: 100%;
+  overflow: hidden;
+}
+.app-project-title .title {
+  color: var(--text-strong);
+  font-size: 13.5px;
+  line-height: 18px;
+  font-weight: 600;
+  letter-spacing: -0.01em;
+  white-space: nowrap;
+  overflow: hidden;
+  padding-left: 1px;
+  text-overflow: ellipsis;
+  flex: 0 1 auto;
+  min-width: 0;
+}
+.app-project-title .meta {
+  color: var(--text-muted);
+  font-size: 11.5px;
+  line-height: 16px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  min-width: 0;
+  flex: 0 1 auto;
+}
+.project-target-platforms,
+.project-feature-chips {
+  display: inline-flex;
+  align-items: center;
+  gap: 4px;
+  min-width: 0;
+  max-width: min(100%, 280px);
+  height: 22px;
+  overflow: hidden;
+  white-space: nowrap;
+  flex: 0 1 auto;
+}
+.project-feature-chips {
+  max-width: min(100%, 260px);
+}
+.project-target-platforms-label,
+.project-feature-chips-label {
+  color: var(--text-muted);
+  font-size: 10px;
+  line-height: 18px;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.06em;
+  flex-shrink: 0;
+}
+.project-target-platform-chip,
+.project-feature-chip {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  min-width: 0;
+  max-width: 92px;
+  height: 20px;
+  padding: 0 8px;
+  border: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
+  border-radius: 999px;
+  color: var(--text-muted);
+  background: color-mix(in srgb, var(--bg-subtle) 88%, transparent);
+  font-size: 11px;
+  line-height: 18px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  flex: 0 1 auto;
+}
+.project-feature-chip.is-landing {
+  color: color-mix(in srgb, var(--accent) 72%, var(--text-strong));
+  border-color: color-mix(in srgb, var(--accent) 26%, transparent);
+  background: color-mix(in srgb, var(--accent) 10%, var(--bg-subtle));
+}
+.project-feature-chip.is-widgets {
+  color: color-mix(in srgb, #0891b2 72%, var(--text-strong));
+  border-color: color-mix(in srgb, #0891b2 26%, transparent);
+  background: color-mix(in srgb, #0891b2 10%, var(--bg-subtle));
+}
+.project-target-platform-chip.is-count {
+  min-width: 28px;
+  max-width: 36px;
+  flex: 0 0 auto;
+  color: var(--text-strong);
+  background: color-mix(in srgb, var(--accent, #7c5cff) 12%, var(--bg-subtle));
+}
+
+/* Project instructions toggle & editor bar */
+.project-instructions-toggle {
+  background: none;
+  border: none;
+  color: var(--text-muted);
+  cursor: pointer;
+  padding: 2px 4px;
+  margin-left: 4px;
+  border-radius: 4px;
+  opacity: 0.6;
+  transition: opacity 0.15s;
+  flex-shrink: 0;
+}
+.project-instructions-toggle:hover {
+  opacity: 1;
+  background: var(--bg-hover, rgba(128, 128, 128, 0.1));
+}
+.project-instructions-bar {
+  padding: 8px 16px 10px;
+  border-bottom: 1px solid var(--border);
+  background: var(--bg-secondary, var(--bg));
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+}
+.project-instructions-label {
+  font-size: 11px;
+  font-weight: 600;
+  color: var(--text-muted);
+  letter-spacing: 0.3px;
+}
+.project-instructions-input {
+  width: 100%;
+  min-height: 48px;
+  max-height: 120px;
+  padding: 8px 10px;
+  font-size: 13px;
+  line-height: 1.45;
+  border: 1px solid var(--border);
+  border-radius: 6px;
+  background: var(--bg);
+  color: var(--text);
+  resize: vertical;
+  font-family: inherit;
+}
+.project-instructions-input:focus {
+  outline: none;
+  border-color: var(--accent, #646cff);
+}
+.project-instructions-actions {
+  display: flex;
+  gap: 6px;
+  justify-content: flex-end;
+}
+
+/* Saved-state entry point: a persistent header chip that reveals the
+   review panel so saved Project instructions stay discoverable (#1822). */
+.project-instructions-chip {
+  display: inline-flex;
+  align-items: center;
+  gap: 4px;
+  margin-left: 6px;
+  padding: 2px 8px;
+  font-size: 11px;
+  font-weight: 600;
+  line-height: 1.6;
+  color: var(--text-muted);
+  background: var(--bg-hover, rgba(128, 128, 128, 0.1));
+  border: 1px solid var(--border);
+  border-radius: 999px;
+  cursor: pointer;
+  transition: color 0.15s, border-color 0.15s;
+  flex-shrink: 0;
+  max-width: 220px;
+}
+.project-instructions-chip span {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.project-instructions-chip:hover,
+.project-instructions-chip.is-open {
+  color: var(--text);
+  border-color: var(--accent, #646cff);
+}
+.project-instructions-bar-head {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 8px;
+}
+.project-instructions-status {
+  display: inline-flex;
+  align-items: center;
+  gap: 3px;
+  font-size: 11px;
+  font-weight: 500;
+  color: var(--accent, #646cff);
+  flex-shrink: 0;
+}
+.project-instructions-preview {
+  width: 100%;
+  max-height: 160px;
+  overflow-y: auto;
+  padding: 8px 10px;
+  font-size: 13px;
+  line-height: 1.5;
+  white-space: pre-wrap;
+  word-break: break-word;
+  border: 1px solid var(--border);
+  border-radius: 6px;
+  background: var(--bg);
+  color: var(--text);
+}
+
+/* Settings: custom instructions textarea */
+.custom-instructions-input {
+  width: 100%;
+  min-height: 80px;
+  max-height: 200px;
+  padding: 10px 12px;
+  font-size: 13px;
+  line-height: 1.5;
+  border: 1px solid var(--border);
+  border-radius: 8px;
+  background: var(--bg);
+  color: var(--text);
+  resize: vertical;
+  font-family: inherit;
+}
+.custom-instructions-input:focus {
+  outline: none;
+  border-color: var(--accent, #646cff);
+}
+
+.topbar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 10px 16px;
+  border-bottom: 1px solid var(--border);
+  background: var(--bg);
+  gap: 16px;
+  flex-wrap: wrap;
+}
+.topbar-left { display: flex; flex-direction: row; align-items: center; gap: 12px; min-width: 0; }
+.topbar-title { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
+.topbar .title { font-weight: 600; font-size: 14px; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.topbar .meta { color: var(--text-muted); font-size: 11.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.topbar-right { display: flex; gap: 6px; align-items: center; flex-wrap: nowrap; }
+
+.topbar .brand-mark {
+  display: inline-flex;
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  align-items: center;
+  justify-content: center;
+  background: linear-gradient(135deg, var(--accent-tint) 0%, var(--accent-soft) 100%);
+  color: var(--accent);
+  flex-shrink: 0;
+  overflow: hidden;
+}
+.topbar .brand-mark .brand-mark-img {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+  display: block;
+  padding: 1px;
+  user-select: none;
+  -webkit-user-drag: none;
+}
+
+.topbar-left .back-btn {
+  align-self: center;
+  padding: 4px 10px;
+  font-size: 12px;
+  background: transparent;
+  border-color: transparent;
+  color: var(--text-muted);
+}
+.topbar-left .back-btn:hover { background: var(--bg-subtle); color: var(--text); border-color: var(--border); }
+
+/* -------- Avatar menu (replaces in-topbar agent picker) ------------- */
+.avatar-menu { position: relative; }
+.avatar-btn {
+  width: 32px;
+  height: 32px;
+  padding: 0;
+  border-radius: 50%;
+  background: linear-gradient(135deg, var(--accent-tint) 0%, var(--accent-soft) 100%);
+  border: 1px solid var(--border);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  overflow: hidden;
+  transition: box-shadow 120ms ease, transform 120ms ease;
+}
+.avatar-btn:hover:not(:disabled) {
+  box-shadow: 0 0 0 3px rgba(194, 83, 45, 0.18);
+  border-color: transparent;
+}
+.avatar-btn:focus-visible {
+  outline: none;
+  box-shadow: 0 0 0 3px rgba(194, 83, 45, 0.32);
+  border-color: transparent;
+}
+.avatar-btn:active:not(:disabled) { transform: scale(0.96); }
+.avatar-btn-photo {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  object-position: 50% 22%;
+  display: block;
+  user-select: none;
+  -webkit-user-drag: none;
+  pointer-events: none;
+}
+.avatar-popover {
+  position: absolute;
+  top: calc(100% + 8px);
+  right: 0;
+  z-index: 80;
+  min-width: 280px;
+  padding: 6px;
+  background: var(--bg-panel);
+  border: 1px solid var(--border);
+  border-radius: var(--radius);
+  box-shadow: var(--shadow-lg);
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+}
+[dir='rtl'] .avatar-popover {
+  right: auto;
+  left: 0;
+}
+.avatar-popover-head {
+  padding: 10px 10px 8px;
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+  border-bottom: 1px solid var(--border-soft);
+  margin-bottom: 4px;
+}
+.avatar-popover-head .who { font-weight: 600; font-size: 13px; }
+.avatar-popover-head .where { font-size: 11.5px; color: var(--text-muted); }
+.avatar-item {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  width: 100%;
+  padding: 8px 10px;
+  font-size: 12.5px;
+  text-align: left;
+  background: transparent;
+  border: none;
+  border-radius: var(--radius-sm);
+  cursor: pointer;
+  color: var(--text);
+  text-decoration: none;
+}
+.avatar-item:hover { background: var(--bg-subtle); }
+/* `` items inside the popover need explicit color + decoration
+   resets — the user-agent stylesheet otherwise paints them blue
+   and underlined, which reads as misalignment next to the button
+   rows that share the same `.avatar-item` class. */
+a.avatar-item,
+a.avatar-item:visited {
+  color: var(--text);
+}
+.avatar-item .avatar-item-icon {
+  width: 18px;
+  height: 18px;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  line-height: 0;
+  color: var(--text-muted);
+  flex-shrink: 0;
+}
+.avatar-item .avatar-item-meta {
+  margin-left: auto;
+  font-size: 11px;
+  color: var(--text-muted);
+  font-variant-numeric: tabular-nums;
+  white-space: nowrap;
+}
+.avatar-section-label {
+  font-size: 10.5px;
+  text-transform: uppercase;
+  letter-spacing: 0.06em;
+  color: var(--text-faint);
+  font-weight: 600;
+  padding: 8px 10px 4px;
+}
+.avatar-model-section {
+  padding: 2px 10px 6px;
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  border-top: 1px dashed var(--border-soft);
+  margin-top: 4px;
+}
+.avatar-select-row {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  font-size: 12px;
+  color: var(--text-muted);
+}
+.avatar-select-label {
+  flex-shrink: 0;
+  min-width: 64px;
+}
+.avatar-select {
+  flex: 1;
+  min-width: 0;
+  font-size: 12px;
+  padding: 4px 6px;
+  border-radius: var(--radius-sm);
+  border: 1px solid var(--border);
+  background-color: var(--bg-panel);
+  color: var(--text);
+  cursor: pointer;
+}
+.avatar-select:focus { outline: 2px solid var(--accent-soft, var(--border-strong)); }
+
+/* Environment pill — only used in entry view header now */
+.env-pill {
+  display: inline-flex;
+  align-items: center;
+  gap: 8px;
+  padding: 4px 12px 4px 4px;
+  background: var(--bg-subtle);
+  border: 1px solid var(--border);
+  border-radius: var(--radius-pill);
+  cursor: pointer;
+  max-width: 360px;
+  min-width: 0;
+  font: inherit;
+  color: inherit;
+}
+.env-pill:hover { background: var(--bg-panel); border-color: var(--border-strong); }
+.env-pill-dot {
+  width: 22px; height: 22px; border-radius: 50%;
+  background: linear-gradient(135deg, #d97757 0%, #b85a3b 100%);
+  flex-shrink: 0;
+}
+.env-pill-dot[data-mode="api"] {
+  background: linear-gradient(135deg, #1c1b1a 0%, #4b4948 100%);
+}
+.env-pill-label { font-weight: 500; font-size: 12px; }
+.env-pill-meta {
+  font-size: 12px; color: var(--text-muted);
+  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
+}
+
+/* -------- Split / panes -------------------------------------------- */
+.split {
+  display: grid;
+  grid-template-columns: 460px 8px minmax(400px, 1fr);
+  min-height: 0;
+  min-width: 0;
+  max-width: 100%;
+  overflow: hidden;
+}
+.split.is-resizing-chat {
+  cursor: col-resize;
+  user-select: none;
+}
+.split.is-resizing-chat iframe {
+  pointer-events: none;
+}
+.split.split-focus {
+  grid-template-columns: minmax(0, 1fr);
+}
+.split-chat-slot {
+  display: flex;
+  min-height: 0;
+  min-width: 0;
+  overflow: hidden;
+}
+.split-chat-slot[hidden] {
+  display: none;
+}
+.split-chat-slot > .pane {
+  flex: 1 1 auto;
+  min-width: 0;
+}
+.pane {
+  display: flex;
+  flex-direction: column;
+  min-height: 0;
+  min-width: 0;
+  background: var(--bg-panel);
+  overflow: hidden;
+}
+.split-resize-handle {
+  width: 8px;
+  min-width: 8px;
+  height: 100%;
+  cursor: col-resize;
+  background:
+    linear-gradient(
+      90deg,
+      transparent 0,
+      transparent 3px,
+      var(--border) 3px,
+      var(--border) 5px,
+      transparent 5px
+    );
+  transition: background 120ms ease;
+  touch-action: none;
+}
+.split-resize-handle:hover,
+.split-resize-handle:focus-visible,
+.split.is-resizing-chat .split-resize-handle {
+  background:
+    linear-gradient(
+      90deg,
+      color-mix(in srgb, var(--accent) 16%, transparent) 0,
+      color-mix(in srgb, var(--accent) 16%, transparent) 3px,
+      var(--accent) 3px,
+      var(--accent) 5px,
+      color-mix(in srgb, var(--accent) 16%, transparent) 5px
+    );
+}
+.split-resize-handle:focus-visible {
+  outline: 2px solid var(--accent);
+  outline-offset: -2px;
+}
diff --git a/apps/web/src/styles/tokens.css b/apps/web/src/styles/tokens.css
new file mode 100644
index 000000000..d47ecef79
--- /dev/null
+++ b/apps/web/src/styles/tokens.css
@@ -0,0 +1,164 @@
+/* ============================================================
+   Open Design — neutral product workspace
+   ============================================================ */
+:root {
+  /* Surface palette — neutral app chrome that does not bias generated artifacts.
+     Keep warm/brand colors out of preview backgrounds; generated product UI
+     must choose its own palette through the active skill/design brief. */
+  --bg: #faf9f7;
+  --bg-app: #faf9f7;
+  --bg-panel: #ffffff;
+  --bg-subtle: #eef1f5;
+  --bg-muted: #e4e8ef;
+  --bg-elevated: #ffffff;
+  --border: #e1e5eb;
+  --border-strong: #c9d0da;
+  --border-soft: #edf0f4;
+
+  --text: #1a1916;
+  --text-strong: #0d0c0a;
+  --text-muted: #74716b;
+  --text-soft: #989590;
+  --text-faint: #b3b0a8;
+
+  /* Accent — Open Design action color for app chrome only. */
+  --accent: #c96442;
+  --accent-strong: #b45a3b;
+  --accent-soft: #f5d8cb;
+  --accent-tint: #fbeee5;
+  --accent-hover: #b45a3b;
+
+  /* Semantic accent tints used by tool / status pills. */
+  --green: #1f7a3a;
+  --green-bg: #e8f7ee;
+  --green-border: #c6ead2;
+  --blue: #2348b8;
+  --blue-bg: #e8efff;
+  --blue-border: #c8d6ff;
+  --purple: #6c3aa6;
+  --purple-bg: #f3ecf9;
+  --purple-border: #e4d4f1;
+  --red: #9c2a25;
+  --red-bg: #fdecea;
+  --red-border: #f5c6c2;
+  --amber: #b26200;
+  --amber-bg: #fff3e0;
+
+  --shadow-xs: 0 1px 0 rgba(28, 27, 26, 0.04);
+  --shadow-sm: 0 1px 2px rgba(28, 27, 26, 0.05), 0 1px 3px rgba(28, 27, 26, 0.04);
+  --shadow-md: 0 6px 24px rgba(28, 27, 26, 0.07), 0 2px 6px rgba(28, 27, 26, 0.04);
+  --shadow-lg: 0 24px 60px rgba(28, 27, 26, 0.16), 0 8px 16px rgba(28, 27, 26, 0.07);
+
+  --radius-sm: 8px;
+  --radius: 12px;
+  --radius-lg: 16px;
+  --radius-pill: 999px;
+
+  /* "Active option" indicator color, intentionally separate from --accent
+     (brand orange used by primary CTAs like Create / Save). Use --selected
+     for "this option is the one currently picked" affordances — selected
+     fidelity card, focused input ring, active filter pill — so a primary
+     CTA and a selected state can coexist on the same screen without
+     competing. --selected-soft is the 16% tint for outer rings / fills. */
+  --selected: #2563eb;
+  --selected-soft: rgba(37, 99, 235, 0.16);
+
+  --serif: 'Source Serif Pro', 'Source Serif 4', 'Iowan Old Style', 'Apple Garamond', Georgia, 'Times New Roman', serif;
+  --sans: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+  --mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;
+}
+
+/* Dark theme variables — shared between explicit [data-theme="dark"] and the
+   OS-level prefers-color-scheme media query (system mode = no data-theme attr). */
+[data-theme="dark"] {
+  color-scheme: dark;
+  --bg: #1a1917;
+  --bg-app: #1a1917;
+  --bg-panel: #222120;
+  --bg-subtle: #272523;
+  --bg-muted: #2e2c29;
+  --bg-elevated: #2a2825;
+  --border: #333128;
+  --border-strong: #46433c;
+  --border-soft: #2a2825;
+
+  --text: #e8e4dc;
+  --text-strong: #f2ede4;
+  --text-muted: #9a9690;
+  --text-soft: #6e6b65;
+  --text-faint: #4e4b46;
+
+  --accent: #d97a56;
+  --accent-strong: #e8896a;
+  --accent-soft: #3d2318;
+  --accent-tint: #2e1a12;
+  --accent-hover: #e8896a;
+
+  --green: #4caf72;
+  --green-bg: #0f2a18;
+  --green-border: #1a4028;
+  --blue: #6b8fe8;
+  --blue-bg: #0f1a38;
+  --blue-border: #1a2c58;
+  --purple: #a87dd4;
+  --purple-bg: #1e1030;
+  --purple-border: #321a50;
+  --red: #e06b65;
+  --red-bg: #2a0e0c;
+  --red-border: #451714;
+  --amber: #e09a40;
+  --amber-bg: #2a1a04;
+
+  --shadow-xs: 0 1px 0 rgba(0, 0, 0, 0.2);
+  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2);
+  --shadow-md: 0 6px 24px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.25);
+  --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.6), 0 8px 16px rgba(0, 0, 0, 0.3);
+}
+
+/* System mode: follow OS preference when no explicit data-theme is set. */
+@media (prefers-color-scheme: dark) {
+  html:not([data-theme]) {
+    color-scheme: dark;
+    --bg: #1a1917;
+    --bg-app: #1a1917;
+    --bg-panel: #222120;
+    --bg-subtle: #272523;
+    --bg-muted: #2e2c29;
+    --bg-elevated: #2a2825;
+    --border: #333128;
+    --border-strong: #46433c;
+    --border-soft: #2a2825;
+
+    --text: #e8e4dc;
+    --text-strong: #f2ede4;
+    --text-muted: #9a9690;
+    --text-soft: #6e6b65;
+    --text-faint: #4e4b46;
+
+    --accent: #d97a56;
+    --accent-strong: #e8896a;
+    --accent-soft: #3d2318;
+    --accent-tint: #2e1a12;
+    --accent-hover: #e8896a;
+
+    --green: #4caf72;
+    --green-bg: #0f2a18;
+    --green-border: #1a4028;
+    --blue: #6b8fe8;
+    --blue-bg: #0f1a38;
+    --blue-border: #1a2c58;
+    --purple: #a87dd4;
+    --purple-bg: #1e1030;
+    --purple-border: #321a50;
+    --red: #e06b65;
+    --red-bg: #2a0e0c;
+    --red-border: #451714;
+    --amber: #e09a40;
+    --amber-bg: #2a1a04;
+
+    --shadow-xs: 0 1px 0 rgba(0, 0, 0, 0.2);
+    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2);
+    --shadow-md: 0 6px 24px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.25);
+    --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.6), 0 8px 16px rgba(0, 0, 0, 0.3);
+  }
+}
diff --git a/apps/web/src/styles/viewer/code.css b/apps/web/src/styles/viewer/code.css
new file mode 100644
index 000000000..eeee981d4
--- /dev/null
+++ b/apps/web/src/styles/viewer/code.css
@@ -0,0 +1,767 @@
+/* Code viewer with line numbers */
+.code-viewer {
+  background: var(--bg-panel);
+  margin: 0;
+  padding: 0;
+  display: grid;
+  grid-template-columns: auto 1fr;
+  font-family: var(--mono);
+  font-size: 12px;
+  line-height: 1.6;
+  min-height: 100%;
+}
+.code-viewer .gutter {
+  background: var(--bg);
+  color: var(--text-faint);
+  text-align: right;
+  padding: 16px 12px 16px 16px;
+  user-select: none;
+  border-right: 1px solid var(--border-soft);
+  white-space: pre;
+  font-variant-numeric: tabular-nums;
+}
+.code-viewer .lines {
+  padding: 16px 16px 16px 18px;
+  white-space: pre;
+  overflow-x: auto;
+  color: var(--text);
+}
+
+.viewer-empty {
+  padding: 48px 24px;
+  text-align: center;
+  color: var(--text-muted);
+  font-size: 13px;
+}
+/* Compact horizontal variant for the upload-failure banner: the message
+   sits inline next to a dismiss button so the user can clear the stale
+   notice without changing tabs. Issue #786. */
+.viewer-empty.viewer-empty-dismissible {
+  padding: 12px 16px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 12px;
+  text-align: left;
+}
+.document-preview {
+  max-width: 860px;
+  margin: 0 auto;
+  padding: 32px 40px 56px;
+  color: var(--text);
+}
+.document-preview h2 {
+  margin: 0 0 24px;
+  font-size: 20px;
+  line-height: 1.25;
+}
+.document-preview section {
+  border-top: 1px solid var(--border-soft);
+  padding-top: 18px;
+  margin-top: 18px;
+}
+.document-preview h3 {
+  margin: 0 0 12px;
+  font-size: 13px;
+  color: var(--text-muted);
+  font-weight: 600;
+}
+.document-preview p {
+  margin: 0 0 8px;
+  font-size: 14px;
+  line-height: 1.65;
+  white-space: pre-wrap;
+}
+.markdown-rendered {
+  max-width: 900px;
+  margin: 0 auto;
+  padding: 24px 28px 40px;
+  color: var(--text);
+  line-height: 1.65;
+  white-space: normal;
+}
+.markdown-status {
+  margin: 12px auto 0;
+  max-width: 900px;
+  padding: 8px 10px;
+  border: 1px solid var(--border-soft);
+  border-radius: 8px;
+  background: var(--bg-panel);
+  color: var(--text-muted);
+  font-size: 12px;
+}
+.markdown-status-error {
+  border-color: color-mix(in oklab, var(--danger, #d04b4b) 45%, var(--border-soft));
+  color: var(--danger, #d04b4b);
+}
+.markdown-rendered h1,
+.markdown-rendered h2,
+.markdown-rendered h3,
+.markdown-rendered h4,
+.markdown-rendered h5,
+.markdown-rendered h6 {
+  margin: 20px 0 10px;
+  line-height: 1.25;
+}
+.markdown-rendered p { margin: 10px 0; }
+.markdown-rendered ul,
+.markdown-rendered ol {
+  margin: 10px 0;
+  padding-left: 24px;
+}
+.markdown-rendered blockquote {
+  margin: 12px 0;
+  padding: 8px 12px;
+  border-left: 3px solid var(--border);
+  color: var(--text-muted);
+  background: var(--bg-panel);
+}
+.markdown-code-block {
+  position: relative;
+}
+.markdown-code-copy {
+  position: absolute;
+  top: 18px;
+  right: 18px;
+  z-index: 1;
+  border: 1px solid var(--border-soft);
+  border-radius: 999px;
+  background: color-mix(in oklab, var(--bg-panel) 92%, black 8%);
+  color: var(--text-muted);
+  font: inherit;
+  font-size: 11px;
+  font-weight: 600;
+  line-height: 1;
+  padding: 7px 10px;
+  cursor: pointer;
+  opacity: 0;
+  transform: translateY(-2px);
+  transition:
+    opacity 120ms ease,
+    transform 120ms ease,
+    color 120ms ease,
+    border-color 120ms ease,
+    background 120ms ease;
+}
+.markdown-code-block:hover .markdown-code-copy,
+.markdown-code-block:focus-within .markdown-code-copy {
+  opacity: 1;
+  transform: translateY(0);
+}
+@media (hover: none) {
+  .markdown-code-copy {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+.markdown-code-copy:hover,
+.markdown-code-copy:focus-visible {
+  color: var(--text);
+  border-color: var(--border);
+  background: var(--bg-elevated, var(--bg));
+}
+.markdown-code-toast {
+  position: absolute;
+  top: 18px;
+  right: 82px;
+  z-index: 1;
+  border-radius: 999px;
+  background: color-mix(in oklab, var(--accent) 18%, var(--bg-panel));
+  color: var(--text);
+  font-size: 11px;
+  font-weight: 600;
+  line-height: 1;
+  padding: 7px 10px;
+  box-shadow: 0 10px 26px color-mix(in oklab, var(--accent) 18%, transparent);
+}
+.markdown-rendered pre {
+  margin: 12px 0;
+  background: var(--bg-panel);
+  border: 1px solid var(--border-soft);
+  border-radius: 8px;
+  padding: 40px 12px 12px;
+  overflow: auto;
+}
+.markdown-rendered code {
+  font-family: var(--mono);
+  font-size: 12px;
+}
+.markdown-rendered a { color: var(--accent); }
+.image-body {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 24px;
+}
+.image-body img {
+  max-width: 100%;
+  max-height: 100%;
+  object-fit: contain;
+  background: white;
+  border: 1px solid var(--border);
+  border-radius: var(--radius-sm);
+}
+.sketch-preview {
+  width: 100%;
+  height: 100%;
+  min-height: 0;
+}
+.sketch-preview svg {
+  width: 100%;
+  height: 100%;
+  display: block;
+  background: white;
+  border: 1px solid var(--border);
+  border-radius: var(--radius-sm);
+}
+.sketch-preview.loading {
+  background:
+    radial-gradient(circle at 8px 8px, #d7d4ce 1px, transparent 1px),
+    #f7f5f1;
+  background-size: 16px 16px;
+  border: 1px solid var(--border);
+  border-radius: var(--radius-sm);
+}
+.sketch-preview-empty-mark path {
+  stroke: #bfbcb6;
+  stroke-width: 6;
+  stroke-linecap: round;
+  fill: none;
+}
+
+/* Sketch editor */
+.sketch-editor {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  min-height: 0;
+  background: var(--bg);
+}
+.sketch-toolbar {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  padding: 8px 12px;
+  border-bottom: 1px solid var(--border);
+  background: var(--bg-panel);
+  flex-wrap: wrap;
+}
+.sketch-tool {
+  padding: 6px 10px;
+  background: var(--bg-panel);
+  border: 1px solid var(--border);
+  border-radius: 6px;
+  font-size: 14px;
+  line-height: 1;
+  min-width: 32px;
+}
+.sketch-tool:hover { background: var(--bg-subtle); }
+.sketch-tool.active { background: var(--accent); color: white; border-color: var(--accent); }
+.sketch-divider { width: 1px; height: 20px; background: var(--border); margin: 0 4px; }
+.sketch-color { width: 32px; height: 28px; padding: 0; border: 1px solid var(--border); border-radius: 6px; cursor: pointer; }
+.sketch-size { width: 80px; background: transparent; border: none; }
+.sketch-spacer { flex: 1; }
+.sketch-canvas-wrap { flex: 1; min-height: 0; position: relative; background: var(--bg); }
+.sketch-canvas-wrap canvas { display: block; cursor: crosshair; }
+
+/* ===========================================================
+   Streaming chat: assistant message header, prose, thinking,
+   tool cards, status pills, grouped action card.
+   =========================================================== */
+.chat-empty-wrap {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 18px;
+  padding: 24px 8px;
+  min-height: 100%;
+}
+.chat-empty {
+  color: var(--text-muted);
+  font-size: 13px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+  gap: 6px;
+  max-width: 44ch;
+}
+.chat-empty-title { font-weight: 600; color: var(--text-strong); font-size: 15px; }
+.chat-empty-hint { line-height: 1.6; }
+
+.chat-examples {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  width: 100%;
+  max-width: 520px;
+}
+
+.chat-example {
+  position: relative;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  width: 100%;
+  padding: 14px 16px;
+  text-align: left;
+  cursor: pointer;
+  background: var(--bg-panel);
+  border: 1px solid var(--border);
+  border-radius: var(--radius);
+  box-shadow: var(--shadow-xs);
+  color: var(--text);
+  font: inherit;
+  overflow: hidden;
+  transition:
+    transform 160ms ease,
+    border-color 160ms ease,
+    box-shadow 160ms ease,
+    background-color 160ms ease;
+  opacity: 0;
+  animation: chat-example-in 380ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
+}
+.chat-example::before {
+  content: '';
+  position: absolute;
+  inset: 0;
+  background: linear-gradient(
+    135deg,
+    var(--accent-tint) 0%,
+    transparent 55%
+  );
+  opacity: 0;
+  transition: opacity 200ms ease;
+  pointer-events: none;
+}
+.chat-example:hover {
+  transform: translateY(-2px);
+  border-color: var(--accent);
+  box-shadow: var(--shadow-md);
+  background: var(--bg-panel);
+}
+.chat-example:hover::before {
+  opacity: 1;
+}
+.chat-example:active {
+  transform: translateY(0);
+}
+.chat-example:focus-visible {
+  outline: 2px solid var(--accent);
+  outline-offset: 2px;
+}
+
+.chat-example-icon {
+  position: relative;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 36px;
+  height: 36px;
+  flex-shrink: 0;
+  border-radius: var(--radius-sm);
+  background: var(--accent);
+  color: #fff;
+  font-size: 18px;
+  line-height: 1;
+  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04), inset 0 -1px 0 rgba(0, 0, 0, 0.08);
+}
+.chat-example-body {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  gap: 3px;
+  min-width: 0;
+  flex: 1;
+}
+.chat-example-head {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  flex-wrap: wrap;
+}
+.chat-example-title {
+  font-weight: 600;
+  color: var(--text-strong);
+  font-size: 13.5px;
+}
+.chat-example-tag {
+  font-family: var(--mono);
+  font-size: 10.5px;
+  letter-spacing: 0.04em;
+  text-transform: uppercase;
+  color: var(--accent);
+  background: var(--accent-tint);
+  border: 1px solid var(--accent-soft);
+  padding: 1px 7px;
+  border-radius: var(--radius-pill);
+  line-height: 1.5;
+  white-space: nowrap;
+}
+.chat-example-prompt {
+  color: var(--text-muted);
+  font-size: 12.5px;
+  line-height: 1.5;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+.chat-example-cta {
+  position: relative;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 24px;
+  height: 24px;
+  flex-shrink: 0;
+  border-radius: 50%;
+  background: var(--bg-subtle);
+  color: var(--text-muted);
+  font-size: 13px;
+  transition: background-color 160ms ease, color 160ms ease, transform 160ms ease;
+}
+.chat-example:hover .chat-example-cta {
+  background: var(--accent);
+  color: #fff;
+  transform: translateX(2px);
+}
+
+/* Connect-your-repo CTA in the empty chat state. Shares the example cards'
+   sizing and accent system but stands apart with a tinted fill so it reads as
+   guidance rather than another starter prompt. */
+.chat-connect-repo {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  width: 100%;
+  max-width: 520px;
+  padding: 14px 16px;
+  background: var(--accent-tint);
+  border: 1px solid var(--accent-soft);
+  border-radius: var(--radius);
+  box-shadow: var(--shadow-xs);
+  opacity: 0;
+  animation: chat-example-in 380ms cubic-bezier(0.22, 1, 0.36, 1) 220ms forwards;
+}
+.chat-connect-repo-icon {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 36px;
+  height: 36px;
+  flex-shrink: 0;
+  border-radius: var(--radius-sm);
+  background: var(--accent);
+  color: #fff;
+}
+.chat-connect-repo-body {
+  display: flex;
+  flex-direction: column;
+  gap: 3px;
+  min-width: 0;
+  flex: 1;
+}
+.chat-connect-repo-title {
+  font-weight: 600;
+  color: var(--text-strong);
+  font-size: 13.5px;
+}
+.chat-connect-repo-text {
+  color: var(--text-muted);
+  font-size: 12.5px;
+  line-height: 1.5;
+}
+.chat-connect-repo button {
+  flex-shrink: 0;
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  white-space: nowrap;
+}
+
+@keyframes chat-example-in {
+  from {
+    opacity: 0;
+    transform: translateY(8px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+.assistant-header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  font-size: 11.5px;
+  color: var(--text-muted);
+  margin-bottom: 8px;
+}
+.assistant-header .dot {
+  width: 7px;
+  height: 7px;
+  border-radius: 50%;
+  background: var(--text-muted);
+}
+.assistant-header .dot[data-active="true"] {
+  background: var(--accent);
+  animation: pulse 1.2s ease-in-out infinite;
+}
+.assistant-label { font-weight: 600; color: var(--text-strong); font-size: 12.5px; }
+.assistant-stats { font-variant-numeric: tabular-nums; margin-left: auto; }
+
+.assistant-flow {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  min-width: 0;
+}
+.prose-block { line-height: 1.6; color: var(--text); }
+.prose-block .md-p { margin: 0; }
+.prose-block .md-p + .md-p { margin-top: 8px; }
+.prose-block .md-h { margin: 10px 0 4px; line-height: 1.3; font-weight: 600; }
+.prose-block .md-h1 { font-size: 18px; }
+.prose-block .md-h2 { font-size: 16px; }
+.prose-block .md-h3 { font-size: 14px; }
+.prose-block .md-h4 { font-size: 13px; }
+.prose-block .md-ul, .prose-block .md-ol { margin: 4px 0; padding-left: 20px; }
+.prose-block .md-ul li, .prose-block .md-ol li { margin: 2px 0; }
+.prose-block .md-inline-code {
+  background: var(--bg-subtle);
+  border: 1px solid var(--border);
+  border-radius: 4px;
+  padding: 1px 5px;
+  font-family: var(--mono);
+  font-size: 0.92em;
+}
+.prose-block .md-code {
+  background: var(--bg-subtle);
+  border: 1px solid var(--border);
+  border-radius: var(--radius-sm);
+  padding: 10px 12px;
+  margin: 6px 0;
+  overflow-x: auto;
+  font-size: 12px;
+  line-height: 1.5;
+}
+.prose-block .md-code code { font-family: var(--mono); }
+.prose-block .md-link {
+  color: var(--accent);
+  text-decoration: underline;
+  word-break: break-word;
+  overflow-wrap: anywhere;
+}
+.prose-block .md-link-bare {
+  /* Long bare URLs (OAuth flows etc.) need to wrap mid-string so they
+     don't escape the chat column. break-word is enough for most agents,
+     but `anywhere` covers query strings with no spaces. */
+  word-break: break-all;
+  overflow-wrap: anywhere;
+}
+.prose-block .md-hr {
+  border: none;
+  border-top: 1px solid var(--border);
+  margin: 10px 0;
+}
+/* GFM pipe tables. Rules are intentionally unscoped so both the chat path
+   (`.prose-block`) and the artifact preview (`.markdown-rendered`) get
+   the same treatment. Horizontal scroll lives on the wrapper so the
+   inner 
can keep `display: table; width: 100%` and distribute + column widths across the container instead of collapsing to content. */ +.md-table-wrap { + display: block; + overflow-x: auto; + max-width: 100%; + margin: 12px 0; + border: 1px solid var(--border); + border-radius: 6px; +} +.md-table { + width: 100%; + border-collapse: collapse; + font-size: 0.95em; +} +.md-table th, +.md-table td { + padding: 8px 12px; + text-align: left; + vertical-align: top; + border-bottom: 1px solid var(--border); +} +.md-table th + th, +.md-table td + td { + border-left: 1px solid var(--border); +} +.md-table thead th { + background: var(--bg-panel); + color: var(--text); + font-weight: 600; + border-bottom-color: var(--border); +} +.md-table tbody tr:last-child td { + border-bottom: none; +} +.md-table tbody tr:nth-child(even) td { + background: color-mix(in oklab, var(--bg-panel) 40%, transparent); +} +.op-waiting { + font-size: 12px; + color: var(--text-muted); + font-style: italic; +} + +/* Thinking blocks */ +.thinking-block { + background: rgba(108, 58, 166, 0.04); + border: 1px solid rgba(108, 58, 166, 0.16); + border-radius: var(--radius-sm); +} +.thinking-toggle { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 7px 12px; + background: transparent; + border: none; + text-align: left; + font-size: 12px; + color: var(--text); + border-radius: var(--radius-sm); +} +.thinking-toggle:hover { background: rgba(108, 58, 166, 0.05); border-color: transparent; } +.thinking-icon { color: var(--purple); } +.thinking-label { font-weight: 500; } +.thinking-preview { + flex: 1; + color: var(--text-muted); + font-style: italic; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.thinking-chev { color: var(--text-muted); font-size: 10px; } +.thinking-body { + margin: 0; + padding: 0 14px 12px 14px; + font-family: var(--mono); + font-size: 11px; + line-height: 1.55; + color: var(--text-muted); + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +/* Status pills */ +.status-pill { + display: inline-flex; + align-self: flex-start; + align-items: flex-start; + flex-wrap: wrap; + gap: 6px; + max-width: 100%; + padding: 3px 12px; + background: var(--bg-subtle); + border: 1px solid var(--border); + border-radius: var(--radius-pill); + font-size: 11.5px; + color: var(--text-muted); +} +.status-label { letter-spacing: 0.02em; } +.status-detail { + min-width: 0; + max-width: 100%; + color: var(--text); + overflow-wrap: anywhere; + white-space: normal; +} + +/* Grouped tool / action card — the collapsible pill from screenshot 9 */ +.action-card { + display: flex; + flex-direction: column; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg-panel); + overflow: hidden; +} +.action-card-toggle { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 8px 12px; + background: transparent; + border: none; + text-align: left; + font-size: 12.5px; + color: var(--text); + cursor: pointer; + border-radius: var(--radius); +} +.action-card-toggle:hover { background: var(--bg-subtle); border-color: transparent; } +.action-card-toggle .ico { + width: 20px; height: 20px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 4px; + background: var(--bg-subtle); + color: var(--text-muted); + font-size: 12px; + font-family: var(--mono); + flex-shrink: 0; +} +.action-card-toggle .summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.action-card-toggle .summary strong { font-weight: 500; } +.action-card-toggle .chev { color: var(--text-faint); font-size: 10px; flex-shrink: 0; } +.action-card-toggle.running .ico { animation: pulse 1.6s ease-in-out infinite; background: var(--purple-bg); color: var(--purple); } +.action-card-body { + padding: 0 12px 10px; + display: flex; + flex-direction: column; + gap: 6px; + border-top: 1px solid var(--border-soft); +} +.action-card-body > .op-card { border-color: transparent; box-shadow: none; padding: 4px 0; } +.action-card-body > .op-card .op-card-head { padding: 6px 0; } + +/* Tool / operation cards — single, ungrouped */ +.op-card { + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg-panel); + overflow: hidden; + box-shadow: var(--shadow-xs); + min-width: 0; + max-width: 100%; +} +.op-card-head { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + font-size: 12.5px; + flex-wrap: wrap; +} +.op-icon { + width: 22px; + height: 22px; + border-radius: 6px; + background: var(--bg-subtle); + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + font-size: 12px; + font-family: var(--mono); + flex-shrink: 0; +} +.op-icon-write { background: var(--green-bg); color: var(--green); } +.op-icon-edit { background: var(--amber-bg); color: var(--amber); } +.op-icon-read { background: var(--blue-bg); color: var(--blue); } +.op-title { font-weight: 500; } diff --git a/apps/web/src/styles/viewer/composio.css b/apps/web/src/styles/viewer/composio.css new file mode 100644 index 000000000..e5706696d --- /dev/null +++ b/apps/web/src/styles/viewer/composio.css @@ -0,0 +1,2141 @@ +/* ---------- Composio API key skeleton ---------- + The Composio config is daemon-backed, so on first paint after a + restart there is a window where the section renders empty even + though a saved key exists. Rather than show a misleading "no key + saved" state, we overlay a skeleton on the input + chip + buttons + so the user understands the field is still resolving. + We intentionally keep the real input mounted underneath the shimmer + so focus, autofill, and accessibility nodes are not torn down on + resolve — the parent label gets aria-busy and the disabled flags + on the buttons are the structural safety net. */ +.field-status-badge-skeleton { + /* Same footprint as the saved-state chip so the row geometry + doesn't shift when hydration completes. The width is a + calibrated guess for a "Saved · ••••XXXX" string; close enough + that the swap-in feels stable without depending on the actual + tail length. */ + width: 86px; + height: 18px; + border: 1px solid var(--border); + background: + linear-gradient( + 90deg, + color-mix(in srgb, var(--text-soft) 8%, var(--bg-subtle)) 0%, + color-mix(in srgb, var(--text-soft) 18%, var(--bg-subtle)) 50%, + color-mix(in srgb, var(--text-soft) 8%, var(--bg-subtle)) 100% + ); + background-size: 200% 100%; + animation: settingsSkeletonShimmer 1.4s ease-in-out infinite; + border-radius: var(--radius-pill); + padding: 0; + color: transparent; +} + +.field-input-skeleton-wrap { + position: relative; + display: flex; + flex: 1; + min-width: 0; +} +.field-input-skeleton-wrap > input { + flex: 1; + min-width: 0; +} +.field-input-skeleton-shimmer { + position: absolute; + inset: 1px; + border-radius: var(--radius); + pointer-events: none; + background: + linear-gradient( + 90deg, + transparent 0%, + color-mix(in srgb, var(--text-soft) 14%, transparent) 50%, + transparent 100% + ); + background-size: 220% 100%; + animation: settingsSkeletonShimmer 1.6s ease-in-out infinite; +} + +/* The whole credentials field gets a softened, "we're checking" feel + while loading: muted label, slightly desaturated input chrome. + Matches the broader Settings shimmer language. */ +.settings-section-connectors-credentials.is-loading .field-label { + color: var(--text-muted); +} +.settings-section-connectors-credentials.is-loading input:disabled { + cursor: progress; + background: color-mix(in srgb, var(--text-soft) 4%, var(--bg-subtle)); + /* Preserve readable placeholder color even when disabled — the + placeholder doubles as the "Checking for a saved key…" cue. */ + color: var(--text-muted); + -webkit-text-fill-color: var(--text-muted); +} +.settings-section-connectors-credentials.is-loading input:disabled::placeholder { + color: var(--text-muted); + opacity: 1; +} + +/* Inline status hint variant — the help line below the input becomes + a small spinner + status string while loading. Sits in the same + slot as the regular hint so layout doesn't jump. */ +.field-hint-loading { + display: inline-flex; + align-items: center; + gap: 6px; + color: var(--text-muted); +} +.field-hint-loading svg { + color: var(--accent-strong); + flex-shrink: 0; +} + +@keyframes settingsSkeletonShimmer { + 0% { background-position: 100% 0; } + 100% { background-position: -100% 0; } +} +@media (prefers-reduced-motion: reduce) { + .field-status-badge-skeleton, + .field-input-skeleton-shimmer { + animation: none; + } + .field-input-skeleton-shimmer { + background: color-mix(in srgb, var(--text-soft) 8%, transparent); + } +} +.deploy-form input, +.deploy-form select { + width: 100%; + min-height: 44px; + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 0 12px; + background: var(--bg-panel); + color: var(--text); + font: inherit; +} +.deploy-config-actions { + display: flex; + justify-content: flex-end; +} +.deploy-field-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; +} +.cloudflare-domain-grid { + grid-template-columns: minmax(180px, 0.85fr) minmax(220px, 1.15fr); +} +.deploy-field-grid.single-field { + grid-template-columns: minmax(0, 1fr); +} +.deploy-field-grid label { + display: flex; + flex-direction: column; + gap: 6px; +} +.field-hint { + color: var(--text-muted); + font-size: 12px; + line-height: 1.35; +} +.deploy-error { + margin: 0; + color: var(--red); +} +.deploy-result-block { + overflow: hidden; + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--bg-panel); + box-shadow: var(--shadow-xs); +} +.deploy-result-block.ready { + border-color: color-mix(in srgb, var(--green) 26%, var(--border)); +} +.deploy-result-block.delayed { + border-color: color-mix(in srgb, #b7791f 34%, var(--border)); +} +.deploy-result-block.protected, +.deploy-result-block.failed { + border-color: color-mix(in srgb, #c96442 38%, var(--border)); +} +.deploy-result-summary { + display: flex; + flex-direction: column; + gap: 6px; + padding: 14px 16px 12px; + background: color-mix(in srgb, var(--bg-subtle) 64%, var(--bg-panel)); +} +.deploy-result-summary-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; +} +.deploy-result-link-head { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} +.deploy-result-badge, +.deploy-result-link-state { + display: inline-flex; + align-items: center; + min-height: 24px; + padding: 0 9px; + border-radius: var(--radius-pill); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.02em; + white-space: nowrap; +} +.deploy-result-badge.ready, +.deploy-result-link-state.ready { + color: #137a3d; + background: color-mix(in srgb, var(--green) 14%, transparent); + border: 1px solid color-mix(in srgb, var(--green) 28%, var(--border)); +} +.deploy-result-badge.delayed, +.deploy-result-link-state.delayed { + color: #9a5b12; + background: color-mix(in srgb, #b7791f 14%, transparent); + border: 1px solid color-mix(in srgb, #b7791f 28%, var(--border)); +} +.deploy-result-badge.protected, +.deploy-result-badge.failed, +.deploy-result-link-state.protected, +.deploy-result-link-state.failed { + color: #a34828; + background: color-mix(in srgb, #c96442 14%, transparent); + border: 1px solid color-mix(in srgb, #c96442 30%, var(--border)); +} +.deploy-result-label { + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text); +} +.deploy-result-message { + margin: 0; + color: var(--text-muted); +} +.deploy-result-links { + display: grid; + border-top: 1px solid var(--border); +} +.deploy-result-link { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 12px 16px; + padding: 14px 16px; + background: var(--bg-panel); +} +.deploy-result-link + .deploy-result-link { + border-top: 1px solid var(--border); +} +.deploy-result-link-main { + display: grid; + min-width: 0; + gap: 6px; +} +.deploy-result-link-label { + color: var(--text); + font-size: 13px; + font-weight: 700; + letter-spacing: 0.01em; +} +.deploy-result-link-message { + margin: 0; + color: var(--text-muted); +} +.deploy-result-url { + min-width: 0; + font-weight: 500; + overflow-wrap: anywhere; +} +.deploy-result-actions { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 2px; + padding: 3px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg-subtle); +} +.deploy-result-actions .viewer-action, +.deploy-result-actions .ghost-link { + min-height: 28px; + border: 0; + background: transparent; +} +.deploy-result-actions .viewer-action:hover:not(:disabled), +.deploy-result-actions .ghost-link:hover { + background: var(--bg-panel); +} +@media (max-width: 640px) { + .deploy-form .field-label-row { + align-items: flex-start; + flex-direction: column; + gap: 6px; + } + .deploy-form .field-label-note { + align-items: flex-start; + max-width: none; + text-align: left; + } + .deploy-field-grid, + .cloudflare-domain-grid { + grid-template-columns: minmax(0, 1fr); + } + .deploy-result-link { + grid-template-columns: minmax(0, 1fr); + } + .deploy-result-actions { + justify-content: flex-start; + flex-wrap: wrap; + width: fit-content; + } +} +.ghost-link.disabled, +.ghost-link[aria-disabled='true'] { + opacity: 0.45; + pointer-events: none; + cursor: not-allowed; +} + +.present-overlay { + position: fixed; + inset: 0; + z-index: 1000; + background: black; + display: flex; +} +.present-overlay iframe { + flex: 1; + width: 100%; + height: 100%; + border: none; + background: white; +} +.present-exit { + position: absolute; + top: calc(env(safe-area-inset-top, 0px) + 20px); + right: calc(env(safe-area-inset-right, 0px) + 20px); + z-index: 1001; + display: inline-flex; + align-items: center; + padding: 6px 12px; + font-size: 12px; + background: rgba(255, 255, 255, 0.92); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + cursor: pointer; +} +.present-exit:hover { background: white; } + +/* Picker (legacy in some surfaces) */ +.picker { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 2px 6px 2px 10px; + background: var(--bg-subtle); + border: 1px solid var(--border); + border-radius: var(--radius-sm); +} +.picker-label { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); +} +.picker select { + border: none; + background: transparent; + padding: 4px 6px; + width: auto; + min-width: 120px; + box-shadow: none; +} +.picker select:focus { outline: none; box-shadow: none; } +.picker.agent-picker select { min-width: 140px; } +.picker.agent-picker .icon-btn { + padding: 2px 8px; + background: transparent; + border: none; +} + +/* Preview pane (legacy single-iframe path; kept for fallbacks) */ +.preview { + display: flex; + flex-direction: column; + background: var(--bg-subtle); + min-height: 0; +} +.preview-header { + padding: 8px 12px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel); + display: flex; + justify-content: space-between; + align-items: center; + font-size: 12px; + color: var(--text-muted); +} +.preview-title { + display: flex; align-items: center; gap: 10px; min-width: 0; +} +.preview-actions { display: flex; gap: 8px; align-items: center; } +.preview-actions .muted { font-size: 11px; } +.preview-actions button { padding: 4px 10px; font-size: 12px; } +.preview-body { flex: 1; min-height: 0; position: relative; } +.preview-body iframe { + width: 100%; height: 100%; border: none; background: white; +} +.preview-empty { + display: flex; align-items: center; justify-content: center; + height: 100%; + color: var(--text-muted); + padding: 24px; + text-align: center; + font-size: 13px; +} + +/* ============================================================ + Merged from main — persistence-era additions: editable project + title, conversations dropdown, examples filters + preview modal, + question form, design-system modal, system reminder collapse, + operations todo card. + ============================================================ */ + +/* Editable project title (inline rename in topbar) */ +.topbar-title .title.editable, +.app-project-title .title.editable { + outline: none; + border-radius: 4px; + padding: 1px 4px; + margin: 0px; + max-width: min(100%, 420px); +} +.topbar-title .title.editable:focus, +.app-project-title .title.editable:focus { + background: var(--bg-subtle); + box-shadow: 0 0 0 1px var(--accent); +} + +/* Conversations dropdown — pill in the project topbar */ +.conv-pill { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg-panel); + font: inherit; + font-size: 12px; + color: inherit; + cursor: pointer; + user-select: none; + max-width: 220px; +} +.conv-pill:hover { background: var(--bg-subtle); } +.conv-pill.open { background: var(--bg-subtle); border-color: var(--accent); } +.conv-pill-icon { font-size: 13px; line-height: 1; } +.conv-pill-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.conv-pill-count { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 18px; + height: 18px; + padding: 0 6px; + border-radius: 9px; + background: var(--bg-subtle); + color: var(--text-muted); + font-size: 10px; +} +.conv-menu { + position: fixed; + width: 320px; + max-height: 420px; + overflow-y: auto; + background: var(--bg-panel); + border: 1px solid var(--border); + border-radius: var(--radius); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); + z-index: 200; + padding: 8px; +} +.conv-menu-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 4px 6px 8px; + border-bottom: 1px solid var(--border); + margin-bottom: 6px; + font-size: 11px; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; +} +.conv-add-btn { padding: 2px 8px; font-size: 11px; } +.conv-menu-empty { + padding: 16px 8px; + font-size: 12px; + color: var(--text-muted); + text-align: center; +} +.conv-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; } +.conv-item { + display: flex; + align-items: stretch; + gap: 4px; + border-radius: 6px; +} +.conv-item.active { background: var(--bg-subtle); } +.conv-item-button { + flex: 1; + display: flex; + flex-direction: column; + gap: 2px; + padding: 6px 8px; + border: none; + background: transparent; + text-align: left; + border-radius: 6px; + cursor: pointer; +} +.conv-item-button:hover { background: var(--bg-subtle); } +.conv-item-name { + font-size: 12px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.conv-item-meta { + font-size: 10px; + color: var(--text-muted); +} +.conv-item-del { + border: none; + background: transparent; + color: var(--text-muted); + font-size: 16px; + width: 24px; + cursor: pointer; + border-radius: 6px; +} +.conv-item-del:hover { background: var(--bg-subtle); color: var(--text); } +.conv-rename-input { + flex: 1; + border: 1px solid var(--accent); + border-radius: 6px; + background: var(--bg-panel); + padding: 6px 8px; + font: inherit; + font-size: 12px; +} + +/* Collapsible system-reminder block in chat */ +.system-reminder-block { + background: var(--bg-subtle); + border: 1px solid var(--border); + border-radius: var(--radius); + margin: 6px 0; +} +.system-reminder-toggle { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 6px 10px; + background: transparent; + border: none; + text-align: left; + font-size: 12px; + color: var(--text-muted); +} +.system-reminder-toggle:hover { background: rgba(0, 0, 0, 0.03); } +.system-reminder-icon { color: var(--text-muted); } +.system-reminder-label { font-weight: 500; color: var(--text); } +.system-reminder-preview { + flex: 1; + color: var(--text-muted); + font-style: italic; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.system-reminder-chev { color: var(--text-muted); font-size: 10px; } +.system-reminder-body { + margin: 0; + padding: 0 12px 10px 12px; + font-family: var(--mono); + font-size: 11px; + line-height: 1.5; + color: var(--text-muted); + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +/* Waiting-on-input pill (richer than the simple italic version above). */ +.op-waiting { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: 8px; + max-width: 100%; + padding: 8px 12px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg-subtle); + font-size: 12px; + color: var(--text-muted); + font-style: normal; +} +.op-waiting-dot { + width: 8px; + height: 8px; + border-radius: 999px; + background: var(--accent); + flex-shrink: 0; + animation: pulse 1.4s ease-in-out infinite; +} +.op-waiting-label { + font-weight: 500; + color: var(--text); +} +.op-waiting-detail { + min-width: 0; + max-width: 100%; + font-family: var(--mono); + font-size: 11px; + background: var(--bg-panel); + padding: 1px 6px; + border-radius: 4px; + color: var(--text-muted); + overflow-wrap: anywhere; + white-space: normal; +} +.op-waiting-hint { + flex-basis: 100%; + font-size: 11px; + color: var(--text-soft); + font-style: italic; +} + +/* op-todo card variant (the .op-todo .todo-list rule already exists above) */ +.op-todo { + border-color: var(--accent-soft); + background: linear-gradient(180deg, var(--accent-tint) 0%, var(--bg-panel) 60%); +} +.op-todo .op-card-head { + border-bottom: 1px solid color-mix(in srgb, var(--accent-soft) 70%, transparent); + background: transparent; +} +.op-todo .op-icon { + background: var(--accent-soft); + color: var(--accent); +} +.op-todo .op-title { + letter-spacing: 0.02em; + text-transform: uppercase; + font-size: 11px; + color: var(--text-strong); +} +.op-todo .op-meta { + margin-left: auto; + font-variant-numeric: tabular-nums; + font-size: 11px; + color: var(--text-strong); +} + +/* + * Pinned task list above the chat composer. The card sits just above the + * prompt input, persistent for the whole conversation rather than buried + * in a single assistant message. It mirrors the Cursor / Codex / Claude + * Code TTY pattern where the todo list is always one click away while you + * are typing the next prompt. The card is collapsible via its own + * header — see `.op-todo-toggle` below. + */ +.chat-pinned-todo { + position: relative; + padding: 0 10px; + transition: + transform 220ms cubic-bezier(0.23, 1, 0.32, 1), + opacity 200ms cubic-bezier(0.23, 1, 0.32, 1); +} +/* Slide-down dismiss: matches the global animation philosophy + * (strong ease-out, ~200ms). The wrapper animates away while the + * Done click still resolves the snapshot key. */ +.chat-pinned-todo-exit { + transform: translateY(24px); + opacity: 0; + pointer-events: none; +} +/* Soft fade above the pinned card so chat text scrolling beneath it + * dissolves into the panel background instead of getting hard-clipped + * at the card's top edge. Sits in the gutter directly above the card, + * pulled up by negative bottom so it overlaps the chat scroll area. */ +.chat-pinned-todo::before { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 100%; + height: 28px; + background: linear-gradient(to top, var(--bg-panel), transparent); + pointer-events: none; +} +.chat-pinned-todo .op-card.op-todo { + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); + /* Square the bottom corners so the card reads as tucked under the + * composer below it, rather than as a floating pill with a gap. */ + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: 0; +} + +/* + * Shared accordion expand/collapse motion. `grid-template-rows: 0fr -> 1fr` + * is the modern way to animate auto-height content; pair it with a + * fade so the body doesn't pop in flat. Strong ease-out curve, with + * asymmetric timing (enter 200ms, exit 140ms) per Emil Kowalski's + * framework — entrance reads as considered, exit is decisive. + */ +.accordion-collapsible { + display: grid; + grid-template-rows: 0fr; + opacity: 0; + transition: + grid-template-rows 140ms cubic-bezier(0.23, 1, 0.32, 1), + opacity 100ms cubic-bezier(0.23, 1, 0.32, 1); +} +.accordion-collapsible.open { + grid-template-rows: 1fr; + opacity: 1; + transition: + grid-template-rows 200ms cubic-bezier(0.23, 1, 0.32, 1), + opacity 200ms cubic-bezier(0.23, 1, 0.32, 1); +} +.accordion-collapsible-inner { + min-height: 0; + overflow: hidden; +} + +/* + * Make the todo card header a clickable expand/collapse affordance. + * Default is expanded while work is in flight; the user can flip it via + * the chevron at the right. Removing the bottom border in the collapsed + * state keeps the card looking like a single pill rather than an empty + * disclosure with a hanging line. + */ +.op-todo .op-todo-head { + display: flex; + align-items: stretch; + padding: 0; +} +.op-todo .op-todo-toggle { + display: flex; + align-items: center; + gap: 8px; + flex: 1; + min-width: 0; + padding: 8px 12px; + background: transparent; + border: 0; + cursor: pointer; + text-align: left; + font: inherit; + color: inherit; +} +.op-todo .op-todo-done { + display: inline-flex; + align-items: center; + margin: 6px 10px 6px 0; + padding: 2px 12px; + font-size: 11px; + font-weight: 500; + border: 1px solid var(--green-border); + border-radius: var(--radius-pill); + background: var(--green-bg); + color: var(--green); + cursor: pointer; +} +.op-todo .op-todo-done:hover { + filter: brightness(1.05); +} +.op-todo-collapsed .op-card-head { + border-bottom-color: transparent; +} +.op-todo-current { + margin-left: 8px; + font-size: 12px; + color: var(--text-muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; + flex: 1; +} +.op-todo-chev { + margin-left: 4px; + font-size: 11px; + color: var(--text-muted); + transition: transform 120ms ease; +} + +/* Question form — interactive form a planning agent can post into chat */ +.question-form { + margin: 8px 0; + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--bg-panel); + box-shadow: var(--shadow-md); + overflow: hidden; +} +.question-form-locked { + background: var(--bg-subtle); + box-shadow: none; + opacity: 0.92; +} +.question-form-head { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 14px; + border-bottom: 1px solid var(--border); + background: linear-gradient(180deg, var(--accent-tint) 0%, var(--bg-panel) 100%); +} +.question-form-locked .question-form-head { + background: var(--bg-subtle); +} +.question-form-icon { + width: 28px; + height: 28px; + border-radius: 999px; + background: var(--accent); + color: white; + font-weight: 600; + font-size: 14px; + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} +.question-form-titles { + flex: 1; + min-width: 0; +} +.question-form-title { + font-size: 14px; + font-weight: 600; + letter-spacing: -0.01em; + color: var(--text); +} +.question-form-desc { + margin-top: 2px; + font-size: 12px; + color: var(--text-muted); + line-height: 1.45; +} +.question-form-pill { + font-size: 10px; + letter-spacing: 0.04em; + text-transform: uppercase; + padding: 3px 8px; + border-radius: 999px; + background: var(--bg-panel); + border: 1px solid var(--border); + color: var(--text-muted); +} +.question-form-body { + display: flex; + flex-direction: column; + gap: 14px; + padding: 14px; +} +.qf-field { display: flex; flex-direction: column; gap: 6px; } +.qf-label { + font-size: 12px; + font-weight: 500; + color: var(--text); + display: flex; + align-items: center; + gap: 4px; +} +.qf-required { color: var(--accent); } +.qf-help { + font-size: 11px; + color: var(--text-muted); + margin-top: -2px; + word-wrap: break-word; + overflow-wrap: break-word; + line-height: 1.4; +} +.qf-options { + display: flex; + flex-wrap: wrap; + gap: 6px; +} +.qf-chip { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + border: 1px solid var(--border); + border-radius: 999px; + background: var(--bg-panel); + font-size: 12px; + cursor: pointer; + transition: border-color 120ms ease, background 120ms ease, color 120ms ease; +} +.qf-chip input { width: auto; margin: 0; display: none; } +.qf-chip-copy { + display: flex; + flex-direction: column; + gap: 2px; + line-height: 1.2; +} +.qf-chip-desc { + color: var(--text-muted); + font-size: 11px; + font-weight: 400; +} +.qf-chip-on .qf-chip-desc { + color: inherit; + opacity: 0.72; +} +.qf-chip:hover { border-color: var(--border-strong); } +.qf-chip-disabled { + cursor: not-allowed; + opacity: 0.48; +} +.qf-chip-disabled:hover { border-color: var(--border); } +.qf-chip-on { + border-color: var(--accent); + background: var(--accent-soft); + color: var(--accent-hover); + font-weight: 500; +} +.question-form-locked .qf-chip { cursor: not-allowed; } +.qf-input, +.qf-select, +.qf-textarea { + font-size: 13px; +} +.question-form-foot { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 14px; + border-top: 1px solid var(--border); + background: var(--bg-subtle); +} +.qf-hint, +.qf-locked-note { + flex: 1; + font-size: 11px; + color: var(--text-muted); + line-height: 1.4; +} + +/* Direction-cards picker — used by the second discovery form when the user + selects "Pick a direction for me". Each card carries a palette swatch + row, a serif/sans type sample, a mood blurb, and a real-world refs + line so the user can scan visually instead of squinting at radio labels. */ +.qf-direction-cards { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + gap: 10px; + margin-top: 4px; +} +.qf-card { + position: relative; + display: flex; + flex-direction: column; + gap: 10px; + padding: 14px; + background: var(--bg); + border: 1px solid var(--border); + border-radius: 10px; + cursor: pointer; + transition: + border-color 0.12s ease, + background 0.12s ease, + transform 0.06s ease; +} +.qf-card input { display: none; } +.qf-card:hover { border-color: var(--border-strong); } +.qf-card:active { transform: translateY(1px); } +.qf-card-on { + border-color: var(--accent, #c96442); + background: color-mix(in oklch, var(--accent, #c96442) 4%, var(--bg)); + box-shadow: 0 0 0 1px var(--accent, #c96442) inset; +} +.qf-card-disabled { cursor: not-allowed; opacity: 0.6; } +.qf-card-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} +.qf-card-title { + font-size: 13px; + font-weight: 600; + letter-spacing: -0.005em; + color: var(--text); + line-height: 1.3; +} +.qf-card-pill { + font-family: ui-monospace, 'JetBrains Mono', monospace; + font-size: 9px; + letter-spacing: 0.06em; + text-transform: uppercase; + padding: 2px 6px; + background: var(--accent, #c96442); + color: #fff; + border-radius: 999px; + flex-shrink: 0; +} +.qf-card-swatches { + display: flex; + gap: 4px; + height: 18px; +} +.qf-card-swatch { + flex: 1; + border-radius: 3px; + border: 1px solid rgba(0, 0, 0, 0.08); + min-width: 0; +} +.qf-card-types { + display: flex; + align-items: baseline; + gap: 12px; + padding: 6px 0; + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); +} +.qf-card-type-display { + font-size: 28px; + line-height: 1; + letter-spacing: -0.02em; + color: var(--text); + flex-shrink: 0; +} +.qf-card-type-body { + font-size: 12px; + color: var(--text-muted); + line-height: 1.3; + letter-spacing: -0.005em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.qf-card-mood { + margin: 0; + font-size: 12px; + line-height: 1.45; + color: var(--text-muted); +} +.qf-card-refs { + margin: 0; + font-size: 11px; + color: var(--text-muted); + line-height: 1.4; +} +.qf-card-refs-label { + font-family: ui-monospace, 'JetBrains Mono', monospace; + font-size: 10px; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--text-muted); + opacity: 0.7; +} + +/* Design-system preview modal */ +.ds-modal-backdrop { + position: fixed; + inset: 0; + z-index: 900; + background: rgba(28, 27, 26, 0.42); + backdrop-filter: blur(2px); + -webkit-app-region: no-drag; + display: flex; + align-items: stretch; + justify-content: center; + padding: 32px; +} +.ds-modal { + width: 100%; + max-width: 1320px; + background: var(--bg); + -webkit-app-region: no-drag; + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg); + display: flex; + flex-direction: column; + overflow: hidden; + min-height: 0; +} +.ds-modal-fullscreen { + max-width: none; + border-radius: 0; +} +/* Two-row header: title row keeps the title/subtitle + close together at + the top; toolbar row hosts tabs (left) and the action cluster (right). + This split lets the title breathe even when the toolbar carries a busy + set of controls (Use plugin + sidebar + Fullscreen + Share + plugin + share + ...), and keeps the close button anchored in its expected + top-right corner. */ +.ds-modal-header { + display: flex; + flex-direction: column; + gap: 12px; + padding: 14px 20px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel); +} +.ds-modal-header-top { + display: flex; + align-items: flex-start; + gap: 12px; +} +.ds-modal-header-toolbar { + display: flex; + align-items: center; + gap: 12px; + flex-wrap: wrap; +} +.ds-modal-title-block { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 3px; +} +.ds-modal-title { + font-size: 16px; + font-weight: 600; + letter-spacing: -0.01em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: var(--text-strong, var(--text)); +} +.ds-modal-subtitle { + font-size: 12px; + color: var(--text-muted); + line-height: 1.5; + /* Allow up to two lines for long descriptions (e.g. Chinese plugin + subtitles) instead of clipping at the first word — keeps the + vertical footprint bounded but readable. */ + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + word-break: break-word; +} +.ds-modal-tabs { + display: inline-flex; + background: var(--bg-subtle); + border: 1px solid var(--border); + border-radius: 999px; + padding: 3px; + gap: 2px; + flex-shrink: 0; +} +.ds-modal-tab { + background: transparent; + border: none; + border-radius: 999px; + padding: 6px 16px; + font-size: 12.5px; + font-weight: 500; + color: var(--text-muted); + cursor: pointer; +} +.ds-modal-tab:hover { color: var(--text); } +.ds-modal-tab.active { + background: var(--bg-panel); + color: var(--text); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); +} +.ds-modal-actions { + display: flex; + align-items: center; + gap: 8px; + justify-content: flex-end; + flex-wrap: wrap; + margin-left: auto; +} +/* Keep individual action labels (Use plugin / DESIGN.md / Fullscreen / + Share / etc.) on a single line; the toolbar row itself can wrap to a + second visual line only at very narrow widths via flex-wrap above. */ +.ds-modal-actions > button, +.ds-modal-actions .share-menu > button { + white-space: nowrap; + flex-shrink: 0; +} +.ds-modal-stage { + flex: 1; + min-height: 0; + background: white; + position: relative; + display: flex; + align-items: stretch; +} +.ds-modal-stage-iframe { + flex: 1; + min-width: 0; + position: relative; + overflow: hidden; + background: white; +} +.ds-modal-stage-iframe-scaler { + position: absolute; + top: 0; + left: 0; + transform-origin: top left; + background: white; + /* Prevent the GPU layer from blurring the scaled iframe on Retina. */ + will-change: transform; +} +.ds-modal-stage-iframe-scaler iframe { + width: 100%; + height: 100%; + border: none; + display: block; + background: white; +} +.ds-modal-stage.has-sidebar .ds-modal-stage-iframe { + flex: 1 1 60%; +} +.ds-modal-sidebar { + position: relative; + flex: 1 1 40%; + min-width: 320px; + max-width: 560px; + border-left: 1px solid var(--border); + background: var(--bg-panel); + overflow: auto; + scrollbar-gutter: stable; + display: flex; + flex-direction: column; +} +.ds-modal-stage-handle { + position: absolute; + top: 50%; + width: 18px; + height: 56px; + transform: translateY(-50%); + background: var(--bg-panel); + border: 1px solid var(--border); + color: var(--text-muted); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + line-height: 1; + padding: 0; + z-index: 3; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06); + transition: color 120ms ease, background 120ms ease; +} +.ds-modal-stage-handle:hover { color: var(--text); background: var(--bg-subtle); } +.ds-modal-stage-handle.is-expand { + right: 0; + border-right: none; + border-radius: 8px 0 0 8px; +} +.ds-modal-stage-handle.is-collapse { + position: sticky; + left: 0; + border-left: none; + border-radius: 0 8px 8px 0; +} +.ds-modal-fullscreen .ds-modal-stage:fullscreen .ds-modal-stage-iframe-scaler, +.ds-modal-stage:fullscreen .ds-modal-stage-iframe-scaler { + height: 100vh; +} + +/* Custom (non-iframe) stage container — used by the plugin media + detail variant so image / video / audio plugins share the + same modal chrome (header, sidebar toggle, fullscreen, share) + as HTML and design-system plugins. The container fills the + stage frame and centers its child media element. */ +.ds-modal-stage-custom { + position: absolute; + inset: 0; + display: flex; + background: var(--bg-subtle, #f6f5f1); +} +.plugin-media-stage { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + overflow: auto; + min-height: 0; +} +.plugin-media-stage__empty { + color: var(--text-muted); + font-size: 13px; +} +.plugin-media-stage__image, +.plugin-media-stage__video, +.plugin-media-stage__audio-poster { + max-width: 100%; + max-height: 100%; + object-fit: contain; + border-radius: 8px; + box-shadow: 0 12px 36px rgba(0, 0, 0, 0.16); + background: white; +} +.plugin-media-stage__video { + width: auto; + height: auto; +} +.plugin-media-stage__audio { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 18px; + width: 100%; + max-width: 480px; +} +.plugin-media-stage__audio-poster { + width: 320px; + height: 320px; + object-fit: cover; +} +.plugin-media-stage__audio-glyph { + width: 200px; + height: 200px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--accent) 18%, transparent), + color-mix(in srgb, var(--accent) 4%, transparent) + ); + color: var(--accent); + box-shadow: 0 12px 36px rgba(0, 0, 0, 0.12); +} +.plugin-media-stage__audio-player { + width: 100%; +} + +/* Sidebar prompt block — top of the right pane, mirrors the + PROMPT BODY card in the previous bespoke media modal so users + still get the prompt + copy affordance, just inside the + unified PreviewModal chrome. */ +.plugin-media-sidebar { + display: flex; + flex-direction: column; + gap: 16px; +} +.plugin-media-sidebar__prompt { + border: 1px solid var(--border); + border-radius: 10px; + padding: 12px 14px; + background: var(--surface-1, var(--bg-panel)); + display: flex; + flex-direction: column; + gap: 8px; +} +.plugin-media-sidebar__prompt-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} +.plugin-media-sidebar__prompt-label { + font-size: 11px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-muted); +} +.plugin-media-sidebar__prompt-copy { + appearance: none; + background: transparent; + border: 1px solid transparent; + color: var(--text-muted); + font-size: 11.5px; + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + border-radius: 6px; + cursor: pointer; + transition: color 120ms ease, background 120ms ease, border-color 120ms ease; +} +.plugin-media-sidebar__prompt-copy:hover { + color: var(--text); + background: var(--bg-subtle); + border-color: var(--border); +} +.plugin-media-sidebar__prompt-body { + margin: 0; + font-family: + ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace; + font-size: 12px; + line-height: 1.55; + color: var(--text); + white-space: pre-wrap; + word-break: break-word; + max-height: 260px; + overflow: auto; +} +.ds-modal-empty { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + font-size: 13px; +} +/* Error state extends .ds-modal-empty: stacks a title, body, and Retry + button instead of a single line, so a fetch failure no longer leaves + the modal stuck at "Loading…" with disabled toolbar buttons and no + recovery path. Issue #860. */ +.ds-modal-error { + flex-direction: column; + gap: 10px; + padding: 0 24px; + text-align: center; +} +.ds-modal-error-title { + color: var(--text); + font-size: 14px; + font-weight: 600; +} +.ds-modal-error-body { + color: var(--text-muted); + font-size: 13px; + max-width: 48ch; + line-height: 1.5; +} +.ds-modal-actions .ghost.is-active { + background: var(--accent-tint); + color: var(--accent); + border-color: var(--accent); +} +/* Accent CTA used by plugin detail wrappers to surface a "Use plugin" + action without forking the whole modal layout. Sits before the + ghost actions, mirroring the accent button used in NewProjectModal + so the affordance reads consistently across surfaces. */ +.ds-modal-primary-action { + appearance: none; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 7px 16px; + border-radius: 6px; + border: 1px solid var(--accent); + background: var(--accent); + color: white; + font-size: 12.5px; + font-weight: 600; + white-space: nowrap; + cursor: pointer; + transition: background-color 120ms ease, border-color 120ms ease, + transform 120ms ease; +} +.ds-modal-primary-action:hover:not(:disabled) { + background: var(--accent-strong, var(--accent)); + border-color: var(--accent-strong, var(--accent)); +} +.ds-modal-primary-action:active:not(:disabled) { + transform: translateY(0.5px); +} +.ds-modal-primary-action:disabled { + cursor: progress; + opacity: 0.6; +} +/* Dedicated icon-button shape for the modal close affordance. Matches the + visual language used by plugin-media-detail__close and + new-project-modal__close so every modal closes with the same control. */ +.ds-modal-close { + appearance: none; + width: 32px; + height: 32px; + border-radius: 999px; + background: transparent; + border: 1px solid transparent; + color: var(--text-muted); + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + flex-shrink: 0; + transition: background-color 120ms ease, color 120ms ease, + border-color 120ms ease; +} +.ds-modal-close svg { + display: block; + width: 14px !important; + height: 14px !important; + stroke-width: 1.9; +} +.ds-modal-close:hover, +.ds-modal-close:focus-visible { + background: var(--bg-subtle); + border-color: var(--border); + color: var(--text); + outline: none; +} + +/* DESIGN.md side panel — monospace source view with light syntax tints, + echoing the styles.refero.design "compact" markdown source pane. */ +.design-spec-empty { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + color: var(--text-muted); + font-size: 12px; +} +.design-spec-pre { + margin: 0; + padding: 16px 18px; + font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; + font-size: 12px; + line-height: 1.6; + /* Wrap long lines instead of forcing the side pane to scroll horizontally — + DESIGN.md prose can have 200+ char paragraphs that otherwise produce a + scrollbar inside the modal. `overflow-wrap: anywhere` keeps long + hyphenated tokens (URLs, file paths) from blowing out the column. */ + white-space: pre-wrap; + overflow-wrap: anywhere; + word-break: break-word; + overflow-y: auto; + overflow-x: hidden; + color: var(--text); + background: var(--bg-panel); + flex: 1; +} +.design-spec-pre code { font: inherit; color: inherit; background: transparent; } +.design-spec-line { display: inline; } +.design-spec-line.is-h1 { color: #2563eb; font-weight: 700; } +.design-spec-line.is-h2 { color: #0891b2; font-weight: 700; } +.design-spec-line.is-h3 { color: #0d9488; font-weight: 600; } +.design-spec-line.is-h4 { color: #16a34a; font-weight: 600; } +.design-spec-line.is-quote { color: #6b7280; font-style: italic; } +.design-spec-line.is-list { color: var(--text); } +.design-spec-line.is-table { color: #7c3aed; } +.design-spec-line.is-fence { color: #dc2626; } +.design-spec-line.is-blank { color: var(--text-muted); } +.md-tk-bold { font-weight: 700; color: var(--text); } +.md-tk-em { font-style: italic; color: var(--text); } +.md-tk-code { + background: var(--bg-subtle); + padding: 0 4px; + border-radius: 3px; + color: #0f766e; +} +.md-tk-color { + display: inline-flex; + align-items: center; + gap: 4px; + color: #be185d; +} +.md-tk-color-swatch { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 2px; + border: 1px solid rgba(0, 0, 0, 0.12); + vertical-align: middle; +} +@media (max-width: 760px) { + .ds-modal-backdrop { padding: 0; } + .ds-modal { border-radius: 0; } + .ds-modal-header { gap: 10px; padding: 12px 14px; } + .ds-modal-actions { justify-content: flex-start; } + .ds-modal-stage { flex-direction: column; } + .ds-modal-stage.has-sidebar .ds-modal-stage-iframe { flex: 1 1 50%; } + .ds-modal-sidebar { + border-left: none; + border-top: 1px solid var(--border); + flex: 1 1 50%; + min-width: 0; + max-width: none; + } + /* On stacked layout the side handles (which assume horizontal split) + would float over content awkwardly — fall back to the header toggle. */ + .ds-modal-stage-handle { display: none; } +} + +/* Examples gallery toolbar — filter pills + richer card metadata */ +.examples-toolbar { + display: flex; + flex-direction: column; + gap: 10px; + position: sticky; + top: 0; + /* Sit above example preview iframes while scrolling (iframes get their own + compositor layer; z-index: 2 was not always enough for a clean mask). */ + z-index: 20; + isolation: isolate; + margin: 0; + padding: 0 0 14px; + background: var(--bg); + border-bottom: 1px solid var(--border-soft); + box-shadow: var(--shadow-sm); +} +.examples-search { + position: relative; + /* Full-width band so scrolling previews cannot show through beside a narrow + search field on wide layouts (toolbar stays sticky; iframes layer high). */ + width: 100%; + max-width: 100%; + align-self: stretch; + background: var(--bg); +} +.examples-search input { + display: block; + width: 100%; + max-width: 360px; + padding: 7px 12px 7px 32px; + background: var(--bg-panel); + border: 1px solid var(--border); + border-radius: 8px; + font-size: 13px; + color: var(--text); +} +.examples-search input::placeholder { color: var(--text-faint); } +.examples-search input:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-soft); +} +.examples-search .search-icon { + position: absolute; + left: 10px; + top: 50%; + transform: translateY(-50%); + color: var(--text-faint); + pointer-events: none; + display: inline-flex; + align-items: center; +} +.examples-filter-row { + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; +} +.examples-filter-label { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--text-muted); + margin-right: 6px; + font-weight: 500; +} +.filter-pill { + background: var(--bg-panel); + border: 1px solid var(--border); + border-radius: 999px; + padding: 5px 12px; + font-size: 12px; + color: var(--text-muted); + cursor: pointer; + display: inline-flex; + gap: 6px; + align-items: center; +} +button.filter-pill:hover:not(:disabled) { + background: var(--bg-muted); + border-color: var(--border-strong); + color: var(--text); +} +/* + Selected filter pill — quiet "I am the chosen one" treatment, NOT a CTA. + Earlier the active pill borrowed the full `--accent` fill, which read as + a primary button competing with the real action ("+ New memory") next to + it. Users would mis-read the count (e.g. "All 0") as a clickable big + orange button. We now keep the selection bordered + slightly weighted so + it still looks "on" but stops shouting; the lone primary button on the + right keeps its monopoly on the accent color. +*/ +.filter-pill.active { + background: var(--bg-subtle); + border-color: var(--border-strong); + color: var(--text); + font-weight: 600; +} +button.filter-pill.active:hover:not(:disabled) { + background: var(--bg-muted); + border-color: var(--border-strong); + color: var(--text); +} +.filter-pill-count { + font-size: 11px; + opacity: 0.7; +} +button.filter-pill:hover:not(:disabled) .filter-pill-count, +.filter-pill.active .filter-pill-count { + color: currentColor; + opacity: 0.9; +} +.example-card-actions { + display: flex; + gap: 8px; + align-items: center; + flex-wrap: wrap; +} +.example-tags { + display: flex; + gap: 6px; + flex-wrap: wrap; + margin-top: 2px; +} +/* Tag variants — keep the worktree base .example-tag style; these add + color-coded variants for mode/platform pills. */ +.example-tag.platform-mobile { color: var(--accent); border-color: var(--accent-soft); background: var(--accent-soft); } +.example-tag.mode-deck { color: var(--accent); border-color: var(--accent-soft); background: var(--accent-soft); } + +/* Example preview hover affordance + click-through overlay */ +.example-preview { + cursor: zoom-in; + transition: border-color 0.15s, box-shadow 0.15s; +} +.example-preview:hover { + border-color: var(--accent); + box-shadow: 0 6px 18px rgba(201, 100, 66, 0.10); +} +.example-preview:focus-visible { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-soft); +} +.example-preview-overlay { + position: absolute; + bottom: 10px; + right: 10px; + padding: 5px 12px; + font-size: 11.5px; + font-weight: 500; + color: white; + background: rgba(28, 27, 26, 0.78); + border-radius: 999px; + opacity: 0; + transition: opacity 0.15s; + pointer-events: none; + letter-spacing: 0.02em; +} +.example-preview:hover .example-preview-overlay, +.example-preview:focus-visible .example-preview-overlay { opacity: 1; } + +/* ============================================================ + Loading primitives — spinner, skeletons, shimmer + ============================================================ */ +@keyframes icon-spin { to { transform: rotate(360deg); } } +@keyframes shimmer { + 0% { background-position: -400px 0; } + 100% { background-position: 400px 0; } +} +.icon-spin { animation: icon-spin 0.9s linear infinite; transform-origin: center; } + +.loading-spinner { + display: inline-flex; + align-items: center; + gap: 6px; + color: var(--text-muted); +} +.loading-spinner-label { font-size: 12px; } + +.skeleton-block, +.skeleton-shimmer { + display: inline-block; + background-color: var(--bg-subtle); + background-image: linear-gradient( + 90deg, + var(--bg-subtle) 0%, + var(--bg-muted) 50%, + var(--bg-subtle) 100% + ); + background-size: 800px 100%; + animation: shimmer 1.4s linear infinite; +} +.skeleton-block + .skeleton-block { margin-top: 6px; } + +.design-card-skeleton { cursor: default; pointer-events: none; } +.design-card-skeleton .design-card-thumb { background: none; } +.design-card-skeleton .design-card-thumb::before, +.design-card-skeleton .design-card-thumb::after { display: none; } +.design-card-skeleton .design-card-thumb { + background-image: linear-gradient( + 90deg, + var(--bg-subtle) 0%, + var(--bg-muted) 50%, + var(--bg-subtle) 100% + ); + background-size: 800px 100%; + animation: shimmer 1.4s linear infinite; +} + +.centered-loader { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + padding: 48px 12px; + color: var(--text-muted); +} +.centered-loader-label { font-size: 12.5px; letter-spacing: 0.01em; } + +/* ============================================================ + Resizable entry sidebar + ============================================================ */ +.entry { + /* The sidebar width is driven by an inline style fed from local state, + with sensible bounds enforced in JS. */ +} +.entry-side { + position: relative; + min-width: 280px; + max-width: 560px; + min-height: 0; +} +.entry-side-resizer { + position: absolute; + top: 0; + right: -3px; + width: 6px; + height: 100%; + cursor: col-resize; + z-index: 5; + background: transparent; + border: 0; + padding: 0; + transition: background-color 120ms ease; +} +.entry-side-resizer:hover, +.entry-side-resizer.dragging { background: var(--accent-soft); } +body.entry-resizing { cursor: col-resize; user-select: none; } + +/* ============================================================ + Composer Import popover (coming-soon menu) + ============================================================ */ +.composer-import-wrap { position: relative; } +.composer-import-menu { + position: absolute; + bottom: calc(100% + 6px); + left: 0; + min-width: 240px; + background: var(--bg-panel); + border: 1px solid var(--border); + border-radius: var(--radius); + box-shadow: var(--shadow-md); + padding: 6px; + display: flex; + flex-direction: column; + gap: 2px; + z-index: 30; + animation: pop-in 180ms cubic-bezier(0.21, 1.02, 0.73, 1); +} +.composer-import-item { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 10px; + background: transparent; + border: none; + border-radius: var(--radius-sm); + color: var(--text-muted); + cursor: not-allowed; + font-size: 12.5px; + text-align: left; + width: 100%; +} +.composer-import-item:hover { + background: var(--bg-subtle); + color: var(--text); +} +.composer-import-item-enabled { + cursor: pointer; + color: var(--text); +} +.composer-import-item-enabled:hover { + background: var(--bg-subtle); + color: var(--text); +} +.composer-import-item-label { flex: 1; } +.composer-import-item-soon { + font-size: 10px; + letter-spacing: 0.05em; + text-transform: uppercase; + color: var(--text-faint); + background: var(--bg-subtle); + border: 1px solid var(--border); + border-radius: var(--radius-pill); + padding: 2px 6px; +} +.composer-import-item .ico { + display: inline-flex; + width: 16px; + align-items: center; + justify-content: center; + color: var(--text-muted); +} + +/* ============================================================ + Chat header: conversation history dropdown + ============================================================ */ +.chat-history-wrap { position: static; display: inline-flex; align-items: center; } +.chat-history-wrap .icon-only { position: relative; } +.chat-history-wrap.open .icon-only { background: var(--bg-subtle); color: var(--text); } +.chat-history-badge { + position: absolute; + top: 2px; + right: 2px; + min-width: 14px; + height: 14px; + padding: 0 3px; + font-size: 9.5px; + font-weight: 600; + letter-spacing: 0; + background: var(--accent); + color: white; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + line-height: 1; +} +.chat-history-menu { + position: absolute; + top: calc(100% + 6px); + left: 12px; + right: 12px; + width: auto; + max-height: min(360px, calc(100vh - 160px)); + display: flex; + flex-direction: column; + background: var(--bg-panel); + border: 1px solid var(--border); + border-radius: var(--radius); + box-shadow: var(--shadow-md); + z-index: 30; + overflow: hidden; + animation: pop-in 180ms cubic-bezier(0.21, 1.02, 0.73, 1); +} +.chat-history-menu-head { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 10px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel); +} +.chat-history-menu-title { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--text-muted); + font-weight: 600; +} +.chat-history-new { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + font-size: 11px; + border-radius: var(--radius-pill); + background: var(--bg-subtle); + border: 1px solid var(--border); + color: var(--text-muted); +} +.chat-history-new:hover { background: var(--bg-muted); color: var(--text); } +.chat-history-list { + display: flex; + flex-direction: column; + gap: 1px; + padding: 4px 6px 6px; + overflow-y: auto; +} +.chat-history-empty { + padding: 16px 8px; + font-size: 12px; + color: var(--text-muted); + text-align: center; + font-style: italic; +} +.chat-conv-item { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 8px; + border-radius: var(--radius-sm); + background: transparent; + border: 1px solid transparent; + color: var(--text); + font-size: 12.5px; + text-align: left; + cursor: pointer; + width: 100%; +} +.chat-conv-item:hover { background: var(--bg-subtle); } +.chat-conv-item.active { + background: var(--accent-tint); + border-color: var(--accent-soft); + color: var(--text-strong); +} +.chat-conv-item-name { + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.chat-conv-item-meta { + font-size: 10.5px; + color: var(--text-faint); + font-variant-numeric: tabular-nums; +} +.chat-conv-item-del { + width: 22px; + height: 22px; + padding: 0; + border: none; + background: transparent; + color: var(--text-faint); + border-radius: 4px; + display: inline-flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 120ms ease, background-color 120ms ease, color 120ms ease; +} +.chat-conv-item:hover .chat-conv-item-del { opacity: 1; } +.chat-conv-item-del:hover { background: var(--red-bg); color: var(--red); } + +/* ============================================================ + Scroll-to-bottom button (chat) + ============================================================ */ +.chat-log-wrap { position: relative; flex: 1; min-height: 0; min-width: 0; display: flex; } +.chat-log-wrap .chat-log { flex: 1; } +.chat-jump-btn { + position: absolute; + bottom: 12px; + /* Margin-based centering (instead of `transform: translateX(-50%)`) + * so the slide-in / slide-out transition can animate `transform` + * without overwriting a centering translate. */ + left: 0; + right: 0; + margin: 0 auto; + width: max-content; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + border-radius: var(--radius-pill); + background: var(--bg-panel); + border: 1px solid var(--border-strong); + color: var(--text); + font-size: 12px; + box-shadow: var(--shadow-sm); + cursor: pointer; + z-index: 6; + /* Default = hidden. The slide tucks below and scales down slightly to + * read as folding into the composer. Strong ease-out curve + * (cubic-bezier(0.23, 1, 0.32, 1)) replaces both built-in `ease` and + * the previous slightly-overshoot variant — Emil Kowalski's framework: + * UI animations should use strong custom ease-out, and entrance pills + * should not bounce. Exit is faster than enter (asymmetric timing): + * the user has already chosen to dismiss; system response should be + * decisive. */ + opacity: 0; + transform: translateY(8px) scale(0.9); + visibility: hidden; + pointer-events: none; + transition: + opacity 140ms cubic-bezier(0.23, 1, 0.32, 1), + transform 140ms cubic-bezier(0.23, 1, 0.32, 1), + visibility 0s linear 140ms; +} +.chat-jump-btn-active { + opacity: 1; + transform: translateY(0) scale(1); + visibility: visible; + pointer-events: auto; + transition: + opacity 200ms cubic-bezier(0.23, 1, 0.32, 1), + transform 200ms cubic-bezier(0.23, 1, 0.32, 1), + visibility 0s linear 0s; +} +.chat-jump-btn:hover { background: var(--bg-subtle); border-color: var(--border-strong); } + +/* ============================================================ + Assistant message footer (bottom-of-message status pill) + ============================================================ */ +.assistant-completion-row { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 6px; + margin-top: 10px; +} +.assistant-footer { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 4px 10px; + background: var(--bg-subtle); + border: 1px solid var(--border); + border-radius: var(--radius-pill); + font-size: 11.5px; + color: var(--text-muted); + width: fit-content; +} +.assistant-footer .dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--text-muted); +} +.assistant-footer .dot[data-active="true"] { + background: var(--accent); + animation: pulse 1.2s ease-in-out infinite; +} +.assistant-footer .assistant-label { + font-weight: 600; + color: var(--text-strong); + font-size: 11.5px; +} +.assistant-footer .assistant-stats { + font-variant-numeric: tabular-nums; + color: var(--text-muted); +} +.assistant-footer[data-unfinished="true"] { + background: var(--amber-bg); + border-color: color-mix(in srgb, var(--amber) 35%, var(--border)); +} +.assistant-footer[data-unfinished="true"] .dot { + background: var(--amber); +} diff --git a/apps/web/src/styles/viewer/core.css b/apps/web/src/styles/viewer/core.css new file mode 100644 index 000000000..b7869a1a7 --- /dev/null +++ b/apps/web/src/styles/viewer/core.css @@ -0,0 +1,1787 @@ +/* -------- Viewer ---------------------------------------------------- */ +.viewer { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; +} +.viewer-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 14px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel); + font-size: 12.5px; + color: var(--text-muted); + gap: 8px; + min-height: 44px; + flex-shrink: 0; +} +.viewer-toolbar-left { display: inline-flex; align-items: center; gap: 8px; } +.viewer-toolbar-actions { display: inline-flex; gap: 2px; align-items: center; flex-wrap: wrap; } +.viewer-toolbar .icon-only, +.viewer-toolbar-actions .icon-only { + width: 28px; + height: 28px; + padding: 0; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-muted); + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 14px; +} +.viewer-toolbar .icon-only:hover:not(:disabled), +.viewer-toolbar-actions .icon-only:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text); } + +/* Shown when a .jsx/.tsx module (loaded by a sibling HTML entry) is opened on + its own — it has no standalone preview, so we point at the page(s) that + render it. Issue #2744. */ +.viewer-module-pointer { + max-width: 460px; + margin: 64px auto; + padding: 0 24px; + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + text-align: center; + color: var(--text-muted); +} +.viewer-module-pointer__title { + margin: 0; + font-size: 15px; + font-weight: 600; + color: var(--text); +} +.viewer-module-pointer__body { + margin: 0; + font-size: 13px; + line-height: 1.5; +} +.viewer-module-pointer__cta { + margin: 4px 0 0; + font-size: 13px; + font-weight: 500; + color: var(--text); +} +.viewer-module-pointer__entries { + list-style: none; + margin: 6px 0 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 6px; + width: 100%; + align-items: center; +} +.viewer-module-pointer__link { + display: inline-flex; + align-items: center; + gap: 7px; + padding: 7px 12px; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--bg-subtle); + color: var(--text); + font-size: 13px; + cursor: pointer; + transition: + background 200ms cubic-bezier(0.23, 1, 0.32, 1), + border-color 200ms cubic-bezier(0.23, 1, 0.32, 1); +} +.viewer-module-pointer__link:hover { + background: var(--bg-muted); + border-color: var(--border-strong); +} +.viewer-module-pointer__link:disabled { + cursor: default; + opacity: 0.6; +} +.viewer-action { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 4px 10px; + background: transparent; + border: none; + border-radius: var(--radius-sm); + color: var(--text-muted); + font-size: 12.5px; + white-space: nowrap; +} +.viewer-action:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text); } +.viewer-action.active { + background: var(--accent-tint); + color: var(--accent-strong); +} +.viewer-action.primary { + background: var(--accent); + border: 1px solid var(--accent); + color: white; +} +.viewer-action.primary:hover:not(:disabled) { + background: var(--accent-hover); + border-color: var(--accent-hover); + color: white; +} +/* Primary actions support a subtle in-flight affordance via data-running. Keeps + layout stable (unlike :disabled-only) so locale text width doesn't jump. */ +.viewer-action.primary[data-running='true'] { + opacity: 0.75; + cursor: progress; +} +/* Preview-only controls: keep layout stable across modes. + When inactive, reserve the same horizontal slot but fully disable interaction. */ +.viewer-preview-controls { + display: inline-flex; + gap: 2px; + align-items: center; + transition: opacity 140ms ease; +} +.viewer-preview-controls[data-active='false'] { + opacity: 0; + pointer-events: none; + user-select: none; +} +.viewer-zoom-level { + min-width: 60px; + justify-content: center; +} +.viewer-divider { + width: 1px; + height: 18px; + background: var(--border); + margin: 0 4px; +} +.viewer-toggle { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 12.5px; + color: var(--text-muted); + padding: 4px 10px; + border-radius: var(--radius-sm); + white-space: nowrap; +} +.viewer-toggle .switch { + position: relative; + width: 28px; + height: 16px; + background: var(--bg-muted); + border-radius: 999px; + border: 1px solid var(--border-strong); + transition: background 120ms ease; +} +.viewer-toggle .switch::after { + content: ''; + position: absolute; + top: 1px; + left: 1px; + width: 12px; height: 12px; + background: var(--bg-panel); + border-radius: 50%; + transition: transform 120ms ease; + box-shadow: var(--shadow-xs); +} +.viewer-toggle.on .switch, +.viewer-toggle.active .switch { background: var(--text); border-color: var(--text); } +.viewer-toggle.on .switch::after, +.viewer-toggle.active .switch::after { transform: translateX(12px); } +.viewer-tabs { display: inline-flex; gap: 2px; } +.viewer-mode-menu { + position: relative; + display: inline-flex; + align-items: center; + width: 96px; +} +.viewer-mode-trigger { + width: 100%; + min-height: 26px; + justify-content: space-between; + gap: 8px; + padding: 4px 8px 4px 10px; + border-color: var(--border); + background-color: var(--bg-panel); + color: var(--text); + font-size: 12.5px; + line-height: 1.2; + box-shadow: var(--shadow-xs); +} +.viewer-mode-trigger:hover:not(:disabled), +.viewer-mode-trigger[aria-expanded='true'] { + border-color: var(--border-strong); + background-color: var(--bg-subtle); +} +.viewer-mode-popover { + position: absolute; + top: calc(100% + 6px); + left: 0; + z-index: 80; + width: 100%; + display: flex; + flex-direction: column; + padding: 5px; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--bg-panel); + box-shadow: var(--shadow-md); + overflow: hidden; +} +.viewer-mode-menu-item { + width: 100%; + min-height: 28px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 5px 8px; + border: 1px solid transparent; + border-radius: var(--radius-sm); + background: transparent; + color: var(--text-muted); + font-size: 12.5px; + text-align: left; +} +.viewer-mode-menu-item:hover, +.viewer-mode-menu-item.active { + background: var(--bg-subtle); + color: var(--text); +} +.viewer-mode-menu-item.active { + background: var(--accent-tint); + color: var(--accent-strong); +} +.viewer-tab { + background: transparent; + border: none; + padding: 4px 10px; + font-size: 12px; + border-radius: var(--radius-sm); + color: var(--text-muted); + white-space: nowrap; +} +.viewer-tab:hover { background: var(--bg-subtle); color: var(--text); } +.viewer-tab.active { + background: var(--bg-subtle); + color: var(--text); + font-weight: 500; +} +.viewer-meta { font-size: 12px; color: var(--text-muted); } +.ghost-link { + font-size: 12px; + color: var(--text-muted); + text-decoration: none; + padding: 4px 10px; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + display: inline-flex; + align-items: center; + gap: 4px; +} +.ghost-link:hover { background: var(--bg-subtle); color: var(--text); } +.viewer-body { + flex: 1; + min-height: 0; + position: relative; + background: var(--bg); + overflow: auto; +} +.live-artifact-refresh-notice { + position: sticky; + top: 0; + z-index: 2; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 9px 14px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel); + color: var(--text-muted); + font-size: 12.5px; +} +.live-artifact-refresh-notice-copy { + display: inline-flex; + align-items: center; + gap: 8px; + min-width: 0; +} +.live-artifact-refresh-notice strong { + color: var(--text); + font-weight: 600; +} +.live-artifact-refresh-notice .icon-only { + width: 24px; + height: 24px; + flex: 0 0 auto; + border: none; + border-radius: var(--radius-sm); + background: transparent; + color: inherit; + font-size: 16px; +} +.live-artifact-refresh-notice .icon-only:hover { + background: color-mix(in srgb, currentColor 12%, transparent); +} +.live-artifact-refresh-notice.running { + background: var(--bg-subtle); +} +.live-artifact-refresh-notice.success { + background: var(--green-bg); + border-color: var(--green-border); + color: var(--green); +} +.live-artifact-refresh-notice.error { + background: var(--red-bg); + border-color: var(--red-border); + color: var(--red); +} +.live-artifact-refresh-notice.success strong, +.live-artifact-refresh-notice.error strong { + color: inherit; +} + +.live-artifact-code-panel { + display: flex; + flex-direction: column; + height: 100%; + min-height: 0; + background: var(--bg); +} +.live-artifact-code-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 12px 16px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel); +} +.live-artifact-code-copy { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} +.live-artifact-code-copy strong { + font-size: 13px; + color: var(--text); +} +.live-artifact-code-copy span { + font-size: 12px; + color: var(--text-muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.live-artifact-code-tabs { + flex-shrink: 0; +} +.live-artifact-code-panel pre.viewer-source { + flex: 1 1 auto; + min-height: 0; +} + +/* ============================================================ + Live artifact — Refresh history panel + ============================================================ */ +.live-artifact-refresh-panel { + display: flex; + flex-direction: column; + gap: 18px; + padding: 20px 22px 28px; + color: var(--text); + background: var(--bg); + min-height: 100%; +} +.live-artifact-refresh-hero { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 20px; + padding: 16px 18px; + border: 1px solid var(--border); + border-radius: 10px; + background: var(--bg-panel); + box-shadow: var(--shadow-xs, 0 1px 2px rgba(16, 24, 40, 0.04)); +} +.live-artifact-refresh-hero-main { + display: flex; + flex-direction: column; + gap: 8px; + min-width: 0; + flex: 1 1 auto; +} +.live-artifact-refresh-hero-desc { + margin: 0; + color: var(--text-muted); + font-size: 12.5px; + line-height: 1.5; +} +.live-artifact-refresh-hero-meta { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 6px; + flex: 0 0 auto; +} +.live-artifact-refresh-hero-metric { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 2px; + text-align: right; +} +.live-artifact-refresh-label { + font-size: 10.5px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--text-muted); +} +.live-artifact-refresh-value { + font-size: 14px; + font-weight: 600; + color: var(--text); + font-variant-numeric: tabular-nums; +} +.live-artifact-refresh-value.muted { + color: var(--text-muted); + font-weight: 500; +} +.live-artifact-refresh-sub { + font-size: 11.5px; + color: var(--text-muted); + font-variant-numeric: tabular-nums; +} + +/* Refresh status badges reuse the .live-artifact-badge pill shape with tone mixins */ +.live-artifact-badge.refresh-status { + text-transform: uppercase; + letter-spacing: 0.04em; +} +.live-artifact-badge.refresh-status.tone-neutral { + color: var(--text-muted); + border-color: var(--border); + background: var(--bg-panel); +} +.live-artifact-badge.refresh-status.tone-running { + color: #1d4ed8; + border-color: rgba(29, 78, 216, 0.22); + background: rgba(29, 78, 216, 0.08); +} +.live-artifact-badge.refresh-status.tone-success { + color: var(--green); + border-color: var(--green-border); + background: var(--green-bg); +} +.live-artifact-badge.refresh-status.tone-warning { + color: var(--amber); + border-color: color-mix(in srgb, var(--amber) 28%, transparent); + background: var(--amber-bg); +} +.live-artifact-badge.refresh-status.tone-error { + color: var(--red); + border-color: var(--red-border); + background: var(--red-bg); +} + +.live-artifact-refresh-facts { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 10px; +} +.live-artifact-refresh-fact { + display: flex; + flex-direction: column; + gap: 4px; + padding: 12px 14px; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--bg-panel); + min-width: 0; +} +.live-artifact-refresh-fact .live-artifact-refresh-value { + font-size: 13px; + font-weight: 600; +} + +.live-artifact-refresh-section { + display: flex; + flex-direction: column; + gap: 10px; +} +.live-artifact-refresh-section-header { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 12px; +} +.live-artifact-refresh-section-header h4 { + margin: 0; + font-size: 12.5px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-muted); +} +.live-artifact-refresh-hint { + font-size: 11.5px; + color: var(--text-muted); +} +.live-artifact-refresh-empty { + padding: 16px 18px; + border: 1px dashed var(--border); + border-radius: 8px; + background: var(--bg-panel); + color: var(--text-muted); + font-size: 12.5px; + line-height: 1.5; +} +.live-artifact-refresh-empty em { + font-style: normal; + font-weight: 600; + color: var(--text); +} + +.live-artifact-refresh-timeline { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--bg-panel); + overflow: hidden; +} +.live-artifact-refresh-event { + display: grid; + grid-template-columns: 20px 1fr; + column-gap: 12px; + padding: 12px 14px; + border-bottom: 1px solid var(--border-soft, var(--border)); + position: relative; +} +.live-artifact-refresh-event:last-child { + border-bottom: none; +} +.live-artifact-refresh-event-dot { + width: 10px; + height: 10px; + border-radius: 999px; + margin-top: 5px; + justify-self: center; + background: var(--border-strong); + box-shadow: 0 0 0 3px var(--bg-panel); +} +.live-artifact-refresh-event.tone-running .live-artifact-refresh-event-dot { + background: #1d4ed8; + animation: liveArtifactRefreshPulse 1.5s ease-in-out infinite; +} +.live-artifact-refresh-event.tone-success .live-artifact-refresh-event-dot { + background: var(--green); +} +.live-artifact-refresh-event.tone-error .live-artifact-refresh-event-dot { + background: var(--red); +} +@keyframes liveArtifactRefreshPulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.55; transform: scale(0.85); } +} +.live-artifact-refresh-event-body { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; +} +.live-artifact-refresh-event-row { + display: inline-flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} +.live-artifact-refresh-event-time { + font-size: 11.5px; + color: var(--text-muted); + font-variant-numeric: tabular-nums; +} +.live-artifact-refresh-event-detail { + font-size: 12.5px; + color: var(--text); + line-height: 1.5; + word-break: break-word; +} + +.live-artifact-refresh-tiles { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--bg-panel); + overflow: hidden; +} +.live-artifact-refresh-tile { + display: grid; + grid-template-columns: 1fr auto; + column-gap: 12px; + row-gap: 6px; + padding: 12px 14px; + border-bottom: 1px solid var(--border-soft, var(--border)); + align-items: center; +} +.live-artifact-refresh-tile:last-child { + border-bottom: none; +} +.live-artifact-refresh-tile-main { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} +.live-artifact-refresh-tile-title { + font-size: 13px; + font-weight: 500; + color: var(--text); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.live-artifact-refresh-tile-meta code { + font-size: 11px; + font-family: var(--mono); + color: var(--text-muted); + background: var(--bg-subtle); + padding: 1px 6px; + border-radius: 4px; +} +.live-artifact-refresh-tile-side { + display: inline-flex; + justify-self: end; +} +.live-artifact-refresh-tile-error { + grid-column: 1 / -1; + font-size: 11.5px; + color: var(--red); + background: var(--red-bg); + border: 1px solid var(--red-border); + border-radius: 6px; + padding: 6px 10px; + line-height: 1.5; + word-break: break-word; +} +.live-artifact-refresh-tile-error.neutral { + color: var(--text-muted); + background: var(--bg-subtle); + border-color: var(--border); +} + +.live-artifact-refresh-kv { + margin: 0; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 10px; + padding: 14px 16px; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--bg-panel); +} +.live-artifact-refresh-kv > div { + display: flex; + flex-direction: column; + gap: 3px; + min-width: 0; +} +.live-artifact-refresh-kv dt { + font-size: 10.5px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--text-muted); +} +.live-artifact-refresh-kv dd { + margin: 0; + font-size: 13px; + color: var(--text); + word-break: break-word; +} +.live-artifact-refresh-kv dd code { + font-family: var(--mono); + font-size: 12px; + background: var(--bg-subtle); + padding: 1px 6px; + border-radius: 4px; +} + +.live-artifact-refresh-raw { + border: 1px solid var(--border); + border-radius: 8px; + background: var(--bg-panel); + overflow: hidden; +} +.live-artifact-refresh-raw > summary { + cursor: pointer; + padding: 10px 14px; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-muted); + user-select: none; + list-style: none; +} +.live-artifact-refresh-raw > summary::-webkit-details-marker { display: none; } +.live-artifact-refresh-raw > summary::before { + content: '▸'; + display: inline-block; + width: 1em; + transition: transform 120ms ease; + color: var(--text-faint); +} +.live-artifact-refresh-raw[open] > summary::before { + transform: rotate(90deg); +} +.live-artifact-refresh-raw > summary:hover { + background: var(--bg-subtle); +} +.live-artifact-refresh-raw-note { + margin: 0; + padding: 0 14px 10px; + color: var(--text-muted); + font-size: 11.5px; + line-height: 1.5; +} +.live-artifact-refresh-raw pre.viewer-source { + border-top: 1px solid var(--border); + min-height: 0; + max-height: 320px; + overflow: auto; +} + +@media (max-width: 640px) { + .live-artifact-refresh-hero { + flex-direction: column; + align-items: stretch; + } + .live-artifact-refresh-hero-meta { + align-items: flex-start; + } + .live-artifact-refresh-hero-metric { + align-items: flex-start; + text-align: left; + } +} +.viewer-body iframe { + width: 100%; + height: 100%; + border: none; + background: white; +} +.artifact-preview-transport-stack { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} +.artifact-preview-transport-stack iframe { + position: absolute; + inset: 0; +} +.artifact-preview-transport-stack iframe[data-od-active='false'] { + visibility: hidden; + pointer-events: none; +} +.viewer-viewport-switcher { + position: relative; + display: inline-flex; + align-items: center; + width: 96px; +} +.viewer-viewport-trigger { + width: 100%; + min-height: 26px; + justify-content: space-between; + gap: 8px; + padding: 4px 8px 4px 10px; + border-color: var(--border); + background-color: var(--bg-panel); + color: var(--text); + font-size: 12.5px; + line-height: 1.2; + box-shadow: var(--shadow-xs); +} +.viewer-viewport-trigger:hover:not(:disabled), +.viewer-viewport-trigger[aria-expanded='true'] { + border-color: var(--border-strong); + background-color: var(--bg-subtle); +} +.viewer-viewport-menu { + position: absolute; + top: calc(100% + 6px); + right: 0; + z-index: 80; + width: 100%; + padding: 5px; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--bg-panel); + box-shadow: var(--shadow-md); + overflow: hidden; +} +.viewer-viewport-menu-item { + width: 100%; + min-height: 28px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 5px 8px; + border: 1px solid transparent; + border-radius: var(--radius-sm); + background: transparent; + color: var(--text-muted); + font-size: 12.5px; + text-align: left; +} +.viewer-viewport-menu-item:hover { + background: var(--bg-subtle); + color: var(--text); +} +.viewer-viewport-menu-item.active { + background: var(--accent-tint); + color: var(--accent-strong); +} +.live-artifact-preview-layer, +.comment-preview-layer { + position: relative; + width: 100%; + height: 100%; + min-height: 0; +} +.live-artifact-preview-layer.preview-viewport[data-active='false'] { + display: none; +} +.preview-viewport:not(.preview-viewport-desktop) { + overflow: auto; + display: flex; + align-items: flex-start; + justify-content: center; + min-width: 0; + padding: 24px; + background: + linear-gradient(45deg, color-mix(in srgb, var(--border) 28%, transparent) 25%, transparent 25%), + linear-gradient(-45deg, color-mix(in srgb, var(--border) 28%, transparent) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, color-mix(in srgb, var(--border) 28%, transparent) 75%), + linear-gradient(-45deg, transparent 75%, color-mix(in srgb, var(--border) 28%, transparent) 75%), + var(--bg-subtle); + background-size: 16px 16px; + background-position: 0 0, 0 8px, 8px -8px, -8px 0; +} +.preview-viewport:not(.preview-viewport-desktop) .preview-frame-clip, +.preview-viewport:not(.preview-viewport-desktop) .comment-frame-clip, +.preview-viewport:not(.preview-viewport-desktop).manual-edit-workspace .manual-edit-canvas { + width: calc(var(--preview-viewport-width) * var(--preview-scale, 1)); + height: calc(var(--preview-viewport-height) * var(--preview-scale, 1)); + max-width: none; + max-height: none; + flex: 0 0 auto; + border: 1px solid var(--border-strong); + border-radius: 18px; + overflow: hidden; + box-shadow: 0 24px 80px rgba(0, 0, 0, 0.22); + background: var(--bg-panel); +} +.preview-viewport:not(.preview-viewport-desktop) .preview-frame-clip > div, +.preview-viewport:not(.preview-viewport-desktop) .comment-frame-clip > div, +.preview-viewport:not(.preview-viewport-desktop).manual-edit-workspace .manual-edit-canvas > div { + will-change: transform; +} +.preview-viewport:not(.preview-viewport-desktop) .preview-frame-clip, +.preview-viewport:not(.preview-viewport-desktop) .comment-frame-clip { + position: relative; + inset: auto; +} +.preview-viewport-mobile .preview-frame-clip, +.preview-viewport-mobile .comment-frame-clip, +.preview-viewport-mobile.manual-edit-workspace .manual-edit-canvas { + border-radius: 28px; +} +.preview-frame-clip, +.comment-frame-clip { + position: absolute; + inset: 0; + overflow: hidden; +} +.comment-overlay-layer { + position: absolute; + inset: 0; + z-index: 3; + pointer-events: none; +} +.board-pod-stroke { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + overflow: visible; + pointer-events: none; +} +.board-pod-stroke polyline { + fill: none; + stroke: rgba(22, 119, 255, 0.95); + stroke-width: 3; + stroke-linecap: round; + stroke-linejoin: round; + filter: drop-shadow(0 0 4px rgba(22, 119, 255, 0.28)); +} +.comment-target-overlay { + position: absolute; + pointer-events: none; + border: 1px solid var(--comment-overlay-border, #1677ff); + background: var(--comment-overlay-bg, rgba(22, 119, 255, 0.24)); + box-shadow: 0 0 0 1px var(--comment-overlay-ring, rgba(22, 119, 255, 0.18)); +} +.comment-target-overlay.selected { + border-width: 2px; +} +/* Per-member overlay highlight triggered by chip hover / remove-button focus. + Only fires from the member rendering branch in CommentTargetOverlay; + single-element snapshots use the non-member fallback and never get hovered. */ +.comment-target-overlay--member.is-hover-focused { + outline: 2px solid rgb(22, 119, 255); + outline-offset: 2px; + z-index: 2; /* float above sibling overlays */ +} +.comment-target-overlay-label { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} +.comment-saved-marker { + position: absolute; + pointer-events: none; +} +.comment-saved-outline { + position: absolute; + inset: 0; + border: 1px dashed rgba(22, 119, 255, 0.72); + background: rgba(22, 119, 255, 0.08); +} +.comment-saved-pin { + position: absolute; + z-index: 2; + left: -8px; + top: -8px; + min-width: 18px; + height: 18px; + padding: 0 5px; + border: 1px solid #0958d9; + border-radius: var(--radius-pill); + background: #1677ff; + color: white; + font-size: 10px; + font-weight: 700; + line-height: 16px; + box-shadow: var(--shadow-sm); + pointer-events: auto; +} +.comment-saved-pin:hover { + background: #0958d9; + transform: translateY(-1px); +} +.comment-popover { + position: absolute; + left: 14px; + top: 14px; + z-index: 40; + width: min(320px, calc(100% - 28px)); + padding: 10px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg-panel); + box-shadow: var(--shadow-lg); +} +.comment-popover-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + margin-bottom: 8px; +} +.comment-popover-head div { + display: grid; + gap: 1px; + min-width: 0; + overflow: hidden; +} +.comment-popover-head strong { + font-size: 13px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.comment-popover-head span { + color: var(--text-muted); + font-size: 12px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.comment-popover-head button.ghost { + flex: 0 0 auto; + white-space: nowrap; +} +.board-pod-summary { + display: grid; + gap: 6px; + margin-bottom: 8px; + padding: 8px; + border: 1px solid var(--border-soft); + border-radius: var(--radius-sm); + background: var(--bg-subtle); +} +.board-pod-summary strong { + font-size: 12px; +} +.board-pod-members { + display: flex; + flex-wrap: wrap; + gap: 6px; + max-height: 132px; + overflow-y: auto; +} +.board-pod-chip { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 7px; + border: 1px solid var(--border-soft); + border-radius: var(--radius-pill); + background: var(--bg-panel); + color: var(--text-muted); + font-size: 11px; +} +.board-pod-chip-remove { + display: inline-flex; + align-items: center; + justify-content: center; + width: 14px; + height: 14px; + padding: 0; + border: 0; + border-radius: 50%; + background: transparent; + color: var(--text-muted); + cursor: pointer; + opacity: 0; + transition: opacity 200ms cubic-bezier(0.23, 1, 0.32, 1), color 200ms cubic-bezier(0.23, 1, 0.32, 1); +} +.board-pod-chip:hover .board-pod-chip-remove, +.board-pod-chip-remove:focus-visible { + opacity: 1; +} +.board-pod-chip-remove:hover, +.board-pod-chip-remove:focus-visible { + color: var(--text-default, var(--text-strong)); +} +.board-note-list { + display: grid; + gap: 6px; + margin-bottom: 8px; +} +.board-note-item { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + padding: 8px 9px; + border: 1px solid var(--border-soft); + border-radius: var(--radius-sm); + background: var(--bg-subtle); + font-size: 12px; +} +.board-note-item span { + /* The note span sits next to a Remove button inside a fixed-width + ~300px popover. Without flex: 1 + min-width: 0 + overflow-wrap an + unbroken long string (URL, hash, base64) refuses to break and + pushes the popover layout sideways past its 320px boundary. + Issue #782. */ + flex: 1; + min-width: 0; + color: var(--text); + overflow-wrap: anywhere; +} +.comment-popover textarea { + min-height: 78px; + max-height: 120px; + resize: vertical; +} +.comment-popover-actions { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 10px; + margin-top: 8px; +} +.comment-popover-actions > * { + /* Buttons may carry labels longer than their natural fair-share of the + 320px popover width (Save comment, Send to chat, Sending..., Add note, + Remove). Without an explicit shrink ceiling and wrap, the row pushes + past the popover's right edge instead of breaking onto a new line. + Issue #779. */ + max-width: 100%; +} +.comment-popover-actions-end { + display: inline-flex; + align-items: center; + justify-content: flex-end; + flex-wrap: wrap; + gap: 10px; + min-width: 0; +} +.comment-popover-remove { + color: var(--red); + background: transparent; + border-color: transparent; +} +.comment-popover-remove:hover:not(:disabled) { + background: var(--red-bg); + border-color: var(--red-border); +} + +/* Right-side comment thread panel. Shown while board (comment) mode + is on; takes the place of the chat sidebar's removed Comments tab. + Floats over the artifact preview at the right edge. */ +.comment-side-panel { + position: absolute; + top: 8px; + right: 8px; + bottom: 8px; + width: 320px; + max-width: calc(100% - 16px); + background: var(--bg-panel, #fff); + border: 1px solid var(--border); + border-radius: 12px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); + display: flex; + flex-direction: column; + z-index: 30; + overflow: hidden; +} +.comment-side-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + padding: 10px 12px; + border-bottom: 1px solid var(--border); + background: var(--bg-panel, #fff); +} +.comment-side-title { + display: inline-flex; + align-items: center; + min-width: 0; + gap: 7px; + color: var(--text); + font-size: 12px; + font-weight: 700; +} +.comment-side-title span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.comment-side-close { + width: 26px; + height: 26px; + flex: 0 0 auto; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + border: 1px solid transparent; + border-radius: 6px; + background: transparent; + color: var(--text-muted); + cursor: pointer; +} +.comment-side-close:hover { + background: var(--bg-subtle); + border-color: var(--border); + color: var(--text); +} +.comment-side-rail { + position: absolute; + top: 8px; + right: 8px; + bottom: 8px; + z-index: 30; + width: 42px; + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + padding: 12px 0; + border: 1px solid var(--border); + border-radius: 12px; + background: var(--bg-panel, #fff); + color: var(--text-muted); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); + cursor: pointer; +} +.comment-side-rail:hover { + color: var(--text); + border-color: var(--border-strong); + background: var(--bg-subtle); +} +.comment-side-rail span { + writing-mode: vertical-rl; + text-orientation: mixed; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; +} +.comment-side-rail strong { + min-width: 18px; + height: 18px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 4px; + border-radius: 999px; + background: #ff5a3c; + color: #fff; + font-size: 10px; + font-weight: 700; + line-height: 1; +} +.comment-side-list { + flex: 1; + overflow-y: auto; + padding: 12px; + display: flex; + flex-direction: column; + gap: 8px; +} +.comment-side-empty { + padding: 24px 8px; + text-align: center; + color: var(--text-muted); + font-size: 12.5px; +} +.comment-side-item { + border: 1px solid transparent; + border-radius: 8px; + padding: 10px 12px; + background: transparent; + display: flex; + flex-direction: column; + gap: 4px; +} +.comment-side-item.selected { + background: #fff1ec; + border-color: #ff8c75; +} +.comment-side-item-head { + display: flex; + align-items: center; + gap: 8px; +} +.comment-side-author { + display: inline-flex; + align-items: center; + gap: 6px; + flex: 1; + min-width: 0; +} +.comment-side-author strong { + font-size: 13px; + color: var(--text); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.comment-side-avatar { + width: 20px; + height: 20px; + border-radius: 50%; + background: #0a0a0a; + color: #fff; + font-size: 11px; + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 600; + flex-shrink: 0; +} +.comment-side-time { + font-size: 11px; + color: var(--text-muted); + white-space: nowrap; +} +.comment-side-check { + width: 16px; + height: 16px; + border-radius: 4px; + border: 1.5px solid var(--border); + background: #fff; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + color: #fff; + flex-shrink: 0; +} +.comment-side-check.checked { + background: #ff5a3c; + border-color: #ff5a3c; +} +.comment-side-body { + font-size: 13px; + color: var(--text); + line-height: 1.45; + word-break: break-word; + white-space: pre-wrap; +} +.comment-side-reply { + align-self: flex-start; + background: transparent; + border: none; + padding: 0; + font-size: 12px; + color: var(--text-muted); + cursor: pointer; +} +.comment-side-reply:hover { + color: var(--text); + text-decoration: underline; +} +.comment-side-selectbar { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-top: 1px solid var(--border); + background: #fff1ec; +} +.comment-side-selectcount { + flex: 1; + font-size: 12px; + color: #cc3a20; +} +.comment-side-selectbar .ghost { + background: transparent; + border: none; + font-size: 12px; + color: var(--text); + cursor: pointer; + padding: 4px 6px; +} +.comment-side-selectbar .ghost:hover { + color: var(--text); + text-decoration: underline; +} +.comment-side-selectbar .primary { + display: inline-flex; + align-items: center; + gap: 4px; + background: #ff5a3c; + color: #fff; + border: 1px solid #ff5a3c; + border-radius: 6px; + padding: 5px 10px; + font-size: 12px; + cursor: pointer; +} +.comment-side-selectbar .primary:disabled { + background: #ffb9aa; + border-color: #ffb9aa; + cursor: not-allowed; +} +.comment-side-selectbar .primary:hover:not(:disabled) { + background: #e94a2d; + border-color: #e94a2d; +} + +/* Anchor for the transient comment-save toast. Centered at the top + of the artifact preview so the confirmation is visible without + covering the popover's former position. */ +.comment-toast-anchor { + position: absolute; + top: 16px; + left: 50%; + transform: translateX(-50%); + z-index: 60; + pointer-events: auto; +} + +/* Inspect panel — sibling of the comment popover. Anchored to the + right side of the preview surface. Width is fixed so layout doesn't + reflow as the user scrubs slider values; controls reserve space for + their numeric readouts. */ +.inspect-panel { + position: absolute; + top: 14px; + right: 14px; + z-index: 5; + width: 296px; + max-height: calc(100% - 28px); + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 14px; + padding: 12px 14px 14px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg-panel); + box-shadow: var(--shadow-lg); + font-size: 12px; +} +.inspect-panel-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; +} +.inspect-panel-head > button { + /* Without an explicit shrink-floor, a long selected-component label + in the title can squeeze the Close button so narrowly that the + "x" glyph stacks into a vertical layout on some font/zoom + combinations. Pinning flex-shrink: 0 keeps the close affordance + on a stable single-line size regardless of label length. Issue + #785. */ + flex-shrink: 0; +} +.inspect-panel-title { + display: grid; + gap: 2px; + min-width: 0; +} +.inspect-panel-title strong { + /* Long target.label values (deeply-nested component selectors) used + to spill past the title container and out of the panel because + this rule had no overflow constraint. Truncate with ellipsis; + the carries title={label} so the full string is still + visible on hover. Issue #780. */ + font-size: 13px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.inspect-panel-title code { + color: var(--text-muted); + font-size: 11px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.inspect-ancestor-notice { + display: flex; + align-items: flex-start; + gap: 8px; + padding: 10px; + color: var(--text); + border: 1px solid color-mix(in srgb, var(--accent) 28%, var(--border)); + border-radius: var(--radius-sm); + background: color-mix(in srgb, var(--accent) 8%, var(--bg-panel)); + font-size: 11px; + line-height: 1.4; +} +.inspect-ancestor-notice-icon { + display: grid; + place-items: center; + flex: 0 0 16px; + width: 16px; + height: 16px; + color: var(--accent); + border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); + border-radius: 999px; + font-size: 10px; + font-weight: 700; + line-height: 1; +} +.inspect-ancestor-notice-text { + min-width: 0; +} +.inspect-ancestor-notice-text strong { + font-weight: 600; +} +.inspect-ancestor-notice-text code { + padding: 1px 3px; + border-radius: 3px; + background: color-mix(in srgb, var(--text) 8%, transparent); + font-family: var(--font-mono); + font-size: 10px; +} + +/* MCP Copy button floats above a syntax-highlighted
; .ghost's
+   transparent background let the dark code surface bleed through and
+   made the affordance effectively invisible. Pin a solid panel
+   background here (not inline) so button.ghost:hover:not(:disabled)
+   still wins the cascade. Issue #742. */
+button.ghost.mcp-copy-btn {
+  background: var(--bg-panel);
+  border: 1px solid var(--border);
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
+}
+button.ghost.mcp-copy-btn:hover:not(:disabled) {
+  background: var(--bg-subtle);
+  border-color: var(--border-strong);
+}
+.inspect-section {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+.inspect-section-label {
+  color: var(--text-muted);
+  font-size: 10px;
+  font-weight: 700;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+}
+.inspect-row {
+  display: grid;
+  grid-template-columns: 64px 1fr auto;
+  align-items: center;
+  gap: 8px;
+}
+.inspect-row > label {
+  color: var(--text-muted);
+  font-size: 11px;
+}
+.inspect-row input[type='color'] {
+  width: 28px;
+  height: 22px;
+  padding: 0;
+  border: 1px solid var(--border);
+  border-radius: 4px;
+  background: transparent;
+}
+.inspect-row input[type='text'],
+.inspect-row select {
+  min-width: 0;
+  padding: 3px 6px;
+  border: 1px solid var(--border);
+  border-radius: 4px;
+  background: var(--bg);
+  font-size: 11px;
+  font-family: inherit;
+}
+.inspect-row input[type='range'] {
+  width: 100%;
+}
+.inspect-row-value {
+  font-variant-numeric: tabular-nums;
+  color: var(--text-muted);
+  font-size: 11px;
+  min-width: 42px;
+  text-align: right;
+}
+.inspect-panel-footer {
+  display: flex;
+  justify-content: space-between;
+  gap: 8px;
+}
+.inspect-panel-error {
+  margin: 0;
+  padding: 6px 8px;
+  border: 1px solid var(--red-border);
+  border-radius: 4px;
+  background: var(--red-bg);
+  color: var(--red);
+  font-size: 11px;
+}
+
+.inspect-empty-hint-container {
+  position: absolute;
+  top: 14px;
+  right: 14px;
+  z-index: 5;
+  padding: 8px 12px;
+  border: 1px dashed var(--border);
+  border-radius: var(--radius);
+  background: var(--bg-panel);
+  color: var(--text-muted);
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 5px;
+  pointer-events: none;
+}
+
+.inspect-empty-hint-container.comment-side-panel-open {
+  right: 340px;
+  max-width: min(480px, calc(100% - 368px));
+}
+
+@media (max-width: 760px) {
+  .inspect-empty-hint-container.comment-side-panel-open {
+    left: 14px;
+    right: 14px;
+    top: 54px;
+    max-width: none;
+  }
+}
+
+.inspect-empty-hint-container button,
+.inspect-empty-hint-container .close-button {
+  pointer-events: auto;
+}
+
+.inspect-empty-hint code {
+  padding: 1px 5px;
+  border-radius: 3px;
+  background: var(--bg);
+  font-size: 11px;
+}
+
+.comments-panel {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  padding: 14px;
+  overflow: auto;
+}
+.comments-section {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+.comments-section h3 {
+  margin: 0;
+  color: var(--text-muted);
+  font-size: 11px;
+  font-weight: 700;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+}
+.comments-empty {
+  margin: 0;
+  padding: 10px 12px;
+  border: 1px dashed var(--border);
+  border-radius: var(--radius);
+  color: var(--text-faint);
+  font-size: 12px;
+}
+.comment-card {
+  display: grid;
+  gap: 5px;
+  padding: 9px 10px;
+  border: 1px solid var(--border);
+  border-radius: var(--radius);
+  background: var(--bg-panel);
+  box-shadow: var(--shadow-xs);
+}
+.comment-card.attached {
+  border-color: var(--accent-soft);
+  background: var(--accent-tint);
+}
+.comment-card-top {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 10px;
+  min-width: 0;
+}
+.comment-card-top strong {
+  min-width: 0;
+  overflow: hidden;
+  color: var(--accent-strong);
+  font-size: 13px;
+  font-weight: 700;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.comment-card-actions {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  flex: 0 0 auto;
+}
+.comment-card p {
+  margin: 0;
+  color: var(--text);
+  font-size: 12.5px;
+  line-height: 1.35;
+  overflow-wrap: anywhere;
+}
+.comment-card-action {
+  flex: 0 0 auto;
+  padding: 3px 8px;
+  border: 1px solid var(--border);
+  border-radius: var(--radius-pill);
+  background: var(--bg);
+  color: var(--text-muted);
+  font-size: 11.5px;
+}
+.comment-card-action:hover {
+  border-color: var(--accent-soft);
+  color: var(--accent-strong);
+}
+.comment-card-action.danger {
+  color: var(--red);
+}
+.comment-card-action.danger:hover {
+  border-color: var(--red-border);
+  background: var(--red-bg);
+}
+.comment-card-meta {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 4px;
+  color: var(--text-faint);
+  font-size: 11px;
+}
+.comment-card-meta span {
+  max-width: 140px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.comment-card-meta span:not(:last-child)::after {
+  content: '·';
+  margin-left: 4px;
+  color: var(--text-faint);
+}
+.comments-footer {
+  display: flex;
+  justify-content: flex-start;
+  padding-top: 2px;
+}
+.comments-footer .primary {
+  padding: 6px 12px;
+  font-size: 12px;
+}
+.comment-history-attachments {
+  gap: 6px;
+}
+.viewer-source {
+  margin: 0;
+  padding: 16px;
+  font-family: var(--mono);
+  font-size: 12px;
+  line-height: 1.55;
+  white-space: pre-wrap;
+  word-break: break-word;
+  color: var(--text);
+  background: var(--bg-panel);
+  min-height: 100%;
+}
diff --git a/apps/web/src/styles/viewer/library.css b/apps/web/src/styles/viewer/library.css
new file mode 100644
index 000000000..7578e4a95
--- /dev/null
+++ b/apps/web/src/styles/viewer/library.css
@@ -0,0 +1,2223 @@
+/* --- Codex hatch-pet atlas import + AI generation panels ------------ */
+
+.pet-atlas-preview {
+  margin-top: 14px;
+  padding: 14px;
+  border: 1px solid var(--border);
+  border-radius: 12px;
+  background: var(--surface);
+  display: grid;
+  gap: 12px;
+}
+
+.pet-atlas-head {
+  display: flex;
+  align-items: flex-start;
+  justify-content: space-between;
+  gap: 12px;
+}
+
+.pet-atlas-head strong {
+  font-size: 13px;
+  font-weight: 600;
+  color: var(--text);
+}
+
+.pet-atlas-thumb {
+  width: 100%;
+  aspect-ratio: 1536 / 1872;
+  max-height: 240px;
+  background-repeat: no-repeat;
+  background-position: center;
+  background-size: contain;
+  background-color: var(--surface-2, #f6f4ee);
+  border-radius: 8px;
+  border: 1px solid var(--border);
+  image-rendering: pixelated;
+}
+
+.pet-atlas-rows {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
+  gap: 6px;
+}
+
+.pet-atlas-row {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  gap: 2px;
+  padding: 8px 10px;
+  border: 1px solid var(--border);
+  border-radius: 8px;
+  background: var(--bg);
+  cursor: pointer;
+  font: inherit;
+  text-align: left;
+  transition: border-color 120ms ease, background 120ms ease;
+}
+
+.pet-atlas-row:hover {
+  border-color: var(--accent);
+}
+
+.pet-atlas-row.active {
+  border-color: var(--accent);
+  background: color-mix(in srgb, var(--accent) 10%, transparent);
+}
+
+.pet-atlas-row[disabled] {
+  opacity: 0.55;
+  cursor: progress;
+}
+
+.pet-atlas-row-name {
+  font-size: 12px;
+  font-weight: 600;
+  color: var(--text);
+}
+
+.pet-atlas-row-meta {
+  font-size: 11px;
+  color: var(--text-muted);
+}
+
+.pet-atlas-actions {
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+}
+
+/* "Hatch with AI" panel */
+
+.pet-hatch {
+  margin-top: 14px;
+  padding: 14px;
+  border: 1px dashed var(--border);
+  border-radius: 12px;
+  background: color-mix(in srgb, var(--accent) 4%, transparent);
+  display: grid;
+  gap: 10px;
+}
+
+.pet-hatch-head h4 {
+  margin: 0 0 4px 0;
+  font-size: 13px;
+  font-weight: 600;
+}
+
+.pet-hatch-prompt {
+  margin: 0;
+  padding: 10px 12px;
+  background: var(--bg);
+  border: 1px solid var(--border);
+  border-radius: 8px;
+  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
+  font-size: 11px;
+  line-height: 1.5;
+  color: var(--text);
+  max-height: 200px;
+  overflow: auto;
+  white-space: pre-wrap;
+  word-break: break-word;
+}
+
+.pet-hatch-actions {
+  display: flex;
+  gap: 8px;
+}
+
+.pet-hatch-foot {
+  margin: 0;
+}
+
+/* "Recently hatched" — Codex pets discovered under ~/.codex/pets/ */
+
+.pet-codex {
+  margin-top: 14px;
+  padding: 14px;
+  border: 1px solid var(--border);
+  border-radius: 12px;
+  background: var(--surface);
+  display: grid;
+  gap: 10px;
+}
+
+.pet-codex-head {
+  display: flex;
+  align-items: flex-start;
+  justify-content: space-between;
+  gap: 12px;
+}
+
+.pet-codex-head h4 {
+  margin: 0 0 4px 0;
+  font-size: 13px;
+  font-weight: 600;
+}
+
+/* Right-aligned button cluster in the codex head — keeps the
+   "Download community pets" + "Refresh" buttons on a single row even
+   when the section title wraps. */
+.pet-codex-head-actions {
+  display: flex;
+  gap: 6px;
+  flex-wrap: wrap;
+  justify-content: flex-end;
+}
+
+.pet-codex-empty {
+  margin: 4px 0 0 0;
+}
+
+/* Inline status line shown after a community sync completes. Stays in
+   the section flow so the result reads as part of the same control
+   group as the trigger button. The error variant tints toward the
+   accent without screaming red — sync errors are usually transient
+   network blips. */
+.pet-codex-sync-status {
+  margin: 6px 0 8px 0;
+  padding: 6px 10px;
+  border-radius: 6px;
+  background: var(--bg-subtle);
+  border: 1px solid var(--border);
+  font-size: 12px;
+  color: var(--text-muted);
+}
+
+.pet-codex-sync-status.error {
+  background: color-mix(in srgb, var(--accent) 8%, var(--bg-subtle));
+  border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
+  color: var(--text-strong);
+}
+
+.pet-codex-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
+  gap: 10px;
+}
+
+.pet-codex-card {
+  position: relative;
+  display: grid;
+  grid-template-columns: 56px 1fr;
+  align-items: center;
+  gap: 10px;
+  padding: 10px;
+  border: 1px solid var(--border);
+  border-radius: 10px;
+  background: var(--bg);
+}
+
+.pet-codex-thumb {
+  position: relative;
+  width: 56px;
+  height: 56px;
+  border-radius: 8px;
+  border: 1px solid var(--border);
+  background-color: var(--surface-2, #f6f4ee);
+  background-image: var(--pet-codex-src);
+  background-repeat: no-repeat;
+  /* Show only the top-left cell of the 8x9 atlas — that is the
+     `idle` row, frame 0, and reads as a recognizable still even
+     before adoption. */
+  background-position: 0 0;
+  background-size: 800% 900%;
+  image-rendering: pixelated;
+}
+
+/* Hover preview — a larger floating overlay that plays through every
+   cell of the 8x9 Codex atlas so users can see what animations (idle,
+   running, waving, jumping, failed, waiting, …) the pet has before they
+   adopt it. The overlay is purely decorative: pointer-events: none
+   prevents it from stealing hover from the card, and aria-hidden keeps
+   it out of the accessibility tree. */
+.pet-codex-thumb-preview {
+  position: absolute;
+  bottom: calc(100% + 8px);
+  left: 50%;
+  width: 128px;
+  /* 128 * (208/192) ≈ 138.7 — matches a single atlas cell aspect. */
+  height: 139px;
+  border-radius: 10px;
+  border: 1px solid var(--border);
+  background-color: var(--surface-2, #f6f4ee);
+  background-image: var(--pet-codex-src);
+  background-repeat: no-repeat;
+  background-position: 0% 0%;
+  background-size: 800% 900%;
+  image-rendering: pixelated;
+  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.22);
+  opacity: 0;
+  pointer-events: none;
+  transform: translate(-50%, 6px) scale(0.82);
+  transform-origin: 50% 100%;
+  transition:
+    opacity 140ms ease,
+    transform 160ms ease;
+  z-index: 20;
+}
+
+.pet-codex-card:hover .pet-codex-thumb-preview,
+.pet-codex-card:focus-within .pet-codex-thumb-preview {
+  opacity: 1;
+  transform: translate(-50%, 0) scale(1);
+  /* 8 column steps (1 row) in 0.6s → ~13 fps per animation, then the row
+     index advances every 0.6s for a full 5.4s sweep through all 9 rows.
+     Animating background-position-x + -y as two independent animations
+     keeps the nested loop in pure CSS. */
+  animation:
+    pet-codex-preview-col 0.6s steps(8, jump-none) infinite,
+    pet-codex-preview-row 5.4s steps(9, jump-none) infinite;
+}
+
+@keyframes pet-codex-preview-col {
+  from { background-position-x: 0%; }
+  to { background-position-x: 100%; }
+}
+
+@keyframes pet-codex-preview-row {
+  from { background-position-y: 0%; }
+  to { background-position-y: 100%; }
+}
+
+@media (prefers-reduced-motion: reduce) {
+  .pet-codex-card:hover .pet-codex-thumb-preview,
+  .pet-codex-card:focus-within .pet-codex-thumb-preview {
+    animation: none;
+    background-position: 0% 0%;
+  }
+}
+
+.pet-codex-meta {
+  display: grid;
+  gap: 2px;
+  min-width: 0;
+  padding-right: 8px;
+}
+
+.pet-codex-title-row {
+  min-width: 0;
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+}
+
+.pet-codex-meta strong {
+  font-size: 12px;
+  font-weight: 600;
+  color: var(--text);
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.pet-codex-default-badge {
+  flex: none;
+  padding: 1px 6px;
+  border-radius: var(--radius-pill);
+  border: 1px solid color-mix(in srgb, var(--accent) 26%, var(--border));
+  background: color-mix(in srgb, var(--accent) 8%, transparent);
+  color: var(--accent-strong);
+  font-size: 9.5px;
+  font-weight: 600;
+  line-height: 1.4;
+}
+
+.pet-codex-description {
+  font-size: 11px;
+  color: var(--text-muted);
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+}
+
+/* Adopt button: absolutely positioned at card's right edge so the
+   meta text always fills the full remaining width. Hidden until hover;
+   adopted (active) icon-only check stays visible at all times. */
+.pet-codex-adopt-btn {
+  position: absolute;
+  right: 10px;
+  top: 50%;
+  transform: translateY(-50%);
+}
+.pet-codex-adopt-btn:not(.active) {
+  opacity: 0;
+  pointer-events: none;
+  transition: opacity 120ms ease;
+}
+.pet-codex-card:hover .pet-codex-adopt-btn:not(.active),
+.pet-codex-card:focus-within .pet-codex-adopt-btn:not(.active) {
+  opacity: 1;
+  pointer-events: auto;
+}
+
+/* Slash-command popover in the chat composer */
+
+.slash-popover {
+  position: absolute;
+  bottom: 100%;
+  left: 0;
+  right: 0;
+  margin-bottom: 4px;
+  max-height: 260px;
+  overflow-y: auto;
+  background: var(--bg-panel);
+  border: 1px solid var(--border);
+  border-radius: var(--radius);
+  box-shadow: var(--shadow-md);
+  z-index: 10;
+  padding: 6px;
+}
+
+.slash-popover-head {
+  display: flex;
+  align-items: baseline;
+  justify-content: space-between;
+  gap: 10px;
+  padding: 4px 6px 6px 6px;
+  border-bottom: 1px solid var(--border);
+  margin-bottom: 4px;
+  font-size: 11px;
+  color: var(--text-muted);
+  text-transform: uppercase;
+  letter-spacing: 0.04em;
+}
+
+.slash-popover-hint {
+  font-size: 10px;
+  text-transform: none;
+  letter-spacing: 0;
+  color: var(--text-muted);
+}
+
+.slash-item {
+  display: flex;
+  align-items: flex-start;
+  gap: 8px;
+  width: 100%;
+  background: transparent;
+  border: 1px solid transparent;
+  border-radius: 6px;
+  padding: 7px 8px;
+  text-align: left;
+  cursor: pointer;
+  font: inherit;
+}
+
+.slash-item:hover,
+.slash-item.active {
+  background: var(--bg-subtle);
+  border-color: var(--border);
+}
+
+.slash-item-icon {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 22px;
+  height: 22px;
+  border-radius: 6px;
+  background: color-mix(in srgb, var(--accent) 12%, transparent);
+  color: var(--accent);
+  flex-shrink: 0;
+  margin-top: 1px;
+}
+
+.slash-item-body {
+  display: grid;
+  gap: 2px;
+  min-width: 0;
+  flex: 1;
+}
+
+.slash-item-row {
+  display: flex;
+  align-items: baseline;
+  gap: 8px;
+}
+
+.slash-item-label {
+  font-size: 12px;
+  font-weight: 600;
+  color: var(--text);
+  background: transparent;
+  padding: 0;
+}
+
+.slash-item-arg {
+  font-size: 10px;
+  color: var(--text-muted);
+  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
+}
+
+.slash-item-desc {
+  font-size: 11px;
+  color: var(--text-muted);
+  line-height: 1.35;
+}
+
+/* --- CJK (Chinese / Japanese / Korean) Comfort Pass --- */
+:lang(zh), :lang(zh-CN), :lang(zh-TW), :lang(ja), :lang(ko) {
+  --sans: "Inter", "PingFang SC", "Microsoft YaHei", "Noto Sans SC", "Hiragino Sans GB", "Source Han Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+}
+
+/* --- Arabic & Persian Comfort Pass --- */
+[dir="rtl"] {
+  --sans: "Cairo", "Inter", "Vazirmatn", "Noto Sans Arabic", "Segoe UI Arabic", "Tahoma", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+}
+
+/* Scope line-height increase to prose and chat to avoid breaking compact UI chrome */
+[dir="rtl"] .prose,
+[dir="rtl"] .chat-message,
+[dir="rtl"] .msg .user-text {
+  line-height: 1.7;
+}
+
+[dir="rtl"] .avatar-item,
+[dir="rtl"] .settings-tab,
+[dir="rtl"] .ds-item-name {
+  text-align: right;
+}
+
+[dir="rtl"] .app-chrome-back svg,
+[dir="rtl"] .back-btn svg,
+[dir="rtl"] .icon-btn svg[data-lucide="chevron-left"],
+[dir="rtl"] .icon-btn svg[data-lucide="chevron-right"],
+[dir="rtl"] .icon-btn svg[data-lucide="arrow-left"],
+[dir="rtl"] .icon-btn svg[data-lucide="arrow-right"],
+[dir="rtl"] .newproj-tabs-arrow svg,
+[dir="rtl"] .icon-only svg[data-lucide="chevron-left"],
+[dir="rtl"] .icon-only svg[data-lucide="chevron-right"],
+[dir="rtl"] .icon-only svg[data-lucide="arrow-left"],
+[dir="rtl"] .icon-only svg[data-lucide="arrow-right"],
+[dir="rtl"] .welcome-pet-teaser svg {
+  transform: scaleX(-1);
+}
+
+[dir="rtl"] .title,
+[dir="rtl"] .meta {
+  text-align: right;
+}
+
+/* ============================================================
+   Quick Switcher (Cmd/Ctrl+P file palette)
+   ============================================================ */
+.qs-overlay {
+  position: fixed;
+  inset: 0;
+  background: rgba(13, 12, 10, 0.32);
+  /* Sits in the modal tier (alongside prompt-template-modal-overlay) so
+   * the palette renders above any open context menus or popovers. */
+  z-index: 1500;
+  display: flex;
+  align-items: flex-start;
+  justify-content: center;
+  padding-top: 12vh;
+}
+.qs-palette {
+  width: min(560px, 92vw);
+  background: var(--bg-panel);
+  border: 1px solid var(--border);
+  border-radius: var(--radius-lg);
+  box-shadow: var(--shadow-lg);
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  color: var(--text);
+}
+.qs-input {
+  appearance: none;
+  border: 0;
+  outline: none;
+  background: transparent;
+  padding: 14px 16px;
+  font-size: 16px;
+  font-weight: 500;
+  color: var(--text);
+  border-bottom: 1px solid var(--border-soft);
+}
+.qs-input::placeholder {
+  color: var(--text-faint);
+}
+.qs-list {
+  max-height: 50vh;
+  overflow-y: auto;
+  padding: 4px 0;
+}
+.qs-empty {
+  padding: 20px 16px;
+  color: var(--text-muted);
+  text-align: center;
+  font-size: 13px;
+}
+.qs-row {
+  display: grid;
+  grid-template-columns: auto 1fr auto;
+  gap: 10px;
+  align-items: baseline;
+  padding: 7px 16px;
+  cursor: pointer;
+  border-left: 3px solid transparent;
+}
+.qs-row-active {
+  background: var(--accent-soft);
+  border-left-color: var(--accent);
+}
+.qs-name {
+  font-weight: 600;
+  font-size: 14px;
+  color: var(--text);
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.qs-path {
+  color: var(--text-muted);
+  font-size: 12.5px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  min-width: 0;
+}
+.qs-kind {
+  font-size: 10.5px;
+  letter-spacing: 0.06em;
+  text-transform: uppercase;
+  color: var(--text-faint);
+  font-weight: 600;
+}
+.qs-row-active .qs-kind {
+  color: var(--accent-strong);
+}
+.qs-footer {
+  display: flex;
+  gap: 14px;
+  padding: 8px 16px;
+  border-top: 1px solid var(--border-soft);
+  font-size: 11.5px;
+  color: var(--text-muted);
+}
+.qs-footer kbd {
+  background: var(--bg-subtle);
+  border: 1px solid var(--border);
+  border-radius: 3px;
+  padding: 0 4px;
+  margin-right: 2px;
+  font-family: ui-monospace, monospace;
+  font-size: 10px;
+  color: var(--text-muted);
+}
+
+/* Library section (Skills & Design Systems management) */
+
+.library-toolbar {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  margin-bottom: 16px;
+}
+/* Skills toolbar: two-row layout.
+   Row 1: search (grows) + New skill button (right, 12px gap).
+   Row 2: filter dropdowns with inline labels. */
+.library-toolbar.skills-toolbar {
+  flex-direction: column;
+  gap: 10px;
+}
+.skills-toolbar-top {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+.skills-toolbar-top .library-search {
+  flex: 1 1 0;
+  min-width: 0;
+}
+
+/* Variant: filter pills on the left, primary action on the right.
+   Used by Memory's toolbar where there is no search input above. */
+.library-toolbar.is-row {
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+
+.library-toolbar.is-row .library-filters {
+  flex: 1;
+  min-width: 0;
+}
+
+/* Primary action button positioned at the right edge of `.library-toolbar`.
+   The default button line-height makes the icon and label vertically off
+   when sitting next to filter pills; tightening the line-height aligns the
+   plus icon with the text baseline. */
+.library-toolbar-action {
+  flex-shrink: 0;
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  line-height: 1;
+}
+
+.library-search {
+  width: 100%;
+  padding: 9px 12px;
+  border: 1px solid var(--border-strong);
+  border-radius: 10px;
+  font-size: 13px;
+  background: var(--bg-panel);
+  color: var(--text);
+  outline: none;
+  box-shadow: var(--shadow-xs);
+  cursor: text;
+  transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;
+}
+
+.library-search:hover {
+  background: var(--bg-elevated);
+  border-color: var(--border-strong);
+}
+
+.library-search:focus {
+  border-color: var(--selected);
+  box-shadow: 0 0 0 3px var(--selected-soft), var(--shadow-xs);
+}
+
+.library-filters {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+}
+
+/*
+ * Compact dropdown row for Skills "Type" + "Category" filters.
+ * Pills don't scale once the option set crosses ~10 entries (Skills'
+ * Category list has 14 today and is growing); they wrap to 2-3 rows
+ * and push the actual cards far below the fold. A labelled