/* ============================================================
   MUPAS Dashboard — Editorial Premium Stylesheet
   ============================================================ */

/* ── Block 2.4 (audit D-P2, 2026-09-14): SELF-HOSTED FONTS.
   Was an @import of fonts.googleapis.com - a render-blocking cross-origin CSS
   fetch plus font fetches on every first visit, and the swap from the fallback
   face was most of the landing's layout shift (CLS 0.095 desktop).
   Two VARIABLE woff2 files in /assets/fonts/ (latin subset; the fallback stack
   covers the rest), each covering its whole weight range, preloaded from
   Server/app.py's index_string.  The *Fallback faces are metric overrides on
   system fonts so text set in them occupies the same lines as the real face:
   when the webfont arrives nothing moves. ── */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url('/assets/fonts/DMSans.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/PlayfairDisplay.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* metric-matched fallbacks (values from the fonts' own vertical metrics vs Arial / Georgia) */
@font-face {
  font-family: 'DM Sans Fallback';
  src: local('Arial'), local('Helvetica'), local('Liberation Sans');
  size-adjust: 100.6%;
  ascent-override: 96.5%;
  descent-override: 30.5%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Playfair Display Fallback';
  src: local('Georgia'), local('Times New Roman'), local('Liberation Serif');
  size-adjust: 104.5%;
  ascent-override: 103%;
  descent-override: 25%;
  line-gap-override: 0%;
}

/* ── CSS Variables ─────────────────────────────────── */
:root {
  --accent: #00e5c3;
  /* Block 2 contrast (2026-09-14): the accent is a FILL colour, tuned for dark. As TEXT on
     white it is 1.61:1. Anything that sets the accent as a text colour uses this token,
     which is the accent itself in dark and a darker teal (measured 5.7:1 on white) in light. */
  --accent-text: #00e5c3;
  --accent-dim: rgba(0, 229, 195, 0.15);
  --accent-glow: rgba(0, 229, 195, 0.35);

  --bg-base: #0b0d10;
  --bg-surface: #13161b;
  --bg-elevated: #1a1e25;
  --bg-panel: #1e2229;
  --bg-modal: #161a20;

  --border: rgba(255, 255, 255, 0.07);
  --border-accent: rgba(0, 229, 195, 0.3);

  --text-primary: #eef0f4;
  --text-secondary: #8a8f9a;
/*  --text-muted: #555a66;*/
  --text-muted: #bbbec5;

  --font-serif: 'Playfair Display', 'Playfair Display Fallback', Georgia, serif;
  --font-sans: 'DM Sans', 'DM Sans Fallback', system-ui, sans-serif;

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;

  --shadow-card: 0 4px 24px rgba(0, 0, 0, 0.4);
  --shadow-accent: 0 0 24px rgba(0, 229, 195, 0.12);

  --transition: 0.22s cubic-bezier(0.4, 0, 0.2, 1);
  --sidebar-w: 52px;
  --sidebar-expanded: 240px;
}

/* Light mode overrides */
[data-mantine-color-scheme="light"] {
  --accent-text: #00755f;   /* 5.7:1 on white, 5.2 on the page, 4.7 on a panel - measured, not the 4.6 estimate for #008f78 (4.0) */
  --bg-base: #f4f5f7;
  --bg-surface: #ffffff;
  --bg-elevated: #f0f2f5;
  --bg-panel: #e8eaee;
  --bg-modal: #ffffff;
  --border: rgba(0, 0, 0, 0.08);
  --text-primary: #111318;
  --text-secondary: #4a5060;
  --text-muted: #9a9faa;
  --shadow-card: 0 4px 24px rgba(0, 0, 0, 0.10);
}

/* ── Reset & Base ──────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  font-family: var(--font-sans);
  background: var(--bg-base);
  color: var(--text-primary);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Dash root fill */
#_dash-app-content,
._dash-loading,
.dash-spinner,
.dash-loading-callback-dag,
._dash-update-notification { overflow: hidden !important; }

/* ── App Shell ─────────────────────────────────────── */
.mupas-shell {
  display: flex;
  flex-direction: column;
  height: 100vh;
  width: 100vw;
  overflow: hidden;
  background: var(--bg-base);
}

/* ── AI Agent chat ──────────────────────────────────── */
.ai-conversation {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 220px;
  max-height: 48vh;
  overflow-y: auto;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
}
.ai-bubble {
  max-width: 82%;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-wrap: break-word;
}
.ai-bubble--user {
  align-self: flex-end;
  background: var(--accent-dim);
  border: 1px solid var(--border-accent);
  color: var(--text-primary);
}
.ai-bubble--agent {
  align-self: flex-start;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  color: var(--text-secondary);
}
.ai-bubble--thinking {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.ai-thinking-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-muted);
  display: inline-block;
  animation: ai-thinking-pulse 1.2s infinite ease-in-out;
}
.ai-thinking-dot:nth-child(2) { animation-delay: 0.2s; }
.ai-thinking-dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes ai-thinking-pulse {
  0%, 80%, 100% { opacity: 0.25; transform: scale(0.8); }
  40%           { opacity: 1;    transform: scale(1); }
}

/* ── Peer Support options ───────────────────────────── */
.peer-support-option {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 14px 12px;
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: background var(--transition);
}
.peer-support-option:hover {
  background: rgba(255, 255, 255, 0.05);
}
.peer-support-option-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  flex-shrink: 0;
}
.peer-support-option-label {
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 600;
  color: #4c6ef5;
}

/* ── Case cards ─────────────────────────────────────── */
.case-card {
  transition: border-color var(--transition), background var(--transition);
}
.case-card:hover {
  border-color: var(--border-accent) !important;
  background: var(--accent-dim) !important;
}

/* ── Chat Affix ─────────────────────────────────────── */
.chat-fab {
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.chat-fab:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45);
}
.chat-unread-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #fa5252;
  display: inline-block;
}

/* ── Footer ─────────────────────────────────────────── */
.mupas-footer {
  flex-shrink: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  padding: 8px 16px;
  border-top: 1px solid var(--border);
  background: var(--bg-base);
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-secondary);   /* 2.2c: muted on the page was 2.43:1 in light */
}

/* ── Header ─────────────────────────────────────────── */
.mupas-header {
  flex-shrink: 0;
  height: 20vh;
  min-height: 120px;
  max-height: 200px;
  display: flex;
  flex-direction: column;
  padding: 0 28px;
  gap: 0;
  background:
    linear-gradient(135deg, #060e1c 0%, #071415 35%, #07100a 65%, #0b1220 100%);
  border-bottom: 1px solid var(--border);
  position: relative;
  overflow: hidden;
  animation: headerReveal 0.55s ease forwards;
  z-index: 20;
}

.mupas-header::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 55% 120% at 12% 60%, rgba(0,229,195,0.10) 0%, transparent 65%),
    radial-gradient(ellipse 35% 110% at 88% 40%, rgba(0,130,255,0.07) 0%, transparent 60%),
    radial-gradient(ellipse 25% 80%  at 50% -10%, rgba(0,229,195,0.05) 0%, transparent 70%);
  pointer-events: none;
}

.mupas-header::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");
  opacity: 0.025;
  pointer-events: none;
}

/*  THE HEADER STAYS DARK IN LIGHT MODE - so everything inside it must keep
    its DARK-MODE colours too.

    That is the whole bug reported 2026-09-03: only the background was pinned
    dark here, while the text tokens flipped with the scheme. --text-secondary
    went from #8a8f9a (light grey) to #4a5060 (dark grey) on a #0e1e30 band -
    about 1.5:1, well under the 4.5:1 needed to read - so the nav looked
    disabled rather than dark.

    Re-declaring the tokens ON .mupas-header makes it a self-contained island:
    every descendant rule that says var(--text-secondary) now resolves against
    these values instead of the page's. That is why this is four lines rather
    than an override for each of the nav, the source tabs, the icons and
    whatever gets added to the header next. */
