/* Colours. Components use only the semantic --color-* tokens below; a theme is the set of values
   they take. js/theme.js puts data-theme="light|dark" on <html> before the first paint (the
   Settings drawer picks it), so a new colour means a new token here, in both themes. */
:root {
    --message-font-size: 0.93rem;
    font-family: Inter, ui-sans-serif, system-ui, sans-serif;
    background: var(--color-bg);
    color: var(--color-text);
    /* Lifetime of the transient accent line under a just-arrived assistant message.
       Home.razor's BranchLeafLineLifetime must stay >= the sum of these three, or the
       class is dropped mid-animation and the line vanishes with a jump. */
    --branch-leaf-draw: 0.6s;
    --branch-leaf-hold: 5s;
    --branch-leaf-fade: 0.8s;
    /* Native checkboxes, radios, ranges and progress bars take the accent the user picked. */
    accent-color: var(--color-accent-strong);
}

.host-gate { max-width: 54rem; margin: 8vh auto; padding: 1.5rem; }
.host-gate h1 { margin: .5rem 0; }
.host-gate > p { color: var(--color-text-muted); }
.host-gate button, .host-gate select { padding: .55rem .8rem; font: inherit; }
.host-gate button { cursor: pointer; }
.host-gate-downloads { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1rem; margin: 2rem 0 1.2rem; }
.host-gate-downloads section { border: 1px solid var(--color-border-strong); background: var(--color-surface); border-radius: .8rem; padding: 1.2rem; }
.host-gate-downloads h2 { margin-top: 0; font-size: 1.1rem; }
.host-gate-downloads a { display: inline-block; margin-top: .5rem; padding: .55rem .8rem; background: var(--color-accent-strong); color: var(--color-bg); border-radius: .4rem; text-decoration: none; }
.host-gate label { margin-right: .6rem; }
.host-gate-error { color: var(--color-danger-text); }

/* Selected text carries the accent too; a tint rather than a fill, so it reads in either theme. */
::selection { background: color-mix(in srgb, var(--color-accent-strong) 35%, transparent); }

:root,
:root[data-theme="dark"] {
    color-scheme: dark;
    /* Surfaces, from the deepest to the most raised. */
    --color-bg-sunken: #111111;
    --color-bg-inset: #151515;
    --color-bg: #171717;
    --color-surface: #1c1c1c;
    --color-surface-raised: #242424;
    --color-input-bg: #121212;
    /* Fills: rows, chips and buttons at rest, hovered, pressed. */
    --color-fill: #292929;
    --color-fill-hover: #2b2b2b;
    --color-fill-active: #333333;
    --color-fill-strong: #3a3a3a;
    --color-fill-stronger: #444444;
    /* Translucent layers for elements that sit on more than one surface. */
    --color-overlay-faint: rgb(255 255 255 / 2%);
    --color-overlay-hover: rgb(255 255 255 / 6%);
    --color-overlay-active: rgb(255 255 255 / 9%);
    --color-overlay-sunken: rgb(0 0 0 / 28%);
    /* The one high-contrast control (Send) and what sits on it. */
    --color-inverse: #f2f2f2;
    --color-inverse-hover: #ffffff;
    --color-inverse-active: #d4d4d4;
    --color-on-inverse: #202020;
    /* Text. text-faint is the lowest level that still reads (4.5:1); text-disabled is below it
       on purpose and is for placeholders and disabled controls only. */
    --color-text-strong: #ffffff;
    --color-text: #ececec;
    --color-text-secondary: #d6d6d6;
    --color-text-muted: #aaaaaa;
    --color-text-subtle: #959595;
    --color-text-faint: #858585;
    --color-text-disabled: #707070;
    /* Borders. */
    --color-border-subtle: #292929;
    --color-border: #383838;
    --color-border-strong: #484848;
    --color-border-stronger: #5c5c5c;
    --color-border-translucent: rgb(255 255 255 / 8%);
    /* Accent: interactive, focus, unread. */
    --color-accent: #7eb4ed;
    --color-accent-strong: #4f91dc;
    --color-accent-text: #bcd7f5;
    --color-accent-bg: #252d38;
    --color-accent-bg-hover: #2f3d4f;
    --color-accent-bg-active: #35465b;
    --color-accent-fill: #345f91;
    --color-on-accent: #ffffff;
    /* Status colours: the base one is for icons and dots, -text for words. */
    --color-danger: #e0665f;
    --color-danger-text: #ffabab;
    --color-danger-text-strong: #ff8a8a;
    --color-danger-bg: #3b292a;
    --color-danger-bg-hover: #4d3031;
    --color-danger-bg-active: #603838;
    --color-danger-border: #704346;
    --color-danger-border-strong: #8c5054;
    --color-warning: #d9a441;
    --color-warning-text: #e5c47a;
    --color-success: #5dc97c;
    --color-success-text: #a6dcb1;
    --color-success-border: #39744b;
    /* A finished run with an unread response. Its own token, not the accent: the accent is the
       user's to pick, and must not make "done" read like "needs you" or "failed". */
    --color-unread: #4f91dc;
    /* The user's own messages: a tint of the accent the user picked, with a hairline of it, so a
       question stands apart from the answers without shouting. -inset is for what sits inside
       the bubble (code blocks, reference chips) and has to read on any accent. */
    --color-user-bubble: color-mix(in srgb, var(--color-accent-strong) 16%, var(--color-bg));
    --color-user-bubble-border: color-mix(in srgb, var(--color-accent-strong) 30%, transparent);
    --color-user-bubble-inset: rgb(0 0 0 / 28%);
    --color-diff-added-bg: color-mix(in srgb, var(--color-success) 10%, transparent);
    --color-diff-removed-bg: color-mix(in srgb, var(--color-danger) 10%, transparent);
    /* Search hits and the line under a review comment. */
    --color-highlight-bg: #e6e6e6;
    --color-highlight-fg: #202020;
    /* Context window layers (the ring around the send button). Categorical, so they stay apart
       from the status colours; the message being typed takes the user's accent. */
    --color-context-instructions: #9a9a94;
    --color-context-tools: #9d95e8;
    --color-context-history: #3fb58e;
    --color-context-draft: var(--color-accent-strong);
    --color-context-reserve: #d9a441;
    --color-context-overhead: #6f6f6a;
    /* Shadow and backdrop alphas are written for the dark theme and scaled here. */
    --shadow-scale: 1;
    --scrim-scale: 1;
}

:root[data-theme="light"] {
    color-scheme: light;
    --color-bg-sunken: #f3f3f3;
    --color-bg-inset: #f6f6f6;
    --color-bg: #ffffff;
    --color-surface: #fafafa;
    --color-surface-raised: #ffffff;
    --color-input-bg: #ffffff;
    --color-fill: #f0f0f0;
    --color-fill-hover: #e9e9e9;
    --color-fill-active: #e2e2e2;
    --color-fill-strong: #d9d9d9;
    --color-fill-stronger: #cccccc;
    --color-overlay-faint: rgb(0 0 0 / 2%);
    --color-overlay-hover: rgb(0 0 0 / 5%);
    --color-overlay-active: rgb(0 0 0 / 8%);
    --color-overlay-sunken: rgb(0 0 0 / 4%);
    --color-inverse: #1f1f1f;
    --color-inverse-hover: #000000;
    --color-inverse-active: #3a3a3a;
    --color-on-inverse: #ffffff;
    --color-text-strong: #0f0f0f;
    --color-text: #1f1f1f;
    --color-text-secondary: #3d3d3d;
    --color-text-muted: #505050;
    --color-text-subtle: #5c5c5c;
    --color-text-faint: #646464;
    --color-text-disabled: #a3a3a3;
    --color-border-subtle: #ededed;
    --color-border: #e0e0e0;
    --color-border-strong: #cfcfcf;
    --color-border-stronger: #b3b3b3;
    --color-border-translucent: rgb(0 0 0 / 10%);
    --color-accent: #1a64b8;
    --color-accent-strong: #2a78d0;
    --color-accent-text: #1b5aa0;
    --color-accent-bg: #eaf2fd;
    --color-accent-bg-hover: #dce9fa;
    --color-accent-bg-active: #cddff6;
    --color-accent-fill: #2a78d0;
    --color-on-accent: #ffffff;
    --color-danger: #d0392f;
    --color-danger-text: #b3261e;
    --color-danger-text-strong: #8f1a13;
    --color-danger-bg: #fdeceb;
    --color-danger-bg-hover: #fbdcd9;
    --color-danger-bg-active: #f6c7c2;
    --color-danger-border: #efb4ae;
    --color-danger-border-strong: #de8a82;
    --color-warning: #b57700;
    --color-warning-text: #8a5a00;
    --color-success: #1f8f4a;
    --color-success-text: #17703a;
    --color-success-border: #9ed4b0;
    --color-unread: #2a78d0;
    --color-user-bubble: color-mix(in srgb, var(--color-accent-strong) 13%, var(--color-bg));
    --color-user-bubble-border: color-mix(in srgb, var(--color-accent-strong) 22%, transparent);
    --color-user-bubble-inset: rgb(255 255 255 / 60%);
    --color-highlight-bg: #ffe08a;
    --color-highlight-fg: #1f1f1f;
    --color-context-instructions: #7d7c76;
    --color-context-tools: #6a61d0;
    --color-context-history: #16876a;
    --color-context-reserve: #b57700;
    --color-context-overhead: #b4b2a9;
    --shadow-scale: 0.3;
    --scrim-scale: 0.55;
}

/* Accent colour (Settings → Accent color). js/theme.js puts data-accent on <html>. Blue is the
   hand-tuned palette in the theme blocks above; every other accent takes one swatch per hue and
   derives the --color-accent-* tokens from it, so the two themes stay in step without a second
   table of values. The swatches are also what the Settings picker paints. */
:root {
    --accent-swatch-blue: #2a78d0;
    --accent-swatch-teal: #0b7d89;
    --accent-swatch-green: #178760;
    --accent-swatch-amber: #9e670d;
    --accent-swatch-orange: #bd5a07;
    --accent-swatch-pink: #cc3494;
    --accent-swatch-purple: #9b4fcf;
    --accent-swatch-indigo: #6b58f5;
}
:root[data-accent="teal"] { --accent-swatch: var(--accent-swatch-teal); }
:root[data-accent="green"] { --accent-swatch: var(--accent-swatch-green); }
:root[data-accent="amber"] { --accent-swatch: var(--accent-swatch-amber); }
:root[data-accent="orange"] { --accent-swatch: var(--accent-swatch-orange); }
:root[data-accent="pink"] { --accent-swatch: var(--accent-swatch-pink); }
:root[data-accent="purple"] { --accent-swatch: var(--accent-swatch-purple); }
:root[data-accent="indigo"] { --accent-swatch: var(--accent-swatch-indigo); }

