/* ============================================================================
 * vega-shell.css — Vega-specific shell styles
 *
 * The @mach33/ui package ships shell.css with the base layout primitives
 * (.m33-app grid, .m33-panel-left, .m33-panel-right, .m33-user, etc.).
 * THIS file is the Vega-specific layer on top — rail widths, chevrons,
 * scrollbars, modals, search bar, chat-message scoping, footer pills.
 *
 * Served from public/m33-static/ via the /m33 mount (framework-server.js
 * line 122). Link tag injected by lib/vega-shell-extras.js headExtras().
 *
 * If you're adding a new style:
 *   - One-off, scoped to one element → inline style="..." is fine
 *   - Repeated 2+ times, OR needs :hover/:focus/media-queries → put it here
 *   - Dynamic (depends on state) → keep the imperative cssText / classList
 *     toggle in JS, but add the class definition here
 * ========================================================================= */

/* ── Compact mode ──────────────────────────────────────────────────────────
 * When ?host=<known> or ?compact=1 (iframe embed), hide standalone shell
 * chrome so the chat panel fills the iframe cleanly. */
html[data-compact="1"], html[data-compact="1"] body { background: transparent !important; }
html[data-compact="1"] .m33-app { grid-template-columns: 0 1fr 0 !important; }
html[data-compact="1"] .m33-app.m33-left-collapsed { grid-template-columns: 0 1fr 0 !important; }
html[data-compact="1"] .m33-app.m33-right-collapsed { grid-template-columns: 0 1fr 0 !important; }
html[data-compact="1"] .m33-app > nav,
html[data-compact="1"] .m33-app > aside,
html[data-compact="1"] .m33-app > header,
html[data-compact="1"] .m33-rail-toggle,
html[data-compact="1"] .m33-rail-chev { display: none !important; }

/* ── Rail widths + footer row height (override @mach33/ui defaults) ───
 * Columns:
 *   Left  : 240px expanded / 56px collapsed (navigation rail)
 *   Right : 294px (team-chat rail — 30% reduction from the original
 *           420px per Brant's call; chat panel was too wide)
 * Rows:
 *   Header: 56px (package default — kept)
 *   Center: 1fr  (package default — kept)
 *   Footer: 64px (was 32px; Vega's status bar holds LLM chips + 36px
 *           roster avatars + env+SHA+fabric, which need real vertical
 *           room. Below 64px the contents overflow the grid row and
 *           get clipped at the viewport bottom — user reported
 *           "contents are 90% outside the footer"). */
.m33-app {
  grid-template-columns: 240px 1fr var(--m33-right-w, 294px) !important;
  grid-template-rows: 56px 1fr 64px !important;
}
.m33-app.m33-left-collapsed {
  grid-template-columns: 56px 1fr var(--m33-right-w, 294px) !important;
  grid-template-rows: 56px 1fr 64px !important;
}
.m33-app.m33-right-collapsed {
  grid-template-columns: 240px 1fr 0 !important;
  grid-template-rows: 56px 1fr 64px !important;
}
.m33-app.m33-left-collapsed.m33-right-collapsed {
  grid-template-columns: 56px 1fr 0 !important;
  grid-template-rows: 56px 1fr 64px !important;
}

/* Rail-chevron positioning vars — STARTING values only.
 * installRailChevrons() (lib/vega-shell-extras.js ~line 1675) writes the
 * live rail width into these on every layout / collapse via
 * style.setProperty, so the chevrons slide with the rail edge.
 * NO !important — that would beat the dynamic JS write and pin the
 * chevron at 240px even when the rail collapses to 56px. */
:root { --m33-rail-chev-left: 240px; --m33-rail-chev-right: 294px; }
/* Control-group chrome — ONE knob for EVERY group-container border: the
 * header Views/Relay/Profile boxes, the footer status pills, and the
 * centre-panel header pills. Now that the per-agent header underline carries
 * the colour, these go neutral grey (matched to the search bar) to cut the
 * colour overload. Change these two values to re-tint every group at once.
 * (operator 2026-07-24) */
:root { --m33-group-border: rgba(255,255,255,0.30); --m33-group-ring: transparent; }

/* ── Sidebar collapse chevrons ────────────────────────────────────────────
 * Default-styled <button> was a sub-pixel sliver that no one noticed.
 * Pill-shaped, semi-transparent at rest, full opacity on hover. */
.m33-rail-chevron {
  position: fixed; top: 50%; transform: translateY(-50%);
  width: 22px; height: 44px; padding: 0;
  background: var(--m33-panel, #1a1d2c);
  border: 1px solid var(--m33-border, rgba(255,255,255,0.18));
  color: var(--m33-text, #e1e4ed);
  border-radius: 0 8px 8px 0;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.55;
  transition: opacity .12s, background .12s, transform .18s;
  z-index: 100;
  box-shadow: 0 4px 14px rgba(0,0,0,0.30);
}
.m33-rail-chevron:hover { opacity: 1; background: rgba(99,102,241,0.18); }
.m33-rail-chevron-left  { left: var(--m33-rail-chev-left, 240px); }
/* Rides --m33-rail-chev-right, same convention as the left chevron — it
 * used to be hard-pinned to right:8px because the JS driving this var
 * always measured #m33-panel-right specifically, which is the CENTRE
 * column in team posture (Relay/Vega swap through the sidebar slot via
 * inline gridColumn), so the icon drifted beside the left chevron
 * instead of tracking the sidebar. lib/vega-shell-extras.js's
 * syncWidths() now measures whichever panel is CURRENTLY the sidebar
 * (m33SidebarPanel), so the var is safe to ride again — and doing so
 * fixes the chevron not following the sidebar's edge when it expands in
 * NORMAL posture too (the bug the right:8px pin itself introduced). */
.m33-rail-chevron-right { right: var(--m33-rail-chev-right, 294px); left: auto; border-radius: 8px 0 0 8px; }
.m33-rail-chevron-right svg { transform: scaleX(-1); }
.m33-rail-chevron.m33-flip svg { transform: scaleX(-1); }
.m33-rail-chevron-right.m33-flip svg { transform: none; }

/* ── App footer ──────────────────────────────────────────────────────────
 * Single full-width row at the bottom of the page, mirroring the header.
 * The @mach33/ui shell template already provides the right DOM:
 *   <footer class="m33-panel m33-footer" id="m33-footer">
 *     <div class="m33-footer-left">   …LLM status…           </div>
 *     <div class="m33-footer-center"> …team-presence roster… </div>
 *     <div class="m33-footer-right">  …env + SHA + fabric…   </div>
 *   </footer>
 * vega-shell-extras populates the three slots on boot.
 *
 * Two overrides on top of the package CSS:
 *   1. The agent skin (every Vega page) hides .m33-footer-left and
 *      .m33-footer-right via `visibility:hidden`. We always want them
 *      shown — re-enable here.
 *   2. Make the footer the same visual weight as the header: nudge
 *      the height up slightly, give footer-center a brighter color
 *      (the roster avatars want full opacity, not the muted footer
 *      default), and align the slots horizontally with consistent gaps. */
body[data-skin="agent"] .m33-footer-left,
body[data-skin="agent"] .m33-footer-right { visibility: visible !important; }

#m33-footer {
  /* Real status-bar height — generous enough that the avatars look
   * intentional and the LLM chips have presence (was 48px which the
   * user called "cramped to short"). Items inside scale up too via
   * the scoped overrides below. */
  min-height: 64px;
  padding: 10px 20px;
  font-size: 12px;
  background: var(--m33-bg, #0d0f14);
  border-top: 1px solid rgba(255,255,255,0.10);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.03);
}
/* Footer-scoped avatar sizing — chat-panel rail stays at 28px; in
 * the global footer they shrink to 24px so the TEAM pill reads as
 * dense and small, matching the LLM + BUILD pills' visual weight. */
#m33-footer .m33-roster-avatar { width: 24px; height: 24px; }
#m33-footer div.m33-roster-avatar { font-size: 9px; }

/* Channel header — smaller MEMBERS avatars (22px) so the pill reads as
 * compact. Keyed on .m33-chan-header, which every placement of the row
 * carries (pinned over Vega's panel, in flow on the four-view page), so
 * the two doors into a channel cannot look different. */
.m33-chan-header .m33-roster-avatar { width: 22px; height: 22px; }
.m33-chan-header div.m33-roster-avatar { font-size: 9px; }

/* Kill the 20px padding the @mach33/ui shell.css drops at the top of
 * the center main panel — that gap sat the Relay chat header below
 * the global app header by an extra 20px ("MASSIVE gap" the user
 * called out 2026-06-21). The Relay surface owns its own header
 * spacing now via .m33-tier1-header, so this padding is dead weight. */
#m33-panel-center { padding-top: 0 !important; }

/* The Relay header is now position:absolute on top of the surface so
 * the chat scrolls UNDER it (same overlay behavior as the composer at
 * the bottom). Anchor against the surface; the messages container's
 * inline padding-top:76px clears the overlap, mask softens the edge. */
.m33-agent-surface { position: relative; }
#m33-footer .m33-footer-left,
#m33-footer .m33-footer-right {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--m33-text-muted, rgba(255,255,255,0.55));
}
#m33-footer .m33-footer-center {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
  justify-content: center;
  /* Center slot is reserved for full-color roster avatars, not muted
   * text — clear the framework's color override. */
  color: var(--m33-text, #e1e4ed);
}
/* The five footer groups read as ONE centered cluster with tight, even gaps
 * — not spread edge-to-edge (operator: "gaps are too large … center the
 * collective 5 groups"). Override the base space-between + the flex:1 centre
 * slot that flung the zones to the edges. Agent skin only. */
