/*
 * Loudly stylesheet: copied to dist/loudly/loudly.css and linked from
 * index.html after LibreChat's own stylesheet (see the Dockerfile), so rules
 * here win over upstream rules of equal specificity.
 *
 * Implements "Loudly Agent chat: style handover" (28 Oct 2026) for the icon
 * rail, sidebar and header, and "Rae type system v3, launch spec"
 * (design.html) for the conversation: agent label, message body, activity
 * rows, output block, composer. Style only: no element is added, removed or
 * reordered; elements the spec names that LibreChat does not render are
 * skipped.
 *
 * Upstream's colours are CSS variables holding bare RGB triplets
 * ("22 22 29", used as rgb(var(--x) / alpha)) on `html` (light) and `.dark`.
 * The spec tokens are declared first, then upstream's variables are pointed
 * at them. Every variable set in `html` is set again in `html.dark`.
 */

/* ===== Fonts: self-hosted (branding/fonts, SIL OFL). Inter static 400/500/600
 * from the official v4.1 release, which keeps the cv11 and ss01 features;
 * JetBrains Mono 400, latin. ===== */

@font-face {
  font-family: "Inter";
  src: url("fonts/inter-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Inter";
  src: url("fonts/inter-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Inter";
  src: url("fonts/inter-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jetbrains-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ===== Tokens ===== */

/* Names and values follow the "Tokens" block of type_system.html (Rae type
 * system v4). Its dark selector, [data-theme="dark"], is html.dark here (that is
 * the class LibreChat sets). Colours and fallbacks not in that block come
 * from the earlier handover (rows, lines, raised, ink). Type tokens are
 * `font` shorthands: `font` resets font-feature-settings and
 * font-variant-numeric, so every rule using one sets them again after it. */
html {
  --font-sans: Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-features: "cv11", "ss01";

  /* type */
  --text-body:          400 16px/1.6 var(--font-sans);
  --text-user:          400 16px/1.5 var(--font-sans);
  --text-input:         400 16px/1.5 var(--font-sans);
  --text-name:          600 14px/1.2 var(--font-sans);
  --text-player-label:  500 13px/1.3 var(--font-sans);
  --text-link:          500 13px/1.3 var(--font-sans);
  --text-tooltip:       500 13px/1.3 var(--font-sans);
  --text-activity-line: 400 12px/1.4 var(--font-mono);  /* whole line, one element */
  --text-activity-meta: 400 13px/1.3 var(--font-sans);  /* tabular-nums */
  --text-output:        400 12px/1.6 var(--font-mono);
  --text-caps:          600 11px/1.2 var(--font-sans);  /* uppercase, 0.06em */
  /* player widget: as shipped, not tokenised */

  /* spacing */
  --chat-max-width: 680px;
  --space-paragraph: 12px;
  --space-block: 16px;
  --space-attached: 8px;   /* row to its items or output, label to card, card to Download, last block to actions */
  --space-items: 6px;
  --radius-output: 8px;
  --radius-composer: 18px;
  --composer-min-height: 96px;

  /* message actions */
  --action-icon: 16px;
  --action-hit: 32px;
  --action-radius: 8px;

  /* colour, light */
  --color-text: #16161d;
  --color-muted: #5e5e6b;
  --color-accent-text: #5b46d6;
  --color-accent: #7c68ee;
  --color-bg: #ffffff;
  --color-surface: #f7f7f9;
  --color-bubble: #f0eff5;
  --color-border: #e4e4ea;
  --row-active: #e9e8f0;
  --row-hover: #eeedf3;
  --line-strong: #cfcfda;
  --composer-border: #d9d9e2;
  --link-underline: #c9c1f5;
  --color-surface2: #efeff3;
  --raised: #ffffff;
  --color-ink: #16161d;
  --color-on-ink: #ffffff;

  /* Upstream variables (RGB triplets) */
  --text-primary: 22 22 29;
  --text-secondary: 94 94 107;
  --text-secondary-alt: 94 94 107;
  --text-tertiary: 94 94 107;
  --link: 91 70 214;
  --link-hover: 91 70 214;
  --link-visited: 91 70 214;
  --ring-primary: 124 104 238;
  --presentation: 255 255 255;
  --header-primary: 255 255 255;
  --surface-primary: 255 255 255;
  --surface-chat: 255 255 255;
  --surface-dialog: 255 255 255;
  --surface-primary-alt: 247 247 249;
  --surface-primary-contrast: 247 247 249;
  --surface-secondary: 247 247 249;
  --surface-secondary-alt: 247 247 249;
  --surface-tertiary: 240 239 245;
  --surface-tertiary-alt: 240 239 245;
  --surface-hover: 238 237 243;
  --surface-hover-alt: 238 237 243;
  --surface-active: 233 232 240;
  --surface-active-alt: 233 232 240;
  --border-light: 228 228 234;
  --border-medium: 228 228 234;
  --border-medium-alt: 228 228 234;
  --border-heavy: 207 207 218;
  --border-xheavy: 207 207 218;

  /* Markdown sizes in upstream are multiples of this */
  --markdown-font-size: 16px;
  --theme-font-family: var(--font-sans);
}

html.dark {
  --color-text: #ededf2;
  --color-muted: #9c9cae;
  --color-accent-text: #a99bf6;
  --color-accent: #7c68ee;
  --color-bg: #0d0d12;
  --color-surface: #15151d;
  --color-bubble: #1e1e29;
  --color-border: #272734;
  --row-active: #24242f;
  --row-hover: #1e1e29;
  --line-strong: #3a3a4a;
  --composer-border: #31313f;
  --link-underline: #4e4486;
  --color-surface2: #272734;
  --raised: #1b1b25;
  --color-ink: #ededf2;
  --color-on-ink: #0d0d12;

  --text-primary: 237 237 242;
  --text-secondary: 156 156 174;
  --text-secondary-alt: 156 156 174;
  --text-tertiary: 156 156 174;
  --link: 169 155 246;
  --link-hover: 169 155 246;
  --link-visited: 169 155 246;
  --ring-primary: 124 104 238;
  --presentation: 13 13 18;
  --header-primary: 13 13 18;
  --surface-primary: 13 13 18;
  --surface-chat: 21 21 29;
  --surface-dialog: 21 21 29;
  --surface-primary-alt: 21 21 29;
  --surface-primary-contrast: 21 21 29;
  --surface-secondary: 21 21 29;
  --surface-secondary-alt: 21 21 29;
  --surface-tertiary: 30 30 41;
  --surface-tertiary-alt: 30 30 41;
  --surface-hover: 30 30 41;
  --surface-hover-alt: 30 30 41;
  --surface-active: 36 36 47;
  --surface-active-alt: 36 36 47;
  --border-light: 39 39 52;
  --border-medium: 39 39 52;
  --border-medium-alt: 39 39 52;
  --border-heavy: 58 58 74;
  --border-xheavy: 58 58 74;

  color-scheme: dark;
}

/* ===== App root ===== */

html,
:host,
.font-sans,
.font-theme-ui {
  font-family: var(--font-sans);
}

html {
  font-feature-settings: var(--font-features);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code,
kbd,
samp,
pre,
.font-mono {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 400;
}

/* Focus: 2px accent outline, offset 2, on buttons, links and inputs. The
 * prompt textarea is inside the composer box and keeps upstream's look. */
:is(button, a, input, select, textarea, [role="button"]):focus-visible:not(#prompt-textarea) {
  outline: 2px solid var(--color-accent) !important;
  outline-offset: 2px !important;
  --tw-ring-shadow: 0 0 #0000 !important;
  box-shadow: none !important;
}

/* ===== Type scale cap: only 16/14/13/12/11px, nothing above 16 =====
 * 16 for anything the agent or the user says, 14 names and titles, 13 system
 * activity, 12 mono, 11 caps labels. Upstream's text-lg and larger
 * utilities, and bare headings, are brought down to 16. Hierarchy comes from
 * weight and colour. Declared before the per-area rules so those (same or
 * higher specificity, later) win. */
[class*="text-lg"],
[class*="text-xl"],
[class*="text-2xl"],
[class*="text-3xl"],
[class*="text-4xl"],
[class*="text-5xl"],
:is(h1, h2, h3, h4, h5, h6):not(.sr-only) {
  font-size: 16px;
  line-height: 1.2;
}

/* ===== Icon rail ===== */

aside > div > div:first-child:has([data-testid="nav-user"]) {
  width: 56px;
  box-sizing: border-box;
  align-items: center;
  gap: 4px;
  padding: 12px 8px;
  border-right: 1px solid var(--color-border);
  background-color: var(--color-surface);
}

aside > div > div:first-child:has([data-testid="nav-user"]) :is(
  [data-testid="close-sidebar-button"],
  [data-testid="new-chat-button"],
  [data-testid^="nav-panel-"],
  [data-testid="nav-user"]
) {
  width: 40px;
  height: 40px;
  border-radius: 10px;
}

/* Separator between New chat and Chats */
aside > div > div:first-child:has([data-testid="nav-user"]) > .border-b {
  width: 24px;
  height: 0;
  margin: 6px 0;
  border-bottom: 1px solid var(--color-border);
}

/* ===== Icon rail icons =====
 * Replaced with one line set (Lucide-style, 1.75 stroke, from side_icons.png).
 * The original <svg> stays in the DOM but is hidden; the new icon is painted
 * by ::before as a mask over the button's text colour, so active, hover and
 * theme colours keep working. Matched on data-testid (not localized).
 * Close sidebar and New chat drew their icon in text-primary upstream. */

[data-testid="close-sidebar-button"] { /* panel-left */
  --loudly-icon: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%221.75%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Crect width=%2218%22 height=%2218%22 x=%223%22 y=%223%22 rx=%222%22/%3E%3Cpath d=%22M9 3v18%22/%3E%3C/svg%3E");
}

[data-testid="new-chat-button"] { /* square-pen */
  --loudly-icon: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%221.75%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7%22/%3E%3Cpath d=%22M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z%22/%3E%3C/svg%3E");
}

[data-testid="nav-panel-conversations"] { /* messages-square */
  --loudly-icon: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%221.75%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M14 9a2 2 0 0 1-2 2H6l-4 4V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2z%22/%3E%3Cpath d=%22M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1%22/%3E%3C/svg%3E");
}

[data-testid="nav-panel-agents"] { /* bot */
  --loudly-icon: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%221.75%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M12 8V5%22/%3E%3Crect width=%2216%22 height=%2212%22 x=%224%22 y=%228%22 rx=%222%22/%3E%3Cpath d=%22M9 13v2%22/%3E%3Cpath d=%22M15 13v2%22/%3E%3C/svg%3E");
}

[data-testid="nav-panel-skills"] { /* document */
  --loudly-icon: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%221.75%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Crect width=%2214%22 height=%2218%22 x=%225%22 y=%223%22 rx=%222%22/%3E%3Cpath d=%22M9 8h6%22/%3E%3Cpath d=%22M9 12h6%22/%3E%3Cpath d=%22M9 16h4%22/%3E%3C/svg%3E");
}

[data-testid="nav-panel-files"] { /* paperclip */
  --loudly-icon: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%221.75%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551%22/%3E%3C/svg%3E");
}

[data-testid="nav-panel-mcp-builder"] { /* tag */
  --loudly-icon: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%221.75%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpath d=%22M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z%22/%3E%3Ccircle cx=%227.5%22 cy=%227.5%22 r=%221%22 fill=%22black%22 stroke=%22none%22/%3E%3C/svg%3E");
}

[data-testid="close-sidebar-button"] > svg,
[data-testid="new-chat-button"] > svg,
[data-testid="nav-panel-conversations"] > svg,
[data-testid="nav-panel-agents"] > svg,
[data-testid="nav-panel-skills"] > svg,
[data-testid="nav-panel-files"] > svg,
[data-testid="nav-panel-mcp-builder"] > svg {
  display: none;
}

[data-testid="close-sidebar-button"]::before,
[data-testid="new-chat-button"]::before,
[data-testid="nav-panel-conversations"]::before,
[data-testid="nav-panel-agents"]::before,
[data-testid="nav-panel-skills"]::before,
[data-testid="nav-panel-files"]::before,
[data-testid="nav-panel-mcp-builder"]::before {
  content: "";
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  background-color: currentColor;
  -webkit-mask: var(--loudly-icon) center / contain no-repeat;
  mask: var(--loudly-icon) center / contain no-repeat;
}

:is([data-testid="close-sidebar-button"], [data-testid="new-chat-button"])::before {
  background-color: rgb(var(--text-primary));
}

/* ===== Chat sidebar ===== */

/* 240 wide: the aside holds the 56px rail plus the list. Upstream sets an
 * inline, user-resizable width (min 360), hence !important while open. */
aside:has(> div > nav[aria-hidden="false"]) {
  width: 296px !important;
  min-width: 296px !important;
}

aside nav {
  border-right: 1px solid var(--color-border);
  background-color: var(--color-surface);
}

aside nav [role="region"] {
  padding: 12px 8px;
}

aside nav [role="region"] .px-3 {
  padding-left: 0;
  padding-right: 0;
}

/* "Projects" and "Chats" rows: name style, 32 high, icon buttons 32x32 */
aside nav [role="region"] .h-8 {
  height: 32px;
  padding-right: 0;
}

aside nav [role="region"] .h-8 > button:first-child {
  color: var(--color-text);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
}

aside nav [role="region"] .h-8 > button:last-child {
  width: 32px;
  height: 32px;
  border-radius: 8px;
}

/* Date group label ("Yesterday"): badge style. Row heights come from the
 * virtualised list, so the label sits at the bottom of its slot. */
aside nav [data-testid="convo-list-row"] h2 {
  display: flex;
  align-items: flex-end;
  box-sizing: border-box;
  height: 100%;
  margin: 0;
  padding: 0 0 6px 8px;
  color: var(--color-muted);
  font-size: 12px !important; /* upstream sets an inline 0.7rem */
  font-weight: 500;
  line-height: 1.2;
}

/* Chat rows */
aside nav [data-testid="convo-item"] {
  height: 36px;
  border-radius: 8px;
}

aside nav [data-testid="convo-item"] > button {
  height: 100%;
  gap: 10px;
  padding: 0 8px;
  border-radius: 8px;
  color: var(--color-text);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.2;
}

aside nav [data-testid="convo-item"] > button > * {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Upstream marks the active row with a 2px bar on its left (::before);
 * the design has none. */
aside nav [data-testid="convo-item"]::before {
  content: none;
}

aside nav [data-testid="convo-item"]:has([aria-current="page"]) > button {
  font-weight: 500;
}

aside nav [data-testid="convo-item"] > div button {
  width: 28px;
  height: 28px;
}

/* ===== Header ===== */

main div.absolute.top-0:has([data-testid="model-selector-button"]) {
  height: 56px;
  box-sizing: border-box;
  padding: 0 16px;
  border-bottom: 1px solid var(--color-border);
  background: var(--color-bg);
}

[data-testid="model-selector-button"] {
  height: 36px;
  padding: 0 12px 0 8px;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  color: var(--color-text);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.2;
}

#export-menu-button {
  width: 36px;
  height: 36px;
  border: 1px solid var(--color-border);
  border-radius: 10px;
}

/* ===== Chat column ===== */

/* Max width 680, centred; 48 sides; 32 between turns (16 + 16), 32 top
 * (16 here + 16 on the first turn, below the 56px header). */
[data-testid="screenshot-target"] {
  padding-top: 16px;
}

div:has(> div.message-render) {
  padding-top: 16px;
  padding-bottom: 16px;
}

@media (min-width: 768px) {
  div:has(> div.message-render) {
    padding-left: 48px;
    padding-right: 48px;
  }
}

div.message-render {
  max-width: var(--chat-max-width);
  padding-left: 0;
  padding-right: 0;
}

/* ===== Rae reply (type system v4) =====
 *
 * Structure LibreChat renders for an assistant turn (v0.8.8):
 *   .agent-turn                         flex column: speaker row, content
 *     > h2                              speaker row (avatar + name)
 *     > div                             flex column: message body, actions
 *       > [data-testid=message-body]    flex column, one child per block:
 *            .text-message (markdown), activity group, single activity row
 *            (+ its output wrapper), more .text-message
 *       > div.w-full                    message actions
 *
 * Rhythm is set with gap on these flex columns. Where two kinds of
 * neighbour need different spacing in the same column (12 between
 * paragraphs vs 16 between blocks; 8 from a row to its own output; 8 from
 * the player label to the card), the closer pair is pulled in by the
 * difference with a negative margin, because CSS cannot give one flex
 * column two gap values. */

/* --- Turn columns --- */

.agent-turn {
  gap: var(--space-block);              /* name to first block: 16 */
}

.agent-turn > h2 {
  gap: 8px;                             /* avatar to name: 8 */
  min-height: 0;
  margin: 0;
  color: var(--color-text);
  font: var(--text-name);
  font-feature-settings: var(--font-features);
}

.agent-turn > div:has(> [data-testid="message-body"]) {
  gap: var(--space-attached);           /* last block to actions: 8 */
}

.agent-turn [data-testid="message-body"] {
  gap: var(--space-block);              /* between different blocks: 16 */
}

.agent-turn [data-testid="message-body"] > * {
  margin-top: 0 !important;             /* upstream mb-2 / mt-1 / my-1.5 / [.text-message+&]:mt-5 */
  margin-bottom: 0 !important;
}

.agent-turn .text-message {
  gap: var(--space-block);
}

/* --- Activity rows ---
 * Every row (skill, tool call, memory, file …) is the same component:
 *   div:has(> .progress-text-wrapper)    row wrapper
 *     .progress-text-content > button
 *       > span[aria-hidden]:first-child  icon
 *       > span.flex > span (1 or 2)      the line ("Ran get_presets", "in loudly")
 *       > span.shrink-0:not([aria-hidden])  meta ("· Tool call 1s", "· Total elapsed 1.4s")
 * A group ("Ran 2 actions") has its own summary button with
 * span.tool-status-text (the line) and a meta span ("· Skill, loudly"). */

:is(div:has(> .progress-text-wrapper), button:has(> .tool-status-text)) {
  min-height: 24px;
}

div:has(> .progress-text-wrapper) {
  height: auto;
}

.progress-text-wrapper {
  height: 24px;
  line-height: 24px;
}

.progress-text-content > button,
button:has(> .tool-status-text) {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 24px;
  color: var(--color-muted);
}

.progress-text-content > button > span.flex:not([aria-hidden]) {
  gap: 0.6ch;                           /* one space between "Ran x" and "in loudly" */
}

/* The line: one element, JetBrains Mono 12/1.4, muted; never bold */
.progress-text-content > button > span.flex:not([aria-hidden]) > span,
button:has(> .tool-status-text) > .tool-status-text {
  color: var(--color-muted);
  font: var(--text-activity-line);
  font-family: var(--font-mono);
  font-feature-settings: var(--font-features);
  font-variant-numeric: tabular-nums;
}

/* Expandable group rows ("Ran 2 actions") in text colour */
button:has(> .tool-status-text) > .tool-status-text {
  color: var(--color-text);
}

/* Meta: Inter 13/1.3, muted, tabular figures (failure text keeps its colour) */
.progress-text-content > button > span.shrink-0:not([aria-hidden]),
.progress-text-content > button > span.shrink-0:not([aria-hidden]) *,
button:has(> .tool-status-text) > span:not(.tool-status-text) {
  font: var(--text-activity-meta);
  font-feature-settings: var(--font-features);
  font-variant-numeric: tabular-nums;
}

.progress-text-content > button > span.shrink-0:not([aria-hidden]):not(.text-status-error),
button:has(> .tool-status-text) > span:not(.tool-status-text) {
  color: var(--color-muted);
}

/* Icons: 16 muted on top-level rows, 14 on group items; chevron muted */
.progress-text-content > button svg,
button:has(> .tool-status-text) svg {
  color: var(--color-muted);
}

.progress-text-content > button > span[aria-hidden="true"]:first-child {
  min-width: 16px;
  width: 16px;
}

.progress-text-content > button > span[aria-hidden="true"]:first-child svg {
  width: 16px;
  height: 16px;
}

[data-testid="tool-call-group-panel"] .progress-text-content > button > span[aria-hidden="true"]:first-child {
  min-width: 14px;
  width: 14px;
}

[data-testid="tool-call-group-panel"] .progress-text-content > button > span[aria-hidden="true"]:first-child svg {
  width: 14px;
  height: 14px;
}

/* Group: summary row, then items 8 below; 1px rule under the group icon,
 * 16 to the item text; items 6 apart (gap), 18px high (12/1.4 line) */
.agent-turn [data-testid="message-body"] > div:has(> [data-testid="tool-call-group-panel"]) {
  display: flex;
  flex-direction: column;
  gap: var(--space-attached);
}

[data-testid="tool-call-group-panel"] > div {
  margin-left: 11px;
  padding-left: 16px;
  border-left: 1px solid var(--color-border);
}

[data-testid="tool-call-group-panel"] > div::before {
  content: none;
}

[data-testid="tool-call-group-panel"] > div > div {
  gap: var(--space-items);
  padding: 2px 0;
}

[data-testid="tool-call-group-panel"] > div > div > * {
  margin: 0;
}

[data-testid="tool-call-group-panel"] div:has(> .progress-text-wrapper),
[data-testid="tool-call-group-panel"] .progress-text-wrapper,
[data-testid="tool-call-group-panel"] .progress-text-content > button {
  min-height: 0;
  height: 18px;
  line-height: 18px;
}

/* Output wrappers follow their row in the same flex column. Collapsed they
 * are 0 high, so they take back the gap they add; open, the box inside
 * sits 8 below its row. */
.agent-turn [data-testid="message-body"] > div:has(> .progress-text-wrapper) + div {
  margin-top: calc(-1 * var(--space-block)) !important;
}

[data-testid="tool-call-group-panel"] div:has(> .progress-text-wrapper) + div {
  margin-top: calc(-1 * var(--space-items));
}

/* Output block: surface fill, 1px border, radius 8, padding 12/14, 8 below
 * its row; "OUTPUT" caps label; body JetBrains Mono 12/1.6, wraps */
div:has(> .progress-text-wrapper) + div > div > div.rounded-lg {
  margin: var(--space-attached) 0 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-output);
  background-color: var(--color-surface);
}

div:has(> .progress-text-wrapper) + div > div > div.rounded-lg > div {
  padding: 12px 14px;
  background: none;
}

div:has(> .progress-text-wrapper) + div > div > div.rounded-lg div.uppercase {
  margin-bottom: 6px;
  color: var(--color-muted);
  font: var(--text-caps);
  font-feature-settings: var(--font-features);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

div:has(> .progress-text-wrapper) + div > div > div.rounded-lg pre {
  color: var(--color-text);
  font: var(--text-output);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ===== Reply body ===== */

/* Body 16/1.6. The markdown is a flex column: 16 between blocks (gap),
 * 12 between consecutive paragraphs. */
.agent-turn .markdown {
  display: flex;
  flex-direction: column;
  gap: var(--space-block);
  color: var(--color-text);
  font: var(--text-body);
  font-feature-settings: var(--font-features);
}

.agent-turn .markdown > * {
  margin-top: 0;
  margin-bottom: 0;
}

.agent-turn .markdown > p + p {
  margin-top: calc(var(--space-paragraph) - var(--space-block));
}

/* Nothing larger than 16px: headings differ by weight only */
.markdown :is(h1, h2, h3, h4, h5, h6) {
  margin: 0 0 var(--space-block);
  color: var(--color-text);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.2;
}

.agent-turn .markdown > :is(h1, h2, h3, h4, h5, h6) {
  margin: 0;
}

.markdown :is(strong, b) {
  font-weight: 600;
}

/* .markdown.prose outranks upstream's `p.whitespace-pre-wrap a, li a`
 * (#06c, 700). */
.markdown.prose a,
.markdown.prose a:visited {
  color: var(--color-accent-text);
  font-weight: 500;
  text-decoration-line: underline;
  text-decoration-color: var(--link-underline);
  text-underline-offset: 3px;
  overflow-wrap: anywhere;
}

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

.agent-turn .markdown ul {
  padding-left: 22px;
}

.agent-turn .markdown li {
  margin-top: 0;
  margin-bottom: 0;
  padding-left: 0;
}

.agent-turn .markdown li + li {
  margin-top: 4px;
}

/* --- Audio player block ---
 * The widget is an mcp-ui iframe (span.mx-1.inline-block.w-full) placed in
 * a markdown paragraph together with the "Download" link; the label is the
 * block right before that paragraph. The widget itself is not touched. */
.agent-turn .markdown > p:has(> span.mx-1.inline-block.w-full) {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-attached);           /* card to Download: 8 */
  margin-top: 0;                        /* 16 from the block above (or 8 from its label, below) */
}

.agent-turn .markdown > p > span.mx-1.inline-block.w-full {
  margin: 0;                            /* card and Download share the left edge */
}

.agent-turn .markdown > p:has(> span.mx-1.inline-block.w-full) > a,
.agent-turn .markdown.prose > p:has(> span.mx-1.inline-block.w-full) > a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  color: var(--color-accent-text);
  font: var(--text-link);
  font-feature-settings: var(--font-features);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: currentColor;
  text-underline-offset: 3px;
}

/* Label: the block right before the player paragraph; 8 to the card */
.agent-turn .markdown > :is(p, h1, h2, h3, h4, h5, h6):has(+ p > span.mx-1.inline-block.w-full) {
  overflow: hidden;
  margin-top: 0;
  color: var(--color-muted);
  font: var(--text-player-label);
  font-feature-settings: var(--font-features);
  text-overflow: ellipsis;
  white-space: nowrap !important;
}

.agent-turn .markdown > :is(p, h1, h2, h3, h4, h5, h6):has(+ p > span.mx-1.inline-block.w-full) :is(strong, b, em) {
  color: inherit;
  font-weight: inherit;
}

.agent-turn .markdown > :is(p, h1, h2, h3, h4, h5, h6):has(+ p > span.mx-1.inline-block.w-full) + p {
  margin-top: calc(var(--space-attached) - var(--space-block));
}

/* The block after the player is a different block: 16, not 12 */
.agent-turn .markdown > p:has(> span.mx-1.inline-block.w-full) + p {
  margin-top: 0;
}

/* --- Message actions ---
 * Same icons, same order. 16px muted icons in 32x32 buttons, radius 8, no
 * gap; the row is pulled 8 left so the first icon lines up with the text. */
.agent-turn > div > div:has(> div > [data-testid="copy-response-button"]) {
  margin: 0;
}

.agent-turn > div > div > div:has(> div > [data-testid="copy-response-button"]) {
  min-height: var(--action-hit);
  margin: 0 0 0 -8px;
  gap: 0;
}

.agent-turn div:has(> [data-testid="copy-response-button"]) {
  gap: 0;
}

.agent-turn div:has(> [data-testid="copy-response-button"]) > button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--action-hit);
  height: var(--action-hit);
  margin: 0;
  padding: 0;
  border-radius: var(--action-radius);
  background: none;
  color: var(--color-muted);
}

.agent-turn div:has(> [data-testid="copy-response-button"]) > button svg {
  width: var(--action-icon);
  height: var(--action-icon);
  color: inherit;
}

.agent-turn div:has(> [data-testid="copy-response-button"]) > button:is(:hover, :focus-visible),
.agent-turn div:has(> [data-testid="copy-response-button"]) > button[aria-pressed="true"] {
  color: var(--color-text);
}

.agent-turn div:has(> [data-testid="copy-response-button"]) > button:is(:hover, :focus-visible) {
  background-color: var(--color-surface2);
}

/* Older replies: actions on hover or focus inside the turn; the latest
 * reply and touch screens always show them. visibility keeps the space. */
@media (hover: hover) {
  [data-testid="screenshot-target"] > div:has(.agent-turn):has(~ div .agent-turn)
    div:has(> [data-testid="copy-response-button"]) {
    visibility: hidden;
  }

  [data-testid="screenshot-target"] > div:has(.agent-turn):has(~ div .agent-turn):is(:hover, :focus-within)
    div:has(> [data-testid="copy-response-button"]) {
    visibility: visible;
  }
}

/* Tooltips LibreChat shows on its buttons: Inter 13/500 on ink */
.tooltip {
  padding: 4px 8px;
  border-radius: 6px;
  background-color: var(--color-ink) !important;
  color: var(--color-on-ink) !important;
  font: var(--text-tooltip);
  font-feature-settings: var(--font-features);
}

/* ===== User bubble ===== */

.user-turn {
  max-width: 80% !important;
}

.user-turn [data-testid="message-body"] {
  padding: 10px 16px;
  border-radius: 18px;
  background-color: var(--color-bubble);
}

.user-turn .markdown,
.user-turn .markdown p {
  margin: 0;
  color: var(--color-text);
  font: var(--text-user);
  font-feature-settings: var(--font-features);
}

/* ===== Composer =====
 * Radius 18, 1px border, raised fill, min-height 96, padding 12/56/12/16.
 * The send button (36) is pinned 10 from the bottom right corner; the 56px
 * right padding keeps typed text clear of it. Input 16/1.5 (never under 16:
 * iOS Safari zooms on focus), 2 rows, grows to 8 (a max height; LibreChat
 * sizes the textarea itself). Upstream's toolbar row stays in flow: it is
 * empty (0 high) unless LibreChat puts other controls in it. */
form:has(#prompt-textarea) {
  max-width: var(--chat-max-width);
}

[data-testid="composer-surface"] {
  position: relative;
  box-sizing: border-box;
  min-height: var(--composer-min-height);
  padding: 12px 56px 12px 16px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-composer);
  background-color: var(--raised);
  box-shadow: none;
}

[data-testid="composer-surface"] > div:first-child > div:not(:first-child):empty {
  display: none;                        /* upstream pr-2.5 spacer */
}

#prompt-textarea {
  min-height: calc(2 * 16px * 1.5);
  max-height: calc(8 * 16px * 1.5) !important;
  padding: 4px 0;
  color: var(--color-text);
  font: var(--text-input);
  font-feature-settings: var(--font-features);
}