:root[data-accent]:not([data-accent="blue"]) {
    --color-accent: color-mix(in srgb, var(--accent-swatch) 60%, #ffffff);
    --color-accent-strong: color-mix(in srgb, var(--accent-swatch) 80%, #ffffff);
    --color-accent-text: color-mix(in srgb, var(--accent-swatch) 30%, #ffffff);
    --color-accent-bg: color-mix(in srgb, var(--accent-swatch) 14%, var(--color-bg));
    --color-accent-bg-hover: color-mix(in srgb, var(--accent-swatch) 24%, var(--color-bg));
    --color-accent-bg-active: color-mix(in srgb, var(--accent-swatch) 32%, var(--color-bg));
    --color-accent-fill: color-mix(in srgb, var(--accent-swatch) 75%, #000000);
}

:root[data-theme="light"][data-accent]:not([data-accent="blue"]) {
    --color-accent: color-mix(in srgb, var(--accent-swatch) 82%, #000000);
    --color-accent-strong: var(--accent-swatch);
    --color-accent-text: color-mix(in srgb, var(--accent-swatch) 72%, #000000);
    --color-accent-bg: color-mix(in srgb, var(--accent-swatch) 9%, var(--color-bg));
    --color-accent-bg-hover: color-mix(in srgb, var(--accent-swatch) 15%, var(--color-bg));
    --color-accent-bg-active: color-mix(in srgb, var(--accent-swatch) 21%, var(--color-bg));
    --color-accent-fill: var(--accent-swatch);
}

.app-loading { position: fixed; inset: 0; display: grid; place-content: center; justify-items: center; gap: 1rem; background: var(--color-bg-sunken); color: var(--color-text); }
.app-loading-mark { display: block; width: 3rem; height: 3rem; }
.app-loading-copy { display: grid; justify-items: center; gap: 0.35rem; }
.app-loading-copy strong { font-size: 1.05rem; font-weight: 500; letter-spacing: 0.01em; }
.app-loading-copy span { color: var(--color-text-subtle); font-size: 0.85rem; }
.app-loading-track { width: min(15rem, 58vw); height: 0.2rem; overflow: hidden; border-radius: 1rem; background: var(--color-fill-active); }
.app-loading-track span { display: block; width: 42%; height: 100%; border-radius: inherit; background: var(--color-accent-strong); animation: app-loading-progress 1.4s ease-in-out infinite; }

@keyframes app-loading-progress { 0% { transform: translateX(-110%); } 55% { transform: translateX(95%); } 100% { transform: translateX(245%); } }

@media (prefers-reduced-motion: reduce) {
    .app-loading-track span { animation: none; }
    .app-loading-track span { width: 65%; }
}

body {
    margin: 0;
    min-width: 320px;
    min-height: 100vh;
}

.legacy-shell {
    display: none;
}

.workspace-shell {
    position: relative;
    display: grid;
    --sidebar-width: 18rem;
    --sidebar-track: var(--sidebar-width);
    /* The divider takes no column of its own: the sidebar's border is the resting line, and the
       grab strip overlaps both sides of it. */
    --left-divider: 0px;
    grid-template-columns: var(--sidebar-track) var(--left-divider) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
    width: 100vw;
    height: 100vh;
    overflow: hidden;
    background: var(--color-bg);
}

.workspace-resizer {
    grid-column: 2;
    grid-row: 1;
    position: relative;
    z-index: 5;
    justify-self: start;
    width: 9px;
    margin-left: -5px;
    cursor: col-resize;
    background: transparent;
    touch-action: none;
    outline: none;
}

/* The accent line waits a moment, so a pointer that only crosses the divider does not flash it. */
.workspace-resizer::after {
    content: "";
    position: absolute;
    inset: 0 auto 0 3px;
    width: 2px;
    background: var(--color-accent-strong);
    opacity: 0;
    transition: opacity 120ms ease 0ms;
}

.workspace-resizer:hover::after { opacity: 1; transition-delay: 150ms; }
.workspace-resizer:focus-visible::after,
.workspace-shell.is-resizing .workspace-resizer::after { opacity: 1; transition-delay: 0ms; }
.workspace-shell.is-resizing { cursor: col-resize; user-select: none; }

/* A hidden sidebar leaves a strip at the window's edge; resting on it shows the divider to drag
   the sidebar back out. The longer delay keeps a pointer passing to the screen edge from lighting it. */
.workspace-shell.sidebar-collapsed { --sidebar-track: 0px; }
.workspace-shell.sidebar-collapsed .workspace-sidebar { visibility: hidden; border-right-color: transparent; }
.workspace-shell.sidebar-collapsed .workspace-resizer { width: 6px; margin-left: 0; }
.workspace-shell.sidebar-collapsed .workspace-resizer::after { inset: 0 auto 0 0; }
.workspace-shell.sidebar-collapsed .workspace-resizer:hover::after { transition-delay: 250ms; }
.workspace-shell.sidebar-collapsed .sidebar-toggle-button { color: var(--color-text-subtle); }

.workspace-shell.sidebar-animating { transition: grid-template-columns 150ms ease; }
.workspace-shell.sidebar-animating .workspace-sidebar { transition: visibility 0s linear 150ms; }
.workspace-shell.sidebar-animating:not(.sidebar-collapsed) .workspace-sidebar { transition: none; }

@media (prefers-reduced-motion: reduce) {
    .workspace-shell.sidebar-animating { transition: none; }
}

.workspace-sidebar {
    grid-column: 1;
    grid-row: 1;
    position: relative;
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    min-height: 0;
    overflow: hidden;
    padding: 0;
    background: var(--color-bg-sunken);
}

.workspace-sidebar {
    border-right: 1px solid var(--color-border-subtle);
}

.sidebar-projects {
    /* One column for whatever marks a row — an icon, a status dot — so the labels beside them all
       start at the same x. A dot is less than half an icon's width, and without a shared slot the
       text next to it simply started wherever the mark happened to end. */
    --nav-mark: 1rem;
    /* Indent for rows nested under a selected project (chat list, directory group, child
       directory rows). Defined here once so the indent stays consistent across all three and can
       be tuned in a single place. */
    --sidebar-row-indent: 1.1rem;
    min-height: 0;
    overflow-y: auto;
    padding: 0.45rem 1rem 1rem;
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.empty-hint,
.workspace-composer span {
    color: var(--color-text-faint);
    font-size: 0.8rem;
}

/* The dot keeps its size and is centred in the shared mark slot, rather than being stretched to
   fill it — a status dot that big would read as a badge. */
.project-nav-row > .workspace-nav-item > .status-dot {
    margin-inline: calc((var(--nav-mark) - 0.35rem - 2px) / 2);
}

/* Reads as an action, not as a project: muted until pointed at, so the list below it stays the
   thing the eye lands on first. */
.new-project-action {
    border-radius: 0.5rem;
    color: var(--color-text-faint);
}

.new-project-action:hover,
.new-project-action:focus-visible {
    background: var(--color-fill-hover);
}

.new-project-action .app-icon {
    width: var(--nav-mark);
    height: var(--nav-mark);
    flex: none;
}

.workspace-nav-item {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    width: 100%;
    padding: 0.3rem 0.55rem;
    text-align: left;
    background: transparent;
    border-color: transparent;
    font-size: 0.9rem;
    line-height: 1.25;
}

.nav-item-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.project-nav-row.selected {
    background: var(--color-fill-hover);
}

.project-nav-row.dragging,
.chat-nav-row.dragging {
    opacity: 0.5;
}

.project-nav-row {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 1.6rem 1.6rem 1.6rem;
    align-items: center;
    column-gap: 0.12rem;
    border-radius: 0.5rem;
}

.project-nav-row .workspace-nav-item {
    grid-column: 1 / -1;
    grid-row: 1;
    min-width: 0;
}

/* Targeted by their own class, not sibling position: nth-of-type counted buttons among same-tag
   siblings, which silently shifted whenever the first sibling toggled between a <button> (view
   mode) and an <input> (the rename editor) — the kebab button would count as the 3rd button
   instead of the 4th, collide with the rename button's column, and force the row onto a second
   grid line. */
.project-nav-row > .project-new-chat-button { grid-column: 2; }
.project-nav-row > .project-rename-button { grid-column: 3; }
.project-nav-row > .project-menu-button { grid-column: 4; }

/* Higher specificity than the generic .icon-action size, which is declared later in this file. */
.project-nav-row > .project-action-button {
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    border-color: transparent;
    background: transparent;
    color: var(--color-text-secondary);
    opacity: 0;
    transition: opacity 120ms ease, background-color 120ms ease, color 120ms ease;
}

.project-nav-row:hover > .project-action-button,
.project-nav-row:focus-within > .project-action-button,
.project-action-button:focus-visible,
.project-menu-button.menu-open {
    opacity: 1;
}

/* Sidebar rows give the name the whole row and let the actions arrive on top of its right end.
   Three reserved button columns cost about five rem of every row to buttons that are invisible
   most of the time, so names were clipped long before they had to be. The name now spans every
   column; the buttons keep their own tracks and paint above it, over a gradient that dissolves
   the text beneath them. Nothing reflows when they appear — only opacity changes. */
.project-nav-row::after,
.chat-nav-row::after,
.directory-nav-row::after {
    content: "";
    position: absolute;
    z-index: 1;
    inset: 0 0 0 auto;
    width: 7.5rem;
    border-radius: inherit;
    background: linear-gradient(to right, transparent, var(--row-bg, var(--color-fill-hover)) 2.5rem);
    opacity: 0;
    pointer-events: none;
    transition: opacity 140ms ease;
}

/* The same states that reveal the buttons, so the fade never shows without them or vice versa. */
.project-nav-row:hover::after,
.project-nav-row:focus-within::after,
.project-nav-row:has(.menu-open)::after,
.chat-nav-row:hover::after,
.chat-nav-row:focus-within::after,
.chat-nav-row:has(.menu-open)::after,
.directory-nav-row:hover::after,
.directory-nav-row:focus-within::after,
.directory-nav-row:has(.menu-open)::after {
    opacity: 1;
}

/* The gradient ends in the row's hovered background, so every state that shows it has to paint
   that background — otherwise the fade reads as a grey patch floating on the sidebar. */
.project-nav-row:hover,
.project-nav-row:focus-within,
.chat-nav-row:focus-within,
.directory-nav-row:focus-within {
    background: var(--color-fill-hover);
}

.project-nav-row > .project-action-button,
.chat-nav-row > .chat-row-action,
.directory-nav-row > .directory-row-action,
.directory-nav-row > .directory-add-button,
.directory-nav-row > .directory-lock-indicator {
    position: relative;
    z-index: 2;
    grid-row: 1;
}

/* Renaming focuses an input inside the row, which would otherwise count as focus-within and drop
   the fade over the text being typed. */
.project-nav-row:has(> .sidebar-inline-editor)::after,
.chat-nav-row:has(> .sidebar-inline-editor)::after,
.directory-nav-row:has(> .sidebar-inline-editor)::after {
    opacity: 0;
}

.project-nav-row:has(> .sidebar-inline-editor) > .project-action-button,
.chat-nav-row:has(> .sidebar-inline-editor) > .chat-row-action,
.directory-nav-row:has(> .sidebar-inline-editor) > .directory-row-action {
    opacity: 0;
    pointer-events: none;
}

/* A directory's access mode is still shown at all times, and it sits in the first of the three
   action slots rather than the last — so clearing it means clearing all three (3 x 1.6rem plus
   the gaps), not one button's width. Reserving less is what let an indicator land on the text. */
.directory-nav-row > .directory-row-main {
    padding-right: 5.2rem;
}

/* Shared chrome for every dropdown-style menu in the app (project/chat/branch/endpoint) —
   one look, one set of numbers, so a new menu never has to invent its own border/radius/shadow. */
.project-menu,
.chat-menu,
.branch-picker-menu,
.composer-endpoint-menu {
    padding: 0.3rem;
    border: 1px solid var(--color-border);
    border-radius: 0.65rem;
    background: var(--color-surface-raised);
    box-shadow: 0 0.7rem 1.8rem rgb(0 0 0 / calc(40% * var(--shadow-scale)));
}

.project-menu > button,
.chat-menu > button,
.branch-picker-menu button,
.composer-endpoint-menu button {
    gap: 0.45rem;
    padding: 0.45rem 0.6rem;
}

.project-menu {
    position: fixed;
    z-index: 30;
    top: 8.6rem;
    left: min(calc(var(--sidebar-width) - 1rem), calc(100vw - 18rem));
    display: grid;
    width: 15rem;
}

.project-menu > button {
    display: flex;
    align-items: center;
    border-color: transparent;
    background: transparent;
    text-align: left;
}

.project-menu > button .app-icon {
    flex: none;
    color: var(--color-text-muted);
}

.project-menu > button.menu-danger .app-icon {
    color: inherit;
}

.project-menu > button:hover .app-icon { color: var(--color-accent); }

.project-menu .menu-danger { color: var(--color-danger-text); }

.menu-danger:hover:not(:disabled) { color: var(--color-danger-text-strong); }

.project-menu-confirm {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 0.35rem;
    align-items: center;
    padding: 0.45rem;
    color: var(--color-danger-text);
    font-size: 0.8rem;
}

.modal-backdrop {
    position: fixed;
    z-index: 40;
    inset: 0;
    display: grid;
    place-items: center;
    padding: 1.5rem;
    background: rgb(0 0 0 / calc(58% * var(--scrim-scale)));
}

@media (height <= 36rem) {
    .modal-backdrop {
        padding: 0.75rem;
    }
}

/* Project settings reuse the permissions drawer; it has no tabs row. */
.permissions-drawer.project-settings-drawer { grid-template-rows: auto minmax(0, 1fr) auto; width: min(33rem, 100vw); overflow: hidden; }
.permissions-drawer.project-settings-drawer > header { align-items: center; padding: 1rem 1.25rem; }
.project-settings-heading { min-width: 0; }
.project-settings-heading p { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.project-settings-drawer .project-settings-content { min-width: 0; padding: 1.1rem 1.25rem 1.5rem; overflow-x: hidden; }

.settings-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 0.8rem;
    padding-top: 0.8rem;
    border-top: 1px solid var(--color-border);
}

.settings-modal-actions button {
    min-width: 5.5rem;
    height: auto;
}

.workspace-chat-list {
    position: relative;
    display: grid;
    gap: var(--sidebar-row-gap, 0.05rem);
    margin: var(--sidebar-group-gap, 0.1rem) 0 0.35rem var(--sidebar-row-indent, 0.65rem);
}

/* Reordering pinned chats (chatListDrag.js). The whole row is the handle; a long press starts the
   drag on touch, so the platform's own long-press callout has to stay out of the way. */
.workspace-chat-list .chat-nav-row[data-chat-id] { -webkit-touch-callout: none; }
.workspace-chat-list.chat-list-dragging { user-select: none; cursor: grabbing; }
.workspace-chat-list.chat-list-dragging .chat-nav-row { cursor: grabbing; }

/* Closes the pinned zone: dropping above it pins or places, dropping below it unpins. */
.chat-pin-divider {
    height: 1px;
    margin: 0.2rem 0.45rem;
    background: var(--color-border-subtle);
}

/* No chat is pinned yet: the zone exists only while dragging, as somewhere to drop. */
.chat-pin-divider.empty {
    height: auto;
    margin: 0 0 0.15rem;
    padding: 0.3rem 0.6rem;
    border: 1px dashed var(--color-border-strong);
    border-radius: 0.45rem;
    background: none;
    color: var(--color-text-muted);
    font-size: 0.78rem;
    text-align: center;
}

.chat-drop-line {
    position: absolute;
    left: 0.2rem;
    right: 0.2rem;
    height: 2px;
    margin-top: -1px;
    border-radius: 1px;
    background: var(--color-accent);
    pointer-events: none;
}

.chat-drag-ghost {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    min-width: 0;
    border-radius: 0.45rem;
    background: var(--color-surface-raised);
    box-shadow: 0 0.4rem 1.2rem rgb(0 0 0 / calc(35% * var(--shadow-scale)));
    opacity: 0.95;
    pointer-events: none;
}

.chat-drag-ghost > .chat-link { flex: 1 1 auto; min-width: 0; }

.chat-drag-hint {
    flex: none;
    padding: 0 0.6rem;
    color: var(--color-accent-text);
    font-size: 0.75rem;
}

.chat-drag-hint:empty { display: none; }

/* Placeholder rows, not a spinner: they carry the geometry of the real rows (mark slot, then
   label), so the list arrives into the shape already on screen instead of pushing it down. Shown
   only while there is nothing to show — see DelayedBusyIndicator for when that is. */
.sidebar-skeleton {
    display: grid;
    gap: var(--sidebar-row-gap, 0.05rem);
}

.sidebar-skeleton-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.55rem;
}

.sidebar-skeleton-mark,
.sidebar-skeleton-label {
    background: linear-gradient(90deg, var(--color-surface-raised) 0%, var(--color-fill-active) 50%, var(--color-surface-raised) 100%);
    background-size: 220% 100%;
    animation: sidebar-skeleton-shimmer 1.3s ease-in-out infinite;
}

.sidebar-skeleton-mark {
    flex: 0 0 auto;
    width: var(--nav-mark, 1rem);
    height: var(--nav-mark, 1rem);
    border-radius: 50%;
}

/* Uneven, like titles are: a column of identical bars reads as a broken table rather than as a
   list that is still arriving. */
.sidebar-skeleton-label {
    height: 0.55rem;
    border-radius: 1rem;
}

.sidebar-skeleton-row:nth-child(4n + 1) .sidebar-skeleton-label { width: 62%; }
.sidebar-skeleton-row:nth-child(4n + 2) .sidebar-skeleton-label { width: 44%; }
.sidebar-skeleton-row:nth-child(4n + 3) .sidebar-skeleton-label { width: 71%; }
.sidebar-skeleton-row:nth-child(4n + 4) .sidebar-skeleton-label { width: 51%; }

@keyframes sidebar-skeleton-shimmer {
    0% { background-position: 120% 0; }
    100% { background-position: -120% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .sidebar-skeleton-mark,
    .sidebar-skeleton-label {
        background: var(--color-fill-hover);
        animation: none;
    }
}

/* "Show N more" takes the row; "Less" only claims the width of its own label, so collapsing
   stays available without competing with the primary action for the eye. */
.chat-list-pager {
    display: flex;
    align-items: center;
    gap: 0.15rem;
}

/* The pager reads as an action inside the chat list, not as another chat: muted like the
   new-project action, and its label starts where chat titles do so the list keeps one text
   edge instead of gaining a second one. */
.chat-list-more,
.chat-list-less {
    padding-left: calc(0.55rem + var(--nav-mark, 1rem) + 0.4rem);
    border-radius: 0.45rem;
    color: var(--color-text-faint);
    font-size: 0.8rem;
}

.chat-list-less {
    width: auto;
    flex: 0 0 auto;
    padding-left: 0.55rem;
}

.chat-list-more:hover,
.chat-list-more:focus-visible,
.chat-list-less:hover,
.chat-list-less:focus-visible {
    background: var(--color-fill-hover);
    color: var(--color-text-secondary);
}

/* Same badge shape as the per-chat branch count, so "something below the fold wants you" is
   said in the vocabulary the rows already use. */
.chat-list-more-attention {
    display: grid;
    min-width: 0.9rem;
    height: 0.82rem;
    box-sizing: border-box;
    place-items: center;
    margin-left: auto;
    padding: 0 0.2rem;
    border-radius: 0.45rem;
    background: var(--color-fill-active);
    color: var(--color-warning);
    font-size: 0.6rem;
    line-height: 1;
}

.global-nav {
    display: grid;
    gap: 0.05rem;
    margin-bottom: 0.5rem;
}

.root-nav {
    margin-top: 0.5rem;
}

.sidebar-global-nav {
    margin: 0;
    padding: 0.65rem 1rem 0.75rem;
    border-top: 1px solid var(--color-border-subtle);
    background: var(--color-bg-sunken);
}

.root-nav .workspace-nav-item {
    gap: 0.55rem;
}

.root-nav .app-icon {
    width: 1rem;
    height: 1rem;
    color: var(--color-text-muted);
}

.root-nav .selected .app-icon {
    color: var(--color-accent);
}


.global-nav .selected {
    background: var(--color-fill-hover);
}

.section-heading p,
.settings-list small {
    color: var(--color-text-faint);
}

.section-heading {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    justify-content: space-between;
}

/* Connections and MCP share the permissions drawer chrome but need room for a list beside a
   form, so the drawer is wider and the two columns stack once the drawer itself gets narrow. */
.permissions-drawer.settings-drawer { grid-template-rows: auto minmax(0, 1fr) auto; width: min(54rem, calc(100vw - 3rem)); }
.settings-drawer-content { min-height: 0; overflow: hidden; container-type: inline-size; }
.permissions-drawer.settings-drawer > footer { min-height: 2.1rem; }
.settings-drawer-error { color: var(--color-danger-text-strong); }

/* Settings: preferences of this device, one group per topic, each a row of choices. */
.app-settings { display: grid; align-content: start; gap: 1.25rem; height: 100%; padding: 1rem 1.2rem; box-sizing: border-box; overflow-y: auto; }
.app-settings-group { display: grid; gap: 0.6rem; margin: 0; padding: 0; border: 0; }
.app-settings-group > legend { margin-bottom: 0.6rem; padding: 0; color: var(--color-text-secondary); font-size: 0.9rem; font-weight: 500; }
.theme-options { display: inline-flex; justify-self: start; gap: 0.25rem; padding: 0.25rem; border: 1px solid var(--color-border); border-radius: 0.6rem; background: var(--color-surface); }
/* The radio stays in the tab order for keyboard users, but the pill is what shows the choice. */
.theme-option { position: relative; display: block; padding: 0.4rem 0.95rem; border-radius: 0.4rem; color: var(--color-text-muted); font-size: 0.85rem; cursor: pointer; }
.theme-option:hover { background: var(--color-fill-hover); color: var(--color-text); }
.theme-option.selected { background: var(--color-fill-strong); color: var(--color-text-strong); }
.theme-option > input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; opacity: 0; cursor: pointer; }
.theme-option:has(> input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: 1px; }
/* Accent picker: a row of swatches; the chosen one carries a white square, as a checkmark would. */
.accent-options { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.accent-option { position: relative; display: grid; place-items: center; width: 2rem; height: 2rem; border-radius: 0.45rem; background: var(--swatch); cursor: pointer; }
.accent-option:hover { box-shadow: 0 0 0 2px var(--color-border-stronger); }
.accent-option.selected::after { content: ""; width: 0.8rem; height: 0.8rem; border-radius: 0.2rem; background: #ffffff; }
.accent-option > input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; opacity: 0; cursor: pointer; }
.accent-option:has(> input:focus-visible) { outline: 2px solid var(--color-text); outline-offset: 2px; }

.global-editor-layout {
    display: grid;
    grid-template-columns: minmax(10rem, 15rem) minmax(0, 1fr);
    gap: 1.25rem;
    height: 100%;
    min-height: 0;
    padding: 1rem 1.2rem;
    box-sizing: border-box;
    overflow: hidden;
}

.global-editor-layout > .settings-list-column,
.global-editor-layout > .global-form {
    min-height: 0;
    overflow-y: auto;
}

@container (width <= 36rem) {
    .global-editor-layout {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto auto;
        height: auto;
        max-height: 100%;
        overflow-y: auto;
    }

    .global-editor-layout > .settings-list-column,
    .global-editor-layout > .global-form {
        overflow: visible;
    }

    /* When the two columns stack, the list must not claim all remaining height — that would
       push the form out of the drawer. Falls back to the original "size to content". */
    .settings-list-column {
        grid-template-rows: auto auto;
    }

    .settings-list {
        max-height: 12rem;
    }
}

/* First row takes every free pixel of the column so a long Connections or MCP list reaches the
   bottom of the drawer; the second row stays the same height as the Add button and stays in
   place. minmax(0, 1fr) lets the cell shrink to the content rather than overflow when the drawer
   itself is too short to host both rows comfortably. */
.settings-list-column {
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    align-content: stretch;
    gap: 0.5rem;
    min-height: 0;
}

.settings-list-add {
    justify-self: start;
}

.settings-list,
.global-form {
    display: grid;
    align-content: start;
    gap: 0.5rem;
    overflow-y: auto;
}

/* No fixed cap here: the 1fr row in .settings-list-column already gives the list a height, and
   the drawer stays the one source of truth for "how tall can the list get". Keeping a numeric
   cap would re-introduce the empty space below the list and push Add away from the bottom. */

.settings-list-select {
    display: grid;
    width: 100%;
    gap: 0.2rem;
    text-align: left;
    border-color: transparent;
    background: transparent;
}

.settings-list-item {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    border-radius: 0.6rem;
}

.settings-list-item.selected {
    background: var(--color-fill-active);
}

.settings-item-remove {
    margin-right: 0.4rem;
    border-color: transparent;
    background: transparent;
}

.global-form {
    padding-right: 0.5rem;
}

.connection-flags {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    align-items: center;
}

.directory-settings {
    display: grid;
    gap: 0.65rem;
    margin-top: 1rem;
}

.section-heading h3,
.section-heading p {
    margin: 0;
}

.directory-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 9rem auto;
    gap: 0.5rem;
    align-items: center;
}

.directory-row input,
.directory-row select,
.directory-row .icon-action,
.directory-settings .section-heading .icon-action {
    height: 2.7rem;
    min-height: 2.7rem;
    box-sizing: border-box;
}

.directory-row .icon-action,
.directory-settings .section-heading .icon-action {
    width: 2.7rem;
}

.chat-nav-row {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 1.6rem 1.6rem 1.6rem;
    min-width: 0;
    align-items: center;
    column-gap: 0.12rem;
    border-radius: 0.45rem;
}

.mcp-tool-policies {
    display: grid;
    gap: 0.65rem;
    margin-top: 0.6rem;
    padding-top: 1rem;
    border-top: 1px solid var(--color-border);
}

.mcp-tool-policies .section-heading > div {
    display: grid;
    gap: 0.2rem;
}

.mcp-tool-policies .section-heading button {
    white-space: nowrap;
}

.mcp-tool-policy-row {
    display: grid;
    grid-template-columns: minmax(8rem, 1fr) 7rem 6rem 7rem;
    gap: 0.5rem;
    align-items: end;
    padding: 0.65rem;
    border: 1px solid var(--color-border);
    border-radius: 0.6rem;
    background: var(--color-surface-raised);
}

.mcp-tool-name {
    display: grid;
    min-width: 0;
    gap: 0.2rem;
}

.mcp-tool-name > span,
.mcp-tool-policy-row label {
    color: var(--color-text-muted);
    font-size: 0.72rem;
}

.mcp-tool-name strong {
    display: flex;
    align-items: center;
    min-width: 0;
    height: 2.7rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mcp-tool-policy-row label {
    gap: 0.2rem;
}

.mcp-tool-policy-row input,
.mcp-tool-policy-row select {
    min-width: 0;
    height: 2.7rem;
    box-sizing: border-box;
}

.mcp-policy-note {
    margin: 0;
    color: var(--color-text-faint);
    font-size: 0.78rem;
}

@container (width <= 46rem) {
    .mcp-tool-policies .section-heading {
        display: grid;
        justify-items: start;
    }

    .mcp-tool-policy-row {
        grid-template-columns: minmax(8rem, 1fr) minmax(7rem, 1fr);
    }
}

.chat-nav-row:hover {
    background: var(--color-fill-hover);
}

/* The open chat is "you are here", not a hover that stuck: an accent tint keeps it apart from
   the row under the pointer. --row-bg carries the tint into the fade under the row buttons. */
.chat-nav-row.selected { --row-bg: var(--color-accent-bg-hover); background: var(--row-bg); }
.chat-nav-row.selected:is(:hover, :focus-within, :has(.menu-open)) { --row-bg: var(--color-accent-bg-active); }
.chat-nav-row.selected .nav-item-label { color: var(--color-text-strong); }

.chat-nav-row .workspace-nav-item {
    grid-column: 1 / -1;
    grid-row: 1;
    min-width: 0;
}

.chat-nav-row > .chat-pin-button { grid-column: 2; }
.chat-nav-row > .chat-row-action:not(.chat-menu-button):not(.chat-pin-button) { grid-column: 3; }
.chat-nav-row > .chat-menu-button { grid-column: 4; }

.sidebar-inline-editor {
    grid-column: 1 / -1;
    grid-row: 1;
    min-width: 0;
    height: 1.75rem;
    padding: 0.2rem 0.45rem;
    border: 1px solid var(--color-accent-strong);
    border-radius: 0.4rem;
    background: var(--color-input-bg);
    color: var(--color-text);
    font: inherit;
    font-size: 0.9rem;
}

.branch-nav-row {
    margin-left: calc(min(var(--branch-depth), 3) * 1rem);
    width: calc(100% - (min(var(--branch-depth), 3) * 1rem));
}

.branch-tree-line {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    align-self: stretch;
    width: 0.75rem;
    margin-left: -0.5rem;
    border-left: 1px solid var(--color-border-strong);
    border-bottom: 1px solid var(--color-border-strong);
    border-bottom-left-radius: 0.35rem;
}

.branch-link {
    display: flex;
    align-items: center;
    gap: 0.45rem;
}

.branch-link .app-icon {
    flex: 0 0 auto;
}

.branch-link .nav-item-label {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Higher specificity than the generic .icon-action size, which is declared later in this file. */
.chat-nav-row > .chat-row-action {
    flex: 0 0 auto;
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    border-color: transparent;
    background: transparent;
    opacity: 0;
    transition: opacity 120ms ease, background-color 120ms ease, color 120ms ease;
}

.chat-nav-row:hover .chat-row-action,
.chat-nav-row:focus-within .chat-row-action,
.chat-row-action:focus-visible,
.chat-menu-button.menu-open {
    opacity: 1;
}

/* Revealed like every other row action, pinned or not. Whether a chat is pinned is said by its
   leading icon and by its position — the list already sorts pinned chats to the top — so this
   button no longer has to be a permanent indicator as well as a control. The pin/unpin toggle
   stays in the muted row-action colour regardless of state, so it reads like every other action
   on the row instead of being promoted to an "active" accent colour. */
.chat-nav-row > .chat-pin-button {
    color: var(--color-text-faint);
}

.directory-nav-group {
    display: grid;
    gap: var(--sidebar-row-gap, 0.05rem);
    margin: var(--sidebar-group-gap, 0.1rem) 0 0 var(--sidebar-row-indent, 0.65rem);
}

.directory-nav-row {
    position: relative;
    box-sizing: border-box;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 1.6rem 1.6rem 1.6rem;
    align-items: center;
    column-gap: 0.12rem;
    font-size: 0.9rem;
    border-radius: 0.45rem;
}

.directory-nav-row:hover {
    background: var(--color-fill-hover);
}

.directory-empty-action {
    min-height: 1.85rem;
    border-radius: 0.45rem;
    color: var(--color-text-subtle);
}

.directory-empty-action:hover:not(:disabled),
.directory-empty-action:focus-visible {
    background: var(--color-fill-hover);
}

.directory-empty-action .app-icon {
    color: var(--color-text-muted);
}

.directory-nav-row-child {
    margin-left: var(--sidebar-row-indent, 0.65rem);
    width: calc(100% - var(--sidebar-row-indent, 0.65rem));
}

/* The label-ish content (toggle/folder/name/count) shares grid column 1 as a plain flex
   row, so the trailing action buttons keep the same fixed columns as project/chat rows. */
.directory-row-main {
    grid-column: 1 / -1;
    grid-row: 1;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    min-width: 0;
    min-height: 1.85rem;
    box-sizing: border-box;
    padding: 0.3rem 0.55rem;
    overflow: hidden;
}

.directory-label {
    flex: none;
    color: var(--color-text-muted);
}

.directory-name {
    flex: 1 1 auto;
    min-width: 0;
}

.directory-count {
    flex: none;
    color: var(--color-text-faint);
    font-size: 0.75rem;
}

.directory-row-main > .directory-toggle {
    flex: none;
    width: 1rem;
    height: 1rem;
    padding: 0;
    border-color: transparent;
    background: transparent;
}

.directory-toggle .app-icon {
    width: 0.75rem;
    height: 0.75rem;
    transition: transform 120ms ease;
}

.directory-toggle[aria-expanded="true"] .app-icon {
    transform: rotate(90deg);
}

/* Higher specificity than the generic .icon-action size, which is declared later in this file. */
.directory-nav-row > .directory-row-action {
    flex: 0 0 auto;
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    border-color: transparent;
    background: transparent;
    opacity: 0;
    transition: opacity 120ms ease, background-color 120ms ease, color 120ms ease;
}

.directory-nav-row:hover > .directory-row-action,
.directory-nav-row:focus-within > .directory-row-action,
.directory-row-action:focus-visible,
.directory-menu-button.menu-open {
    opacity: 1;
}

/* Section-level add actions remain discoverable; only row-context actions are progressive. */
.directory-nav-row-header > .directory-add-button {
    opacity: 1;
}

/* Mirrors the add(+)/edit/menu column layout shared by project and chat rows, so the
   action buttons line up in the same vertical columns across all three row types. */
.directory-nav-row > .directory-add-button { grid-column: 2; }
.directory-nav-row > .directory-edit-button { grid-column: 3; }
.directory-nav-row > .directory-menu-button { grid-column: 4; }

.directory-lock-indicator {
    grid-column: 2;
    display: inline-grid;
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    place-items: center;
    border-color: transparent;
    background: transparent;
    color: var(--color-text-faint);
    transition: color 120ms ease, background-color 120ms ease;
}

.directory-lock-indicator .app-icon {
    width: 1rem;
    height: 1rem;
}

.directory-lock-indicator.directory-lock-rw {
    color: var(--color-accent);
}

.directory-lock-indicator:hover:not(:disabled),
.directory-lock-indicator:focus-visible {
    background: var(--color-fill-strong);
}

@media (hover: none), (pointer: coarse) {
    .project-nav-row.selected > .project-action-button,
    .chat-nav-row.selected > .chat-row-action,
    .directory-nav-row-child > .directory-row-action {
        opacity: 1;
    }
}

.context-menu-backdrop {
    position: fixed;
    z-index: 29;
    inset: 0;
    background: transparent;
}

.chat-menu {
    position: absolute;
    z-index: 31;
    top: calc(100% + 0.25rem);
    right: 0;
    display: grid;
    width: 14rem;
}

.chat-menu > button {
    display: flex;
    align-items: center;
    border-color: transparent;
    background: transparent;
    text-align: left;
}

.chat-menu .menu-danger { color: var(--color-danger-text); }

.chat-link {
    color: var(--color-text-muted);
}

.chat-link > .app-icon,
.chat-icon-with-branch-count > .app-icon {
    color: var(--color-text-disabled);
    transition: color 120ms ease, opacity 120ms ease;
}

.chat-link > .app-icon.run-status-generating,
.chat-icon-with-branch-count > .app-icon.run-status-generating {
    color: var(--color-text-strong);
}

.chat-link > .app-icon.run-status-unread,
.chat-icon-with-branch-count > .app-icon.run-status-unread { color: var(--color-unread); }
.chat-link > .app-icon.run-status-attention,
.chat-icon-with-branch-count > .app-icon.run-status-attention { color: var(--color-warning); }
.chat-link > .app-icon.run-status-failed,
.chat-icon-with-branch-count > .app-icon.run-status-failed { color: var(--color-danger); }
.chat-link > .app-icon.run-status-interrupted,
.chat-icon-with-branch-count > .app-icon.run-status-interrupted { color: var(--color-text-faint); }

/* The count overlays the lower-right corner of the chat icon, so it remains visible without
   taking horizontal space from the title. Its colour represents the highest-priority visible
   state among alternative branches; the chat icon keeps representing the main branch. */
.chat-icon-with-branch-count {
    position: relative;
    display: inline-flex;
    flex: 0 0 auto;
}

.chat-branch-count {
    position: absolute;
    right: -0.38rem;
    bottom: -0.28rem;
    display: grid;
    min-width: 0.78rem;
    height: 0.72rem;
    box-sizing: border-box;
    place-items: center;
    padding: 0 0.12rem;
    border: 1px solid var(--color-border-subtle);
    border-radius: 0.4rem;
    background: var(--color-fill-active);
    color: var(--color-text-muted);
    font-size: 0.56rem;
    font-weight: 600;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.chat-branch-count.run-status-generating { color: var(--color-text-strong); }
.chat-branch-count.run-status-unread { color: var(--color-unread); }
.chat-branch-count.run-status-attention { color: var(--color-warning); }
.chat-branch-count.run-status-failed { color: var(--color-danger); }
.chat-branch-count.run-status-interrupted { color: var(--color-text-faint); }

.workspace-conversation {
    position: relative;
    grid-column: 3;
    grid-row: 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto auto auto;
    min-width: 0;
    min-height: 0;
}

.compact-action {
    padding: 0.4rem 0.55rem;
    font-size: 0.78rem;
}

.close-settings {
    display: none;
    padding: 0.2rem 0.55rem;
    background: transparent;
    border-color: transparent;
    font-size: 1.4rem;
}

.app-icon {
    flex: 0 0 auto;
    width: 1rem;
    height: 1rem;
}

.icon-action {
    display: inline-grid;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    place-items: center;
}

.settings-tabs {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0.2rem;
    margin-bottom: 1rem;
    padding: 0.2rem;
    border-radius: 0.6rem;
    background: var(--color-bg-inset);
}

.settings-tabs button {
    padding: 0.45rem 0.25rem;
    border-color: transparent;
    background: transparent;
    font-size: 0.78rem;
}

.settings-tabs button.selected {
    background: var(--color-fill-active);
}

.endpoint-detail,
.settings-placeholder {
    display: grid;
    gap: 0.65rem;
}

.endpoint-detail {
    margin-top: 0.85rem;
}

.endpoint-toolbar {
    display: flex;
    gap: 0.5rem;
    align-items: end;
}

.endpoint-toolbar label {
    flex: 1;
}

.endpoint-toolbar > .icon-action {
    align-self: end;
    width: 2.7rem;
    height: 2.7rem;
    box-sizing: border-box;
}

.endpoint-toolbar > .endpoint-remove-action {
    border-color: var(--color-danger-border);
    background: var(--color-fill-active);
    color: var(--color-danger-text);
    font-size: 1.35rem;
    line-height: 1;
}

.endpoint-toolbar > .endpoint-remove-action:hover:not(:disabled) {
    background: var(--color-danger-bg-hover);
    color: var(--color-danger-text);
}

.form-actions {
    display: flex;
    gap: 0.5rem;
}

.secondary-action {
    background: transparent;
    border-color: transparent;
}

.messages-area {
    --history-rail-top: 2.9rem;
    position: relative;
    grid-row: 2;
    grid-column: 1;
    min-width: 0;
    min-height: 0;
}

.workspace-messages {
    /* Message chrome that has to fit beside a bubble asks this width, not the window's. */
    container: feed / inline-size;
    height: 100%;
    box-sizing: border-box;
    overflow-y: auto;
    /* Reserve the vertical scrollbar gutter up front, even before content overflows, so the
       measured width stays stable as the transcript grows or shrinks. chatScrollbarGutter.js
       re-measures on every layout change and writes the real value to --chat-scrollbar-width
       on .workspace-conversation, which the absolute header then uses for its right inset. */
    scrollbar-gutter: stable;
    padding: 3rem max(2rem, calc((100% - 48rem) / 2)) 1.1rem;
    /* Browser scroll anchoring: when a block above the viewport changes height — a tool group
       being expanded, an image or markdown block finishing its layout — the reading position
       stays put instead of sliding down. Stated explicitly because it is load-bearing here, not
       incidental: messageScroll.js re-anchors on top of it for the cases the browser skips. */
    overflow-anchor: auto;
}

/* Round, quiet, and only present when there is something new below (see MessageFeed): an arrow
   with an accent dot. Centre it over the chat column, immediately above the composer status
   (for example, "Continuing selected chat") and the input below that. Use auto inline margins
   rather than translateX so the hover transform remains independent. */
.messages-jump {
    position: absolute;
    z-index: 5;
    right: 0;
    bottom: 0;
    left: 0;
    display: flex;
    width: 2.25rem;
    height: 2.25rem;
    align-items: center;
    justify-content: center;
    margin-inline: auto;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    box-shadow: 0 2px 10px rgb(0 0 0 / calc(45% * var(--shadow-scale)));
    color: var(--color-text-secondary);
    transition: color 140ms ease, border-color 140ms ease, transform 140ms ease;
}

.messages-jump:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
    transform: translateY(1px);
}

.messages-jump:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

/* The dot is the whole message: something is down there. It pulses only while an answer is
   actually being written, so a finished answer waiting below stays still. */
.messages-jump-dot {
    position: absolute;
    top: 0.3rem;
    right: 0.3rem;
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 999px;
    background: var(--color-accent);
}

.messages-jump-live .messages-jump-dot {
    animation: messages-jump-pulse 1.6s ease-in-out infinite;
}

@keyframes messages-jump-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.25; }
}

@media (prefers-reduced-motion: reduce) {
    .messages-jump-live .messages-jump-dot { animation: none; }
}

/* Sits in the left gutter of the centred message column, so it never shifts the text. */
.history-markers {
    position: absolute;
    z-index: 4;
    top: var(--history-rail-top);
    bottom: 1.1rem;
    left: 0.5rem;
    display: flex;
    width: 1.1rem;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    justify-content: safe center;
    gap: 0.55rem;
    padding: 0.6rem 0;
    overflow: hidden auto;
    opacity: 0.45;
    transition: opacity 140ms ease;
    scrollbar-width: none;
}

.history-markers::-webkit-scrollbar {
    display: none;
}

.history-markers.idle {
    display: none;
}

.messages-area:hover .history-markers {
    opacity: 1;
}

/* The button is a comfortable hit target; the visible dash is drawn by ::before,
   so a 2px line never means a 2px thing to aim at. */
.history-marker {
    position: relative;
    display: flex;
    width: 1.1rem;
    height: 0.7rem;
    flex: none;
    align-items: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--color-text-disabled);
    /* Scaled rather than resized: transform stays off the layout path, so scrolling never reflows.
       The actual factor is written by historyMarkers.js as an inline transform.
       Origin is centred, not left-anchored: the hover fisheye can reach ~1.6x, and centring
       the growth keeps half of that bulge away from the message text instead of pushing the
       full amount rightward into the gutter.
       Resting scale is 1, matching what the JS writes while the cursor is off the strip, so
       there's no visible jump on first paint before the fisheye module has hooked up. */
    transform: scaleX(1);
    transform-origin: center center;
    transition: transform 120ms ease, color 120ms ease;
}

.history-marker::before {
    width: 100%;
    height: 2px;
    border-radius: 999px;
    background: currentColor;
    content: "";
    transition: height 120ms ease;
}

.history-marker-user { color: var(--color-text-secondary); }
.history-marker.active { color: var(--color-text-strong); }

/* Hover cannot use transform — the fisheye factor already owns it — so it reacts with colour
   and a thicker dash instead. */
.history-marker:hover { color: var(--color-text-strong); }
.history-marker:hover::before { height: 4px; }

/* A fork point is one stripe (::before, unchanged) plus a short upright tick to its left —
   the branch mark — rather than a second stacked dash. Vertical, not diagonal: a diagonal
   tick would visibly skew as the horizontal fisheye scale changes; a vertical one only shifts
   sideways, which reads as intentional rather than broken. */
.history-marker-fork::before {
    margin-left: 0.3rem;
    width: calc(100% - 0.3rem);
}

.history-marker-fork::after {
    position: absolute;
    left: 0;
    width: 2px;
    height: 0.5rem;
    border-radius: 999px;
    background: currentColor;
    content: "";
    transition: width 120ms ease;
}

.history-marker-fork:hover::after { width: 3px; }

/* Doubled class so state beats .active and :hover — a failed message must not read as
   "just the current one" simply because the user scrolled to it. */
.history-marker.history-marker-generating { color: var(--color-text-strong); }
.history-marker.history-marker-unread { color: var(--color-accent); }
.history-marker.history-marker-incomplete { color: var(--color-warning); }
.history-marker.history-marker-failed { color: var(--color-danger); }

/* Positioned via JS (top/left), not CSS: it lives as a sibling of .history-markers so its
   box isn't clipped by the strip's own overflow, which the strip needs for its internal scroll.
   Multi-line now (question, blank-line gap, answer) instead of a single truncated preview string,
   so it's a flex column rather than a single nowrap/ellipsis line. */
.history-marker-tip {
    position: absolute;
    z-index: 5;
    top: 0;
    left: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-width: 20rem;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 0.78rem;
    line-height: 1.35;
    opacity: 0;
    pointer-events: none;
    transform: translateY(-50%);
    box-shadow: 0 0.4rem 1.2rem rgb(0 0 0 / calc(35% * var(--shadow-scale)));
}

.history-marker-tip.visible {
    opacity: 1;
}

/* Sit right over the strip's own top/bottom edge (same left/width as .history-markers), toggled
   by historyMarkers.js alongside the fade mask — same canScrollUp/canScrollDown signal, just a
   second, more explicit form of it. Hoverable (unlike the rest of the strip's decorations) so
   resting the pointer on one scrolls the feed in that direction; pointer-events stays off while
   hidden so an invisible arrow can't steal a click/hover meant for the strip underneath it. */
.history-scroll-hint {
    position: absolute;
    z-index: 6;
    left: 0.5rem;
    width: 1.1rem;
    height: 1.2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-subtle);
    opacity: 0;
    pointer-events: none;
    cursor: default;
    transition: opacity 140ms ease, color 120ms ease, transform 140ms ease;
}

.history-scroll-hint svg {
    width: 1.1rem;
    height: 1.1rem;
}

.history-scroll-hint.visible {
    opacity: 1;
    pointer-events: auto;
}

.history-scroll-hint.visible:hover {
    color: var(--color-text-strong);
}

/* Visible confirmation that the hold-to-scroll gesture is actually running — the accent colour
   plus a touch of scale reads as "active", distinct from the plain white :hover state above. */
.history-scroll-hint.scrolling {
    color: var(--color-accent);
    transform: scale(1.15);
}

.history-scroll-hint-up {
    top: calc(var(--history-rail-top) - .25rem);
}

.history-scroll-hint-down {
    bottom: 0.85rem;
}

/* Question and answer are each clamped to 5 lines rather than truncated to a fixed character
   count — the tooltip is now big enough to show real paragraphs, not a one-line preview. Content
   is rendered (sanitized) markdown HTML, not plain text, so no white-space: pre-wrap here — real
   <p>/<br> tags carry the line breaks now, and preserving literal source whitespace on top of
   that would just add stray gaps between them. */
.history-marker-tip-question,
.history-marker-tip-answer {
    display: -webkit-box;
    display: box;
    -webkit-line-clamp: 5;
    line-clamp: 5;
    -webkit-box-orient: vertical;
    box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
}

.history-marker-tip-question > :first-child,
.history-marker-tip-answer > :first-child {
    margin-top: 0;
}

.history-marker-tip-question > :last-child,
.history-marker-tip-answer > :last-child {
    margin-bottom: 0;
}

.history-marker-tip-answer {
    color: var(--color-text-subtle);
}

/* The tip is filled with the same rendered markdown the transcript shows, so every global style
   applies to it — including the marketing hero's h1/h2 sizing, which turned a single heading into
   a banner that swallowed the whole preview. `.markdown-content` resets that, but the tip is not
   inside it.

   These rules reset it again and go further: a preview is not a document. Structure only has to
   stay recognizable within five clamped lines, so headings drop to body size and keep nothing but
   their weight, and every vertical margin is tighter than the transcript's. */
.history-marker-tip-question :is(h1, h2, h3, h4, h5, h6) {
    margin: 0.35em 0 0.1em;
    font-size: 1em;
    line-height: 1.25;
    font-weight: 600;
}

.history-marker-tip-answer :is(h1, h2, h3, h4, h5, h6) {
    margin: 0.35em 0 0.1em;
    font-size: 1em;
    line-height: 1.25;
    font-weight: 600;
    color: var(--color-text-secondary);
}

.history-marker-tip-question p,
.history-marker-tip-answer p {
    margin: 0.25em 0;
    font-size: inherit;
}

.history-marker-tip-question :is(ul, ol),
.history-marker-tip-answer :is(ul, ol) {
    margin: 0.25em 0;
    padding-left: 1.1em;
}

.history-marker-tip-question li,
.history-marker-tip-answer li {
    margin: 0.05em 0;
}

.history-marker-tip-question blockquote,
.history-marker-tip-answer blockquote {
    margin: 0.25em 0;
    padding-left: 0.5em;
    border-left: 2px solid var(--color-border);
}

/* Code keeps its monospace identity but not its box: a scrollable pre inside a tooltip nobody can
   scroll is just wasted height, so it clips with the rest of the clamped preview. */
.history-marker-tip-question :is(pre, code),
.history-marker-tip-answer :is(pre, code) {
    font-size: 0.92em;
}

.history-marker-tip-question pre,
.history-marker-tip-answer pre {
    margin: 0.25em 0;
    padding: 0.2em 0.35em;
    overflow: hidden;
    white-space: pre-wrap;
}

.history-marker-tip-question hr,
.history-marker-tip-answer hr {
    margin: 0.35em 0;
}

/* A full-size image or table would set the tip's height on its own, past anything line-clamp can
   restrain. */
.history-marker-tip-question :is(img, table),
.history-marker-tip-answer :is(img, table) {
    max-width: 100%;
    max-height: 3rem;
}

.history-marker-tip-question table,
.history-marker-tip-answer table {
    display: block;
    overflow: hidden;
    font-size: 0.92em;
}

/* Time, short date (only when the message isn't from today), and the branch badge, in that
   order, on one line above the question. */
.history-marker-tip-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.7rem;
}

.history-marker-tip-time {
    color: var(--color-text-subtle);
}

.history-marker-tip-fork {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    color: var(--color-accent);
}

.history-marker-tip-fork svg {
    flex: none;
    width: 0.85rem;
    height: 0.85rem;
}

.workspace-empty {
    display: grid;
    min-height: 100%;
    place-content: center;
    text-align: center;
}

/* The project the new chat will land in: the accent ties the name to the choice the user made. */
.workspace-empty strong { color: var(--color-accent); font-weight: 600; }

.workspace-message,
.markdown-content {
    /* Without min-width:0 in a grid/flex track, an unbreakable child (long token, wide table)
       forces the track to grow past its share and the whole page ends up horizontally
       scrollable. With it, overflow stays contained to this article. */
    min-width: 0;
    max-width: 100%;
}

.workspace-message {
    /* Long pre/table inside .markdown-content already get overflow-x:auto via .markdown-content
       > pre / > table. This is the safety net for any other content we forget to wrap.
       `clip`, not `hidden`: hidden turns the other axis into `auto` as well, so anything drawn a
       pixel past the article's bottom — the hover toolbar, centred on the last line — gave every
       message its own vertical scrollbar. Clip trims the width and leaves the height alone. */
    overflow-x: clip;
}

.workspace-message {
    position: relative;
    max-width: 48rem;
    /* Browser-native virtualization is a better fit than Blazor Virtualize here: transcript
       items have highly variable heights, while stable DOM ids are required by history markers
       and scroll restoration. Chromium can skip layout/paint for distant message contents and
       reuse the real measured height after an item has entered the viewport. */
    content-visibility: auto;
    contain-intrinsic-block-size: auto 12rem;
    /* The only vertical gap in the feed. Every other spacing here is zero on purpose: an article
       that adds its own padding turns one margin into several different-looking gaps, which is
       what made the rhythm look accidental. */
    margin: 0 auto 0.75rem;
    padding: 0;
}

.workspace-message.user {
    /* Named so the toolbar can centre on the last line; .branch-leaf trims it below. */
    --message-padding-end: 0.45rem;
    width: fit-content;
    max-width: 75%;
    margin-right: 0;
    padding: 0.45rem 0.8rem;
    padding-bottom: var(--message-padding-end);
    border: 1px solid var(--color-user-bubble-border);
    border-radius: 1rem;
    background: var(--color-user-bubble);
}

/* What the bubble holds is drawn for the plain transcript; on the accent tint each piece needs
   its own step. Links and review marks are accent tints themselves and would melt into the
   bubble at their usual strength; greys (chips, code) would look like grime on a coloured
   surface, so they sit on a translucent inset instead. */
.workspace-message.user .markdown-content .file-link {
    background: color-mix(in srgb, var(--color-accent) 35%, transparent);
}
.workspace-message.user .review-comment-mark {
    background-color: color-mix(in srgb, var(--color-accent) 38%, transparent);
}
.workspace-message.user .message-resource-chip {
    border-color: var(--color-user-bubble-border);
    background: var(--color-user-bubble-inset);
}
.workspace-message.user .markdown-content pre {
    background: var(--color-user-bubble-inset);
}

.workspace-message.user.resource-only .message-actions {
    position: static;
    justify-content: flex-end;
    margin-top: .2rem;
    padding: 0;
    background: transparent;
    opacity: 1;
    pointer-events: auto;
}

.message-empty-label { display: block; color: var(--color-text-secondary); font-size: .82rem; }
.workspace-message.user.resource-only .message-resource-list { margin: .3rem 0 0; }
.workspace-message.user.resource-only .message-resource-chip { min-width: 0; }
.message-resource-open, .message-resource-content { display: inline-flex; flex: 1; min-width: 0; align-items: center; gap: .35rem; }
.message-resource-content > span, .message-resource-open > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* === Branch leaf transient effects === */

/* The leaf marker is structural, not visual: it identifies the bottom of the
   active branch (used by JS, history markers, layout). The line itself is drawn
   separately by .branch-leaf-arrived at the moment an assistant response lands,
   so static visits to a chat don't trigger an animation. */
.workspace-message.branch-leaf {
    /* Declared after .workspace-message.user, so it trims a question's bubble too. */
    --message-padding-end: 0.3rem;
    padding-bottom: var(--message-padding-end);
}

/* Drawn left-to-right, held, then faded out in place, on the instant the leaf becomes
   a new assistant message. --accent-color keeps it consistent with the unread history
   marker and the scroll-hint's "scrolling" state; a pseudo-element (not a border)
   keeps the animation on transform/opacity, off the layout path, so it never
   causes a reflow. Draw and fade are two animations rather than one set of keyframes:
   a single timeline has to scale the line back to zero to end, which reads as the line
   rewinding itself instead of dissolving where it was drawn. */
.workspace-message.branch-leaf-arrived::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    border-radius: 999px;
    background: var(--color-accent);
    transform-origin: left center;
    animation:
        branch-leaf-draw var(--branch-leaf-draw) cubic-bezier(0.22, 0.61, 0.36, 1) 1 forwards,
        branch-leaf-fade var(--branch-leaf-fade) ease-out calc(var(--branch-leaf-draw) + var(--branch-leaf-hold)) 1 forwards;
    pointer-events: none;
}

@keyframes branch-leaf-draw {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
}

@keyframes branch-leaf-fade {
    from { opacity: 1; }
    to { opacity: 0; }
}

/* Reduced-motion users get the same marker without the movement: it appears at full width
   instead of being drawn, then still fades on the same schedule. The setting is about motion,
   not about things changing, so dropping only the transform keeps the line from sitting there
   as a permanent rule on the transcript. */
@media (prefers-reduced-motion: reduce) {
    .workspace-message.branch-leaf-arrived::after {
        transform: scaleX(1);
        animation: branch-leaf-fade var(--branch-leaf-fade) ease-out
            calc(var(--branch-leaf-draw) + var(--branch-leaf-hold)) 1 forwards;
    }
}

.workspace-message.fork-source {
    border-radius: 0.6rem;
    outline: 1px solid var(--color-accent-strong);
    outline-offset: 0.3rem;
}

.workspace-message.streaming {
    opacity: 0.95;
}

/* An assistant note written before a batch of tool calls, now the head of the block whose tools
   it announces. It stays in the transcript — it is the only place the model explains what it is
   doing — but reads a step quieter than the final answer, so the answer still wins the page. */
.preamble-content {
    margin-bottom: 0;
    color: var(--color-text-muted);
    font-size: 0.92rem;
}

/* A short progress note shouldn't open up as much vertical space as a full answer does. */
.preamble-content > :first-child { margin-top: 0; }
.preamble-content > :last-child { margin-bottom: 0; }

.workspace-message.streaming .message-role::after {
    content: "";
    display: inline-block;
    width: 0.4rem;
    height: 0.4rem;
    margin-left: 0.4rem;
    border-radius: 50%;
    background: var(--color-accent);
    animation: streaming-pulse 1s ease-in-out infinite alternate;
}

.stop-button {
    display: inline-grid;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    place-items: center;
    border-radius: 50%;
    border-color: var(--color-border-stronger);
    background: var(--color-fill-strong);
}

.stop-button .app-icon {
    width: 1.15rem;
    height: 1.15rem;
}

@keyframes streaming-pulse {
    to { opacity: 0.25; }
}

.message-status {
    color: var(--color-text-faint);
    font-size: 0.72rem;
}

.branch-link {
    padding: 0.25rem 0;
    background: transparent;
    border: 0;
    font-size: 0.8rem;
}

/* Floated over the message's own corner rather than laid out under it. In flow these buttons
   reserved ~30px beneath every single message — more than half the height of a collapsed tool
   row — and that space was invisible at all times except on hover.

   What they cover fades into the page rather than being hidden behind a panel: a gradient that
   runs from transparent to the feed's own background, so the text dissolves under the buttons
   the way the sidebar's row actions already work. A box with its own border read as something
   dropped on top of the message. */
.message-actions {
    position: absolute;
    right: 0;
    bottom: 0;
    z-index: 2;
    display: flex;
    gap: 0.1rem;
    align-items: center;
    padding-left: 2rem;
    background: linear-gradient(to right, transparent, var(--color-bg) 1.8rem);
    opacity: 0;
    /* Not clickable while invisible, so an unhovered message's corner still selects its text. */
    pointer-events: none;
    transition: opacity 140ms ease;
}

/* A question is right-aligned and at most 75% wide, so the feed is always empty to its left: the
   toolbar sits there, beside the bubble's last line, and covers none of its text. Inside the
   bubble it hid a short question whole — and with it the text a comment is made by selecting.
   The right padding, not a margin, keeps the pointer over the article on its way to the buttons. */
.workspace-message.user .message-actions {
    right: 100%;
    padding: 0 0.3rem 0 0;
    background: none;
}

/* Centred on the message's last line of text, not sat on the article's bottom edge, which the
   taller buttons and the bottom padding put below the text. The chip takes the text's font, so
   1lh is that line, and the padding is what lies beneath it. */
.workspace-message.user .message-actions,
.workspace-message.assistant .message-actions {
    bottom: calc(var(--message-padding-end, 0px) + (1lh - 1.7rem) / 2);
    font-size: var(--message-font-size);
    line-height: 1.5;
}

/* The branch control is a row of its own under the text; the chip centres on the line above it. */
.workspace-message.user:has(.message-branch-indicator) .message-actions,
.workspace-message.assistant:has(.message-branch-indicator) .message-actions {
    bottom: calc(var(--message-padding-end, 0px) + 1.9rem + (1lh - 1.7rem) / 2);
}

/* Too narrow a feed leaves no room beside the bubble. The chip then straddles the bubble's top
   edge, in the gap above it, as a pill of its own — still off the text, and taking no height. */
@container feed (max-width: 30rem) {
    .workspace-message.user:not(.resource-only) .message-actions,
    .workspace-message.user:not(.resource-only):has(.message-branch-indicator) .message-actions {
        top: -1.1rem;
        right: 0.6rem;
        bottom: auto;
        padding: 0.05rem;
        border: 1px solid var(--color-border);
        border-radius: 999px;
        background: var(--color-surface-raised);
    }
}

/* The toolbar is drawn outside the bubble. `content-visibility:auto` also applies paint
   containment, so both constraints have to be relaxed while the toolbar is actually on screen. */
.workspace-message.user:hover,
.workspace-message.user:has(:focus-visible),
.workspace-message.user:has(.message-more-menu:popover-open) {
    overflow: visible;
    content-visibility: visible;
}

/* An answer's chip stays in its corner, but centring it on the last line lets its hover fill
   reach a couple of pixels below the article, which paint containment would cut off. The width
   clip stays, so wide content still cannot spill sideways. */
.workspace-message.assistant:hover,
.workspace-message.assistant:has(:focus-visible) {
    content-visibility: visible;
}

/* Keep the branch control as its own row. The actions are anchored to the article, so without
   this offset they cover the branch trigger as soon as the message is hovered. */
.workspace-message:has(.message-branch-indicator) .message-actions {
    bottom: 1.9rem;
}

/* An open picker is deliberately drawn outside the message. The horizontal clip cuts it at the
   sides, while content visibility adds paint containment; together they turned
   the menu into the tiny nested scroller shown inside a user bubble. Relax both only while the
   dropdown exists, keeping long-content containment and virtualization for the normal feed. */
.workspace-message:has(.branch-picker-menu) {
    overflow: visible;
    content-visibility: visible;
}

/* Keyboard focus, not focus of any kind. `:focus-within` also matches the focus a mouse click
   leaves behind on the button it just pressed, so after copying a message the chip stayed up
   until focus happened to move somewhere else — reading as a popup that refuses to close.
   `:has(:focus-visible)` matches only focus the browser judges worth showing, which is what a
   Tab arrival is and what a click is not. */
.workspace-message:hover .message-actions,
.workspace-message:has(:focus-visible) .message-actions,
.workspace-message:has(.message-more-menu:popover-open) .message-actions {
    opacity: 1;
    pointer-events: auto;
}

/* One quiet colour for every action; the pointer brings it up. Colour is left to the one
   destructive action, in the More menu. */
.message-actions .icon-action {
    width: 1.7rem;
    height: 1.7rem;
    border-color: transparent;
    background: transparent;
    color: var(--color-text-muted);
}

.message-actions .icon-action:hover:not(:disabled),
.message-actions .icon-action.menu-open {
    background: var(--color-fill);
    color: var(--color-text);
}

.message-copy-action .copied-icon,
.message-copy-action.is-copied .app-icon:not(.copied-icon) { display: none; }
.message-copy-action.is-copied .copied-icon { display: block; color: var(--color-success); }

/* A popover, so the top layer keeps it clear of the feed's scroller and the bubble's
   containment; messageActions.js pins it to its button. The shared .chat-menu look applies, but
   its display would otherwise keep a closed popover on screen. */
.message-more-menu {
    position: fixed;
    inset: auto;
    width: max-content;
    min-width: 13rem;
    margin: 0;
    color: var(--color-text);
}
.message-more-menu:not(:popover-open) { display: none; }
.message-more-menu:focus { outline: none; }
.message-more-menu > hr { margin: 0.25rem 0.2rem; border: 0; border-top: 1px solid var(--color-border); }
.message-more-menu > button:disabled { opacity: 0.45; cursor: default; }

/* Hover does not exist on touch, and an action nobody can reach is worse than one that takes
   room, so there they stay visible. */
@media (hover: none) {
    .message-actions {
        opacity: 1;
        pointer-events: auto;
    }

    .workspace-message.user {
        overflow: visible;
        content-visibility: visible;
    }
}

.message-branch-indicator {
    position: relative;
    display: flex;
    align-items: center;
    min-height: 1.8rem;
    margin-top: 0.1rem;
    color: var(--color-text-subtle);
}

.branch-connector {
    align-self: stretch;
    width: 0.8rem;
    margin-left: 0.3rem;
    border-left: 1px solid var(--color-border-strong);
    border-bottom: 1px solid var(--color-border-strong);
    border-bottom-left-radius: 0.35rem;
}

.branch-picker-trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.4rem;
    border-color: transparent;
    background: transparent;
    color: var(--color-text-subtle);
    font-size: 0.75rem;
}

.branch-pending {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.4rem;
    color: var(--color-accent);
    font-size: 0.75rem;
}

.branch-picker-menu {
    position: absolute;
    z-index: 25;
    top: calc(100% + 0.2rem);
    left: 0.8rem;
    display: grid;
    min-width: 13rem;
    max-width: min(24rem, 70vw);
}

.branch-picker-menu button {
    display: grid;
    grid-template-columns: 1.2rem auto minmax(0, 1fr);
    align-items: center;
    gap: 0.4rem;
    border-color: transparent;
    background: transparent;
    text-align: left;
}

.branch-picker-menu button:hover,
.branch-picker-menu button.selected {
    background: var(--color-fill-active);
}

.branch-option-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.branch-option-spacer {
    width: 1rem;
}

/* Single status-dot language used everywhere a run's state shows up: project/chat/branch-tree
   rows and the branch picker menu. A run in a given state always reads the same wherever it
   appears. Hollow (border only, transparent center) is the explicit "nothing to report" state —
   every row always shows a dot, never an absence of one. */
.status-dot {
    display: inline-block;
    width: 0.35rem;
    height: 0.35rem;
    flex-shrink: 0;
    border: 1px solid var(--color-border-stronger);
    border-radius: 50%;
    background: transparent;
}

.status-dot.run-status-generating {
    border-color: var(--color-inverse-hover);
    background: var(--color-inverse-hover);
}

.status-dot.run-status-unread { border-color: var(--color-unread); background: var(--color-unread); }

.status-dot.run-status-attention { border-color: var(--color-warning); background: var(--color-warning); }

.status-dot.run-status-failed { border-color: var(--color-danger); background: var(--color-danger); }

.status-dot.run-status-interrupted {
    border-color: var(--color-text-faint);
    background: repeating-linear-gradient(45deg, var(--color-text-faint) 0 2px, transparent 2px 4px);
}

.workspace-composer {
    position: relative;
    grid-row: 5;
    grid-column: 1;
    width: min(48rem, calc(100% - 3rem));
    margin: 0 auto 1.25rem;
    padding: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.85rem;
    background: var(--color-fill);
}

.workspace-composer.resource-drop-active {
    outline: 2px dashed var(--color-accent);
    outline-offset: 3px;
}
.workspace-composer.resource-drop-active::after {
    position: absolute;
    z-index: 2;
    inset: 0;
    display: grid;
    place-items: center;
    border-radius: inherit;
    background: color-mix(in srgb, var(--color-surface) 88%, transparent);
    color: var(--color-text-strong);
    content: "Drop files to attach them to this message";
    font-size: .86rem;
    font-weight: 500;
    pointer-events: none;
}

.composer-status {
    grid-row: 4;
    grid-column: 1;
    width: min(48rem, calc(100% - 3rem));
    margin: 0 auto 0.4rem;
    color: var(--color-text-faint);
    font-size: 0.8rem;
}

.composer-generating-status {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: bottom;
    white-space: nowrap;
}

/* Dimmer than the status text it shares the line with: the counter is a position marker, not a
   message, and it must not read as louder than a fork/replace chip standing next to it. */
.composer-history-counter { margin-right: .4rem; color: var(--color-text-disabled); }

.branch-nav-row > .branch-link,
.branch-nav-row > .sidebar-inline-editor { margin-left: .75rem; width: calc(100% - .75rem); }

.branch-nav-row > .branch-link {
    padding: 0.3rem 0.55rem;
    font-size: 0.9rem;
}

.chat-queue { grid-row: 3; grid-column: 1; width: min(48rem, calc(100% - 3rem)); margin: 0 auto .4rem; }
.queue-header { display: flex; align-items: center; justify-content: space-between; gap: .45rem; }
.queue-summary, .queue-actions { display: flex; align-items: center; gap: .45rem; }
.queue-summary { padding: .35rem .5rem; border-color: transparent; background: transparent; color: var(--color-text-muted); }
.queue-summary > .app-icon:last-child { transition: transform 150ms ease; }
.queue-summary[aria-expanded="true"] > .app-icon:last-child { transform: rotate(90deg); }
/* Capped at ~5 rows (row height + gap, times 5, plus one row's padding of breathing room) with
   overflow-y:auto — always applied rather than conditioned on count, since a shorter queue just
   never reaches this height and never shows a scrollbar. */
.queue-items { display: grid; gap: .2rem; padding: .3rem 0; max-height: calc(5 * 1.9rem + 4 * .2rem); overflow-y: auto; }
.queue-item { display: flex; min-width: 0; align-items: center; gap: .2rem; padding: .2rem .4rem; border-radius: .4rem; background: var(--color-surface-raised); }
.queue-item.dragging { opacity: .45; }
.queue-item.drop-target { box-shadow: inset 0 2px var(--color-accent); }
.queue-drag-handle { display: inline-grid; flex: 0 0 auto !important; width: 1.4rem; height: 1.4rem; padding: 0; border-color: transparent; background: transparent; color: var(--color-text-disabled); cursor: grab; place-items: center; touch-action: none; }
.queue-drag-handle:active { cursor: grabbing; }
.queue-item > span, .queue-item > input { flex: 1; min-width: 0; }
.queue-item > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.queue-item > input { padding: .3rem; }
.queue-item .icon-action { width: 1.4rem; height: 1.4rem; }
.queue-item .icon-action .app-icon { width: .85rem; height: .85rem; }
.queue-item .queue-send-now:hover { color: var(--color-warning-text); }
.queue-busy { pointer-events: none; }
.queue-transition-status { display: inline-flex; align-items: center; gap: .35rem; color: var(--color-warning); }
.queue-transition-status .app-icon { width: .9rem; height: .9rem; }
.queue-actions { justify-content: flex-end; gap: .35rem; flex: 0 0 auto; }
.queue-action-button {
    display: inline-grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: 1px solid transparent;
    border-radius: .4rem;
    background: transparent;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.queue-action-button .app-icon { width: 1rem; height: 1rem; }
.queue-action-button:hover { background: var(--color-overlay-hover); }
.queue-action-button:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.queue-action-resume:hover { color: var(--color-success); border-color: var(--color-success-border); }
.queue-action-clear:hover { color: var(--color-danger); border-color: var(--color-danger-border); }
.queue-clear-confirm { display: flex; align-items: center; gap: .4rem; color: var(--color-text-secondary); font-size: .85rem; }

/* Why the turn stopped, and what to do about it. Deliberately the same card as the file-change
   summary it sits under - same width, radius, border and background - so the end of a turn reads
   as one stack of cards rather than a pile of unrelated widgets. */
.turn-status {
    display: flex;
    box-sizing: border-box;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    max-width: 48rem;
    margin: 0 auto 0.75rem;
    padding: 0.58rem 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.85rem;
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 0.92rem;
}

.turn-status-icon { display: inline-grid; flex: 0 0 auto; place-items: center; }
.turn-status-icon .app-icon { width: 1rem; height: 1rem; }
.turn-status-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Failed turns show the full provider error (often a multi-line JSON blob). The chip stays the
   same width, but the text wraps inside its track and scrolls vertically when the message
   gets long, so a 4kb error no longer reads as "Failed: litellm.Context". */
.turn-status-failed .turn-status-text {
    max-height: 7.5rem;
    overflow-x: hidden;
    overflow-y: auto;
    text-overflow: clip;
    white-space: pre-wrap;
    word-break: break-word;
    align-self: stretch;
}
.turn-status-actions { display: flex; flex: 0 0 auto; align-items: center; gap: 0.3rem; }

/* The copy button sits in the same row as the error text. Always rendered, but muted so a
   quiet chip doesn't carry an action button as loud as Retry/Discard; brightens on hover and
   keyboard focus so the affordance is unmistakable when the user looks for it. Same vocabulary
   as .message-actions: 1.7rem square, transparent background, border only on hover. */
.turn-status-copy {
    flex: 0 0 auto;
    width: 1.7rem;
    height: 1.7rem;
    padding: 0;
    border-color: transparent;
    background: transparent;
    color: inherit;
    opacity: 0.5;
    transition: opacity 140ms ease, background-color 140ms ease, color 140ms ease;
    align-self: flex-start;
}

.turn-status:hover .turn-status-copy,
.turn-status:focus-within .turn-status-copy,
.turn-status-copy:focus-visible {
    opacity: 1;
}

.turn-status-copy:hover {
    background: var(--color-fill);
    color: var(--color-text);
}

.turn-status-action {
    padding: 0.2rem 0.55rem;
    border: 1px solid var(--color-border);
    border-radius: 0.45rem;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 0.82rem;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 140ms ease, border-color 140ms ease;
}

.turn-status-action:hover { background: var(--color-fill); }
.turn-status-action:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.turn-status-primary { border-color: var(--color-success-border); color: var(--color-success); }
.turn-status-stopped .turn-status-icon { color: var(--color-warning); }
.turn-status-failed .turn-status-icon { color: var(--color-danger); }
.turn-status-failed .turn-status-text { color: var(--color-danger-text); }
}
.fork-mode {
    display: flex;
    min-width: 0;
    align-items: center;
    gap: 0.4rem;
}

.fork-mode > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.fork-mode .icon-action {
    display: inline-grid;
    flex: 0 0 auto;
    align-self: center;
    width: 1.7rem;
    height: 1.7rem;
    margin-left: auto;
    border-color: transparent;
    background: transparent;
    line-height: 0;
    place-items: center;
}

.fork-mode .icon-action .app-icon { display: block; margin: auto; }

/* SVGs are geometrically centred, while the font's visual centre sits lower than its line box.
   Nudge both glyphs onto the text's optical axis so the close button no longer floats above it. */
.fork-mode > .app-icon,
.fork-mode .icon-action { transform: translateY(.25rem); }

.replace-mode {
    color: var(--color-danger-text);
}

.message-actions .destructive-action {
    color: var(--color-danger-text);
}

.message-actions .destructive-action:hover:not(:disabled) {
    background: var(--color-danger-bg);
    color: var(--color-danger-text-strong);
}

.workspace-composer:focus-within {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 1px var(--color-accent);
}

.workspace-composer textarea {
    min-height: 4.5rem;
    max-height: calc(14 * 1.2em);
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--color-text-strong);
    caret-color: var(--color-accent);
    outline: none;
    overflow-y: hidden;
    resize: none;
    field-sizing: content;
}

