/* ===========================================================================
   Curvo Enclave — brand theme override for LibreChat
   ---------------------------------------------------------------------------
   Loaded AFTER LibreChat's bundled stylesheet, so these definitions win on
   cascade order without !important.

   HOW IT WORKS
   LibreChat is layered: a primitive palette (--gray-*, --red-*, …) and a
   semantic layer (--surface-*, --text-*, --border-*, --header-*) defined in
   terms of those primitives, e.g. `--surface-secondary: var(--gray-50)`.
   Crucially the `.dark` block maps onto the SAME primitives. So redefining the
   gray ramp re-themes light and dark together, and we only override semantic
   tokens where Curvo needs a hue the ramp can't express (accents, focus).

   ⚠️ VERIFIED AGAINST v0.8.8-rc2 (bundle index.BM_GrH7q.css). The claim that this
   "degrades gracefully" if upstream renames a token was WRONG and cost an outage:
   rc2 changed the primitives to RGB channel triplets, our hex values made 344
   declarations invalid, and dark mode shipped at 1.15:1 contrast — see the ramp
   block for the full account. A format mismatch does not degrade, it DELETES.

   AFTER EVERY IMAGE BUMP, RUN:
       uv run --with requests python3 tools/check_theme_token_formats.py
   It resolves every token this file sets against the live bundle and refuses any
   the bundle cannot consume, plus flags overrides that have gone inert and ramp
   steps upstream reads that we do not define. Do not rely on looking at the app:
   half of these failures are invisible in light mode.

   Brand refs: Primary Blue #00ACFF, Blue #2164C4, Midnight #162E63,
   Dark Gray #333B42. Type: Poppins (headers SemiBold, body Medium).
   =========================================================================== */

/* --- Typography ------------------------------------------------------------
   Self-hosted so the app makes no third-party font request (Enclave handles
   Restricted data; no external calls). Files sit beside this stylesheet in the
   dist dir, served by `app.use(staticCache(appConfig.paths.dist))`. Deliberately
   NOT under /images/ — that path is wrapped in createValidateImageRequest, which
   is only a pass-through while `secureImageLinks` is unset; enabling it later
   would 401 every font. Falls back to the system stack if the files are absent. */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/Poppins-Regular.woff2') format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/Poppins-Medium.woff2') format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/Poppins-SemiBold.woff2') format('woff2');
}

/* Brand colours in BOTH encodings, on purpose — they are consumed two different
   ways and one format cannot serve both:
     --curvo-*      hex, for OUR OWN plain declarations (background-color: var(…))
     --curvo-*-rgb  "R G B" triplets, for UPSTREAM tokens the bundle wraps in
                    rgb(), e.g. --brand-purple and --ring-primary. Feeding hex
                    into rgb() silently discards the declaration; see the ramp
                    block below for the full explanation and the outage it caused. */
:root,
html,
.dark {
  --curvo-blue: #00acff;
  --curvo-blue-deep: #2164c4;
  --curvo-midnight: #162e63;
  --curvo-dark-gray: #333b42;

  --curvo-blue-rgb: 0 172 255; /* #00acff */
  --curvo-blue-deep-rgb: 33 100 196; /* #2164c4 */
  --curvo-midnight-rgb: 22 46 99; /* #162e63 */
}

/* Body copy is Poppins Medium per the brand guidelines. Monospace is left
   alone — code blocks should stay on Roboto Mono. */
html,
body,
button,
input,
textarea,
select {
  font-family: 'Poppins', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
}
body {
  font-weight: 500;
}
h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: 'Poppins', ui-sans-serif, system-ui, sans-serif;
  font-weight: 600;
}