/* Bookend to the header's double-line: a SINGLE agent-colour rule above the
 * footer, so the app is framed top and bottom in the house colour. */
body[data-skin="agent"] #m33-footer {
  justify-content: center;
  gap: 8px;
  border-top: 1px solid var(--m33-agent-accent, rgba(255,255,255,0.10)) !important;
}
body[data-skin="agent"] #m33-footer .m33-footer-center { flex: 0 1 auto; }
body[data-skin="agent"] #m33-footer .m33-footer-left,
body[data-skin="agent"] #m33-footer .m33-footer-right { gap: 8px; }

/* Launcher tile wordmark — the NAME is the prominent line; the descriptor
 * is a small colour-carrying label beneath it. (operator: name/descriptor
 * size & type were reversed; the descriptor carries the agent's house
 * colour, set inline per tile in headerExtras.) Descriptor stays muted for
 * locked tiles ("No access"), which get no inline colour. */
.m33-launcher-meta .m33-launcher-app {
  font-size: 13px; font-weight: 600; color: var(--m33-text, #e1e4ed);
  text-transform: none; letter-spacing: normal;
}
.m33-launcher-meta .m33-launcher-skin {
  font-size: 11px; font-weight: 400; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--m33-text-muted, rgba(255,255,255,0.55));
}
/* Labeled group containers — each slot wraps its content in a
 * .vega-footer-group with a .vega-footer-label prefix. Same min-height
 * across all three so the LLM / TEAM / BUILD pills read as a row of
 * matched chips. */
/* Shared base pill recipe — used by both the global app footer
 * (.vega-footer-group) and the Relay chat-panel header
 * (.vega-header-group). Same look on both so the top and bottom of
 * the app read as the same visual language. */
.vega-footer-group,
.vega-header-group {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 12px;
  background: rgba(255,255,255,0.025);
  border: 1px solid var(--m33-group-border);
  border-radius: 10px;
  /* Locked height — keeps each pill in a row visually aligned. min-height
   * let the TEAM pill grow 2px because the presence dot has bottom:-1px
   * which bled past the avatar. height + visible overflow keeps the
   * dot showing while pinning the pill at 36px exactly. */
  height: 36px;
  box-sizing: border-box;
  overflow: visible;
}
/* Footer pills are a touch taller — host the 24px roster avatars
 * comfortably. */
.vega-footer-group { height: 40px; }

.vega-footer-label,
.vega-header-label {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(255,255,255,0.45);
  padding-right: 10px;
  margin-right: 0;
  border-right: 1px solid rgba(255,255,255,0.08);
  white-space: nowrap;
  flex: 0 0 auto;
}

/* LLM pill content lives inside footer-left; pill-style not needed
 * anymore since the footer itself is the container. Just style the
 * dot + label. */
.vega-llm-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #7dffaf;
  box-shadow: 0 0 4px rgba(125,255,175,0.6);
  flex: 0 0 auto;
  transition: background 200ms;
}
.vega-llm-label {
  font-size: 11px;
  letter-spacing: 0.02em;
  color: rgba(255,255,255,0.85);
  cursor: pointer;
}
.vega-fabric-stats {
  font-size: 9px;
  opacity: 0.55;
  letter-spacing: 0.01em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
}
/* ── Roster avatar nodes (used in both the channel-rail header + the
 *    global footer pill via ChannelRailView.syncRosterDOM) ───────────── */
.m33-roster-node {
  position: relative;
  display: inline-block;
}
/* #145 — the per-avatar Page (🔔) button is ALWAYS visible and clickable: it is
 * its own explicit control (a hover-reveal read as "paging is gone"). Clicking
 * the 🔔 sends a page; clicking the avatar only opens the DM. */
.m33-roster-page {
  opacity: 0.9;
  pointer-events: auto;
  transition: opacity 120ms ease, transform 120ms ease;
}
.m33-roster-node:hover .m33-roster-page,
.m33-roster-page:hover,
.m33-roster-page:focus {
  opacity: 1;
  transform: scale(1.12);
}
.m33-roster-avatar {
  width: 28px; height: 28px;
  border-radius: 50%;
  display: block;
  object-fit: cover;
  background: rgba(255,255,255,0.10);
  transition: opacity 200ms, filter 200ms, box-shadow 200ms;
}
/* When the avatar is the initials-fallback <div> (no avatar_url), it
 * needs flex centering for the letters; <img> just uses object-fit. */
div.m33-roster-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 600;
}

/* ── Scrollbars ────────────────────────────────────────────────────────────
 * Browser-default scrollbars on dark UIs read as harsh light stripes. */