.composer-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.6rem;
}

.composer-resource-list,
.message-resource-list {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    margin-bottom: .45rem;
}

.composer-resource-chip,
.message-resource-chip {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    max-width: 100%;
    padding: .2rem .45rem;
    border: 1px solid var(--color-border-strong);
    border-radius: .45rem;
    background: var(--color-fill-active);
    color: var(--color-text-secondary);
    font-size: .78rem;
}

.composer-resource-chip > span,
.message-resource-chip > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.composer-resource-chip .app-icon,
.message-resource-chip .app-icon { flex: none; width: .9rem; height: .9rem; }
.composer-resource-chip button { display: inline-flex; padding: .08rem; border: 0; background: transparent; color: var(--color-text-muted); }
.composer-resource-chip button:hover { color: var(--color-text-strong); }
.composer-resource-chip button .app-icon { width: .8rem; height: .8rem; }
.message-resource-list { margin-top: .5rem; }
/* The picture under the pointer while any path is dragged (chatComposer.js): the chip it becomes
   in the composer. Drawn off screen only for the browser's snapshot. */
.path-drag-image { position: fixed; top: -1000px; left: -1000px; max-width: 18rem; pointer-events: none; }
.path-drag-source { opacity: .45; }
.message-resource-chip { max-width: min(100%, 25rem); }
.message-resource-open { padding: 0; border: 0; background: transparent; color: inherit; cursor: pointer; }
.message-resource-remove { display: inline-flex; flex: 0 0 auto; margin-left: .2rem; padding: 0; border: 0; background: transparent; color: var(--color-text-muted); cursor: pointer; }
.message-resource-remove:hover { color: var(--color-text-strong); }
.message-resource-remove .app-icon { width: .75rem; height: .75rem; }
/* Comments and local-path links share one mark — see .file-link: the same tint, underline and
   rounded corners, and the icon tells them apart. Clone keeps the corners on every line a mark
   wraps onto; a comment inside another is not tinted twice. */
