/* * Design tokens. Every surface uses the same horizontal inset (--x), the * spacing scale (--s1…--s5), the type scale (--fs-*), thin separators and no * shadows. Colour is reserved for the logo, errors, diffs and code. * * Interaction: secondary controls rest at --muted and brighten to --text on * hover; primary controls (send, stop) rest at --text and dim on hover. * * Theme: src/web/theme.ts (and an inline script in the page, src/hosts/web.ts) * always sets from the sidebar choice or the * system preference. Colours below key off that attribute, never a media query. */ :root { color-scheme: light; --bg: #fbfaf8; --text: #1d1b19; --muted: #77716a; --faint: #a8a29b; --line: #e7e4df; --hover: #f1eee9; --user: #f0ede8; --code: #f5f3f0; --scrollbar: #b8b2aa; --ok: #2f9e6a; --unread: #2f7de1; --working: #e8820c; --bad: #c8433d; --add: #e3f6ea; --add-ink: #1f7a4b; --del: #fde7e6; --del-ink: #b03a35; --scrim: rgb(0 0 0 / 0.2); --x: 16px; --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --bar: 52px; --bar-pad: 6px; /* 40px icon target + 6px puts a 20px glyph's box on the inset */ --row: 44px; --control: 40px; --control-sm: 32px; --tool-icon: 16px; --radius: 4px; --fs-xs: 11px; --fs-sm: 13px; --fs-md: 15px; --fs-lg: 17px; --fs-code: 12px; --fw-medium: 500; --fw-strong: 600; --sans: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif; --mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", Menlo, monospace; --safe-top: env(safe-area-inset-top, 0px); --safe-bottom: env(safe-area-inset-bottom, 0px); --chat-width: 760px; --chat-size: 100%; font-family: var(--sans); font-size: 16px; line-height: 1.5; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; background: var(--bg); color: var(--text); -webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; } :root[data-theme="dark"] { color-scheme: dark; --bg: #151413; --text: #ecebe8; --unread: #5b9cf0; --working: #f59e42; --muted: #9b958d; --faint: #6c6761; --line: #2c2a27; --hover: #22201e; --user: #252321; --code: #1a1918; --scrollbar: #595550; --ok: #5cc493; --bad: #f07a72; --add: #17301f; --add-ink: #7fd8a3; --del: #3a1d1b; --del-ink: #f4a19a; --scrim: rgb(0 0 0 / 0.45); } @media (min-width: 700px) { :root { --chat-width: 1120px; --chat-size: calc(100% - 56px); } } /* Base */ * { box-sizing: border-box; } html, body, #root { height: 100%; margin: 0; overflow: hidden; overscroll-behavior: none; } button, input, textarea, select { font: inherit; color: inherit; } button { cursor: pointer; background: none; border: 0; padding: 0; text-align: inherit; } button:disabled { cursor: default; opacity: 0.45; } :focus-visible { outline: 1px solid var(--text); outline-offset: 2px; } h1, h2, h3, p, figure { margin: 0; } a { color: inherit; text-underline-offset: 3px; } .muted { color: var(--muted); } .small { font-size: var(--fs-sm); } .mono { font-family: var(--mono); font-size: var(--fs-sm); } .error { color: var(--bad); } .success { display: flex; align-items: center; gap: var(--s2); color: var(--ok); } /* Shared primitives */ /* Bars: top bar, drawer head, sheet head, viewer head, drawer foot. */ .topbar, .drawer-top, .sheet-head, .viewer-head, .drawer-foot { display: flex; align-items: center; gap: var(--s1); min-height: var(--bar); padding: 0 var(--bar-pad); background: var(--bg); } .topbar, .drawer-top, .sheet-head, .viewer-head { border-bottom: 1px solid var(--line); } /* 40px icon targets with 20px glyphs: 6px bar padding puts glyph edges on the inset. */ .icon-button, .collapse-sidebar { display: grid; place-items: center; flex: none; width: var(--control); height: var(--control); color: var(--muted); } .icon-button:hover, .collapse-sidebar:hover { color: var(--text); } /* Rows bleed to the container edge; text stays on the inset. */ .row-button, .model-row, .artifact-row, .login-action, .hit, .session-main, .subagent-row, .guest-row, .share-row { display: flex; align-items: center; gap: var(--s2); width: calc(100% + 2 * var(--x)); min-height: var(--row); margin: 0 calc(-1 * var(--x)); padding: var(--s2) var(--x); color: var(--text); font-size: var(--fs-md); text-align: left; text-decoration: none; } .row-button:hover, .model-row:hover, .artifact-row:hover, .login-action:hover, .hit:hover, .session-main:hover, .subagent-row:hover { background: var(--hover); } .model-row.selected, .session-main.active, .subagent-row.active { box-shadow: inset 2px 0 var(--text); } .row-button > span, .model-row > span, .artifact-row > span { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--s2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .row-button > svg, .login-action > svg { flex: none; color: var(--faint); } .row-button.danger, .login-action.danger { color: var(--bad); } /* Section labels. */ .label, .group h3, .model-list h3 { display: flex; align-items: center; gap: var(--s1); margin: 0; padding: var(--s4) 0 var(--s1); color: var(--muted); font-size: var(--fs-xs); font-weight: var(--fw-strong); letter-spacing: 0.04em; text-transform: uppercase; } .stack > .label, .field > .label { padding-top: 0; } /* Underlined inputs. */ .input, .search { display: flex; align-items: center; gap: var(--s2); width: 100%; height: var(--row); padding: 0; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; outline: 0; background: transparent; color: var(--text); font-size: var(--fs-md); } .search { color: var(--muted); } .input:focus, .search:focus-within { border-bottom-color: var(--text); } textarea.input { height: auto; padding: var(--s2) 0; resize: none; } .search input { flex: 1; min-width: 0; height: 100%; border: 0; outline: 0; background: transparent; color: var(--text); } .search input::-webkit-search-cancel-button { display: none; } ::placeholder { color: var(--faint); } /* Outline buttons. */ .button, .login-continue { display: inline-flex; align-items: center; justify-content: center; gap: var(--s2); min-height: var(--control); padding: 0 var(--s4); border: 1px solid var(--line); border-radius: var(--radius); background: transparent; color: var(--text); font-size: var(--fs-sm); font-weight: var(--fw-strong); text-decoration: none; white-space: nowrap; } .button:hover, .login-continue:hover { background: var(--hover); } .button.primary { border-color: var(--text); } .button.danger { color: var(--bad); } .button.block, .login-continue { width: 100%; } .login-continue { justify-content: space-between; } /* Secondary text actions: dotted underline. */ .text-action { display: inline-flex; align-items: center; min-height: var(--control-sm); color: var(--muted); font-size: var(--fs-sm); white-space: nowrap; } .text-action.danger { color: var(--bad); } /* In vertical stacks a text action hugs its label instead of stretching. */ .stack > .text-action, .messages > .text-action, .login-flow > .text-action, .provider-detail > .text-action { align-self: flex-start; } .text-action, .footer-action, .tool-link, .title-cwd, .crumb, .cwd-pick span { text-decoration: underline dotted; text-decoration-color: var(--faint); text-underline-offset: 3px; } /* Only where a pointer hovers: touch browsers keep :hover after a tap, which made toggles look still on. */ @media (hover: hover) { .text-action:hover, .footer-action:hover, .tool-link:hover, .title-cwd:hover, .crumb:hover, .cwd-pick:hover span { color: var(--text); text-decoration-color: currentColor; } } /* Inline status text: stopped, compacting, retrying, errors. */ .notice { align-self: flex-start; color: var(--muted); font-size: var(--fs-sm); overflow-wrap: anywhere; } .notice.error { color: var(--bad); } .empty-note { padding: var(--s5) 0; color: var(--muted); font-size: var(--fs-sm); text-align: center; } /* Right-aligned muted value in a row: cwd, model, provider status. */ .row-value { flex: none; max-width: 55%; overflow: hidden; color: var(--muted); font-size: var(--fs-sm); text-overflow: ellipsis; white-space: nowrap; } /* Share link rows: label left, actions right. */ .share-row > span:first-child { flex: 1; } /* Layout */ .app { display: flex; height: 100%; background: var(--bg); } .main { position: relative; display: flex; flex: 1; flex-direction: column; min-width: 0; } /* The transcript and the composer floating over its bottom (.dock); a terminal panel may sit below. */ .chat { position: relative; display: flex; flex: 1; flex-direction: column; min-height: 0; } .drop-overlay { position: absolute; inset: var(--s2); z-index: 12; display: grid; place-items: center; border: 1px dashed var(--muted); background: color-mix(in srgb, var(--bg) 90%, transparent); font-size: var(--fs-md); pointer-events: none; } .drawer { position: fixed; inset-block: 0; inset-inline-start: 0; z-index: 20; width: min(86vw, 320px); padding-top: var(--safe-top); border-inline-end: 1px solid var(--line); background: var(--bg); transform: translateX(-102%); transition: transform 0.25s cubic-bezier(0.2, 0.8, 0.2, 1); } .drawer:dir(rtl) { transform: translateX(102%); } .drawer-scrim { position: fixed; inset: 0; z-index: 15; background: var(--scrim); opacity: 0; pointer-events: none; transition: opacity 0.25s; } .drawer-open .drawer { transform: none; } /* drawer-swipe.ts: a mostly horizontal drag is the drawer's even when it cannot cancel the touch (one that interrupts a fling), so the browser must not pan it. Vertical scrollers declare it themselves: a scroll container may reset the inherited value. Horizontal scrollers inside (code blocks, tables) keep their own panning. */ @media (max-width: 899px) { .app, .scroller, .session-scroll { touch-action: pan-y pinch-zoom; } } .drawer-open .drawer-scrim { opacity: 1; pointer-events: auto; } @media (min-width: 900px) { .drawer, .drawer:dir(rtl) { position: static; flex: 0 0 300px; transform: none; } .drawer-scrim, .topbar .menu { display: none; } .sidebar-collapsed .drawer { display: none; } .sidebar-collapsed .topbar .sidebar-open { display: grid; } } /* Top bar */ .topbar { position: relative; z-index: 5; padding-top: var(--safe-top); min-height: calc(var(--bar) + var(--safe-top)); } .new-session-view .topbar { border-bottom-color: transparent; } .title { flex: 1; min-width: 0; padding: 0 var(--s1); } .new-session-view .topbar .title { visibility: hidden; } .title h1 { overflow: hidden; font-size: var(--fs-md); font-weight: var(--fw-strong); line-height: 1.3; text-overflow: ellipsis; white-space: nowrap; } .title p, .title-cwd, .crumb { display: block; max-width: 100%; overflow: hidden; color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); line-height: 1.4; text-align: left; text-overflow: ellipsis; white-space: nowrap; } .crumb { font-family: var(--sans); } .sub-tag { margin-right: var(--s1); color: var(--muted); font-size: var(--fs-xs); font-weight: var(--fw-strong); letter-spacing: 0.04em; text-transform: uppercase; vertical-align: 1px; } .offline { padding: 0 var(--s2); color: var(--bad); font-size: var(--fs-xs); } /* Artifact count sits inline beside its icon. */ .artifact-button { display: inline-flex; align-items: center; gap: var(--s1); height: var(--control); padding: 0 calc(var(--x) - var(--bar-pad)); color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); } .artifact-button:hover { color: var(--text); } .toast { position: absolute; top: calc(var(--bar) + var(--safe-top) + var(--s2)); right: var(--x); left: var(--x); z-index: 8; padding: var(--s2) var(--s3); border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); color: var(--bad); font-size: var(--fs-sm); text-align: left; animation: rise 0.2s ease-out; } .banner { padding: var(--s2) var(--x); border-bottom: 1px solid var(--line); color: var(--bad); font-size: var(--fs-sm); overflow-wrap: anywhere; } /* Transcript */ .scroller { flex: 1; overflow-y: auto; /* The transcript keeps the reading position itself (use-chat-scroll.ts). */ overflow-anchor: none; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; /* Room for the composer dock floating over the transcript's bottom: App sets padding-bottom to its height. */ } /* The chat transcript keeps the reader's place with the browser's scroll anchoring where it has it (use-chat-scroll.ts), counting from the top: content arriving below the reader moves nothing. The spacers standing for rows not rendered are never anchors: one below the rows moves down when rows are rendered before it. */ .scroller.transcript { overflow-anchor: auto; } .row-space { overflow-anchor: none; } .scroller, .composer textarea, .sheet-body, .session-scroll { scrollbar-width: thin; scrollbar-color: var(--scrollbar) transparent; } .scroller::-webkit-scrollbar, .composer textarea::-webkit-scrollbar { width: 4px; height: 4px; background: transparent; } .scroller::-webkit-scrollbar-thumb, .composer textarea::-webkit-scrollbar-thumb { border-radius: 999px; background: var(--scrollbar); } /* The dock never paints over the transcript's scrollbar lane: App sets right to the lane's width (at least 4px). */ .dock { position: absolute; right: 4px; bottom: 0; left: 0; z-index: 4; } /* The transcript is a virtual list (use-chat-scroll.ts): `.rows` holds a spacer for the rows above, the rows near the viewport, and a spacer for the rows below. Each row carries its spacing to the row before it as padding, so its measured height is everything it adds; a row is a flex column like the list it replaces (its margins stay inside). */ .rows { display: flow-root; } .row { display: flex; flex-direction: column; padding-top: var(--s4); } /* A transcript appearing (opening a conversation) fades in rather than snapping in (`.messages`). */ @keyframes transcript-appear { from { opacity: 0; } } /* A message arriving at the end of the transcript fades in and rises a little (Chat.tsx `useArriving`; several at once one after another). Opacity and transform only: no layout changes, so the reader's place and the measured row heights stay as they are. */ @keyframes row-arrive { from { opacity: 0; transform: translateY(8px); } } .row.arriving { animation: row-arrive 0.28s cubic-bezier(0.2, 0.8, 0.2, 1) both; } @media (prefers-reduced-motion: reduce) { .row.arriving { animation: none; } } /* A tool call ending one response and one starting the next stack like parallel calls. */ .row.first, .row.joined { padding-top: 0; } .messages { display: flex; flex-direction: column; animation: transcript-appear 0.18s ease-out; gap: var(--s4); width: var(--chat-size); max-width: var(--chat-width); margin: 0 auto; padding: var(--s5) var(--x); } @media (prefers-reduced-motion: reduce) { .messages { animation: none; } } .user { display: flex; justify-content: flex-end; padding-left: 12%; } .guest-message { justify-content: flex-start; padding-right: 12%; padding-left: 0; } .bubble { max-width: 100%; padding: var(--s2) var(--s3); border-radius: var(--radius); background: var(--user); overflow-wrap: anywhere; white-space: pre-wrap; } .speaker { margin-bottom: var(--s1); color: var(--muted); font-size: var(--fs-xs); font-weight: var(--fw-strong); white-space: normal; } .bubble-files { display: flex; flex-wrap: wrap; gap: var(--s1); margin-top: var(--s2); white-space: normal; } .bubble-note { margin-top: var(--s1); color: var(--muted); font-size: var(--fs-xs); white-space: normal; } .bubble-files span { padding: 1px var(--s2); border: 1px solid var(--line); border-radius: var(--radius); font-family: var(--mono); font-size: var(--fs-xs); overflow-wrap: anywhere; } .assistant { display: flex; flex-direction: column; gap: var(--s3); min-width: 0; } .streaming > .md > :last-child::after { content: ""; display: inline-block; width: 6px; height: 6px; margin-left: var(--s1); border-radius: 50%; background: var(--muted); vertical-align: middle; animation: pulse 1s ease-in-out infinite; } .message-actions { display: flex; margin: calc(-1 * var(--s2)) 0 0 calc(-1 * var(--s2)); } /* Replies: a button beside a person's message (on hover; a coarse pointer uses a long press instead), the quoted line above a reply, and a brief highlight of the message a quoted line leads to. */ .reply-button { display: inline-flex; flex: none; align-items: center; align-self: flex-start; justify-content: center; width: var(--control-sm); height: var(--control-sm); color: var(--muted); } .reply-button:hover { color: var(--text); } .guest-message .reply-button { order: 1; } .user .reply-button { opacity: 0; } .user:hover .reply-button, .user .reply-button:focus-visible { opacity: 1; } /* Editing a person's message (`/tree`): beside it on hover, always shown where there is no hover. */ .edit-button { display: inline-flex; flex: none; align-items: center; align-self: flex-start; justify-content: center; width: var(--control-sm); height: var(--control-sm); color: var(--faint); opacity: 0; } .edit-button:hover { color: var(--text); } .user:hover .edit-button, .edit-button:focus-visible { opacity: 1; } @media (pointer: coarse) { .edit-button { opacity: 1; } } /* Phones and tablets reply with a long press. */ @media (pointer: coarse) { .reply-button { display: none; } } .composer .replying { display: flex; align-items: center; justify-content: space-between; gap: var(--s2); padding: var(--s1) var(--s3) 0; color: var(--muted); font-size: var(--fs-xs); } .composer .replying button { display: inline-flex; color: var(--muted); } .reply-quote { display: block; max-width: 100%; margin-bottom: var(--s1); padding-left: var(--s2); overflow: hidden; border-left: 2px solid var(--line); color: var(--muted); font-size: var(--fs-xs); text-align: left; text-overflow: ellipsis; white-space: nowrap; } .reply-quote:hover { color: var(--text); } .flash { animation: flash 1.6s ease-out; } @keyframes flash { from { background: var(--hover); } } .copy { display: inline-flex; align-items: center; gap: var(--s1); min-height: var(--control-sm); padding: 0 var(--s2); color: var(--muted); font-size: var(--fs-xs); } .copy:hover { color: var(--text); } .copy.done { color: var(--ok); } .marker { padding: var(--s2) 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); color: var(--muted); font-size: var(--fs-sm); } .marker summary { cursor: pointer; list-style: none; } .working { position: sticky; bottom: 0; z-index: 2; display: flex; align-self: flex-start; align-items: center; gap: var(--s1); min-height: 20px; } .working span { width: 4px; height: 4px; border-radius: 50%; background: var(--muted); animation: pulse 1.2s infinite ease-in-out; } .working span:nth-child(2) { animation-delay: 0.15s; } .working span:nth-child(3) { animation-delay: 0.3s; } /* Markdown */ .md { overflow-wrap: anywhere; } .md > * + * { margin-top: 0.7em; } .marker > .md { margin-top: var(--s2); } .md p, .md li { line-height: 1.6; } .md ul, .md ol { padding-left: 1.3em; } /* Browser margins off without outweighing the spacing between blocks (zero specificity). */ :where(.md ul, .md ol, .md h3, .md h4, .md h5, .md h6) { margin: 0; } /* Room for the widest marker: "10." needs more than "9." (Markdown sets --digits for two or more). */ .md ol { padding-left: calc(0.7em + var(--digits, 1) * 0.6em); } /* A short list inside a long one sets no --digits: it must not inherit the outer width. */ .md li ol { --digits: 1; } .md li + li { margin-top: 0.25em; } /* Blocks inside an item (nested lists, code, quotes) sit as close as the next item would, whether the item's text starts with markup or not; a loose item's paragraphs are as far apart as top-level ones. */ .md li > :is(ul, ol, blockquote, div, hr) { margin-top: 0.25em; } .md li > p + p { margin-top: 0.7em; } /* Task items show their checkbox instead of a bullet (in a loose item it sits in the first paragraph). */ .md li.task { list-style: none; } .md li.task > input, .md li.task > p:first-child > input { margin: 0 0.4em 0 -1.2em; vertical-align: -0.1em; } .md h3, .md h4, .md h5, .md h6 { font-size: 1em; font-weight: var(--fw-strong); } .md > * + :is(h3, h4, h5, h6) { margin-top: 1.1em; } .md code { padding: 0.1em 0.35em; border-radius: var(--radius); background: var(--hover); font-family: var(--mono); font-size: 0.86em; } .md blockquote { margin: 0; padding-left: var(--s3); border-left: 1px solid var(--line); color: var(--muted); } .md hr { border: 0; border-top: 1px solid var(--line); } .md .table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); } .md table { width: 100%; border-collapse: collapse; font-size: 0.92em; } /* Headers stay on one line; long cells wrap at words instead of making the table scroll far. */ .md th { white-space: nowrap; } .md td { overflow-wrap: normal; } .md th, .md td { padding: var(--s1) var(--s2); border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); text-align: left; } .md th { background: var(--code); font-weight: var(--fw-strong); } .md th:last-child, .md td:last-child { border-right: 0; } .md tbody tr:last-child td { border-bottom: 0; } .md .align-center { text-align: center; } .md .align-right { text-align: right; } .code-block { overflow: hidden; border: 1px solid var(--line); border-radius: var(--radius); background: var(--code); } .code-head { display: flex; align-items: center; justify-content: space-between; padding: 0 0 0 var(--s3); border-bottom: 1px solid var(--line); color: var(--faint); font-family: var(--mono); font-size: var(--fs-xs); } .code-block pre { margin: 0; padding: var(--s3); overflow-x: auto; font-size: var(--fs-sm); line-height: 1.5; tab-size: 3; } .md .code-block code { padding: 0; background: none; font-size: inherit; } /* highlight.js tokens */ .hljs-keyword, .hljs-selector-tag, .hljs-built_in, .hljs-doctag { color: #b0428a; } .hljs-string, .hljs-regexp, .hljs-addition, .hljs-attribute { color: #2b7d4b; } .hljs-number, .hljs-literal, .hljs-symbol, .hljs-bullet { color: #b35a00; } .hljs-title, .hljs-section, .hljs-name { color: #2a5fbf; } .hljs-type, .hljs-class .hljs-title, .hljs-title.class_ { color: #8a5a00; } .hljs-comment, .hljs-quote, .hljs-meta { color: var(--faint); font-style: italic; } .hljs-attr, .hljs-variable, .hljs-template-variable, .hljs-property { color: #7a3fb0; } .hljs-deletion { color: var(--del-ink); } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: 700; } [data-theme="dark"] .hljs-keyword, [data-theme="dark"] .hljs-selector-tag, [data-theme="dark"] .hljs-built_in, [data-theme="dark"] .hljs-doctag { color: #f28fc8; } [data-theme="dark"] .hljs-string, [data-theme="dark"] .hljs-regexp, [data-theme="dark"] .hljs-addition, [data-theme="dark"] .hljs-attribute { color: #9fd99a; } [data-theme="dark"] .hljs-number, [data-theme="dark"] .hljs-literal, [data-theme="dark"] .hljs-symbol, [data-theme="dark"] .hljs-bullet { color: #f5b073; } [data-theme="dark"] .hljs-title, [data-theme="dark"] .hljs-section, [data-theme="dark"] .hljs-name { color: #8ab4ff; } [data-theme="dark"] .hljs-type, [data-theme="dark"] .hljs-class .hljs-title, [data-theme="dark"] .hljs-title.class_ { color: #f0cf7a; } [data-theme="dark"] .hljs-attr, [data-theme="dark"] .hljs-variable, [data-theme="dark"] .hljs-template-variable, [data-theme="dark"] .hljs-property { color: #c9a4f5; } /* Thinking and tool calls share one compact row style. */ .thinking button, .tool-head, .report-head { display: flex; align-items: center; gap: var(--s2); width: 100%; min-height: var(--control-sm); color: var(--muted); font-size: var(--fs-sm); text-align: left; } .thinking.live button span:first-of-type { background: linear-gradient(90deg, var(--muted) 30%, var(--text) 50%, var(--muted) 70%); background-size: 200% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; animation: shimmer 1.6s linear infinite; } .thinking-body { margin-top: var(--s1); padding-left: var(--s3); border-left: 1px solid var(--line); color: var(--muted); font-size: var(--fs-sm); overflow-wrap: anywhere; white-space: pre-wrap; } .chev { display: inline-flex; flex: none; color: var(--faint); transition: transform 0.2s; } .chev.open { transform: rotate(90deg); } .tool { min-width: 0; } /* Consecutive calls stack without a gap, within a response and across response boundaries. */ .tool + .tool { margin-top: calc(-1 * var(--s3)); } .tool-header { display: flex; align-items: center; } .tool-head { flex: 1; min-width: 0; } .tool-icon { display: grid; flex: none; place-items: center; width: var(--tool-icon); color: var(--muted); } .tool-name { flex: none; color: var(--text); font-weight: var(--fw-strong); } .tool-summary { flex: 1; min-width: 0; overflow: hidden; font-family: var(--mono); font-size: var(--fs-code); text-overflow: ellipsis; white-space: nowrap; } .tool-state { display: grid; flex: none; place-items: center; color: var(--muted); font-size: var(--fs-xs); } .tool-expand { display: grid; flex: none; place-items: center; width: var(--control-sm); min-height: var(--control-sm); } .tool-body { max-height: 60vh; margin: var(--s1) 0 var(--s2); overflow: auto; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--code); } .tool-body pre:not(.diff) { margin: 0; padding: var(--s2) var(--s3); font-family: var(--mono); font-size: var(--fs-code); line-height: 1.5; overflow-wrap: anywhere; tab-size: 3; white-space: pre-wrap; } .tool-body pre + pre { border-top: 1px dashed var(--line); } .tool-command { color: var(--text); } .tool-sub { display: flex; flex-direction: column; gap: var(--s2); padding: var(--s2) var(--s3); font-size: var(--fs-sm); } .tool-link { display: inline-block; padding: var(--s1) 0 var(--s1) calc(var(--tool-icon) + var(--s2)); color: var(--muted); font-size: var(--fs-sm); } .tool-body pre.tool-error { color: var(--bad); } /* Code mode scripts: the first lines until "Show all"; highlighted exactly like write's content. */ .script.clip pre { max-height: calc(6 * 1.5 * var(--fs-code) + var(--s2)); overflow: hidden; } /* A failed script is still source: only its output takes the card's error color. */ .tool.error .script pre { color: var(--text); } .script + pre { border-top: 1px dashed var(--line); } .tool-link.script-more { padding-left: var(--s3); } /* Calls a tool made to other tools: compact status rows aligned with the card's summary. */ .nested-calls { margin: 0 0 var(--s1); padding: 0; list-style: none; } .nested-call { display: grid; grid-template-columns: var(--tool-icon) auto minmax(0, 1fr) auto; column-gap: var(--s2); align-items: center; min-height: 24px; padding-left: calc(var(--tool-icon) + var(--s2) + var(--depth, 0) * var(--s3)); color: var(--muted); font-size: var(--fs-sm); } .nested-state { display: grid; place-items: center; } .nested-call.error .nested-state, .nested-call.interrupted .nested-state { color: var(--bad); } .nested-name { color: var(--text); } /* A one-shot agent's row opens its transcript. */ .nested-open { color: var(--text); text-align: left; text-decoration: underline dotted; text-underline-offset: 3px; } .nested-open:hover { text-decoration-style: solid; } .nested-summary, .nested-detail { min-width: 0; overflow: hidden; font-family: var(--mono); font-size: var(--fs-code); text-overflow: ellipsis; white-space: nowrap; } .nested-time { color: var(--faint); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; } .nested-detail { grid-column: 2 / -1; padding-bottom: var(--s1); } .nested-error { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; color: var(--bad); overflow-wrap: anywhere; white-space: pre-wrap; } /* Artifact and file cards: the body opens the viewer; the bordered end downloads. */ .file-tool { padding: var(--s1) 0; } .tool + .file-tool, .file-tool + .tool { margin-top: 0; } .file-card { display: flex; align-items: stretch; max-width: 420px; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; } .file-card-main { display: flex; flex: 1; align-items: center; gap: var(--s3); min-width: 0; padding: var(--s2) var(--s3); text-align: left; } .file-card-main:hover { background: var(--hover); } .file-card-icon { display: grid; flex: none; place-items: center; width: var(--control-sm); height: var(--control-sm); border: 1px solid var(--line); border-radius: var(--radius); color: var(--muted); } .file-card-text { display: flex; flex-direction: column; min-width: 0; } .file-card-text strong { overflow: hidden; font-size: var(--fs-md); font-weight: var(--fw-medium); text-overflow: ellipsis; white-space: nowrap; } .file-card-text small { color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); } .file-card-download { display: grid; flex: none; place-items: center; width: var(--row); border-left: 1px solid var(--line); color: var(--muted); } .file-card-download:hover { background: var(--hover); color: var(--text); } /* An artifact rendered at its tool call site (`inline: true`): below the card, at most about 70vh high. */ .inline-artifact { margin-top: var(--s2); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; } .inline-artifact.markdown, .inline-artifact.text { max-height: 70vh; overflow: auto; padding: var(--s3); } .inline-artifact > pre { margin: 0; font-family: var(--mono); font-size: var(--fs-sm); overflow-wrap: anywhere; white-space: pre-wrap; } .inline-artifact > iframe { display: block; width: 100%; max-height: 70vh; border: 0; background: white; } .inline-artifact > .notice { margin: 0; } .file-media { display: block; width: 100%; max-height: 60vh; object-fit: contain; } audio.file-media { padding: var(--s2); } .diff { margin: 0; padding: var(--s2) 0; overflow-x: auto; font-family: var(--mono); font-size: var(--fs-code); line-height: 1.5; tab-size: 3; } .diff-lines { display: block; width: max-content; min-width: 100%; } .diff-line { display: block; padding: 0 var(--s3); } .diff .add { background: var(--add); color: var(--add-ink); } .diff .del { background: var(--del); color: var(--del-ink); } /* Git patches: one gutter as wide as the largest line number, code in its own column. */ .patch .diff-line { display: grid; grid-template-columns: var(--gutter, 3ch) max-content; column-gap: var(--s2); padding: 0 var(--s2); } .diff-number { color: var(--faint); text-align: right; user-select: none; } .patch .meta, .patch .hunk { color: var(--muted); } .patch .hunk { background: var(--hover); } .patch .meta .diff-code, .patch .hunk .diff-code { grid-column: 1 / -1; } .patch .meta .diff-number, .patch .hunk .diff-number { display: none; } /* Workspace panel: full screen on phones, overlay column on tablets, docked on wide screens. */ .workspace { position: fixed; inset: 0; z-index: 35; display: flex; flex-direction: column; padding: var(--safe-top) 0 var(--safe-bottom); background: var(--bg); } /* Tabs, list and review bar; with an open file beside it (.split), the panel's right-hand column. */ .workspace-column { position: relative; display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; } @media (min-width: 900px) { .workspace { left: auto; width: min(520px, 100%); border-left: 1px solid var(--line); } } @media (min-width: 1280px) { /* min-width: 0, or the widest unbreakable content (a table, a long line in a Markdown preview) widens the docked panel past its width and squeezes the chat to nothing. */ .workspace { position: relative; flex: 0 0 var(--workspace-width, 460px); width: auto; min-width: 0; padding: 0; } /* An open file left of the list. The list keeps its width; the chat (.main, flex: 1) and the file split the rest equally, because both grow by the same factor from bases that differ by the list. */ .workspace.split { flex: 1 1 var(--workspace-width, 460px); flex-direction: row; } .workspace.split .workspace-column { flex: 0 0 var(--workspace-width, 460px); } .workspace-preview { display: flex; flex: 1 1 0; flex-direction: column; min-width: 0; border-right: 1px solid var(--line); } .workspace-preview-body { flex: 1; min-height: 0; overflow-y: auto; padding-bottom: var(--s4); scrollbar-width: thin; scrollbar-color: var(--scrollbar) transparent; } /* Drag the left edge to resize the docked panel. touch-action: none, or the browser turns a finger drag into a pan and stops delivering pointer moves. */ .workspace-resize { position: absolute; top: 0; bottom: 0; left: -3px; z-index: 2; width: 6px; cursor: col-resize; touch-action: none; user-select: none; } .workspace-resize:hover, .workspace-resize:active { background: var(--line); } /* Fingers get a wider target; the visible line stays thin. */ @media (pointer: coarse) { .workspace-resize { left: -12px; width: 24px; background: linear-gradient(var(--line), var(--line)) center / 2px 100% no-repeat transparent; } .workspace-resize:hover, .workspace-resize:active { background: linear-gradient(var(--muted), var(--muted)) center / 2px 100% no-repeat transparent; } } } @media (max-width: 1279px) { .workspace-resize { display: none; } } .workspace-head { display: flex; flex: none; align-items: center; gap: var(--s3); min-height: var(--bar); padding: 0 var(--bar-pad); border-bottom: 1px solid var(--line); } .workspace-location { min-width: 0; overflow: hidden; color: var(--faint); font-family: var(--mono); font-size: var(--fs-xs); text-align: right; text-overflow: ellipsis; white-space: nowrap; } /* Icon tabs: 10px side padding puts the first 20px glyph on the inset; counts sit beside their icon. */ .workspace-tabs { display: flex; min-width: 0; } .workspace-tabs button { display: inline-flex; flex: none; align-items: center; gap: var(--s1); height: var(--control); padding: 0 10px; color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); } .workspace-tabs button:hover, .workspace-tabs button[aria-pressed="true"] { color: var(--text); } .workspace-tabs button[aria-pressed="true"] { box-shadow: inset 0 -1px var(--text); } /* Rows, bars and content bleed to the panel edges; text keeps the inset. */ .workspace-body { flex: 1; min-height: 0; overflow-y: auto; padding-bottom: var(--s4); scrollbar-width: thin; scrollbar-color: var(--scrollbar) transparent; } .workspace-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; min-height: var(--control-sm); padding: 0 var(--x); font-size: var(--fs-xs); } .workspace-crumbs .crumb { display: inline; } .workspace-note { padding: var(--s2) var(--x); } .workspace-list { display: flex; flex-direction: column; } .workspace-row { display: flex; align-items: center; gap: var(--s2); min-height: var(--control-sm); padding: 0 var(--x); color: var(--text); font-family: var(--mono); font-size: var(--fs-code); text-align: left; } .workspace-row:hover { background: var(--hover); } /* The file open beside the list. */ .workspace-row[aria-current="true"] { box-shadow: inset 2px 0 var(--text); background: var(--hover); } .workspace-row svg { flex: none; color: var(--faint); } .workspace-row.directory { color: var(--muted); } .workspace-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .workspace-row small { flex: none; color: var(--faint); font-size: var(--fs-xs); } .workspace-status { flex: none; width: 1.5ch; color: var(--muted); font-weight: var(--fw-strong); } .workspace-status.status-A, .workspace-status.status-\? { color: var(--add-ink); } .workspace-status.status-D { color: var(--del-ink); } /* File and diff view: one sticky bar, then content edge to edge. */ /* Path bar and view switch stay together at the top of the scrolling panel. */ .workspace-bars { position: sticky; top: 0; z-index: 1; background: var(--bg); } .workspace-bar { display: flex; align-items: center; gap: var(--s3); min-height: var(--control); padding-right: var(--bar-pad); border-bottom: 1px solid var(--line); background: var(--bg); } .workspace-bar .back svg { transform: rotate(180deg); } .workspace-path { flex: 1; min-width: 0; overflow: hidden; direction: rtl; font-family: var(--mono); font-size: var(--fs-code); text-align: left; text-overflow: ellipsis; white-space: nowrap; } .workspace-bar .text-action.on { color: var(--text); text-decoration-style: solid; text-decoration-color: currentColor; } .workspace-view pre { margin: 0; padding: var(--s2) 0; border: 0; border-radius: 0; background: none; font-family: var(--mono); font-size: var(--fs-code); line-height: 1.5; tab-size: 3; } .workspace-view pre.code { padding: var(--s2) var(--s2); overflow-x: auto; white-space: pre; } .workspace-view.wrap pre.code, .workspace-view.wrap .diff-code { white-space: pre-wrap; overflow-wrap: anywhere; } .workspace-view.wrap .diff-lines { width: auto; } .workspace-view.wrap .patch .diff-line { grid-template-columns: var(--gutter, 3ch) minmax(0, 1fr); } /* Terminal tab: tabs per terminal, the screen (libghostty-vt rendered into DOM rows), and keys phones lack. overflow: clip here and on the screen, not hidden: a hidden box still scrolls when the browser reveals the focused textarea (Android does as the keyboard opens), which shifted the rows out of place for good. */ .workspace-body.terminal-body { display: flex; overflow: clip; padding-bottom: 0; } .terminal { --term-fg: var(--text); --term-bg: var(--bg); display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; } .terminal-tabs { display: flex; flex: none; align-items: center; gap: var(--s1); min-height: var(--control); padding: 0 var(--bar-pad) 0 var(--x); overflow-x: auto; overscroll-behavior-x: contain; border-bottom: 1px solid var(--line); scrollbar-width: none; } .terminal-tabs > button:not(.icon-button) { flex: none; max-width: 14em; height: var(--control-sm); padding: 0 var(--s2); overflow: hidden; color: var(--muted); font-size: var(--fs-sm); text-overflow: ellipsis; white-space: nowrap; } .terminal-tabs > button[aria-pressed="true"] { box-shadow: inset 0 -1px var(--text); color: var(--text); } .terminal-tabs > button.exited { text-decoration: line-through; } .terminal-tabs > .icon-button[aria-pressed="true"] { color: var(--text); } .terminal-outdated, .terminal-confirm { display: flex; align-items: baseline; gap: var(--s3); } /* The terminal below the chat: its top edge drags to resize it. */ .terminal-panel { position: relative; display: flex; flex: none; flex-direction: column; height: var(--terminal-height, 40%); min-height: 120px; max-height: 85%; border-top: 1px solid var(--line); background: var(--bg); } .terminal-resize { position: absolute; top: -3px; right: 0; left: 0; z-index: 5; height: 6px; cursor: row-resize; touch-action: none; user-select: none; } .terminal-resize:hover, .terminal-resize:active { background: var(--line); } @media (pointer: coarse) { .terminal-resize { top: -12px; height: 24px; background: linear-gradient(var(--line), var(--line)) center / 100% 2px no-repeat transparent; } } .terminal-start { align-self: center; margin: var(--s4); } .terminal-screen { position: relative; flex: 1; min-height: 0; padding: var(--s1) 6px; overflow: clip; background: var(--term-bg); color: var(--term-fg); cursor: default; /* Fingers scroll the terminal itself (Terminal.tsx), never the page. */ touch-action: none; } .terminal-content { position: relative; height: 100%; overflow: clip; font: 13px / 1.3 var(--mono); font-variant-ligatures: none; white-space: pre; user-select: text; } .terminal-row { height: var(--cell-height); overflow: hidden; line-height: var(--cell-height); } /* Backgrounds fill the whole cell: an inline span's background covers only the font's height, leaving gaps between rows of colored lines. */ .terminal-row > span { display: inline-block; height: var(--cell-height); vertical-align: top; } .terminal-row .cell { display: inline-block; overflow: visible; text-align: center; vertical-align: top; } /* Block elements are drawn as rectangles of the whole cell (Terminal.tsx), not glyphs. */ .terminal-row .cell.block { height: var(--cell-height); } .terminal-cursor { position: absolute; top: 0; left: 0; width: var(--cell-width); height: var(--cell-height); background: var(--cursor, var(--term-fg)); opacity: 0.45; pointer-events: none; } .terminal-cursor[data-style="bar"] { width: 2px; opacity: 1; } .terminal-cursor[data-style="underline"] { top: calc(var(--cell-height) - 2px); height: 2px; opacity: 1; } .terminal-cursor[data-style="hollow"], .terminal-screen:not(:focus-within) .terminal-cursor { background: none; outline: 1px solid var(--cursor, var(--term-fg)); outline-offset: -1px; opacity: 1; } /* Where the shown rows are in the scrollback, while scrolled up. */ .terminal-scrollbar { position: absolute; right: 0; width: 3px; border-radius: 999px; background: var(--scrollbar); pointer-events: none; } /* Takes keys and text; invisible, at the cursor so input method popups appear there. 16px: iOS zooms smaller inputs. */ .terminal-input { position: absolute; top: var(--s1); left: 6px; width: 1px; height: 1em; padding: 0; border: 0; font-size: 16px; opacity: 0; resize: none; caret-color: transparent; } .terminal-keys { display: flex; flex: none; gap: var(--s1); padding: var(--s1) var(--s2); overflow-x: auto; overscroll-behavior-x: contain; border-top: 1px solid var(--line); scrollbar-width: none; touch-action: pan-x; } .terminal-keys button { flex: none; min-width: var(--control-sm); height: var(--control-sm); padding: 0 var(--s2); border: 1px solid var(--line); border-radius: var(--radius); color: var(--muted); font: var(--fs-sm) var(--mono); } .terminal-keys button[aria-pressed="true"] { border-color: var(--text); color: var(--text); } .workspace-media { padding: var(--s2); } /* The second bar holds the view switch and Wrap. */ .workspace-bar.secondary { min-height: var(--control-sm); padding-left: var(--x); } .workspace-views { gap: var(--s3); } .workspace-views button { height: var(--control-sm); font-size: var(--fs-xs); } .workspace-hint { padding: var(--s1) var(--x) 0; color: var(--faint); font-size: var(--fs-xs); } .workspace-markdown { padding: var(--s2) var(--s2); } /* Line numbers are tap targets for comments; a selected range is tinted. */ button.diff-number { font: inherit; cursor: pointer; } button.diff-number:hover { color: var(--text); } .diff-line.selected { box-shadow: inset 2px 0 var(--text); background: var(--hover); } .line-comment { display: flex; align-items: flex-start; gap: var(--s2); margin: var(--s1) var(--s2) var(--s1) calc(var(--gutter, 3ch) + var(--s2) * 2); padding: var(--s1) var(--s2); border-left: 2px solid var(--muted); background: var(--bg); color: var(--text); font-family: var(--sans); font-size: var(--fs-sm); white-space: pre-wrap; } /* Unwrapped code is as wide as its longest line and scrolls sideways: comments keep to the visible width and stay in view while scrolling. */ .workspace-view pre.patch { container-type: inline-size; } .workspace-view .line-comment { position: sticky; left: calc(var(--gutter, 3ch) + var(--s2) * 2); box-sizing: border-box; width: calc(100cqw - var(--gutter, 3ch) - var(--s2) * 3); } .line-comment-text { flex: 1; min-width: 0; color: inherit; font: inherit; overflow-wrap: anywhere; text-align: left; white-space: pre-wrap; } .line-comment.current { border-left-color: var(--text); background: var(--hover); } .comment-nav { display: flex; align-items: center; color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); } .comment-nav .icon-button { width: var(--control-sm); height: var(--control-sm); } .comment-nav .icon-button:first-child svg { transform: rotate(180deg); } .line-comment.outdated { flex-wrap: wrap; border-left-color: var(--faint); } .line-comment-outdated { flex: 1 0 100%; color: var(--muted); font-size: var(--fs-xs); overflow-wrap: anywhere; } .line-comment-outdated code { font-family: var(--mono); } .comment-row { display: flex; align-items: flex-start; border-bottom: 1px solid var(--line); } .comment-row .workspace-row { flex: 1; min-width: 0; align-items: flex-start; padding-top: var(--s2); padding-bottom: var(--s2); } .comment-row .workspace-name { white-space: normal; } .comment-quote { color: var(--muted); white-space: pre-wrap; } .comment-text { font-family: var(--sans); font-size: var(--fs-sm); white-space: pre-wrap; } .comment-actions { display: flex; flex: none; align-items: center; gap: var(--s1); padding: var(--s1) var(--bar-pad) 0 0; } .workspace-section { margin: 0; padding: var(--s3) var(--x) var(--s1); color: var(--muted); font-size: var(--fs-xs); font-weight: var(--fw-strong); letter-spacing: 0.04em; text-transform: uppercase; } .commit-row { align-items: flex-start; padding-top: var(--s1); padding-bottom: var(--s1); } .commit-row .workspace-name { white-space: normal; font-family: var(--sans); font-size: var(--fs-sm); } .commit-row .workspace-name .muted { font-family: var(--mono); font-size: var(--fs-xs); } /* Pushed commits: hollow dot; not yet pushed: filled. */ .commit-dot { flex: none; width: 7px; height: 7px; margin-top: 6px; border: 1px solid var(--faint); border-radius: 50%; } .commit-row.unpushed .commit-dot { border-color: var(--text); background: var(--text); } .commit-meta { display: flex; flex-direction: column; gap: var(--s1); padding: var(--s2) var(--x); border-bottom: 1px solid var(--line); font-size: var(--fs-xs); } .commit-message { margin: 0; font-family: var(--sans); font-size: var(--fs-sm); white-space: pre-wrap; overflow-wrap: anywhere; } .workspace-commit { flex: none; color: var(--muted); font-size: var(--fs-xs); } .workspace-upload { order: 1; margin-left: auto; cursor: pointer; } .comment-count { flex: none; color: var(--muted); font-family: var(--sans); font-size: var(--fs-xs); } .line-comment .icon-button { width: var(--control-sm); height: var(--control-sm); margin: calc(-1 * var(--s1)) calc(-1 * var(--s2)) 0 0; } .line-comment.editor { flex-direction: column; align-items: stretch; } .line-comment textarea { width: 100%; padding: var(--s1) 0; border: 0; border-bottom: 1px solid var(--line); outline: 0; resize: vertical; background: transparent; font: inherit; } .line-comment-actions { display: flex; justify-content: flex-end; gap: var(--s3); } .review-bar { flex: none; display: flex; align-items: center; gap: var(--s3); padding: var(--s2) var(--x); border-top: 1px solid var(--line); background: var(--bg); font-size: var(--fs-sm); } .workspace-row.ignored { color: var(--faint); } .workspace-row.match { align-items: flex-start; padding-top: var(--s1); padding-bottom: var(--s1); } .workspace-row.match .workspace-name { white-space: pre; } .workspace-search { width: auto; margin: 0 var(--x); } /* Files: the name filter with the upload action beside it. */ .workspace-filter { display: flex; align-items: center; gap: var(--s3); padding-right: var(--x); } .workspace-filter .workspace-search { flex: 1; min-width: 0; margin-right: 0; } .workspace-filter .workspace-upload { flex: none; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* Folder chevrons take a file icon's width, so names line up at each depth. */ .workspace-tree .chev { justify-content: center; width: 15px; } .fuzzy-hit { color: var(--text); font-weight: var(--fw-strong); } .workspace-glob > span { flex: none; width: 4.5em; font-size: var(--fs-sm); } .workspace-glob input { font-family: var(--mono); font-size: var(--fs-sm); } .diff-line.marked { background: var(--hover); box-shadow: inset 2px 0 var(--muted); } .patch .hunk button.diff-code { color: inherit; font: inherit; text-align: left; } .patch .hunk button.diff-code:hover { color: var(--text); } .review-bar > span { flex: 1; color: var(--muted); } .workspace-toggle.on { color: var(--text); } .report { padding-left: var(--s3); border-left: 1px solid var(--line); } .report-head strong { color: var(--text); } .report-body { margin-top: var(--s1); font-size: var(--fs-md); overflow-wrap: anywhere; } .report-body.clamp { display: -webkit-box; overflow: hidden; -webkit-line-clamp: 4; -webkit-box-orient: vertical; } .images { display: flex; flex-wrap: wrap; gap: var(--s2); padding-top: var(--s2); } /* Chat.tsx sizes each image's box from its header (width and aspect-ratio) before it decodes; a row of images keeps each one's own height instead of stretching it, and a box that is off by a border never distorts the picture. */ .images img { align-self: flex-start; max-width: 100%; max-height: 320px; object-fit: contain; border: 1px solid var(--line); border-radius: var(--radius); } /* Empty state */ .hero { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--s4); min-height: 100%; padding: var(--s5); text-align: center; } .hero-title { display: flex; align-items: center; gap: var(--s3); } .hero h2 { font-size: 22px; font-weight: var(--fw-strong); letter-spacing: -0.02em; } .cwd-pick { display: inline-flex; align-items: center; gap: var(--s2); max-width: 100%; color: var(--muted); font-family: var(--mono); font-size: var(--fs-sm); } .cwd-pick span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pi-logo { width: 64px; height: 64px; flex: none; shape-rendering: crispEdges; } .hero-title .pi-logo { width: 32px; height: 32px; } .pi-logo.compact { width: 24px; height: 24px; } .pi-logo-coral { fill: rgb(228 138 122); } .pi-logo-blue { fill: rgb(79 142 179); } .pi-logo-yellow { fill: rgb(234 182 93); } /* Composer: same inset as transcript text, one vertical step. */ .composer-wrap { position: relative; padding-bottom: var(--safe-bottom); background: linear-gradient(transparent, var(--bg) var(--s5)); } .subagent-wrap { padding-bottom: 0; background: var(--bg); } .composer, .queued, .subagent-bar, .session-usage { width: var(--chat-size); max-width: var(--chat-width); margin: 0 auto; padding-right: var(--x); padding-left: var(--x); background: var(--bg); } .composer { padding-top: var(--s2); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); } /* `@` and Tab file completion (p2 addition, src/web/file-completion.tsx): a list just above the textarea. */ .file-completion { display: flex; flex-direction: column; max-height: 40vh; overflow-y: auto; margin-bottom: var(--s2); border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); font-size: var(--fs-sm); } .file-completion > [role="option"] { display: flex; gap: var(--s3); align-items: baseline; padding: var(--s1) var(--s2); cursor: pointer; } .file-completion > [role="option"].selected { background: var(--hover); } .file-completion-path, .file-completion-hint { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .file-completion-hint { padding: var(--s1) var(--s2); } .queued { display: flex; flex-direction: column; padding-top: var(--s1); border-top: 1px solid var(--line); } .queued + .composer { padding-top: var(--s1); border-top: 0; } .queued > div { display: flex; align-items: center; gap: var(--s2); min-height: var(--control-sm); font-size: var(--fs-sm); } .queue-edit { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .queue-edit span { margin-right: var(--s1); color: var(--muted); font-weight: var(--fw-strong); } .queue-remove { display: grid; flex: none; place-items: center end; width: var(--control-sm); height: var(--control-sm); color: var(--muted); } .queue-remove:hover { color: var(--text); } .composer textarea { display: block; width: 100%; max-height: 200px; margin: 0; padding: 0; border: 0; outline: 0; resize: none; overflow-y: hidden; background: transparent; font-size: 16px; line-height: 1.5; } .attachments { display: flex; flex-wrap: wrap; gap: var(--s1); margin-bottom: var(--s2); } .attachment { display: inline-flex; align-items: center; gap: var(--s2); max-width: 100%; height: var(--control-sm); padding-left: var(--s1); border: 1px solid var(--line); border-radius: var(--radius); font-size: var(--fs-sm); } .attachment img { width: 24px; height: 24px; border-radius: var(--radius); object-fit: cover; } .attachment-name { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .attachment button { display: grid; place-items: center; width: var(--control-sm); height: 100%; color: var(--muted); } /* 40px targets; the outer buttons hug the inset so glyphs align with text. */ .composer-bar { display: flex; align-items: center; gap: var(--s1); height: var(--control); } .composer-bar > :first-child { place-items: center start; } .composer-bar > :last-child { place-items: center end; } .spacer { flex: 1; } .round { display: grid; flex: none; place-items: center; width: var(--control); height: var(--control); color: var(--text); } .round:hover { color: var(--muted); } .attach { color: var(--muted); cursor: pointer; } .attach:hover { color: var(--text); } .send:disabled { color: var(--faint); opacity: 1; } .chip { display: inline-flex; align-items: center; height: var(--control); padding: 0 var(--s2); color: var(--muted); font-size: var(--fs-sm); } .chip.toggle.on { color: var(--text); font-weight: var(--fw-strong); } .to-bottom { position: absolute; top: -40px; left: 50%; display: grid; place-items: center; width: var(--control-sm); height: var(--control-sm); border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); translate: -50% 0; animation: rise 0.2s ease-out; } .subagent-bar { display: flex; align-items: center; gap: var(--s2); min-height: var(--control); border-top: 1px solid var(--line); color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); } .subagent-bar span { flex: 1; min-width: 0; overflow-wrap: anywhere; } .subagent-bar .round { place-items: center end; } /* Footer: model left, stats right, context bar beneath. */ .session-usage { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0 var(--s3); padding-top: var(--s1); padding-bottom: var(--s2); white-space: nowrap; } .session-controls, .session-stats { display: flex; align-items: center; min-width: 0; min-height: 24px; } .session-controls { flex: 1; } .session-stats { flex: none; gap: var(--s1); margin-left: auto; } .usage { color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); white-space: nowrap; } .client-count { display: inline-flex; align-items: center; gap: var(--s1); } .model-control { min-width: 0; max-width: 100%; overflow: hidden; color: var(--muted); font-size: var(--fs-xs); text-overflow: ellipsis; white-space: nowrap; } .model-control.static { cursor: default; text-decoration: none; } .context-usage { display: flex; flex: 1 0 100%; align-items: center; gap: var(--s2); min-height: 16px; } .context-track { flex: 1; min-width: 0; height: 2px; border: 0; background: var(--line); appearance: none; -webkit-appearance: none; } .context-track::-webkit-progress-bar { background: var(--line); } .context-track::-webkit-progress-value { background: var(--muted); } .context-track::-moz-progress-bar { background: var(--muted); } /* Loading: a skeleton of the bar in its space (the track, and a block over the numbers' invisible text, so it has their box), so nothing moves when the numbers come. A soft highlight sweeps across it; still with reduced motion. */ @keyframes skeleton-sweep { from { background-position: 150% 0; } to { background-position: -50% 0; } } .context-usage.pending .context-track, .context-usage.pending .skeleton-text::before { background: linear-gradient( 90deg, var(--line) 30%, color-mix(in srgb, var(--faint) 45%, var(--line)) 50%, var(--line) 70% ) var(--line); background-size: 250% 100%; animation: skeleton-sweep 1.4s ease-in-out infinite; } .context-usage.pending .context-track { border-radius: 1px; } .context-usage.pending .skeleton-text { position: relative; color: transparent; } .context-usage.pending .skeleton-text::before { position: absolute; inset: calc(50% - 4.5px) 0 auto; height: 9px; border-radius: 3px; content: ""; } @media (prefers-reduced-motion: reduce) { .context-usage.pending .context-track, .context-usage.pending .skeleton-text::before { animation: none; } } /* Drawer */ .drawer-content { display: flex; flex-direction: column; height: 100%; } .drawer-top { justify-content: space-between; padding-left: var(--x); } .brand { display: flex; align-items: center; gap: var(--s2); font-size: var(--fs-md); font-weight: var(--fw-strong); } .drawer-actions { display: flex; align-items: center; } .collapse-sidebar svg { transform: rotate(180deg); } .new-session { display: inline-flex; align-items: center; gap: var(--s1); height: var(--control); padding: 0 var(--s2); color: var(--muted); font-size: var(--fs-sm); } .new-session:hover { color: var(--text); } .drawer-content > .search { width: auto; margin: 0 var(--x); } .drawer-content > .empty-note { padding: var(--s4) var(--x); text-align: left; } .session-scroll { flex: 1; overflow-y: auto; padding: 0 var(--x) var(--s4); } .group h3 { overflow: hidden; font-family: var(--mono); text-overflow: ellipsis; text-transform: none; letter-spacing: 0; white-space: nowrap; } .session-main { min-height: var(--control); padding-top: var(--s1); padding-bottom: var(--s1); } .session-title { display: flex; flex: 1; align-items: center; gap: var(--s1); min-width: 0; overflow: hidden; font-size: var(--fs-md); text-overflow: ellipsis; white-space: nowrap; } .session-title svg { flex: none; color: var(--muted); } .session-meta { display: flex; flex: none; align-items: center; gap: var(--s1); color: var(--faint); font-size: var(--fs-xs); } .session-header { display: flex; align-items: stretch; margin: 0 calc(-1 * var(--x)); } .session-header .session-main { flex: 1; min-width: 0; width: 0; margin: 0; } .session-header.has-subagents .session-main { padding-right: var(--s1); } .subagent-toggle { display: inline-flex; flex: none; align-items: center; justify-content: center; gap: var(--s1); min-width: var(--control-sm); margin-right: var(--s2); padding: 0 var(--s1); color: var(--muted); font-size: var(--fs-xs); } .subagent-toggle:hover { color: var(--text); } /* The branch count beside the subagent count: the fork icon before it. */ .toggle-branches { display: inline-flex; align-items: center; gap: 2px; } .subagent-row { min-height: var(--control-sm); padding-top: 2px; padding-bottom: 2px; color: var(--muted); } .subagent-row .session-title { font-size: var(--fs-sm); } .subagent-stored { color: var(--faint); } .subagent-row .session-meta { max-width: 40%; overflow: hidden; font-family: var(--mono); text-overflow: ellipsis; white-space: nowrap; } /* `/tree` branches under their session: the fork icon before the label (docs/tree.md). */ .branch-row > svg { flex: none; color: var(--faint); } .branch-row .session-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* Renaming a branch: the pencil at the row's end on hover, always shown where there is no hover. */ .branch-item { position: relative; } .branch-item .branch-row { padding-right: calc(var(--x) + var(--control-sm)); } .branch-rename { position: absolute; top: 50%; right: 0; display: inline-flex; align-items: center; justify-content: center; width: var(--control-sm); height: var(--control-sm); color: var(--faint); opacity: 0; transform: translateY(-50%); } .branch-rename:hover { color: var(--text); } .branch-item:hover .branch-rename, .branch-rename:focus-visible { opacity: 1; } @media (pointer: coarse) { .branch-rename { opacity: 1; } } .subagent-location { min-width: 0; overflow: hidden; text-overflow: ellipsis; } .branch { flex: none; width: 10px; height: 12px; margin-top: calc(-1 * var(--s2)); border-bottom: 1px solid var(--line); border-left: 1px solid var(--line); } .live-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--working); animation: pulse 1.2s ease-in-out infinite; } /* An answer the owner has not read yet: steady, unlike the working dot. */ .unread-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--unread); } .hit { display: block; } .hit strong { display: block; font-size: var(--fs-sm); font-weight: var(--fw-strong); } .hit span { display: -webkit-box; overflow: hidden; color: var(--muted); font-size: var(--fs-sm); -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .drawer-foot { justify-content: flex-start; padding-bottom: var(--safe-bottom); border-top: 1px solid var(--line); } .foot-button { display: flex; align-items: center; gap: var(--s2); height: var(--control); padding: 0 calc(var(--x) - var(--bar-pad)); color: var(--muted); font-size: var(--fs-sm); } .foot-button:hover, .foot-button.on { color: var(--text); } .foot-button.on { font-weight: var(--fw-strong); } /* The footer's "More" menu: owner entries in a list opening upwards from the button. */ .foot-menu { position: relative; } .foot-menu-chevron { display: flex; transform: rotate(-90deg); transition: transform 0.15s ease-out; } .foot-button[aria-expanded="true"] .foot-menu-chevron { transform: rotate(90deg); } .foot-menu-list { position: absolute; bottom: calc(100% + var(--s1)); left: 0; z-index: 5; display: flex; flex-direction: column; min-width: 180px; padding: var(--s1) 0; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); box-shadow: 0 6px 24px rgb(0 0 0 / 0.18); } .foot-menu-list[hidden] { display: none; } .foot-menu-list .foot-button { width: 100%; padding: 0 var(--x); text-align: left; } .foot-menu-list .foot-button:hover { background: var(--hover); } /* Sheets: bottom sheet on phones, centred dialog on wide screens. Above the workspace panel (35), below the viewer (40): only the top navigation layer is ever shown. */ .sheet-layer { position: fixed; inset: 0; z-index: 38; display: flex; align-items: flex-end; justify-content: center; } .scrim { position: absolute; inset: 0; background: var(--scrim); animation: fade 0.2s ease-out; } .sheet { position: relative; display: flex; flex-direction: column; width: 100%; max-width: 560px; max-height: 88dvh; padding-bottom: var(--safe-bottom); border-top: 1px solid var(--line); background: var(--bg); animation: slide 0.25s cubic-bezier(0.2, 0.8, 0.2, 1); } .sheet-head { justify-content: space-between; padding-left: var(--x); } /* Phones: the sheet's top edge (a grip above the header, and the header) drags it down to close it (src/web/sheet-swipe.ts); no browser gesture there, so the drag owns its touch. The content keeps scrolling. */ .sheet-grip { display: flex; justify-content: center; padding-top: var(--s2); touch-action: none; } .sheet-grip::before { width: 36px; height: 4px; border-radius: 2px; background: var(--line); content: ""; } @media (max-width: 699px) { .sheet-head { touch-action: none; } } .sheet-head h2 { font-size: var(--fs-lg); font-weight: var(--fw-strong); } .sheet-body { overflow-y: auto; padding: var(--s3) var(--x) var(--s5); } .sheet-body > .muted.small { margin-top: var(--s3); } .sheet-sticky { position: sticky; top: calc(-1 * var(--s3)); z-index: 1; margin: calc(-1 * var(--s3)) calc(-1 * var(--x)) 0; padding: var(--s3) var(--x) 0; border-bottom: 1px solid var(--line); background: var(--bg); } .sheet-sticky .search { border-bottom: 0; } .model-default { margin: var(--s2) 0 0; } @media (min-width: 700px) { .sheet-layer { align-items: center; } .sheet { border: 1px solid var(--line); animation: pop 0.2s ease-out; } /* Dialogs on wide screens: no grip, no swipe. */ .sheet-grip { display: none; } } .field { margin-bottom: var(--s3); } .stack { display: flex; flex-direction: column; gap: var(--s3); } .inline-form { display: flex; align-items: center; gap: var(--s3); } .inline-form > .input { flex: 1; min-width: 0; } .button.done { color: var(--ok); } .list { display: flex; flex-direction: column; margin-top: var(--s3); } .directory-use, .directory-error { margin-top: var(--s3); } .directory-head { padding-bottom: var(--s3); } .list .row-button.mono { font-family: var(--mono); font-size: var(--fs-sm); } /* Thinking level: text tabs aligned on the inset. */ .segments { display: flex; flex-wrap: wrap; gap: var(--s4); min-width: 0; margin: 0; padding: 0; border: 0; } .segments button { height: var(--control-sm); color: var(--muted); font-size: var(--fs-sm); text-transform: capitalize; } .segments button[aria-pressed="true"] { box-shadow: inset 0 -1px var(--text); color: var(--text); font-weight: var(--fw-strong); } .model-list section h3 { padding-top: var(--s4); } .model-row strong, .artifact-row strong { display: block; overflow: hidden; font-size: var(--fs-md); font-weight: var(--fw-medium); text-overflow: ellipsis; white-space: nowrap; } .model-row small, .artifact-row small { display: block; color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); } .model-row > span, .artifact-row > span { display: block; } .model-row > svg { flex: none; } .artifact-row > small { flex: none; } /* Provider login */ .login-sheet { max-width: 480px; } .provider-detail, .login-flow, .login-step { display: flex; flex-direction: column; gap: var(--s3); } .login-actions { display: flex; flex-direction: column; } .login-action { justify-content: space-between; } .provider-result { color: var(--ok); font-size: var(--fs-sm); } .code-box { padding: var(--s3) 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); font-family: var(--mono); font-size: 24px; letter-spacing: 0.15em; text-align: center; user-select: all; } .guest-row { justify-content: space-between; } .guest-row > span:first-child { min-width: 0; overflow-wrap: anywhere; } .forward-row > .inline-form { flex: none; } .inline-form > .forward-port { flex: 0 0 5.5rem; } .forward { padding-bottom: var(--s2); } .forward-public { padding-bottom: var(--s1); } .forward-link { min-width: 0; overflow-wrap: anywhere; } /* Artifact viewer */ .artifact-list { display: flex; flex-direction: column; } .viewer-layer { position: fixed; inset: 0; z-index: 40; padding: var(--safe-top) 0 var(--safe-bottom); background: var(--bg); } .viewer-panel { display: flex; flex-direction: column; height: 100%; } .viewer-head { gap: var(--s2); padding-left: var(--x); } .viewer-head > div { flex: 1; min-width: 0; } .viewer-head strong, .viewer-head span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .viewer-head strong { font-size: var(--fs-md); } .viewer-head span { color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); } .viewer-content { flex: 1; min-height: 0; } .viewer-content.preview iframe { width: 100%; height: 100%; border: 0; background: white; } .viewer-content.document { width: 100%; max-width: 900px; margin: 0 auto; overflow: auto; padding: var(--s5) var(--x) calc(2 * var(--s5)); } .viewer-content.media { display: grid; place-items: center; overflow: auto; padding: var(--s4) var(--x); } .viewer-content.media .file-media { max-height: 100%; } /* A plain-text document wraps; code blocks inside rendered Markdown keep their lines and scroll sideways. */ .viewer-content.document > pre { margin: 0; font-family: var(--mono); font-size: var(--fs-sm); overflow-wrap: anywhere; white-space: pre-wrap; } .artifact-diagnostics { flex: none; max-height: 38%; border-top: 1px solid var(--line); } .artifact-diagnostics > button { display: flex; justify-content: space-between; gap: var(--s3); width: 100%; padding: var(--s2) var(--x); color: var(--muted); font-size: var(--fs-xs); font-weight: var(--fw-strong); } .artifact-diagnostics.has-errors > button { color: var(--bad); } .artifact-diagnostics > div { max-height: 28vh; overflow: auto; padding: 0 var(--x) var(--s3); font-family: var(--mono); font-size: var(--fs-xs); } .artifact-diagnostics p { overflow-wrap: anywhere; white-space: pre-wrap; } .artifact-diagnostics p + p { margin-top: var(--s1); } .artifact-diagnostics strong { text-transform: uppercase; } /* Cost panel */ .cost-sheet { max-width: 760px; } .cost-summary { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); margin-top: var(--s3); color: var(--muted); font-family: var(--mono); font-size: var(--fs-xs); } .cost-summary strong { color: var(--text); font-size: var(--fs-md); } /* Cost and cache hit rate share one x axis: each column is one response across both rows. */ .cost-chart { --cost-h: 112px; --caption-h: 28px; --cache-h: 56px; position: sticky; top: calc(-1 * var(--s3)); z-index: 2; margin: var(--s3) calc(-1 * var(--x)) var(--s4); padding: var(--s3) var(--x) var(--s2); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--bg); } .cost-chart figcaption, .cost-chart-caption { color: var(--muted); font-size: var(--fs-xs); font-weight: var(--fw-strong); } .cost-chart figcaption { margin-bottom: var(--s2); } .cost-chart-body { display: grid; grid-template-rows: var(--cost-h) var(--caption-h) var(--cache-h) auto; grid-template-columns: 64px minmax(0, 1fr); column-gap: var(--s2); } /* Spans both columns over the plot's empty middle row. */ .cost-chart-caption { position: relative; z-index: 1; grid-row: 2; grid-column: 1 / -1; align-self: end; padding-bottom: var(--s1); pointer-events: none; } .cost-y-axis { display: flex; grid-row: 1; grid-column: 1; flex-direction: column; justify-content: space-between; color: var(--faint); font-family: var(--mono); font-size: var(--fs-xs); text-align: right; } .cache-y-axis { grid-row: 3; } .cost-plot { grid-row: 1 / -1; grid-column: 2; min-width: 0; overflow-x: auto; scrollbar-width: thin; } /* Top and middle gridlines of each chart; track borders draw the baselines. */ .cost-bars { display: flex; gap: var(--s2); min-width: max-content; background-image: linear-gradient(var(--line), var(--line)), linear-gradient(var(--line), var(--line)), linear-gradient(var(--line), var(--line)), linear-gradient(var(--line), var(--line)); background-position: 0 0, 0 calc(var(--cost-h) / 2), 0 calc(var(--cost-h) + var(--caption-h)), 0 calc(var(--cost-h) + var(--caption-h) + var(--cache-h) / 2); background-size: 100% 1px; background-repeat: no-repeat; } .cost-column { display: grid; grid-template-rows: var(--cost-h) var(--caption-h) var(--cache-h) auto; justify-items: center; width: 24px; } .cost-column:hover, .cost-column.selected { background: var(--hover); } .cost-bar-track { display: flex; grid-row: 1; align-items: flex-end; width: 12px; border-bottom: 1px solid var(--line); } .cost-bar { display: block; width: 100%; min-height: 2px; background: var(--muted); } .cost-column.selected .cost-bar { background: var(--text); } .cache-bar-track { grid-row: 3; } .cost-column small { grid-row: 4; margin-top: var(--s1); color: var(--faint); font-family: var(--mono); font-size: var(--fs-xs); } .cost-table-wrap { overflow-x: auto; margin: 0 calc(-1 * var(--x)); scrollbar-width: thin; } .cost-table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: var(--fs-xs); white-space: nowrap; } .cost-table th, .cost-table td { padding: var(--s2) var(--s2); border-bottom: 1px solid var(--line); text-align: left; } .cost-table th:first-child, .cost-table td:first-child { padding-left: var(--x); } .cost-table th:last-child, .cost-table td:last-child { padding-right: var(--x); } .cost-table th:nth-last-child(-n + 2), .cost-table td:nth-last-child(-n + 2) { text-align: right; } .cost-table th { color: var(--muted); font-weight: var(--fw-strong); } .cost-table tbody tr { scroll-margin-top: 290px; } .cost-table tbody tr.selected td { background: var(--hover); } /* Motion */ .spinner { display: inline-block; width: 12px; height: 12px; border: 1px solid var(--line); border-top-color: var(--muted); border-radius: 50%; animation: spin 0.8s linear infinite; } /* The loaded history's top edge (Earlier messages, loading, Start of conversation): one height in every state, a spinner that fades in while a page loads. */ .history-edge { display: flex; align-items: center; justify-content: center; gap: var(--s2); height: var(--control-sm); color: var(--faint); font-size: var(--fs-xs); } button.history-edge { width: 100%; color: var(--muted); transition: color 0.3s; } button.history-edge:hover:not(:disabled) { color: var(--text); } .history-edge .spinner { opacity: 0; transition: opacity 0.3s; } /* Loading is shown by the spinner and a dimmer label, not the disabled buttons' fade. */ button.history-edge:disabled { opacity: 1; } .history-edge.loading { color: var(--faint); } .history-edge.loading .spinner { opacity: 1; } /* Block-level, so auto margins centre it in any container (panels, sheets, the hero). It shows only when loading takes a moment: a fast load (a conversation kept on the device) never flashes it. */ .spinner.large { display: block; flex: none; width: 24px; height: 24px; margin: var(--s5) auto; animation: spin 0.8s linear infinite, spinner-appear 0.2s ease-out 0.4s backwards; } @keyframes spinner-appear { from { opacity: 0; } } @keyframes spin { to { transform: rotate(360deg); } } @keyframes pulse { 50% { opacity: 0.35; } } @keyframes shimmer { to { background-position: -200% 0; } } @keyframes slide { from { transform: translateY(100%); } } @keyframes pop { from { opacity: 0; transform: scale(0.98); } } @keyframes fade { from { opacity: 0; } } @keyframes rise { from { opacity: 0; transform: translateY(6px); } } @media (prefers-reduced-motion: reduce) { .drawer, .drawer-scrim, .sheet, .scrim, .toast, .to-bottom { animation: none; transition: none; } } /* Envs panel. */ .env-form .field { display: flex; flex-direction: column; margin-bottom: 0; } .env-row { display: flex; gap: var(--s3); } .env-row > .field { flex: 1; min-width: 0; } .env-row > .env-port { flex: 0 0 6rem; } .check { display: flex; align-items: flex-start; gap: var(--s2); font-size: var(--fs-sm); } .env-test { display: flex; flex-direction: column; gap: var(--s2); margin-top: var(--s3); } .env-test p { margin: 0; } .env-fingerprint { overflow-wrap: anywhere; } /* Location picker: loading state while a (remote) listing arrives. */ .directory-loading { display: flex; align-items: center; gap: var(--s2); padding: var(--s3) 0; color: var(--muted); font-size: var(--fs-sm); } .directory-busy > .row-button { opacity: 0.5; } .directory-note { margin: var(--s2) 0 0; } /* History tool cards (plugins/history): the entries found or read, each revealing itself in the transcript. */ .history-hits { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--s1); font-size: var(--fs-sm); } .history-snippet { color: var(--muted); overflow-wrap: anywhere; }