/* Design tokens extracted from QuickSMS's actual shipped CSS - project design.
   Evolving the existing brand identity, not replacing it; stripped of the
   Bootstrap-admin-template chrome (heavy shadows, gradient badges, dense borders). */
:root {
  --brand-primary: #448bff;
  --brand-dark: #192039;
  --brand-accent-cyan: #14bae4;
  --brand-accent-pink: #f54394;
  --success: #31c971;
  --warning: #f4c414;
  --danger: #e5484d;
  --text-primary: #1a1d23;
  --text-secondary: #5e676f;
  --surface: #f5f5f6;
  --surface-elevated: #ffffff;
  --border: #e2e5eb;

  --success-bg: #e8f9ee;
  --success-text: #1a7a45;
  --danger-bg: #fdecec;
  --danger-text: #a92323;
  --warning-bg: #fff6dc;
  --warning-text: #8a6d00;
  --selected-bg: #eef4ff;
  --bubble-customer-bg: #eceff3;
  --bubble-automation-bg: #e7f0ff;

  /* Sidebar/topbar ("chrome") colors - light theme gets a light chrome, not a
     permanently-dark one. User explicitly flagged the fixed-dark sidebar as a light-mode
     gap: "left bar and nav bar need to obey light theme". */
  --chrome-bg: #ffffff;
  --chrome-text: var(--text-primary);
  --chrome-text-muted: var(--text-secondary);
  --chrome-border: var(--border);
  --chrome-hover: var(--surface);
  --chrome-active: #eef2fb;

  --radius: 6px;
  --font: "Rubik", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --card-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.05);
  /* Tells the browser which theme its OWN unstyled chrome should render in - the
     native date-picker calendar popup, spinner arrows, scrollbars. Without this an
     <input type="date"> keeps a white calendar popup even once its box is styled dark. */
  color-scheme: light;
}
:root[data-theme="dark"] { --card-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.3); color-scheme: dark; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --card-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.3); color-scheme: dark; }
}

/* ---- dark theme (project design follow-up: user-requested dark/light toggle)
   Every color in this file is a CSS variable, so dark mode is a token swap, not a
   parallel stylesheet - [data-theme] is set on <html> by the toggle script in
   layouts/app.twig, defaulting to the OS preference on first visit. ---- */
:root[data-theme="dark"] {
  --text-primary: #e8e9ec;
  --text-secondary: #9aa0ab;
  /* Navy-tinted, matching --brand-dark's (#192039) hue family, not flat/desaturated
     black - "are you sure why black" flagged the page content reading as pure black
     next to the visibly-navy sidebar/topbar. */
  --surface: #171b2c;
  --surface-elevated: #1e2338;
  --border: #333c58;

  --success-bg: #123422;
  --success-text: #4fdb8e;
  --danger-bg: #3a1618;
  --danger-text: #ff8a8e;
  --warning-bg: #3a3011;
  --warning-text: #f4c414;
  --selected-bg: #1e2a42;
  --bubble-customer-bg: #262a33;
  --bubble-automation-bg: #1e2c42;

  --chrome-bg: var(--brand-dark);
  --chrome-text: #ffffff;
  --chrome-text-muted: rgba(255,255,255,0.75);
  --chrome-border: rgba(255,255,255,0.12);
  --chrome-hover: rgba(255,255,255,0.08);
  --chrome-active: rgba(255,255,255,0.1);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --text-primary: #e8e9ec;
    --text-secondary: #9aa0ab;
    --surface: #171b2c;
    --surface-elevated: #1e2338;
    --border: #333c58;

    --success-bg: #123422;
    --success-text: #4fdb8e;
    --danger-bg: #3a1618;
    --danger-text: #ff8a8e;
    --warning-bg: #3a3011;
    --warning-text: #f4c414;
    --selected-bg: #1e2a42;
    --bubble-customer-bg: #262a33;
    --bubble-automation-bg: #1e2c42;

    --chrome-bg: var(--brand-dark);
    --chrome-text: #ffffff;
    --chrome-text-muted: rgba(255,255,255,0.75);
    --chrome-border: rgba(255,255,255,0.12);
    --chrome-hover: rgba(255,255,255,0.08);
    --chrome-active: rgba(255,255,255,0.1);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font);
  background: var(--surface);
  color: var(--text-primary);
  font-size: 14px;
  /* "content too large, zoom out 10%" - uniformly scales everything (including click
     targets, so nothing becomes hard to hit) rather than hand-tuning every spacing
     value. Well-supported in Chromium, which this app is built/tested against. */
  zoom: 90%;
}

a { color: var(--brand-primary); text-decoration: none; }
a:hover { text-decoration: underline; }

.app-shell {
  display: flex;
  flex-direction: column;
  /* zoom: 90% on body shrinks rendered content but not the viewport itself, so a plain
     100vh box only fills 90% of the real window, leaving a blank gap below it -
     compensate by sizing for the post-zoom viewport (100 / 0.9). */
  height: calc(100vh / 0.9);
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  height: 56px;
  background: var(--chrome-bg);
  color: var(--chrome-text);
  border-bottom: 1px solid var(--chrome-border);
  flex-shrink: 0;
}
.topbar .brand { display: flex; align-items: center; gap: 10px; font-weight: 600; letter-spacing: 0.02em; }
/* Logo chip stays white regardless of theme (the logo asset itself assumes a light
   background) - flagged directly as "too small to read", sized up from 22px. */