.review-comment-mark {
    background-color: color-mix(in srgb, var(--color-accent) 22%, transparent);
    text-decoration: underline solid color-mix(in srgb, var(--color-accent) 80%, transparent);
    border-radius: .25rem;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
}
.review-comment-mark .review-comment-mark { background-color: transparent; }
/* The comment under the pointer in the message's comment list. */
.review-comment-mark.is-emphasized, .workspace-message.user .review-comment-mark.is-emphasized { background-color: color-mix(in srgb, var(--color-accent) 50%, transparent); }
/* The comment icon of the edit statistics (.workspace-change-comments): the same AppIcon markup,
   colour and size, so a comment looks the same wherever it is counted or marked. */
.review-comment-marker {
    display: inline-flex;
    margin-left: 0.2em;
    vertical-align: -0.1em;
    color: var(--color-accent);
    pointer-events: none;
}
.review-comment-marker .app-icon { width: .85rem; height: .85rem; }
/* The app's one tooltip (js/tooltips.js): every title, a local path's access, the comment under
   the pointer. It wears the chrome of the comment editor, in a smaller type than the transcript. */
.app-tooltip {
    position: fixed;
    z-index: 1000;
    max-width: min(18rem, calc(100vw - 1rem));
    padding: .12rem .35rem;
    border: 1px solid var(--color-border-stronger);
    border-radius: .3rem;
    /* Lifted off whatever it covers: a lighter surface and brighter text than the transcript, a
       ring of page background so the text behind never runs up to its edge, and a firm shadow. */
    background: var(--color-fill-strong);
    box-shadow: 0 0 0 3px var(--color-bg), 0 .4rem 1.2rem rgb(0 0 0 / calc(65% * var(--shadow-scale)));
    color: var(--color-text-strong);
    font-size: .78rem;
    line-height: 1.3;
    pointer-events: none;
}
.app-tooltip[hidden] { display: none; }
/* The global p rule sets its own size and a muted colour; a tooltip's text takes the tooltip's. */
.app-tooltip p { margin: 0; color: inherit; font-size: inherit; white-space: pre-wrap; overflow-wrap: anywhere; }
.app-tooltip p + p { margin-top: .2rem; padding-top: .2rem; border-top: 1px solid var(--color-border-stronger); }
.message-review-text { cursor: text; }
.message-review-toggle { display: inline-flex; align-items: center; gap: .3rem; margin-top: .25rem; padding: .1rem 0; border: 0; background: transparent; color: var(--color-accent); font-size: .8rem; cursor: pointer; }
.message-review-toggle .app-icon { width: .85rem; height: .85rem; }
.message-review-toggle .app-icon:last-child { width: .75rem; height: .75rem; opacity: .75; }
.message-review-toggle:hover, .message-review-toggle:focus-visible { color: var(--color-accent-text); }
.message-review-editor { position: fixed; inset: auto; z-index: 110; width: min(17rem, calc(100vw - 1.5rem)); margin: 0; padding: .4rem; transform: translate(-50%, -100%); border: 1px solid var(--color-border-stronger); border-radius: .45rem; background: var(--color-fill-hover); box-shadow: 0 .5rem 1.5rem rgb(0 0 0 / calc(60% * var(--shadow-scale))); }
.message-review-editor.is-below { transform: translateX(-50%); }
.message-review-editor small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--color-accent); font-size: .7rem; }
.message-review-editor textarea { display: block; box-sizing: border-box; width: 100%; min-height: 3rem; margin: .3rem 0; resize: vertical; font-size: .93rem; }
.message-review-editor-actions { display: flex; gap: .25rem; justify-content: flex-end; }
.message-review-editor-actions button { padding: .2rem .4rem; font-size: .72rem; }
.message-review-editor .error { margin: .35rem 0 0; }
/* A message's comment list: one quiet panel of rows, not a card per comment. Each row is the
   quoted fragment (one line, under the accent rule the editor and marks use) over the comment
   itself; edit and delete appear on the row under the pointer or focus. */
