/* ==========================================================================
   NutriCenter — design tokens
   Direction: "botanical clinical warmth" — a calm, editorial, trustworthy
   practice tool. Warm paper over cold SaaS-gray; a characterful serif for
   headings over generic system sans; deep forest green + clay terracotta
   instead of default-blue. Light theme only (no dark mode, by decision).

   Vollkorn (vendored below, latin + cyrillic subsets — client nicknames may
   be Cyrillic per the data model) carries headings/wordmarks; body/UI text
   stays on a curated system-sans stack (no Inter/Roboto/Arial/system-ui).
   No CDN at runtime, same vendoring pattern as htmx.min.js/alpine.min.js.
   ========================================================================== */

@font-face {
  font-family: "Vollkorn";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/vollkorn-600-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}

@font-face {
  font-family: "Vollkorn";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/vollkorn-600-cyrillic.woff2") format("woff2");
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

:root {
  /* Color */
  --color-bg: #fbf6ec;
  --color-bg-elevated: #fffdf8;
  --color-sidebar-bg: #f1e8d6;
  --color-border: #e2d3b3;
  --color-border-strong: #cbb98e;
  --color-text: #2a2318;
  --color-text-muted: #6d6046;
  --color-text-faint: #9c8f6f;
  --color-primary: #2f5233;
  --color-primary-hover: #24401f;
  --color-primary-contrast: #fbf6ec;
  --color-accent: #bd5b3b;
  --color-accent-hover: #a44b2e;
  --color-accent-soft: #f1ded3;
  --color-danger: #a83a2b;
  --color-danger-bg: #f7e3dc;
  --color-danger-border: #e9bca9;
  --color-focus-ring: #bd5b3b;

  /* Type */
  --font-display: "Vollkorn", Georgia, "Iowan Old Style", "Palatino Linotype", serif;
  --font-body: "Avenir Next", "Century Gothic", "Segoe UI", Verdana, sans-serif;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.5rem;
  --font-size-2xl: 1.9rem;
  --line-height-base: 1.55;

  /* Space (4px base) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-8: 3rem;

  /* Shape */
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 18px;
  --radius-full: 999px;

  /* Elevation (warm-tinted, not gray) */
  --shadow-sm: 0 1px 2px rgba(42, 35, 24, 0.07);
  --shadow-md: 0 4px 14px rgba(42, 35, 24, 0.09);
  --shadow-lg: 0 16px 40px rgba(42, 35, 24, 0.16);

  /* Motion */
  --transition-fast: 120ms ease;
  --transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

* { box-sizing: border-box; }

/* Standard Alpine.js pattern: hides an element until Alpine has evaluated
   its directives. Currently unused in this codebase's live markup --
   `x-cloak` combined with `x-show` on the same element was confirmed (live,
   in a real browser) to silently break `x-show`'s binding specifically on
   content that arrives via an htmx-boosted swap (the element ends up with
   no inline style at all, falling back to its default visible display,
   worse than the FOUC it was meant to fix). The prompt-template picker menu
   hit this exactly and was rewritten to use `template x-if` instead (see
   _chat_panel.html), which has no FOUC risk in the first place and needs no
   x-cloak. Keep this rule for any future element that uses x-cloak WITHOUT
   x-show, but do not pair the two again without re-verifying in a real
   browser. */
[x-cloak] { display: none !important; }

body {
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  margin: 0;
  padding: 0;
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2 {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--color-text);
  margin: 0 0 var(--space-3);
  letter-spacing: 0.01em;
}

h1 { font-size: var(--font-size-2xl); }
h2 {
  font-size: var(--font-size-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-muted);
  font-weight: 600;
}

a {
  color: var(--color-accent);
  text-decoration: none;
}

a:hover { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.error {
  color: var(--color-danger);
  background: var(--color-danger-bg);
  border: 1px solid var(--color-danger-border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  font-size: var(--font-size-sm);
}

form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

label {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  font-weight: 600;
}

input[type="password"],
input[type="text"],
input[type="search"] {
  font: inherit;
  font-size: var(--font-size-base);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-bg-elevated);
  color: var(--color-text);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

input[type="password"]:focus,
input[type="text"]:focus,
input[type="search"]:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px var(--color-accent-soft);
}

button {
  font: inherit;
  font-size: var(--font-size-base);
  font-weight: 600;
  padding: var(--space-2) var(--space-4);
  border: none;
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: background var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
}

button:hover { background: var(--color-primary-hover); box-shadow: var(--shadow-md); }
button:active { transform: translateY(1px); box-shadow: var(--shadow-sm); }

/* Chat message list (spec-ui-redesign Story 3 / CAP-3, chat-layout-reference.md):
   assistant messages are plain full-width text (Markdown rendered server-side
   into real HTML, so no pre-wrap here -- that would render the raw newlines
   between the Markdown converter's own block tags as literal blank space);
   user messages are a right-aligned bubble sized to their own content, kept
   as literal escaped text with pre-wrap preserved (today's behavior,
   unchanged). The column itself is centered with a max-width, never
   edge-to-edge, even alongside the persistent sidebar. */
.chat-column {
  max-width: 46rem;
  margin: 0 auto;
}

.chat-messages {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.message {
  display: flex;
  flex-direction: column;
  max-width: 100%;
}

.message-assistant {
  align-items: stretch;
}

.message-user {
  align-items: flex-end;
}

.message-content-assistant {
  width: 100%;
}

/* Markdown-rendered HTML (paragraphs/lists/tables/code) needs its own
   internal spacing reset -- the browser's default block margins otherwise
   stack awkwardly at the top/bottom of each message. */
.message-content-assistant > :first-child { margin-top: 0; }
.message-content-assistant > :last-child { margin-bottom: 0; }

.message-content-assistant table {
  border-collapse: collapse;
  font-size: var(--font-size-sm);
}

.message-content-assistant th,
.message-content-assistant td {
  border: 1px solid var(--color-border);
  padding: var(--space-1) var(--space-2);
  text-align: left;
}

.message-content-assistant code {
  background: var(--color-sidebar-bg);
  border-radius: var(--radius-sm);
  padding: 0.1em 0.3em;
  font-size: 0.9em;
}

.message-content-assistant pre {
  background: var(--color-sidebar-bg);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  overflow-x: auto;
}

.message-content-assistant pre code {
  background: none;
  padding: 0;
}

/* A model reply can emit its own `# Heading`/`> quote`/nested lists — these
   must read as in-message formatting, not borrow the page's global h1/h2
   styling (reserved for real page titles like <h1>{{ client.nickname }}</h1>),
   which would otherwise make a chat message look like a fake page heading. */
.message-content-assistant h1,
.message-content-assistant h2,
.message-content-assistant h3,
.message-content-assistant h4,
.message-content-assistant h5,
.message-content-assistant h6 {
  font-family: inherit;
  text-transform: none;
  letter-spacing: normal;
  color: var(--color-text);
  font-weight: 600;
  line-height: var(--line-height-base);
  margin: var(--space-3) 0 var(--space-2);
}

.message-content-assistant h1 { font-size: var(--font-size-lg); }
.message-content-assistant h2 { font-size: var(--font-size-lg); }
.message-content-assistant h3,
.message-content-assistant h4,
.message-content-assistant h5,
.message-content-assistant h6 { font-size: var(--font-size-base); }

.message-content-assistant blockquote {
  margin: var(--space-2) 0;
  padding: var(--space-1) var(--space-3);
  border-left: 3px solid var(--color-border-strong);
  color: var(--color-text-muted);
}

.message-content-assistant ul,
.message-content-assistant ol {
  margin: var(--space-2) 0;
  padding-left: var(--space-5);
}

.message-content-assistant ul ul,
.message-content-assistant ul ol,
.message-content-assistant ol ul,
.message-content-assistant ol ol {
  margin: 0;
}

.message-bubble {
  max-width: 85%;
  background: var(--color-accent-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
}

.message-content-user {
  white-space: pre-wrap;
  word-break: break-word;
}

/* Timestamp + copy button share one row (feature-message-copy-button). */
.message-meta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-top: var(--space-1);
}

.message-user .message-meta {
  justify-content: flex-end;
}

.message-timestamp {
  font-size: var(--font-size-sm);
  color: var(--color-text-faint);
}

/* Sized down and more muted than .message-retry/.composer-attach to match
   the plain metadata text it sits next to, not because copying an assistant
   reply is a low-value action -- it's ambient precisely so it never
   competes with the message content itself. \FE0E forces the clipboard
   glyph's text/monochrome presentation (same fix as the client-header
   icons) so the hover/copied color changes actually apply instead of being
   ignored by a color emoji glyph. */
.message-copy {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-faint);
  font-size: 0;
  box-shadow: none;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.message-copy::before {
  content: "\1F4CB\FE0E";
  font-size: var(--font-size-sm);
}

.message-copy:hover {
  background: var(--color-accent-soft);
  color: var(--color-accent);
}

.message-copy:active {
  transform: none;
  box-shadow: none;
}

.message-copy--copied::before {
  content: "\2713";
}

.message-copy--copied {
  color: var(--color-accent);
}

.message-attachments {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  font-size: var(--font-size-sm);
}

.message-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

.message-not-sent {
  color: var(--color-danger);
  font-size: var(--font-size-sm);
  font-weight: 600;
}

.message-actions form {
  margin: 0;
}

/* Story 8 / CAP-9: scoped override so the Retry button reads as a small,
   subordinate below-message affordance (chat-layout-reference.md's
   below-message icon-row pattern) instead of inheriting the app-wide
   full-size primary `button` style above. Mirrors .btn-toggle-create's
   "de-emphasized but present" override shape, sized down to composer
   control proportions, with the same subtle-interactive-control hover
   already used by .composer-attach / .composer-template-trigger. */
.message-retry {
  font-size: var(--font-size-sm);
  font-weight: 400;
  padding: var(--space-1) var(--space-2);
  background: transparent;
  color: var(--color-text-muted);
  border: 1px solid var(--color-border-strong);
  box-shadow: none;
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.message-retry:hover {
  background: var(--color-accent-soft);
  color: var(--color-accent);
  border-color: var(--color-accent);
  box-shadow: none;
}

/* Overrides the base `button:active` rule's transform/box-shadow (higher
   specificity than .message-retry alone) so a press/tap stays flat instead
   of picking up the primary button's depress effect. */
.message-retry:active {
  transform: none;
  box-shadow: none;
}

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

/* Story 7 / CAP-8: response-waiting indicator. Cloned from the hidden
   <template id="optimistic-loading-template"> in _chat_panel.html and
   appended alongside the just-sent optimistic message on htmx:beforeRequest.
   Reuses .message-content-assistant for the shared plain, left-aligned,
   no-bubble assistant visual language -- only the three animated dots
   themselves are new here. */
.message-loading-dots {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) 0;
}

.message-loading-dots span {
  width: 0.4rem;
  height: 0.4rem;
  border-radius: var(--radius-full);
  background: var(--color-text-faint);
  animation: message-loading-bounce 1.2s infinite ease-in-out both;
}

.message-loading-dots span:nth-child(1) { animation-delay: -0.24s; }
.message-loading-dots span:nth-child(2) { animation-delay: -0.12s; }
.message-loading-dots span:nth-child(3) { animation-delay: 0s; }

@keyframes message-loading-bounce {
  0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
  40% { transform: scale(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .message-loading-dots span {
    animation: none;
    opacity: 0.8;
  }
}

/* Composer (spec-ui-redesign Story 4 / CAP-4, chat-layout-reference.md):
   a floating rounded pill, not edge-to-edge -- internal padding, compact "+"
   attach trigger on the left, the prompt-template select + send button
   grouped on the right. Keeps the page-wide `form { flex-direction: column }`
   rule (chip row stacked above the input row, Story 5 / CAP-5); `.composer-
   body` below carries the row layout the pill's input line needs. */
.composer {
  /* Bug fix: establishes a positioning context for #attachments (.sr-only,
     `position: absolute` with no top/left, i.e. static-position fallback).
     With no positioned ancestor anywhere above it, that fallback resolves
     against the initial containing block (the viewport/document root), not
     against .main's own scroll clip -- its static position is computed
     from its place in .main's *unclipped* normal-flow layout, which in a
     long chat can be thousands of pixels tall. Focusing it (which happens
     automatically when the native file-picker dialog closes after picking
     a file) then makes the browser scroll the outer page to "reveal" that
     coordinate, growing document.documentElement's scrollable area to
     match -- the whole page scrolls away under both panels into blank
     space, since nothing else in normal flow reaches that far down.
     Reproduced live (Playwright + a real DragEvent-free file-picker
     focus()) against a 41-message chat: before this rule,
     document.documentElement.scrollTop jumped to ~4528px on focus with
     document.body.scrollHeight still 757px; after, it stays 0. */
  position: relative;
  gap: var(--space-2);
  max-width: 46rem;
  margin: var(--space-5) auto 0;
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

/* Story 5 / CAP-5: the removable-chip preview row for selected attachments,
   stacked above the input line inside the pill -- hidden entirely (via
   Alpine `x-show`) until at least one file is selected. */
.composer-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.composer-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  max-width: 12rem;
  padding: var(--space-1) var(--space-1) var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border-strong);
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
}

.composer-chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.composer-chip-remove {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  box-shadow: none;
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
  font-weight: 400;
  line-height: 1;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.composer-chip-remove:hover {
  background: var(--color-accent-soft);
  color: var(--color-accent);
}

/* The pill's input row -- attach trigger, textarea, template picker + send --
   carries the row layout `.composer` itself used to have before the chip
   row (above) was stacked on top of it. */
.composer-body {
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  gap: var(--space-2);
}

.composer-attach {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  margin-bottom: 2px;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border-strong);
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: var(--font-size-lg);
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.composer-attach:hover {
  background: var(--color-accent-soft);
  border-color: var(--color-accent);
}

/* Auto-grow (spec-ui-redesign Story 4): Alpine sets an explicit inline
   height on `#content` up to its scrollHeight on init/input; this max-height
   caps the visible growth and hands off to native internal scrolling once
   content exceeds it, per the "never unbounded" constraint. */
.composer-input {
  flex: 1 1 auto;
  resize: none;
  border: none;
  background: transparent;
  font: inherit;
  font-size: var(--font-size-base);
  color: var(--color-text);
  padding: var(--space-2) 0;
  line-height: var(--line-height-base);
  max-height: 12rem;
  overflow-y: auto;
  overflow-wrap: break-word;
  word-break: break-word;
}

/* Unlike the other inputs' :focus (which swap outline for a border/shadow
   ring), the composer pill itself already gets that treatment below via
   :focus-within -- an inner ring on the borderless textarea would double up. */
.composer-input:focus {
  outline: none;
  box-shadow: none;
}

.composer:focus-within {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px var(--color-accent-soft), var(--shadow-md);
}

.composer-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: 2px;
}

/* Story 6 / CAP-6: the Prompt Template picker, a compact icon-only trigger
   (sized/styled like .composer-attach) that opens a floating menu of
   template names -- replaces the old always-visible `<select>`. The wrap is
   `position: relative` so the menu (`position: absolute; bottom: 100%`) opens
   upward above the composer pill instead of downward off the viewport. */
.composer-template-wrap {
  position: relative;
  flex: 0 0 auto;
}

.composer-template-trigger {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border-strong);
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: 0;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.composer-template-trigger::before {
  content: "\2630";
  font-size: var(--font-size-lg);
}

.composer-template-trigger:hover,
.composer-template-trigger[aria-expanded="true"] {
  background: var(--color-accent-soft);
  border-color: var(--color-accent);
}

.composer-template-menu {
  position: absolute;
  bottom: 100%;
  right: 0;
  margin: 0 0 var(--space-2);
  padding: var(--space-1);
  min-width: 12rem;
  max-width: 16rem;
  max-height: 16rem;
  overflow-y: auto;
  list-style: none;
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  z-index: 10;
}

.composer-template-option {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text);
  font-size: var(--font-size-sm);
  font-weight: 400;
  box-shadow: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.composer-template-option:hover {
  background: var(--color-accent-soft);
  color: var(--color-accent);
}

.composer-send {
  flex: 0 0 auto;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border-radius: var(--radius-full);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0;
}

.composer-send::before {
  content: "\2191";
  font-size: var(--font-size-lg);
}

/* home.html and client.html opt into the shell layout via a body class
   (set through base.html's body_class block) rather than a :has(#app-shell)
   selector, so this doesn't depend on browser support for :has(). Pages
   that don't declare the block (login.html, hide_client_confirm.html) keep
   today's simple centered card, elevated off the warm paper background. */
body:not(.has-shell) {
  max-width: 26rem;
  margin: 4rem auto;
  padding: var(--space-6) var(--space-5);
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--color-accent);
  color: var(--color-primary-contrast);
  padding: var(--space-2) var(--space-4);
  border-radius: 0 0 var(--radius-sm) 0;
  font-weight: 600;
}

.skip-link:focus {
  left: 0;
}

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

#app-shell {
  height: 100vh;   /* fallback for browsers without dvh */
  height: 100dvh;  /* tracks mobile Safari/Chrome's dynamic viewport */
}

.sidebar,
.main {
  height: 100%;
  overflow-y: auto;
  /* Each panel already fills exactly #app-shell's 100dvh and scrolls its
     own content, so a wheel/trackpad gesture that runs past a panel's own
     start/end has nowhere further to go -- without this, the browser
     chains that leftover input to the page and rubber-bands the whole
     layout (both panels) off-screen instead of just stopping. */
  overscroll-behavior: contain;
  padding: var(--space-5);
}

/* spec-drag-drop-attachment: reserve the border space up front (kept
   transparent at rest) so toggling .main--drag-over's border-color below
   never shifts layout -- `* { box-sizing: border-box }` (top of file)
   means this doesn't add to .main's outer size either way. .main-only,
   deliberately not folded into the shared .sidebar, .main rule above --
   .sidebar never gets .main--drag-over and has no use for the reserved
   border space. */
.main {
  border: 2px solid transparent;
}

/* spec-drag-drop-attachment / CAP-2: the whole-window drop-zone highlight,
   toggled by <main>'s isDragOver while an OS file drag is over any part of
   the chat window. Styled after .composer:focus-within's existing accent-
   border treatment below, reusing the same tokens. An inset shadow (rather
   than .composer's outward one) is used deliberately -- .main scrolls its
   own content (overflow-y: auto), and an inset shadow paints inside the
   border box, so it can never be clipped by that scroll region. */
.main--drag-over {
  border-color: var(--color-accent);
  box-shadow: inset 0 0 0 3px var(--color-accent-soft);
}

.sidebar {
  background:
    /* faint organic grain, purely decorative, kept subtle enough to never
       compete with the roster text sitting on top of it */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E"),
    var(--color-sidebar-bg);
  border-right: 1px solid var(--color-border);
}

.sidebar form { margin-top: 0; }

.sidebar h2 { margin-top: var(--space-5); }

.sidebar-create { margin-bottom: var(--space-2); }

.btn-toggle-create {
  width: 100%;
  background: transparent;
  color: var(--color-primary);
  border: 1px dashed var(--color-border-strong);
  box-shadow: none;
}

.btn-toggle-create:hover {
  background: var(--color-bg-elevated);
  border-color: var(--color-primary);
  box-shadow: none;
}

/* Specificity (0,2,0) — must beat `.sidebar form { margin-top: 0; }`
   (0,1,1) above, or the toggle-form's own spacing is silently discarded. */
.sidebar .sidebar-create-form { margin-top: var(--space-3); }

.client-search {
  width: 100%;
  margin: 0;
  /* Neutralize native search-input chrome (WebKit/Safari's rounded corners
     and built-in clear/"×" button) so it matches the app's other inputs. */
  -webkit-appearance: none;
  appearance: none;
}

.no-matches {
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
  margin: var(--space-2) 0 0;
}

.client-list {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.client-list a {
  display: block;
  color: var(--color-text);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  border-left: 3px solid transparent;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.client-list a:hover {
  background: var(--color-bg-elevated);
  text-decoration: none;
}

.client-list a.active {
  background: var(--color-accent-soft);
  border-left-color: var(--color-accent);
  font-weight: 600;
}

.main--empty {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  color: var(--color-text-muted);
}

.back-link {
  display: none;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-muted);
}

@media (max-width: 767px) {
  #app-shell[data-view="list"] .main { display: none; }
  #app-shell[data-view="chat"] .sidebar { display: none; }
  #app-shell[data-view="chat"] .back-link {
    display: inline-block;
    margin-bottom: var(--space-3);
  }
}

@media (min-width: 768px) {
  #app-shell {
    display: grid;
    grid-template-columns: 320px 1fr;
  }
}

/* spec-client-header-rename-delete: compact client header (name + Rename/
   Delete icon buttons), replacing the old plain <h1> + always-visible
   rename <form> + trailing "Delete Client" link. */
.client-header {
  margin-bottom: var(--space-4);
}

.client-header-view {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.client-header-name {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  overflow-wrap: break-word;
  word-break: break-word;
}

.client-header-actions {
  flex: 0 0 auto;
  display: flex;
  gap: var(--space-2);
}

/* Icon-only controls, same circular small-control language as
   .composer-attach / .composer-template-trigger / .message-retry. */
.client-header-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border-strong);
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: 0;
  box-shadow: none;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.client-header-btn::before {
  font-size: var(--font-size-lg);
}

/* \FE0E forces the text/monochrome presentation of these codepoints --
   without it, several browser/OS font stacks default to a full-color emoji
   glyph that ignores the CSS `color` property, silently breaking the
   hover-to-accent/danger color transition below. */
.client-header-btn--rename::before { content: "\270E\FE0E"; }
.client-header-btn--delete::before { content: "\1F5D1\FE0E"; }

.client-header-btn:hover {
  background: var(--color-accent-soft);
  color: var(--color-accent);
  border-color: var(--color-accent);
}

.client-header-btn--delete:hover {
  background: var(--color-danger-bg);
  color: var(--color-danger);
  border-color: var(--color-danger-border);
}

/* Inline rename row (view/edit split, spec-client-header-rename-delete):
   overrides the base `form` rule's column layout/margin-top so the input and
   its Save/Cancel controls sit on one line, replacing the name in place. */
.client-header-edit {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

.client-header-edit input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
}

/* Small, de-emphasized text buttons -- same "subordinate control" sizing as
   .message-retry, used for Save/Cancel and the delete dialog's No. */
.client-header-btn-text {
  flex: 0 0 auto;
  font-size: var(--font-size-sm);
  font-weight: 400;
  padding: var(--space-1) var(--space-3);
  background: transparent;
  color: var(--color-text-muted);
  border: 1px solid var(--color-border-strong);
  box-shadow: none;
}

.client-header-btn-text:hover {
  background: var(--color-accent-soft);
  color: var(--color-accent);
  border-color: var(--color-accent);
  box-shadow: none;
}

.client-header-btn-text:active {
  transform: none;
  box-shadow: none;
}

.client-header-btn-text--save {
  color: var(--color-primary-contrast);
  background: var(--color-primary);
  border-color: var(--color-primary);
}

.client-header-btn-text--save:hover {
  background: var(--color-primary-hover);
  color: var(--color-primary-contrast);
  border-color: var(--color-primary-hover);
}

/* Delete-confirm modal (spec-client-header-rename-delete): the app's first
   native <dialog>, styled to match the existing card/elevation language
   rather than the browser's default plain-white-box chrome. Alpine only
   calls showModal()/close() on it -- no extra JS dependency. */
.delete-dialog {
  width: min(24rem, calc(100vw - var(--space-6)));
  padding: var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg-elevated);
  color: var(--color-text);
  box-shadow: var(--shadow-lg);
}

.delete-dialog::backdrop {
  background: rgba(42, 35, 24, 0.4);
}

.delete-dialog p {
  margin: 0 0 var(--space-4);
  overflow-wrap: break-word;
}

.delete-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}

.delete-dialog-confirm {
  margin: 0;
}

.delete-dialog-yes {
  background: var(--color-danger);
  box-shadow: none;
}

.delete-dialog-yes:hover {
  background: var(--color-danger);
  filter: brightness(0.92);
  box-shadow: none;
}