#prompt-textarea::placeholder {
  color: var(--color-muted);
  font-size: 16px;
}

[data-testid="composer-surface"] > div:last-child {
  padding: 0;
}

[data-testid="composer-surface"] div:has(> #send-button) {
  position: absolute;
  right: 10px;
  bottom: 10px;
  margin: 0;
}

/* Send: 36px. Empty: surface2 fill, muted icon. Ready: ink fill, on-ink icon. */
#send-button,
[data-testid="send-button"] {
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: 50%;
  background-color: var(--color-ink);
  color: var(--color-on-ink);
}

#send-button svg,
[data-testid="send-button"] svg {
  color: var(--color-on-ink);
}

#send-button:disabled,
[data-testid="send-button"]:disabled {
  background-color: var(--color-surface2);
  color: var(--color-muted);
  opacity: 1;
}

#send-button:disabled svg,
[data-testid="send-button"]:disabled svg {
  color: var(--color-muted);
}

/* ===== Buttons and clickable labels: control style ===== */

[role="menuitem"],
[role="option"] {
  font-size: 13px;
  font-weight: 500;
}

/* ===== User menu (avatar): hide "Help", "Archived chats", "Settings" =====
 * The only item in the account menu that opens a submenu (Help & FAQ,
 * keyboard shortcuts, terms/privacy links). Matched on aria-haspopup, not
 * the localized label. The keyboard-shortcuts dialog still opens with its
 * shortcut. */