[data-mantine-color-scheme="light"] .mupas-header {
  background: linear-gradient(135deg, #0e1e30 0%, #0e2225 35%, #0c1d14 65%, #121d2e 100%);

  --text-primary:   #eef0f4;
  --text-secondary: #a8aebb;   /* lifted from the dark-mode #8a8f9a: this band
                                  is darker than the page it normally sits on,
                                  and 4.5:1 is the floor, not the target */
  --text-muted:     #bbbec5;
  --border:         rgba(255, 255, 255, 0.10);
}

/*  The nav items are dmc.Button(variant="subtle", color="gray"), and Mantine
    resolves its OWN gray palette from the colour scheme - so in light mode it
    paints a dark label regardless of what our tokens say. The island above
    cannot reach that; this does.

    Mantine renders the label in an inner element, so the colour has to be set
    on the label and section as well as the root, or the button text stays grey
    while only its icon changes. */
[data-mantine-color-scheme="light"] .mupas-header .nav-link,
[data-mantine-color-scheme="light"] .mupas-header .nav-link .mantine-Button-label,
[data-mantine-color-scheme="light"] .mupas-header .nav-link .mantine-Button-section,
[data-mantine-color-scheme="light"] .mupas-header .mantine-Button-root,
[data-mantine-color-scheme="light"] .mupas-header .mantine-ActionIcon-root {
  color: var(--text-secondary);
}

[data-mantine-color-scheme="light"] .mupas-header .nav-link:hover,
[data-mantine-color-scheme="light"] .mupas-header .nav-link:hover .mantine-Button-label,
[data-mantine-color-scheme="light"] .mupas-header .mantine-ActionIcon-root:hover {
  color: var(--text-primary);
}

/*  The active source tab and the LIVE pill are accent-coloured and unaffected -
    --accent does not flip - but the INACTIVE tabs use --text-secondary and were
    part of the same complaint. Covered by the island above. */

@keyframes headerReveal {
  from { opacity: 0; transform: translateY(-12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Header rows ───────────────────────────────────── */
.header-row {
  display: flex;
  align-items: center;
  gap: 20px;
  position: relative;
  z-index: 1;
}

.header-row--top {
  flex: 1;
  padding-top: 4px;
}

.header-row--bottom {
  flex-shrink: 0;
  height: 44px;
  padding-bottom: 6px;
}

.header-rule {
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(0,229,195,0.25) 20%,
    rgba(0,229,195,0.12) 60%,
    transparent 100%
  );
  margin: 0 -28px;
  flex-shrink: 0;
  position: relative;
  z-index: 1;
}

/* ── Logo ──────────────────────────────────────────── */
.header-logo {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
  text-decoration: none;
}

.logo-mark {
  display: flex;
  align-items: center;
  gap: 1px;
  background: linear-gradient(135deg, rgba(0,229,195,0.18) 0%, rgba(0,229,195,0.06) 100%);
  border: 1px solid rgba(0,229,195,0.28);
  border-radius: 10px;
  padding: 7px 10px 7px 9px;
  box-shadow: 0 0 20px rgba(0,229,195,0.10), inset 0 1px 0 rgba(255,255,255,0.06);
}

.logo-m {
  font-family: var(--font-serif);
  font-size: 26px;
  font-weight: 700;
  color: var(--accent);
  line-height: 1;
  letter-spacing: -0.03em;
  margin-right: 3px;
}

.logo-inner {
  display: flex;
  align-items: center;
  gap: 0;
  font-family: var(--font-serif);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  line-height: 1;
}

.logo-accent { color: var(--accent); }

.logo-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.logo-wordmark {
  font-family: var(--font-serif);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--text-primary);
  line-height: 1;
}

.logo-tm {
  font-size: 0.42em;
  vertical-align: super;
  letter-spacing: 0;
  margin-left: 1px;
}

.logo-tagline {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  text-transform: uppercase;
  white-space: nowrap;
}

/* ── Primary nav ───────────────────────────────────── */
.header-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
  justify-content: center;
}

.nav-link {
  font-family: var(--font-sans);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  text-decoration: none;
  padding: 7px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: all var(--transition);
  white-space: nowrap;
  position: relative;
}

.nav-link::after {
  content: '';
  position: absolute;
  bottom: 3px;
  left: 50%;
  transform: translateX(-50%) scaleX(0);
  width: 20px;
  height: 1.5px;
  background: var(--accent);
  border-radius: 2px;
  transition: transform var(--transition);
}

.nav-link:hover {
  color: var(--text-primary);
  background: rgba(255,255,255,0.04);
  border-color: var(--border);
}

.nav-link--active {
  color: var(--accent) !important;
  background: var(--accent-dim) !important;
  border-color: var(--border-accent) !important;
}

.nav-link--active::after {
  transform: translateX(-50%) scaleX(1);
}

/* ── Header action buttons ─────────────────────────── */
.header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.header-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.03em;
  text-decoration: none;
  padding: 8px 18px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: all var(--transition);
  cursor: pointer;
  white-space: nowrap;
}

.header-btn--ghost {
  color: var(--text-secondary);
  border-color: var(--border);
  background: rgba(255,255,255,0.03);
}

.header-avatar-btn {
  cursor: pointer;
  border: 2px solid var(--border);
  transition: border-color var(--transition);
  flex-shrink: 0;
}

.header-avatar-btn:hover {
  border-color: var(--border-accent);
}

.header-btn--ghost:hover {
  color: var(--text-primary);
  border-color: rgba(255,255,255,0.15);
  background: rgba(255,255,255,0.06);
  transform: translateY(-1px);
}

.header-btn--community {
  color: #000;
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 2px 12px rgba(0,229,195,0.20);
}

.header-btn--community:hover {
  background: #00ccad;
  border-color: #00ccad;
  box-shadow: 0 4px 20px rgba(0,229,195,0.35);
  transform: translateY(-1px);
}

/* ── Header divider (vertical) ─────────────────────── */
.header-divider {
  width: 1px;
  height: 22px;
  background: var(--border);
  flex-shrink: 0;
}

/* ── Tab controls ──────────────────────────────────── */
.header-tab-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
  flex-shrink: 0;
}

.tab-controls {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.tab-ctrl-btn {
  width: 30px;
  height: 30px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: rgba(255,255,255,0.03);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all var(--transition);
  flex-shrink: 0;
}

.tab-ctrl-btn:hover {
  color: var(--accent);
  border-color: var(--border-accent);
  background: var(--accent-dim);
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(0,229,195,0.12);
}

.tab-ctrl-btn.disabled {
  opacity: 0.35;
  pointer-events: none;
  cursor: default;
}

/* ── Live badge ─────────────────────────────────────── */
.live-badge {
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-sans);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-dim);
  border: 1px solid var(--border-accent);
  border-radius: 20px;
  padding: 4px 12px;
  flex-shrink: 0;
}

.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 6px var(--accent-glow);
  animation: livePulse 2s ease-in-out infinite;
}

@keyframes livePulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 6px var(--accent-glow); }
  50%       { opacity: 0.5; box-shadow: 0 0 12px var(--accent-glow); }
}

/* ── Device Tabs ───────────────────────────────────── */
/* The tab list is wrapped in a Mantine ScrollArea; the real scroller is
   .mantine-ScrollArea-viewport.  min-width:0 lets this flex item shrink below
   its content width so the ScrollArea actually overflows (and shows a bar).
   overflow must stay visible here so the ScrollArea's scrollbar is not clipped. */
.device-tabs-wrap {
  flex: 1;
  min-width: 0;
  position: relative;
  z-index: 1;
}

.device-tabs-wrap .mantine-Tabs-root { height: 100%; }

/* Make the Mantine ScrollArea scrollbar for the tab strip clearly visible. */
.device-tabs-wrap .mantine-ScrollArea-scrollbar {
  background: rgba(255, 255, 255, 0.06) !important;
  border-radius: 4px !important;
}
.device-tabs-wrap .mantine-ScrollArea-scrollbar[data-orientation="horizontal"] {
  height: 10px !important;
}
.device-tabs-wrap .mantine-ScrollArea-thumb {
  background: rgba(128, 128, 128, 0.85) !important;
  border-radius: 4px !important;
}
.device-tabs-wrap .mantine-ScrollArea-thumb:hover {
  background: var(--accent) !important;
}

.device-tabs-wrap .mantine-Tabs-list {
  border-bottom: none !important;
  gap: 4px;
  flex-wrap: nowrap;
}

.device-tabs-wrap .mantine-Tabs-tab {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 5px 14px;
  transition: all var(--transition);
  white-space: nowrap;
}

.device-tabs-wrap .mantine-Tabs-tab:hover {
  color: var(--text-primary);
  background: rgba(255,255,255,0.05);
  border-color: var(--border);
}

.device-tabs-wrap .mantine-Tabs-tab[data-active] {
  color: var(--accent) !important;
  background: var(--accent-dim) !important;
  border-color: var(--border-accent) !important;
}

/* ── Theme Toggle ──────────────────────────────────── */
.theme-toggle-btn {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: rgba(255,255,255,0.04);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all var(--transition);
  color: var(--text-secondary);
  position: relative;
  z-index: 1;
}

.theme-toggle-btn:hover {
  color: var(--accent);
  border-color: var(--border-accent);
  background: var(--accent-dim);
}

/* ── Main Content Area ─────────────────────────────── */
.mupas-body {
  flex: 1;
  display: flex;
  overflow: hidden;
  position: relative;
}