.message-review-comments { display: grid; max-width: 32rem; margin-top: .25rem; border: 1px solid var(--color-border-strong); border-radius: .5rem; background: var(--color-fill-hover); overflow: hidden; }
.message-review-comment-list { display: grid; gap: 1px; max-height: 15rem; margin: 0; padding: .25rem; overflow-y: auto; list-style: none; }
.message-review-comment { position: relative; display: flex; align-items: flex-start; gap: .25rem; border-radius: .35rem; }
.message-review-comment:hover, .message-review-comment:focus-within { background: var(--color-fill-strong); }
.message-review-comment-open { display: grid; gap: .15rem; flex: 1; min-width: 0; padding: .35rem .5rem; border: 0; border-radius: inherit; background: transparent; color: inherit; text-align: left; font: inherit; cursor: pointer; }
.message-review-comment-open:focus-visible { outline: none; }
.message-review-quote { overflow: hidden; padding-left: .45rem; border-left: 2px solid var(--color-accent); color: var(--color-text-muted); font-size: .78rem; line-height: 1.35; text-overflow: ellipsis; white-space: nowrap; }
.message-review-body { color: var(--color-text-strong); font-size: .9rem; line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere; }
.message-review-comment-actions { display: flex; gap: .1rem; padding: .25rem .25rem 0 0; opacity: 0; transition: opacity .12s ease; }
.message-review-comment:hover .message-review-comment-actions, .message-review-comment:focus-within .message-review-comment-actions { opacity: 1; }
@media (hover: none) { .message-review-comment-actions { opacity: 1; } }
.message-review-comments-footer { display: flex; justify-content: flex-end; padding: .25rem .35rem; border-top: 1px solid var(--color-border-strong); }
.message-review-attach { display: inline-flex; align-items: center; gap: .3rem; padding: .2rem .5rem; border: 0; border-radius: .3rem; background: transparent; color: var(--color-accent); font-size: .78rem; cursor: pointer; }
.message-review-attach .app-icon { width: .8rem; height: .8rem; }
.message-review-attach:hover, .message-review-attach:focus-visible { background: var(--color-accent-bg); color: var(--color-accent-text); }
.queue-resource-count { color: var(--color-text-muted); white-space: nowrap; }

.composer-resource-menu-anchor { position: relative; display: inline-flex; margin-right: auto; }
.composer-add-resource { width: 2rem; height: 2rem; }
.composer-resource-menu-backdrop { position: fixed; inset: 0; z-index: 29; }
.composer-resource-menu {
    position: absolute;
    bottom: calc(100% + .5rem);
    left: 0;
    z-index: 30;
    display: grid;
    width: 13rem;
    padding: .35rem;
    border: 1px solid var(--color-border-strong);
    border-radius: .65rem;
    background: var(--color-fill-hover);
    box-shadow: 0 .6rem 2rem rgb(0 0 0 / calc(53% * var(--shadow-scale)));
}
.composer-resource-menu-label { padding: .3rem .55rem; color: var(--color-text-subtle); font-size: .75rem; }
.composer-resource-menu button { display: flex; align-items: center; gap: .6rem; padding: .5rem .55rem; border: 0; border-radius: .35rem; background: transparent; color: var(--color-text); text-align: left; }
.composer-resource-menu button:hover,
.composer-resource-menu button:focus-visible { background: var(--color-fill-stronger); }
.composer-resource-menu button .app-icon { width: 1rem; height: 1rem; }
.composer-review-submenu { display: grid; max-height: 12.5rem; overflow-y: auto; border-top: 1px solid var(--color-border-strong); margin-top: .2rem; padding-top: .2rem; }
.composer-review-submenu button span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.composer-review-submenu button { min-height: 2.5rem; }

.composer-endpoint {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.composer-endpoint-trigger {
    display: inline-flex;
    max-width: 12rem;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.4rem;
    border-color: transparent;
    background: transparent;
    color: var(--color-text-secondary);
    font-size: 0.82rem;
}

.composer-endpoint-trigger span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.composer-endpoint-trigger .app-icon {
    flex: none;
    width: 0.85rem;
    height: 0.85rem;
    color: var(--color-text-subtle);
}

.composer-endpoint-trigger:disabled {
    color: var(--color-text-disabled);
}

.composer-endpoint-menu {
    position: absolute;
    z-index: 31;
    bottom: calc(100% + 0.4rem);
    left: 0;
    display: grid;
    min-width: 12rem;
    max-width: min(20rem, 70vw);
}

.composer-endpoint-menu button {
    display: grid;
    grid-template-columns: 1rem minmax(0, 1fr) auto;
    align-items: center;
    text-align: left;
}

.composer-endpoint-menu button:hover,
.composer-endpoint-menu button.selected {
    background: var(--color-fill-active);
}

.composer-endpoint-menu button > span:nth-child(2) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.composer-endpoint-default {
    flex: none;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    background: var(--color-overlay-active);
    color: var(--color-accent);
    font-size: 0.68rem;
}

.composer-send {
    display: inline-grid;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    place-items: center;
    border: 1px solid var(--color-inverse);
    border-radius: 50%;
    background: var(--color-inverse);
    color: var(--color-on-inverse);
}

.composer-send:hover:not(:disabled) {
    border-color: var(--color-inverse-hover);
    background: var(--color-inverse-hover);
    color: var(--color-on-inverse);
    transform: translateY(-1px);
}

.composer-send:active:not(:disabled) {
    border-color: var(--color-inverse-active);
    background: var(--color-inverse-active);
    color: var(--color-on-inverse);
    transform: translateY(0);
}

.composer-send:disabled {
    border-color: var(--color-border-strong);
    background: var(--color-fill-active);
    color: var(--color-text-disabled);
    opacity: 1;
}

.composer-send .app-icon {
    width: 1.2rem;
    height: 1.2rem;
}

.composer-send-tooltip {
    position: absolute;
    bottom: calc(100% + 0.5rem);
    right: 0;
    display: grid;
    gap: 0.3rem;
    padding: 0.55rem 0.7rem;
    min-width: max-content;
    border-radius: 0.5rem;
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: 0 6px 20px rgb(0 0 0 / calc(45% * var(--shadow-scale)));
    font-size: 0.78rem;
    line-height: 1;
    white-space: nowrap;
    opacity: 0;
    transform: translateY(2px);
    transition: opacity 120ms ease, transform 120ms ease;
    pointer-events: none;
    z-index: 10;
}

.composer-send-wrapper {
    position: relative;
    display: inline-flex;
}

/* The hint waits a moment before it appears, so a pointer that only passes by to click Send
   does not flash it; it leaves at once. The ring takes its layer colours after the same pause. */
.composer-send-wrapper {
    --composer-send-hint-delay: 600ms;
}

.composer-send-wrapper:hover .composer-send-tooltip,
.composer-send-wrapper:focus-within .composer-send-tooltip {
    opacity: 1;
    transform: translateY(0);
    transition-delay: var(--composer-send-hint-delay);
}

/* Context ring. The halo around the send button is grey at rest, so the composer stays calm, and
   takes the layer colours while the tooltip is open to explain them. A window that is filling up
   colours it on its own: amber from 75%, red and a slow breath from 90%. */
.context-ring { display: block; overflow: visible; }
.context-ring circle { fill: none; }
.context-ring-track { stroke: var(--color-border-subtle); }
.context-ring-segment {
    stroke: var(--context-color);
    transition: stroke-dasharray 450ms cubic-bezier(0.2, 0.8, 0.2, 1),
        stroke-dashoffset 450ms cubic-bezier(0.2, 0.8, 0.2, 1), stroke 160ms ease, opacity 160ms ease;
}
.context-ring-segment.reserved { opacity: 0.45; }
.context-layer-instructions { --context-color: var(--color-context-instructions); }
.context-layer-tools { --context-color: var(--color-context-tools); }
.context-layer-history { --context-color: var(--color-context-history); }
.context-layer-draft { --context-color: var(--color-context-draft); }
.context-layer-reserve { --context-color: var(--color-context-reserve); }
.context-layer-overhead { --context-color: var(--color-context-overhead); }

.composer-send-ring {
    position: absolute;
    top: 50%;
    left: 50%;
    translate: -50% -50%;
    pointer-events: none;
    transition: transform 120ms ease;
}
.composer-send-ring .context-ring-segment { stroke: var(--color-text-faint); }
.composer-send-ring .context-ring-segment.context-layer-draft { stroke: var(--color-text-muted); }
.composer-send-wrapper.context-warning .composer-send-ring .context-ring-segment { stroke: var(--color-warning); }
.composer-send-wrapper.context-critical .composer-send-ring .context-ring-segment { stroke: var(--color-danger); }
.composer-send-wrapper.context-critical .composer-send-ring { animation: context-ring-breathe 2.4s ease-in-out infinite; }
.composer-send-wrapper:hover .composer-send-ring .context-ring-segment,
.composer-send-wrapper:focus-within .composer-send-ring .context-ring-segment {
    stroke: var(--context-color);
    transition-delay: 0s, 0s, var(--composer-send-hint-delay), 0s;
}
.composer-send-wrapper:has(.composer-send:hover:not(:disabled)) .composer-send-ring { transform: translateY(-1px); }
@keyframes context-ring-breathe { 50% { opacity: 0.55; } }

.composer-send-context { display: flex; align-items: center; gap: 0.9rem; padding: 0.15rem 0.1rem 0.1rem; }
.composer-send-context-chart { position: relative; flex: none; }
.composer-send-context-center {
    position: absolute;
    inset: 0;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 0.25rem;
}
.composer-send-context-percent { color: var(--color-text-strong); font-size: 1.05rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.composer-send-context-total { color: var(--color-text-subtle); font-size: 0.66rem; font-variant-numeric: tabular-nums; }
.composer-send-context-legend { display: grid; gap: 0.38rem; min-width: 10.5rem; }
.composer-send-context-row { display: flex; align-items: center; gap: 0.45rem; color: var(--color-text-muted); }
.composer-send-context-label { flex: 1; }
.composer-send-context-value { color: var(--color-text); font-variant-numeric: tabular-nums; }
.composer-send-context-row.free .composer-send-context-value { color: var(--color-text-subtle); }
.context-swatch { flex: none; width: 0.55rem; height: 0.55rem; border-radius: 0.15rem; background: var(--context-color); }
.composer-send-context-row.reserved .context-swatch { opacity: 0.45; }
.composer-send-context-row.free .context-swatch { background: none; box-shadow: inset 0 0 0 1px var(--color-border-strong); }
.composer-send-context-note { padding: 0.1rem 0.1rem 0; color: var(--color-text-subtle); font-size: 0.72rem; }
.composer-send-context-note.context-note-warning { color: var(--color-warning-text); }
.composer-send-context-note.context-note-critical { color: var(--color-danger-text); }
.composer-send-tooltip-divider { height: 1px; margin: 0.25rem 0 0.15rem; background: var(--color-border-subtle); }

@media (prefers-reduced-motion: reduce) {
    .context-ring-segment { transition: none; }
    .composer-send-wrapper.context-critical .composer-send-ring { animation: none; }
}

.composer-send-tooltip-row {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: var(--color-text-muted);
    transition: color 80ms ease;
}

.composer-send-tooltip-row.active {
    color: var(--color-text-strong);
}

.composer-send-tooltip-row.active kbd {
    background: var(--color-fill-strong);
    border-color: var(--color-border-stronger);
    color: var(--color-text-strong);
}

.composer-send-tooltip-label {
    margin-left: 0.4rem;
}

.composer-send-tooltip kbd {
    display: inline-block;
    min-width: 1.3rem;
    padding: 0.18rem 0.4rem;
    border: 1px solid var(--color-border);
    border-bottom-width: 2px;
    border-radius: 0.3rem;
    background: var(--color-fill);
    color: var(--color-text-secondary);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.72rem;
    text-align: center;
    line-height: 1;
}

.workspace-form,
.endpoint-settings,
.endpoint-card {
    display: grid;
    gap: 0.75rem;
}

.project-settings-drawer .workspace-form {
    align-content: start;
    min-width: 0;
    gap: .8rem;
}

.project-settings-drawer .workspace-form label { min-width: 0; gap: .3rem; font-size: .78rem; font-weight: 500; }
.project-settings-drawer .workspace-form textarea {
    min-height: 4.25rem;
    resize: vertical;
}

.project-settings-drawer .workspace-form input,
.project-settings-drawer .workspace-form select { min-height: 2.45rem; font-size: .86rem; }

.endpoint-settings {
    margin: 0;
    padding: 0;
    border: 0;
}

.endpoint-card {
    padding: 0.85rem;
    border: 1px solid var(--color-border);
    border-radius: 0.75rem;
    background: var(--color-surface-raised);
}

.settings-placeholder {
    align-content: start;
    padding-top: 0.5rem;
}

.settings-placeholder h3 {
    margin: 0;
}

.settings-placeholder p {
    font-size: 0.95rem;
}

.settings-placeholder .settings-note {
    color: var(--color-text-faint);
    font-size: 0.84rem;
    line-height: 1.45;
}

.settings-placeholder button {
    justify-self: start;
    margin-top: 0.5rem;
}

.status {
    color: var(--color-success-text);
    font-size: 0.9rem;
}

.toast-region {
    position: fixed;
    z-index: 50;
    /* Sits in the header band at top-right, where the chat has no content, and stays compact so it
       doesn't cover the conversation. The recent-history stack only unfolds on hover/focus. */
    inset: .3rem 1rem auto auto;
    pointer-events: none;
}

.toast {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 0.5rem;
    align-items: center;
    max-width: 22rem;
    padding: 0.25rem 0.3rem 0.25rem 0.6rem;
    border: 1px solid var(--color-border-strong);
    border-radius: 0.75rem;
    background: var(--color-fill);
    box-shadow: 0 1rem 3rem rgb(0 0 0 / calc(45% * var(--shadow-scale)));
    pointer-events: auto;
}

.toast.success { border-color: var(--color-success-border); }
.toast.error { border-color: var(--color-danger-border-strong); }
.toast.info { border-color: var(--color-accent-strong); }
.toast.attention:not(.error) { border-color: var(--color-warning); }

.toast span {
    color: var(--color-text);
    font-size: 0.85rem;
}

.toast > span, .toast .toast-message-link {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.toast .icon-action {
    width: 1.5rem;
    height: 1.5rem;
    border-color: transparent;
    background: transparent;
}

.workspace-header {
    position: absolute;
    z-index: 6;
    top: 0;
    /* Keep the header from sliding under the chat's vertical scrollbar. The JS in
       chatScrollbarGutter.js measures the live scrollbar width and writes it to
       --chat-scrollbar-width on .workspace-conversation, which this element inherits.
       Fallback 0px keeps the header flush to the right on macOS overlay scrollbars
       (where offsetWidth - clientWidth is 0). */
    right: var(--chat-scrollbar-width, 0px);
    left: 0;
    display: flex;
    align-items: center;
    gap: .35rem;
    min-width: 0;
    height: 2.4rem;
    box-sizing: border-box;
    /* Centre the first 1.65rem button (the sidebar toggle) over the 1.1rem history rail at left .5rem. */
    padding: .2rem .5rem .45rem .225rem;
    background: linear-gradient(to bottom, var(--color-bg) 0%, color-mix(in srgb, var(--color-bg) 92%, transparent) 55%, transparent 100%);
    pointer-events: none;
}

.workspace-history-actions { display: flex; gap: .1rem; pointer-events: auto; }
.sidebar-toggle-button { flex: none; pointer-events: auto; }
.workspace-header .icon-action { width: 1.65rem; height: 1.65rem; }
.workspace-header .icon-action:disabled { opacity: .35; cursor: default; }
.workspace-location { min-width: 0; margin-left: .2rem; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--color-text-muted); font-size: .74rem; }
.notification-quick-access { position: relative; flex: none; margin-right: .25rem; pointer-events: auto; }
.history-notifications-button { position: relative; flex: none; display: flex; align-items: center; justify-content: center; width: 1.65rem; height: 1.65rem; box-sizing: border-box; padding: 0; border: 1px solid transparent; border-radius: .5rem; background: transparent; color: var(--color-text-muted); cursor: pointer; pointer-events: auto; }
.history-notifications-button:hover, .history-notifications-button[aria-expanded="true"] { background: linear-gradient(to right, transparent, var(--color-fill-hover) 2.5rem); color: var(--color-text); }
.history-notifications-button:has(.notification-count) { color: var(--color-accent); }
.notification-count { position: absolute; top: -.3rem; right: -.5rem; min-width: .85rem; padding: .04rem .2rem; border-radius: 1rem; background: var(--color-accent-fill); color: var(--color-on-accent); font-size: .62rem; line-height: 1.2; text-align: center; }
.notification-quick-popover { position: absolute; top: 100%; left: 0; z-index: 7; width: min(21rem, calc(100vw - 1rem)); padding-top: .35rem; visibility: hidden; opacity: 0; transform: translateY(-.25rem); pointer-events: none; transition: opacity 120ms ease, transform 120ms ease, visibility 120ms ease; }
.notification-quick-access:not(.preview-hidden):is(:hover, :focus-within) .notification-quick-popover { visibility: visible; opacity: 1; transform: translateY(0); pointer-events: auto; }
.notification-quick-surface { max-height: min(22rem, calc(100vh - 4rem)); overflow-y: auto; padding: .35rem; border: 1px solid var(--color-border); border-radius: .7rem; background: var(--color-surface-raised); box-shadow: 0 .6rem 1.8rem rgb(0 0 0 / calc(38% * var(--shadow-scale))); }
.notification-quick-heading { padding: .4rem .55rem .5rem; color: var(--color-text-muted); font-size: .72rem; font-weight: 600; }
.notification-quick-item { display: flex; align-items: flex-start; gap: .55rem; width: 100%; padding: .55rem; border: 0; border-radius: .45rem; background: transparent; color: var(--color-text-secondary); cursor: pointer; text-align: left; }
.notification-quick-item:hover, .notification-quick-item:focus-visible { outline: none; background: var(--color-fill-active); }
.notification-quick-item.new { background: var(--color-accent-bg); }
.notification-quick-item.new:hover, .notification-quick-item.new:focus-visible { background: var(--color-accent-bg-hover); }
.notification-quick-item > .app-icon { flex: none; margin-top: .1rem; color: var(--color-accent); }
.notification-quick-item.attention > .app-icon { color: var(--color-warning); }
.notification-quick-item.error > .app-icon { color: var(--color-danger); }
.notification-quick-item > span { display: grid; min-width: 0; gap: .2rem; }
.notification-quick-item strong { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; font-size: .79rem; font-weight: 500; line-height: 1.35; }
.notification-quick-item small { color: var(--color-text-subtle); font-size: .68rem; }
.notification-quick-empty { margin: .2rem .55rem .65rem; color: var(--color-text-subtle); font-size: .78rem; }
.notification-quick-all { display: flex; align-items: center; justify-content: space-between; width: 100%; margin-top: .2rem; padding: .55rem; border: 0; border-top: 1px solid var(--color-border); border-radius: 0 0 .45rem .45rem; background: transparent; color: var(--color-accent-text); cursor: pointer; text-align: left; font-size: .76rem; }
.notification-quick-all:hover, .notification-quick-all:focus-visible { outline: none; background: var(--color-fill-active); color: var(--color-text-strong); }
.notification-quick-all .app-icon { width: .9rem; height: .9rem; }

@media (prefers-reduced-motion: reduce) {
    .notification-quick-popover { transition: none; }
}

.toast-region { display: grid; justify-items: end; gap: .25rem; width: min(22rem, calc(100vw - 2rem)); }
.toast-region:not(:hover):not(:focus-within) .toast-recent { display: none; }
/* Once hovered, the region itself catches the pointer so crossing the gap to the history doesn't collapse it. */
.toast-region:hover { pointer-events: auto; }
.toast-message-link { border: 0; padding: 0; background: transparent; color: var(--color-text); cursor: pointer; font: inherit; font-size: .85rem; text-align: left; }
.toast-message-link:hover { text-decoration: underline; }
.toast-recent { display: grid; justify-self: stretch; gap: .15rem; padding: .45rem .7rem; border: 1px solid var(--color-border); border-radius: .65rem; background: var(--color-surface-raised); box-shadow: 0 .5rem 1.5rem rgb(0 0 0 / calc(25% * var(--shadow-scale))); pointer-events: auto; }
.toast-recent button { overflow: hidden; border: 0; padding: .25rem 0; background: transparent; color: var(--color-text-secondary); cursor: pointer; text-align: left; text-overflow: ellipsis; white-space: nowrap; font-size: .78rem; }
.toast-recent button:hover { color: var(--color-text-strong); text-decoration: underline; }
.toast-recent button:disabled { cursor: default; text-decoration: none; }
.toast-recent span { color: var(--color-text-faint); font-size: .7rem; }

.notifications-drawer { grid-template-rows: auto auto minmax(0, 1fr); }
.notification-filters { display: flex; gap: .4rem; padding: .75rem 1.2rem; border-bottom: 1px solid var(--color-border); }
.notification-filters button { padding: .35rem .6rem; border: 0; border-radius: .45rem; background: transparent; color: var(--color-text-muted); cursor: pointer; }
.notification-filters button.selected { background: var(--color-fill-active); color: var(--color-text-strong); }
.notification-history { min-height: 0; overflow-y: auto; padding: .5rem; }
.notification-history-item { display: flex; align-items: start; gap: .65rem; width: 100%; padding: .7rem; border: 0; border-radius: .5rem; background: transparent; color: var(--color-text-secondary); cursor: pointer; text-align: left; }
.notification-history-item:hover { background: var(--color-fill-active); }
.notification-history-item.new { background: var(--color-accent-bg); }
.notification-history-item:disabled { cursor: default; }
.notification-history-item > span { display: grid; min-width: 0; gap: .25rem; }
.notification-history-item strong { font-size: .82rem; font-weight: 500; }
.notification-history-item small { color: var(--color-text-subtle); font-size: .71rem; }
.notification-history-item.attention > .app-icon { color: var(--color-warning); }
.notification-history-item.error > .app-icon { color: var(--color-danger); }

.workspace-advanced {
    margin: 1rem 0;
    padding: 0.8rem;
    border: 1px solid var(--color-border);
    border-radius: 0.75rem;
}

.shell {
    display: grid;
    gap: 1.5rem;
    max-width: 72rem;
    margin: 0 auto;
    padding: 2rem;
    box-sizing: border-box;
}

.hero {
    padding: 2rem;
    border: 1px solid var(--color-border);
    border-radius: 1.5rem;
    background: var(--color-surface);
    box-shadow: 0 1.5rem 4rem rgb(0 0 0 / calc(30% * var(--shadow-scale)));
}

.eyebrow {
    color: var(--color-accent);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

h1 {
    margin: 0.5rem 0;
    font-size: clamp(2.5rem, 8vw, 5rem);
}

h2 {
    margin: 0.25rem 0 0;
}

p {
    margin: 0;
    color: var(--color-text-muted);
    font-size: 1.15rem;
}

#blazor-error-ui {
    display: none;
    position: fixed;
    inset: auto 1rem 1rem;
    padding: 1rem;
    border-radius: 0.75rem;
    background: var(--color-danger-bg-active);
}

.projects {
    width: 100%;
    padding: 2rem;
    border: 1px solid var(--color-border);
    border-radius: 1.5rem;
    background: var(--color-surface);
    box-sizing: border-box;
}

.projects-header,
.actions {
    display: flex;
    gap: 1rem;
    align-items: center;
    justify-content: space-between;
}

button {
    padding: 0.65rem 0.9rem;
    border: 1px solid transparent;
    border-radius: 0.6rem;
    background: transparent;
    color: var(--color-text);
    font: inherit;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease, transform 120ms ease;
}

button:hover:not(:disabled),
button.selected {
    color: var(--color-accent);
}

button:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

button:disabled {
    color: var(--color-text-disabled);
    cursor: default;
    opacity: 0.6;
}

button:active:not(:disabled) {
    background: var(--color-fill-stronger);
}

.project-layout {
    display: grid;
    grid-template-columns: minmax(14rem, 0.7fr) minmax(18rem, 1.3fr);
    gap: 1.5rem;
    margin-top: 1.5rem;
}

.project-list,
.project-editor {
    display: grid;
    gap: 0.75rem;
}

.security-editor {
    display: grid;
    gap: 0.75rem;
    padding-top: 1rem;
    border-top: 1px solid var(--color-border);
}

.chat-editor {
    display: grid;
    gap: 0.75rem;
    max-width: 48rem;
    margin-top: 1rem;
}

.chat-response {
    padding: 1rem;
    border: 1px solid var(--color-accent-strong);
    border-radius: 0.75rem;
    background: var(--color-surface-raised);
}

.chat-response.selected-branch {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 1px var(--color-accent);
}

.markdown-content {
    min-width: 0;
    max-width: 100%;
    overflow-wrap: anywhere;
    word-break: break-word;
    font-size: var(--message-font-size);
    line-height: 1.5;
}

.markdown-content > :first-child {
    margin-top: 0;
}

.markdown-content > :last-child {
    margin-bottom: 0;
}

.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4,
.markdown-content h5,
.markdown-content h6 {
    margin: 0.7em 0 0.3em;
    line-height: 1.25;
    font-weight: 600;
}

.markdown-content h1 { font-size: 1.25rem; }
.markdown-content h2 { font-size: 1.12rem; }
.markdown-content h3 { font-size: 1rem; }
.markdown-content h4 { font-size: 0.94rem; }
.markdown-content h5 { font-size: 0.88rem; }
.markdown-content h6 { font-size: 0.84rem; color: var(--color-text-muted); }

/* Global h1/h2 are sized for the marketing hero (clamp(2.5rem, 8vw, 5rem)) and would otherwise
   turn headings inside assistant messages into banner-sized text. The rules above reset them to
   in-chat proportions. */

.markdown-content p {
    margin: 0.4em 0;
    color: inherit;
    font-size: 0.93rem;
}

.markdown-content ul,
.markdown-content ol {
    margin: 0.4em 0;
    padding-left: 1.4em;
}

.markdown-content li { margin: 0.15em 0; }

.markdown-content blockquote {
    margin: 0.5em 0;
    padding: 0.1em 0.8em;
    border-left: 3px solid var(--color-border);
    color: var(--color-text-muted);
}

.markdown-content hr {
    margin: 0.8em 0;
    border: 0;
    border-top: 1px solid var(--color-border);
}

/* Horizontal scroll lives inside the message, not on the chat container: a long code block, a
   wide table, or an unbreakable token expands this element only, so its neighbours still fit
   the column. The element itself never grows past its column width, so the chat grid track
   stays at its allocated size. */
.markdown-content > pre,
.markdown-content > table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
}

.markdown-content pre {
    padding: 0.75rem;
    border-radius: 0.5rem;
    background: var(--color-input-bg);
}

.markdown-content code {
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.88em;
}

.security-editor h3 {
    margin: 1rem 0 0;
}

.security-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr)) auto auto;
    gap: 0.5rem;
    align-items: center;
}