.account-settings-popover > [role="menuitem"][aria-haspopup="menu"] {
  display: none;
}

/* "Settings" has a data-testid; "Archived chats" has none and is matched as
 * the item right before it. The separator after Settings is hidden too, so
 * the menu reads: email, one line, Log out. */
.account-settings-popover > [data-testid="nav-settings"],
.account-settings-popover > [role="menuitem"]:has(+ [data-testid="nav-settings"]),
.account-settings-popover > [data-testid="nav-settings"] + [role="separator"] {
  display: none;
}

/* ===== Icon rail: hide Agent Builder, Skills, Attach Files, MCP Settings =====
 * Matched on data-testid (not localized). The panels stay reachable only
 * through their URLs or keyboard shortcuts, if any. */
aside [data-testid="nav-panel-agents"],
aside [data-testid="nav-panel-skills"],
aside [data-testid="nav-panel-files"],
aside [data-testid="nav-panel-mcp-builder"] {
  display: none !important;
}

/* ===== Login / register pages: hide the theme toggles =====
 * Neither button has a data-testid or id. The anchor is the theme toggle's
 * aria-keyshortcuts="Ctrl+Shift+T": a hardcoded literal (not localized, not
 * platform-dependent) that appears once in the whole client bundle. The
 * high-contrast toggle has no hook of its own, so it is reached through the
 * container the two share; the theme button is also matched directly, so it
 * stays hidden if upstream ever splits them. Only the buttons are hidden:
 * the Ctrl+Shift+T shortcut still switches theme. */
div:has(> button[aria-keyshortcuts="Ctrl+Shift+T"]),
button[aria-keyshortcuts="Ctrl+Shift+T"] {
  display: none;
}

/* ===== Agent contact line ("Contact: …"): hidden =====
 * Shown under an agent's name and description on the chat landing page (and
 * on marketplace cards/dialogs, which are disabled here). No data-testid,
 * id or aria hook, and the "Contact:" label is localized, so it is matched
 * by structure: a div whose only children are span.shrink-0 (label) then
 * span.min-w-0.truncate (name, email or "No contact available"). That pair
 * occurs only in this component in the v0.8.8 bundle (checked). The whole
 * line is hidden, not just the value, so no bare "Contact:" is left. */
div:has(> span.shrink-0:first-child + span.min-w-0.truncate:last-child) {
  display: none;
}