/* ── Sidebar ───────────────────────────────────────── */
.mupas-sidebar {
  flex-shrink: 0;
  width: var(--sidebar-w);
  background: var(--bg-surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 12px 0;
  gap: 8px;
  transition: width var(--transition);
  overflow: hidden;
  position: relative;
  z-index: 10;
}

.mupas-sidebar.expanded {
  width: var(--sidebar-expanded);
  align-items: flex-start;
}

.sidebar-toggle {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--text-secondary);
  transition: all var(--transition);
  flex-shrink: 0;
  margin: 0 8px;
}

.sidebar-toggle:hover {
  color: var(--accent);
  background: var(--accent-dim);
}

.sidebar-icon-btn {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--text-secondary);
  transition: all var(--transition);
  flex-shrink: 0;
  position: relative;
  margin: 0 8px;
}

.sidebar-icon-btn:hover {
  color: var(--text-primary);
  background: rgba(255,255,255,0.05);
  border-color: var(--border);
}

.sidebar-icon-btn.active {
  color: var(--accent);
  background: var(--accent-dim);
  border-color: var(--border-accent);
}

.sidebar-label {
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  opacity: 0;
  transition: opacity var(--transition);
  pointer-events: none;
  margin-left: 10px;
}

.mupas-sidebar.expanded .sidebar-label {
  opacity: 1;
  pointer-events: auto;
}

.sidebar-item-row {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 0;
}

.sidebar-item-clickable {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  width: 100%;
  cursor: pointer;
}

.mupas-sidebar.expanded .sidebar-item-clickable {
  justify-content: flex-start;
}

.sidebar-item-clickable .sidebar-icon-btn,
.sidebar-item-clickable .sidebar-toggle {
  flex-shrink: 0;
}

.sidebar-item-clickable .sidebar-label {
  white-space: nowrap;
}

.sidebar-sep {
  width: calc(100% - 16px);
  height: 1px;
  background: var(--border);
  margin: 4px 8px;
}

/* ── Main Content ──────────────────────────────────── */
.mupas-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 12px;
  gap: 10px;
  background: var(--bg-base);
  animation: contentReveal 0.5s 0.1s ease both;
}

@keyframes contentReveal {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Controls Bar ──────────────────────────────────── */
.controls-bar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  flex-wrap: wrap;
}

.controls-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary);   /* 2.2c: muted on white was 2.65:1 */
}

.controls-group {
  display: flex;
  align-items: center;
  gap: 8px;
}

.time-nav-btn {
  width: 30px;
  height: 30px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all var(--transition);
  flex-shrink: 0;
}

.time-nav-btn:hover {
  color: var(--accent);
  border-color: var(--border-accent);
  background: var(--accent-dim);
}

/* ── Chart Panels ──────────────────────────────────── */
.panels-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow: hidden;
}

.chart-panel {
  flex: 1;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  overflow: hidden;
  transition: border-color var(--transition), box-shadow var(--transition);
  position: relative;
}

.chart-panel:hover {
  border-color: rgba(255,255,255,0.12);
}

.chart-panel.expanded-panel {
  flex: 1;
  border-color: var(--border-accent);
  box-shadow: var(--shadow-accent);
}

.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}

.panel-title {
  font-family: var(--font-serif);
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: 8px;
}

.panel-badge {
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-text);   /* 2.2c: the accent AS TEXT on its own tint was 1.46:1 in light */
  background: var(--accent-dim);
  border: 1px solid var(--border-accent);
  padding: 2px 8px;
  border-radius: 20px;
}

.panel-actions {
  display: flex;
  align-items: center;
  gap: 6px;
}

.panel-action-btn {
  width: 26px;
  height: 26px;
  border-radius: 5px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all var(--transition);
  flex-shrink: 0;
}

.panel-action-btn:hover {
  color: var(--accent);
  border-color: var(--border-accent);
  background: var(--accent-dim);
}

.sensor-select-trigger {
  width: 160px;
  height: 26px;
  border-radius: 5px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 0 8px;
  cursor: pointer;
  transition: all var(--transition);
  flex-shrink: 0;
  font-family: var(--font-sans);
  font-size: 12px;
}

.sensor-select-trigger:hover {
  color: var(--accent);
  border-color: var(--border-accent);
  background: var(--accent-dim);
}

.sensor-select-trigger-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chart-wrap {
  flex: 1;
  position: relative;
  overflow: hidden;
  /* ensures the Plotly SVG fills the panel */
  min-height: 0;
}

/* ── 5.2: the instrument strip under the chart ─────────────────────────────
   Three measured facts and, on an analog slot, the channel map's caveat.
   Quiet by design: it must read as a caption, never as a status or an
   alert - nothing here is a threshold and nothing is "detected". */
.panel-strip {
  flex-shrink: 0;
  display: flex;
  flex-wrap: nowrap;            /* ONE line on desktop: the chart keeps its height */
  align-items: baseline;
  gap: 0 14px;
  padding-top: 2px;
  font-family: var(--font-sans);
  font-size: 11px;
  line-height: 1.3;
  color: var(--text-muted);
  min-width: 0;
  overflow: hidden;
}
.panel-strip-item { white-space: nowrap; flex: 0 0 auto; }
.panel-strip-item--raw, .panel-strip-item--unwired { color: var(--text-secondary); }
.panel-strip-item--caveat { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-style: italic; }
.panel-strip-item--caveat::before { content: "Note: "; font-style: normal; }
@media (max-width: 600px) {
  .panel-strip { flex-wrap: wrap; gap: 1px 10px; font-size: 10.5px; }
  .panel-strip-item--caveat { flex-basis: 100%; }
}

/* ════════════════════════════════════════════════════
   PLOTLY / dcc.Graph overrides
   ════════════════════════════════════════════════════ */

/* Make the graph container fill the chart-wrap */
.mupas-graph,
.mupas-graph .js-plotly-plot,
.mupas-graph .plot-container,
.mupas-graph .svg-container {
  width: 100% !important;
  height: 100% !important;
}

/* The outermost Dash graph div */
.mupas-graph {
  width: 100%;
  height: 100%;
}

/* ── Mode bar ─────────────────────────────────────── */
.mupas-graph .modebar {
  background: transparent !important;
  border: none !important;
  padding: 2px 4px !important;
  /* push it to the top-right, clear of legend */
  top: 2px !important;
  right: 4px !important;
}

.mupas-graph .modebar-btn {
  color: var(--text-muted) !important;
  opacity: 0.7;
  transition: all var(--transition) !important;
}

.mupas-graph .modebar-btn:hover,
.mupas-graph .modebar-btn.active {
  color: var(--accent) !important;
  opacity: 1 !important;
  background: var(--accent-dim) !important;
  border-radius: var(--radius-sm) !important;
}

/* Mode bar icon fill/stroke */
.mupas-graph .modebar-btn path {
  fill: currentColor !important;
}

/* ── Plotly tooltip ───────────────────────────────── */
.mupas-graph .hoverlayer .hovertext rect {
  fill: var(--bg-elevated) !important;
  stroke: var(--border-accent) !important;
  rx: 6 !important;
}

.mupas-graph .hoverlayer .hovertext text {
  fill: var(--text-primary) !important;
  font-family: var(--font-sans) !important;
  font-size: 12px !important;
}

/* Unified hover vertical line */
.mupas-graph .hoverlayer .zeroline {
  stroke: var(--border-accent) !important;
  stroke-width: 1 !important;
}

/* ── Plotly legend ────────────────────────────────── */
.mupas-graph .legend .bg {
  fill: var(--bg-surface) !important;
  stroke: var(--border) !important;
  rx: 6 !important;
}

.mupas-graph .legend text {
  fill: var(--text-secondary) !important;
  font-family: var(--font-sans) !important;
  font-size: 11px !important;
}

/* ── Axis labels & ticks ──────────────────────────── */
.mupas-graph .xtick text,
.mupas-graph .ytick text,
.mupas-graph .g-xtitle text,
.mupas-graph .g-ytitle text {
  fill: var(--text-muted) !important;
  font-family: var(--font-sans) !important;
  font-size: 10px !important;
}

/* ── Grid lines ───────────────────────────────────── */
.mupas-graph .gridlayer .ygrid {
  stroke: rgba(255,255,255,0.06) !important;
}

[data-mantine-color-scheme="light"] .mupas-graph .gridlayer .ygrid {
  stroke: rgba(0,0,0,0.07) !important;
}

/* ── Zoom / selection box ─────────────────────────── */
.mupas-graph .select-outline {
  stroke: var(--accent) !important;
  stroke-width: 1px !important;
  fill: var(--accent-dim) !important;
}

/* ── Range slider (if ever used) ─────────────────── */
.mupas-graph .rangeslider-bg {
  fill: var(--bg-elevated) !important;
}

.mupas-graph .rangeslider-slidebox {
  fill: var(--accent-dim) !important;
  stroke: var(--border-accent) !important;
}