/* No forced white chip - the logo file has a real alpha channel (confirmed via its WEBP
   ALPH chunk), so it sits directly on the topbar/sidebar like the real reference does,
   instead of an inconsistent white patch that doesn't obey the theme. */
.brand-logo { display: flex; align-items: center; }
.brand-logo img { height: 36px; display: block; }
.brand-product { font-size: 14px; font-weight: 500; color: var(--chrome-text-muted); padding-left: 10px; border-left: 1px solid var(--chrome-border); }
.topbar .user-menu { display: flex; align-items: center; gap: 12px; font-size: 13px; position: relative; }
.topbar .user-menu form { display: inline; }
.topbar .user-menu button {
  background: none; border: none; color: var(--chrome-text-muted); cursor: pointer; font-size: 13px;
}
.user-dropdown { position: relative; }
.bell-toggle {
  position: relative; background: none; border: none; color: var(--chrome-text-muted); cursor: pointer;
  padding: 6px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
}
.bell-toggle:hover { background: var(--chrome-hover); color: var(--chrome-text); }
.bell-toggle svg { width: 18px; height: 18px; }
.notif-badge {
  background: var(--danger); color: #fff; font-size: 10px; font-weight: 700; line-height: 1;
  border-radius: 999px; padding: 2px 5px; min-width: 15px; text-align: center;
}
.bell-toggle .notif-badge { position: absolute; top: 2px; right: 2px; }
#bell-menu-list a { display: block; padding: 8px 10px; border-radius: 4px; font-size: 12.5px; color: var(--text-primary); }
#bell-menu-list a:hover { background: var(--surface); text-decoration: none; }

/* ---- toast notifications (bottom-right, auto-dismiss, no visible countdown) ---- */
.toast-container { position: fixed; bottom: 20px; right: 20px; z-index: 1000; display: flex; flex-direction: column; gap: 10px; width: 300px; }
.toast {
  background: var(--surface-elevated); box-shadow: var(--card-shadow); border-left: 3px solid var(--brand-primary);
  border-radius: 6px; padding: 12px 14px; font-size: 12.5px; cursor: pointer;
  animation: toast-in 0.25s ease-out;
}
.toast:hover { box-shadow: 0 4px 14px rgba(0,0,0,0.15); }
.toast .toast-body { color: var(--text-secondary); margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.toast.toast-leaving { animation: toast-out 0.3s ease-in forwards; }
@keyframes toast-in { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
@keyframes toast-out { to { opacity: 0; transform: translateX(20px); } }

/* ---- "Need help?" contact modal ---- */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(10, 12, 20, 0.55); z-index: 1100;
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.modal-card {
  background: var(--surface-elevated); border-radius: 10px; box-shadow: 0 12px 32px rgba(0,0,0,0.25);
  width: 100%; max-width: 380px; overflow: hidden;
}
.modal-card-header {
  display: flex; align-items: center; justify-content: space-between; padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.modal-card-header h2 { margin: 0; font-size: 15px; }
.modal-close {
  background: none; border: none; cursor: pointer; color: var(--text-secondary);
  display: flex; align-items: center; padding: 4px; border-radius: var(--radius);
}
.modal-close:hover { background: var(--surface); color: var(--text-primary); }
.modal-close svg { width: 16px; height: 16px; }
.modal-card-body { padding: 16px 20px 20px; }
.help-intro { font-size: 12.5px; color: var(--text-secondary); margin: 0 0 14px; }
.help-contact-row {
  display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 8px;
  color: var(--text-primary); margin-bottom: 6px;
}
.help-contact-row:last-child { margin-bottom: 0; }
.help-contact-row:hover { background: var(--surface); }
.help-contact-icon {
  flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--brand-primary) 15%, transparent); color: var(--brand-primary);
}
.help-contact-icon svg { width: 16px; height: 16px; }
.help-contact-label { display: block; font-size: 11px; color: var(--text-secondary); }
.help-contact-value { display: block; font-size: 13.5px; font-weight: 500; }
.user-dropdown-toggle {
  display: flex !important; align-items: center; gap: 8px; color: var(--chrome-text) !important;
  padding: 4px 6px 4px 10px !important; border-radius: 999px;
}
.user-dropdown-toggle:hover { background: var(--chrome-hover); }
.user-avatar {
  width: 26px; height: 26px; border-radius: 50%; background: var(--brand-primary);
  color: #fff; font-size: 11px; font-weight: 600; display: flex; align-items: center; justify-content: center;
  overflow: hidden; flex-shrink: 0;
}
.user-avatar img { width: 100%; height: 100%; object-fit: cover; }

/* ---- My Profile: large avatar preview + upload control ---- */
.avatar-upload { display: flex; align-items: center; gap: 18px; margin-bottom: 20px; }
.avatar-upload .user-avatar { width: 72px; height: 72px; font-size: 24px; }
.avatar-upload-controls { display: flex; flex-direction: column; gap: 6px; }
.avatar-upload-controls .form-hint { margin-top: 0; }

/* ---- sidebar nav (project design-inspired grouping, kept flat/light per
   project design - borrowed information architecture, not the heavy
   admin-template visual chrome that doc explicitly steers away from) ---- */
.app-body { display: flex; flex: 1; min-height: 0; }
.sidebar {
  width: 208px;
  flex-shrink: 0;
  background: var(--chrome-bg);
  color: var(--chrome-text-muted);
  border-right: 1px solid var(--chrome-border);
  overflow-y: auto;
  padding: 14px 0;
}
.sidebar-group { margin-bottom: 4px; }
.sidebar-group-label {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--chrome-text-muted); opacity: 0.7; padding: 14px 18px 6px;
}
.sidebar-link {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 18px; font-size: 13px; color: var(--chrome-text-muted);
  border-left: 2px solid transparent;
}
.sidebar-link:hover { color: var(--chrome-text); text-decoration: none; background: var(--chrome-hover); }
.sidebar-link.active { color: var(--chrome-text); background: var(--chrome-active); border-left-color: var(--brand-primary); }
.sidebar-link .icon { width: 15px; text-align: center; font-size: 12px; flex-shrink: 0; opacity: 0.9; display: inline-flex; }
.sidebar-link .icon svg { width: 15px; height: 15px; }