*::-webkit-scrollbar { width: 6px; height: 6px; }
*::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,0.10);
  border-radius: 3px;
}
*::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
*::-webkit-scrollbar-track { background: transparent; }
* { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.10) transparent; }

/* ── Patton agent skin overrides ──────────────────────────────────────────
 * Bump chat-meta visibility on the Patton agent skin so author labels
 * (PATTEN / user name) stand out. Vega defaults to 10px / muted;
 * Patton gets 11.5px, slightly less muted, and the author name a
 * vivid accent so the eye lands on it. Scoped to data-app so Vega's
 * chat stays as-is. */
body[data-app="patton"][data-skin="agent"] .m33-chat-meta {
  font-size: 11.5px;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.72);
}
body[data-app="patton"][data-skin="agent"] .m33-chat-author {
  font-weight: 700;
  color: #b8c0ff;
}
body[data-app="patton"][data-skin="agent"] .m33-chat-row.m33-from-user .m33-chat-author {
  color: #ffd9a8;
}
body[data-app="patton"][data-skin="agent"] .m33-chat-time {
  opacity: 0.85;
  font-weight: 500;
}

/* ── Page transition flash guard ──────────────────────────────────────────
 * Hide body content until the overlay is shown opaque on the new page.
 * The overlay element lives at the end of <body>; this style keeps the
 * intervening flash out of the user's view. Removed by the body-end script
 * once the overlay has taken over. */
html[data-m33-transitioning="1"] body > *:not(.m33-transition) { visibility: hidden; }
html[data-m33-transitioning="1"] body { background: #000; }

/* ── Modal size variants ──────────────────────────────────────────────────
 * Override the package's 540px default when openModal() is called with
 * opts.size. The body still scrolls on overflow; the FRAME is the part
 * that's fixed-sized. */
.m33-modal[data-m33-size="large"] {
  max-width: 1100px;
  max-height: calc(100vh - 64px);
}
.m33-modal[data-m33-size="fixed"] {
  /* Admin Settings: don't let content shifts (switching tabs, expanding
   * tables) resize the whole modal. Width and height are clamped within
   * the viewport so small screens don't overflow, but on a desktop the
   * modal lands at the same size every time. */
  width: min(1200px, calc(100vw - 48px));
  max-width: min(1200px, calc(100vw - 48px));
  height: min(800px, calc(100vh - 64px));
  max-height: min(800px, calc(100vh - 64px));
}
.m33-modal[data-m33-size="fixed"] .m33-modal-body {
  flex: 1 1 auto;
  min-height: 0; /* allow inner scroll instead of pushing the modal taller */
}

/* ── Global search bar ────────────────────────────────────────────────── */
.m33-global-search {
  position: relative;
  display: inline-flex;
  align-items: center;
  margin: 0 4px;
  /* #173 — lift the search's whole stacking context above the header
     siblings (pills / session box sit at z 800/900/4000). At z-index 200
     the results dropdown was trapped beneath them regardless of its own
     z-index. Stays below app modals (z 2147483000). */
  z-index: 5000;
}
/* Centred in the gap between the title and the session/views box: as the
 * header's middle flex child (hoistSearch places it there), auto side
 * margins absorb the free space evenly, floating the bar mid-gap. Natural
 * width is preserved so the results popup still anchors under the input. */
.m33-global-search.m33-search-centered { margin-left: auto; margin-right: auto; }
.m33-search-input-wrap {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.30);
  border-radius: 6px;
  padding: 0 8px;
  height: 26px;
  width: 300px;             /* larger + FIXED — no grow-on-focus */
  transition: border-color 200ms ease, background 200ms ease;
}
.m33-search-input-wrap:focus-within {
  /* width stays 300px — the bar is a fixed length now; only the accent
   * border + wash change on focus. */
  border-color: rgba(125,138,255,0.55);
  background: rgba(125,138,255,0.08);
}
.m33-search-icon {
  flex-shrink: 0;
  opacity: 0.45;
}
.m33-search-input-wrap:focus-within .m33-search-icon { opacity: 0.7; }
.m33-search-input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: #e8eaff;
  font: 500 11px/1.4 -apple-system, system-ui, sans-serif;
  letter-spacing: 0.01em;
  padding: 0;
}
.m33-search-input::placeholder { color: rgba(255,255,255,0.30); }
.m33-search-clear {
  flex-shrink: 0;
  background: transparent;
  border: none;
  color: rgba(255,255,255,0.4);
  font: 500 14px/1 sans-serif;
  cursor: pointer;
  padding: 0 1px;
  transition: color 120ms;
}
.m33-search-clear:hover { color: rgba(255,255,255,0.8); }
.m33-search-kbd {
  flex-shrink: 0;
  font: 500 9px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: rgba(255,255,255,0.25);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 3px;
  padding: 1px 3px;
}
.m33-search-input-wrap:focus-within .m33-search-kbd { display: none; }

/* Dropdown results panel.
   #173 — position:FIXED, not absolute. The header (and its overflow-y:auto
   panel) clips any absolutely-positioned dropdown that extends below it: the
   results rendered into the DOM (hidden=false, full of rows) but were trapped
   inside the clipped header and never painted on screen. This is the SAME
   escape the app-launcher panel uses (see hoistSearch / placePanel). JS sets
   top/left from the input's live bounding rect (positionResults). */