/* --- Primitive ramp --------------------------------------------------------
   Cool neutrals on a consistent ~215° hue so surfaces read as the same family
   as CurvoX's chrome. Deliberately NOT Midnight (#162E63) for dark surfaces:
   at that saturation the whole UI drowns in blue and leaves no contrast
   headroom for #00ACFF as an accent. Midnight is reserved for accents.

   ⚠️⚠️ THESE ARE "R G B" CHANNEL TRIPLETS, NOT HEX. DO NOT "TIDY" THEM INTO HEX.
   LibreChat consumes primitives as `rgb(var(--gray-100))` and
   `rgb(var(--text-primary) / var(--tw-text-opacity))` — 344 such usages in the
   v0.8.8-rc2 bundle. A hex value makes the whole declaration INVALID, so the
   browser DROPS it silently and the element falls back to an inherited or
   initial colour. There is no console error and no visual clue in light mode,
   where the fallbacks happen to land dark-on-light and look fine.

   THIS SHIPPED BROKEN. The ramp was hex, tested against v0.8.7 where these were
   plain colours. The v0.8.8-rc2 bump moved the palette to triplets, every
   rgb(var(--gray-*)) declaration started being discarded, and DARK MODE rendered
   near-black text on the near-black chat surface — unreadable. Light mode looked
   normal, which is why it survived the bump. This file's own header asked for a
   visual check after any image bump; that check is what would have caught it, and
   it is now enforced by tools/check_theme_token_formats.py instead of a comment.

   To change a colour: edit the hex in the comment AND the triplet, or run the
   checker, which recomputes and compares. Values below are the same Curvo ramp
   as before, purely re-encoded. */
:root,
html {
  --gray-20:  237 240 245;  /* #edf0f5 */
  --gray-50:  245 247 250;  /* #f5f7fa */
  --gray-100: 232 237 243;  /* #e8edf3 */
  --gray-200: 219 226 236;  /* #dbe2ec */
  --gray-300: 194 204 219;  /* #c2ccdb */
  --gray-400: 147 161 181;  /* #93a1b5 */
  --gray-500:  93 107 128;  /* #5d6b80 */
  --gray-600:  63  75  92;  /* #3f4b5c */
  /* rc2 reads 650 and 875 steps that 0.8.7 did not. Upstream's are NEUTRAL greys
     (57 57 57 and 18 18 18), which read visibly warm against this cool ramp.
     Interpolated from the neighbouring Curvo steps. */
  --gray-650:  53  64  80;  /* #354050 */
  --gray-700:  43  53  69;  /* #2b3545 */
  --gray-800:  30  39  53;  /* #1e2735 */
  --gray-850:  22  29  41;  /* #161d29 */
  --gray-875:  18  25  36;  /* #121924 */
  --gray-900:  15  21  31;  /* #0f151f */
  /* rc2 added a 950 step (dark:bg-gray-950). Without it upstream's near-black
     shows through against our ramp on the darkest surfaces. */
  --gray-950:  10  14  22;  /* #0a0e16 */

  /* LibreChat's lone hardcoded accent — used for focus/brand flourishes.
     Must be the TRIPLET form: the bundle wraps this in rgb() (5 usages). */
  --brand-purple: var(--curvo-blue-rgb);
}

/* --- Semantic overrides ----------------------------------------------------
   Only where the ramp can't carry the brand.
   Tokens the bundle wraps in rgb() take the -rgb TRIPLET form. Measured against
   the rc2 bundle: --ring-primary 6 usages, --brand-purple 5, --surface-secondary
   17, --text-tertiary 7, --text-secondary-alt 2. The alias tokens below resolve
   to --gray-* and are therefore correct automatically now the ramp is triplets. */
html {
  --ring-primary: var(--curvo-blue-rgb);
  /* ⚠️ DEAD IN v0.8.8-rc2 — `var(--header-primary)` has ZERO usages in the rc2
     bundle (measured), so this and the two --header-*-hover tokens below no
     longer do anything and the header renders upstream's colour. Kept rather
     than deleted because the intent is still wanted and upstream may restore or
     rename the token; re-map it when the replacement is identified. Not the
     cause of the dark-mode outage, just collateral from the same bump. */
  --header-primary: #ffffff;
}
.dark {
  --brand-purple: var(--curvo-blue-rgb);
  --ring-primary: var(--curvo-blue-rgb);
  /* Midnight as the dark header reads as the CurvoX sider without tinting
     every surface. ⚠️ Also dead in rc2 — see the note above. */
  --header-primary: var(--curvo-midnight);
  --header-hover: #1d3a78;
  --header-button-hover: #1d3a78;

  /* ACCESSIBILITY FIX (not merely cosmetic). Upstream maps dark --text-tertiary
     and --text-secondary-alt to --gray-500, which measures ~1.9:1 against the
     dark chat surface — well under WCAG AA 4.5:1, and it's used for timestamps,
     section labels and the composer placeholder. Lifting both to the --gray-300
     step clears AA. Verified by measurement, not by eye. */
  --text-tertiary: var(--gray-300);
  --text-secondary-alt: var(--gray-300);
}