select {
    min-height: 2.7rem;
    padding: 0.55rem;
    border: 1px solid var(--color-border-strong);
    border-radius: 0.5rem;
    background: var(--color-input-bg);
    color: var(--color-text);
    font: inherit;
}

.checkbox {
    display: flex;
    gap: 0.4rem;
    align-items: center;
    white-space: nowrap;
}

.checkbox input {
    width: auto;
}

.project-list button {
    display: grid;
    gap: 0.25rem;
    text-align: left;
    background: var(--color-surface-raised);
}

.project-list span {
    overflow: hidden;
    color: var(--color-text-muted);
    text-overflow: ellipsis;
    white-space: nowrap;
}

label {
    display: grid;
    gap: 0.4rem;
    color: var(--color-text-secondary);
}

input,
textarea {
    width: 100%;
    padding: 0.65rem;
    border: 1px solid var(--color-border-strong);
    border-radius: 0.5rem;
    background: var(--color-input-bg);
    color: var(--color-text);
    font: inherit;
    box-sizing: border-box;
}

textarea {
    min-height: 6rem;
    resize: none;
}

.project-settings-drawer label,
.project-settings-drawer input,
.project-settings-drawer select,
.project-settings-drawer textarea {
    min-width: 0;
    max-width: 100%;
}

.project-settings-drawer input:not([type="checkbox"]),
.project-settings-drawer textarea,
.project-settings-drawer select {
    padding: 0.5rem 0.65rem;
}

.project-settings-drawer .form-actions {
    flex-wrap: wrap;
}

.danger {
    border-color: var(--color-danger-border);
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
}

.danger:hover:not(:disabled) {
    border-color: var(--color-danger-border-strong);
    background: var(--color-danger-bg-hover);
    color: var(--color-danger-text);
}

.error {
    margin-top: 1rem;
    color: var(--color-danger-text);
}

@media (width <= 700px) {
    .shell,
    .projects {
        padding: 1rem;
    }

    .project-layout {
        grid-template-columns: 1fr;
    }

    .security-row {
        grid-template-columns: 1fr;
    }
}

@media (width <= 1100px) {
    .workspace-shell { --sidebar-width: 15rem; grid-template-columns: var(--sidebar-track) var(--left-divider) minmax(0, 1fr); }
}

@media (width <= 720px) {
    .workspace-shell {
        grid-template-columns: 1fr;
        grid-template-rows: auto 1fr;
    }

    .workspace-sidebar { grid-row: 1; }
    /* The stacked layout has no divider to bring a hidden sidebar back, so it always shows. */
    .workspace-shell.sidebar-collapsed .workspace-sidebar { visibility: visible; }
    .workspace-conversation { grid-column: 1; grid-row: 2; }

    .workspace-resizer,
    .sidebar-toggle-button {
        display: none;
    }

    .workspace-sidebar {
        max-height: 13rem;
        border-right: 0;
        border-bottom: 1px solid var(--color-border-subtle);
    }

    .workspace-messages {
        padding: 3rem 1rem 1rem;
    }

    .history-markers {
        display: none;
    }
}

.shell.legacy-shell {
    display: none;
}

.tool-approval { padding: 1rem; border: 1px solid currentColor; border-radius: .5rem; }
.tool-approval-batch { margin: .25rem 0 .5rem; opacity: .75; font-size: .875em; }
.tool-invocation pre, .tool-approval pre { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 24rem; overflow: auto; }
.tool-approval-actions { position: relative; display: flex; justify-content: flex-end; gap: .45rem; align-items: center; }
.tool-approval-actions > button { margin: 0; }
.tool-approval-more { position: relative; }
.tool-approval-more > summary { display: grid; width: 2.35rem; height: 2.35rem; padding: 0; place-items: center; list-style: none; }
.tool-approval-more > summary::-webkit-details-marker { display: none; }
.tool-approval-menu { position: absolute; z-index: 20; right: 0; bottom: calc(100% + .4rem); display: grid; width: 17rem; padding: .3rem; border: 1px solid var(--color-border); border-radius: .65rem; background: var(--color-surface-raised); box-shadow: 0 .7rem 1.8rem rgb(0 0 0 / calc(40% * var(--shadow-scale))); }
.tool-approval-menu button { display: grid; gap: .15rem; margin: 0; padding: .55rem .65rem; border-color: transparent; background: transparent; text-align: left; }
.tool-approval-menu button:hover { background: var(--color-fill-active); }
.tool-approval-menu button span { color: var(--color-text-subtle); font-size: .75rem; }
.tool-approval-global-confirm { display: grid; gap: .55rem; padding: .65rem; border-top: 1px solid var(--color-border); font-size: .8rem; }
.tool-approval-global-confirm > div { display: flex; justify-content: flex-end; gap: .4rem; }
.tool-approval-global-confirm > div button { display: block; width: auto; padding: .4rem .55rem; }

/* A question from the assistant. Built to read as one decision: the accent edge marks it as the
   thing to act on, and every option is a full-width hit target so choosing is one click anywhere
   on the line rather than one on a control the size of a full stop. */
.user-prompt { display: grid; gap: .85rem; padding: 1rem; border: 1px solid var(--color-border); border-left: 3px solid var(--color-accent-strong); border-radius: .5rem; background: var(--color-surface-raised); }
.user-prompt-title { font-size: .95rem; }
.user-prompt-question { display: grid; gap: .3rem; margin: 0; padding: 0; border: 0; }
.user-prompt-question > legend { display: flex; align-items: baseline; gap: .45rem; margin-bottom: .35rem; padding: 0; font-size: .9rem; }
.user-prompt-question + .user-prompt-question { padding-top: .65rem; border-top: 1px solid var(--color-border); }
.user-prompt-chip { flex: none; padding: .1rem .4rem; border-radius: .35rem; background: var(--color-fill-active); color: var(--color-text-muted); font-size: .7rem; text-transform: uppercase; letter-spacing: .03em; }
.user-prompt-option { display: flex; align-items: start; gap: .55rem; padding: .4rem .5rem; border: 1px solid transparent; border-radius: .4rem; cursor: pointer; }
.user-prompt-option:hover { border-color: var(--color-border); background: var(--color-surface-raised); }
/* Radios and checkboxes have to opt out of the global `input { width: 100% }`, the same way
   .checkbox does: left as is, one control takes the whole row and shoulders its own label off it. */
.user-prompt-option > input { width: auto; flex: none; margin: .15rem 0 0; padding: 0; }
.user-prompt-option > span { display: grid; flex: 1; gap: .15rem; min-width: 0; }
.user-prompt-option-label { font-size: .875rem; }
.user-prompt-option-description { color: var(--color-text-subtle); font-size: .75rem; }
.user-prompt-other-input { width: 100%; margin-top: .15rem; padding: .3rem .45rem; border: 1px solid var(--color-border); border-radius: .35rem; background: var(--color-surface); color: inherit; font-size: .8rem; }
.user-prompt-note { margin: 0; color: var(--color-text-subtle); font-size: .75rem; }
.user-prompt-note.ready { color: var(--color-accent-text); }
.user-prompt kbd { display: inline-block; padding: .08rem .3rem; border: 1px solid var(--color-border-strong); border-bottom-width: 2px; border-radius: .25rem; background: var(--color-fill-hover); color: inherit; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .7rem; line-height: 1.1; }
.user-prompt-actions { display: flex; align-items: center; justify-content: flex-end; gap: .45rem; }
.user-prompt-actions > button { display: inline-flex; align-items: center; justify-content: center; margin: 0; }
.user-prompt-actions > button > span { display: inline-flex; align-items: center; line-height: 1.25; }
.user-prompt-actions > button:not(.secondary-action) kbd { display: inline-flex; align-items: center; justify-content: center; margin-left: .35rem; border-color: var(--color-border-translucent); background: var(--color-overlay-sunken); transform: translateY(1px); }
.user-prompt-error { margin: 0; color: var(--color-danger-text-strong); font-size: .8rem; }

/* Only a tint: presses go through to the page, and runEvents.watchDrawerDismiss closes the drawer. */
.permissions-drawer-backdrop { position: fixed; z-index: 44; inset: 0; background: rgb(0 0 0 / calc(12% * var(--scrim-scale))); pointer-events: none; }
.permissions-drawer { position: fixed; z-index: 45; top: 0; right: 0; bottom: 0; display: grid; grid-template-rows: auto auto minmax(0, 1fr) auto; width: min(26rem, 100vw); border-left: 1px solid var(--color-border); background: var(--color-surface); box-shadow: -1rem 0 2.5rem rgb(0 0 0 / calc(38% * var(--shadow-scale))); }
.permissions-drawer > header { display: flex; align-items: start; justify-content: space-between; gap: 1rem; padding: 1.2rem 1.2rem .9rem; border-bottom: 1px solid var(--color-border); }
.permissions-drawer h2, .permissions-drawer h3, .permissions-drawer p { margin: 0; }
.permissions-drawer h2 { font-size: 1.05rem; }
.permissions-drawer > header p, .permissions-intro, .permission-item p, .permissions-drawer > footer { color: var(--color-text-subtle); font-size: .78rem; }
.permissions-tabs { display: flex; gap: .2rem; padding: .7rem 1.2rem; border-bottom: 1px solid var(--color-border); }
.permissions-tabs button { padding: .35rem .55rem; border-color: transparent; background: transparent; color: var(--color-text-subtle); }
.permissions-tabs button.selected { background: var(--color-fill-hover); color: var(--color-text); }
.permissions-tabs button span, .menu-count { margin-left: auto; color: var(--color-text-faint); font-size: .72rem; }
.permissions-content { min-height: 0; padding: 1rem 1.2rem; overflow-y: auto; }
.permissions-intro { margin-bottom: 1.1rem !important; line-height: 1.45; }
.permissions-group { display: grid; gap: .45rem; margin-bottom: 1.1rem; }
.permissions-group h3 { display: flex; gap: .4rem; align-items: center; font-size: .85rem; }
.permissions-group h3 .app-icon { width: .95rem; height: .95rem; color: var(--color-text-subtle); }
.permission-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .7rem; align-items: center; padding: .7rem; border: 1px solid var(--color-border); border-radius: .6rem; background: var(--color-surface-raised); }
.permission-item-title { display: flex; gap: .5rem; align-items: center; }
.permission-decision { color: var(--color-accent); font-size: .75rem; }
.permission-item p { margin-top: .25rem; }
.permissions-empty { display: grid; justify-items: center; gap: .35rem; padding: 2.5rem 1rem; color: var(--color-text-subtle); text-align: center; }
.permissions-empty .app-icon { width: 1.4rem; height: 1.4rem; }
.permissions-drawer > footer { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .85rem 1.2rem; border-top: 1px solid var(--color-border); }
.permissions-reset-all { color: var(--color-danger-text); }
.project-menu .menu-count, .chat-menu .menu-count { margin-left: auto; }

@media (width <= 44rem) {
    .permissions-drawer, .permissions-drawer.settings-drawer { width: 100vw; }
    .permissions-drawer-backdrop { background: transparent; }
}

/* Tool activity: one quiet line per invocation, so a multi-call turn reads as a few status rows
   rather than competing with the conversation around it. Raw arguments and bulk output live
   inside the collapsed body and are not built into the DOM until a row is opened. */
.tool-activity {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.tool-row {
    display: flex;
    flex-wrap: nowrap;
    width: 100%;
    align-items: center;
    gap: 0.45rem;
    min-height: 1.3rem;
    padding: 0.05rem 0;
    color: var(--color-text-faint);
    font-size: 0.76rem;
    cursor: pointer;
    list-style: none;
}

.tool-row::-webkit-details-marker {
    display: none;
}

.tool-row:hover {
    color: var(--color-text-muted);
}

/* One calm heading replaces all intermediate blocks in a model turn. It intentionally shares
   the tool-row scale and palette so expanding a turn reveals detail without changing visual
   hierarchy. */
.turn-summary {
    display: flex;
    flex-wrap: nowrap;
    width: min(100%, 48rem);
    min-height: 1.3rem;
    margin: 0 auto 0.75rem;
    padding: 0.05rem 0;
    align-items: center;
    gap: 0.35rem;
    border: 0;
    background: none;
    color: var(--color-text-faint);
    font: inherit;
    font-size: 0.76rem;
    text-align: left;
    cursor: pointer;
}

.turn-summary:hover,
.turn-summary.is-expanded {
    color: var(--color-text-muted);
}

.turn-summary.is-static {
    cursor: default;
}

.turn-summary.is-static:hover {
    color: var(--color-text-faint);
}

.turn-summary-text {
    min-width: 0;
    overflow: hidden;
    font-variant-numeric: tabular-nums;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.turn-summary-spinner,
.turn-summary-chevron {
    display: inline-grid;
    flex: 0 0 auto;
    place-items: center;
}

.turn-summary-spinner .app-icon,
.turn-summary-chevron .app-icon {
    width: 0.85rem;
    height: 0.85rem;
}

.turn-summary-spinner {
    color: var(--color-accent);
}

.turn-summary-chevron {
    color: var(--color-text-disabled);
    transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1), color 140ms ease;
}

.turn-summary:hover .turn-summary-chevron,
.turn-summary.is-expanded .turn-summary-chevron {
    color: var(--color-text-subtle);
}

.turn-summary.is-expanded .turn-summary-chevron {
    transform: rotate(90deg);
}

/* A model-only context checkpoint belongs to the expanded work log. Its slim label signals
   that earlier work was summarized without making it look like another assistant answer. */
.context-checkpoint {
    display: flex;
    width: min(100%, 48rem);
    margin: -0.35rem auto 0.75rem;
    align-items: center;
    gap: 0.4rem;
    color: var(--color-text-muted);
    font-size: 0.75rem;
    line-height: 1.4;
}

.context-checkpoint-mark {
    display: inline-grid;
    width: 1rem;
    height: 1rem;
    flex: 0 0 auto;
    place-items: center;
    border: 1px solid var(--color-border-stronger);
    border-radius: 50%;
    color: var(--color-text-muted);
}

.context-checkpoint-mark .app-icon { width: 0.65rem; height: 0.65rem; }
.context-checkpoint-detail { color: var(--color-text-disabled); }

@media (width <= 34rem) {
    .context-checkpoint { flex-wrap: wrap; }
    .context-checkpoint-detail { flex-basis: 100%; margin-left: 1.4rem; }
}

/* The matching control at the far end of an expanded turn remains deliberately icon-only. Its
   centred placement marks the boundary of the intermediate detail without looking like another
   message or competing with the final answer below it. */
.turn-collapse-footer {
    display: grid;
    width: 2rem;
    height: 2rem;
    margin: -0.15rem auto 0.75rem;
    padding: 0;
    place-items: center;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--color-text-disabled);
    cursor: pointer;
}

.turn-collapse-footer:hover,
.turn-collapse-footer:focus-visible {
    background: var(--color-overlay-hover);
    color: var(--color-text-muted);
}

.turn-collapse-footer .app-icon {
    width: 0.9rem;
    height: 0.9rem;
}


.turn-summary.fork-source {
    border-radius: 0.6rem;
    outline: 1px solid var(--color-accent-strong);
    outline-offset: 0.3rem;
}

.turn-summary.branch-leaf-arrived {
    position: relative;
    padding-bottom: 0.3rem;
}

.turn-summary.branch-leaf-arrived::after {
    content: "";
    position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    height: 1px;
    background: var(--color-accent);
    transform-origin: left center;
    animation:
        branch-leaf-draw var(--branch-leaf-draw) cubic-bezier(0.22, 0.61, 0.36, 1) 1 forwards,
        branch-leaf-fade var(--branch-leaf-fade) ease-out calc(var(--branch-leaf-draw) + var(--branch-leaf-hold)) 1 forwards;
    pointer-events: none;
}

/* Intermediate blocks are mounted progressively when a turn opens. Fade each newly mounted
   block into its final position; no height animation, so scroll anchoring has a stable box from
   the first frame. */
.workspace-message.turn-message-arriving {
    animation: turn-message-arrive 260ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
    animation-delay: var(--turn-arrive-delay, 0ms);
    will-change: opacity, transform;
}