.m33-search-results {
  position: fixed;
  /* Width is SET BY positionResults() from the search bar's own measured
     rect, so the dropdown lines up on both edges. This is only the value
     that applies for the frame before the first positioning pass — it must
     not be a second, competing definition of the bar's width (a hardcoded
     360 against the bar's 300 is what made the right edge disagree). */
  min-width: 240px;
  max-height: 380px;
  overflow-y: auto;
  background: rgba(14,16,28,0.95);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6), 0 0 0 1px rgba(0,0,0,0.3);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  padding: 4px;
  z-index: 5000;
}
.m33-search-result {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-radius: 6px;
  cursor: pointer;
  transition: background 100ms;
}
.m33-search-result:hover,
.m33-search-result.m33-active {
  background: rgba(125,138,255,0.14);
}
.m33-search-result-glyph {
  flex-shrink: 0;
  width: 22px; height: 22px;
  border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  font: 600 11px/1 -apple-system, system-ui, sans-serif;
  color: #fff;
  background: rgba(255,255,255,0.08);
}
.m33-search-result-text {
  flex: 1; min-width: 0;
}
.m33-search-result-label {
  font: 500 13px/1.3 -apple-system, system-ui, sans-serif;
  color: #e8eaff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The matched words inside a message-search snippet. <mark> stays the
   element — it is the semantic tag for a search hit and screen readers
   announce it — but its UA default is a yellow block, which on this dark
   panel reads as a highlighter smear across the one line the user is
   trying to scan. Underline the match instead: same information, no
   second background colour competing with the row's own hover/active
   states. */
.m33-search-result-label mark {
  background: transparent;
  color: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
.m33-search-result-sub {
  font: 400 11px/1.3 -apple-system, system-ui, sans-serif;
  color: rgba(255,255,255,0.4);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.m33-search-result-score {
  flex-shrink: 0;
  font: 400 10px/1 ui-monospace, SFMono-Regular, monospace;
  color: rgba(255,255,255,0.2);
}
.m33-search-empty {
  padding: 16px;
  text-align: center;
  color: rgba(255,255,255,0.35);
  font: 400 12px/1.4 -apple-system, system-ui, sans-serif;
}

/* Surface highlighting: canvas cards, chat rows, table rows */
.m33-card.m33-search-dimmed {
  opacity: 0.12 !important;
  pointer-events: none;
  transition: opacity 300ms ease;
}
.m33-card.m33-search-highlighted {
  box-shadow: 0 0 0 2px rgba(125,138,255,0.55), 0 0 16px rgba(125,138,255,0.15) !important;
  z-index: 1;
  transition: box-shadow 300ms ease;
}
.m33-chat-row.m33-search-dimmed {
  opacity: 0.15;
  transition: opacity 300ms ease;
}
/* #207 — a task/notification "Go to" lands the reader on the exact message
   and flashes it once so it's found at a glance, then fades to normal. */
.m33-msg-flash {
  animation: m33-msg-flash 2.4s ease-out;
  border-radius: 8px;
}
@keyframes m33-msg-flash {
  0%   { background: rgba(125,138,255,0.30); box-shadow: 0 0 0 2px rgba(125,138,255,0.45); }
  70%  { background: rgba(125,138,255,0.16); box-shadow: 0 0 0 2px rgba(125,138,255,0.20); }
  100% { background: transparent;            box-shadow: 0 0 0 2px rgba(125,138,255,0); }
}

/* #251 — per-channel notification bell + its menu (tier-1 header). The bell
   dims and grows an "@" badge when the channel is set to mentions-only. */
.m33-tc-notify { opacity: 0.62; transition: opacity 140ms ease, color 140ms ease; }
.m33-tc-notify:hover { opacity: 1; }
.m33-tc-notify[data-level="mentions"] { opacity: 0.9; color: #f5b455; }
.m33-tc-notify-badge {
  display: none;
  position: absolute;
  top: -2px; right: -3px;
  min-width: 12px; height: 12px;
  padding: 0 2px;
  box-sizing: border-box;
  border-radius: 7px;
  background: #f5b455;
  color: #1a1206;
  font-size: 8px; font-weight: 800; line-height: 12px;
  text-align: center;
  align-items: center; justify-content: center;
  pointer-events: none;
}
.m33-tc-notify[data-level="mentions"] .m33-tc-notify-badge { display: flex; }
.m33-tc-notify-menu {
  position: absolute;
  top: calc(100% + 6px); right: 0;
  min-width: 236px;
  padding: 5px;
  background: rgba(20,22,30,0.98);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px;
  box-shadow: 0 16px 40px rgba(0,0,0,0.5);
  z-index: 60;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.m33-tc-notify-head {
  font-size: 10px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #8a8fa3;
  padding: 6px 9px 4px;
}
.m33-tc-notify-opt {
  display: flex; align-items: flex-start; gap: 8px;
  width: 100%;
  padding: 8px 9px;
  background: transparent;
  border: 0; border-radius: 7px;
  color: #dfe3ef;
  text-align: left; cursor: pointer;
}
.m33-tc-notify-opt:hover { background: rgba(255,255,255,0.06); }
.m33-tc-notify-check {
  flex: 0 0 14px;
  width: 14px;
  color: #7d8aff;
  font-size: 12px; line-height: 18px;
  opacity: 0;
}
.m33-tc-notify-opt.is-active .m33-tc-notify-check { opacity: 1; }
.m33-tc-notify-txt { display: flex; flex-direction: column; gap: 1px; }
.m33-tc-notify-txt strong { font-size: 12.5px; font-weight: 600; }
.m33-tc-notify-txt em { font-size: 10.5px; font-style: normal; color: #8a8fa3; }

/* Channel actions ("…" kebab, tier-1 header) — Leave lives here now, not
   crowded against the channel title in the picker row. Same wrap/menu
   convention as .m33-tc-notify above (position:relative wrap, hidden-toggle
   menu anchored top-right). */
.m33-tc-more { opacity: 0.62; transition: opacity 140ms ease; }
.m33-tc-more:hover { opacity: 1; }
.m33-tc-more-menu {
  position: absolute;
  top: calc(100% + 6px); right: 0;
  min-width: 168px;
  padding: 5px;
  background: rgba(20,22,30,0.98);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px;
  box-shadow: 0 16px 40px rgba(0,0,0,0.5);
  z-index: 60;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.m33-tc-more-opt {
  display: flex; align-items: center;
  width: 100%;
  padding: 8px 9px;
  background: transparent;
  border: 0; border-radius: 7px;
  color: #dfe3ef;
  text-align: left; cursor: pointer;
  font-size: 12.5px;
}
.m33-tc-more-opt:hover { background: rgba(255,255,255,0.06); }
/* `display:flex` above outranks the hidden attribute's UA default, so an
   option the trunk hides (Leave, on #general and DMs) would stay on screen
   and stay clickable. The menu is no longer all-or-nothing — Copy link is
   offered everywhere, Leave is not — so each option needs this. */
.m33-tc-more-opt[hidden] { display: none; }
.m33-tc-more-leave { color: #f5786e; }

/* #170 — shared-reply reference: a compact quote line above a reply's bubble
   that links back to the message it answers. */
.m33-tc-replyref {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 85%;
  margin: 0 0 3px;
  padding: 2px 8px 2px 7px;
  background: rgba(125,138,255,0.08);
  border: 0;
  border-left: 2px solid rgba(125,138,255,0.55);
  border-radius: 4px;
  color: #9aa0b4;
  font: inherit;
  font-size: 11px;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
}
.m33-tc-replyref:hover { background: rgba(125,138,255,0.14); }
.m33-tc-replyref-arrow { flex: 0 0 auto; color: #8b93ff; font-size: 12px; }
.m33-tc-replyref-name { flex: 0 0 auto; font-weight: 600; color: #c9cddc; }
.m33-tc-replyref-snip {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0.75;
}

/* ── Animations ───────────────────────────────────────────────────────── */
@keyframes m33-sandbox-pulse {
  0%, 100% { border-color: rgba(240,200,80,0.85); box-shadow: 0 24px 64px rgba(0,0,0,0.55), 0 0 12px rgba(240,200,80,0.25); }
  50%      { border-color: rgba(240,200,80,1);    box-shadow: 0 24px 64px rgba(0,0,0,0.55), 0 0 24px rgba(240,200,80,0.45); }
}
@keyframes m33spin { to { transform: rotate(360deg); } }

/* ── Thinking-bubble animation ────────────────────────────────────────────
 * Two cases:
 *  1. .m33-chat-msg.m33-thinking-empty — assigned to an empty assistant
 *     bubble while we wait for the first delta. Shows three pulsing
 *     dots so the user has a visible signal that Vega is working.
 *  2. .m33-chat-msg-thinking — applied during a tool round-trip
 *     ("Searching…"). Adds an italic + soft pulse so the placeholder
 *     reads as in-progress rather than as the final answer. */
/* One pseudo-element renders all three dots: the element background is
 * the MIDDLE dot; the left/right dots are box-shadows. Each dot brightens
 * (and swells +1px via shadow spread) in sequence — left → middle →
 * right — so the indicator reads as a live typing wave, not the old
 * all-at-once blink. color-mix keeps it themed off currentColor. */
.m33-chat-msg.m33-thinking-empty:empty::after {
  content: '';
  display: inline-block;
  vertical-align: middle;
  margin: 8px 16px 8px 14px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: color-mix(in srgb, currentColor 30%, transparent);
  box-shadow: -11px 0 0 0 color-mix(in srgb, currentColor 30%, transparent),
               11px 0 0 0 color-mix(in srgb, currentColor 30%, transparent);
  animation: m33-thinking-wave 1.2s ease-in-out infinite;
}
@keyframes m33-thinking-wave {
  0%, 100% {
    background-color: color-mix(in srgb, currentColor 30%, transparent);
    box-shadow: -11px 0 0 1px color-mix(in srgb, currentColor 90%, transparent),
                 11px 0 0 0   color-mix(in srgb, currentColor 30%, transparent);
  }
  33% {
    background-color: color-mix(in srgb, currentColor 90%, transparent);
    box-shadow: -11px 0 0 0 color-mix(in srgb, currentColor 30%, transparent),
                 11px 0 0 0 color-mix(in srgb, currentColor 30%, transparent);
  }
  66% {
    background-color: color-mix(in srgb, currentColor 30%, transparent);
    box-shadow: -11px 0 0 0   color-mix(in srgb, currentColor 30%, transparent),
                 11px 0 0 1px color-mix(in srgb, currentColor 90%, transparent);
  }
}
/* Long waits shouldn't strobe for motion-sensitive users — hold the dots
 * steady at a calm mid-opacity instead. */
@media (prefers-reduced-motion: reduce) {
  .m33-chat-msg.m33-thinking-empty:empty::after { animation: none; opacity: 0.55; }
}
.m33-chat-msg-thinking {
  font-style: italic;
  opacity: 0.7;
  animation: m33-thinking-pulse 1.6s ease-in-out infinite;
}
@keyframes m33-thinking-pulse {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 0.85; }
}

/* ── Markdown rendering inside assistant bubbles ──────────────────────────
 * mdToHtmlEscaped produces <h1>..<h4>, <strong>, <em>, <code>, <ul>,
 * <ol>, <blockquote>, <p>. Without scoped styles those inherit the
 * surrounding chat font and end up too large / not enough spaced.
 * Tighten them to the bubble's reading scale. */
.m33-chat-msg h1, .m33-chat-msg h2, .m33-chat-msg h3, .m33-chat-msg h4 {
  margin: 12px 0 4px;
  font-weight: 600;
  line-height: 1.25;
}
.m33-chat-msg h1 { font-size: 1.25em; }
.m33-chat-msg h2 { font-size: 1.15em; }
.m33-chat-msg h3 { font-size: 1.05em; }
.m33-chat-msg h4 { font-size: 1em; opacity: 0.85; }
.m33-chat-msg h1:first-child, .m33-chat-msg h2:first-child,
.m33-chat-msg h3:first-child, .m33-chat-msg h4:first-child { margin-top: 0; }
/* Paragraph rhythm. These margins are now the ONLY gap between blocks: the
 * bubble collapses whitespace once it holds block elements (see the pre-wrap
 * note in packages/@mach33/ui/static/shell.css), so what used to read as a
 * paragraph break was 6px of margin plus a 21px phantom blank line. At the
 * bare 6px the prose reads cramped, so the gap is stated deliberately here
 * instead of being whatever fell out of the doubling. */
.m33-chat-msg p { margin: 11px 0; }
.m33-chat-msg p:first-child { margin-top: 0; }
.m33-chat-msg p:last-child  { margin-bottom: 0; }
.m33-chat-msg ul, .m33-chat-msg ol { margin: 11px 0 11px 18px; padding: 0; }
.m33-chat-msg li { margin: 4px 0; }
.m33-chat-msg code {
  background: rgba(255,255,255,0.08);
  border-radius: 3px;
  padding: 1px 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9em;
}
.m33-chat-msg strong { font-weight: 600; }
.m33-chat-msg blockquote {
  margin: 6px 0;
  padding: 2px 10px;
  border-left: 2px solid rgba(255,255,255,0.25);
  opacity: 0.85;
}

/* ─────────────────────────────────────────────────────────────
 * Modality picker + mute (Relay header — UI scaffolding)
 *
 * Three pills (Text / Audio / Video), exactly one active at a time.
 * Mute button appears only when modality is Audio or Video.
 * State-only today; actual call wiring lands when /api/chat/stream
 * routes through the Proto's turn() in a later phase.
 * ───────────────────────────────────────────────────────────── */
.m33-modality-group { gap: 8px; }
.m33-modality-btn {
  height: 24px;
  padding: 0 10px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.01em;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 6px;
  color: rgba(255,255,255,0.55);
  cursor: pointer;
  transition: background 120ms, border-color 120ms, color 120ms;
  font-family: inherit;
}
.m33-modality-btn:hover:not(.m33-active):not(:disabled) {
  border-color: rgba(255,255,255,0.20);
  color: rgba(255,255,255,0.85);
}
.m33-modality-btn.m33-active {
  background: rgba(99,179,237,0.12);
  border-color: rgba(99,179,237,0.35);
  color: rgba(160,200,240,0.95);
}
.m33-modality-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.m33-mute-btn {
  height: 28px;
  padding: 0 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 8px;
  color: rgba(255,255,255,0.80);
  font-size: 11px;
  font-weight: 500;
  cursor: pointer;
  transition: background 120ms, border-color 120ms, color 120ms;
  font-family: inherit;
}
.m33-mute-btn:hover:not([data-muted="1"]):not(:disabled) {
  border-color: rgba(255,255,255,0.25);
  color: rgba(255,255,255,1);
}
.m33-mute-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  color: rgba(255,255,255,0.4);
}
.m33-mute-btn[data-muted="1"] {
  background: rgba(220,80,80,0.15);
  border-color: rgba(220,80,80,0.45);
  color: rgba(255,160,160,0.95);
}
.m33-mute-btn[data-muted="1"]:hover {
  background: rgba(220,80,80,0.20);
  border-color: rgba(220,80,80,0.60);
}
.m33-mute-icon { font-size: 13px; line-height: 1; }

/* ─────────────────────────────────────────────────────────────
 * Channel pill — split (label-toggle + ▾ arrow) + Tasks pill inside CHANNEL
 *
 * The channel pill is now two adjacent button zones rendered as one pill:
 *   .m33-tc-chan-toggle  — click name flips chat↔tasks
 *   .m33-tc-chan-arrow   — click ▾ opens channel/DM picker
 * Tasks pill (.m33-tier1-btn-tasks) lives INSIDE the CHANNEL group
 * alongside the channel pill — they are mutually-exclusive centre-panel views.
 * ───────────────────────────────────────────────────────────── */
.m33-tc-chan-split {
  /* Pill chrome moves to the wrap; inner buttons are flush. Mirrors the
   * existing .m33-tc-chan look (transparent + 14px radius active state). */
  border-radius: 14px;
  background: rgba(99,102,241,0.18);
  border: 1px solid var(--m33-accent, #6366f1);
}
.m33-tc-chan-split .m33-tc-chan-toggle:hover,
.m33-tc-chan-split .m33-tc-chan-arrow:hover {
  background: rgba(255,255,255,0.06);
}
.m33-tc-chan-split .m33-tc-chan-arrow:hover { opacity: 1; }

/* Tasks pill inside the CHANNEL group — match the modality pills' size + shape
 * so the two centre-panel pickers (channel chat / tasks) read as siblings. */
.m33-tier1-btn-tasks {
  height: 24px;
  padding: 0 10px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.01em;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 6px;
  color: rgba(255,255,255,0.55);
  cursor: pointer;
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  transition: background 120ms, border-color 120ms, color 120ms;
}
.m33-tier1-btn-tasks:hover:not(.m33-active) {
  border-color: rgba(255,255,255,0.20);
  color: rgba(255,255,255,0.85);
}
.m33-tier1-btn-tasks.m33-active {
  background: rgba(99,179,237,0.12);
  border-color: rgba(99,179,237,0.35);
  color: rgba(160,200,240,0.95);
}

/* When tier-1 is "tasks", de-emphasise the channel pill so the user can
 * see at a glance which view is active. Keep it clickable — clicking the
 * channel label flips back to chat. */
.m33-agent-surface[data-tier1="tasks"] .m33-tc-chan-split {
  background: rgba(255,255,255,0.04);
  border-color: rgba(255,255,255,0.10);
}

/* ============================================================
 * Shared agent header — 3-box chrome (views · Relay · profile)
 * A "project/views" box, a "Relay" box (implanted agent), and a profile box,
 * all sharing the same blue rounded-rect outline; plus a header bottom edge
 * line and a soft chat-stream fade under the header and composer. Shared
 * across all agent skins; only box contents differ.
 * ============================================================ */
body[data-skin="agent"] .m33-header {
  /* Agent signature strip — a thin DOUBLE rule in the running agent's house
   * colour (--m33-agent-accent, set in the shell head from lib/agent-marks).
   * 4px renders `double` as two hairlines with a clear gap: whose room you
   * are in, at a glance. Falls back to the neutral edge line when unset. */
  border-bottom: 4px double var(--m33-agent-accent, rgba(255, 255, 255, 0.1)) !important;
}
/* The thin brand-suffix half (Research / Communications / Operations …)
 * carries the same house colour as the signature strip: "Vega Research"
 * reads green, "Relay Communications" reads indigo — the wordmark tied to
 * the room. Agent skin only; login/application chrome keeps its own colour. */
body[data-skin="agent"] .m33-app-name-thin { color: var(--m33-agent-tint, var(--m33-agent-accent, inherit)); }
body[data-skin="agent"] .nw-research-box .nw-grp-label,
body[data-skin="agent"] .nw-relay-box .nw-grp-label {
  font: 600 10px/1 inherit; color: rgba(255,255,255,0.5);
  text-transform: uppercase; letter-spacing: 0.06em;
  padding: 0 8px 0 4px; align-self: center; white-space: nowrap;
}
body[data-skin="agent"] .nw-relay-box { margin-left: 8px; }
/* The Relay label matches its neighbours (Chat/Canvas/Graph buttons):
 * same family, same 12px, same muted color — mixed case, label not heading. */
body[data-skin="agent"] .nw-relay-box .nw-grp-label {
  /* Longhands, not the `font:` shorthand — `inherit` is not a valid
   * family inside the shorthand, so browsers DROP the whole declaration
   * silently (measured live: label rendered 14px while Graph is 12px). */
  text-transform: none; letter-spacing: 0;
  font-size: 12px; font-weight: 400; line-height: 1;
  color: var(--m33-text-muted);
}

/* One header height. The Relay group's icon buttons (13px SVGs) and the
 * search pill rendered shorter than the text-button groups — pin all three
 * to the same scale so the header reads as one row of equals. */
body[data-skin="agent"] .nw-research-box,
body[data-skin="agent"] .nw-relay-box { min-height: 34px; align-items: center; }
body[data-skin="agent"] .nw-relay-box .m33-mode-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding-top: 7px; padding-bottom: 7px; min-height: 27px; box-sizing: border-box;
}
/* ── Relay surface selector — split buttons ────────────────────────────────
   The global bar picks a surface. Each chat surface is a SPLIT button: body =
   switch surface, arrow = open its picker (Agent Chat=sessions, Channel
   Chats=channels). HARD RULE: the arrow can NEVER clip/crowd out —
   flex-shrink:0 on the arrow + overflow:visible on the split. */
body[data-skin="agent"] .m33-surf-split {
  display: inline-flex; align-items: stretch; overflow: visible;
  border: 1px solid rgba(255,255,255,0.13); border-radius: 8px;
  background: rgba(255,255,255,0.045);
}
body[data-skin="agent"] .m33-surf-split .m33-surf-body,
body[data-skin="agent"] .m33-surf-split .nw-grp-label { border: 0; background: transparent; border-radius: 8px 0 0 8px; }
body[data-skin="agent"] .m33-surf-arrow {
  flex-shrink: 0; display: inline-flex; align-items: center; gap: 4px;
  min-width: 22px; padding: 0 8px; align-self: stretch;
  border: 0; border-left: 1px solid rgba(255,255,255,0.12); border-radius: 0 8px 8px 0;
  background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 11px; opacity: 0.7;
}
body[data-skin="agent"] .m33-surf-arrow:hover { opacity: 1; background: rgba(255,255,255,0.07); }
/* Channel Chats is a solo surface button — its channel picker · Mode · Members
   live in the channel's own tier-1 header, not here. A plain box, no split. */
body[data-skin="agent"] .m33-surf-solo {
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid rgba(255,255,255,0.13); border-radius: 8px;
  background: rgba(255,255,255,0.045); padding: 5px 10px;
  color: inherit; font: inherit; font-size: 12px; cursor: pointer; text-decoration: none;
}
body[data-skin="agent"] .m33-surf-solo:hover { background: rgba(255,255,255,0.08); }
/* the session switcher, folded into the Agent Chat split, must be INLINE — not
   the absolutely-positioned standalone banner it is by default. */
body[data-skin="agent"] .m33-surf-split .m33-ideation-switcher {
  position: static !important; inset: auto !important; transform: none !important; margin: 0 !important;
}
/* In the agent header the search reads as a SIBLING of the control-group
 * pills (nw boxes / .m33-mode-toggle): same height, 8px corners, card fill,
 * and the shared --m33-group-border. (operator 2026-07-24: height/corners/
 * shading didn't match.) Login (application skin) keeps the flat field look. */
body[data-skin="agent"] .m33-search-input-wrap {
  height: 34px;
  border-radius: 8px;
  background: var(--m33-card, #22253a);
  border-color: var(--m33-group-border);
}

/* The user's own chat bubble carries the AGENT colour (like the header
 * underline), not the global indigo — Vega green, Relay indigo, Patton amber…
 * Overrides the framework's .m33-chat-msg.m33-from-user{background:accent}.
 * Falls back to --m33-accent where no agent colour is injected. */
body[data-skin="agent"] .m33-chat-msg.m33-from-user {
  background: var(--m33-agent-accent, var(--m33-accent));
  color: var(--m33-agent-ink, #fff);   /* dark ink on light accents (Cristi yellow) */
}

/* Primary actions (Send, Assistant, Create…) carry the agent colour, not the
 * global indigo; hover lightens it. And the composer prompt boxes take the
 * agent colour on focus. */
body[data-skin="agent"] .m33-primary-btn { background: var(--m33-agent-accent, var(--m33-accent)); color: var(--m33-agent-ink, #fff); }
body[data-skin="agent"] .m33-primary-btn:hover { background: color-mix(in oklab, var(--m33-agent-accent, var(--m33-accent)) 85%, #fff); }
body[data-skin="agent"] .m33-chat-form input:focus { border-color: var(--m33-agent-accent, var(--m33-accent)); }

/* Project/views group, Relay group, and profile group share the same blue
 * outline + rounded-rect shape (the Relay box keeps the shape, signalling the
 * implanted agent by content rather than a different border). */
body[data-skin="agent"] .nw-research-box,
body[data-skin="agent"] .nw-relay-box,
body[data-skin="agent"] .nw-profile-box {
  border: 1px solid var(--m33-group-border) !important;
  box-shadow: 0 0 0 1px var(--m33-group-ring);
}
body[data-skin="agent"] .nw-profile-box {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 3px 10px 3px 6px; border-radius: 8px;
}

/* Newton: the project dropdown IS the context control, so hide the older
 * session switcher. Vega/Patton reuse that switcher, moved into the views box
 * (so it stays visible there) — hence this hide stays Newton-only. */
body[data-app="newton"] .m33-ideation-switcher { display: none !important; }

/* Context dropdown — replaces the static "Research" label. Newton shows the
 * current project; Vega/Patton mount the session switcher into the slot. */
body[data-skin="agent"] .nw-proj-dd { position: relative; display: inline-flex; align-items: center; }
body[data-skin="agent"] .nw-proj-dd-btn {
  display: inline-flex; align-items: center; gap: 5px;
  background: none; border: none; cursor: pointer;
  font: 600 11px/1 inherit; color: rgba(255,255,255,0.8);
  padding: 0 8px 0 6px; white-space: nowrap;
}
body[data-skin="agent"] .nw-proj-dd-btn:hover { color: #fff; }
body[data-skin="agent"] .nw-proj-dd-chev { font-size: 9px; opacity: 0.55; }
/* Fixed positioning so the menu escapes the fixed-height header's clip;
 * coordinates are set from the button's rect when it opens. */
body[data-skin="agent"] .nw-proj-dd-menu {
  position: fixed; min-width: 240px;
  background: #1a1d2c; border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px; box-shadow: 0 16px 48px rgba(0,0,0,0.5);
  padding: 6px; z-index: 4000;
}
body[data-skin="agent"] .nw-proj-dd-item {
  display: block; width: 100%; text-align: left; box-sizing: border-box;
  padding: 8px 10px; border-radius: 7px; background: none; border: none;
  cursor: pointer; font: inherit; color: #dfe1f1; text-decoration: none;
}
body[data-skin="agent"] .nw-proj-dd-item:hover { background: rgba(125,138,255,0.12); }
body[data-skin="agent"] .nw-pdd-name { display: block; font: 600 12px/1.3 inherit; color: #fff; }
body[data-skin="agent"] .nw-pdd-sub { display: block; font: 11px/1.3 inherit; color: rgba(255,255,255,0.45); }
body[data-skin="agent"] .nw-pdd-all { border-top: 1px solid rgba(255,255,255,0.08); margin-top: 4px; padding-top: 8px; }

/* Vega/Patton mount the session switcher into the views-box slot. The switcher
 * is an absolutely-positioned standalone banner by default — neutralise that
 * inside the slot so it flows inline within the group box. */
body[data-skin="agent"] .nw-session-slot .m33-ideation-switcher {
  position: static !important; inset: auto !important; transform: none !important;
  margin: 0 !important; display: inline-flex !important;
}

/* Chat stream fades under the header and composer (like Newton). */
body[data-skin="agent"] .m33-chat-stream {
  padding-top: 40px;
  padding-bottom: 40px;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 40px, #000 calc(100% - 40px), transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 40px, #000 calc(100% - 40px), transparent 100%);
}

/* Team posture swaps panels (Relay → centre slot, Vega → right slot), so
 * the right chevron collapses the Vega side panel — it stays visible.
 * The very subtle right-rail resizer: a 5px grab strip on the panel's
 * left edge; invisible at rest, a hairline accent on hover/drag. */
.m33-right-resizer {
  position: fixed; top: 56px; bottom: 64px;
  right: var(--m33-rail-chev-right, 294px); width: 5px; margin-right: -2px;
  cursor: col-resize; z-index: 900; background: transparent;
}
.m33-right-resizer:hover, .m33-right-resizer.m33-dragging {
  background: linear-gradient(to right, transparent 1px, rgba(125,138,255,0.35) 2px, rgba(125,138,255,0.35) 3px, transparent 4px);
}
.m33-app.m33-right-collapsed ~ .m33-right-resizer, body[data-compact="1"] .m33-right-resizer { display: none; }

/* ── App fade-in ──────────────────────────────────────────────────────────
 * The shell eases in on load instead of popping — the closing half of the
 * login → boot → app transition ("when the agent is ready it fades back
 * in"). Cheap and universal: every page load gets the same 400ms ease,
 * which also softens ordinary navigations. */
@keyframes m33AppFadeIn { from { opacity: 0; } to { opacity: 1; } }
.m33-app { animation: m33AppFadeIn 400ms ease; }
@media (prefers-reduced-motion: reduce) { .m33-app { animation: none; } }

/* Icon-corner alerts on the Relay group (operator, 2026-07-21):
 * missed-chat count over the chat icon, and a pulsing amber dot over the
 * calendar icon when a live meeting expects the user. */
body[data-skin="agent"] .nw-relay-box .m33-mode-btn { position: relative; }
/* Corner badges OVERLAY the icon by ~25% (operator): anchored inside the
 * button so a quarter of the badge sits on the glyph's corner. */
body[data-skin="agent"] 
/* Calendar locked (no canCalendar grant): visible but inert — the click
 * handler blocks navigation, this just says so at a glance. */
.nw-relay-box .m33-mode-btn.m33-cal-locked { opacity: 0.35; cursor: not-allowed; }

/* Alerts bell badge — unacknowledged count, same corner-pill law as the
 * chat badge (red, ~25% overlay, 9+ cap done by the writer). */
.nw-relay-box .m33-alert-badge {
  position: absolute; top: -2px; right: -2px;
  min-width: 18px; height: 18px; box-sizing: border-box;
  padding: 0 4px; border-radius: 10px;
  background: #e2544a; color: #fff;
  border: 1.5px solid rgba(16,17,26,0.9);
  font-size: 11px; font-weight: 700; line-height: 15px; text-align: center;
}

/* ── Channel header grammar (operator, 2026-07-22) ──────────────────────
 * The global header's grammar: fully rounded pill PERIMETERS in the
 * subtle blue; labels normal-case inside; modes are monochrome stroke
 * icons (active white, idle muted); mute is the warm mic-off that only
 * exists during audio/video; ☰/⤢ are borderless ghosts.
 *
 * Keyed on .m33-chan-header, not on one placement. When the four-view
 * /channel page wrote its own header row these rules did not reach it,
 * and the pills came out as bare text — the same regression as the
 * missing Mode group, one layer down. */
.m33-chan-header .vega-header-group {
  /* EXACT global-header recipe (nw-research/relay-box): same border,
   * same faint outer ring, same card fill, same 8px soft-round that
   * reads pill-like at this height, same 34px. One visual language. */
  border: 1px solid var(--m33-group-border);
  box-shadow: 0 0 0 1px var(--m33-group-ring);
  background: var(--m33-card, #22253a);
  border-radius: 8px;
  height: 34px; min-height: 34px;
}
.m33-chan-header .vega-header-label {
  font-size: 12px; font-weight: 400; text-transform: none;
  letter-spacing: 0; color: var(--m33-text-muted, #9aa0b4); padding-right: 0;
}
.m33-chan-header .m33-modality-btn,
.m33-chan-header .m33-mute-btn {
  background: none; border: 0; padding: 2px; cursor: pointer; line-height: 0;
}
.m33-chan-header .m33-modality-btn svg { stroke: #8a8fa3; }
.m33-chan-header .m33-modality-btn.m33-active svg { stroke: #ffffff; }
.m33-chan-header .m33-mute-btn svg { stroke: #f0a29c; }
.m33-chan-header .m33-mute-btn[data-muted="1"] svg { stroke: #e2544a; }
.m33-chan-header .m33-hdr-ghost {
  background: none; border: 0; color: #8a8fa3; cursor: pointer;
  font-size: 14px; line-height: 1; padding: 4px 6px; opacity: 0.85;
}
.m33-chan-header .m33-hdr-ghost:hover { color: #d7d9e0; }

/* The active-channel chip inside the header pill goes NEUTRAL (operator,
 * 2026-07-22: "the blue highlight around the channel is not required") —
 * blue belongs to the group perimeter only. Soft white fill, no accent
 * border, matching the approved design's inner-content neutrality. */
.m33-chan-header .m33-tc-chan-active {
  background: rgba(255,255,255,0.07) !important;
  border-color: transparent !important;
}

/* ── Relay-in-the-centre gets Vega's reading column (operator, 2026-07-22)
 * Team posture puts the Relay panel in the centre slot; its timeline and
 * composer take the SAME centered --m33-read-col the Vega chat stream uses
 * (shell.css .m33-chat-stream recipe) so both centre experiences read
 * identically. Scoped to team posture — in the right rail the panel
 * stays edge-to-edge (a 294px rail needs no clamp). */
body[data-manifold="team"] #m33-panel-right .m33-tc-messages {
  padding-left: max(10px, calc((100% - var(--m33-read-col)) / 2)) !important;
  padding-right: max(10px, calc((100% - var(--m33-read-col)) / 2)) !important;
}
/* Pinned-messages panel takes the transcript's slot 1-for-1 (trunk.js
 * togglePinnedView swaps them), so it needs the SAME reading-column clamp —
 * otherwise pin rows stretch edge-to-edge while the transcript above/below
 * them stays centered on --m33-read-col. */
body[data-manifold="team"] #m33-panel-right .m33-tc-pinned-panel {
  padding-left: max(10px, calc((100% - var(--m33-read-col)) / 2)) !important;
  padding-right: max(10px, calc((100% - var(--m33-read-col)) / 2)) !important;
}
body[data-manifold="team"] #m33-panel-right .m33-tc-composer {
  /* The composer is an ABSOLUTE bottom bar with inline left:0;right:0;
   * margin:0 — margins can't centre it (inline wins) and shouldn't: the
   * blurred bar spans the panel. Centre its CONTENT instead. */
  align-items: center !important;
}
body[data-manifold="team"] #m33-panel-right .m33-tc-composer > * {
  width: 100%; max-width: var(--m33-read-col);
}
body[data-manifold="team"] #m33-panel-right .m33-tier1-header {
  /* The pill header follows the same reading column as the content
   * below it — Channel/Mode/Members align with the conversation
   * instead of clinging to the panel edges. */
  /* !important: the header ships inline padding:0 14px. */
  padding-left: max(14px, calc((100% - var(--m33-read-col)) / 2)) !important;
  padding-right: max(14px, calc((100% - var(--m33-read-col)) / 2)) !important;
}

/* EVERY footer pill wears the global blue recipe (operator,
 * 2026-07-22): LLMs, LQMS, Prompt, Team (Humans | Agents), Build —
 * the bottom of the app speaks the same language as the top. */
#m33-footer .vega-footer-group {
  border: 1px solid var(--m33-group-border);
  box-shadow: 0 0 0 1px var(--m33-group-ring);
  background: var(--m33-card, #22253a);
  border-radius: 8px;
}

/* Footer pills distribute EVENLY across the row (operator, 2026-07-22):
 * the three slot wrappers dissolve (display:contents) so every pill —
 * LLMs · LQMs · Prompt · Team · Build — is a direct flex item of the
 * footer's existing space-between row. */
#m33-footer .m33-footer-left,
#m33-footer .m33-footer-center,
#m33-footer .m33-footer-right { display: contents; }

/* ── Collapsed rail is ICONS ONLY (operator 2026-08-06) ─────────────────
 * One clean column of icons: no labels, no group headers, no tallies.
 * The framework already hides .m33-nav-label / .m33-nav-section /
 * .m33-nav-badge at 56px — but Vega's live badges carry their own
 * classes (ensureBadge: .m33-nav-badge-count / .m33-nav-badge-inbox)
 * and escaped that rule, leaving loose numbers jumbled across the
 * collapsed column. Hide them the same way. Hover still names the
 * item (each .m33-nav-item carries title="<label>"). */
.m33-left-collapsed .m33-nav-badge-count,
.m33-left-collapsed .m33-nav-badge-inbox { display: none !important; }

/* Collapsed icons keep their EXPANDED size (operator): the framework
 * bumps them to 24/18px at 56px width, which reads as a size jump and
 * breaks the visual line against the expanded state. Pin both states
 * to the same 16px box. */
.m33-left-collapsed .m33-nav-icon { width: 16px !important; height: 16px !important; }
.m33-left-collapsed .m33-nav-icon svg { width: 16px !important; height: 16px !important; }
.m33-left-collapsed .m33-nav-icon img { width: 16px !important; height: 16px !important; }
/* And they keep their POSITION: expanded geometry is panel 8px + item
 * 10px = icon left edge 18px. Collapsed panel padding is 4px, so a
 * 14px item pad reproduces the same 18px — toggling the rail leaves
 * every icon exactly in place (no center-jump). */
.m33-left-collapsed .m33-nav-item {
  justify-content: flex-start !important;
  padding: 8px 0 8px 14px !important;
}