/* main content area alongside the sidebar */
.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
.app-main .inbox-shell, .app-main .page { flex: 1; min-height: 0; }

/* ---- admin pages (businesses/services/keywords) ---- */
/* "make full use of screen page in all sections" - quicksms's own pages use
   container-fluid (near-full-width), not a narrow fixed column with large blank
   margins either side. 900px left huge unused space on anything wider than a laptop. */
.page { max-width: 1600px; margin: 0 auto; padding: 28px 32px; width: 100%; flex: 1; min-height: 0; overflow-y: auto; }
.page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
.page-header h1 { font-size: 18px; margin: 0; }
.page-header .meta { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }
.page-header-row { display: flex; flex-direction: column; }

.card { background: var(--surface-elevated); border: none; box-shadow: var(--card-shadow); border-radius: 8px; padding: 20px; margin-bottom: 16px; }
.card h2 { font-size: 14px; margin: 0 0 14px; font-weight: 600; }

.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.data-table thead { box-shadow: 0 1px 3px rgba(0,0,0,0.05); }
.data-table th { text-align: left; color: var(--text-secondary); font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; padding: 8px 10px; border-bottom: 1px solid var(--border); }
.id-badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; background: var(--warning-bg); color: var(--warning-text); }
.back-link { display: inline-flex; align-items: center; gap: 4px; color: var(--text-secondary); font-size: 13px; }
.back-link:hover { text-decoration: none; color: var(--text-primary); }
.back-link svg { width: 15px; height: 15px; }
.data-table td { padding: 10px; border-bottom: 1px solid var(--border); }
.data-table tr:last-child td { border-bottom: none; }
.data-table tr:hover td { background: var(--surface); }
.data-table a.row-link { display: block; color: var(--text-primary); }
.data-table a.row-link:hover { text-decoration: none; }

.form-group { margin-bottom: 16px; }
.form-group label { display: block; font-size: 12px; color: var(--text-secondary); margin-bottom: 4px; }
.form-group input, .form-group select, .form-group textarea {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius);
  font-size: 13px; font-family: var(--font);
  /* Unstyled inputs otherwise fall back to the browser's own white/black UA
     rendering in every theme - invisible on the light theme's white cards, but a
     stray white box once the surrounding card goes dark. */
  background: var(--surface-elevated); color: var(--text-primary);
}
.form-group textarea { min-height: 80px; resize: vertical; }
.form-row { display: flex; gap: 16px; }
/* color-scheme:dark (set at :root above) already asks the browser to draw the
   popup calendar itself in a dark palette, but Chromium still renders the small
   calendar-icon glyph inside the closed <input type="date"> box in its light-mode
   (dark) ink regardless - a dark-on-navy icon that reads as a stray black smudge
   next to everything else on this page. Inverting just the glyph, not the input,
   fixes that without touching the (already-correct) popup itself. */
:root[data-theme="dark"] input[type="date"]::-webkit-calendar-picker-indicator,
:root[data-theme="dark"] input[type="time"]::-webkit-calendar-picker-indicator,
:root[data-theme="dark"] input[type="datetime-local"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) input[type="date"]::-webkit-calendar-picker-indicator,
  :root:not([data-theme="light"]) input[type="time"]::-webkit-calendar-picker-indicator,
  :root:not([data-theme="light"]) input[type="datetime-local"]::-webkit-calendar-picker-indicator {
    filter: invert(1);
  }
}
.form-row .form-group { flex: 1; }
.form-hint { font-size: 11.5px; color: var(--text-secondary); margin-top: 3px; }
.form-actions { display: flex; gap: 10px; margin-top: 20px; }