/* --- shadcn/ui token block -------------------------------------------------
   These are raw `H S% L%` triplets consumed inside hsl(); they must stay in
   that format, not hex. --primary drives primary buttons: the deeper Blue is
   used rather than #00ACFF because white text on #00ACFF is only ~2.6:1,
   while on #2164C4 it clears 4.5:1. */
html {
  --primary: 215 71% 45%; /* #2164C4 */
  --primary-foreground: 0 0% 100%;
  --ring: 199 100% 50%; /* #00ACFF */
  --border: 215 31% 89%; /* #DBE2EC */
  --input: 215 31% 89%;
  --accent: 214 40% 94%;
  --accent-foreground: 215 30% 18%;
}
.dark {
  --background: 217 35% 9%; /* #0F151F */
  --primary: 199 100% 50%; /* #00ACFF — on dark, the bright blue has room */
  --primary-foreground: 217 35% 9%;
  --ring: 199 100% 50%;
  --border: 216 24% 22%;
  --input: 216 24% 22%;
  --accent: 216 26% 20%;
  --accent-foreground: 0 0% 98%;
}
/* --- Dark-mode logo --------------------------------------------------------
   startup.sh overwrites every bundled logo.svg with the full-colour Curvo mark,
   which is near-invisible on a dark surface — the wordmark is Dark Gray. The
   brand guidelines require the white knocked-out variant on Midnight/dark, so
   swap it in dark mode. Requires curvo-logo-white.png shipped next to this file
   in the dist dir (source: work/assets/curvo_logo_w_fixed.png). */
.dark img[src*='logo.svg'] {
  content: url('/curvo-logo-white.png');
}

/* The dark page background is a literal rgba() in the bundle (style.css
   `.dark body, .dark html`), not a token. Matched at the same specificity so
   cascade order carries it — otherwise upstream's #212121 shows through at the
   document edges and on overscroll. */
.dark body,
.dark html {
  background-color: #0f151f;
}

/* --- Literal Tailwind gray utilities -------------------------------------
   tailwind.config.cjs duplicates the gray ramp as hardcoded hex, so ~570 class
   usages compile to literals that ignore the CSS variables above. These rules
   re-map those utilities to the Curvo ramp. Specificity matches Tailwind's own
   output (.cls for base, .dark .dark\:cls for the dark variant), so they win
   purely on cascade order — this file must load AFTER the app bundle. */