/* ── Stagger animations for panels ───────────────── */
.chart-panel:nth-child(1) { animation: panelReveal 0.4s 0.15s ease both; }
.chart-panel:nth-child(2) { animation: panelReveal 0.4s 0.25s ease both; }
.chart-panel:nth-child(3) { animation: panelReveal 0.4s 0.35s ease both; }

@keyframes panelReveal {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Mantine overrides ─────────────────────────────── */

.mantine-Select-input,
.mantine-DatePickerInput-input,
.mantine-TimePicker-input,
.mantine-TimePicker-control,
.mantine-DateTimePicker-input {
  background: var(--bg-elevated) !important;
  border-color: var(--border) !important;
  color: var(--text-primary) !important;
  font-family: var(--font-sans) !important;
  font-size: 13px !important;
  border-radius: var(--radius-sm) !important;
  height: 32px !important;
  min-height: 32px !important;
  padding: 0 10px !important;
  transition: border-color var(--transition) !important;
}

.mantine-Select-input:focus,
.mantine-DatePickerInput-input:focus {
  border-color: var(--border-accent) !important;
}

.mantine-Select-dropdown {
  background: var(--bg-modal) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius-md) !important;
  box-shadow: var(--shadow-card) !important;
}

.mantine-Select-option {
  font-family: var(--font-sans) !important;
  font-size: 13px !important;
  color: var(--text-secondary) !important;
  border-radius: var(--radius-sm) !important;
  transition: all var(--transition) !important;
}

.mantine-Select-option:hover,
.mantine-Select-option[data-selected] {
  background: var(--accent-dim) !important;
  color: var(--accent) !important;
}

/* dcc.Dropdown (react-select) used in the data-feed panels — Satellite Category
   and Earthquake Time Period.  Default react-select is white with light text,
   which is unreadable on the dark panels; theme it to match. */
.dropdown-selection .Select-control,
.dropdown-selection .Select-menu-outer,
.dropdown-selection .Select-menu {
  background-color: #0d1117 !important;
  border-color: #30363d !important;
  color: #e6edf3 !important;
}
.dropdown-selection .Select-control:hover {
  border-color: #58a6ff !important;
}
.dropdown-selection .Select-value-label,
.dropdown-selection .Select-value,
.dropdown-selection .Select-input > input,
.dropdown-selection .Select--single > .Select-control .Select-value {
  color: #e6edf3 !important;
}
.dropdown-selection .Select-placeholder {
  color: #8b949e !important;
}
.dropdown-selection .Select-menu-outer {
  border: 1px solid #30363d !important;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5) !important;
}
.dropdown-selection .Select-option,
.dropdown-selection .VirtualizedSelectOption {
  background-color: #0d1117 !important;
  color: #e6edf3 !important;
}
.dropdown-selection .Select-option.is-focused,
.dropdown-selection .Select-option:hover,
.dropdown-selection .VirtualizedSelectFocusedOption {
  background-color: #1a2330 !important;
  color: #58a6ff !important;
}
.dropdown-selection .Select-option.is-selected {
  background-color: #161b22 !important;
  color: #58a6ff !important;
}
.dropdown-selection .Select-arrow {
  border-color: #8b949e transparent transparent !important;
}
.dropdown-selection.is-open .Select-arrow {
  border-color: transparent transparent #8b949e !important;
}

/* Modal
   DEFAULT for every modal: cap the height and make the BODY the only scroll
   region, so the header (title + help "?" + close) stays pinned and is never
   covered when tall content scrolls. Implemented here (rather than per modal)
   because BaseModal subclasses each build their own dmc.Modal. These are
   deliberately NOT !important, so any modal that sets its own `styles=` (Cases,
   DeviceSettings, PortalPanelSettings, …) overrides them via inline styles. */
.mantine-Modal-content {
  background: var(--bg-modal) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius-lg) !important;
  box-shadow: var(--shadow-card) !important;
  display: flex;
  flex-direction: column;
  max-height: 90vh;
}

.mantine-Modal-header {
  /* Solid (was transparent) so scrolled body content can never show through the
     pinned header. */
  background: var(--bg-modal) !important;
  border-bottom: 1px solid var(--border) !important;
  padding: 16px 20px !important;
  flex-shrink: 0;
}

.mantine-Modal-title {
  font-family: var(--font-serif) !important;
  font-size: 18px !important;
  font-weight: 600 !important;
  color: var(--text-primary) !important;
}

.mantine-Modal-close {
  color: var(--text-secondary) !important;
  transition: color var(--transition) !important;
}

.mantine-Modal-close:hover { color: var(--accent) !important; }

.mantine-Modal-body {
  padding: 20px !important;
  font-family: var(--font-sans) !important;
  color: var(--text-secondary) !important;
  font-size: 14px !important;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.mantine-Textarea-input,
.mantine-TextInput-input {
  background: var(--bg-elevated) !important;
  border-color: var(--border) !important;
  color: var(--text-primary) !important;
  font-family: var(--font-sans) !important;
  border-radius: var(--radius-sm) !important;
}

.mantine-Button-root {
  font-family: var(--font-sans) !important;
  font-weight: 500 !important;
  letter-spacing: 0.02em !important;
  border-radius: var(--radius-sm) !important;
  transition: all var(--transition) !important;
}

.mantine-Button-root[data-variant="filled"] {
  background: var(--accent) !important;
  color: #000 !important;
}

.mantine-Button-root[data-variant="filled"]:hover {
  background: #00ccad !important;
  box-shadow: 0 4px 16px var(--accent-glow) !important;
  transform: translateY(-1px);
}

.mantine-SegmentedControl-root {
  background: var(--bg-elevated) !important;
  border: 1px solid var(--border) !important;
  border-radius: var(--radius-sm) !important;
}

.mantine-SegmentedControl-label {
  font-family: var(--font-sans) !important;
  font-size: 12px !important;
  color: var(--text-secondary) !important;
}

.mantine-SegmentedControl-indicator {
  background: var(--accent-dim) !important;
  border: 1px solid var(--border-accent) !important;
}

.mantine-SegmentedControl-label[data-active] {
  color: var(--accent-text) !important;   /* 2.2c: 1.32:1 in light on the indicator tint */
}

.mantine-Tooltip-tooltip {
  font-family: var(--font-sans) !important;
  font-size: 12px !important;
  background: var(--bg-elevated) !important;
  border: 1px solid var(--border) !important;
  color: var(--text-primary) !important;
  border-radius: var(--radius-sm) !important;
}

/* Annotation items */
.anno-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  cursor: pointer;
  transition: all var(--transition);
  margin-bottom: 8px;
}

.anno-item:hover {
  border-color: var(--border-accent);
  background: var(--accent-dim);
}

.anno-dot-sm {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
  margin-top: 5px;
}

.anno-text { flex: 1; }
.anno-title {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  margin-bottom: 2px;
}
.anno-time {
  font-size: 11px;
  color: var(--text-muted);
}

/* Chat */
.chat-input-area {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

/* Scrollbar */
::-webkit-scrollbar { width: 4px; height: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }

/* DateTimePicker */
.mantine-DateTimePicker-wrapper,
.mantine-DatePickerInput-wrapper { min-width: 160px; }

/* Flex spacer */
.flex-spacer { flex: 1; }

/* Time-range summary in the controls bar
   ("Jul 22 15:16 → 16:16 EDT · 1 hour · mupasm-0522-43").
   The bar is flex-wrap, so a long label used to push itself onto a second row.
   Let it shrink and ellipsize instead, and keep the digits from jittering as the
   window scrubs. */
.time-range-display {
  color: var(--text-secondary) !important;   /* 2.2c: Mantine's dimmed (#868e96) is 3.3:1 on white */
  flex: 0 1 auto;
  min-width: 0;
  max-width: 44ch;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 1200px) {
  /* Tight bars: give the summary less room before the help button. */
  .time-range-display { max-width: 30ch; }
}

/* Annotation overlay */
.anno-tooltip-box {
  position: absolute;
  background: var(--bg-elevated);
  border: 1px solid var(--border-accent);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  font-size: 12px;
  color: var(--text-primary);
  box-shadow: var(--shadow-card);
  z-index: 20;
  min-width: 180px;
  pointer-events: none;
}

.anno-tooltip-title {
  font-family: var(--font-serif);
  font-size: 14px;
  font-weight: 600;
  color: var(--accent);
  margin-bottom: 4px;
}

.help_button {
    position: fixed;
    top: 15px;
    right: 50px;
    z-index: 20000;
}

/* Help icon shown in the upper-right of data-source panels (device data,
   earthquake tracker, ADS-B, satellite tracker).  Mirrors the BaseModal help
   affordance but flows within the panel header instead of being viewport-fixed. */
.panel-help-button {
    color: #8b949e;
    flex-shrink: 0;
}
.panel-help-button:hover {
    color: #58a6ff;
}

/* ── Observation annotation canvas ─────────────────── */
.annotation-canvas-wrap {
  display: flex;
}

.annotation-canvas-wrap .js-plotly-plot,
.annotation-canvas-wrap .plot-container {
  width: 100% !important;
  height: 100% !important;
}

/* Hover-reveal side panel for annotation tools (color / width). Collapsed to
   a slim icon rail; widens on hover to expose the full controls. */
.annotation-tools-rail {
  width: 56px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  flex-shrink: 0;
  transition: width var(--transition);
}

.annotation-tools-rail:hover,
.annotation-tools-rail:focus-within {
  width: 260px;
}

.annotation-tools-rail-inner {
  display: flex;
  width: 260px;
  height: 100%;
  box-sizing: border-box;
}

.annotation-tools-icon-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 56px;
  flex-shrink: 0;
  padding-top: 14px;
  color: var(--text-muted);
}