.stat-pill { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 11px; background: var(--surface); color: var(--text-secondary); }

/* ---- stat-card summary row (matches the reference QuickSMS-family dashboard's card
   recipe: soft shadow instead of a border, an icon accent, not a flat bordered box) ---- */
.stat-cards { display: flex; gap: 14px; margin-bottom: 16px; }
.stat-card {
  flex: 1; background: var(--surface-elevated); border: none; box-shadow: var(--card-shadow);
  border-radius: 8px; padding: 16px 18px; display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
}
.stat-card .stat-value { font-size: 24px; font-weight: 700; color: var(--text-primary); line-height: 1.2; }
.stat-card .stat-label { font-size: 11.5px; color: var(--text-secondary); margin-top: 3px; }
.stat-card .stat-icon {
  flex-shrink: 0; width: 38px; height: 38px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 16px;
  background: color-mix(in srgb, var(--stat-color, var(--brand-primary)) 15%, transparent);
  color: var(--stat-color, var(--brand-primary));
}
.stat-card .stat-icon svg { width: 18px; height: 18px; }

/* ---- filter/search bar above a data table ---- */
.filter-bar { display: flex; gap: 8px; padding: 14px 20px; border-bottom: 1px solid var(--border); }
.filter-bar input[type="search"], .filter-bar input[type="text"] {
  flex: 1; padding: 7px 10px; border: 1px solid var(--border); border-radius: var(--radius);
  font-size: 13px; font-family: var(--font);
  background: var(--surface-elevated); color: var(--text-primary);
}
.filter-bar button {
  font-size: 12px; padding: 7px 14px; border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--surface-elevated); color: var(--text-primary); cursor: pointer;
}

/* ---- per-row "quick links" action menu ---- */
.row-actions { position: relative; text-align: right; }
.row-actions-toggle {
  background: none; border: none; cursor: pointer; color: var(--text-secondary);
  font-size: 15px; padding: 4px 6px; border-radius: var(--radius); line-height: 1;
  display: inline-flex; align-items: center;
}
.row-actions-toggle svg { width: 16px; height: 16px; }
.row-actions-toggle:hover { background: var(--surface); }
.row-actions-menu {
  position: absolute; right: 0; top: 100%; z-index: 10; min-width: 140px;
  background: var(--surface-elevated); border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 4px 14px rgba(0,0,0,0.08); padding: 4px;
}
.row-actions-menu a {
  display: block; padding: 6px 10px; font-size: 12.5px; color: var(--text-primary); border-radius: 4px;
}
.row-actions-menu a:hover { background: var(--surface); text-decoration: none; }
.row-actions-menu .dropdown-header {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-secondary);
  padding: 6px 10px 4px; font-weight: 600;
}
.topbar .user-menu button:hover { color: var(--chrome-text); }
#theme-toggle { font-size: 15px !important; padding: 2px 6px !important; border-radius: 4px; }
#theme-toggle:hover { background: rgba(255,255,255,0.1); }

.flash {
  padding: 10px 20px;
  font-size: 13px;
  border-bottom: 1px solid var(--border);
}
.flash-success { background: var(--success-bg); color: var(--success-text); }
.flash-error { background: var(--danger-bg); color: var(--danger-text); }

/* ---- three-pane inbox layout (project design) ---- */
.inbox-shell { display: flex; flex: 1; min-height: 0; }

/* 2026-09-27: was a fixed 340px sidebar that always sat next to a thread-pane: now the
   list is the only thing in .inbox-shell on the index page (no conversation open), so
   it runs full width there. Once a conversation IS open (inbox/show.twig passes
   compact:true to the include), .inbox-list-compact below puts it back to a narrow
   sidebar beside the thread - "the convo page should maintain the list on the left
   side", not have the thread cover it. */
.inbox-list {
  width: 100%;
  flex: 1;
  min-width: 0;
  background: var(--surface-elevated);
  display: flex;
  flex-direction: column;
}
.inbox-list-header { padding: 14px 16px; border-bottom: 1px solid var(--border); }
.inbox-list-header h1 { font-size: 15px; margin: 0 0 8px; }
.inbox-filters { display: flex; gap: 6px; flex-wrap: wrap; }
/* The filter-type/value/date controls below aren't wrapped in .form-group (that'd
   force them into its stacked label+full-width layout, wrong for an inline filter
   bar) - so, same as .composer textarea above, they'd silently fall through to the
   browser's own UA dark-mode gray instead of the app's navy --surface-elevated. */
