/* Chat app, the padded shell and the conversation sidebar. */
/* ---- app shell (v3 / Figma reference): a padded grey "shell" holding two
   floating rounded cards, the sidebar and the chat panel, instead of a
   flush, edge-to-edge layout. Card-in-card, not full-bleed. */
/* No flex `gap` here on purpose, the sidebar's "hidden" state below works by
   giving it a large negative margin-left rather than removing it from the
   flex flow, and `gap` would still insert its full space on top of that
   negative margin (double-counted, since gap applies regardless of an
   item's rendered size), producing a detached-looking rail with a stray
   offset. Explicit margins on .sidebar/.rail instead, cleanly overridable
   per state. */
.app { width: 100%; height: 100dvh; display: flex; background: var(--bg); overflow: hidden;
       padding: var(--shell-pad);
       padding-left: max(var(--shell-pad), var(--safe-l));
       padding-right: max(var(--shell-pad), var(--safe-r)); }
/* Sidebar */
.sidebar {
  width: var(--sidebar-w); flex: none; position: relative;
  background: var(--panel);
  border-radius: var(--r-xl);
  box-shadow: var(--elev-1);
  display: flex; flex-direction: column;
  margin-right: var(--shell-gap);
  transition: margin-left 0.24s var(--ease);
}
.sidebar.hidden { margin-left: calc(-1 * var(--sidebar-w) - var(--shell-gap) - 1px); margin-right: 0; }

/* ---- pre-paint shell state ------------------------------------------------------------
   Set by the inline script in index.html's <head>, and the reason it is inline there: the
   modules that own these states are deferred, so the shell painted in the WRONG state and
   was corrected afterwards. Two visible symptoms, both reported:
     - in the drawer, the sidebar appeared and then vanished (js/chat/context.js adds
       body.panel-mode, and the rule that hides it is display:none -- a hard flash)
     - on the full page, the sidebar slid out to the left (js/chat/mobile.js closes it on a
       phone, animating `margin-left` through the 0.24s transition above)
   .shell-boot suppresses that transition for the first frames so applying the real state
   can never animate; .collapsed-boot pre-applies the phone default. Both are removed once
   the modules have run -- .collapsed-boot MUST be, or it would keep overriding margin-left
   and the sidebar could never be opened. .panel-boot is left in place permanently: it says
   the same thing as body.panel-mode and holds even if the module graph fails to load. */
html.panel-boot .sidebar,
html.panel-boot .rail,
html.panel-boot #toggleSidebar,
html.panel-boot #minimizeChatBtn,
html.panel-boot #closeChatBtn { display: none !important; }
html.collapsed-boot .sidebar { margin-left: calc(-1 * var(--sidebar-w) - var(--shell-gap) - 1px); margin-right: 0; }
html.shell-boot .sidebar, html.shell-boot .rail { transition: none !important; }

/* ---- TEMPORARY: companies page switched off -------------------------------------------
   Every route to /chat/companies.html is hidden rather than removed, so nothing is lost and
   the page still resolves if opened directly. Delete this block to bring them back. */
#companiesLink,
#railCompanies,
.about-link[href="companies.html"] { display: none !important; }
.sidebar-resizer { position: absolute; top: 0; right: -4px; width: 9px; height: 100%; cursor: ew-resize; z-index: 60; touch-action: none; }
.sidebar-resizer::before { content: ""; position: absolute; top: 0; right: 4px; width: 1px; height: 100%; background: transparent; transition: background 0.15s, box-shadow 0.15s; }
.sidebar-resizer:hover::before, .sidebar-resizer.dragging::before { background: var(--ring); box-shadow: 0 0 0 1px var(--ring); }
body.sidebar-resizing, body.sidebar-resizing .sidebar { transition: none !important; }
body.sidebar-resizing { cursor: ew-resize; user-select: none; }
body.sidebar-collapsed .sidebar-resizer { display: none; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; scroll-behavior: auto !important; transition: none !important; }
  .send:hover:not(:disabled) { transform: none; }
}