.annotation-tools-full {
  flex: 1;
  min-width: 0;
  padding: 12px 14px 14px 4px;
  box-sizing: border-box;
}

.iframe_headline {
  transform: scale(0.85); /* Scale down to 66% */
  transform-origin: top left; /* Anchor the scale */  
}


/* ── Store call-to-action links (portal → /store) ─────────────────────────── */
.store-cta-link { display: inline-flex; align-items: center; gap: 6px; font-size: 13px;
  font-weight: 600; color: var(--accent); text-decoration: none; padding: 5px 12px;
  border-radius: var(--radius-sm); border: 1px solid var(--border-accent);
  background: var(--accent-dim); white-space: nowrap; transition: all var(--transition); }
.store-cta-link:hover { background: var(--accent); color: #04211d; border-color: var(--accent); }
.store-note-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 12px; border: 1px dashed var(--border-accent); border-radius: var(--radius-md);
  background: var(--accent-dim); }
.store-note-text { font-size: 13px; color: var(--text-secondary); flex: 1; min-width: 160px; }

/* ── Experience check-in marquee banner (under the portal header) ─────────── */
.xp-banner {
  display: flex; align-items: stretch; gap: 8px;
  height: 40px; flex-shrink: 0;
  padding: 0 20px 0 28px;   /* full shell width; left pad aligns with header content */
  background: linear-gradient(90deg, rgba(0,229,195,0.14), rgba(0,229,195,0.03) 45%, rgba(0,229,195,0.11));
  border-top: 1px solid rgba(0,229,195,0.18);
  border-bottom: 1px solid rgba(0,229,195,0.18);
  position: relative; overflow: hidden;
  animation: xpBannerReveal 0.5s ease both;
}
.xp-banner--hidden { display: none; }
@keyframes xpBannerReveal {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.xp-banner-main {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; cursor: pointer;
}
.xp-banner-icon {
  flex-shrink: 0; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; background: var(--accent); color: var(--bg-base);
  box-shadow: 0 0 0 4px rgba(0,229,195,0.15);
  animation: xpIconPulse 2.2s ease-in-out infinite;
}
@keyframes xpIconPulse {
  0%,100% { box-shadow: 0 0 0 4px rgba(0,229,195,0.15); }
  50%     { box-shadow: 0 0 0 7px rgba(0,229,195,0.04); }
}
.xp-banner-badge {
  flex-shrink: 0; font-family: var(--font-sans);
  font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--bg-base); background: var(--accent);
  padding: 2px 7px; border-radius: 999px;
}
.xp-banner-viewport {
  flex: 1; min-width: 0; overflow: hidden; display: flex; align-items: center;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}
.xp-banner-track {
  display: inline-flex; white-space: nowrap; will-change: transform;
  animation: xpMarquee 28s linear infinite;
}
.xp-banner-track > span {
  font-family: var(--font-sans); font-size: 13px; color: var(--text-primary); padding-right: 8px;
}
.xp-banner:hover .xp-banner-track { animation-play-state: paused; }
@keyframes xpMarquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.xp-banner-cta {
  flex-shrink: 0; display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-sans); font-size: 12px; font-weight: 600; color: var(--accent);
  padding: 5px 12px; border: 1px solid var(--border-accent); border-radius: 999px;
  background: rgba(0,229,195,0.08); transition: background .15s ease, color .15s ease, transform .15s ease;
}
.xp-banner-main:hover .xp-banner-cta {
  background: var(--accent); color: var(--bg-base); transform: translateX(2px);
}
.xp-banner-dismiss {
  flex-shrink: 0; align-self: center; display: grid; place-items: center;
  width: 24px; height: 24px; border: none; background: transparent;
  color: var(--text-muted); cursor: pointer; border-radius: 6px;
}
.xp-banner-dismiss:hover { color: var(--text-primary); background: rgba(255,255,255,0.06); }
@media (prefers-reduced-motion: reduce) {
  .xp-banner-track, .xp-banner-icon, .xp-banner { animation: none; }
}

/* ── MUPAS Guide chat modal (placeholder) ────────────────────────────────── */
.xp-chat-titledot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 3px rgba(0,229,195,0.2);
}
.xp-chat-intro {
  display: flex; align-items: center; gap: 6px;
  font-family: var(--font-sans); font-size: 12px; color: var(--text-muted);
}
.xp-chat-messages {
  display: flex; flex-direction: column; gap: 10px;
  min-height: 220px; max-height: 46vh; overflow-y: auto; padding: 6px 2px;
}
.xp-chat-row { display: flex; gap: 8px; align-items: flex-end; }
.xp-chat-row--bot  { justify-content: flex-start; }
.xp-chat-row--user { justify-content: flex-end; }
.xp-chat-avatar {
  flex-shrink: 0; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; background: var(--accent); color: var(--bg-base);
}
.xp-chat-bubble {
  max-width: 78%; font-family: var(--font-sans); font-size: 13.5px; line-height: 1.5;
  padding: 9px 13px; border-radius: 14px;
}
.xp-chat-row--bot .xp-chat-bubble {
  background: var(--bg-elevated); color: var(--text-primary);
  border: 1px solid var(--border); border-bottom-left-radius: 4px;
}
.xp-chat-row--user .xp-chat-bubble {
  background: var(--accent); color: var(--bg-base); border-bottom-right-radius: 4px;
}
.xp-chat-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.xp-chat-composer { display: flex; gap: 8px; align-items: center; }
.xp-chat-input { flex: 1; }
.xp-chat-send { background: var(--accent) !important; color: var(--bg-base) !important; }