.inbox-filters select, .inbox-filters input[type="text"], .inbox-filters input[type="datetime-local"] {
  padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius);
  font-size: 12.5px; font-family: var(--font);
  background: var(--surface-elevated); color: var(--text-primary);
}
.inbox-filters a {
  font-size: 12px; padding: 4px 8px; border-radius: 999px; color: var(--text-secondary);
  border: 1px solid var(--border);
}
.inbox-filters a.active { background: var(--brand-primary); border-color: var(--brand-primary); color: #fff; }
.inbox-filters a:hover { text-decoration: none; border-color: var(--brand-primary); }

/* "add mobile filter" - #filterToggleBtn is hidden on desktop (the filter form below
   it is just always visible there, as before) and only appears at narrow widths,
   where the 3-row filter form would otherwise permanently eat a lot of vertical
   space above the list. .filter-bar-wrap starts collapsed on mobile and opens on
   tap, via conversation_list.twig's own inline script (adds/removes .open). */
.filter-toggle-btn { display: none; margin-top: 8px; }
@media (max-width: 640px) {
  .filter-toggle-btn { display: inline-flex; }
  .filter-bar-wrap { display: none; }
  .filter-bar-wrap.open { display: block; }
}

/* One bordered/backgrounded box holding both the service-search input and the service
   <select>, instead of two separate boxes sitting next to each other - "the search and
   all services should be in one box". Each child is stripped of its own border/bg and
   just fills its share of the shared box; a 1px inner divider is the only seam. */
.combo-box {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-elevated);
  overflow: hidden;
}
.combo-box input, .combo-box select {
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--text-primary);
  padding: 6px 10px;
  font-size: 12.5px;
  font-family: var(--font);
}
.combo-box input { flex: 1; min-width: 90px; border-right: 1px solid var(--border); }
.combo-box select { flex: 1; min-width: 110px; }
.combo-box input:focus, .combo-box select:focus { outline: none; background: var(--surface); }

/* 2026-09-27: full-width single-line row (identity | preview | meta) - see .inbox-list
   above for why this is no longer a narrow-sidebar stacked card. */