/* ---- sidebar top: brand lockup + tools ---- */
.sidebar-toptools { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); padding: var(--sp-4) var(--sp-3) var(--sp-2); }
.sb-brand { display: flex; align-items: center; gap: 9px; text-decoration: none; color: var(--text); min-width: 0; }
/* report-theme.css sets a global `a:hover { text-decoration: underline; }` for
   real inline text links (bot-message links, etc.), but that same rule also
   catches our nav-styled anchors (brand lockup, Companies link) since they're
   <a> tags too. None of our UI chrome should ever show a hover underline. */
.sb-brand:hover, .sb-brand:focus,
.sidebar-nav-item:hover, .sidebar-nav-item:focus,
.about-link:hover, .about-link:focus { text-decoration: none; }
/* Aligned to the icon column the rest of the sidebar uses, not to the toptools strip's
   own padding. New chat, Library and Companies all place their icon at 24px from the
   sidebar edge (a 12px container inset plus a 12px inset inside the item), while
   .sidebar-toptools only has the 12px container inset, so the star sat 12px short of
   that column. The extra 12px here puts its left edge on the same vertical line.
   Width only, no height: .av-star's aspect-ratio derives the rest. */
.sb-brand { padding-left: var(--sp-3); }
.sb-star { width: 22px; color: var(--accent); }
/* ---- sidebar wordmark -------------------------------------------------------
   font-weight: 500 (Satoshi Medium). The app uses two weights only, 400 and 500 -- see
   CLAUDE.md, "Weight: two only". Nothing here is bold.

   --font-display + the tighter tracking match .empty h2, the other place the product name
   is set, so the two lockups are the same typographic object at different sizes.
   `white-space: nowrap` + the parent's `min-width: 0` let the name clip rather than wrap
   to a second line when the sidebar is dragged narrow.
   -------------------------------------------------------------------------- */