/* ── Learn modal ─────────────────────────────────────────────────────────── */
.learn-header-note {
  display: flex; align-items: center; gap: 6px;
  color: var(--text-muted); font-family: var(--font-sans); font-size: 12px;
}
.learn-intro {
  color: var(--text-secondary); font-family: var(--font-sans);
  font-size: 13px; line-height: 1.5;
}
.learn-list { display: flex; flex-direction: column; gap: 8px; }
.learn-item {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 14px; border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--bg-elevated);
  text-decoration: none;
  transition: border-color .15s ease, transform .15s ease, background .15s ease;
}
.learn-item:hover {
  border-color: var(--border-accent); background: var(--bg-panel);
  transform: translateY(-1px);
}
.learn-item-icon {
  flex-shrink: 0; width: 38px; height: 38px; border-radius: 8px;
  display: grid; place-items: center;
}
.learn-item-icon--pdf  { background: rgba(255,90,90,0.12); color: #ff7a7a; }
.learn-item-icon--link { background: var(--accent-dim); color: var(--accent); }
.learn-item-body { flex: 1; min-width: 0; }
.learn-item-title {
  font-family: var(--font-sans); font-size: 14px; font-weight: 600;
  color: var(--text-primary); line-height: 1.35;
}
.learn-item-source {
  font-family: var(--font-sans); font-size: 11px; font-weight: 600;
  color: var(--accent); margin-top: 1px;
  text-transform: uppercase; letter-spacing: 0.04em;
}
.learn-item-desc {
  font-family: var(--font-sans); font-size: 12.5px;
  color: var(--text-secondary); line-height: 1.5; margin-top: 4px;
}
.learn-item-open { flex-shrink: 0; color: var(--text-muted); align-self: center; }
.learn-item:hover .learn-item-open { color: var(--accent); }
.learn-item--doc { cursor: pointer; }
.learn-tabs-list { margin-bottom: 4px; }
.learn-faq-answer {
  font-family: var(--font-sans); font-size: 13px;
  color: var(--text-secondary); line-height: 1.55;
}

/* In-app PDF reader (Documents tab). The iframe embeds the PDF with the
   browser toolbar hidden (#toolbar=0) so there's no download/print button. */
.learn-viewer-frame-wrap {
  width: 100%; height: 82vh;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  overflow: hidden; background: #2b2b2b;
}
.learn-viewer-frame { width: 100%; height: 100%; border: 0; display: block; }

/* ── Peer Support room (MupasPeerRoomModal) ─────────────────────────────────
   The transcript is rendered CLIENTSIDE from the poll, so these classes are
   applied by JS in MupasPeerRoomModal._RENDER_JS, not by any Python that
   builds a component. Renaming one here needs the JS renamed with it. */

/* The room is two regions: a conversation that scrolls and a dock that
   never moves.  The modal body is a fixed-height flex column (set inline in
   MupasPeerRoomModal, overriding the global "body is the scroll region"
   rule), so the shell fills it and the scroll region is the only part that
   grows.  Without this a dozen messages pushed Post, "Take the floor" and
   "Close the room" off the bottom of the screen. */
.peer-room-shell {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1 1 auto;
  /* min-height:0 is what actually permits the child to shrink and scroll -
     a flex item defaults to min-height:auto and refuses to go below its
     content, which silently defeats the overflow below. */
  min-height: 0;
}

.peer-room-scroll {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.peer-room-dock {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 0 0 auto;          /* never shrinks, never scrolls away */
  padding-top: 10px;
  border-top: 1px solid var(--border);
  background: var(--bg-modal);
}

.peer-transcript {
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* Grows to fill the scroll region rather than carrying its own viewport
     height: the dock is measured first, so the transcript gets exactly what
     is left over on any screen. */
  flex: 1 1 auto;

  /* BOTH of these, and the pair is the whole point.

     `min-height: 0` lets a flex item shrink below its content - the default
     is `auto`, which refuses to, and that is what makes the box taller than
     the space it was given.  `overflow-y: auto` then scrolls the surplus
     INSIDE the box.

     Without them the transcript computed shorter than its content and, with
     nothing telling it to clip, the messages painted straight over the
     "Held for your review" panel below (seen 2026-08-31).  It was written
     with a max-height and its own overflow originally; the flex rewrite kept
     the growing and dropped the clipping. */
  min-height: 0;
  overflow-y: auto;

  padding: 10px;
  background: var(--bg-elevated);
  border-radius: 10px;
}

.peer-post {
  padding: 8px 11px;
  border-radius: 10px;
  background: var(--bg-surface, #232830);
  border-left: 3px solid transparent;
  max-width: 82%;
  align-self: flex-start;
}

.peer-post-from {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary, #9aa4b2);
  margin-bottom: 3px;
}

.peer-post-text {
  font-size: 14px;
  color: var(--text-primary);
  /* Member text, already redacted. Wrap it hard: a pasted URL or a long
     unbroken run must not widen the room for everybody else. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.peer-post-mine {
  align-self: flex-end;
  background: var(--accent-dim);
}

.peer-post-specialist {
  border-left-color: var(--accent);
  align-self: flex-start;
  max-width: 92%;
}

/* A risk flag is surfaced, never softened and never hidden — the classifier is
   explicitly told not to remove distressing content, and the styling must not
   quietly undo that by making it recede. */
.peer-post-risk {
  border-left-color: #ff8080;
  background: rgba(255, 128, 128, 0.10);
}

/* Held posts are visible ONLY to the specialist (transcript() filters them for
   everyone else, including their author). Dashed, so a specialist reading the
   room can see at a glance that this one is not in it. */
.peer-post-held {
  border-left-color: #f6ad55;
  border: 1px dashed rgba(246, 173, 85, 0.55);
  opacity: 0.9;
}

.peer-post-empty {
  color: var(--text-secondary, #9aa4b2);
  font-size: 13px;
  font-style: italic;
  align-self: center;
  padding: 14px 0;
}

.peer-banner {
  font-size: 12px;
  color: var(--accent2, #f6ad55);
  min-height: 16px;
}

/* ── MupasAI: the wait, and the answer ───────────────────
   A MupasAI answer is ~19s cold.  These rules exist so that wait reads as
   PROGRESS rather than as a hang - a member who thinks it has hung presses
   the button again, and a second question is a second 10-credit charge.
   Every colour here is a token, so the panel follows the colour scheme; the
   .ai-bubble rules above predate that and still carry a dark-mode literal. */
.ai-wait-notice {
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-style: italic;
  padding: 2px 4px 6px;
}
.ai-progress-panel {
  align-self: flex-start;
  max-width: 82%;
  min-width: 260px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  font-family: var(--font-sans);
}
/* The bar is capped at 92% in Python - see progress_panel().  A bar that
   fills and then waits is the classic progress lie. */
.ai-progress-track {
  height: 3px;
  border-radius: 2px;
  background: var(--border);
  overflow: hidden;
  margin-bottom: 10px;
}
.ai-progress-fill {
  height: 100%;
  background: var(--accent);
  border-radius: 2px;
  transition: width 420ms ease-out;
}
.ai-stage-list {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.ai-stage-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  line-height: 1.35;
}
.ai-stage-row--done    .ai-stage-label { color: var(--text-secondary); }
.ai-stage-row--active  .ai-stage-label { color: var(--text-primary); font-weight: 600; }
/* Skipped: dimmed and NOT ticked.  A tick would claim work we never saw. */
.ai-stage-row--skipped .ai-stage-label { color: var(--text-muted); opacity: 0.5; }
.ai-stage-row--pending .ai-stage-label { color: var(--text-muted); opacity: 0.6; }

.ai-stage-tick { color: var(--accent); font-size: 12px; width: 12px; text-align: center; }
.ai-stage-skip { color: var(--text-muted); font-size: 12px; width: 12px; text-align: center; opacity: 0.5; }
.ai-stage-dot  { width: 12px; }
.ai-stage-dot::before {
  content: "";
  display: block;
  width: 5px; height: 5px;
  margin: 0 auto;
  border-radius: 50%;
  background: var(--text-muted);
  opacity: 0.35;
}
.ai-stage-spin {
  width: 12px; height: 12px;
  border-radius: 50%;
  border: 2px solid var(--border-accent);
  border-top-color: var(--accent);
  display: inline-block;
  animation: ai-stage-spin 0.85s linear infinite;
}
@keyframes ai-stage-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .ai-stage-spin      { animation: none; border-top-color: var(--accent); }
  .ai-progress-fill   { transition: none; }
  .ai-thinking-dot    { animation: none; opacity: 0.6; }
}

.ai-wait-slow {
  color: var(--text-muted);
  font-style: italic;
  margin-top: 9px;
}

/* The answer.  An abstention renders HERE, in this style - never as an
   error - so the honest behaviour never looks like a fault. */
.ai-answer-text { white-space: pre-wrap; }
.ai-answer-abstained {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 13px;
  font-style: italic;
}
.ai-sources {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}
.ai-sources-caption {
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 5px;
}
.ai-sources-list { margin: 0; padding-left: 16px; }
.ai-source {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.5;
}

/* ── MupasAI Lane G: published research, NOT the member's records ──────
   The ordering carries the safety property - the corpus decline stays on
   screen and this sits BENEATH it. These rules are the second line of
   defence, not the first.

   ⚠ DELIBERATELY NOT var(--accent). Accent means "your data" everywhere else
   in this portal (the LIVE pill, the active source tab, the member's own chat
   bubble), so using it here would say the opposite of what this block means.
   A muted slate-blue reads as "reference material" without reading as an
   error - this is legitimate content, not a fault. */
.ai-background {
  align-self: flex-start;
  max-width: 82%;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-left: 3px solid #6b7fa8;
  border-radius: var(--radius-md);
  padding: 12px 14px;
}
.ai-bg-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px;
  margin-bottom: 8px;
  padding-bottom: 7px;
  border-bottom: 1px solid var(--border);
}
.ai-bg-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #8fa3cc;
}
/* The qualifier is the half that does the work - keep it legible, not a
   footnote. Same size as the title, deliberately not smaller. */
.ai-bg-qualifier {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--text-muted);
}

/* The offer that precedes it. A member must CHOOSE background rather than be
   handed it - that choice is a better guarantee than any card. */
/* ⚠ THIS HAS TO BE NOTICED. It was dashed, transparent and muted-13px, and a
   real member reported it as easy to miss (Jim, 2026-09-10). The failure mode
   of a subtle offer is NOT a missed upsell - it is a member seeing a bare
   decline and concluding the assistant simply failed, which is the exact case
   Lane G exists to rescue. So: solid ground, an accent stripe, full-strength
   text, and a button at the size of a real control.

   It stays a QUESTION though, never a nag - one line, one button, no badge and
   no animation. The member is about to spend credits and must be able to
   ignore it without being chased. */
.ai-offer {
  align-self: flex-start;
  max-width: 82%;
  margin-top: 6px;
  padding: 14px 16px;
  border: 1px solid var(--border-accent);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
}
/* text-primary, not muted: a muted question reads as a footnote and gets
   skipped, which is what happened. */
.ai-offer-text {
  font-size: 14px;
  color: var(--text-primary);
  font-family: var(--font-sans);
  margin-bottom: 11px;
  line-height: 1.5;
}

@media (prefers-color-scheme: light) {
  .ai-background  { border-left-color: #5a6e96; }
  .ai-bg-title    { color: #4a5f8a; }
}

/* "Attributed, not verified" — a DIFFERENT claim from the header. The header
   says where the answer came from; this says how well it was checked.
   Legible, not a footnote: it is the one thing a member cannot infer from
   anything else on the card. Corpus answers never carry it. */
.ai-bg-caveat {
  margin-top: 9px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-muted);
  font-style: italic;
}

/* ── Notification Centre bell (2026-09-13) ─────────────────────────────────
   The header is a dark island and Mantine's subtle ActionIcon resolves its
   grey from the colour scheme, so without this the bell paints dark-on-dark in
   light mode (the same trap as the nav buttons, fixed 2026-09-03). */
.mupas-header .header-bell,
.mupas-header .header-bell svg { color: var(--text-secondary); }
.mupas-header .header-bell:hover { color: #ffffff; background: rgba(255,255,255,.08); }
.nc-coverage { padding: 12px 14px; border: 1px solid var(--border-accent);
  border-radius: var(--radius-md); background: var(--bg-elevated); }
.nc-card + .nc-card { margin-top: 0; }
.nc-card--legacy { opacity: .85; }

/* Block 1.7 (2026-09-14): the crisis line at the top of the peer-support and AI modals. */
.crisis-line { display:flex; align-items:flex-start; gap:8px; padding:10px 12px; border-radius: var(--radius-md, 8px);
               border:1px solid var(--border); border-left:3px solid var(--warning, #e8a33d); background: var(--bg-elevated);
               color: var(--text-primary); font-family: var(--font-sans); font-size:13px; line-height:1.45; }
.crisis-line:empty { display:none; }
.crisis-line-icon { flex:0 0 auto; margin-top:1px; color: var(--warning, #e8a33d); }

.mupas-footer .footer-legal-link { color: var(--text-secondary); text-decoration: underline; }


/* ── Block 2.1 (audit D-U6, 2026-09-14): one keyboard focus ring for the whole site.
   :focus-visible fires for keyboard focus and not for mouse clicks, so it does not
   paint a ring on every button somebody taps. The two `outline: none !important`
   rules that used to erase it are gone. ── */
:focus-visible { outline: 2px solid var(--accent) !important; outline-offset: 2px; }
.mantine-Select-input:focus-visible, .mantine-TextInput-input:focus-visible,
.mantine-PasswordInput-input:focus-visible, .mantine-Textarea-input:focus-visible {
  outline-offset: 0; }

/* ── Block 2.2: the skip link. Visually hidden until it takes keyboard focus. ── */
.skip-link { position:absolute; left:-9999px; top:8px; z-index:10000; padding:10px 16px;
  background: var(--accent); color:#06221d; font-family: var(--font-sans); font-weight:700;
  border-radius: var(--radius-sm, 6px); text-decoration:none; }
.skip-link:focus, .skip-link:focus-visible { left:8px; }

/* ── Block 2.2: 44 px touch targets on narrow screens (WCAG 2.5.8). ── */
@media (max-width: 900px) {
  .home .btn, .home .nav a, .home .inline-link, .home .footer-links a, .home .footer-linkbtn,
  .mupas-header .header-btn, .mupas-header .mantine-ActionIcon-root, .mupas-header .header-bell {
    min-height: 44px; min-width: 44px; }
  .home .inline-link, .home .footer-links a { display:inline-flex; align-items:center; }
}

/* ── Block 2.6: the theme toggle shows the icon for the scheme that is NOT showing
   (i.e. what a click gives you), switched by the attribute Mantine keeps on <html>. ── */
.scheme-icon { display: none; line-height: 0; }
[data-mantine-color-scheme="dark"] .scheme-icon--dark { display: inline-flex; }
[data-mantine-color-scheme="light"] .scheme-icon--light { display: inline-flex; }


/* ══ Block 4.1 (2026-09-15): the portal on a tablet and a phone ═══════════════
   Three breakpoints.  Measured before: header-actions overflowed at 820px, the
   nav and two buttons at 390px, the controls bar wrapped to four rows, the
   52px rail stayed, the chat FAB covered the footer (audit D-U2).            */

/* the burger and the header menu wrapper - inert on desktop */
.portal-burger { display: none; }
.header-menu { display: contents; }
/* the chat FAB clears the footer at every width (it sat on it) */
.mantine-Affix-root { bottom: 56px !important; }

/* ── ≤1200: breathing room ─────────────────────────── */
@media (max-width: 1200px) {
  .mupas-header { padding: 0 18px; }
  .header-rule { margin: 0 -18px; }
  .header-row { gap: 12px; }
  .logo-tagline { display: none; }
}

/* ── ≤900: tablet.  Header collapses to logo + burger; nav and actions live
   in an off-canvas panel; the controls bar is sticky and compact; the FAB
   clears the footer. ───────────────────────────────── */
@media (max-width: 900px) {
  .mupas-shell { height: auto; min-height: 100vh; overflow: visible; }
  /* animation:none / transform:none - the desktop reveal animation leaves a
     transform on the header, and a transformed ancestor turns the panel's
     position:fixed into 'fixed inside the header' (measured: a white strip). */
  .mupas-header { height: auto; min-height: 0; max-height: none; padding: 0 12px; overflow: visible;
                  animation: none; transform: none; opacity: 1; }
  .header-row--top { padding: 8px 0; gap: 10px; }
  .header-logo .logo-mark { height: 34px !important; }
  .logo-wordmark { font-size: 20px; }
  .header-rule { margin: 0 -12px; }

  .portal-burger {
    display: inline-flex; flex-direction: column; justify-content: center; gap: 5px;
    margin-left: auto; width: 44px; height: 44px; padding: 10px; border-radius: 8px;
    background: transparent; border: 1px solid var(--border); cursor: pointer;
  }
  .portal-burger-bar { display: block; height: 2px; width: 100%; background: var(--text-primary);
    border-radius: 1px; transition: transform var(--transition), opacity var(--transition); }
  .mupas-header.nav-open .portal-burger-bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .mupas-header.nav-open .portal-burger-bar:nth-child(2) { opacity: 0; }
  .mupas-header.nav-open .portal-burger-bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  /* the off-canvas panel: closed = off screen, never display:none, so every
     callback that targets something inside it keeps a mounted element */
  .header-menu {
    display: flex; flex-direction: column; align-items: stretch; gap: 8px;
    position: fixed; top: 0; right: 0; bottom: 0; width: min(340px, 90vw);
    padding: 64px 16px 24px; overflow-y: auto;
    background: var(--bg-surface); border-left: 1px solid var(--border);
    box-shadow: -12px 0 40px rgba(0,0,0,.35);
    transform: translateX(105%); transition: transform .25s ease;
    z-index: 900;
  }
  .mupas-header.nav-open .header-menu { transform: translateX(0); }
  /* the top row must paint above the tabs row while the panel is out, or the
     tabs row (a later sibling with its own z-index) shows through it */
  .mupas-header.nav-open .header-row--top { z-index: 5; }
  /* the burger stays on top of the open panel: it is the close control */
  .mupas-header.nav-open .portal-burger { position: relative; z-index: 901; border-color: var(--text-secondary); }
  /* a dim backdrop under the panel so the page reads as behind it */
  .mupas-header.nav-open::after { content: ''; position: fixed; inset: 0; background: rgba(0,0,0,.35); z-index: 899; }
  .header-menu .header-nav { flex: 0 0 auto; flex-direction: column; align-items: stretch;
                             justify-content: flex-start; gap: 2px; }
  /* the panel is the PAGE surface inside the dark island: give it the page's
     light tokens back (the island re-declares them on .mupas-header) */
  [data-mantine-color-scheme="light"] .header-menu {
    --text-primary: #111318; --text-secondary: #4a5060; --text-muted: #9a9faa;
    --border: rgba(0, 0, 0, 0.08); }
  .header-menu .mantine-Button-label, .header-menu .mantine-Button-section { color: var(--text-primary) !important; }
  .header-menu .header-nav .nav-link, .header-menu .header-btn { width: 100%; justify-content: flex-start; min-height: 44px; }
  .header-menu .header-actions { flex-wrap: wrap; justify-content: flex-start; gap: 10px;
    padding-top: 10px; border-top: 1px solid var(--border); }
  /* the header is a dark island; the panel is the surface, so the tokens
     inside it must come from the page, not the island */
  .header-menu { color: var(--text-primary); }

  /* the bottom row: tabs get the width, the label goes */
  .header-row--bottom { height: 40px; gap: 6px; }
  .header-tab-label span { display: none; }
  .header-divider { display: none; }

  /* body: the rail and the content stack; the content scrolls the page */
  .mupas-body { overflow: visible; }
  .mupas-content { overflow: visible; padding: 8px; gap: 8px; }
  .panels-container { overflow: visible; }
  .chart-panel { min-height: 300px; padding: 8px 10px; }

  /* controls: sticky, compact, no summary line */
  .controls-bar { position: sticky; top: 0; z-index: 30; gap: 6px; padding: 6px 8px; }
  .controls-bar .controls-label { display: none; }
  .controls-bar .header-divider { display: none; }
  .time-range-display { display: none; }

  /* the chat FAB sits above the footer, not on it.  !important: Mantine
     writes the offsets as inline CSS variables on the element. */
  .mantine-Affix-root { bottom: 72px !important; right: 14px !important; }
  /* the panel header: title and actions wrap instead of pushing past the edge */
  .chart-panel .panel-header { flex-wrap: wrap; gap: 6px; }
  .chart-panel .panel-title { min-width: 0; flex: 1 1 140px; }
  .chart-panel .panel-title > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .chart-panel .panel-badge { display: none; }
  .chart-panel .panel-actions { flex-shrink: 0; margin-left: auto; }
  .mupas-footer { padding: 10px 12px 14px; }
}

/* ── ≤600: phone.  The rail becomes a strip; the tabs row tightens. ── */
@media (max-width: 600px) {
  .mupas-body { flex-direction: column; }
  .mupas-sidebar, .mupas-sidebar.expanded {
    width: 100%; height: auto; flex-direction: row; align-items: center;
    padding: 4px 6px; gap: 4px; overflow-x: auto; overflow-y: hidden;
    border-right: none; border-bottom: 1px solid var(--border);
  }
  .mupas-sidebar .sidebar-sep { width: 1px; height: 28px; margin: 0 2px; }
  .mupas-sidebar .sidebar-label { display: none; }
  .header-row--top { gap: 8px; }
  .logo-wordmark { font-size: 18px; }
  .header-tab-label { display: none; }
  .tab-controls { gap: 2px; }
  /* 4.4b: the active-source strip gets the whole row; the four panel buttons
     and LIVE drop beneath it.  Measured 2026-09-15: sharing the row left the
     strip 78px wide with the device chip clipped to "MUPAS" and no scroll. */
  .header-row--bottom { flex-wrap: wrap; height: auto; row-gap: 4px; }
  .header-row--bottom .device-tabs-wrap { flex: 1 1 100%; order: 0; height: 36px; }
  .header-row--bottom .tab-controls { order: 1; }
  .header-row--bottom .flex-spacer { order: 2; }
  .header-row--bottom .live-badge { order: 3; }
  .chart-panel { min-height: 260px; }
  .controls-bar { padding: 4px 6px; }
  .controls-bar .mantine-Select-root, .controls-bar .mantine-DateTimePicker-root { min-width: 0; }
}


/* ══ Block 4.3 (2026-09-15): loading choreography and pressed states ═══════════
   Dash marks a component whose update is in flight with data-dash-is-loading.
   The chart dims rather than being replaced - a Loading wrapper would re-mount
   the graph (losing zoom) and, applied globally, the AI transcript and the
   peer-room dock, which keep their scroll position by counting messages.   */
[data-dash-is-loading="true"] .js-plotly-plot,
.js-plotly-plot[data-dash-is-loading="true"] { opacity: .5; transition: opacity .25s ease; }
.js-plotly-plot { transition: opacity .25s ease; }
.chart-panel--skeleton { justify-content: stretch; }
.chart-panel--skeleton .panel-header { justify-content: space-between; margin-bottom: 8px; }

/* pressed states: the portal had none (audit D-V3) */
.header-btn:active, .time-nav-btn:active, .panel-action-btn:active, .tab-ctrl-btn:active,
.sidebar-item-clickable:active, .theme-toggle-btn:active, .sensor-select-trigger:active,
.portal-burger:active { transform: scale(.96); transition: transform .08s ease; }
@media (prefers-reduced-motion: reduce) {
  .header-btn:active, .time-nav-btn:active, .panel-action-btn:active, .tab-ctrl-btn:active,
  .sidebar-item-clickable:active, .theme-toggle-btn:active, .sensor-select-trigger:active,
  .portal-burger:active { transform: none; }
  .js-plotly-plot { transition: none; }
}


/* ------------------------------------------------------------------ */
/*  Q102 - the research continuation, INSIDE the answer bubble         */
/* ------------------------------------------------------------------ */
/* A continuation is not a second answer and must not read as one, so this is
   a section WITHIN the agent bubble rather than a card beside it: the corpus
   half sits above it, always, and the two belong to one reply.

   ⚠ Deliberately NOT the accent colour, for the reason the Lane G card
   records: accent means "your data" everywhere else in this portal - the LIVE
   pill, the active source tab, the member's own bubble - so accent here would
   say the opposite of what the block means. The same slate as .ai-background,
   because it is the same claim about where the words came from. */
.ai-research {
  margin-top: 14px;
  padding-top: 12px;
  padding-left: 11px;
  border-top: 1px solid var(--border);
  border-left: 3px solid #6b7fa8;
}
.ai-research-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #8fa3cc;
  margin-bottom: 6px;
}
/* "Attributed, not verified" is a SEPARATE claim from the heading - where it
   came from vs how well it was checked - and a member cannot infer it from
   the payload, so it is never shrunk into a footnote. */
.ai-research-caveat {
  margin-top: 8px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-muted);
}
[data-mantine-color-scheme="light"] .ai-research {
  border-left-color: #5a6e96;
}
[data-mantine-color-scheme="light"] .ai-research-title {
  color: #4a5f8a;
}

/* Q102: where to look at your own readings. A quiet caption, never a call to
   action - it points at charts the member already owns and must not read as a
   claim about what those charts contain. */
.ai-readings-note {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-muted);
}

/* Q102: a research block's citations. A list a member can look up, never a
   link - naming a source is a smaller and truer promise than offering to
   fetch it. */
.ai-research-sources {
  margin: 8px 0 0 0;
  padding-left: 18px;
  list-style: disc;
}
.ai-research-source {
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* ── Active Data Sources strip (2026-10-03, Jim: "I like please do it") ──────
   [N sources v] [chips ...] [+N more].  The chips are short (ellipsis, full
   name in the title); the dot is the device's STATUS - the coverage panel's
   own rule: sending / quiet (nothing in 7 days) / off / not measured.       */
.device-tabs-wrap { display: flex; align-items: center; gap: 6px; }
.device-tabs-wrap > .mantine-Tabs-root { flex: 1 1 auto; min-width: 0; }
.src-menu-btn, .src-more {
  font-family: var(--font-sans); font-size: 12px; font-weight: 600;
  /* the header is a DARK ISLAND in both schemes: fixed colours, never the
     page's --bg-elevated (light in light mode = white box, light text). */
  color: #eef0f4; background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.16); border-radius: var(--radius-sm);
  padding: 4px 10px; white-space: nowrap; cursor: pointer; flex: 0 0 auto;
  min-height: 28px; display: inline-flex; align-items: center;
}
.src-menu-btn:hover, .src-more:hover { border-color: var(--border-accent); }
.src-menu-btn:focus-visible, .src-more:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.device-tabs-wrap .src-chip-label {
  display: inline-block; max-width: 220px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}
.src-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  flex-shrink: 0; box-sizing: border-box;
}
.src-dot--sending    { background: #2fb36d; }
.src-dot--quiet      { background: #e0a030; }
.src-dot--silent     { background: #8a8f9a; }
.src-dot--unmeasured { background: transparent; border: 1.5px solid #8a8f9a; }
.src-dot--feed       { background: transparent; border: 1.5px dashed #8a8f9a; }
.src-menu-dropdown { max-height: min(70vh, 520px); overflow-y: auto; min-width: 280px; }
.src-menu-dropdown .src-item-state { color: var(--text-secondary); font-size: 12px; }
.src-menu-dropdown .src-item--current { font-weight: 700; }
/* The strip takes ALL the row's spare width (Jim, 2026-10-03: "make the tab
   list wider").  The spacer that keeps LIVE at the right edge was flex:1 too,
   so the two split the space 50/50 and the chips got half the row - measured
   426 px each at 1366.  LIVE stays right because the strip now fills up to it. */
.header-row--bottom .flex-spacer { flex: 0 0 0; }
.src-menu-dropdown .src-row { display: flex; align-items: center; gap: 2px; }
.src-menu-dropdown .src-row > .mantine-Menu-item { flex: 1 1 auto; min-width: 0; }
.src-remove {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: var(--radius-sm);
  border: 1px solid transparent; background: transparent; cursor: pointer;
  color: var(--text-secondary); font-size: 16px; line-height: 1;
}
.src-remove:hover { color: #d9534f; border-color: var(--border); }
.src-remove:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
@media (max-width: 600px) {
  .device-tabs-wrap .src-chip-label { max-width: 110px; }
  .src-menu-btn, .src-more { padding: 4px 8px; }
}