.conversation-row {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 12px 24px;
  border-bottom: 1px solid var(--border);
  border-left: 3px solid transparent;
  color: var(--text-primary);
}
.conversation-row:hover { background: var(--surface); text-decoration: none; }
.conversation-row.selected { background: var(--selected-bg); border-left-color: var(--brand-primary); }
.conversation-row .row-identity { flex: 0 0 260px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.conversation-row .msisdn { font-weight: 600; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.conversation-row .service-name { font-size: 11px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.conversation-row .preview { flex: 1; min-width: 0; font-size: 13px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conversation-row .row-meta { flex: 0 0 auto; display: flex; align-items: center; gap: 10px; }
.conversation-row .row-shortcode {
  font-size: 10.5px; font-family: ui-monospace, "SF Mono", Consolas, monospace;
  color: var(--text-secondary); background: var(--surface); border: 1px solid var(--border);
  border-radius: 999px; padding: 1px 7px; white-space: nowrap;
}
.conversation-row .preview-time { font-size: 11px; color: var(--text-secondary); white-space: nowrap; }
.conversation-row .preview-delivery { font-size: 11px; color: var(--text-secondary); white-space: nowrap; }

/* Once a conversation is open (inbox/show.twig includes the list with compact:true),
   the list goes back to a narrow left-hand sidebar instead of running full width -
   "the convo page should maintain the list on the left side", not have the thread
   cover it. The full-width .conversation-row layout above doesn't fit a 340px column,
   so it's put back into a stacked card here, same shape the sidebar always used. */
.inbox-list-compact { width: 340px; flex: 0 0 340px; border-right: 1px solid var(--border); }
.inbox-list-compact .conversation-row { flex-direction: column; align-items: stretch; gap: 4px; padding: 12px 16px; }
/* was: not overriding flex-basis here left .row-identity/.preview/.row-meta carrying
   their full-width row's `flex: 0 0 260px` / `flex: 1` straight into this column-flex
   layout, where flex-basis sets HEIGHT instead of width - .row-identity alone forced
   every row to a 260px-tall block of empty space before its preview/status line ever
   showed up. `flex: 0 0 auto` on all three makes each one size to its own content, the
   normal stacked-card shape this sidebar always had. */
.inbox-list-compact .row-identity { flex: 0 0 auto; flex-direction: row; justify-content: space-between; align-items: baseline; width: 100%; }
.inbox-list-compact .preview { flex: 0 0 auto; white-space: normal; font-size: 12px; }
.inbox-list-compact .row-meta { flex: 0 0 auto; justify-content: space-between; width: 100%; }

/* The thread + context panes sit beside the (now narrow, .inbox-list-compact) list in
   normal flow, taking the rest of the width - not a full overlay covering the list.
   Escape still closes it (see inbox/show.twig's keydown listener, using this element's
   data-back-url) same as a mouse click on .thread-close - it just navigates back to
   the plain (full-width) list instead of un-hiding it in place. */
.thread-overlay {
  flex: 1;
  min-width: 0;
  display: flex;
  background: var(--surface);
}
.thread-close {
  font-size: 18px;
  line-height: 1;
  padding: 4px 10px;
}

.badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; }
.badge-needs_reply { background: var(--danger-bg); color: var(--danger-text); }
.badge-open { background: var(--success-bg); color: var(--success-text); }
.badge-waiting_for_customer { background: var(--warning-bg); color: var(--warning-text); }
.badge-resolved { background: var(--surface); color: var(--text-secondary); }
.badge-archived { background: var(--surface); color: var(--text-secondary); }

/* ---- service governance lifecycle badges (project design) ---- */
.badge-lifecycle-draft { background: var(--surface); color: var(--text-secondary); }
.badge-lifecycle-testing { background: var(--selected-bg); color: var(--brand-primary); }
.badge-lifecycle-shadow { background: var(--selected-bg); color: var(--brand-primary); }
.badge-lifecycle-awaiting_approval { background: var(--warning-bg); color: var(--warning-text); }
.badge-lifecycle-active { background: var(--success-bg); color: var(--success-text); }
.badge-lifecycle-rejected { background: var(--danger-bg); color: var(--danger-text); }
.badge-lifecycle-suspended { background: var(--warning-bg); color: var(--warning-text); }
.badge-lifecycle-archived { background: var(--surface); color: var(--text-secondary); }
.badge-lifecycle-disabled { background: var(--surface); color: var(--text-secondary); }

/* ---- TEST tenant marker (project design: "The UI must show a
   prominent TEST badge") - amber/black, deliberately louder than the status badges
   above so it can't be mistaken for ordinary chrome. ---- */
.badge-test {
  display: inline-block; padding: 2px 7px; border-radius: 4px; font-size: 10px; font-weight: 700;
  letter-spacing: 0.04em; background: var(--warning); color: #3a2e00; vertical-align: middle; margin-left: 6px;
}

.empty-state { padding: 40px 20px; text-align: center; color: var(--text-secondary); font-size: 13px; }

/* ---- thread ---- */
.thread-pane { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.thread-header { padding: 14px 20px; border-bottom: 1px solid var(--border); background: var(--surface-elevated); display: flex; justify-content: space-between; align-items: center; }
.thread-header h2 { margin: 0; font-size: 15px; }
.thread-header .meta { font-size: 12px; color: var(--text-secondary); }
.thread-actions { display: flex; gap: 8px; }
/* .btn is the one base button rule - previously ".thread-actions button, .btn" also
   matched any bare <button> in that container by element selector, which outranks a
   single class like .btn-primary on `background` (both matching rules were the same
   specificity tier except the element+descendant part), silently overriding
   .btn-primary's blue background back to the default surface color while its
   `color: #fff` still applied - white text on a white/light background, invisible in
   light mode. Every button, including plain ones, now needs the .btn class instead of
   relying on being inside .thread-actions. */
.btn {
  font-size: 12px; padding: 6px 12px; border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--surface-elevated); color: var(--text-primary); cursor: pointer;
}
/* .btn.btn-primary (not just .btn-primary): several container-scoped rules below
   (.filter-bar button, .composer-tabs button, etc.) match by bare element selector
   scoped to a class, which has the same specificity tier as a single class like
   .btn-primary and can silently win on `background` depending on source order -
   already bit us once (SHORTCODE_ENGINE-era inbox buttons), doing it again here via
   .filter-bar button on the reports date-filter form. Compounding .btn + .btn-primary
   (0,2,0) beats any single container+element selector (0,1,1) unconditionally, so this
   class of bug can't recur regardless of where a primary button is placed. */
.btn.btn-primary { background: var(--brand-primary); color: #fff; border-color: var(--brand-primary); }

.thread-messages { flex: 1; overflow-y: auto; padding: 16px 20px; display: flex; flex-direction: column; gap: 5px; }

/* 2026-09-27: "why so thick" - was padding:10px 14px + border-radius:10px + a 4px
   gap before the meta line, on top of a 10px gap between bubbles - each on its own
   reasonable, but stacked across a whole thread it read as bulky/thick, not the tight
   chat-bubble feel the rest of the theme goes for. Trimmed padding, radius, meta
   spacing, and inter-bubble gap all down together rather than any single one alone. */
.message { max-width: 60%; padding: 6px 10px; border-radius: 8px; font-size: 13px; line-height: 1.35; }
.message .meta { font-size: 10px; opacity: 0.6; margin-top: 2px; }
.message-customer { align-self: flex-start; background: var(--bubble-customer-bg); color: var(--text-primary); border-bottom-left-radius: 2px; }
.message-automation { align-self: flex-end; background: var(--bubble-automation-bg); color: var(--text-primary); border-bottom-right-radius: 2px; }
.message-agent { align-self: flex-end; background: var(--brand-primary); color: #fff; border-bottom-right-radius: 2px; }
.message-note { align-self: center; background: var(--warning-bg); color: var(--warning-text); border-radius: var(--radius); font-style: italic; max-width: 80%; }
.message-system { align-self: center; background: none; color: var(--text-secondary); font-size: 11px; }

.delivery-status { font-size: 10.5px; opacity: 0.8; }

.composer { border-top: 1px solid var(--border); background: var(--surface-elevated); padding: 12px 20px; }
.composer textarea {
  width: 100%; min-height: 60px; border: 1px solid var(--border); border-radius: var(--radius);
  padding: 8px 10px; font-family: var(--font); font-size: 13px; resize: vertical;
  background: var(--surface-elevated); color: var(--text-primary);
}
.composer-actions { display: flex; justify-content: space-between; margin-top: 8px; }
.composer-tabs { display: flex; gap: 4px; margin-bottom: 8px; }
.composer-tabs button { font-size: 12px; padding: 4px 10px; border-radius: var(--radius); border: 1px solid var(--border); background: var(--surface-elevated); cursor: pointer; }
.composer-tabs button.active { background: var(--brand-dark); color: #fff; border-color: var(--brand-dark); }

/* ---- context panel ---- */
.context-pane { width: 280px; flex-shrink: 0; border-left: 1px solid var(--border); background: var(--surface-elevated); padding: 16px; font-size: 13px; overflow-y: auto; }
.context-pane h3 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-secondary); margin: 16px 0 8px; }
.context-pane h3:first-child { margin-top: 0; }
/* align-items: flex-start (not the flex default, stretch) + text-align: right on the
   value - without these, a value that's short enough to fit one line looks fine, but a
   long one (e.g. a service name that wraps) has its second line start flush left under
   the label instead of lining up with the first line's right edge - "why alignment bad
   on one [but not the other]". */
.context-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; padding: 4px 0; }
.context-row .label { color: var(--text-secondary); flex-shrink: 0; }
.context-row > span:last-child { text-align: right; }

/* ---- auth ---- */
.auth-shell { min-height: calc(100vh / 0.9); display: flex; align-items: center; justify-content: center; background: var(--brand-dark); position: relative; }
.auth-watermark {
  position: absolute; bottom: 16px; left: 20px;
  font-size: 11.5px; color: rgba(255,255,255,0.35); letter-spacing: 0.02em;
}
.auth-card { background: var(--surface-elevated); color: var(--text-primary); border-radius: 10px; padding: 32px; width: 340px; }
.auth-logo { margin-bottom: 14px; }
.auth-logo img { height: 32px; }
.auth-card h1 { font-size: 15px; font-weight: 500; color: var(--text-secondary); margin: 0 0 20px; }
.auth-card label { display: block; font-size: 12px; color: var(--text-secondary); margin-bottom: 4px; }
.auth-card input { width: 100%; padding: 9px 10px; border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 14px; font-size: 13px; }
.auth-card button { width: 100%; padding: 10px; background: var(--brand-primary); color: #fff; border: none; border-radius: var(--radius); font-size: 13px; cursor: pointer; }

[hidden] { display: none !important; }

/* ---- trace timeline ---- */
.trace-chain { display: flex; flex-direction: column; align-items: flex-start; }
.trace-step { display: flex; align-items: flex-start; gap: 12px; }
.trace-time { width: 64px; flex-shrink: 0; font-size: 11px; color: var(--text-secondary); padding-top: 2px; font-variant-numeric: tabular-nums; }
.trace-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--brand-primary); margin-top: 5px; flex-shrink: 0; }
.trace-body strong { font-size: 13px; text-transform: uppercase; letter-spacing: 0.02em; }
.trace-payload { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }
.trace-arrow { margin-left: 76px; color: var(--border); font-size: 14px; line-height: 1.4; }
code { background: var(--surface); padding: 1px 5px; border-radius: 3px; font-size: 11.5px; }

/* ---- reports module (detailed CSAT/NPS/CES breakdowns) ---- */
.scoring-hint {
  font-size: 11.5px; color: var(--text-secondary); background: var(--surface);
  border-radius: var(--radius); padding: 8px 12px; margin-bottom: 10px; line-height: 1.5;
}
.report-grid { display: grid; grid-template-columns: 1fr 1.3fr; gap: 20px; align-items: center; }
@media (max-width: 700px) { .report-grid { grid-template-columns: 1fr; } }
.report-chart-box { position: relative; height: 200px; }
.report-subheading { font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-secondary); margin: 20px 0 10px; }
.scale-breakdown { margin-top: 14px; display: flex; flex-direction: column; gap: 6px; font-size: 12.5px; color: var(--text-secondary); }
.scale-breakdown .dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
.scale-breakdown strong { margin-left: 6px; color: var(--text-primary); }

/* ---- CSAT/CES mini score cards (visual sibling of the NPS big-number treatment) ---- */
.score-mini-row { display: flex; gap: 20px; }
.score-mini { flex: 1; }
.score-mini-value { font-size: 26px; font-weight: 700; }
.score-mini-value.nps-score-good { color: var(--success); }
.score-mini-value.score-mini-warn { color: var(--warning); }
.score-mini-value.nps-score-bad { color: var(--danger); }
.score-mini-max { font-size: 14px; font-weight: 500; color: var(--text-secondary); }
.score-mini-label { font-size: 11.5px; color: var(--text-secondary); margin-top: 2px; }

/* ---- NPS summary (score + promoter/passive/detractor stacked bar) ---- */
.nps-summary { display: flex; gap: 18px; align-items: center; padding-bottom: 16px; margin-bottom: 14px; border-bottom: 1px solid var(--border); }
.nps-score { font-size: 34px; font-weight: 700; flex-shrink: 0; width: 70px; text-align: center; }
.nps-score-good { color: var(--success); }
.nps-score-bad { color: var(--danger); }
.nps-detail { flex: 1; min-width: 0; }
.nps-label { font-size: 12px; color: var(--text-secondary); margin-bottom: 6px; }
.nps-stack { display: flex; height: 8px; border-radius: 999px; overflow: hidden; background: var(--surface); }
.nps-seg-detractor { background: var(--danger); }
.nps-seg-passive { background: var(--warning); }
.nps-seg-promoter { background: var(--success); }
.nps-legend { display: flex; gap: 14px; margin-top: 8px; font-size: 11px; color: var(--text-secondary); }
.nps-legend .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 4px; }