.sb-wordmark {
  font-family: var(--font-display); font-weight: 500; font-size: 15px;
  letter-spacing: -0.015em; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.sb-tools { display: flex; align-items: center; gap: 2px; flex: none; }

.sidebar-header {
  padding: var(--sp-2) var(--sp-3) var(--sp-3); display: flex; flex-direction: column;
  gap: var(--sp-2); margin-bottom: var(--sp-2);
}
/* Header rows sit flush and let .sidebar-header's own `gap` do all the spacing, so the
   four rows (New chat, Library, Companies, and Settings down in the footer) are evenly
   pitched. There used to be an extra `margin-bottom: 4px` under New chat specifically,
   to keep a filled black pill from crowding the plain text row beneath it. New chat is
   not a pill any more -- it is the same nav row as the rest -- so singling it out would
   now just make the first gap bigger than the others for no reason. */
.sidebar-header .sidebar-nav-item { margin: 0; }

/* NO `.sidebar .new` RULE. New chat carries .sidebar-nav-item in the markup and is styled
   entirely by that rule below, exactly like Library, Companies and Settings.
   It used to be a solid brand-black pill with a white label (from an early Figma
   reference), which left one filled box in a sidebar whose whole visual language is
   colour-on-transparent -- and it needed its own ~10 lines of fill, radius, padding,
   hover and icon-colour declarations to say something the shared rule already says.
   Deleting the block IS the change: there is no second definition left to drift. */

/* Hover tooltip card for icon buttons. */
.tip { position: relative; }
.tip::after {
  content: attr(data-tip); position: absolute; top: calc(100% + 7px); left: 50%; transform: translateX(-50%);
  background: var(--btn-solid-bg); color: var(--btn-solid-fg); font-size: 11.5px; font-weight: 500; padding: 5px 9px;
  border-radius: var(--r); white-space: nowrap; pointer-events: none; opacity: 0;
  transition: opacity 0.12s; z-index: 60; box-shadow: 0 6px 18px rgba(10, 10, 10,0.28);
}
.tip.tip-right::after { left: auto; right: 0; transform: none; }
.tip.tip-left::after { left: 0; right: auto; transform: none; }
.tip.tip-up::after { top: auto; bottom: calc(100% + 7px); }
.tip.tip-side::after { top: 50%; left: calc(100% + 8px); right: auto; bottom: auto; transform: translateY(-50%); }
.tip:hover::after { opacity: 1; }

/* Collapsed icon rail, same floating rounded card treatment as the full
   sidebar (not a flush strip), with its own margin-right for the shell gap. */
.rail { display: none; }
body.sidebar-collapsed .rail {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  width: var(--rail-w); flex: none; padding: 14px 0; background: var(--panel);
  border-radius: var(--r-xl); box-shadow: var(--elev-1);
  /* The hidden sidebar's oversized negative margin-left (needed to push its OWN
     box fully off-canvas) also eats into this flex sibling's start position,
     since flex layout advances by margin+width, without this, the rail ends up
     flush against the viewport edge instead of sitting in .app's padding gutter.
     The --sidebar-w terms cancel out algebraically, so this constant offset stays
     correct even when the sidebar is resized. */
  margin-left: calc(var(--shell-gap) + 1px);
  margin-right: var(--shell-gap);
}
.rail .rail-spacer { flex: 1; }

/* ---- rail nav items: the collapsed twins of .sidebar-nav-item ----------------
   #railLibrary, #railCompanies and #railSettings are the same three destinations as
   Library / Companies / Settings in the expanded sidebar, so they get the same signal:
   #F06731 on hover, and STAYING #F06731 for as long as the thing they open is on screen.
   Without this they fell back to .icon-btn's generic grey-to-black hover, so collapsing
   the sidebar silently changed what those three buttons looked like when you used them --
   the one difference between the two modes should be the width, not the interaction.

   Placed after the `body.sidebar-collapsed .rail` block above and scoped under `.rail`, so
   it beats `.icon-btn:hover` (chat-messages.css) on specificity rather than on source
   order across files. Background stays transparent in both states, exactly as
   .sidebar-nav-item does -- colour is the entire signal, there is no pill.

   `.active` is set by the same code that lights the expanded button: syncLibraryActive()
   in js/chat/library.js and syncSettingsActive() in js/chat/settings.js each toggle it on
   BOTH ids, so the two modes cannot get out of step. #railCompanies has no `.active`
   because it is a link to another page, not a panel.

   No companion `svg` rule is needed (unlike .sidebar-nav-item below, which has to undo an
   explicit --muted on its icon): .icon-btn never colours its glyph directly, so the
   rail icons are already drawn in `currentColor` and follow the button on their own. */
.rail .rail-nav:hover, .rail .rail-nav.active { background: transparent; color: var(--accent); }


/* Library / Companies / Settings. These carry NO background in any state -- not at
   rest, not on hover, not while selected. Colour is the entire signal: black at rest,
   #F06731 on hover and while the panel they open is on screen.
   The border-radius is therefore inert (nothing paints inside the box), but it is left
   on the 24px default so the item is already correct if a background is ever
   reintroduced. */
.sidebar-nav-item {
  margin: 0 var(--sp-3) var(--sp-1); padding: 9px 12px; border: none; border-radius: var(--r);
  background: transparent; color: var(--text); font: inherit; font-size: 13.5px;
  font-weight: 500; cursor: pointer; display: flex; align-items: center; gap: 10px;
  /* Only `color` actually animates now; `background` stays in the list so a future
     background change eases in rather than snapping. */
  text-align: left; text-decoration: none; transition: background 0.14s, color 0.12s;
}
/* Hover and selected are the SAME state, deliberately. "Selected" means the thing this
   button opens is currently on screen (Library panel open, Settings modal open), so it
   looks exactly like the hover it was clicked from -- the item stays lit for as long as
   you are in that place, instead of snapping back to resting the moment the pointer
   leaves.
   NO background in either state: the orange (#F06731, --accent) on both the label and
   the icon is the entire signal. The grey pill that used to sit behind them is gone --
   with the text already changing colour it was a second, redundant cue for the same
   thing, and it put a filled box around three items in a sidebar that has no other
   boxes. */
.sidebar-nav-item:hover, .sidebar-nav-item.active { background: transparent; color: var(--accent); }
/* display:flex above out-specifies the UA [hidden] rule, same reason as .icon-btn[hidden]. */
.sidebar-nav-item[hidden] { display: none; }
/* `currentColor` rather than a second `var(--accent)`: the icon then follows the label
   in every state automatically, including any state added later. */
.sidebar-nav-item svg { width: 17px; height: 17px; flex: none; color: var(--muted); transition: color 0.12s; }
.sidebar-nav-item:hover svg, .sidebar-nav-item.active svg { color: currentColor; }
/* A flex column, matching .sidebar-header, so its .sidebar-nav-item children stretch
   to the full row width. As a plain block this container left the Settings button at
   its own content width, because `width: auto` on a <button> is shrink-to-fit even at
   display: flex, so its hover background covered only the icon and the word instead of
   the whole row, unlike Library and Companies in the header above (which stretch
   because THAT container is a flex column). Fixing it here rather than with
   `width: 100%` on the item keeps both groups getting their width the same way. */
/* No border-top -- `margin-top: auto` already pins Settings to the bottom of the
   sidebar and the padding gives it room; the hairline was decoration. */
.sidebar-footer { margin-top: auto; padding: var(--sp-2) 0; display: flex; flex-direction: column; }
.sidebar .list { flex: 1; overflow-y: auto; padding: 0 var(--sp-2) var(--sp-2); }

.sidebar-section:first-child .sidebar-section-label { padding-top: var(--sp-2); }
.sidebar-section-label {
  font-size: 12.5px; font-weight: 500; color: var(--text); letter-spacing: 0.06em;
  padding: var(--sp-3) var(--sp-3) var(--sp-1);
  display: flex; align-items: center; gap: 6px; cursor: pointer; user-select: none;
  list-style: none; transition: color 0.12s;
}
.sidebar-section-label::-webkit-details-marker { display: none; }
.sidebar-section-label .chev { width: 12px; height: 12px; flex: none; transition: transform 0.18s var(--ease); opacity: 0.8; }
details[open] > .sidebar-section-label .chev { transform: rotate(90deg); }
.sidebar-section-label:hover { color: var(--text); }

.conv {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 11px; margin: 1px 0; border-radius: var(--r);
  cursor: pointer; font-size: 13px; color: var(--text); transition: background 0.12s;
}
/* Active (selected) and hover use the SAME lighter grey, the selected chat
   shouldn't look darker/heavier than a plain hover, just persistently "on". */
.conv:hover, .conv.active, .conv.active:hover { background: var(--hover); }
.conv-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.conv-title-line { display: flex; align-items: center; gap: 6px; min-width: 0; }
.conv .t { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conv-snippet { font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conv-snippet mark { background: transparent; color: var(--accent); font-weight: 500; }
.conv .kebab { opacity: 0; border: none; background: none; color: var(--muted); cursor: pointer; padding: 2px; flex: none; display: grid; place-items: center; border-radius: var(--r); }
.conv:hover .kebab, .conv:focus-within .kebab, .conv .kebab.open { opacity: 1; }
/* No hover FILL behind the icon -- the glyph changing colour is the signal. Matches
   .icon-btn (chat-messages.css): a grey disc around a bare icon is chrome this app
   does not use anywhere else. */
.conv .kebab:hover { background: rgba(133,133,133,0.18); color: var(--text); }
.conv .kebab.open { background: rgba(133,133,133,0.18); color: var(--text); }
.conv .kebab svg { width: 16px; height: 16px; }

/* Row context menu popover. */
.ctx-menu {
  position: fixed; z-index: 120; min-width: 172px; padding: 6px;
  background: var(--panel); border-radius: var(--r);
  box-shadow: var(--elev-2); display: flex; flex-direction: column; gap: 1px;
  animation: ctxpop 0.13s var(--ease);
}
@keyframes ctxpop { from { opacity: 0; transform: translateY(-4px) scale(0.98); } to { opacity: 1; transform: translateY(0) scale(1); } }
.ctx-menu button {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  border: none; background: none; color: var(--text); font: inherit; font-size: 13px;
  padding: 8px 10px; border-radius: var(--r); cursor: pointer;
}
.ctx-menu button:hover { background: var(--hover); }
.ctx-menu button svg { width: 15px; height: 15px; flex: none; color: var(--muted); }
.ctx-menu button.danger { color: #C05227; }
.ctx-menu button.danger svg { color: #C05227; }
.ctx-menu button.danger:hover { background: rgba(192,82,39,0.12); }
.ctx-menu .sep { height: 1px; background: var(--border); margin: 4px 2px; }

/* Confirm dialog (delete). */
.modal-backdrop { position: fixed; inset: 0; background: rgba(10, 10, 10,0.32); z-index: 140; display: grid; place-items: center; opacity: 0; pointer-events: none; transition: opacity 0.16s; }
.modal-backdrop.show { opacity: 1; pointer-events: auto; }
.modal-box { width: min(430px, calc(100vw - 40px)); background: var(--panel); border-radius: var(--r-xl); box-shadow: var(--elev-2); padding: 24px 24px 20px; transform: translateY(10px) scale(0.98); transition: transform 0.16s var(--ease); }
.modal-backdrop.show .modal-box { transform: translateY(0) scale(1); }
.modal-box h3 { margin: 0 0 8px; font-family: var(--font-display); font-size: 18px; font-weight: 500; letter-spacing: -0.01em; }
.modal-box p { margin: 0 0 20px; font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.modal-box p b { color: var(--text); font-weight: 500; }
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; }
.modal-actions button { font: inherit; font-size: 13px; font-weight: 500; padding: 9px 18px; border-radius: var(--r); cursor: pointer; transition: background 0.14s, filter 0.14s; }
.modal-btn-cancel { background: transparent; color: var(--text); }
.modal-btn-cancel:hover { background: var(--hover); }
.modal-btn-danger { background: #C05227; color: #fff; }
.modal-btn-danger:hover { filter: brightness(0.88); }
.modal-btn-primary { background: var(--accent); color: #fff; }
.modal-btn-primary:hover { background: var(--accent-press); }

/* Select-mode toolbar + bulk bar. */
.list-toolbar { display: flex; justify-content: flex-end; padding: 0 var(--sp-3) var(--sp-1); }
.select-toggle { border: none; background: none; color: var(--muted); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; padding: 3px 6px; border-radius: var(--r); opacity: 0.6; transition: opacity 0.12s, color 0.12s, background 0.12s; }
/* No hover FILL behind the icon -- the glyph changing colour is the signal. Matches
   .icon-btn (chat-messages.css): a grey disc around a bare icon is chrome this app
   does not use anywhere else. */
.select-toggle:hover { color: var(--text); opacity: 1; }
.select-toggle.active { color: var(--text); opacity: 1; }
.bulk-bar { display: flex; align-items: center; gap: var(--sp-2); margin: 0 var(--sp-3) var(--sp-2); padding: var(--sp-2) var(--sp-3); border-radius: var(--r); background: var(--hover); font-size: 12.5px; color: var(--muted); }
.bulk-bar[hidden] { display: none; }
.bulk-bar span { flex: 1; }
.bulk-bar button { border: none; background: transparent; font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; padding: 4px 9px; border-radius: var(--r); }
.bulk-delete { color: #C05227; }
.bulk-delete:hover { background: rgba(192,82,39,0.12); }
.bulk-cancel { color: var(--text); }
.bulk-cancel:hover { background: rgba(133,133,133,0.14); }
.conv .del { font-size: 15px; }
.conv .del:hover { color: #C05227; }
.conv .rename svg { width: 14px; height: 14px; }
.conv .rename:hover { color: var(--text); }
.conv-rename { flex: 1; min-width: 0; font: inherit; font-size: 13px; color: var(--text); background: var(--surface-2); border-radius: var(--r); padding: 4px 8px; outline: none; box-shadow: 0 0 0 3px rgba(133,133,133,0.18); }
.conv-busy { width: 7px; height: 7px; border-radius: var(--r-pill); background: var(--accent); flex: none; animation: pulse 1.1s infinite ease-in-out; }
@keyframes pulse { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }
.turn-stop { align-self: flex-start; margin-top: 8px; background: var(--surface-2); color: var(--muted); font: inherit; font-size: 12px; padding: 5px 12px; border-radius: var(--r-pill); cursor: pointer; transition: color 0.14s; }
.turn-stop:hover { background: rgba(192,82,39,0.08); color: #C05227; }