@keyframes turn-message-arrive {
    from {
        opacity: 0;
        transform: translateY(-0.35rem);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* The running turn's current text under its row. Written like an answer, because it may turn out
   to be one; a replacement rises out of the row instead of snapping in. */
.workspace-message.live-text {
    animation: turn-message-arrive 300ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

@media (prefers-reduced-motion: reduce) {
    .workspace-message.live-text { animation: none; }
}

.tool-row-glyph {
    display: inline-grid;
    flex: 0 0 auto;
    place-items: center;
    width: 0.9rem;
    height: 0.9rem;
}

.tool-row-glyph .app-icon {
    width: 0.85rem;
    height: 0.85rem;
}

.tool-row-failed > .tool-row .tool-row-glyph {
    color: var(--color-danger);
}

/* Shown on a collapsed group's summary, so the count of failures is legible before anyone
   decides whether to open it. The number carries the meaning; the colour only reinforces it. */
.tool-row-failure-count {
    flex: 0 0 auto;
    padding: 0 0.3rem;
    border: 1px solid var(--color-danger-border);
    border-radius: 0.5rem;
    color: var(--color-danger);
    font-size: 0.66rem;
}

.tool-row-title {
    color: var(--color-text-secondary);
    white-space: nowrap;
}

/* The detail is the elastic part of the row: it gives up width first so the status, the title and
   the duration always stay on one line. Adapters shorten long values themselves, keeping the
   informative end — CSS only handles the overflow that remains. */
.tool-row-detail {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tool-row-safety {
    flex: 0 0 auto;
    padding: 0 0.3rem;
    border: 1px solid var(--color-danger-border);
    border-radius: 0.5rem;
    color: var(--color-danger-text);
    font-size: 0.66rem;
}

.tool-row-meta {
    display: flex;
    flex: 0 0 auto;
    gap: 0.4rem;
    align-items: center;
    margin-left: auto;
    padding-left: 0.4rem;
    white-space: nowrap;
}

.tool-row-elapsed {
    color: var(--color-text-disabled);
}

.tool-row-spinner {
    animation: tool-row-spin 0.9s linear infinite;
}

@keyframes tool-row-spin {
    to { transform: rotate(360deg); }
}

/* A row that appeared while its group was already open. Only the content is animated — the row
   takes its full height immediately, because the feed's stick-to-bottom scroll runs right after
   the render and a height still growing under it would leave the view short of the bottom.
   `backwards` holds the start frame through the stagger delay, so a row waiting its turn is
   invisible rather than flashing in and out. */
.tool-row-arriving {
    animation: tool-row-arrive 160ms ease-out backwards;
    animation-delay: calc(var(--arrive-index, 0) * 40ms);
}

@keyframes tool-row-arrive {
    from { opacity: 0; transform: translateY(-0.2rem); }
}

/* The second flicker in a live group: a row swapping its spinner for an outcome and growing a
   summary and a duration on the right. Marked only on rows the user actually watched run, so
   opening a finished group stays perfectly still. */
.tool-row-resolved > .tool-row > .tool-row-glyph,
.tool-row-resolved > .tool-row > .tool-row-meta {
    animation: tool-row-settle 120ms ease-out;
}

@keyframes tool-row-settle {
    from { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .turn-summary-chevron { transition: none; }
    .turn-summary.branch-leaf-arrived::after {
        transform: scaleX(1);
        animation: branch-leaf-fade var(--branch-leaf-fade) ease-out
            calc(var(--branch-leaf-draw) + var(--branch-leaf-hold)) 1 forwards;
    }
    .workspace-message.turn-message-arriving { animation: none; }
    .tool-row-arriving,
    .tool-row-resolved > .tool-row > .tool-row-glyph,
    .tool-row-resolved > .tool-row > .tool-row-meta {
        animation: none;
    }
}

.tool-row-body {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    margin: 0.2rem 0 0.45rem 1.35rem;
    padding-left: 0.6rem;
    border-left: 1px solid var(--color-border);
}

.tool-row-facts {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.1rem 0.6rem;
    margin: 0;
    font-size: 0.72rem;
}

.tool-row-facts dt {
    color: var(--color-text-disabled);
}

.tool-row-facts dd {
    margin: 0;
    overflow-wrap: anywhere;
    color: var(--color-text-muted);
}

.tool-row-args,
.tool-row-output {
    max-height: 22rem;
    margin: 0;
    overflow: auto;
    color: var(--color-text-subtle);
    font-size: 0.72rem;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.tool-row-output {
    color: var(--color-text-muted);
}

.tool-row-running {
    margin: 0;
    color: var(--color-text-disabled);
    font-size: 0.72rem;
}

/* Every group is one collapsed line; opening it reveals its rows, indented. */
.tool-activity-batch > .tool-activity-rows {
    margin-left: 1.35rem;
}


/* Net file changes for the run, as a card closing the turn. Structured like a small commit
   summary: a header naming the count and the totals, then one row per file with its own counts
   on the right. */
.workspace-changes {
    position: relative;
    box-sizing: border-box;
    width: 100%;
    max-width: 48rem;
    margin: 0 auto 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: 0.85rem;
    background: var(--color-surface);
    font-size: 0.78rem;
    overflow: hidden;
    transition: border-color 180ms ease;
}

.workspace-changes.is-collapsed {
    overflow: visible;
}

.workspace-changes-preview {
    position: absolute;
    z-index: 20;
    bottom: calc(100% + 0.35rem);
    left: 50%;
    width: max-content;
    min-width: min(20rem, calc(100vw - 2rem));
    max-width: min(30rem, calc(100vw - 2rem));
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--color-border);
    border-radius: 0.85rem;
    background: var(--color-fill);
    box-shadow: 0 0.55rem 1.6rem rgb(0 0 0 / calc(35% * var(--shadow-scale)));
    opacity: 0;
    visibility: hidden;
    pointer-events: auto;
    transform: translate(-50%, 0.2rem);
    transition: opacity 110ms ease, transform 110ms ease, visibility 0s linear 110ms;
}

.workspace-changes-preview::after {
    position: absolute;
    top: 100%;
    right: 0;
    left: 0;
    height: 0.36rem;
    content: "";
}

.workspace-changes.is-collapsed:not(.is-preview-suppressed):hover .workspace-changes-preview {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0);
    transition-delay: 160ms;
}

.workspace-changes-preview ul {
    max-height: min(18rem, 50vh);
    margin: 0;
    padding: 0;
    overflow: auto;
    list-style: none;
}

.workspace-changes-preview li {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    min-height: 1.35rem;
    padding: 0.08rem 0;
    line-height: 1.15;
}

.workspace-changes-preview-path {
    min-width: 0;
    overflow: hidden;
    color: var(--color-text);
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The same two fixed columns as the expanded file list, so +/− line up from row to row. */
.workspace-changes-preview-counts {
    display: grid;
    flex: 0 0 auto;
    grid-template-columns: 3rem 3rem;
    column-gap: 0.45rem;
    margin-left: auto;
    font-variant-numeric: tabular-nums;
}

.workspace-changes-preview-counts .workspace-added { text-align: right; }
.workspace-changes-preview-counts .workspace-removed { text-align: left; }

.workspace-changes-header {
    display: flex;
    box-sizing: border-box;
    width: 100%;
    align-items: center;
    gap: 0.5rem;
    padding: 0.58rem 0.75rem;
    border: 0;
    border-bottom: 1px solid transparent;
    border-radius: inherit;
    background: transparent;
    color: var(--color-text);
    font: inherit;
    font-size: 0.92rem;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 140ms ease, border-color 180ms ease;
}

.workspace-changes-header:hover {
    background: var(--color-fill);
}

.workspace-changes.is-collapsed .workspace-changes-header {
    width: 100%;
}

.workspace-changes.is-expanded .workspace-changes-header {
    border-bottom-color: var(--color-border-subtle);
    border-radius: 0.85rem 0.85rem 0 0;
}

.workspace-changes-icon {
    display: inline-grid;
    flex: 0 0 auto;
    place-items: center;
    width: 1.6rem;
    height: 1.6rem;
    border: 1px solid var(--color-border);
    border-radius: 0.45rem;
    background: var(--color-surface-raised);
    color: var(--color-text-muted);
}

.workspace-changes-icon .app-icon {
    width: 0.9rem;
    height: 0.9rem;
}

.workspace-changes-title {
    min-width: 0;
    overflow: hidden;
    color: var(--color-text);
    font-weight: 600;
    text-overflow: ellipsis;
}

.workspace-change-approximate,
.workspace-change-binary {
    color: var(--color-text-disabled);
}

.workspace-changes-chevron {
    display: inline-grid;
    flex: 0 0 auto;
    place-items: center;
    color: var(--color-text-subtle);
    transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1), color 140ms ease;
}

.workspace-changes-chevron .app-icon {
    width: 0.85rem;
    height: 0.85rem;
}

.workspace-changes-header:hover .workspace-changes-chevron {
    color: var(--color-text-secondary);
}

.workspace-changes.is-expanded .workspace-changes-chevron {
    transform: rotate(180deg);
}

.workspace-changes-totals,
.workspace-change-counts {
    display: flex;
    gap: 0.45rem;
    margin-left: auto;
    padding-left: 0.5rem;
    font-variant-numeric: tabular-nums;
}

.workspace-changes-details {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    visibility: hidden;
    transition: grid-template-rows 200ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 130ms ease,
        visibility 0s linear 200ms;
}

.workspace-changes-details-inner {
    min-height: 0;
    overflow: hidden;
    transform: translateY(-0.25rem);
    transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.workspace-changes.is-expanded .workspace-changes-details {
    grid-template-rows: 1fr;
    opacity: 1;
    visibility: visible;
    transition-delay: 0s;
}

.workspace-changes.is-expanded .workspace-changes-details-inner {
    transform: translateY(0);
}

/* The sign is always present, so colour is reinforcement rather than the only signal. */
.workspace-added { color: var(--color-success); }
.workspace-removed { color: var(--color-danger); }

/* Two columns meeting on a fixed axis: additions end where deletions begin, on the same vertical
   line in every row. Without the reserved width each pair simply packed together, so "+10 −1"
   and "+21 −12" put their numbers in different places and the column read as ragged. Tabular
   figures (set on the container) are what make equal character counts equal widths. */
.workspace-added,
.workspace-removed {
    min-width: 2.2rem;
}

.workspace-added { text-align: right; }
.workspace-removed { text-align: left; }

/* Line counts roll between values (AnimatedCount.razor). --count is registered as an integer so
   the browser interpolates it in whole steps; ::before prints it. The ease-out spends most of
   the time near the target, so a large jump still settles on a readable number. */
@property --count { syntax: "<integer>"; inherits: false; initial-value: 0; }
.animated-count { counter-reset: count var(--count); transition: --count 700ms cubic-bezier(0.2, 0.8, 0.2, 1); }
.animated-count::before { content: attr(data-sign) counter(count); }
@starting-style { .animated-count.count-in { --count: 0; } }
/* Deliberately kept under prefers-reduced-motion: nothing moves, only the digits in place change,
   which is not the kind of motion that setting guards against. Windows turns it on for everyone
   who switches off "Show animations in Windows", so honouring it here hid the effect entirely. */

.workspace-changes-list {
    margin: 0;
    padding: 0.25rem 0;
    list-style: none;
}

.workspace-change-summary {
    display: flex;
    box-sizing: border-box;
    width: 100%;
    align-items: center;
    gap: 0.5rem;
    padding: 0.2rem 0.7rem;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.workspace-change-summary:hover {
    background: var(--color-surface-raised);
}

.workspace-change-kind {
    flex: 0 0 auto;
    width: 0.9rem;
    color: var(--color-text-disabled);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.workspace-change-path {
    min-width: 0;
    overflow: hidden;
    color: var(--color-text-muted);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.workspace-changes .workspace-changes-totals,
.workspace-changes .workspace-change-counts {
    display: grid;
    grid-template-columns: 3rem 3rem;
    align-items: center;
    column-gap: .45rem;
    padding-left: 0;
}

.workspace-changes .workspace-changes-totals { flex: 0 0 auto; margin: 0; padding: 0 .7rem 0 .5rem; }
.workspace-changes .workspace-changes-totals .workspace-added,
.workspace-changes .workspace-changes-totals .workspace-removed,
.workspace-changes .workspace-change-counts .workspace-added,
.workspace-changes .workspace-change-counts .workspace-removed { min-width: 0; }
.workspace-changes .workspace-changes-totals .workspace-added,
.workspace-changes .workspace-change-counts .workspace-added { text-align: right; }
.workspace-changes .workspace-changes-totals .workspace-removed,
.workspace-changes .workspace-change-counts .workspace-removed { text-align: left; }

.workspace-change-comments {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    gap: .2rem;
    color: var(--color-accent);
    font-size: .78rem;
    font-variant-numeric: tabular-nums;
}

.workspace-change-comments .app-icon { width: .85rem; height: .85rem; }

.workspace-change-body {
    padding: 0 0 0.45rem;
}

/* One scroll box for the whole diff: lines do not wrap, so a long line moves the viewport rather
   than reflowing the file around it. */
.workspace-change-diff {
    max-height: 24rem;
    overflow: auto;
    border-top: 1px solid var(--color-border-subtle);
    border-bottom: 1px solid var(--color-border-subtle);
    background: var(--color-bg-inset);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.72rem;
    line-height: 1.5;
}

.diff-line {
    display: flex;
    align-items: baseline;
    white-space: pre;
}

/* Tinted the whole width, not just behind the text, so the shape of a change is visible while
   skimming — but faintly, because the sign column is what actually states the direction. */
.diff-line-added { background: var(--color-diff-added-bg); }
.diff-line-removed { background: var(--color-diff-removed-bg); }

/* The numbers stay put while a long line scrolls past them; without this the only thing telling
   you where you are would slide off the left edge. */
.diff-gutter {
    position: sticky;
    left: 0;
    flex: 0 0 2.8rem;
    padding-right: 0.4rem;
    background: inherit;
    color: var(--color-text-disabled);
    text-align: right;
    user-select: none;
}

.diff-gutter + .diff-gutter {
    left: 2.8rem;
    border-right: 1px solid var(--color-border-subtle);
}

.diff-sign {
    position: sticky;
    left: 5.6rem;
    flex: 0 0 1.1rem;
    padding-left: 0.35rem;
    background: inherit;
    user-select: none;
}

.diff-line-added .diff-sign { color: var(--color-success); }
.diff-line-removed .diff-sign { color: var(--color-danger); }

.diff-text {
    padding-right: 0.7rem;
    color: var(--color-text-muted);
}

.diff-line-context .diff-text { color: var(--color-text-subtle); }
.diff-line-added .diff-text { color: var(--color-success-text); }
.diff-line-removed .diff-text { color: var(--color-danger-text); }

/* Where in the file the lines below it sit — a caption between hunks, not a line of the file. */
.diff-hunk {
    padding: 0.15rem 0.7rem;
    background: var(--color-surface);
    color: var(--color-text-disabled);
    white-space: pre;
}

.diff-note {
    padding: 0.2rem 0.7rem;
    color: var(--color-warning);
}

.workspace-change-unavailable {
    margin: 0;
    padding: 0.1rem 0.7rem 0.2rem 2.1rem;
    color: var(--color-text-faint);
}

@media (prefers-reduced-motion: reduce) {
    .workspace-changes,
    .workspace-changes-header,
    .workspace-changes-preview,
    .workspace-changes-chevron,
    .workspace-changes-details,
    .workspace-changes-details-inner {
        transition: none;
    }
}

@media (max-width: 640px) {
    .workspace-changes { font-size: 0.72rem; }
    .workspace-changes-header { font-size: 0.85rem; }
    .workspace-change-summary { flex-wrap: wrap; }
}

/* Search matches use the same light surface and dark text as the diff comment action on hover. */
.search-highlight {
    background: var(--color-highlight-bg);
    color: var(--color-highlight-fg);
    border-radius: 0.2rem;
    padding: 0.05em 0.25em;
    font-weight: 600;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
    box-shadow: 0 0 0 1px var(--color-highlight-bg);
}

.markdown-content pre .search-highlight,
.markdown-content code .search-highlight {
    background: var(--color-highlight-bg);
    color: var(--color-highlight-fg);
    font-weight: inherit;
    box-shadow: 0 0 0 1px var(--color-highlight-bg);
}

/* Keep short matches legible in the user-message bubble. */
.workspace-message.user .search-highlight {
    padding: 0.1em 0.3em;
}


/* Sidebar search. While a query is running these results stand in for the project tree, so they
   match its rhythm rather than introducing a panel of their own. */
.sidebar-search { display: flex; align-items: center; gap: .25rem; padding: .5rem .5rem .25rem; }
.sidebar-search-input { flex: 1; min-width: 0; }
.sidebar-search-results { display: flex; flex-direction: column; gap: .125rem; padding: 0 .25rem .5rem; overflow-y: auto; }
.search-result { display: flex; flex-direction: column; gap: .125rem; width: 100%; padding: .375rem .5rem; border: none; border-radius: .375rem; background: none; color: inherit; text-align: left; cursor: pointer; }
.search-result:hover, .search-result:focus-visible { background: var(--color-overlay-hover); }
.search-result-where { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-result-snippet { opacity: .85; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.search-result-meta { opacity: .6; font-size: .8125em; }

/* Two coarse judgements sit side by side so they read as one comparison rather than two
   unrelated fields, and each keeps its word under the track: the number alone says nothing. */
.connection-ratings {
    display: flex;
    gap: 1.25rem;
}

.connection-ratings > label {
    flex: 1;
    min-width: 0;
}

.connection-ratings input[type="range"] {
    width: 100%;
    accent-color: var(--color-accent);
}

.connection-ratings small {
    color: var(--color-text-subtle);
    font-size: 0.85rem;
}

/* The Model input pairs with a <datalist> for autocomplete. The loading spinner sits inline to
   the right so the user knows a network call is in flight, without taking up an extra row. */
.model-field-row {
    display: flex;
    align-items: flex-end;
    gap: 0.5rem;
}

/* A div, not a label: a label refocuses the input on any click inside it, which reopened the list
   right after an outside press had closed it. The colour is the one labels get. */
.model-field-row > .model-field {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    color: var(--color-text-secondary);
}

.model-field-row > .model-field > input {
    width: 100%;
}

/* A compact square that the spinner can rotate inside; sits at the bottom of the row so its
   vertical centre matches the input's vertical centre. */
.model-loading {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    color: var(--color-text-subtle);
}

@media (prefers-reduced-motion: reduce) {
    .model-loading .tool-row-spinner { animation: none; }
}

/* The Model picker is a free-text input with an inline chevron trigger. Clicking the chevron (or
   focusing the input) opens a list of catalog models — independent of what the user has typed —
   so a saved but non-catalog id never hides the catalog behind a filter the browser applied
   silently. The picker is absolutely positioned so the drawer layout does not reflow when it
   opens. */
.model-picker {
    position: relative;
    display: flex;
    width: 100%;
}

.model-picker-input {
    flex: 1;
    min-width: 0;
    padding-right: 2.25rem;
}

.model-picker-toggle {
    position: absolute;
    right: 0.25rem;
    top: 50%;
    transform: translateY(-50%);
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-subtle);
    background: transparent;
    border: none;
    cursor: pointer;
}

.model-picker-toggle:disabled { cursor: default; opacity: 0.6; }

.model-picker-list {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    max-height: 18rem;
    overflow-y: auto;
    margin: 0;
    padding: 0.25rem;
    list-style: none;
    background: var(--color-surface);
    border: 1px solid var(--color-border-translucent);
    border-radius: 0.4rem;
    box-shadow: 0 8px 24px rgb(0 0 0 / calc(35% * var(--shadow-scale)));
    z-index: 50;
}

.model-picker-empty {
    padding: 0.6rem 0.75rem;
    color: var(--color-text-subtle);
    font-size: 0.9rem;
}

/* The input picks up a red outline while a load has failed, matching the error pane below it so
   the picker reads as a single errored unit rather than an empty control with a stray toast. */
.model-picker-input-error {
    border-color: var(--color-danger);
}

.model-picker-error {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.6rem 0.75rem;
}

.model-picker-error-message {
    margin: 0;
    color: var(--color-danger-text);
    font-size: 0.85rem;
    line-height: 1.3;
    word-break: break-word;
}

.model-picker-retry {
    align-self: flex-start;
}

.model-picker-error-hint {
    color: var(--color-danger-text);
    line-height: 1.3;
    word-break: break-word;
}

.model-picker-retry-link {
    margin-left: 0.35rem;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
}

.model-picker-option {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
    width: 100%;
    padding: 0.45rem 0.6rem;
    border: none;
    background: transparent;
    color: inherit;
    text-align: left;
    cursor: pointer;
    border-radius: 0.3rem;
}

.model-picker-option:hover,
.model-picker-option.selected {
    background: var(--color-overlay-hover);
}

.model-picker-option-id {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.9rem;
}

.model-picker-option-name {
    color: var(--color-text-subtle);
    font-size: 0.8rem;
}

/* SVG fences are displayed in an image context, keeping model-authored markup
   out of the page DOM. Large drawings stay within the message width. */
.svg-block {
    margin: 0.85rem 0;
    padding: 0.85rem;
    border: 1px solid var(--color-border-translucent);
    border-radius: 0.5rem;
    background: var(--color-overlay-faint);
    text-align: center;
}

.svg-block img {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 0 auto;
}

/* Mermaid fences are replaced with a block that contains the SVG or a parse error. */
.mermaid-block {
    box-sizing: border-box;
    margin: 0.85rem 0;
    padding: 0.85rem 1rem;
    min-width: 0;
    max-width: 100%;
    border: 1px solid var(--color-border-translucent);
    border-radius: 0.5rem;
    background: var(--color-overlay-faint);
    overflow-x: auto;
}

.mermaid-block .mermaid-svg {
    display: flex;
    justify-content: center;
}

.mermaid-block .mermaid-svg svg {
    max-width: 100%;
    height: auto;
}

/* The render() call replaces the original fence, but if Blazor's diff momentarily brings the
   <pre><code> back (for instance, between two streaming tokens that re-emit the same fence with
   different contents) the wrapper still exists with a state attribute — the original must not
   show alongside the rendered diagram. */
.mermaid-block > pre,
.mermaid-block pre.language-mermaid,
.mermaid-block code.language-mermaid {
    display: none;
}

/* Error state. Kept inside the same block so the failed diagram still consumes the same vertical
   space the user expected, and the source is preserved verbatim for the model (and the human
   reviewing it) to copy back. The accent colour matches the existing destructive-action
   styling already used elsewhere in the feed. */
.mermaid-block[data-mermaid-state="error"] {
    border-color: var(--color-danger-border);
    background: var(--color-danger-bg);
}

.mermaid-error {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    font-size: 0.9rem;
}

.mermaid-error-header {
    font-weight: 600;
    color: var(--color-danger-text);
}

.mermaid-error-message {
    margin: 0;
    color: var(--color-text-secondary);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.mermaid-error .mermaid-source {
    display: block;
    margin: 0;
    padding: 0.6rem 0.75rem;
    border-radius: 0.375rem;
    background: var(--color-overlay-sunken);
    color: var(--color-text-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.8rem;
    line-height: 1.45;
    overflow-x: auto;
}

/* Directory picker ------------------------------------------------------- */

/* A fixed height, not one that follows the listing: a folder with three subdirectories and one
   with three hundred are two steps of the same walk, and a dialog that resized under every step
   moved its own Select button out from under the pointer. The list scrolls inside instead. */
.directory-picker-modal {
    display: grid;
    grid-template-rows: auto auto minmax(0, 1fr) auto;
    /* The single column is pinned to the dialog. Left to size itself it takes the widest path in
       the list — those never wrap — and grows past the fixed width, where `overflow: hidden` cuts
       off whatever is furthest right: the close button, "Go", "Cancel". Every box down the tree
       that holds a path needs its own `min-width: 0` for the same reason. */
    grid-template-columns: minmax(0, 1fr);
    width: min(34rem, calc(100vw - 3rem));
    height: min(34rem, calc(100vh - 3rem));
    box-sizing: border-box;
    padding: 1.1rem;
    overflow: hidden;
    border: 1px solid var(--color-border-strong);
    border-radius: 1rem;
    background: var(--color-surface);
    box-shadow: 0 1.5rem 5rem rgb(0 0 0 / calc(60% * var(--shadow-scale)));
}

@media (height <= 36rem) {
    .directory-picker-modal {
        width: min(34rem, calc(100vw - 1.5rem));
        height: calc(100vh - 1.5rem);
    }
}

.directory-picker-modal > header {
    display: flex;
    align-items: start;
    justify-content: space-between;
    margin-bottom: 0.8rem;
    padding-bottom: 0.7rem;
    border-bottom: 1px solid var(--color-border);
}

.directory-picker-modal > header div {
    display: grid;
    gap: 0.2rem;
}

.directory-picker-modal > header span { color: var(--color-text-faint); font-size: 0.8rem; }

.directory-picker-toolbar {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.6rem;
}

.directory-picker-path,
.directory-picker-filter {
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--color-border-strong);
    border-radius: 0.4rem;
    background: var(--color-bg-inset);
    color: inherit;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.8rem;
}

.directory-picker-filter {
    margin-bottom: 0.4rem;
    font-family: inherit;
}

.directory-picker-body {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
}

.directory-picker-body > .empty-hint {
    display: grid;
    flex: 1;
    place-content: center;
    margin: 0;
}

.directory-picker-list {
    flex: 1;
    min-width: 0;
    min-height: 0;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
    list-style: none;
}

.directory-picker-item {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
    padding: 0.3rem 0.55rem;
    border: 0;
    background: transparent;
    color: inherit;
    font-size: 0.85rem;
    text-align: left;
    cursor: pointer;
}

.directory-picker-item:hover,
.directory-picker-item:focus-visible { background: var(--color-fill); }

/* The icon keeps its size and the name gives way: a long file name is ellipsised rather than
   pushing the row wider than the list it sits in. */
.directory-picker-item > svg { flex: none; }

.directory-picker-item span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.directory-picker-warning {
    margin: 0.5rem 0 0;
    color: var(--color-warning-text);
    font-size: 0.8rem;
}

.directory-picker-actions {
    align-items: center;
}

/* The chosen path is the one thing worth reading twice before pressing Select, so it sits in the
   footer next to the button rather than only in the box above, where a long path scrolls out.
   Left-to-right even though the end of a path is the informative part: `direction: rtl` would
   keep the tail in view but reorder the punctuation, printing "C:\" as "\:C". */
.directory-picker-selection {
    flex: 1;
    min-width: 0;
    margin-right: auto;
    overflow: hidden;
    color: var(--color-text-faint);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.75rem;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.directory-picker-empty {
    display: grid;
    flex: 1;
    align-content: center;
    justify-items: center;
    gap: 0.35rem;
    padding: 1.5rem 1rem;
    color: var(--color-text-faint);
    text-align: center;
}

.directory-picker-empty p { margin: 0; font-size: 0.8rem; }

/* A file is a destination, not a doorway: it gets its own mark and stays lit once picked, because
   nothing else on screen changes when you click one. */
.directory-picker-item.selected {
    background: var(--color-accent-bg-hover);
    color: var(--color-accent-text);
}

.directory-picker-item.selected:hover { background: var(--color-accent-bg-active); }

.directory-picker-file svg { opacity: 0.75; }

/* The path a question was answered with, with the picker one click away. */
.user-prompt-path-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.user-prompt-path-row .user-prompt-other-input {
    flex: 1;
    min-width: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.8rem;
}

.user-prompt-path-row button { flex: none; }

.user-prompt-paths {
    display: grid;
    gap: .55rem;
    padding: .65rem .75rem;
    border: 1px solid var(--color-border);
    border-radius: .65rem;
}

.user-prompt-paths ul {
    display: grid;
    gap: .35rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.user-prompt-paths li {
    display: flex;
    align-items: center;
    gap: .5rem;
    min-width: 0;
}

.user-prompt-paths li > span {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.user-prompt-paths > button { justify-self: start; }
.chat-menu > .review-submenu-trigger { width: 100%; }
.review-submenu-trigger > span { flex: 1; min-width: 0; }
.review-submenu-trigger > .app-icon:last-child { width: .8rem; height: .8rem; color: var(--color-text-subtle); }
.review-submenu { position: fixed; inset: auto; box-sizing: border-box; width: min(18rem, calc(100vw - 1rem)); margin: 0; padding: .3rem; border: 1px solid var(--color-border); border-radius: .65rem; background: var(--color-surface-raised); box-shadow: 0 .7rem 1.8rem rgb(0 0 0 / calc(40% * var(--shadow-scale))); color: var(--color-text-secondary); font-size: .8rem; }
.review-menu-list { display: grid; gap: .1rem; max-height: min(20.9rem, calc(100vh - 2rem)); overflow-y: auto; min-width: 0; }
.review-menu-row { display: flex; align-items: center; min-width: 0; border-radius: .4rem; }
.review-menu-row:hover, .review-menu-row:focus-within { background: var(--color-fill-active); }
.review-menu-row > button { border: 0; background: transparent; color: inherit; font: inherit; cursor: pointer; }
.review-menu-row .review-menu-open {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex: 1;
    min-width: 0;
    height: 2rem;
    padding: .35rem .6rem;
    border: 0;
    border-radius: .4rem;
    background: transparent;
    color: var(--color-text-secondary);
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.review-menu-row .review-menu-open > .app-icon { flex: 0 0 auto; width: 1rem; height: 1rem; color: var(--color-text-muted); }
.review-menu-row .review-menu-open > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.review-menu-delete, .review-menu-cancel { display: grid; place-items: center; flex: 0 0 1.7rem; height: 1.7rem; margin-right: .15rem; padding: 0; border-radius: .3rem; color: var(--color-text-muted) !important; }
.review-menu-delete:hover, .review-menu-delete:focus-visible, .review-menu-confirm-delete:hover { background: var(--color-danger-bg-hover); color: var(--color-danger-text) !important; }
.review-menu-delete .app-icon, .review-menu-cancel .app-icon { width: .9rem; height: .9rem; }
.review-menu-confirm-label { flex: 1; min-width: 0; padding-left: .6rem; }
.review-menu-confirm-delete { padding: .2rem .4rem; border-radius: .3rem; color: var(--color-danger-text) !important; }
.workspace-changes-top { display: flex; align-items: center; min-width: 0; }
.workspace-changes-top .workspace-changes-header { flex: 1; width: auto; min-width: 0; }
.workspace-changes-top .workspace-changes-chevron { margin-left: auto; }
.workspace-review-action { flex: 0 0 auto; margin-left: .5rem; padding: .2rem .55rem; border: 1px solid var(--color-border-stronger); border-radius: .45rem; background: var(--color-fill); color: var(--color-text); cursor: pointer; }
.workspace-review-action:hover { background: var(--color-fill-strong); }
.workspace-changes.is-expanded .workspace-changes-top { border-bottom: 1px solid var(--color-border-subtle); }
.workspace-changes.is-expanded .workspace-changes-header { border-bottom-color: transparent; }

.review-workspace { position: fixed; z-index: 100; top: 0; right: 0; bottom: 0; width: min(90rem, 94vw); display: flex; flex-direction: column; min-width: 0; color: var(--color-text); background: var(--color-surface); border-left: 1px solid var(--color-border); box-shadow: -1rem 0 2.5rem rgb(0 0 0 / calc(38% * var(--shadow-scale))); }
.review-workspace-header { display: flex; align-items: center; gap: .6rem; min-height: 2.8rem; padding: .4rem .8rem; border-bottom: 1px solid var(--color-border); }
.review-workspace-header > .app-icon { flex: none; width: 1rem; height: 1rem; color: var(--color-text-muted); }
.review-workspace-identity { display: flex; flex-direction: column; flex: 1; min-width: 0; gap: .05rem; }
.review-workspace-identity strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .86rem; font-weight: 600; }
.review-workspace-identity span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--color-text-subtle); font-size: .7rem; }
.review-workspace-delete-confirm { color: var(--color-text-secondary); font-size: .76rem; white-space: nowrap; }
.review-workspace-delete-yes { padding: .2rem .45rem; border: 0; border-radius: .3rem; background: var(--color-danger-bg-hover); color: var(--color-danger-text); font-size: .76rem; cursor: pointer; }
.review-workspace-delete-yes:hover { background: var(--color-danger-bg-active); }
.review-workspace-delete-cancel { padding: .2rem .45rem; border: 0; border-radius: .3rem; background: transparent; color: var(--color-text-muted); font-size: .76rem; cursor: pointer; }
.review-workspace-delete-cancel:hover, .review-workspace-delete-cancel:focus-visible { background: var(--color-fill-active); color: var(--color-text-strong); }
.review-workspace-delete:hover, .review-workspace-delete:focus-visible { color: var(--color-danger-text); background: var(--color-danger-bg-hover); }
.review-workspace-header .review-close { margin-left: auto; }
.review-error { margin: .35rem 1rem; padding: .5rem .65rem; border: 1px solid var(--color-danger-border); border-radius: .45rem; color: var(--color-danger-text); }
.review-legacy-summary { max-width: 48rem; margin: 1rem; color: var(--color-text-muted); font-size: .8rem; }
.review-legacy-summary section { padding: .5rem 0; border-top: 1px solid var(--color-border); }
.review-legacy-summary strong { color: var(--color-text-secondary); font-weight: 500; }
.review-legacy-summary section p { margin: .25rem 0 0 .75rem; white-space: pre-wrap; }
.review-workspace-body { display: grid; grid-template-columns: var(--review-tree-width, 22rem) .35rem minmax(0, 1fr); flex: 1; min-height: 0; }
.review-diff-scroll, .review-file-tree { overflow: auto; min-width: 0; }
.review-diff-scroll { grid-column: 3; padding: .25rem 0 1rem; scroll-behavior: smooth; background: var(--color-bg-inset); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .78rem; line-height: 1.55; }
.review-file-tree { grid-column: 1; grid-row: 1; border-right: 1px solid var(--color-border); padding: .45rem; }
.review-tree-resizer { grid-column: 2; grid-row: 1; z-index: 2; background: var(--color-fill); cursor: col-resize; touch-action: none; }
.review-tree-resizer:hover, .review-tree-resizer:focus-visible, .review-workspace-body.is-resizing .review-tree-resizer { background: var(--color-accent); }
.review-workspace-body.is-resizing { cursor: col-resize; user-select: none; }
.review-file-tree input[type=search] { width: 100%; margin-bottom: .35rem; }
.review-file { margin: 0; border-bottom: 1px solid var(--color-border-subtle); scroll-margin-top: 0; }
.review-file .workspace-change-summary { min-height: 1.65rem; }
.review-file.is-tree-hovered > .workspace-change-summary { background: var(--color-fill-active); box-shadow: inset 3px 0 var(--color-accent); }
.review-file .review-diff-content { border-top: 1px solid var(--color-border-subtle); }
.diff-line-review:hover { outline: 1px solid var(--color-border-stronger); }
.diff-line-review.diff-line-commented { box-shadow: inset .18rem 0 var(--color-accent); }
.diff-line-review.diff-line-selected { box-shadow: inset .18rem 0 var(--color-accent); outline: 1px solid var(--color-accent); outline-offset: -1px; }
.diff-line-comment { position: sticky; left: 0; z-index: 1; flex: 0 0 1.8rem; padding: 0; border: 0; border-radius: .2rem; background: var(--color-fill-active); color: var(--color-text); cursor: pointer; opacity: 0; }
.diff-line-review:hover .diff-line-comment, .diff-line-comment:focus-visible { opacity: 1; }
.diff-line-comment:hover, .diff-line-comment:focus-visible { background: var(--color-highlight-bg); color: var(--color-highlight-fg); }
.diff-line-review .diff-gutter { left: 1.8rem; }
.diff-line-review .diff-gutter + .diff-gutter { left: 4.6rem; }
.diff-line-review .diff-sign { left: 7.4rem; }
.review-diff-scroll .diff-hunk, .review-diff-scroll .diff-note { padding-left: .6rem; }
.review-diff-content { min-width: 0; }
.review-comment-range-hint { display: block; margin-bottom: .2rem; color: var(--color-text-subtle); font-size: .68rem; }
.review-file-comment-action { display: block; margin: .1rem .45rem .1rem auto; padding: .1rem .25rem; border: 0; background: transparent; color: var(--color-accent); font-size: .68rem; cursor: pointer; }
.review-file-comment-action:hover { text-decoration: underline; }
.review-comment, .review-comment-editor { margin: .15rem .5rem .2rem 6rem; padding: .25rem .4rem; border: 1px solid var(--color-border-strong); border-radius: .35rem; background: var(--color-surface-raised); font-family: Inter, ui-sans-serif, system-ui, sans-serif; font-size: .75rem; line-height: 1.4; }
.review-comment-anchor { margin-bottom: .15rem; color: var(--color-accent); font-size: .68rem; }
.review-comment { box-sizing: border-box; width: fit-content; max-width: calc(100% - 6.5rem); }
.review-comment.is-editing, .review-comment-editor { box-sizing: border-box; width: min(30rem, calc(100% - 6.5rem)); }
.review-comment .review-comment-body { display: block; width: 100%; margin: 0 0 .2rem; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; white-space: pre-wrap; overflow-wrap: anywhere; cursor: pointer; }
.review-comment > .review-comment-editor { margin: 0; padding: 0; border: 0; background: transparent; }
.review-comment.is-editing > .review-comment-editor { width: 100%; }
.review-comment-actions { display: flex; gap: .15rem; justify-content: flex-end; }
.review-comment-editor textarea { display: block; box-sizing: border-box; width: 100%; min-height: 2.5rem; margin-bottom: .2rem; font: inherit; resize: vertical; }
.review-comment-icon-action, .message-review-editor-actions .review-comment-icon-action { display: inline-grid; place-items: center; width: 1.5rem; height: 1.5rem; padding: 0; border: 0; border-radius: .3rem; background: transparent; color: var(--color-text-muted); cursor: pointer; }
.review-comment-icon-action .app-icon { width: .85rem; height: .85rem; }
.review-comment-icon-action:hover:not(:disabled), .review-comment-icon-action:focus-visible { background: var(--color-fill-strong); color: var(--color-text-strong); }
.review-comment-icon-action.is-save { color: var(--color-accent); }
.review-comment-icon-action.is-danger:hover:not(:disabled), .review-comment-icon-action.is-danger:focus-visible { background: var(--color-danger-bg-hover); color: var(--color-danger-text); }
.review-comment-icon-action:disabled { opacity: .4; cursor: default; }
.review-tree-row { display: flex; align-items: center; gap: .2rem; min-width: 0; min-height: 1.6rem; padding: .1rem; font-size: .76rem; line-height: 1.2; }
.review-tree-row:hover { background: var(--color-fill-active); }
.review-tree-row input[type=checkbox] { flex: 0 0 .8rem; width: .8rem; height: .8rem; margin: 0 .1rem 0 0; vertical-align: middle; }
.review-tree-row a { display: inline-flex; align-items: center; gap: .25rem; flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; color: var(--color-text-secondary); text-decoration: none; }
.review-tree-row a:hover { text-decoration: underline; }
.review-tree-file-chevron { flex: none; width: .72rem; height: .72rem; color: var(--color-text-subtle); }
.review-tree-file-status { flex: 0 0 .8rem; color: var(--color-text-subtle); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .68rem; text-align: center; text-decoration: none; }
.review-tree-file-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.review-tree-counts { display: inline-grid; grid-template-columns: 2rem 2rem; align-items: center; gap: .2rem; flex: none; margin-left: auto; font-size: .7rem; font-variant-numeric: tabular-nums; }
.review-tree-counts .workspace-added, .review-tree-counts .workspace-removed { min-width: 0; }
.review-tree-counts .workspace-added { grid-column: 1; }
.review-tree-counts .workspace-removed { grid-column: 2; }
.review-tree-comments { flex: none; font-size: .7rem; line-height: 1; }
.review-tree-folder-toggle { display: flex; align-items: center; gap: .2rem; width: 100%; min-width: 0; padding: .1rem; border: 0; background: transparent; color: var(--color-text-secondary); text-align: left; font-size: .76rem; cursor: pointer; }
.review-tree-folder-toggle:hover { background: var(--color-fill-active); }
.review-tree-folder-toggle .app-icon { flex: none; width: .72rem; height: .72rem; color: var(--color-text-subtle); }
.review-tree-folder-toggle span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.review-tree-children { margin-left: .4rem; border-left: 1px solid var(--color-border); padding-left: .2rem; }
@media (width <= 900px) { .review-workspace { left: 0; width: 100vw; } .review-workspace-body { grid-template-columns: minmax(0, 1fr); } .review-diff-scroll { grid-column: 1; } .review-file-tree, .review-tree-resizer { display: none; } }

/* Memory: the person's entries and the open project's, one list beside the entry being edited. */
.memory-group-label { margin: 0.4rem 0 0.1rem; padding: 0 0.4rem; color: var(--color-text-secondary); font-size: 0.8rem; font-weight: 500; }
.memory-group-label:first-child { margin-top: 0; }
.memory-editor .settings-list .empty-hint { margin: 0; padding: 0 0.4rem; font-size: 0.8rem; }
.memory-kind { display: inline-flex; justify-self: start; gap: 0.25rem; padding: 0.25rem; border: 1px solid var(--color-border); border-radius: 0.6rem; background: var(--color-surface); }
.memory-editor .settings-list-select > small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.memory-disabled .settings-list-select > span { color: var(--color-text-faint); }
.memory-editor .settings-list-select > span { display: flex; gap: 0.35rem; align-items: center; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.memory-form textarea { min-height: 9rem; resize: vertical; }
.memory-meta { margin: 0; color: var(--color-text-faint); font-size: 0.8rem; }

/* Project instructions sit under the project's details in its settings drawer. */
.project-instructions { display: grid; min-width: 0; gap: .55rem; margin-top: 1.15rem; padding-top: 1.1rem; border-top: 1px solid var(--color-border); }
.project-instructions .section-heading { min-width: 0; }
.project-instructions .section-heading h3 { font-size: .94rem; }
.project-instructions .section-heading p { margin-top: .2rem; color: var(--color-text-subtle); font-size: .75rem; line-height: 1.4; }
.project-instructions-text { min-height: 7.5rem; max-height: 20rem; resize: vertical; font-family: inherit; font-size: .84rem; line-height: 1.45; }
.project-instructions .checkbox { display: flex; align-items: flex-start; gap: .5rem; min-width: 0; margin-top: .15rem; color: var(--color-text-secondary); font-size: .77rem; line-height: 1.4; white-space: normal; }
.project-instructions .checkbox input { flex: none; margin: .15rem 0 0; }
.project-instructions .checkbox span { min-width: 0; overflow-wrap: anywhere; }
.instruction-sources { display: grid; gap: .2rem; min-width: 0; margin: .15rem 0 0; padding: 0; list-style: none; font-size: .74rem; }
.instruction-sources li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .5rem; min-width: 0; }
.instruction-sources li > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; direction: rtl; text-align: left; }
.instruction-sources li small, .instruction-budget { color: var(--color-text-faint); }
.instruction-sources li.excluded > span { color: var(--color-text-faint); text-decoration: line-through; }
.instruction-budget { margin: 0; font-size: .73rem; }
.instruction-budget.over { color: var(--color-danger-text); }
.model-context-disclosure { min-width: 0; margin-top: .2rem; padding-top: .65rem; border-top: 1px solid var(--color-border); }
.model-context-disclosure > summary { display: flex; align-items: center; gap: .4rem; min-width: 0; color: var(--color-text-secondary); font-size: .8rem; cursor: pointer; list-style: none; }
.model-context-disclosure > summary::-webkit-details-marker { display: none; }
.model-context-disclosure > summary .app-icon { flex: none; width: .75rem; height: .75rem; transition: transform .15s ease; }
.model-context-disclosure[open] > summary .app-icon { transform: rotate(90deg); }
.model-context-stale { margin: .55rem 0 0 !important; color: var(--color-text-subtle); font-size: .73rem; }
.model-context-preview { display: grid; gap: .45rem; min-width: 0; padding-top: .55rem; font-size: .76rem; }
.model-context-preview > p { margin: 0; color: var(--color-text-subtle); line-height: 1.4; }
.model-context-preview details { min-width: 0; padding: .45rem .55rem; border: 1px solid var(--color-border); border-radius: .4rem; }
.model-context-preview summary { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .5rem; min-width: 0; cursor: pointer; }
.model-context-preview summary strong { min-width: 0; overflow-wrap: anywhere; font-weight: 500; }
.model-context-preview summary small { color: var(--color-text-faint); white-space: nowrap; }
.model-context-preview pre { box-sizing: border-box; max-width: 100%; max-height: 16rem; margin: .5rem 0 0; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font-size: .72rem; line-height: 1.45; }

/* Links to local paths in messages: they are not followed; a right-click offers to attach them. */
/* One link colour in messages, from the theme: without it links took the browser's defaults —
   blue, and purple once visited — while a path in a code span that became a link kept the text
   colour. A local path differs only by its dashed underline. */
.markdown-content a,
.markdown-content a:visited,
.markdown-content .file-link { color: var(--color-accent); }
.markdown-content a:hover,
.markdown-content .file-link:hover { color: var(--color-accent-strong); }
/* A local path carries the same mark as a message comment — the same tint and underline — and is
   told apart by being a link: accent text, and after it an icon for what the project may do there
   instead of the comment icon. */
.markdown-content .file-link {
    background: color-mix(in srgb, var(--color-accent) 22%, transparent);
    text-decoration: underline solid color-mix(in srgb, var(--color-accent) 80%, transparent);
    border-radius: .25rem;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
    cursor: context-menu;
}
.markdown-content .file-link[data-file-access="none"] {
    color: var(--color-text-muted);
    background: color-mix(in srgb, var(--color-text-muted) 22%, transparent);
    text-decoration-color: color-mix(in srgb, var(--color-text-muted) 80%, transparent);
}
.markdown-content .file-link[data-file-access]::after {
    content: "";
    display: inline-block;
    width: 0.75em;
    height: 0.75em;
    margin-left: 0.2em;
    vertical-align: -0.05em;
    background-color: var(--file-access-color);
    -webkit-mask: var(--file-access-icon) center / contain no-repeat;
    mask: var(--file-access-icon) center / contain no-repeat;
}
.markdown-content .file-link[data-file-access="read"] { --file-access-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='10' width='16' height='11' rx='2' fill='black' stroke='none'/%3E%3Cpath d='M8 10V7a4 4 0 0 1 8 0v3'/%3E%3C/svg%3E"); --file-access-color: var(--color-text-faint); }
.markdown-content .file-link[data-file-access="write"] { --file-access-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='6' y='11' width='15' height='10' rx='2' fill='black' stroke='none'/%3E%3Cpath d='M9 11V7a4 4 0 0 1 7.8-1.2'/%3E%3C/svg%3E"); --file-access-color: var(--color-accent); }
.markdown-content .file-link[data-file-access="none"] { --file-access-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M5.6 5.6l12.8 12.8'/%3E%3C/svg%3E"); --file-access-color: var(--color-danger-text); }
.file-link-menu { position: fixed; top: auto; right: auto; }
.file-link-menu-path { overflow: hidden; padding: 0.35rem 0.9rem 0.2rem; color: var(--color-text-faint); font-size: 0.8rem; text-overflow: ellipsis; white-space: nowrap; }