/* ---- dashboard ---- */
.dashboard-columns { display: grid; grid-template-columns: 1.3fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 900px) { .dashboard-columns { grid-template-columns: 1fr; } }

.metric-row { padding: 12px 0; border-bottom: 1px solid var(--border); }
.metric-row:last-child { border-bottom: none; }
.metric-label { font-size: 12.5px; margin-bottom: 6px; }
.metric-prompt { font-size: 11.5px; color: var(--text-secondary); margin-top: 2px; }
.metric-bar-track { height: 6px; border-radius: 999px; background: var(--surface); overflow: hidden; }
.metric-bar { height: 100%; border-radius: 999px; }
.metric-bar-good { background: var(--success); }
.metric-bar-warn { background: var(--warning); }
.metric-bar-bad { background: var(--danger); }
.metric-value { font-size: 12px; margin-top: 4px; font-weight: 600; }
.metric-count { font-weight: 400; color: var(--text-secondary); }

.activity-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px; }
.activity-row { font-size: 12px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
.activity-row:last-child { border-bottom: none; }
.activity-actor { font-weight: 600; }
.activity-action { color: var(--text-secondary); margin-top: 1px; }

/* ---- donut chart legend (conversation status card) ---- */
.donut-wrap { display: flex; align-items: center; gap: 24px; }
.donut-canvas-box { position: relative; width: 150px; height: 150px; flex-shrink: 0; }
.metrics-canvas-box { position: relative; width: 100%; }
.donut-legend { list-style: none; margin: 0; padding: 0; flex: 1; }
.donut-legend li { display: flex; align-items: center; gap: 8px; font-size: 12.5px; padding: 6px 0; color: var(--text-secondary); }
.donut-legend .dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
.donut-legend strong { margin-left: auto; color: var(--text-primary); font-weight: 700; }

/* ---- activity feed - real timeline component, pulled directly from QuickSMS's own
   theme.css (.timeline/.tl-item/.tl-dot/.tl-content/.tl-date, home.twig's "Account
   Activity" panel) rather than a hand-rolled approximation. Colors swapped for our
   own CSS variables so dark mode still works, geometry/spacing kept identical. ---- */
.timeline { position: relative; border-color: var(--border); padding: 0; margin: 0; max-height: 380px; overflow-y: auto; }
.tl-item { border-radius: 3px; position: relative; display: flex; }
.tl-item > * { padding: 10px; }
.tl-item:last-child .tl-dot:after { display: none; }
.tl-item.active .tl-dot:before { border-color: var(--brand-primary); box-shadow: 0 0 0 4px rgba(68, 139, 255, 0.2); }
.tl-dot { position: relative; border-color: var(--border); width: 30px; flex-shrink: 0; }
.tl-dot:before, .tl-dot:after {
  content: ''; position: absolute; border-color: inherit; border-width: 2px; border-style: solid;
  border-radius: 50%; width: 10px; height: 10px; top: 15px; left: 50%; transform: translateX(-50%);
}
.tl-dot:after {
  width: 0; height: auto; top: 25px; bottom: -15px;
  border-right-width: 0; border-top-width: 0; border-bottom-width: 0; border-radius: 0;
}
.tl-content p { margin: 0 0 2px; font-size: 13px; color: var(--text-primary); }
.tl-content p:last-child { margin-bottom: 0; }
.tl-content a { font-weight: 600; }
.tl-date { font-size: 11px; color: var(--text-secondary); }

/* ---- simulator ---- */
.simulator-result { margin-top: 20px; }
.sim-step { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.sim-step:last-child { border-bottom: none; }
.sim-step .sim-label { color: var(--text-secondary); width: 140px; flex-shrink: 0; }
.sim-banner { background: var(--brand-dark); color: #fff; padding: 8px 14px; border-radius: var(--radius); font-size: 12px; margin-bottom: 16px; text-align: center; }

/* ---- simulator action-chain result (project design Step 4:
   the simulator must show the full ordered chain, not just the winning action) ---- */
.sim-step:has(.sim-action-chain) { align-items: flex-start; }
.sim-action-chain { list-style: decimal; margin: 4px 0 0; padding-left: 18px; }
.sim-action-chain li { margin-bottom: 6px; }
.sim-action-meta { display: block; font-size: 11px; color: var(--text-secondary); margin-top: 2px; }
.sim-payload { margin-top: 6px; }
.sim-payload-label { display: block; font-size: 11px; color: var(--text-secondary); margin-bottom: 2px; }
.sim-payload pre {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 8px 10px; font-size: 11.5px; color: var(--text-primary); overflow-x: auto; margin: 0;
  font-family: 'SFMono-Regular', Consolas, monospace;
}