.bg-gray-20 { background-color: #edf0f5; }
.bg-gray-50 { background-color: #f5f7fa; }
.bg-gray-100 { background-color: #e8edf3; }
.bg-gray-200 { background-color: #dbe2ec; }
.bg-gray-300 { background-color: #c2ccdb; }
.bg-gray-400 { background-color: #93a1b5; }
.bg-gray-500 { background-color: #5d6b80; }
.bg-gray-600 { background-color: #3f4b5c; }
.bg-gray-700 { background-color: #2b3545; }
.bg-gray-800 { background-color: #1e2735; }
.bg-gray-850 { background-color: #161d29; }
.bg-gray-900 { background-color: #0f151f; }
/* rc2 added a 950 utility. Upstream compiles it to #030712 — a near-black that is
   BOTH off-ramp and darker than our darkest surface, so without this the deepest
   dark-mode panels drop out of the Curvo palette entirely. */
.bg-gray-950 { background-color: #0a0e16; }

.text-gray-20 { color: #edf0f5; }
.text-gray-50 { color: #f5f7fa; }
.text-gray-100 { color: #e8edf3; }
.text-gray-200 { color: #dbe2ec; }
.text-gray-300 { color: #c2ccdb; }
.text-gray-400 { color: #93a1b5; }
.text-gray-500 { color: #5d6b80; }
.text-gray-600 { color: #3f4b5c; }
.text-gray-700 { color: #2b3545; }
.text-gray-800 { color: #1e2735; }
.text-gray-850 { color: #161d29; }
.text-gray-900 { color: #0f151f; }

.border-gray-20 { border-color: #edf0f5; }
.border-gray-50 { border-color: #f5f7fa; }
.border-gray-100 { border-color: #e8edf3; }
.border-gray-200 { border-color: #dbe2ec; }
.border-gray-300 { border-color: #c2ccdb; }
.border-gray-400 { border-color: #93a1b5; }
.border-gray-500 { border-color: #5d6b80; }
.border-gray-600 { border-color: #3f4b5c; }
.border-gray-700 { border-color: #2b3545; }
.border-gray-800 { border-color: #1e2735; }
.border-gray-850 { border-color: #161d29; }
.border-gray-900 { border-color: #0f151f; }

/* Mid-ramp TEXT utilities used without a dark: variant. `text-gray-500` (60 usages,
   the single most common literal) is legible on light but only ~2.8:1 on the dark
   chat surface. Lift the mid steps in dark mode so unpaired usages stay readable;
   backgrounds are excluded — a bg lift there would wash out panels. */
.dark .text-gray-400,
.dark .text-gray-500,
.dark .text-gray-600 {
  color: #a9b6c9;
}

.dark .dark\:bg-gray-20 { background-color: #edf0f5; }
.dark .dark\:bg-gray-50 { background-color: #f5f7fa; }
.dark .dark\:bg-gray-100 { background-color: #e8edf3; }
.dark .dark\:bg-gray-200 { background-color: #dbe2ec; }
.dark .dark\:bg-gray-300 { background-color: #c2ccdb; }
.dark .dark\:bg-gray-400 { background-color: #93a1b5; }
.dark .dark\:bg-gray-500 { background-color: #5d6b80; }
.dark .dark\:bg-gray-600 { background-color: #3f4b5c; }
.dark .dark\:bg-gray-700 { background-color: #2b3545; }
.dark .dark\:bg-gray-800 { background-color: #1e2735; }
.dark .dark\:bg-gray-850 { background-color: #161d29; }
.dark .dark\:bg-gray-900 { background-color: #0f151f; }
/* The one rc2 actually emits in dark mode (.dark\:bg-gray-950 -> #030712). */
.dark .dark\:bg-gray-950 { background-color: #0a0e16; }

.dark .dark\:text-gray-20 { color: #edf0f5; }
.dark .dark\:text-gray-50 { color: #f5f7fa; }
.dark .dark\:text-gray-100 { color: #e8edf3; }
.dark .dark\:text-gray-200 { color: #dbe2ec; }
.dark .dark\:text-gray-300 { color: #c2ccdb; }
.dark .dark\:text-gray-400 { color: #93a1b5; }
.dark .dark\:text-gray-500 { color: #5d6b80; }
.dark .dark\:text-gray-600 { color: #3f4b5c; }
.dark .dark\:text-gray-700 { color: #2b3545; }
.dark .dark\:text-gray-800 { color: #1e2735; }
.dark .dark\:text-gray-850 { color: #161d29; }
.dark .dark\:text-gray-900 { color: #0f151f; }

.dark .dark\:border-gray-20 { border-color: #edf0f5; }
.dark .dark\:border-gray-50 { border-color: #f5f7fa; }
.dark .dark\:border-gray-100 { border-color: #e8edf3; }
.dark .dark\:border-gray-200 { border-color: #dbe2ec; }
.dark .dark\:border-gray-300 { border-color: #c2ccdb; }
.dark .dark\:border-gray-400 { border-color: #93a1b5; }
.dark .dark\:border-gray-500 { border-color: #5d6b80; }
.dark .dark\:border-gray-600 { border-color: #3f4b5c; }
.dark .dark\:border-gray-700 { border-color: #2b3545; }
.dark .dark\:border-gray-800 { border-color: #1e2735; }
.dark .dark\:border-gray-850 { border-color: #161d29; }
.dark .dark\:border-gray-900 { border-color: #0f151f; }

/* NOTE — the agent "Contact:" line is NOT suppressed here, deliberately.
   LibreChat renders it unconditionally (there is no interface flag; "contact"
   does not occur in rc2's config.ts), and with support_contact empty it falls
   through to owner_contact.name — the author's personal name, on a surface every
   SSO principal including B2B guests can see. The fix is to give it a real value
   rather than to hide the element: agent_data_sampler.json sets support_contact
   to a mailbox, so the line renders as a mailto link. Keeping this as DATA rather
   than CSS means it stays visible in the agent JSON and cannot drift silently. */

/* ===========================================================================
   One Curvo chrome — the look of the "One Curvo Vision" mockups (Sep 2026):
   a Midnight sidebar in BOTH modes, Midnight user bubbles, the logomark as the
   assistant avatar, a Curvo-blue composer. Selectors verified against the
   v0.8.8-rc2 DOM (headless, 2026-09-29).
   =========================================================================== */

/* --- Midnight sidebar ------------------------------------------------------
   The icon rail and the history panel both paint --surface-primary-alt and read
   every colour through the semantic tokens, so redefining those tokens ON THE
   ASIDE turns it into a dark island without touching the chat surface. Scoped
   custom properties beat the html/.dark definitions by inheritance, so one rule
   covers both modes. TRIPLETS, not hex: these are wrapped in rgb() upstream. */
aside[aria-label='Control Panel'] {
  --surface-primary: 22 46 99;         /* #162e63 Midnight */
  --surface-primary-alt: 22 46 99;     /* #162e63 */
  --surface-primary-contrast: 22 46 99;
  --surface-secondary: 29 56 115;      /* #1d3873 */
  --surface-tertiary: 29 56 115;
  --surface-hover: 33 62 122;          /* #213e7a */
  --surface-hover-alt: 44 80 150;      /* #2c5096 */
  --surface-active: 38 72 140;         /* #26488c */
  --surface-active-alt: 38 72 140;
  /* v0.8.9 theme roles: the history panel paints bg-surface-sidebar (no longer
     surface-primary-alt) and rows use the nav hover/selected roles. Without these the
     panel stayed light under our white text (white-on-white, local rc1 boot 2026-11-05).
     Inert on v0.8.8, which never reads them. */
  --surface-sidebar: 22 46 99;         /* #162e63 */
  --surface-nav-hover: 33 62 122;      /* #213e7a */
  --surface-nav-selected: 38 72 140;   /* #26488c */
  --surface-card: 29 56 115;           /* #1d3873 */
  --surface-card-hover: 33 62 122;
  --text-primary: 255 255 255;
  --text-secondary: 205 217 240;       /* #cdd9f0 */
  --text-secondary-alt: 170 189 224;   /* #aabde0 */
  --text-tertiary: 170 189 224;
  --border-light: 36 66 124;           /* #24427c */
  --border-medium: 50 84 150;          /* #325496 */
  --border-medium-alt: 50 84 150;
  --border-heavy: 72 106 170;          /* #486aaa */
  color: #fff;
}
/* Literal Tailwind grays (see the utility block above) ignore the tokens, so
   inside the island any mid/dark gray text is lifted to the secondary step. */
aside[aria-label='Control Panel'] :is(.text-gray-400, .text-gray-500, .text-gray-600,
  .text-gray-700, .text-gray-800, .text-gray-900) {
  color: #cdd9f0;
}

/* MCP Settings rail icon: hidden, COSMETICALLY. There is no permission that
   hides it without breaking agents — the panel shows whenever MCP_SERVERS.USE
   is on and servers are configured, and USE is also the runtime gate on every
   agent's MCP tools. USER already lacks MCP_SERVERS.CREATE, so this removes a
   read-only status panel, not a capability. Hidden for ADMIN too (CSS cannot
   see the role); admins can re-show it from devtools when debugging tools. */
aside[aria-label='Control Panel'] [data-testid='nav-panel-mcp-builder'] {
  display: none;
}

/* Guest badge: a "Guest" pill on the account avatar when curvo-session.js has tagged
   <html data-curvo-role="GUEST"> from the app's own /api/user response. Cosmetic only; what a
   guest can do is enforced server-side by the GUEST role (deploy/roles.py). Yellow accent (used
   sparingly per the brand guide) so it stands out on the Midnight rail; Midnight text on it
   8.28:1 measured. Orange was rejected: Midnight on #ED7845 is only ≈4.3:1, under AA for 9px text. */
html[data-curvo-role='GUEST'] aside[aria-label='Control Panel'] [data-testid='nav-user'] {
  position: relative;
  overflow: visible;
}
html[data-curvo-role='GUEST'] aside[aria-label='Control Panel'] [data-testid='nav-user']::after {
  content: 'Guest';
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  padding: 0 5px;
  border-radius: 6px;
  background: #ffc534;
  color: var(--curvo-midnight);
  font: 600 9px/14px Poppins, ui-sans-serif, system-ui, sans-serif;
  letter-spacing: 0.02em;
  white-space: nowrap;
  pointer-events: none;
}

/* --- Persistent logo -------------------------------------------------------
   Official SVGs inlined once as custom properties, used by the rules below.
     --curvo-wordmark  Curvo-Logo-Light.svg (blue mark, white word), allowed on
                       Midnight and the variant the deck uses. viewBox widened:
                       the toolkit export crops the mark's outer arc at x=0.
     --curvo-mark      Curvo-Icon-RGB.svg (blue disc, white C).
   Where it shows, by state (DOM verified 2026-09-29):
     sidebar expanded   full logo at the head of WHICHEVER panel is open (the
                        panel swaps its child: history list, agent form, ...),
                        so it is hung on the <nav>, not on one panel
     sidebar collapsed  the <nav> goes opacity-0/width 0, so the mark shows in
                        the rail's top slot instead. The slot is reserved in
                        both states so the rail icons never jump on toggle.
     mobile (<md)       no aside at all; the mark sits beside the menu button */
:root {
  --curvo-wordmark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 483.69 96.96'%3E%3Cg%3E%3Cg%3E%3Cpath fill='%2300acff' d='M24.9,44.48c0-10.02,8.15-18.17,18.17-18.17h20.1c.69.89,1.75,1.41,2.88,1.41,2.01,0,3.65-1.64,3.65-3.65s-1.64-3.65-3.65-3.65c-1.13,0-2.18.52-2.88,1.41h-20.1c-12.49,0-22.65,10.16-22.65,22.65s10.16,22.65,22.65,22.65h51.27c.69.86,1.73,1.37,2.84,1.37,2.01,0,3.65-1.64,3.65-3.65s-1.64-3.65-3.65-3.65c-1.15,0-2.21.54-2.91,1.45h-51.21c-10.02,0-18.17-8.15-18.17-18.17'/%3E%3Cpath fill='%2300acff' d='M106.03,71.47c-1.13,0-2.18.52-2.88,1.41h-60.09c-15.66,0-28.4-12.74-28.4-28.4s12.74-28.4,28.4-28.4h37.81c.69.89,1.75,1.41,2.88,1.41,2.01,0,3.65-1.64,3.65-3.65s-1.64-3.65-3.65-3.65c-1.13,0-2.19.52-2.88,1.41h-37.81c-18.13,0-32.88,14.75-32.88,32.88s14.75,32.88,32.88,32.88h60.09c.69.89,1.75,1.41,2.88,1.41,2.01,0,3.65-1.64,3.65-3.65s-1.64-3.65-3.65-3.65'/%3E%3Cpath fill='%2300acff' d='M88.34,81.66c-1.13,0-2.18.52-2.88,1.41h-42.39c-21.28,0-38.59-17.31-38.59-38.59S21.8,5.89,43.07,5.89h28.95c.69.89,1.75,1.41,2.88,1.41,2.01,0,3.65-1.64,3.65-3.65s-1.64-3.65-3.65-3.65c-1.13,0-2.18.52-2.88,1.41h-28.95C19.32,1.41,0,20.73,0,44.48s19.32,43.07,43.07,43.07h42.39c.69.89,1.75,1.41,2.88,1.41,2.01,0,3.65-1.64,3.65-3.65s-1.64-3.65-3.65-3.65'/%3E%3Cpath fill='%23fff' d='M130.55,43.09c0-18.55,14-32.67,32.92-32.67,17.2,0,27.88,11.05,30.34,20.39l-15.11,4.3c-1.35-5.16-7.12-10.31-15.23-10.31-10.32,0-16.95,8.47-16.95,18.3s6.63,18.42,16.95,18.42c8.11,0,13.88-4.79,15.23-9.95l15.11,4.18c-2.46,9.45-13.14,20.26-30.22,20.26-19.04,0-33.04-14.25-33.04-32.92'/%3E%3Cpath fill='%23fff' d='M261.73,12.14v62.15h-16.21v-7.86c-4.18,5.53-10.93,9.58-19.9,9.58-16.34,0-24.56-10.93-24.56-27.64V12.14h16.21v33.16c0,9.46,4.3,15.6,13.26,15.6s14.99-7.12,14.99-18.18V12.14h16.21Z'/%3E%3Cpath fill='%23fff' d='M310.85,11.28v15.23c-13.27-2.09-20.51,4.05-20.51,20.75v27.02h-16.21V12.14h16.21v9.21c3.68-7.25,10.81-11.42,20.51-10.07'/%3E%3Cpolygon fill='%23fff' points='385.37 12.14 359.7 74.29 341.39 74.29 315.72 12.14 333.53 12.14 350.6 56.35 367.68 12.14 385.37 12.14'/%3E%3Cpath fill='%23fff' d='M382.66,43.21c0-18.67,14.13-32.8,33.29-32.8s33.16,14.12,33.16,32.8-14,32.8-33.16,32.8-33.29-14.25-33.29-32.8M433.39,43.21c0-10.32-7.12-18.06-17.44-18.06s-17.44,7.74-17.44,18.06,7,18.05,17.44,18.05,17.44-7.74,17.44-18.05'/%3E%3Cpolygon fill='%23fff' points='464.14 12.34 461.14 12.34 461.14 25.69 463.91 25.69 463.91 16.77 468.45 22.89 472.83 16.75 472.83 25.69 475.69 25.69 475.69 12.34 472.6 12.34 468.41 18.47 464.14 12.34'/%3E%3Cpolygon fill='%23fff' points='452.07 25.69 454.97 25.69 454.97 15.03 459.31 15.03 459.31 12.34 447.76 12.34 447.76 15.03 452.07 15.03 452.07 25.69'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  --curvo-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 153.95 153.1'%3E%3Cg%3E%3Cg%3E%3Cellipse fill='%2300acff' cx='76.97' cy='76.55' rx='76.97' ry='76.55'/%3E%3Cg%3E%3Cpath fill='%23fff' d='M47.6,76.55c0-10.44,8.53-18.92,19.03-18.92h21.06c.72.92,1.83,1.47,3.01,1.47,2.11,0,3.83-1.7,3.83-3.8s-1.72-3.8-3.83-3.8c-1.18,0-2.29.54-3.01,1.47h-21.06c-13.08,0-23.73,10.58-23.73,23.59s10.64,23.59,23.73,23.59h53.69c.72.9,1.82,1.42,2.98,1.42,2.11,0,3.82-1.71,3.82-3.8s-1.72-3.8-3.82-3.8c-1.2,0-2.32.56-3.04,1.51h-53.63c-10.49,0-19.03-8.49-19.03-18.92'/%3E%3Cpath fill='%23fff' d='M132.57,104.66c-1.18,0-2.29.54-3.01,1.47h-62.93c-16.4,0-29.74-13.27-29.74-29.58s13.34-29.58,29.74-29.58h39.59c.72.92,1.83,1.47,3.01,1.47,2.11,0,3.82-1.7,3.82-3.8s-1.72-3.8-3.82-3.8c-1.18,0-2.29.54-3.01,1.47h-39.59c-18.99,0-34.44,15.36-34.44,34.25s15.45,34.25,34.44,34.25h62.93c.72.93,1.83,1.47,3.01,1.47,2.11,0,3.82-1.71,3.82-3.8s-1.72-3.8-3.82-3.8'/%3E%3Cpath fill='%23fff' d='M114.04,115.27c-1.18,0-2.29.54-3.01,1.47h-44.39c-22.28,0-40.41-18.03-40.41-40.19s18.13-40.19,40.41-40.19h30.32c.73.92,1.83,1.47,3.01,1.47,2.11,0,3.82-1.71,3.82-3.8s-1.72-3.8-3.82-3.8c-1.18,0-2.29.54-3.01,1.47h-30.32c-24.87,0-45.11,20.12-45.11,44.86s20.23,44.86,45.11,44.86h44.39c.72.93,1.83,1.47,3.01,1.47,2.11,0,3.82-1.71,3.82-3.8s-1.72-3.8-3.82-3.8'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}

aside[aria-label='Control Panel'] nav {
  display: flex;
  flex-direction: column;
}
aside[aria-label='Control Panel'] nav::before {
  content: '';
  flex-shrink: 0;
  height: 36px;
  margin: 8px 16px 0;
  background: var(--curvo-wordmark) left center / auto 26px no-repeat;
}
/* The panel child is h-full, which would now overflow by the logo's height. */
aside[aria-label='Control Panel'] nav > * {
  flex: 1 1 0;
  height: auto;
  min-height: 0;
}

/* Rail top slot: always takes its 36px, painted only while the panel is shut. */
aside[aria-label='Control Panel'] > div > div:first-child::before {
  content: '';
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  background: var(--curvo-mark) center / 28px no-repeat;
  visibility: hidden;
}
aside[aria-label='Control Panel'] > div > div:first-child:has(~ nav.opacity-0)::before {
  visibility: visible;
}

/* Mobile header: the left cluster holding the menu button is md:hidden, so
   this only ever renders below the md breakpoint. */
.top-0.h-\[52px\] > .md\:hidden::after {
  content: '';
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  margin-left: 4px;
  background: var(--curvo-mark) center / contain no-repeat;
}

/* The active conversation carries a 2px ::before indicator in --text-primary
   (white here); Primary Blue marks it as the brand accent instead. */
aside[aria-label='Control Panel'] .bg-surface-active-alt::before {
  background-color: var(--curvo-blue);
}

/* "New chat" is the one primary action in the rail: Primary Blue with a
   Midnight glyph (5.4:1; a white glyph on #00ACFF is only 2.6:1). */
aside[aria-label='Control Panel'] a[data-testid='new-chat-button'] {
  background-color: var(--curvo-blue);
  color: var(--curvo-midnight);
}
aside[aria-label='Control Panel'] a[data-testid='new-chat-button']:hover {
  background-color: #33bdff;
}
aside[aria-label='Control Panel'] a[data-testid='new-chat-button'] svg {
  color: var(--curvo-midnight);
}

/* --- Messages --------------------------------------------------------------
   User turn: Midnight bubble, white text. The prose plugin sets a literal
   #424242 on paragraphs, so the colour is forced on the markdown subtree. */
.user-turn [data-testid='message-body'] {
  background-color: var(--curvo-midnight);
}
.user-turn [data-testid='message-body'] .markdown,
.user-turn [data-testid='message-body'] .markdown * {
  color: #fff;
}
/* On the dark surface Midnight sits close to the page; a hairline keeps the
   bubble's edge. */
.dark .user-turn [data-testid='message-body'] {
  box-shadow: inset 0 0 0 1px #24427c;
}

/* Assistant avatar: an agent with no uploaded avatar falls back to lucide's
   feather glyph. Swap in the logomark (official Curvo-Icon-RGB.svg, inlined so
   no extra file has to be shipped). An agent WITH its own avatar renders an
   <img>, not the feather, so it is left alone. */
.agent-turn h2 .rounded-full:has(svg.lucide-feather) {
  background: var(--curvo-mark) center / cover no-repeat;
  box-shadow: none;
}
.agent-turn h2 .rounded-full:has(svg.lucide-feather) svg.lucide-feather {
  visibility: hidden;
}

/* Tables read as data, not prose: small caps-style headers as in the mockup. */
.markdown th {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgb(var(--text-secondary));
}
/* v0.8.9 paints the header row from its own role (white by default); keep the light
   gray-100 band v0.8.8 drew (measured 232 237 243). Light mode only: dark keeps upstream's dark default. */
html:not(.dark) {
  --table-header-fill: 232 237 243;    /* #e8edf3 */
}

/* --- Composer --------------------------------------------------------------
   Softer card radius and a Curvo-blue focus edge; the send button becomes the
   deep Blue with a white glyph (5.2:1). Upstream dims it when disabled.
   Each selector names BOTH DOMs so a rollback keeps the look: v0.8.8 (Tailwind
   class + #send-button) and v0.8.9 (data-testid composer-surface / send-button,
   the button lost its id). Verified headless on both stacks, 2026-11-05. */
:is(.sm\:rounded-3xl:has(#prompt-textarea), [data-testid='composer-surface']) {
  border-radius: 16px;
}
:is(.sm\:rounded-3xl, [data-testid='composer-surface']):has(#prompt-textarea:focus) {
  border-color: var(--curvo-blue);
  box-shadow: 0 0 0 3px rgb(var(--curvo-blue-rgb) / 0.18);
}
:is(#send-button, [data-testid='send-button']) {
  background-color: var(--curvo-blue-deep);
  border-radius: 10px;
}
:is(#send-button, [data-testid='send-button']),
:is(#send-button, [data-testid='send-button']) svg {
  color: #fff;
}
