/* ═══════════════════════════════════════════════════════════════════════════
   SCAPBOT DESIGN SYSTEM - Clean Minimalism

   COLOR ARCHITECTURE:
   - Single Source of Truth: :root CSS variables below
   - Format: RGB channels (for Tailwind opacity modifier support)
   - Usage:  rgb(var(--color-primary-600))         → solid color
             rgb(var(--color-primary-600) / 0.1)   → with alpha
   - Đổi brand color: chỉ sửa :root → toàn bộ website tự cập nhật
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════════
   SELF-HOSTED FONTS — Inter (avoids Google Fonts render-blocking CSS chain)
   ═══════════════════════════════════════════════════════════════════════════ */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/inter-400.ttf') format('truetype');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/inter-500.ttf') format('truetype');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/inter-600.ttf') format('truetype');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/inter-700.ttf') format('truetype');
}

/* ═══════════════════════════════════════════════════════════════════════════
   NAVIGATION PROGRESS BAR — shows during HTMX boost page transitions
   ═══════════════════════════════════════════════════════════════════════════ */
#nav-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  height: 1px;
  background: linear-gradient(90deg, rgb(99 102 241), rgb(129 140 248));
  z-index: 9999;
  pointer-events: none;
  transition: width 1.2s cubic-bezier(0.1, 0.5, 0.1, 1);
  opacity: 0;
}
.dark #nav-progress {
  background: linear-gradient(90deg, rgb(129 140 248), rgb(165 180 252));
}

/* ═══════════════════════════════════════════════════════════════════════════
   CSS CUSTOM PROPERTIES — :root (Single Source of Truth)
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* Primary — Indigo */
  --color-primary-50: 238 242 255;    /* #eef2ff */
  --color-primary-100: 224 231 255;   /* #e0e7ff */
  --color-primary-200: 199 210 254;   /* #c7d2fe */
  --color-primary-300: 165 180 252;   /* #a5b4fc */
  --color-primary-400: 129 140 248;   /* #818cf8 */
  --color-primary-500: 99 102 241;    /* #6366f1 */
  --color-primary-600: 79 70 229;     /* #4f46e5 */
  --color-primary-700: 67 56 202;     /* #4338ca */
  --color-primary-800: 55 48 163;     /* #3730a3 */
  --color-primary-900: 49 46 129;     /* #312e81 */
  --color-primary-950: 30 27 75;      /* #1e1b4b */

  /* Surface — Slate */
  --color-surface-50: 248 250 252;    /* #f8fafc */
  --color-surface-100: 241 245 249;   /* #f1f5f9 */
  --color-surface-200: 226 232 240;   /* #e2e8f0 */
  --color-surface-300: 203 213 225;   /* #cbd5e1 */
  --color-surface-400: 148 163 184;   /* #94a3b8 */
  --color-surface-500: 100 116 139;   /* #64748b */
  --color-surface-600: 71 85 105;     /* #475569 */
  --color-surface-700: 51 65 85;      /* #334155 */
  --color-surface-800: 30 41 59;      /* #1e293b */
  --color-surface-900: 15 23 42;      /* #0f172a */
  --color-surface-950: 2 6 23;        /* #020617 */

  /* Accent — Emerald */
  --color-accent-50: 236 253 245;     /* #ecfdf5 */
  --color-accent-100: 209 250 229;    /* #d1fae5 */
  --color-accent-200: 167 243 208;    /* #a7f3d0 */
  --color-accent-300: 110 231 183;    /* #6ee7b7 */
  --color-accent-400: 52 211 153;     /* #34d399 */
  --color-accent-500: 16 185 129;     /* #10b981 */
  --color-accent-600: 5 150 105;      /* #059669 */
  --color-accent-700: 4 120 87;       /* #047857 */
  --color-accent-800: 6 95 70;        /* #065f46 */
  --color-accent-900: 6 78 59;        /* #064e3b */

  /* Semantic — Error (Red) */
  --color-error-50: 254 242 242;      /* #fef2f2 */
  --color-error-300: 252 165 165;     /* #fca5a5 */
  --color-error-500: 239 68 68;       /* #ef4444 */
  --color-error-600: 220 38 38;       /* #dc2626 */
  --color-error-700: 185 28 28;       /* #b91c1c */
  --color-error-800: 153 27 27;       /* #991b1b */

  /* Semantic — Warning (Amber) */
  --color-warning-50: 255 251 235;    /* #fffbeb */
  --color-warning-800: 146 64 14;     /* #92400e */

  /* Semantic — Info (Blue) */
  --color-info-50: 239 246 255;       /* #eff6ff */
  --color-info-800: 30 64 175;        /* #1e40af */

  /* Decorative — Orbs */
  --color-violet-500: 139 92 246;     /* #8b5cf6 */
  --color-blue-500: 59 130 246;       /* #3b82f6 */
  --color-cyan-500: 6 182 212;        /* #06b6d4 */
  --color-pink-500: 236 72 153;       /* #ec4899 */
}

/* ═══════════════════════════════════════════════════════════════════════════
   BASE STYLES
   ═══════════════════════════════════════════════════════════════════════════ */

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
  scroll-behavior: smooth;
}

/* Global thin scrollbar — consistent across entire site */
* {
  scrollbar-width: thin;
  scrollbar-color: rgb(var(--color-surface-300)) transparent;
}
*::-webkit-scrollbar { width: 4px; height: 4px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: rgb(var(--color-surface-300));
  border-radius: 9999px;
}
*::-webkit-scrollbar-thumb:hover {
  background: rgb(var(--color-surface-400));
}
.dark *,
.dark {
  scrollbar-color: rgb(var(--color-surface-600)) transparent;
}
.dark *::-webkit-scrollbar-thumb {
  background: rgb(var(--color-surface-600));
}
.dark *::-webkit-scrollbar-thumb:hover {
  background: rgb(var(--color-surface-500));
}

::selection {
  background-color: rgb(var(--color-primary-100));
  color: rgb(var(--color-primary-900));
}

/* Alpine.js cloak — hide elements until Alpine initializes */
[x-cloak] { display: none !important; }


/* Lottie FOUC Prevention — reserve space before web component loads */
dotlottie-player:not(:defined) {
  display: block;
  width: 120px;
  height: 120px;
}

/* Lucide Icon FOUC Prevention — <i> is inline (ignores width/height).
   inline-block makes Tailwind w-4/h-4 reserve 16×16px space BEFORE
   lucide.createIcons() replaces <i> with <svg>. No layout shift. */
i[data-lucide] {
  display: inline-block;
}
/* Nút icon-only (icon là con DUY NHẤT): inline-block vẫn dính line-height
   strut ~24px của button không-flex → nút cao hơn SVG sau-replace 10px →
   co lại khi lucide chạy = giật (đo trang Hiệu suất 2026-07-22, header
   card 61→51px). SVG lucide render display:block → <i> phải block y hệt.
   :only-child nên KHÔNG ảnh hưởng icon nằm cạnh text — an toàn toàn cục,
   code MỚI có nút icon-only là tự được bảo vệ. */
button > i[data-lucide]:only-child {
  display: block;
}

/* ═══════════════════════════════════════════════════════════════════════════
   BUTTONS
   ═══════════════════════════════════════════════════════════════════════════ */

/* Base button styles - shared by all button variants */
.btn,
.btn-primary,
.btn-secondary,
.btn-ghost,
.btn-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.625rem 1rem;
  border-radius: 0.75rem;
  font-weight: 500;
  font-size: 0.875rem;
  transition: all 0.2s ease-out;
  cursor: pointer;
  border: none;
}

.btn:disabled,
.btn-primary:disabled,
.btn-secondary:disabled,
.btn-ghost:disabled,
.btn-danger:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Primary Button - Indigo */
.btn-primary {
  background-color: rgb(var(--color-primary-600));
  color: white;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.btn-primary:hover:not(:disabled) {
  background-color: rgb(var(--color-primary-700));
  box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
}

.btn-primary:active:not(:disabled) {
  background-color: rgb(var(--color-primary-800));
}

/* Secondary Button - Outlined */
.btn-secondary {
  background-color: white;
  color: rgb(var(--color-surface-700));
  border: 1px solid rgb(var(--color-surface-200));
}

.btn-secondary:hover:not(:disabled) {
  background-color: rgb(var(--color-surface-50));
  border-color: rgb(var(--color-surface-300));
}

/* Ghost Button - Transparent */
.btn-ghost {
  background-color: transparent;
  color: rgb(var(--color-surface-600));
}

.btn-ghost:hover:not(:disabled) {
  background-color: rgb(var(--color-surface-100));
  color: rgb(var(--color-surface-900));
}

/* Danger Button - Red */
.btn-danger {
  background-color: rgb(var(--color-error-600));
  color: white;
}

.btn-danger:hover:not(:disabled) {
  background-color: rgb(var(--color-error-700));
}

/* ═══════════════════════════════════════════════════════════════════════════
   INPUTS
   ═══════════════════════════════════════════════════════════════════════════ */

.input {
  width: 100%;
  padding: 0.75rem 1rem;
  background-color: white;
  border: 1px solid rgb(var(--color-surface-200));
  border-radius: 0.75rem;
  color: rgb(var(--color-surface-900));
  font-size: 1rem;
  transition: all 0.2s;
}

.input::placeholder {
  color: rgb(var(--color-surface-400));
}

.input:hover {
  border-color: rgb(var(--color-surface-300));
}

.input:focus {
  outline: none;
  border-color: rgb(var(--color-primary-500));
  box-shadow: 0 0 0 3px rgb(var(--color-primary-500) / 0.1);
}

.input-error {
  border-color: rgb(var(--color-error-300));
}

.input-error:focus {
  border-color: rgb(var(--color-error-500));
  box-shadow: 0 0 0 3px rgb(var(--color-error-500) / 0.1);
}

.input-label {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  color: rgb(var(--color-surface-700));
  margin-bottom: 0.375rem;
}

.input-hint {
  margin-top: 0.375rem;
  font-size: 0.75rem;
  color: rgb(var(--color-surface-500));
}

.input-error-text {
  margin-top: 0.375rem;
  font-size: 0.75rem;
  color: rgb(var(--color-error-600));
}

/* --- Checkbox Custom --- */
.checkbox {
  -webkit-appearance: none;
  appearance: none;
  width: 1.125rem;
  height: 1.125rem;
  border: 1.5px solid rgb(var(--color-surface-300));
  border-radius: 0.375rem;
  background-color: white;
  cursor: pointer;
  transition: all 0.15s ease;
  position: relative;
  flex-shrink: 0;
}

.checkbox:hover {
  border-color: rgb(var(--color-primary-400));
}

.checkbox:focus {
  outline: none;
  border-color: rgb(var(--color-primary-500));
  box-shadow: 0 0 0 3px rgb(var(--color-primary-500) / 0.1);
}

.checkbox:checked {
  background-color: rgb(var(--color-primary-600));
  border-color: rgb(var(--color-primary-600));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='white'%3E%3Cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
}

.checkbox:checked:hover {
  background-color: rgb(var(--color-primary-700));
  border-color: rgb(var(--color-primary-700));
}

/* .radio — anh em với .checkbox (radio hệ thống, thay basic trình duyệt — anh
   bắt 2026-08-13 ở drawer Phân bổ). Tròn + chấm trắng trên nền primary khi chọn.
   KHÔNG hiệu ứng chuyển (luật anh đặt 2026-08-11 — đổi trạng thái TỨC THÌ). */
.radio {
  -webkit-appearance: none;
  appearance: none;
  width: 1.125rem;
  height: 1.125rem;
  border: 1.5px solid rgb(var(--color-surface-300));
  border-radius: 9999px;
  background-color: white;
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
}
.radio:hover { border-color: rgb(var(--color-primary-400)); }
.radio:focus {
  outline: none;
  border-color: rgb(var(--color-primary-500));
  box-shadow: 0 0 0 3px rgb(var(--color-primary-500) / 0.1);
}
.radio:checked {
  background-color: rgb(var(--color-primary-600));
  border-color: rgb(var(--color-primary-600));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='white'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
}
.radio:checked:hover {
  background-color: rgb(var(--color-primary-700));
  border-color: rgb(var(--color-primary-700));
}
.radio:disabled { cursor: not-allowed; opacity: 0.6; }

.checkbox:indeterminate {
  background-color: rgb(var(--color-primary-600));
  border-color: rgb(var(--color-primary-600));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='white'%3E%3Crect x='3' y='7' width='10' height='2' rx='1'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
}

/* ═══════════════════════════════════════════════════════════════════════════
   CARDS
   ═══════════════════════════════════════════════════════════════════════════ */

.card {
  background-color: white;
  border-radius: 1rem;
}

.card-hover {
  transition: all 0.2s;
}

.card-hover:hover {
  box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.1);
  transform: translateY(-2px);
}

/* ═══════════════════════════════════════════════════════════════════════════
   LINKS
   ═══════════════════════════════════════════════════════════════════════════ */

.link {
  color: rgb(var(--color-primary-600));
  text-underline-offset: 2px;
  transition: color 0.15s;
}

.link:hover {
  color: rgb(var(--color-primary-700));
  text-decoration: underline;
}

/* ═══════════════════════════════════════════════════════════════════════════
   LOADING SPINNER
   ═══════════════════════════════════════════════════════════════════════════ */

.spinner {
  border-radius: 50%;
  border: 2px solid rgb(var(--color-surface-200));
  border-top-color: rgb(var(--color-primary-600));
  animation: spin 0.8s linear infinite;
}

.spinner-sm {
  width: 1rem;
  height: 1rem;
}

.spinner-md {
  width: 1.5rem;
  height: 1.5rem;
}

.spinner-lg {
  width: 2rem;
  height: 2rem;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   ALERTS
   ═══════════════════════════════════════════════════════════════════════════ */

.alert,
.alert-info,
.alert-success,
.alert-warning,
.alert-error {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem;
  border-radius: 0.75rem;
}

.alert-info {
  background-color: rgb(var(--color-info-50));
  color: rgb(var(--color-info-800));
}

.alert-success {
  background-color: rgb(var(--color-accent-50));
  color: rgb(var(--color-accent-800));
}

.alert-warning {
  background-color: rgb(var(--color-warning-50));
  color: rgb(var(--color-warning-800));
}

.alert-error {
  background-color: rgb(var(--color-error-50));
  color: rgb(var(--color-error-800));
}

/* ═══════════════════════════════════════════════════════════════════════════
   AUTH PAGES - Modern Tech Background
   ═══════════════════════════════════════════════════════════════════════════ */

.auth-container {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  position: relative;
  overflow: hidden;
  /* Gradient background */
  background: linear-gradient(
    135deg,
    rgb(var(--color-surface-900)) 0%,
    rgb(var(--color-surface-800)) 50%,
    rgb(var(--color-surface-900)) 100%
  );
}

/* Animated mesh gradient overlay */
.auth-container::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background:
    radial-gradient(ellipse at 20% 30%, rgb(var(--color-primary-500) / 0.15) 0%, transparent 50%),
    radial-gradient(ellipse at 80% 70%, rgb(var(--color-violet-500) / 0.15) 0%, transparent 50%),
    radial-gradient(ellipse at 50% 50%, rgb(var(--color-blue-500) / 0.1) 0%, transparent 60%);
  animation: meshMove 15s ease-in-out infinite;
  pointer-events: none;
}

/* Grid pattern overlay */
.auth-container::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image:
    linear-gradient(rgb(var(--color-primary-500) / 0.03) 1px, transparent 1px),
    linear-gradient(90deg, rgb(var(--color-primary-500) / 0.03) 1px, transparent 1px);
  background-size: 50px 50px;
  pointer-events: none;
}

@keyframes meshMove {
  0%, 100% {
    transform: translate(0, 0) scale(1);
  }
  33% {
    transform: translate(-20px, 10px) scale(1.02);
  }
  66% {
    transform: translate(20px, -10px) scale(0.98);
  }
}

/* Floating orbs */
.auth-orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(60px);
  opacity: 0.4;
  animation: float 20s ease-in-out infinite;
  pointer-events: none;
}

.auth-orb-1 {
  width: 400px;
  height: 400px;
  background: linear-gradient(135deg, rgb(var(--color-primary-500)), rgb(var(--color-violet-500)));
  top: -100px;
  left: -100px;
  animation-delay: 0s;
}

.auth-orb-2 {
  width: 300px;
  height: 300px;
  background: linear-gradient(135deg, rgb(var(--color-blue-500)), rgb(var(--color-cyan-500)));
  bottom: -50px;
  right: -50px;
  animation-delay: -5s;
}

.auth-orb-3 {
  width: 200px;
  height: 200px;
  background: linear-gradient(135deg, rgb(var(--color-violet-500)), rgb(var(--color-pink-500)));
  top: 50%;
  right: 20%;
  animation-delay: -10s;
}

@keyframes float {
  0%, 100% {
    transform: translate(0, 0) rotate(0deg);
  }
  25% {
    transform: translate(30px, -30px) rotate(5deg);
  }
  50% {
    transform: translate(-20px, 20px) rotate(-5deg);
  }
  75% {
    transform: translate(20px, 30px) rotate(3deg);
  }
}

/* Auth card - white background */
.auth-card {
  position: relative;
  z-index: 10;
  width: 100%;
  max-width: 28rem;
  background: white;
  border-radius: 1.5rem;
  box-shadow:
    0 25px 50px -12px rgba(0, 0, 0, 0.4),
    0 0 0 1px rgba(255, 255, 255, 0.1);
  padding: 2.5rem;
  animation: cardSlideUp 0.5s ease-out;
}

/* Wider variant for onboarding (glassmorphism, fit-content) */
.centered-card-wide {
  /* 🔴 KHUNG PHẢI SỞ HỮU KÍCH THƯỚC CỦA NÓ (anh chốt 2026-08-11, sau lần
     thứ N modal tạo trợ lý vỡ cỡ). Bản cũ `max-width: fit-content` = kích
     thước MỌC RA từ nội dung — mà nội dung là CHỮ ĐA NGÔN NGỮ đổi độ dài
     theo tiếng, cộng .industry-card aspect-ratio 3/2 (ngang quyết định cao)
     → một câu phụ đề dài là banh modal 876px, thẻ phồng 262×175. Đo bằng
     Playwright mới lộ (soi chữ không thấy — đây là lỗi HÌNH HỌC).
     30rem = lưới 3 cột tự nhiên (26rem) + đệm 2×2rem. Nội dung dài thì
     XUỐNG DÒNG trong khung, không được nong khung. */
  width: 100%;
  max-width: 30rem;
  padding: 2rem;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(24px) saturate(1.4);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.4);
  box-shadow:
    0 25px 60px -12px rgba(0, 0, 0, 0.2),
    0 8px 24px -4px rgba(0, 0, 0, 0.08),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

.dark .centered-card-wide {
  background: rgba(var(--color-surface-800), 0.82);
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow:
    0 25px 60px -12px rgba(0, 0, 0, 0.5),
    0 8px 24px -4px rgba(0, 0, 0, 0.3),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
}

.auth-header {
  text-align: center;
  margin-bottom: 2rem;
}

.auth-title {
  font-size: 1.5rem;
  font-weight: 700;
  color: rgb(var(--color-surface-900));
  margin-bottom: 0.5rem;
  letter-spacing: -0.025em;
}

.auth-subtitle {
  color: rgb(var(--color-surface-500));
  font-size: 0.95rem;
}

@keyframes cardSlideUp {
  from {
    opacity: 0;
    transform: translateY(30px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Dark mode support */
.dark .auth-container {
  background: linear-gradient(
    135deg,
    rgb(var(--color-surface-950)) 0%,
    rgb(var(--color-surface-900)) 50%,
    rgb(var(--color-surface-950)) 100%
  );
}

.dark .auth-card {
  background: rgb(var(--color-surface-800));
}

.dark .auth-title {
  color: rgb(var(--color-surface-50));
}

.dark .auth-subtitle {
  color: rgb(var(--color-surface-400));
}

/* ═══════════════════════════════════════════════════════════════════════════
   OTP INPUT
   ═══════════════════════════════════════════════════════════════════════════ */

.otp-input {
  width: 3rem;
  height: 3.5rem;
  text-align: center;
  font-size: 1.5rem;
  font-weight: 600;
  background: white;
  border: 2px solid rgb(var(--color-surface-200));
  border-radius: 0.75rem;
  color: rgb(var(--color-surface-900));
  transition: all 0.2s;
}

.otp-input:focus {
  outline: none;
  border-color: rgb(var(--color-primary-500));
  box-shadow: 0 0 0 3px rgb(var(--color-primary-500) / 0.1);
}

/* ═══════════════════════════════════════════════════════════════════════════
   INDUSTRY CARDS (Onboarding)
   ═══════════════════════════════════════════════════════════════════════════ */

.industry-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1rem 0.75rem;
  border: 1.5px solid rgb(var(--color-surface-200));
  border-radius: 0.75rem;
  cursor: pointer;
  transition: all 0.2s ease;
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(8px);
  aspect-ratio: 3 / 2;
}

.industry-card:hover {
  border-color: rgb(var(--color-primary-300));
  background: rgba(var(--color-primary-50), 0.8);
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgb(var(--color-primary-500) / 0.1);
}

.industry-card.selected {
  border-color: rgb(var(--color-primary-500));
  background: rgba(var(--color-primary-50), 0.9);
  box-shadow:
    0 0 0 3px rgb(var(--color-primary-500) / 0.15),
    0 4px 16px rgb(var(--color-primary-500) / 0.12);
  transform: translateY(-2px);
}

.industry-card i,
.industry-card svg {
  transition: transform 0.2s ease;
}

.industry-card:hover i,
.industry-card:hover svg,
.industry-card.selected i,
.industry-card.selected svg {
  transform: scale(1.15);
}

/* ═══════════════════════════════════════════════════════════════════════════
   AVATAR PICKER (Onboarding)
   ═══════════════════════════════════════════════════════════════════════════ */

.avatar-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  cursor: pointer;
  transition: all 0.2s;
}

.avatar-option:hover {
  transform: scale(1.05);
}

.avatar-option.selected {
  transform: scale(1.05);
}

/* ═══════════════════════════════════════════════════════════════════════════
   UTILITIES
   ═══════════════════════════════════════════════════════════════════════════ */

.scrollbar-hide {
  -ms-overflow-style: none;
  scrollbar-width: none;
}

.scrollbar-hide::-webkit-scrollbar {
  display: none;
}

/* Hide native number-input spinner arrows (Chrome/Safari/Firefox) */
.no-spinner::-webkit-outer-spin-button,
.no-spinner::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.no-spinner {
  -moz-appearance: textfield;
  appearance: textfield;
}

/* Scrollbar: overlay (no space reserved), thumb hidden until hover, uses design system colors */
.scrollbar-hover {
  overflow-y: auto;
  overflow-y: overlay; /* Chrome/Edge: scrollbar overlays content, no layout shift */
  scrollbar-color: transparent transparent;
}

.scrollbar-hover:hover {
  scrollbar-color: rgb(var(--color-surface-300)) transparent;
}

.dark .scrollbar-hover:hover {
  scrollbar-color: rgb(var(--color-surface-600)) transparent;
}

.scrollbar-hover::-webkit-scrollbar-thumb {
  background: transparent;
}

.scrollbar-hover:hover::-webkit-scrollbar-thumb {
  background: rgb(var(--color-surface-300));
}

.scrollbar-hover:hover::-webkit-scrollbar-thumb:hover {
  background: rgb(var(--color-surface-400));
}

.dark .scrollbar-hover:hover::-webkit-scrollbar-thumb {
  background: rgb(var(--color-surface-600));
}

.dark .scrollbar-hover:hover::-webkit-scrollbar-thumb:hover {
  background: rgb(var(--color-surface-500));
}

/* Chat bubbles */
.chat-bubble-user {
  background: linear-gradient(135deg, rgb(var(--color-primary-500)), rgb(var(--color-primary-600)));
  color: white;
  border-radius: 1.25rem 1.25rem 0.375rem 1.25rem;
  padding: 0.75rem 1rem;
  box-shadow: 0 2px 8px rgb(var(--color-primary-500) / 0.3);
  animation: bubbleIn 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.chat-bubble-assistant {
  background: white;
  color: rgb(var(--color-surface-800));
  border-radius: 1.25rem 1.25rem 1.25rem 0.375rem;
  padding: 0.75rem 1rem;
  border: 1px solid rgb(var(--color-surface-200));
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
  animation: bubbleIn 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

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

/* --- Streaming indicator (typewriter pulse) -------------------------------
 * Small soft dot appended to the streaming assistant bubble. Pulses in
 * place — no harsh blink, no rectangular caret. Matches ChatGPT/Claude.
 */
.chat-stream-cursor {
  display: inline-block;
  width: 0.4em;
  height: 0.4em;
  margin-left: 0.35em;
  vertical-align: 0.05em;
  background: currentColor;
  border-radius: 9999px;
  opacity: 0.6;
  animation: chatStreamPulse 1.2s ease-in-out infinite;
}
@keyframes chatStreamPulse {
  0%, 100% { opacity: 0.25; transform: scale(0.85); }
  50%      { opacity: 0.75; transform: scale(1); }
}
/* Suppress bubble-in animation while streaming to avoid jiggle on each tick */
.chat-bubble-streaming {
  animation: none !important;
}

/* Highlight bubble khi click notification — §4.9.2.
   Khi msg.highlight=true (Alpine reactive), bubble đổi nền indigo nhạt (đồng
   bộ theme primary). Khi flag=false (sau 3s), bubble fade về bg gốc qua
   transition. `!important` cần để override gradient của chat-bubble-user
   và bg trắng của chat-bubble-assistant. */
.chat-bubble-user,
.chat-bubble-assistant {
  transition: background-color 0.6s ease;
}
.chat-bubble-user.msg-highlight,
.chat-bubble-assistant.msg-highlight {
  background-color: rgba(165, 180, 252, 0.35) !important;  /* indigo-300 alpha 0.35 */
  background-image: none !important;
}
.dark .chat-bubble-user.msg-highlight,
.dark .chat-bubble-assistant.msg-highlight {
  background-color: rgba(99, 102, 241, 0.25) !important;  /* indigo-500 alpha 0.25 cho dark mode */
}

/* Assistant card in selector */
.assistant-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.25rem;
  border: 2px solid rgb(var(--color-surface-200));
  border-radius: 1rem;
  cursor: pointer;
  transition: all 0.2s ease-out;
  background: white;
}

.assistant-card:hover {
  border-color: rgb(var(--color-primary-300));
  background: rgb(var(--color-primary-50));
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgb(var(--color-primary-500) / 0.12);
}

.assistant-card.selected {
  border-color: rgb(var(--color-primary-500));
  background: rgb(var(--color-primary-50));
  box-shadow: 0 0 0 3px rgb(var(--color-primary-500) / 0.15);
}

/* Typing dots */
.typing-dot {
  width: 0.375rem;
  height: 0.375rem;
  background: rgb(var(--color-surface-400));
  border-radius: 50%;
  animation: typingBounce 1.4s ease-in-out infinite;
}

.typing-dot:nth-child(2) { animation-delay: 0.15s; }
.typing-dot:nth-child(3) { animation-delay: 0.3s; }

@keyframes typingBounce {
  0%, 60%, 100% { transform: translateY(0); }
  30% { transform: translateY(-6px); }
}

/* Messaging composer — AI mode typing dots (smaller, subtle, inline with text) */
.msg-typing-dot {
  opacity: 0.5;
  animation: msgTypingBounce 1.4s ease-in-out infinite;
}
@keyframes msgTypingBounce {
  0%, 60%, 100% { transform: translateY(0); }
  30% { transform: translateY(-2.5px); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   CHAT DASHBOARD - ChatGPT-style chat inside layouts.User() wrapper
   ═══════════════════════════════════════════════════════════════════════════ */

/* Wrapper: offset user.templ <main> padding, fill full viewport (no topbar) */
.chat-dashboard-wrapper {
  margin: -1rem;
  height: 100vh;
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
}

/* Top Action Bar — flex row, right-aligned */
.chat-top-bar {
  display: flex;
  justify-content: flex-end;
  padding: 0.5rem 0.75rem;
  shrink: 0;
  z-index: 10;
}
/* Mobile: leave space for hamburger menu (fixed top-3 left-3 z-30) */
@media (max-width: 1023px) {
  .chat-top-bar {
    justify-content: space-between;
    padding: 0.625rem 0.75rem;
  }
  .chat-top-bar::before {
    content: '';
    width: 2.5rem; /* spacer for hamburger */
  }
}
.chat-top-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.375rem 0.5rem;
  border-radius: 0.5rem;
  border: none;
  background: transparent;
  color: rgb(var(--color-surface-500));
  cursor: pointer;
  transition: all 0.15s;
}
.chat-top-btn:hover {
  background: rgb(var(--color-surface-100));
  color: rgb(var(--color-surface-700));
}
.chat-top-btn-primary {
  background: rgb(var(--color-surface-100));
  color: rgb(var(--color-surface-700));
}
.chat-top-btn-primary:hover {
  background: rgb(var(--color-surface-200));
}
@media (min-width: 640px) {
  .chat-dashboard-wrapper { margin: -1.5rem; }
}
@media (min-width: 1024px) {
  .chat-dashboard-wrapper { margin: -2rem; }
}

/* Scrollable messages area */
.chat-messages {
  flex: 1;
  overflow-y: auto;
  scroll-behavior: smooth;
  display: flex;
  flex-direction: column;
}

/* chat-messages scrollbar — handled by global * rule */

/* Input area: bottom of chat */
.chat-input-area {
  padding: 0.75rem 1.5rem 1.5rem;
  max-width: 48rem;
  margin: 0 auto;
  width: 100%;
}

/* Textarea container with rounded border */
.chat-textarea-container {
  border: 1px solid rgb(var(--color-surface-300));
  border-radius: 1.5rem;
  background: white;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.04);
  position: relative;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.chat-textarea-container:focus-within {
  border-color: rgb(var(--color-surface-400));
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

/* Auto-resize textarea */
.chat-textarea {
  resize: none;
  border: none;
  outline: none;
  width: 100%;
  min-height: 52px;
  max-height: 200px;
  padding: 0.875rem 1rem 0.25rem;
  font-size: 0.9375rem;
  line-height: 1.5;
  background: transparent;
  font-family: inherit;
  color: rgb(var(--color-surface-900));
}
.chat-textarea::placeholder {
  color: rgb(var(--color-surface-400));
}

/* Toolbar row inside textarea container */
.chat-input-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.375rem 0.5rem 0.5rem;
}

/* Circular send button */
.chat-send-btn {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  cursor: pointer;
  transition: all 0.15s;
  background: rgb(var(--color-surface-200));
  color: rgb(var(--color-surface-400));
}
.chat-send-btn.active {
  background: rgb(var(--color-primary-600));
  color: white;
}
.chat-send-btn.active:hover {
  background: rgb(var(--color-primary-700));
}

/* Attach button */
.chat-attach-btn {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  cursor: pointer;
  transition: all 0.15s;
  background: transparent;
  color: rgb(var(--color-surface-400));
}
.chat-attach-btn:hover {
  background: rgb(var(--color-surface-100));
  color: rgb(var(--color-surface-600));
}

/* Compact Assistant Selector in input toolbar */
.chat-assistant-compact {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.625rem;
  border-radius: 1rem;
  border: 1px solid rgb(var(--color-surface-200));
  background: transparent;
  cursor: pointer;
  transition: all 0.15s;
}
.chat-assistant-compact:hover {
  border-color: rgb(var(--color-surface-300));
  background: rgb(var(--color-surface-50));
}

/* Quick Prompt Cards — large, refined */
.chat-quick-card {
  min-height: 90px;
  border: 1px solid rgb(var(--color-surface-200));
  border-radius: 1rem;
  padding: 1rem 1.25rem;
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 0.2s ease;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  background: white;
  text-align: left;
}
.chat-quick-card:hover {
  border-color: rgb(var(--color-surface-300));
  background: rgb(var(--color-surface-50));
}

/* History Drawer — slides from right */
.chat-history-drawer {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 320px;
  max-width: 100%;
  background: white;
  border-left: 1px solid rgb(var(--color-surface-200));
  box-shadow: -4px 0 24px rgba(0, 0, 0, 0.08);
  z-index: 20;
  display: flex;
  flex-direction: column;
}
@media (max-width: 639px) {
  .chat-history-drawer {
    width: 100%;
    border-left: none;
  }
}

/* History Item */
.chat-history-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.625rem 0.75rem;
  border-radius: 0.5rem;
  cursor: pointer;
  transition: background 0.15s;
}
.chat-history-item:hover {
  background: rgb(var(--color-surface-50));
}

/* ═══════════════════════════════════════════════════════════════════════════
   QR SCANNER EFFECT
   Corner brackets + scan line animation for professional QR display
   ═══════════════════════════════════════════════════════════════════════════ */

.qr-scanner-frame {
  position: relative;
  overflow: hidden;
  border-radius: 0.75rem;
  padding: 2px;
}

/* Corner brackets — 4 L-shaped borders */
.qr-corner {
  position: absolute;
  width: 24px;
  height: 24px;
  z-index: 2;
}
.qr-corner-tl {
  top: 0; left: 0;
  border-top: 3px solid rgb(var(--color-primary-500));
  border-left: 3px solid rgb(var(--color-primary-500));
  border-top-left-radius: 0.75rem;
}
.qr-corner-tr {
  top: 0; right: 0;
  border-top: 3px solid rgb(var(--color-primary-500));
  border-right: 3px solid rgb(var(--color-primary-500));
  border-top-right-radius: 0.75rem;
}
.qr-corner-bl {
  bottom: 0; left: 0;
  border-bottom: 3px solid rgb(var(--color-primary-500));
  border-left: 3px solid rgb(var(--color-primary-500));
  border-bottom-left-radius: 0.75rem;
}
.qr-corner-br {
  bottom: 0; right: 0;
  border-bottom: 3px solid rgb(var(--color-primary-500));
  border-right: 3px solid rgb(var(--color-primary-500));
  border-bottom-right-radius: 0.75rem;
}

/* Scan line — ping-pong sweep (top ↓ bottom ↑ top, infinite loop) */
/* Note: Use hardcoded rgba() inside gradients for browser compatibility */
.qr-scan-line {
  position: absolute;
  top: 4px;
  left: 6px;
  right: 6px;
  height: 4px;
  z-index: 3;
  border-radius: 2px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(99, 102, 241, 0.2) 10%,
    rgba(99, 102, 241, 0.45) 50%,
    rgba(99, 102, 241, 0.2) 90%,
    transparent 100%
  );
  filter: blur(1.5px);
  box-shadow:
    0 0 10px rgba(99, 102, 241, 0.3),
    0 0 30px rgba(99, 102, 241, 0.15),
    0 0 50px rgba(99, 102, 241, 0.05);
  animation: qr-scan 3s ease-in-out infinite;
}

@keyframes qr-scan {
  0%   { top: 4px; }
  50%  { top: calc(100% - 8px); }
  100% { top: 4px; }
}

/* Subtle pulse glow on the frame */
.qr-scanner-frame::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 0.75rem;
  box-shadow: inset 0 0 0 1px rgba(99, 102, 241, 0.15);
  pointer-events: none;
  z-index: 1;
  animation: qr-glow 3s ease-in-out infinite;
}

@keyframes qr-glow {
  0%, 100% { box-shadow: inset 0 0 0 1px rgba(99, 102, 241, 0.1); }
  50% { box-shadow: inset 0 0 0 1px rgba(99, 102, 241, 0.3), 0 0 12px rgba(99, 102, 241, 0.1); }
}

/* Dark mode adjustments */
.dark .qr-corner-tl,
.dark .qr-corner-tr,
.dark .qr-corner-bl,
.dark .qr-corner-br {
  border-color: rgb(var(--color-primary-400));
}

.dark .qr-scan-line {
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(129, 140, 248, 0.25) 10%,
    rgba(129, 140, 248, 0.5) 50%,
    rgba(129, 140, 248, 0.25) 90%,
    transparent 100%
  );
  box-shadow:
    0 0 10px rgba(129, 140, 248, 0.35),
    0 0 30px rgba(129, 140, 248, 0.18),
    0 0 50px rgba(129, 140, 248, 0.08);
}

.dark .qr-scanner-frame::after {
  box-shadow: inset 0 0 0 1px rgba(129, 140, 248, 0.2);
  animation: qr-glow-dark 3s ease-in-out infinite;
}

@keyframes qr-glow-dark {
  0%, 100% { box-shadow: inset 0 0 0 1px rgba(129, 140, 248, 0.1); }
  50% { box-shadow: inset 0 0 0 1px rgba(129, 140, 248, 0.4), 0 0 16px rgba(129, 140, 248, 0.2); }
}

/* QR scan effect: subtle decorative animation — always enabled */

/* ═══════════════════════════════════════════════════════════════════════════
   PERFORMANCE PAGE — 2-panel layout (stats + chat)
   ═══════════════════════════════════════════════════════════════════════════ */

/* Wrapper: same negative-margin technique as messaging-wrapper */
.perf-wrapper {
  position: relative; /* anchor for mobile/tablet chat overlay */
  margin: -4.5rem -1rem -1rem -1rem;
  padding-top: 3.5rem;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
@media (min-width: 640px) {
  .perf-wrapper { margin: -4.5rem -1.5rem -1.5rem -1.5rem; }
}
@media (min-width: 1024px) {
  .perf-wrapper {
    margin: -2rem;
    padding-top: 0;
    flex-direction: row;
  }
}

/* Panel 1 (left): fixed header + scrollable content.
   🔴 min-height:0 BẮT BUỘC: mobile wrapper là flex-COLUMN → chiều cao thành trục
   chính, min-height:auto mặc định làm panel phình bằng nội dung (2309px) thay vì
   bị kẹp 100vh → scroller trong (x-ref perfScroll) tưởng đủ chỗ, cả trang chết
   cuộn (wrapper overflow:hidden cắt cụt). Desktop (row) vô can: chiều cao là trục
   chéo, stretch sẵn. Đo 2026-08-06, lỗi gốc từ 150775b6 (03/03). */
.perf-panel-left {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* ── Bot Chat Panel (shared: admin + assistant) ──
   Drawer overlay toggled via BotLogo button.
   Mobile: full overlay | md: 50% right | lg+: fixed 400px drawer.
   Inline pages (body[data-panel-inline="true"]): seamless at xl+.
   sidebar-content gets xl:pr-[400px] via Alpine (sidebarContentClass) on inline pages. */
.bot-chat-panel {
  position: fixed;
  right: 0;
  top: 3.5rem; /* below mobile topbar h-14 */
  bottom: 0;
  left: 0;
  z-index: 30; /* above main content, below modals (z-50) */

  display: flex;
  flex-direction: column;
  min-width: 0;
  border-left: 1px solid rgb(var(--color-surface-200));
  background-color: white;
  /* overflow: visible (default) — anh chốt 2026-05-27 (Phase 1a): trước
     `overflow-y: auto` ở đây → CSS spec coerce overflow-x cũng clip → tooltip
     bên trên paperclip/smile composer button bị cắt khi vượt viền trái panel.
     Inner messages list (.flex-1.overflow-y-auto in ChatPanel templ) đã tự
     handle scroll → outer overflow-y dư thừa. Tooltip nay extend tự do
     ra ngoài viền chat panel giống Sale messaging. */
}@media (min-width: 768px) {
  .bot-chat-panel {
    left: auto;
    width: 50%;
    box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
  }
}
@media (min-width: 1024px) {
  /* lg: overlay drawer, top:0 (no mobile topbar), width từ CSS var
     (resizable — anh chốt 2026-06-10 phần 2). User kéo handle LEFT edge
     để rộng/hẹp; localStorage 'scapbot_chat_panel_w' persist; CSS var
     '--chat-panel-w' set bởi:
       (a) FOUC inline script shared.templ — TRƯỚC first paint, đọc localStorage
       (b) Alpine startChatResize trong app.js — REAL-TIME khi user kéo
     Fallback 400px khi var chưa set (user chưa từng resize). */
  .bot-chat-panel {
    top: 0;
    width: var(--chat-panel-w, 400px);
  }
  /* Khi user đang kéo handle: tắt MỌI transition trên panel + sidebar-content
     để cảm giác instant theo chuột. user-select:none + cursor:col-resize
     toàn body tránh select text vô tình khi kéo nhanh ra ngoài handle. */
  body.chat-panel-resizing {
    user-select: none;
    cursor: col-resize;
  }
  body.chat-panel-resizing .bot-chat-panel,
  body.chat-panel-resizing .sidebar-content {
    transition: none !important;
  }
}
@media (min-width: 1280px) {
  /* xl+ inline pages (Hiệu suất/Dashboard): sidebar-content padding-right
     khớp width chat panel — sync 1 CSS var → resize 1 nơi → apply mọi nơi.
     Trước đây hardcode Tailwind 'xl:pr-[400px]' qua Alpine :class — đã bỏ
     để CSS rule này control thay. Marker class 'chat-panel-shown' ở body
     do app.js (assistantChatPanel x-effect) toggle theo botPanel state. */
  body[data-panel-inline="true"].chat-panel-shown .sidebar-content {
    padding-right: var(--chat-panel-w, 400px);
  }
}
@media (min-width: 1280px) {
  /* xl+, inline pages (performance/dashboard): seamless panel (no shadow, no overlay z-index) */
  body[data-panel-inline="true"] .bot-chat-panel {
    z-index: auto;
    box-shadow: none;
  }
}
.dark .bot-chat-panel {
  border-left-color: rgb(var(--color-surface-700));
  background-color: rgb(var(--color-surface-800));
}
/* Script editor overlay = z-[60]. Chat panel mặc định z-30 sẽ bị che khi editor mở.
   Bump chat panel lên z-[70] để click logo trong toolbar editor mở chat panel
   hiển thị đè lên editor. Reuse cơ chế botPanel toggle như mọi page khác.
   2026-06-06 anh báo: widget-drawer của chat panel (Thông báo, Lịch sử) + backdrop
   z-50 cũng bị editor che → bump cùng class. */
/* Chat panel: perf LUÔN hiện mặc định, editor LUÔN ẩn mặc định, KHÔNG co kéo (anh chốt 2026-07-03).
   Gốc: botPanel gánh 2 vai (hiển thị chat + padding perf page) → toggle nó = co kéo. Fix: GIỮ botPanel
   nguyên (perf padding không đổi → không co kéo), ẩn chat trong editor bằng CSS display (không đụng state).
   Logo trong editor bật lại qua cờ body.se-chat-open → chat đè LÊN editor (z-70 = điểm cao nhất). Đóng
   editor: bỏ .script-editor-open → chat về theo botPanel (perf luôn hiện), padding nguyên → mượt. */
body.script-editor-open .bot-chat-panel {
  z-index: 70;
  display: none !important;
}
body.script-editor-open.se-chat-open .bot-chat-panel {
  display: flex !important;
}
/* "Editor như 1 TRANG phủ lên" (anh chốt 2026-07-03): editor overlay opaque z-60 fixed inset-0 phủ LÊN TRÊN
   mọi thứ, KHÔNG ẩn/đụng gì bên dưới → đóng editor thì mọi thứ Y NGUYÊN (không "thò thụt"). z-80 dưới đây NÂNG
   CHAT drawer (Thông báo/Lịch sử mở TỪ editor) lên trên editor. LOẠI TRỪ settings drawer (:not(.settings-drawer-panel)):
   .widget-drawer z-index tự nhiên = 50 (< editor z-60) → settings drawer giữ z-50 → editor TỰ CHE, KHÔNG cần hạ
   (z-40 kỳ cục) hay nâng editor (kéo theo cả chat). Trước: settings drawer dính z-80 oan → đè editor → phải đóng
   nó (slide). Giờ hết. wfOpenEditor KHÔNG settingsOpen=false → drawer mở nguyên trạng, editor phủ lên; đóng editor
   → drawer còn y nguyên. Chat drawer (.widget-drawer, không có .settings-drawer-panel) vẫn z-80 (đè editor). */
body.script-editor-open .widget-drawer:not(.settings-drawer-panel) {
  z-index: 80;
}
body.script-editor-open .chat-drawer-backdrop {
  z-index: 75;
}
/* KHÔNG ẩn perf page (.perf-panel-left) nữa — trước ẩn bằng visibility gây "thò thụt" (biến mất khi mở /
   hiện lại khi đóng). Editor overlay opaque z-60 fixed inset-0 TỰ CHE perf page → không cần đụng. "Editor như
   1 trang phủ lên": giữ nguyên mọi thành phần bên dưới, chỉ phủ editor lên trên. */
/* Lock body scroll when overlay is open (prevent scroll bleed-through) */
body.chat-panel-open,
body.modal-open {
  overflow: hidden !important;
  touch-action: none;
}
/* FOUC: hidden before body.ready */
body:not(.ready) .bot-chat-panel {
  display: none !important;
}
/* KHIÊN LOAD ĐẦU (cùng gia đình khiên htmx-nav bên dưới): trước body.ready
   (double-rAF sau mount) mọi transition trong content OFF — phần tử render
   lần đầu không chạy vệt transition/@starting-style (bubbles lịch sử chat,
   nút màu động...). ready gắn xong → hiệu ứng runtime hoạt động bình thường. */
body:not(.ready) #main-content *,
body:not(.ready) #main-content *::before,
body:not(.ready) #main-content *::after {
  transition: none !important;
}
/* Instant layout changes during HTMX page navigation.
   Prevents chat panel slide-out and sidebar-content padding animation
   when navigating between pages. Manual BotLogo toggle keeps animation.
   Scoped by data-panel-inline: only hide/reset when LEAVING inline page.
   When staying on inline page, panel + padding are preserved (no flash). */
body.htmx-nav .bot-chat-panel,
body.htmx-nav .sidebar-content {
  transition-duration: 0s !important;
}
/* TẮT MỌI TRANSITION trong content mới suốt khoảnh khắc chuyển trang hx-boost
   (fix HỆ THỐNG — anh truy "có triệt để cho code sau không" 2026-07-22).
   Đo được: phần tử mới swap vào có first-paint CHƯA-KỊP-STYLE (màu đen default
   dù class/biến CSS/html.dark đều đủ) → transition-colors lấy ĐEN làm giá trị
   xuất phát → vệt đen→màu-đích 150ms trên MỌI phần tử có transition (3 nút
   panel, 3 tab conv list, ...). Tắt transition trong lúc nav: đổi màu tức thì
   cùng frame recalc = 0 vệt; htmx-nav gỡ sau settle+2 rAF nên hover/click tay
   sau đó vẫn mượt bình thường. Phần tử MỚI viết sau này tự được bảo vệ. */
body.htmx-nav #main-content *,
body.htmx-nav #main-content *::before,
body.htmx-nav #main-content *::after {
  transition: none !important;
}
/* ĐỔI THEME TỨC THÌ (anh chốt 2026-08-11): setTheme() gắn html.theme-instant
   trong 2 frame quanh lúc toggle class `dark` → MỌI phần tử đổi màu cùng một
   frame, không vùng nào phai chậm hơn vùng nào. Không có nó: body 0ms nhưng
   sidebar-panel 300ms (transition-all của animation thu gọn) + menu item 150ms
   (transition-colors) → sidebar "giật sáng" so với body. Cùng họ cơ chế với
   body.htmx-nav ngay trên. */
html.theme-instant *,
html.theme-instant *::before,
html.theme-instant *::after {
  transition: none !important;
}
body.htmx-nav:not([data-panel-inline="true"]) .bot-chat-panel {
  visibility: hidden !important;
}
body.htmx-nav:not([data-panel-inline="true"]) .sidebar-content {
  padding-right: 0 !important;
}
@media (min-width: 1280px) {
  /* Going TO inline page (CHỈ xl+): force panel visible + positioned instantly (no Alpine
     x-transition). display:block !important overrides Alpine's inline display:none from x-show.
     transform+opacity override x-transition:enter-start classes (translate-x-full opacity-0).
     🔴 PHẢI nằm trong media xl (khớp JS botPanel = isInlinePage && innerWidth>=1280):
     dưới xl trạng thái ĐÍCH là ẨN (panel = overlay toàn màn, chỉ mở bằng BotLogo) — rule
     này từng đứng ngoài media → hx-boost quay VỀ Hiệu suất trên mobile ép panel display:block
     đè display:none của Alpine suốt cửa sổ htmx-nav (~150-580ms, đo rAF 2026-08-06) = nháy
     full-screen rồi mới ẩn. */
  body.htmx-nav[data-panel-inline="true"] .bot-chat-panel {
    display: block !important;
    transform: translateX(0) !important;
    opacity: 1 !important;
  }
  /* Going TO inline page: set padding instantly (no Alpine delay).
     Dùng CSS var --chat-panel-w để khớp user-resized width (anh chốt 2026-06-10 phần 2). */
  body.htmx-nav[data-panel-inline="true"] .sidebar-content {
    padding-right: var(--chat-panel-w, 400px) !important;
  }
  /* Inline pages xl+: push sidebar-content right before ready (prevent layout shift) */
  body:not(.ready)[data-panel-inline="true"] .sidebar-content {
    padding-right: var(--chat-panel-w, 400px);
  }
}

/* Panel 2 (right): chat — drawer overlay until xl, inline from xl+.
   Same responsive pattern as messaging info panel (cssMsgInfoPanel).
   Mobile: full overlay | md: half overlay | lg: half overlay, top:0 | xl: inline 400px */
.perf-panel-right {
  /* Mobile: absolute overlay below mobile topbar (h-14 = 3.5rem) */
  position: absolute;
  left: 0;
  right: 0;
  top: 3.5rem;
  bottom: 0;
  z-index: 20;

  flex-shrink: 0;
  border-left: 1px solid rgb(var(--color-surface-200));
  display: flex;
  flex-direction: column;
  min-width: 0;
  background-color: white;
  overflow-y: auto;
}
@media (min-width: 768px) {
  /* Tablet: overlay right half, with shadow */
  .perf-panel-right {
    left: auto;
    width: 50%;
    /* shadow-xl — exact Tailwind standard, same as messaging info panel (md:shadow-xl) */
    box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
  }
}
@media (min-width: 1024px) {
  /* lg: still overlay drawer, but top:0 (no mobile topbar at lg+) */
  .perf-panel-right { top: 0; }
}
@media (min-width: 1280px) {
  /* xl: inline panel in flex-row layout */
  .perf-panel-right {
    position: relative;
    inset: auto;
    width: 400px;
    z-index: auto;
    box-shadow: none;
    overflow-y: hidden;
  }
}
/* ═══════ FOUC Prevention ═══════
   NOTE: Universal rule `body:not(.ready) #main-content { opacity: 0 }` lives in
   shared.templ inline <style> — NOT here. Inline = available before first paint on all devices.
   Only layout-specific rules below (need media queries + multiple properties). */

/* Performance chat panel (BotChatPanel): layout-specific rules.
   Panel uses absolute on <xl, relative on xl+. CSS must match initial Alpine state
   (botPanel: innerWidth >= 1280) to prevent layout shift on handoff. */
body:not(.ready) .perf-panel-right {
  display: none !important;
}
@media (min-width: 1280px) {
  body:not(.ready) .perf-panel-right {
    display: flex !important;
    position: relative;
    inset: auto;
    width: 400px;
    z-index: auto;
    box-shadow: none;
    overflow-y: hidden;
  }
}
.dark .perf-panel-right {
  border-left-color: rgb(var(--color-surface-700));
  background-color: rgb(var(--color-surface-800));
}

/* ── Widget Dashboard (Panel 1) ── */

/* Icon lucide CHƯA-replace phải chiếm chỗ Y HỆT SVG sau-replace ngay frame đầu.
   Đường hx-boost: trang paint TRƯỚC khi lucide.createIcons() chạy — thẻ <i>
   rỗng là inline nên w-3.5, h-3.5 không ăn; kể cả inline-block (rule chung
   phía trên) vẫn dính line-height strut 24px của button không-flex (đo
   computed từng frame 2026-07-22: button 36px → SVG block thay vào co 26px
   → header card 61 co 51 → MỌI card co 10px đồng loạt = giật). SVG lucide
   render display:block → i phải block y hệt: button 6+14+6=26 từ frame đầu.
   LƯU Ý: comment CSS cấm chứa chuỗi sao-gạch (đóng comment sớm → browser
   drop rule kế tiếp im lặng — chính bug này đã nuốt rule bên dưới 1 vòng). */
.widget-card i[data-lucide] {
  display: block;
}

.widget-card {
  background: white;
  border-radius: 1rem;
  border: 1px solid rgb(var(--color-surface-200));
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
}
.dark .widget-card {
  background: rgb(var(--color-surface-800));
  border-color: rgb(var(--color-surface-700));
}
.widget-card:hover {
  border-color: rgb(var(--color-surface-300));
}
.dark .widget-card:hover {
  border-color: rgb(var(--color-surface-600));
}
.widget-actions {
  opacity: 0;
  transition: opacity 0.15s ease;
}
.widget-card:hover .widget-actions,
.widget-card:focus-within .widget-actions {
  opacity: 1;
}
.widget-handle {
  cursor: grab;
  touch-action: none;
}
.widget-handle:active {
  cursor: grabbing;
}
.widget-dragging {
  opacity: 0.5;
  transform: scale(0.98);
}
.widget-drag-over {
  border-color: rgb(var(--color-primary-400)) !important;
  box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15);
}
.dark .widget-drag-over {
  border-color: rgb(var(--color-primary-500)) !important;
  box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
}

/* ══════════════════════════════════════════════════════════════════════
   Onboarding Tour — spotlight overlay, dialogue bubble, typewriter
   ══════════════════════════════════════════════════════════════════════ */

/* Overlay: full-screen dark backdrop. JS cuts a rounded-rect hole
   via clip-path to spotlight the target element. */
.tour-overlay {
  position: fixed;
  inset: 0;
  z-index: 9998;
  background: rgba(0, 0, 0, 0.85);
  transition: opacity 0.4s ease, clip-path 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: auto;
}
.dark .tour-overlay {
  background: rgba(0, 0, 0, 0.92);
}

/* Driver.js: hide popover, keep only spotlight overlay */
.tour-driver-hidden {
  display: none !important;
}
/* Ensure tour float buttons are clickable above Driver.js */
.tour-float .tour-bubble,
.tour-float .tour-btn-sm-skip,
.tour-float .tour-btn-sm-next {
  pointer-events: auto !important;
  position: relative;
  z-index: 1100000001;
}

/* Force tour robot size — dotlottie-player defaults to 120px */
.tour-robot-size {
  width: 190px !important;
  height: 190px !important;
}

/* Floating container for robot + bubble (must be above Driver.js overlay) */
.tour-float {
  position: fixed;
  z-index: 1100000000;
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
  transition: top 0.5s cubic-bezier(0.4, 0, 0.2, 1),
              left 0.5s cubic-bezier(0.4, 0, 0.2, 1),
              transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.tour-float > * {
  pointer-events: auto;
}

/* Dialogue bubble */
.tour-bubble {
  max-width: 340px;
  padding: 16px 20px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.3);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12),
              0 2px 8px rgba(0, 0, 0, 0.06);
  transform-origin: bottom center;
}
.dark .tour-bubble {
  background: rgba(30, 30, 46, 0.9);
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4),
              0 0 16px rgba(99, 102, 241, 0.08);
}

/* Bubble tail (triangle pointing down to robot) */
.tour-bubble::after {
  content: '';
  position: absolute;
  bottom: -8px;
  left: 50%;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 9px solid transparent;
  border-right: 9px solid transparent;
  border-top: 9px solid rgba(255, 255, 255, 0.85);
}
.dark .tour-bubble::after {
  border-top-color: rgba(30, 30, 46, 0.9);
}

/* Typewriter cursor */
.tour-cursor {
  display: inline-block;
  width: 2px;
  height: 1.1em;
  background: rgb(var(--color-primary-500));
  margin-left: 2px;
  vertical-align: text-bottom;
  animation: tour-blink 0.6s step-end infinite;
}
@keyframes tour-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

/* Bubble entrance animation */
@keyframes tour-bubble-in {
  0% { opacity: 0; transform: scale(0.3) translateY(10px); }
  60% { transform: scale(1.04) translateY(-2px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
.tour-bubble-enter {
  animation: tour-bubble-in 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

/* Robot entrance animation */
@keyframes tour-robot-in {
  0% { opacity: 0; transform: translateY(20px) scale(0.8); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
.tour-robot-enter {
  animation: tour-robot-in 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}

/* Typing dots animation */
.tour-dots span {
  display: inline-block;
  width: 6px;
  height: 6px;
  margin: 0 2px;
  border-radius: 50%;
  background: rgb(var(--color-primary-400));
  animation: tour-dot-bounce 1.2s ease-in-out infinite;
}
.tour-dots span:nth-child(2) { animation-delay: 0.15s; }
.tour-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes tour-dot-bounce {
  0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
  40% { transform: translateY(-6px); opacity: 1; }
}

/* Tour progress dots */
.tour-progress {
  display: flex;
  gap: 6px;
  justify-content: center;
  margin-top: 12px;
}
.tour-progress-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.15);
  transition: all 0.3s ease;
}
.dark .tour-progress-dot {
  background: rgba(255, 255, 255, 0.15);
}
.tour-progress-dot.active {
  width: 18px;
  border-radius: 3px;
  background: rgb(var(--color-primary-500));
}

/* Tour action buttons */
.tour-btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 18px;
  font-size: 0.8125rem;
  font-weight: 500;
  border-radius: 10px;
  background: rgb(var(--color-primary-600));
  color: white;
  transition: background 0.2s ease;
  cursor: pointer;
  border: none;
}
.tour-btn-primary:hover {
  background: rgb(var(--color-primary-700));
}
/* Small skip button (inside bubble, next to "next") */
.tour-btn-sm-skip {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  font-size: 0.6875rem;
  font-weight: 500;
  border-radius: 8px;
  color: rgb(var(--color-surface-500));
  background: rgb(var(--color-surface-100));
  border: none;
  cursor: pointer;
  transition: all 0.2s;
}
.tour-btn-sm-skip:hover {
  background: rgb(var(--color-surface-200));
  color: rgb(var(--color-surface-700));
}
.dark .tour-btn-sm-skip {
  color: rgb(var(--color-surface-400));
  background: rgb(var(--color-surface-700));
}
.dark .tour-btn-sm-skip:hover {
  background: rgb(var(--color-surface-600));
  color: rgb(var(--color-surface-200));
}

/* Small next button (inside bubble) */
.tour-btn-sm-next {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  font-size: 0.6875rem;
  font-weight: 500;
  border-radius: 8px;
  background: rgb(var(--color-primary-600));
  color: white;
  border: none;
  cursor: pointer;
  transition: background 0.2s;
}
.tour-btn-sm-next:hover {
  background: rgb(var(--color-primary-700));
}

.tour-btn-skip {
  padding: 6px 12px;
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.6);
  background: none;
  border: none;
  cursor: pointer;
  transition: color 0.2s;
}
.tour-btn-skip:hover {
  color: rgba(255, 255, 255, 0.9);
}
.dark .tour-btn-skip {
  color: rgba(255, 255, 255, 0.4);
}
.dark .tour-btn-skip:hover {
  color: rgba(255, 255, 255, 0.7);
}

/* Tour text styling */
.tour-text {
  font-size: 1rem;
  line-height: 1.6;
  color: rgb(var(--color-surface-800));
  white-space: pre-line;
  font-style: italic;
  letter-spacing: 0.01em;
}
.dark .tour-text {
  color: rgb(var(--color-surface-100));
}
.tour-text .tour-highlight {
  color: rgb(var(--color-primary-600));
  font-weight: 600;
}
.dark .tour-text .tour-highlight {
  color: rgb(var(--color-primary-400));
}

/* ── Skill Cards (hover border + icon animation) ── */

.skill-card {
  transition: border-color 0.2s ease, opacity 0.3s ease, filter 0.3s ease, box-shadow 0.2s ease;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.04);
}
.skill-card:hover {
  box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.08);
}
.dark .skill-card {
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.15);
}
.dark .skill-card:hover {
  box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.25);
}
.skill-card:hover .skill-icon {
  transform: scale(1.25) rotate(-12deg);
}
.skill-icon {
  transition: transform 0.3s ease;
}

/* Hover border per skill color */
.skill-card[data-skill="sale"]:hover { border-color: rgb(var(--color-primary-300)); }
.skill-card[data-skill="marketing"]:hover { border-color: #c4b5fd; }
.skill-card[data-skill="tax"]:hover { border-color: #fcd34d; }
.skill-card[data-skill="research"]:hover { border-color: #7dd3fc; }

.dark .skill-card[data-skill="sale"]:hover { border-color: rgb(var(--color-primary-500) / 0.5); }
.dark .skill-card[data-skill="marketing"]:hover { border-color: rgba(139, 92, 246, 0.5); }
.dark .skill-card[data-skill="tax"]:hover { border-color: rgba(245, 158, 11, 0.5); }
.dark .skill-card[data-skill="research"]:hover { border-color: rgba(14, 165, 233, 0.5); }

/* Widget Drawer — slides from right, matches Panel 2 width */
.widget-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  width: 100%;
  max-width: 360px;
  background: white;
  border-left: 1px solid rgb(var(--color-surface-200));
  box-shadow: -4px 0 24px rgba(0, 0, 0, 0.08);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
@media (min-width: 1280px) {
  .widget-drawer { max-width: 400px; }
}
.dark .widget-drawer {
  background: rgb(var(--color-surface-800));
  border-left-color: rgb(var(--color-surface-700));
  box-shadow: -4px 0 24px rgba(0, 0, 0, 0.3);
}

/* Picker modal catalog item */
.widget-picker-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem;
  border-radius: 0.75rem;
  cursor: pointer;
  transition: background-color 0.15s ease;
}
.widget-picker-item:hover {
  background: rgb(var(--color-surface-50));
}
.dark .widget-picker-item:hover {
  background: rgb(var(--color-surface-700));
}
.widget-picker-item.is-added {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ═══════════════════════════════════════════════════════════════════════════
   MESSAGING INBOX — 3-panel layout inside assistant layout
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════════
   DOCUMENTS V2 — 2-panel layout + detail drawer
   ═══════════════════════════════════════════════════════════════════════════ */

/* Wrapper: same negative-margin full-viewport technique as messaging */
.docs-v2-wrapper {
  position: relative;
  margin: -4.5rem -1rem -1rem -1rem;
  padding-top: 3.5rem;
  height: 100vh;
  display: flex;
  overflow: clip;
}
@media (min-width: 640px) {
  .docs-v2-wrapper { margin: -4.5rem -1.5rem -1.5rem -1.5rem; }
}
@media (min-width: 1024px) {
  .docs-v2-wrapper { margin: -2rem; padding-top: 0; }
}

/* FOUC prevention during Alpine init (hx-boost navigation).
   is-init: added in Alpine init(), removed after double-rAF.
   CHỈ chặn transition — rule opacity-hide panel con ĐÃ GỠ (2026-07-22):
   thời chưa-có-skeleton nó che FOUC, giờ panel SSR + skeleton đúng-số-dòng
   đã đúng ngay từ HTML → giấu rồi hiện bụp sau double-rAF CHÍNH LÀ nháy
   (anh bắt: nháy cả reload lẫn hx-boost). */
.docs-v2-wrapper.is-init * {
  transition-duration: 0s !important;
}

/* Scrollable areas */
.docs-v2-folder-scroll { overflow-y: auto; }
.docs-v2-file-scroll { overflow-y: auto; }

/* Detail drawer: always overlay (same pattern as perf-panel-right) */
.docs-v2-detail-panel {
  position: absolute;
  right: 0;
  top: 3.5rem;
  bottom: 0;
  width: 100%;
  z-index: 20;
  border-left: 1px solid rgb(var(--color-surface-200));
  display: flex;
  flex-direction: column;
  min-width: 0;
  background-color: white;
  overflow-y: auto;
}
@media (min-width: 768px) {
  .docs-v2-detail-panel {
    width: 50%;
    box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
  }
}
@media (min-width: 1024px) {
  .docs-v2-detail-panel { top: 0; }
}
@media (min-width: 1280px) {
  .docs-v2-detail-panel { width: 400px; }
}
.dark .docs-v2-detail-panel {
  border-left-color: rgb(var(--color-surface-700));
  background-color: rgb(var(--color-surface-800));
}

/* Wide modifier: wider drawer (Add Document) — only overrides width */
@media (min-width: 768px) {
  .docs-v2-detail-panel.docs-v2-wide { width: 75%; }
}
@media (min-width: 1280px) {
  .docs-v2-detail-panel.docs-v2-wide { width: 800px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PRODUCTS V2 — 2-panel layout (same pattern as Documents V2)
   ═══════════════════════════════════════════════════════════════════════════ */
.prod-v2-wrapper {
  position: relative;
  margin: -4.5rem -1rem -1rem -1rem;
  padding-top: 3.5rem;
  height: 100vh;
  display: flex;
  overflow: clip;
}
@media (min-width: 768px) {
  .prod-v2-wrapper { margin: -4.5rem -1.5rem -1.5rem -1.5rem; }
}
@media (min-width: 1024px) {
  .prod-v2-wrapper { margin: -2rem; padding-top: 0; }
}
/* FOUC prevention — CHỈ chặn transition; opacity-hide đã gỡ (xem ghi chú
   khối docs-v2 phía trên — panel SSR + skeleton đúng từ đầu, giấu = nháy). */
.prod-v2-wrapper.is-init * {
  transition-duration: 0s !important;
}
/* Scrollable areas */
.prod-v2-cat-scroll { overflow-y: auto; }
.prod-v2-product-scroll { overflow-y: auto; }

/* ═══════════════════════════════════════════════════════════════════════════
   ADMIN USERS — Full-width wrapper (same negative-margin pattern)
   ═══════════════════════════════════════════════════════════════════════════ */
.users-wrapper {
  position: relative;
  margin: -4.5rem -1rem -1rem -1rem;
  padding-top: 3.5rem;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: clip;
}
@media (min-width: 640px) {
  .users-wrapper { margin: -4.5rem -1.5rem -1.5rem -1.5rem; }
}
@media (min-width: 1024px) {
  .users-wrapper { margin: -2rem; padding-top: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LAYOUT ENGINE — Standard wrapper (all skill pages use this)
   ═══════════════════════════════════════════════════════════════════════════ */
.layout-wrapper {
  position: relative;
  margin: -4.5rem -1rem -1rem -1rem;
  padding-top: 3.5rem;
  height: 100vh;
  display: flex;
  overflow: clip;
}
.layout-wrapper.flex-col {
  flex-direction: column;
}
@media (min-width: 640px) {
  .layout-wrapper { margin: -4.5rem -1.5rem -1.5rem -1.5rem; }
}
@media (min-width: 1024px) {
  .layout-wrapper { margin: -2rem; padding-top: 0; }
}
.layout-wrapper.is-init * { transition-duration: 0s !important; }
body:not(.ready) .layout-wrapper > div:first-child > * { opacity: 0; }


/* ═══════════════════════════════════════════════════════════════════════════
   ADMIN ASSISTANTS — Full-width wrapper (legacy, kept for backward compat)
   ═══════════════════════════════════════════════════════════════════════════ */
.assistants-wrapper {
  position: relative;
  margin: -4.5rem -1rem -1rem -1rem;
  padding-top: 3.5rem;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: clip;
}
@media (min-width: 640px) {
  .assistants-wrapper { margin: -4.5rem -1.5rem -1.5rem -1.5rem; }
}
@media (min-width: 1024px) {
  .assistants-wrapper { margin: -2rem; padding-top: 0; }
}

/* ADMIN BILLING — uses .layout-wrapper from engine (no custom CSS needed) */

/* ADMIN LOGS — uses .layout-wrapper from engine (no custom CSS needed) */

/* ADMIN SETTINGS — uses .layout-wrapper from engine (no custom CSS needed) */

/* ═══════════════════════════════════════════════════════════════════════════
   CUSTOMERS V2 — Kanban/Table view toggle (uses .layout-wrapper from engine)
   ═══════════════════════════════════════════════════════════════════════════ */
/* View toggle via <html> class — scoped to customers page via #custKanbanView / #custTableView */
html.cust-kanban .layout-wrapper #custTableView { display: none; }
html:not(.cust-kanban) .layout-wrapper #custKanbanView { display: none; }
/* FOUC: style toggle buttons via CSS (before Alpine init) to prevent flash on HTMX swap */
.cust-view-toggle .cust-btn-kanban,
.cust-view-toggle .cust-btn-list { transition: none; }
html.cust-kanban .cust-view-toggle .cust-btn-kanban { background: rgb(var(--color-primary-50)); color: rgb(var(--color-primary-600)); }
html.cust-kanban .cust-view-toggle .cust-btn-list { background: transparent; color: rgb(var(--color-surface-500)); }
html:not(.cust-kanban) .cust-view-toggle .cust-btn-list { background: rgb(var(--color-primary-50)); color: rgb(var(--color-primary-600)); }
html:not(.cust-kanban) .cust-view-toggle .cust-btn-kanban { background: transparent; color: rgb(var(--color-surface-500)); }
/* 🔴 Dark: cả `dark` lẫn `cust-kanban` đều là class TRÊN CHÍNH <html> → selector
   phải là html.dark.cust-kanban (nối liền). Bản cũ viết `.dark html...` (dark là
   tổ tiên của html — vô nghĩa) + `html... .dark ...` (dark ở phần tử con — không
   có) → dark mode không rule nào khớp, nút active rơi về nền primary-50 SÁNG
   (anh bắt 2026-08-06 "dark mode nền trắng"). */
html.dark.cust-kanban .cust-view-toggle .cust-btn-kanban { background: rgb(var(--color-primary-900) / 0.3); color: rgb(var(--color-primary-400)); }
html.dark:not(.cust-kanban) .cust-view-toggle .cust-btn-list { background: rgb(var(--color-primary-900) / 0.3); color: rgb(var(--color-primary-400)); }
/* Hover nút KHÔNG active — đồng bộ mẫu toggle lưới/danh sách trang Sản phẩm
   (hover:bg-surface-50 / dark:hover:bg-surface-700) */
html.cust-kanban .cust-view-toggle .cust-btn-list:hover,
html:not(.cust-kanban) .cust-view-toggle .cust-btn-kanban:hover { background: rgb(var(--color-surface-50)); }
html.dark.cust-kanban .cust-view-toggle .cust-btn-list:hover,
html.dark:not(.cust-kanban) .cust-view-toggle .cust-btn-kanban:hover { background: rgb(var(--color-surface-700)); }
/* Products: FOUC prevention — default list view before Alpine init */
html.prod-list #prodCardGrid { display: none; }
html:not(.prod-list) #prodListTable { display: none; }
/* Documents: FOUC prevention — default list view before Alpine init */
html.doc-list #cardGrid { display: none; }
html:not(.doc-list) #listTable { display: none; }
/* Kanban column scroll */
.cust-kanban-scroll {
  overflow-y: auto;
  -ms-overflow-style: none;
  scrollbar-width: none;
}
.cust-kanban-scroll::-webkit-scrollbar {
  display: none;
}
/* Thanh cuộn OVERLAY sidebar hội thoại (messaging.templ + helpers.go convSb) —
   style ĐỒNG NHẤT .scrollbar-hover của khung chat: 4px, bo tròn, surface-300
   (light) / surface-600 (dark), đậm hơn khi hover thumb. */
.msg-conv-sb-thumb {
  position: absolute;
  right: 0;
  width: 4px;
  border-radius: 9999px;
  background: rgb(var(--color-surface-300));
}
.msg-conv-sb-thumb:hover {
  background: rgb(var(--color-surface-400));
}
.dark .msg-conv-sb-thumb {
  background: rgb(var(--color-surface-600));
}
.dark .msg-conv-sb-thumb:hover {
  background: rgb(var(--color-surface-500));
}
/* Nền conv ĐƯỢC CHỌN trên trang Nhắn tin (class cssMsgConvActive — messaging_styles.go).
   SÁNG: giữ NGUYÊN primary-50/50 cũ = rgb(var(--color-primary-50)/.5) (#eef2ff @50%).
   TỐI: rgba(92,86,255,.2) = indigo #5C56FF @20% (anh chốt 2026-07-25) — nổi rõ trên nền
   tối, khác màu cũ primary-900/20 chìm nghỉm. Tự chứa trong app.css (không phụ thuộc
   class Tailwind trong build). !important thắng hover:bg cssMsgConvItem; .dark (0,2,0)
   thắng bản light (0,1,0) khi ở tối. */
.msg-conv-active-bg {
  background-color: rgba(227, 233, 252, 0.5) !important; /* anh chốt 2026-08-13 */
}
.dark .msg-conv-active-bg {
  background-color: rgba(92, 86, 255, 0.2) !important;
}
/* ═══════════════════════════════════════════════════════════════════════════
   STYLE DANH SÁCH HỘI THOẠI — 4 phương án, anh chốt 2026-08-14
   ───────────────────────────────────────────────────────────────────────────
   Cột trái mang class .conv-style-{id} (SSR, messaging.templ) ∈
     sang   — Sang trọng: nền tím nhạt + viền trái primary, item sát mép (MẶC ĐỊNH,
              = utility trong cssMsgConvItem/.msg-conv-active-bg ở trên, KHÔNG cần luật)
     tinh   — Tinh tế (Telegram): ô chọn xanh #3390ec chữ trắng, bo 1.5rem, lề .5rem
     truyen — Truyền thông (Zalo): ô chọn xanh nhạt #e5f1ff chữ đậm, bo 10px
     noi    — Nổi bật: nền chuyển sắc tím nhạt + VIỀN 2px primary, bo 1rem

   Bất biến của cả 4: NỀN CỘT LUÔN TRẮNG · HOVER LUÔN XÁM NHẠT (giữ nguyên
   utility hover:bg-surface-50 trong cssMsgConvItem) — chỉ ô ĐANG CHỌN đổi.

   Specificity: .conv-style-X .msg-conv-item.msg-conv-active-bg = (0,3,0) thắng
   .msg-conv-active-bg (0,1,0) và .dark .msg-conv-active-bg (0,2,0); vẫn cần
   !important vì luật bị đè có !important. Hook class: msg-conv-item ·
   msg-conv-av · msg-conv-tag · js-conv-name · js-conv-time · js-conv-preview ·
   js-conv-unread (server + SSE client dựng cùng bộ hook).
   ─────────────────────────────────────────────────────────────────────────── */

/* HOVER hàng hội thoại — XÁM TRUNG TÍNH (anh chốt 2026-08-14: "chuyển tông xám
   chứ đừng giữ tông xanh"). Bảng `surface` của hệ là SLATE nên surface-50/100
   đều ám xanh; đây là chỗ DUY NHẤT cần xám thuần nên khai riêng thay vì đẻ thêm
   một thang màu mới. Đậm hơn một bậc so với bản cũ (#f8fafc → #f1f1f2).
   Áp cho CẢ 4 phong cách (luật gắn vào .msg-conv-item, không gắn .conv-style-*).
   Hàng ĐANG CHỌN không bị đè: .msg-conv-active-bg có !important. */
.msg-conv-item:hover {
  background-color: #f1f1f2;
}
.dark .msg-conv-item:hover {
  background-color: #33333a;
}

/* Thanh minh hoạ chữ trong PREVIEW drawer "Phong cách nhắn tin" (chat_style.templ).
   Tô bằng currentColor để tự ăn theo màu chữ của từng style, nhưng ĐỘ ĐỤC tách ra
   đây: nền trắng thì rất nhạt (chữ đen gắt — anh chốt 2026-08-14), còn hàng chọn
   của Tinh tế (chữ trắng trên nền xanh) phải đục hơn mới thấy. */
.cs-bar {
  background-color: currentColor;
}
.cs-bar-name {
  opacity: 0.4;
}
.cs-bar-prev {
  opacity: 0.24;
}
.conv-style-tinh .msg-conv-active-bg .cs-bar-name {
  opacity: 0.92;
}
.conv-style-tinh .msg-conv-active-bg .cs-bar-prev {
  opacity: 0.6;
}

/* Khoảng đệm ĐẦU DANH SÁCH (anh chốt 2026-08-14): 3 style có lề ngang .5rem thì ô
   đầu tiên dính sát viền dưới thanh TAB PHÂN LOẠI → thêm đệm trên cho cân với lề
   ngang. Sang trọng giữ nguyên (item sát mép, không có lề nên không lệch). */
.conv-style-tinh .msg-conv-listwrap,
.conv-style-truyen .msg-conv-listwrap,
.conv-style-noi .msg-conv-listwrap {
  padding-top: 0.5rem;
}

/* Hình dạng ô (3 style có lề/bo) — áp cho MỌI item để hover bo giống hàng chọn,
   chiều cao hàng không nhảy khi chọn. */
.conv-style-tinh .msg-conv-item,
.conv-style-truyen .msg-conv-item,
.conv-style-noi .msg-conv-item {
  margin: 0 0.5rem 2px;
  border-left-width: 0;
}
.conv-style-tinh .msg-conv-item {
  padding: 0.625rem 0.75rem;
  border-radius: 14px; /* anh chốt 2026-08-14 (1.5rem → 14px) */
}
.conv-style-truyen .msg-conv-item {
  padding: 0.625rem 0.75rem;
  border-radius: 10px;
}
/* Nổi bật: viền 2px trong suốt sẵn ở MỌI item → chọn chỉ đổi màu viền.
   Khe dọc 6px (thay 2px) để chừa chỗ cho LỚP RING mờ của hàng chọn (bên dưới) —
   ring vẽ bằng box-shadow nên không chiếm layout, phải tự chừa. */
.conv-style-noi .msg-conv-item {
  padding: 0.5rem 0.625rem;
  border: 2px solid transparent;
  border-radius: 1rem;
  margin-bottom: 6px;
}

/* ── Tinh tế — ô chọn xanh đặc, thành phần bên trong đảo TRẮNG ────────────── */
.conv-style-tinh .msg-conv-item.msg-conv-active-bg {
  background-color: #3390ec !important;
}
.dark .conv-style-tinh .msg-conv-item.msg-conv-active-bg {
  background-color: #1f6cb0 !important;
}
.conv-style-tinh .msg-conv-active-bg .js-conv-name {
  color: #fff;
}
.conv-style-tinh .msg-conv-active-bg .js-conv-time {
  color: rgba(255, 255, 255, 0.75);
}
.conv-style-tinh .msg-conv-active-bg .js-conv-preview {
  color: rgba(255, 255, 255, 0.85);
}
/* (0,4,0) — thắng cả .dark .js-conv-preview.js-conv-cust (0,3,0) */
.conv-style-tinh .msg-conv-active-bg .js-conv-preview.js-conv-cust {
  color: #fff;
}
.conv-style-tinh .msg-conv-active-bg .js-conv-unread {
  background-color: #fff;
  color: #3390ec;
}
.conv-style-tinh .msg-conv-active-bg .msg-conv-av {
  background-color: rgba(255, 255, 255, 0.24);
}
/* Chữ cái đầu VÀ icon (lucide render ra <svg stroke="currentColor">, không phải <i>
   — anh phát hiện 2026-08-14: icon bot của hội thoại "system" giữ màu indigo nên
   chìm trên nền xanh) → ép trắng cả ba dạng. */
.conv-style-tinh .msg-conv-active-bg .msg-conv-av span,
.conv-style-tinh .msg-conv-active-bg .msg-conv-av i,
.conv-style-tinh .msg-conv-active-bg .msg-conv-av svg {
  color: #fff;
}
.conv-style-tinh .msg-conv-active-bg .msg-conv-tag {
  background-color: rgba(255, 255, 255, 0.22);
  color: #fff;
}

/* ── Truyền thông — ô chọn xanh nhạt, chữ giữ tông đậm ────────────────────── */
.conv-style-truyen .msg-conv-item.msg-conv-active-bg {
  background-color: #e5f1ff !important;
}
.dark .conv-style-truyen .msg-conv-item.msg-conv-active-bg {
  background-color: #1b3b60 !important;
}
.conv-style-truyen .msg-conv-active-bg .msg-conv-tag {
  background-color: #fff;
  color: #0b4f9e;
}
.dark .conv-style-truyen .msg-conv-active-bg .msg-conv-tag {
  background-color: rgba(255, 255, 255, 0.14);
  color: #bcd8fb;
}
.conv-style-truyen .msg-conv-active-bg .msg-conv-av {
  background-color: #cfe0fb;
}
.dark .conv-style-truyen .msg-conv-active-bg .msg-conv-av {
  background-color: #1e3f66;
}

/* ── Nổi bật — nền chuyển sắc tím nhạt + viền 2px primary ─────────────────── */
/* Viền + LỚP RING MỜ bao ngoài — mượn đúng ngữ pháp focus ring của ô nhập
   (`ring-2 ring-primary-500/20`), anh chốt 2026-08-14. Ring = box-shadow nên
   KHÔNG chiếm layout (đã chừa khe 6px ở trên). */
.conv-style-noi .msg-conv-item.msg-conv-active-bg {
  background-color: transparent !important;
  background-image: linear-gradient(90deg, #dcd9ff 0%, #efeeff 100%) !important;
  border-color: rgb(var(--color-primary-300)); /* nhạt hơn 2 bậc — anh chốt 2026-08-14 */
  /* Cạnh TRÁI phải khai riêng + !important: cssMsgConvActive mang utility
     `!border-l-primary-500` (border-left-color … !important) nên nếu không đè,
     riêng cạnh trái vẫn đậm trong khi 3 cạnh kia đã nhạt. */
  border-left-color: rgb(var(--color-primary-300)) !important;
  box-shadow: 0 0 0 3px rgb(var(--color-primary-500) / 0.2);
}
.dark .conv-style-noi .msg-conv-item.msg-conv-active-bg {
  background-image: linear-gradient(90deg, #231f45 0%, #2c2860 100%) !important;
  border-color: #5b56a8;
  border-left-color: #5b56a8 !important;
  box-shadow: 0 0 0 3px rgb(127 122 255 / 0.28);
}
/* Tin cuối ở hàng chọn: đậm hơn 1 bậc (surface-500 → 700) cho nổi trên nền chuyển
   sắc tím nhạt — anh chốt 2026-08-14. :not(.js-conv-cust) để KHÔNG đè màu indigo
   đậm của "tin cuối là của khách". */
.conv-style-noi .msg-conv-active-bg .js-conv-preview:not(.js-conv-cust) {
  color: rgb(var(--color-surface-700));
}
.dark .conv-style-noi .msg-conv-active-bg .js-conv-preview:not(.js-conv-cust) {
  color: rgb(var(--color-surface-200));
}
.conv-style-noi .msg-conv-active-bg .msg-conv-tag {
  background-color: #fff;
  color: rgb(var(--color-primary-700));
}
.dark .conv-style-noi .msg-conv-active-bg .msg-conv-tag {
  background-color: rgba(255, 255, 255, 0.14);
  color: rgb(var(--color-primary-200));
}
.conv-style-noi .msg-conv-active-bg .msg-conv-av {
  background-color: #d5d2ff;
}
.dark .conv-style-noi .msg-conv-active-bg .msg-conv-av {
  background-color: #38337a;
}

/* Sang trọng — avatar CHỮ CÁI của hàng ĐANG CHỌN đổi nền #dfe4f3 (anh chốt 18/8,
   RGB 223·228·243): nền hàng chọn của sang là tím primary nhạt, avatar cũng
   primary-50 → gần như tàng hình. CHỈ avatar, CHỈ style sang, cùng khuôn active-
   only với 3 style kia. Dark giữ nguyên giá trị utility cũ (primary-900/30). */
.conv-style-sang .msg-conv-active-bg .msg-conv-av {
  background-color: #dfe4f3;
}
.dark .conv-style-sang .msg-conv-active-bg .msg-conv-av {
  background-color: rgb(49 46 129 / 0.3);
}

/* Preview "tin cuối" khi tin CUỐI là của KHÁCH (chờ mình trả lời) → đậm + indigo
   (anh chốt 2026-07-25). Marker .js-conv-cust toggle bởi server (msgConvListItem —
   LastFromCustomer) + SSE live (helpers.go theo data.direction). Specificity 0,2,0
   (light) / 0,3,0 (dark) thắng text-surface-500/400 — khỏi !important. */
.js-conv-preview.js-conv-cust {
  font-weight: 600;
  color: rgb(var(--color-primary-600));
}
.dark .js-conv-preview.js-conv-cust {
  color: rgb(var(--color-primary-400));
}
/* Mobile kanban toolbar: exact 50/50 via CSS Grid (4 direct children → 2x2) */
@media (max-width: 639px) {
  .cust-kanban-toolbar {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
  }
  .cust-kanban-toolbar .cust-bulk-action button,
  .cust-kanban-toolbar button.cust-bulk-action {
    width: 100%;
  }
}
/* Desktop: flex single row, ActionDropdown auto-width */
@media (min-width: 640px) {
  .cust-kanban-toolbar {
    display: flex !important;
  }
  .cust-bulk-action > div > button {
    width: auto !important;
  }
}
/* Mobile kanban: full-screen snap carousel per stage */
@media (max-width: 639px) {
  .cust-kanban-board {
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding: 0.75rem 1rem !important;
    scroll-padding-inline: 1rem;
    overflow-y: hidden !important;
  }
  .cust-kanban-inner {
    gap: 1rem !important;
    align-items: flex-start !important;
  }
  .cust-kanban-col {
    width: calc(100vw - 2rem) !important;
    flex: 0 0 auto !important;
    max-height: 100% !important;
    scroll-snap-align: start;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PRODUCT CREATE — full-width form (negative margin to break out of <main>)
   ═══════════════════════════════════════════════════════════════════════════ */
.prod-create-wrapper {
  margin: -4.5rem -1rem -1rem -1rem;
  padding-top: 3.5rem;
  min-height: 100vh;
  /* Use overflow-x: clip instead of hidden so the wrapper doesn't become a scroll container.
     With `hidden`, browsers force overflow-y to `auto` (CSS spec quirk), creating a 2nd scroll
     parallel to body whenever a dropdown/popover extends below visible area. */
  overflow-x: clip;
}
@media (min-width: 640px) {
  .prod-create-wrapper { margin: -4.5rem -1.5rem -1.5rem -1.5rem; }
}
@media (min-width: 1024px) {
  .prod-create-wrapper { margin: -2rem; padding-top: 0; }
}

/* WYSIWYG rich editor: heading, list, link styles inside contenteditable */
[x-ref="richEditor"] h1 { font-size: 1.5rem; font-weight: 700; line-height: 1.3; margin: 0.5em 0; }
[x-ref="richEditor"] h2 { font-size: 1.25rem; font-weight: 600; line-height: 1.3; margin: 0.5em 0; }
[x-ref="richEditor"] h3 { font-size: 1.1rem; font-weight: 600; line-height: 1.3; margin: 0.4em 0; }
[x-ref="richEditor"] h4 { font-size: 1rem; font-weight: 600; line-height: 1.3; margin: 0.4em 0; }
[x-ref="richEditor"] ul { list-style: disc; padding-left: 1.5em; margin: 0.4em 0; }
[x-ref="richEditor"] ol { list-style: decimal; padding-left: 1.5em; margin: 0.4em 0; }
[x-ref="richEditor"] li { margin: 0.15em 0; }
[x-ref="richEditor"] a { color: rgb(var(--color-primary-600)); text-decoration: underline; }
[x-ref="richEditor"] s, [x-ref="richEditor"] strike { text-decoration: line-through; }
[x-ref="richEditor"] blockquote { border-left: 3px solid rgb(var(--color-primary-400)); padding-left: 0.75em; margin: 0.5em 0; color: rgb(var(--color-surface-500)); font-style: italic; }
[x-ref="richEditor"] code { background: rgb(var(--color-surface-100)); padding: 0.15em 0.35em; border-radius: 0.25em; font-family: ui-monospace, monospace; font-size: 0.85em; }
[x-ref="richEditor"] pre { background: rgb(var(--color-surface-100)); padding: 0.75em 1em; border-radius: 0.5em; margin: 0.5em 0; overflow-x: auto; }
[x-ref="richEditor"] pre code { background: none; padding: 0; font-size: 0.85em; }
[x-ref="richEditor"] table { border-collapse: collapse; width: 100%; margin: 0.5em 0; }
[x-ref="richEditor"] th, [x-ref="richEditor"] td { border: 1px solid rgb(var(--color-surface-200)); padding: 0.4em 0.6em; text-align: left; font-size: 0.875em; }
[x-ref="richEditor"] th { background: rgb(var(--color-surface-50)); font-weight: 600; }
[x-ref="richEditor"] hr { border: none; border-top: 2px solid rgb(var(--color-surface-200)); margin: 0.75em 0; }

/* Markdown preview prose styles */
.md-preview { font-size: 0.875rem; line-height: 1.6; color: inherit; }
.md-preview h1 { font-size: 1.5rem; font-weight: 700; margin: 1rem 0 0.5rem; }
.md-preview h2 { font-size: 1.25rem; font-weight: 600; margin: 0.75rem 0 0.5rem; }
.md-preview h3 { font-size: 1.1rem; font-weight: 600; margin: 0.5rem 0 0.25rem; }
.md-preview p { margin: 0.5rem 0; }
.md-preview ul, .md-preview ol { padding-left: 1.5rem; margin: 0.5rem 0; }
.md-preview ul { list-style: disc; }
.md-preview ol { list-style: decimal; }
.md-preview li { margin: 0.15rem 0; }
.md-preview blockquote { border-left: 3px solid rgb(var(--color-surface-300)); padding-left: 1rem; opacity: 0.8; margin: 0.5rem 0; }
.md-preview code { background: rgb(var(--color-surface-100)); padding: 0.15rem 0.35rem; border-radius: 0.25rem; font-size: 0.85em; }
.md-preview pre { background: rgb(var(--color-surface-100)); padding: 1rem; border-radius: 0.5rem; overflow-x: auto; margin: 0.5rem 0; }
.md-preview pre code { background: none; padding: 0; }
.md-preview hr { border-color: rgb(var(--color-surface-200)); margin: 1rem 0; }
.md-preview a { color: rgb(var(--color-primary-600)); text-decoration: underline; }
.md-preview strong { font-weight: 600; }
.md-preview em { font-style: italic; }
.md-preview del { text-decoration: line-through; }
.md-preview table { width: 100%; border-collapse: collapse; margin: 0.5rem 0; font-size: 0.85em; }
.md-preview th, .md-preview td { border: 1px solid rgb(var(--color-surface-200)); padding: 0.4rem 0.75rem; text-align: left; }
.md-preview th { background: rgb(var(--color-surface-50)); font-weight: 600; }
.dark .md-preview code { background: rgb(var(--color-surface-700)); }
.dark .md-preview pre { background: rgb(var(--color-surface-700)); }
.dark .md-preview blockquote { border-left-color: rgb(var(--color-surface-500)); }
.dark .md-preview hr { border-color: rgb(var(--color-surface-600)); }
.dark .md-preview th, .dark .md-preview td { border-color: rgb(var(--color-surface-600)); }
.dark .md-preview th { background: rgb(var(--color-surface-700)); }

/* ═══════════════════════════════════════════════════════════════════════════ */

/* Wrapper: negate assistant.templ <main> padding exactly, fill full viewport.
   Mobile:  p-4 pt-[4.5rem]     → margin: -4.5rem -1rem -1rem -1rem
   SM:      sm:p-6 sm:pt-[4.5rem] → margin: -4.5rem -1.5rem -1.5rem -1.5rem
   LG:      lg:p-8 (all 2rem)   → margin: -2rem
   Mobile topbar (h-14 = 3.5rem, lg:hidden) → padding-top: 3.5rem on mobile/tablet */
.messaging-wrapper {
  position: relative; /* anchor for tablet info overlay */
  margin: -4.5rem -1rem -1rem -1rem;
  padding-top: 3.5rem; /* space for fixed MobileTopbar h-14 */
  height: 100vh;
  display: flex;
  overflow: hidden;
}
@media (min-width: 640px) {
  .messaging-wrapper { margin: -4.5rem -1.5rem -1.5rem -1.5rem; }
}
@media (min-width: 1024px) {
  .messaging-wrapper { margin: -2rem; padding-top: 0; }
}

/* Conversation list + Messages area — only overflow, scrollbar styling is global */
.msg-conv-scroll { overflow-y: auto; }
.msg-messages-area { overflow-y: auto; }

/* Message bubbles — 4 sender types.
   Hiệu ứng bubble-mới = @starting-style (xem block bên dưới) — chỉ phần tử
   MỚI SINH; load đầu + hx-boost bị khiên body:not(.ready)/body.htmx-nav tắt. */
/* Noto Color Emoji — self-hosted Google emoji font (Apache 2.0).
   Loaded LAZILY via JS after window.load (see messaging.templ).
   Reason: 10.6 MB ttf. Even with font-display: swap, the browser still
   counts the font fetch toward the document load event, so the tab
   spinner stays active for the whole download on incognito / cold-cache
   reloads. Postponing the @font-face registration until after load
   keeps the spinner off while letting emoji upgrade to the brand glyph
   as soon as the font arrives. System emoji renders in the meantime —
   .messaging-wrapper falls back to platform-native (Apple Color Emoji,
   Segoe UI Emoji, etc.) for the few seconds before swap. */

/* Apply emoji font to entire messaging page (bubbles + sidebar preview).
   System emoji fonts are listed BEFORE Noto so first paint uses them
   without waiting for the lazy fetch. */
.messaging-wrapper {
  font-family: 'Inter', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji', system-ui, sans-serif;
}


.msg-bubble-customer {
  background: var(--msg-in-bg);
  color: var(--msg-in-fg);
  /* Mặc định = tin lẻ/đầu cụm: đuôi ↓ ở góc dưới cạnh gửi (trái) */
  border-radius: var(--r-big) var(--r-big) var(--r-big) var(--r-tail);
}

/* Bong bóng "phía mình" — 1 MÀU CHUẨN ScapBot (màu chủ shop = primary tím).
   Gộp AI + nhân viên + chủ shop về CÙNG nền để đồng bộ; cơ chế "chọn style"
   sau này chỉ cần đổi 4 biến --msg-out-* (anh chốt 2026-08-13).
   Trước đây: agent = xanh dương riêng, owner/ai = tím → cùng phía mình mà 2 màu. */
:root {
  --msg-out-bg: linear-gradient(135deg, rgb(var(--color-primary-500)), rgb(var(--color-primary-600)));
  --msg-out-fg: #fff;
  --msg-out-shadow: 0 2px 8px rgb(var(--color-primary-500) / 0.25);
  /* Màu bong bóng KHÁCH — biến để style "chọn giao diện" đổi (mặc định = sang) */
  --msg-in-bg: rgb(var(--color-surface-200));
  --msg-in-fg: rgb(var(--color-surface-800));
  /* Bo góc theo cụm — mô hình A ScapBot (anh chốt 2026-08-13: 26 / 5 / 5).
     Style "chọn giao diện" sau này chỉ set lại 3 biến này trong .chat-style-X:
     style vuông (Zalo/Subiz) đặt cả 3 bằng nhau → tự mất đuôi cụm. */
  --r-big: 26px;   /* góc thường (cạnh xa người gửi) */
  --r-tail: 5px;   /* góc ĐUÔI nhọn (đầu ↓ / cuối ↑) */
  --r-join: 5px;   /* góc NỐI ở tin giữa cụm */
  /* Mặc định = tin lẻ/đầu cụm: đuôi ↓ ở góc dưới cạnh gửi (phải) */
  --msg-out-radius: var(--r-big) var(--r-big) var(--r-tail) var(--r-big);
}
.msg-bubble-ai,
.msg-bubble-agent,
.msg-bubble-owner {
  background: var(--msg-out-bg);
  color: var(--msg-out-fg);
  /* Dùng --r-* TRỰC TIẾP (KHÔNG qua --msg-out-radius): biến trung gian khai ở
     :root nên resolve sẵn giá trị mặc định → override per-style .chat-style-X
     không ăn (đã đo: Truyền thống ra 26px thay vì 8px). anh bắt 2026-08-13. */
  border-radius: var(--r-big) var(--r-big) var(--r-tail) var(--r-big);
  box-shadow: var(--msg-out-shadow);
}
/* ── Bo góc theo cụm — override cho tin GIỮA và CUỐI (2 class → thắng radius
   mặc định 1-class ở trên). single/first giữ mặc định (đuôi ↓). Cạnh suy từ
   class màu: phía mình (ai/agent/owner) đuôi cạnh PHẢI, khách cạnh TRÁI. ── */
.msg-bubble-ai.msg-pos-middle,
.msg-bubble-agent.msg-pos-middle,
.msg-bubble-owner.msg-pos-middle {
  border-radius: var(--r-big) var(--r-join) var(--r-join) var(--r-big);
}
.msg-bubble-ai.msg-pos-last,
.msg-bubble-agent.msg-pos-last,
.msg-bubble-owner.msg-pos-last {
  border-radius: var(--r-big) var(--r-tail) var(--r-big) var(--r-big);
}
.msg-bubble-customer.msg-pos-middle {
  border-radius: var(--r-join) var(--r-big) var(--r-big) var(--r-join);
}
.msg-bubble-customer.msg-pos-last {
  border-radius: var(--r-tail) var(--r-big) var(--r-big) var(--r-big);
}

/* ── Ảnh ĐƠN trong lịch sử chat — MỘT luật khổ ảnh cho CẢ BA bộ dựng bong bóng
   (templ dựng lịch sử · renderContentHTML dựng tin vừa đến · _appendBubble
   dựng tin mình vừa gửi). Ba bộ trước đây mỗi bộ khai một kiểu, cả ba đều
   object-cover nên CẮT MẤT RÌA ảnh; riêng bộ lịch sử còn ép khung cứng
   240×256 → ảnh nào cũng thành vuông (anh bắt 2026-08-13).

   Bề NGANG cố định 240px: giữ layout khỏi giật khi ảnh tải xong — lý do khung
   cứng ra đời (đo CLS 0.0067×2 lúc chuyển hội thoại, 2026-07-22).
   Bề CAO tự theo ảnh: mới thấy trọn nội dung. Không chốt cao được vì CSDL
   KHÔNG lưu kích thước ảnh (đo prod 13/8: 0/50.205 tin có width·height).
   min-height giữ chỗ lúc chưa tải; max-height chặn ảnh dọc quá khổ, và
   contain (KHÔNG phải cover) nên ảnh dọc bị chặn vẫn hiện trọn, chỉ hụt
   hai bên. ── */
/* ── Khối TRÍCH DẪN: vạch trái CHỈ đè màu ở ba phong cách anh báo 14/8.
   Hình thức chung (dày 2px, lề, độ mờ) vẫn do bộ dựng khai như cũ — anh chốt
   giữ nguyên, chỉ tối ưu đúng chỗ khó nhìn.

   Ba phong cách này có nền bong bóng nuốt mất vạch tím:
     · Sang trọng — bong bóng mình là gradient tím, vạch tím lẫn vào nền
     · Hiện đại   — bong bóng mình xanh #0084ff
     · Cổ điển    — bong bóng mình trắng, vạch tím nhạt quá
   currentColor = màu chữ của CHÍNH bong bóng đó → luôn tương phản, đúng cả
   sáng lẫn tối mà không phải khai từng trường hợp.

   🔴 PHA LOÃNG 28%, đừng dùng currentColor nguyên chất (anh bắt 14/8):
   để nguyên thì trên bong bóng KHÁCH (nền nhạt, chữ gần đen) vạch thành đen
   đặc, nặng hơn cả chữ trong bong bóng. Anh chốt mức 28%: giữ đúng tông của
   từng bong bóng nhưng chỉ gợi ý nhẹ, không hút mắt khỏi nội dung.

   Trình duyệt không hiểu color-mix thì cả dòng này vô hiệu và vạch quay về
   màu tím do bộ dựng khai — lùi về đúng bản cũ, không vỡ. ── */
.chat-style-sang .msg-quote,
.chat-style-hien .msg-quote,
.chat-style-co .msg-quote { border-left-color: color-mix(in srgb, currentColor 28%, transparent); }

.msg-img-single {
  width: 240px;
  max-width: 100%;
  height: auto;
  min-height: 120px;
  max-height: 320px;
  object-fit: contain;
  border-radius: 0.75rem;
}

/* ── Avatar theo cụm (Phase B) — avatar render ở hàng ĐẦU/CUỐI cụm (hàng giữa
   thuần = spacer). Hiện/ẩn theo vị trí + căn theo style. MẶC ĐỊNH: avatar ĐẦU
   cụm, căn đáy (Sang trọng). visibility:hidden GIỮ chỗ w-7 → cột thẳng hàng. ── */
.msg-av-last:not(.msg-av-first) { visibility: hidden; } /* mặc định: chỉ hiện đầu cụm */
.msg-av-middle { visibility: hidden; }
/* Phong cách (Telegram): avatar ở CUỐI cụm, sát góc đuôi vát */
.chat-style-phong .msg-av-first:not(.msg-av-last) { visibility: hidden; }
.chat-style-phong .msg-av-last { visibility: visible; }
/* ── Truyền thống / Hiện đại / Cổ điển — anh chốt 2026-08-15 ──────────────
   ① Avatar căn với MÉP TRÊN bong bóng (trước đây căn giữa).
      Sang trọng + Tinh tế GIỮ NGUYÊN căn đáy, anh dặn rõ không đụng.
   ② Tên người gửi (nhóm Zalo) chuyển lên ĐẦU bong bóng, trước đây ở dưới.

   Làm bằng thứ tự flex chứ không đổi mã dựng: cùng một khối HTML phục vụ cả 5
   phong cách, đổi mã dựng là phải khai điều kiện phong cách trong cả BA bộ
   dựng bong bóng — đúng thứ vừa gây ra mấy vụ lệch nhau. CSS đè theo phong
   cách thì chỉ một nhà. ── */
.chat-style-truyen .msg-row,
.chat-style-hien .msg-row,
.chat-style-co .msg-row { align-items: flex-start; }

/* Bong bóng thành cột để đảo được thứ tự nhãn tên lên trên.

   🔴 CHỈ ĐỔI KHỐI NÀO THẬT SỰ CHỨA NHÃN TÊN — anh bắt 2026-08-15: tên tệp tràn
   ra ngoài bong bóng ở ba phong cách này.

   Bản đầu nhắm thẳng `.msg-bubble-customer`, nhưng đó là lớp MÀU chứ không phải
   lớp "bong bóng chữ": nó nằm trên cả THẺ TỆP, thẻ tin thoại, thẻ bình chọn —
   những thứ vốn xếp NGANG (biểu tượng · tên · dung lượng). Bẻ chúng thành cột
   là làm hỏng khung, và tên tệp mất chỗ cắt.

   `:has()` cho phép hỏi đúng câu cần hỏi: "khối này có nhãn tên bên trong
   không". Trình duyệt cũ không hiểu `:has()` thì luật này bị bỏ qua — nhãn tên
   nằm dưới như trước, xấu hơn tí nhưng KHÔNG vỡ khung. Hỏng an toàn. */
.chat-style-truyen .msg-bubble-customer:has(> .msg-sender-name-footer),
.chat-style-hien .msg-bubble-customer:has(> .msg-sender-name-footer),
.chat-style-co .msg-bubble-customer:has(> .msg-sender-name-footer) { display: flex; flex-direction: column; }

/* order:-1 đẩy nhãn tên lên đầu. Đảo luôn lề: nhãn vốn khai mt-1/-mb-1 cho vị
   trí DƯỚI, ở trên thì cần khoảng cách ngược lại. */
.chat-style-truyen .msg-sender-name-footer,
.chat-style-hien .msg-sender-name-footer,
.chat-style-co .msg-sender-name-footer {
  order: -1;
  margin-top: 0;
  margin-bottom: 0.125rem;
}

/* ── ẢNH/VIDEO/TỆP Ở TRÊN, CHỮ Ở DƯỚI (anh chốt 2026-08-16) ────────────────
   Anh: "khi gửi ảnh kèm chữ thì đồng nhất ảnh bên trên chữ bên dưới (tương tự
   cho cả video, tệp…)".

   Làm bằng thứ tự flex chứ KHÔNG đổi mã dựng. Khối chú thích được dựng ở HAI
   nơi (templ msgChuThichTep + bộ dựng tin-đến-trực-tiếp bocChuThich), mỗi nơi
   lại có năm nhánh loại nội dung — dời tay là mười chỗ phải sửa khớp nhau,
   đúng cái bệnh "vá một nơi quên nơi kia" đã trả giá nhiều lần. CSS thì một
   nhà. Cùng cách đã dùng cho nhãn tên ở khối ngay trên.

   Khối chứa vốn đã là `flex flex-col` ở cả năm nhánh nên order ăn ngay.

   Nhãn tên đẩy xuống order 2 để nằm DƯỚI chú thích. Ba phong cách truyền
   thống/hiện đại/cổ điển khai order:-1 cho nhãn tên (khối trên) — khai đó
   riêng hơn nên vẫn thắng, nhãn tên ở các phong cách ấy vẫn nằm trên cùng.

   Trình duyệt không hiểu order thì rơi về thứ tự trong mã (chữ trên, ảnh
   dưới) — xấu hơn tí nhưng không vỡ. Hỏng an toàn. */
.msg-chu-thich {
  order: 1;
  margin-top: 0.25rem;
  margin-bottom: 0;
}
.msg-sender-name-footer { order: 2; }

/* ═══════════════════════════════════════════════════════════════════════════
   CHỌN GIAO DIỆN TIN NHẮN — 5 style (anh chốt 2026-08-13).
   Class .chat-style-{id} gắn trên CHAT PANEL (bao #msg-bubbles + composer).
   Mỗi style CHỈ đổi BIẾN (màu out/in + nền khung + 3 biến bo) → không sửa
   cấu trúc. sang = MẶC ĐỊNH (= :root, không cần khối). Composer sẽ tối ưu sau.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 2 · PHONG CÁCH (Telegram): xanh gradient + đuôi vát mô hình B ── */
.chat-style-phong {
  --msg-out-bg: #effdde; --msg-out-fg: #1c2a17; --msg-out-shadow: 0 1px 1px rgba(0,0,0,.06);
  --msg-in-bg: #ffffff; --msg-in-fg: #1a1a1a;
  --msg-out-link: #1678c2; /* bong bóng sáng → link xanh (trắng không đọc được) */
  --r-big: 14px; --r-tail: 5px; --r-join: 9px;
}
.chat-style-phong .msg-thread-bg {
  background:
    radial-gradient(rgba(148,156,148,.32) 1.4px, transparent 1.4px),
    radial-gradient(rgba(64,72,64,.24) 1.4px, transparent 1.4px),
    linear-gradient(165deg, #dcedcb 0%, #aed49c 50%, #79b374 100%);
  background-size: 26px 26px, 26px 26px, cover;
  background-position: 0 0, 13px 13px, center;
  background-attachment: local, local, scroll;
}
/* mô hình B: đuôi CHỈ ở tin cuối/lẻ; đầu+giữa chỉ bo nối. single/middle dùng
   luật mặc định (=model A trùng B); chỉ cần override FIRST + LAST. */
.chat-style-phong .msg-bubble-ai.msg-pos-first,
.chat-style-phong .msg-bubble-agent.msg-pos-first,
.chat-style-phong .msg-bubble-owner.msg-pos-first { border-radius: var(--r-big) var(--r-big) var(--r-join) var(--r-big); }
/* Chữ phụ trên nền hoạ tiết xanh: surface-400 chìm → TRẮNG sáng + bóng nhẹ cho
   nổi (anh chốt 2026-08-13, mở rộng 2026-08-14).
   · .msg-system-note  — ghi chú hệ thống ("Khách chuyển giai đoạn: …")
   · .msg-meta-faint   — nhãn phụ mờ: GIỜ tin + "Khách đã xem". Anh bắt
     2026-08-14: hai thứ này còn tối trong khi ghi chú hệ thống đã sáng →
     nhìn lệch nhau. Một luật cho cả hai thay vì chép hai lần.
   🔴 Móc .msg-meta-faint phải có mặt ở CẢ HAI bộ dựng bong bóng (templ
   msgBubble + renderBubbleHTML bên service) — thiếu một bên thì tin vừa về
   khác tin cũ. !important thắng utility text-surface-400. */
.chat-style-phong .msg-system-note,
.chat-style-phong .msg-meta-faint {
  color: rgba(255, 255, 255, 0.95) !important;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.22);
}

/* Góc CÓ ĐUÔI = radius 0 (đo Telegram: bubble.is-group-last đặt corner 0 để
   đuôi nối liền mạch cạnh thẳng — còn bo 5px thì đuôi trông "dán" rời rạc). */
.chat-style-phong .msg-bubble-ai.msg-pos-last,
.chat-style-phong .msg-bubble-agent.msg-pos-last,
.chat-style-phong .msg-bubble-owner.msg-pos-last { border-radius: var(--r-big) var(--r-join) 0 var(--r-big); }
.chat-style-phong .msg-bubble-ai.msg-pos-single,
.chat-style-phong .msg-bubble-agent.msg-pos-single,
.chat-style-phong .msg-bubble-owner.msg-pos-single { border-radius: var(--r-big) var(--r-big) 0 var(--r-big); }
.chat-style-phong .msg-bubble-customer.msg-pos-first { border-radius: var(--r-big) var(--r-big) var(--r-big) var(--r-join); }
.chat-style-phong .msg-bubble-customer.msg-pos-last { border-radius: var(--r-join) var(--r-big) var(--r-big) 0; }
.chat-style-phong .msg-bubble-customer.msg-pos-single { border-radius: var(--r-big) var(--r-big) var(--r-big) 0; }
/* Đuôi vát NHỌN (cánh tam giác) — CHỈ tin CUỐI/LẺ cụm, giống Telegram thật.
   Màu theo biến --msg-out-bg/--msg-in-bg nên tự đúng sáng/tối. */
.chat-style-phong .msg-bubble-ai,
.chat-style-phong .msg-bubble-agent,
.chat-style-phong .msg-bubble-owner,
.chat-style-phong .msg-bubble-customer { position: relative; }
.chat-style-phong .msg-bubble-ai.msg-pos-last::after,
.chat-style-phong .msg-bubble-agent.msg-pos-last::after,
.chat-style-phong .msg-bubble-owner.msg-pos-last::after,
.chat-style-phong .msg-bubble-ai.msg-pos-single::after,
.chat-style-phong .msg-bubble-agent.msg-pos-single::after,
.chat-style-phong .msg-bubble-owner.msg-pos-single::after {
  /* Đuôi = NGUYÊN XI path message-tail.svg của Telegram Web (đo 2026-08-13):
     cao 17px ôm sát cạnh (nửa trên chỉ cong ra 2px), nhô 6.7px, MŨI ARC BO
     TRÒN r=1 nằm hơi trên đáy. Bong bóng có đuôi đặt radius 0 ở góc đó
     (luật dưới) để đuôi NỐI LIỀN MẠCH cạnh thẳng — đúng cách Telegram làm. */
  content: ''; position: absolute; right: -7px; bottom: 0; width: 7px; height: 17px;
  background: var(--msg-out-bg);
  -webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="7" height="17"><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 0 1 6 17z" fill="%23fff"/></svg>') no-repeat left bottom / 7px 17px;
          mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="7" height="17"><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 0 1 6 17z" fill="%23fff"/></svg>') no-repeat left bottom / 7px 17px;
}
.chat-style-phong .msg-bubble-customer.msg-pos-last::after,
.chat-style-phong .msg-bubble-customer.msg-pos-single::after {
  /* Phía khách: CÙNG path Telegram, lật gương scaleX(-1) — đảm bảo đối xứng tuyệt đối. */
  content: ''; position: absolute; left: -7px; bottom: 0; width: 7px; height: 17px;
  background: var(--msg-in-bg);
  transform: scaleX(-1);
  -webkit-mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="7" height="17"><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 0 1 6 17z" fill="%23fff"/></svg>') no-repeat left bottom / 7px 17px;
          mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="7" height="17"><path d="M6 17H0V0c.193 2.84.876 5.767 2.05 8.782.904 2.325 2.446 4.485 4.625 6.48A1 1 0 0 1 6 17z" fill="%23fff"/></svg>') no-repeat left bottom / 7px 17px;
}
.dark .chat-style-phong {
  --msg-out-bg: #2b5278; --msg-out-fg: #ffffff;
  --msg-in-bg: #182533; --msg-in-fg: #e6ebf0;
  --msg-out-link: #8ec7ff;
}
.dark .chat-style-phong .msg-thread-bg {
  background:
    radial-gradient(rgba(255,255,255,.05) 1.4px, transparent 1.4px),
    radial-gradient(rgba(255,255,255,.03) 1.4px, transparent 1.4px),
    linear-gradient(165deg, #0f1c26 0%, #123040 55%, #0b202e 100%);
  background-size: 26px 26px, 26px 26px, cover;
  background-position: 0 0, 13px 13px, center;
  background-attachment: local, local, scroll;
}

/* ── 3 · TRUYỀN THỐNG (Zalo): vuông 8px, nền xám ── */
.chat-style-truyen {
  --msg-out-bg: #dbebff; --msg-out-fg: #0a1f38; --msg-out-shadow: none;
  --msg-in-bg: #ffffff; --msg-in-fg: #1a1a1a;
  --msg-out-link: #1565c0;
  --r-big: 8px; --r-tail: 8px; --r-join: 8px;
}
.chat-style-truyen .msg-bubble-customer { border: 1px solid #e6e8ee; }
.chat-style-truyen .msg-thread-bg { background: #f4f5f7; }
.dark .chat-style-truyen {
  --msg-out-bg: #1c3d5a; --msg-out-fg: #dbeafe;
  --msg-in-bg: #2a2f3a; --msg-in-fg: #e5e7eb;
  --msg-out-link: #8ec7ff;
}
.dark .chat-style-truyen .msg-bubble-customer { border-color: #3a4050; }
.dark .chat-style-truyen .msg-thread-bg { background: #1b1f27; }

/* ── 4 · HIỆN ĐẠI (Messenger): pill 18px, xanh dương ── */
.chat-style-hien {
  --msg-out-bg: #0084ff; --msg-out-fg: #ffffff; --msg-out-shadow: none;
  --msg-in-bg: #e9eaed; --msg-in-fg: #050505;
  --r-big: 18px; --r-tail: 18px; --r-join: 18px;
}
.chat-style-hien .msg-thread-bg { background: #ffffff; }
.dark .chat-style-hien {
  --msg-in-bg: #303236; --msg-in-fg: #e4e6eb;
}
.dark .chat-style-hien .msg-thread-bg { background: #17181c; }

/* ── 5 · CỔ ĐIỂN (Subiz): mình trắng+viền mảnh, khách tím-xám, bo 8px ── */
.chat-style-co {
  --msg-out-bg: #ffffff; --msg-out-fg: #2c3345; --msg-out-shadow: 0 1px 2px rgba(23,33,64,.12);
  --msg-in-bg: #e4e7f2; --msg-in-fg: #2c3345;
  --msg-out-link: rgb(var(--color-primary-600));
  --r-big: 8px; --r-tail: 8px; --r-join: 8px;
}
.chat-style-co .msg-bubble-ai,
.chat-style-co .msg-bubble-agent,
.chat-style-co .msg-bubble-owner { border: 1px solid rgba(23,33,64,.05); }
.chat-style-co .msg-thread-bg { background: #f5f6f8; }
.dark .chat-style-co {
  --msg-out-bg: #232733; --msg-out-fg: #e4e7f2;
  --msg-in-bg: #2f3442; --msg-in-fg: #dfe3ee;
  --msg-out-link: #b7b3ff;
}
.dark .chat-style-co .msg-bubble-ai,
.dark .chat-style-co .msg-bubble-agent,
.dark .chat-style-co .msg-bubble-owner { border-color: rgba(255,255,255,.05); }
.dark .chat-style-co .msg-thread-bg { background: #15181f; }

/* ĐÃ GỠ hiệu ứng bubble-mới-trượt-vào (@starting-style + transition 0.3s) —
   anh diệt 2026-08-19, có máy đo puppeteer làm chứng (do_nhay2/3.js):
   ① phạm luật TỨC THÌ 11/8 (transition đóng băng từ 7/22, nay chính chủ đòi gỡ);
   ② nó chính là "CÚ NHÁY" anh bắt suốt buổi: fade lần 1 khi ĐẮP bong bóng
      dựng sẵn, rồi bước THAY-NODE của SSE sinh phần tử mới → @starting-style
      KÍCH LẠI fade lần 2 — hai nhịp mờ-hiện liền nhau = chớp-chớp
      (máy pixel đo dao động tối↔sáng 4.715px lặp đều; getAnimations() bắt
      sống CSSTransition opacity+transform RUNNING trên chính bubble vừa gửi).
   Bong bóng nay hiện TỨC THÌ, một phát, đứng luôn. */

/* Link trong bubble chat (Web Risk — webrisk_render.go + webrisk_hook.go).
   Màu tự đổi theo NỀN bubble cha: tin khách (nền sáng) → primary; tin shop/AI/agent/owner
   (gradient đậm chữ trắng) → trắng + gạch chân, không bị chìm trên nền màu. */
.msg-link-safe {
  color: rgb(var(--color-primary-600));
  text-decoration: underline;
  text-underline-offset: 2px;
  word-break: break-all;
}
.msg-link-safe:hover { color: rgb(var(--color-primary-700)); }
.dark .msg-link-safe { color: rgb(var(--color-primary-400)); }
.dark .msg-link-safe:hover { color: rgb(var(--color-primary-300)); }
/* Link phía mình theo BIẾN --msg-out-link (mặc định #fff cho bong bóng gradient
   tối Sang trọng/Hiện đại). Style bong bóng SÁNG (Phong cách/Truyền thống/Cổ
   điển) đặt lại biến — anh bắt 2026-08-13: link trắng trên nền sáng không đọc được. */
.msg-bubble-ai .msg-link-safe,
.msg-bubble-agent .msg-link-safe,
.msg-bubble-owner .msg-link-safe,
.dark .msg-bubble-ai .msg-link-safe,
.dark .msg-bubble-agent .msg-link-safe,
.dark .msg-bubble-owner .msg-link-safe {
  color: var(--msg-out-link, #fff);
  text-decoration-color: color-mix(in srgb, var(--msg-out-link, #fff) 70%, transparent);
}
.msg-bubble-ai .msg-link-safe:hover,
.msg-bubble-agent .msg-link-safe:hover,
.msg-bubble-owner .msg-link-safe:hover {
  text-decoration-color: var(--msg-out-link, #fff);
}
/* Link ĐỘC bị chặn: đỏ + icon cảnh báo (không gạch ngang — anh chốt 2026-07-16) */
.msg-link-unsafe {
  display: inline-flex;
  align-items: baseline;
  gap: 0.25rem;
  color: rgb(225 29 72); /* rose-600 */
  font-weight: 500;
  word-break: break-all;
  cursor: not-allowed;
}
.dark .msg-link-unsafe { color: rgb(251 113 133); } /* rose-400 */
.msg-link-unsafe svg {
  width: 0.875rem;
  height: 0.875rem;
  align-self: center;
  flex-shrink: 0;
}
/* Đang kiểm tra an toàn — xám, chưa cho bấm */
.msg-link-checking {
  color: rgb(var(--color-surface-500));
  word-break: break-all;
}
.dark .msg-link-checking { color: rgb(var(--color-surface-400)); }

/* Bubble pending state — Production Commit 2 (anh chốt 2026-05-21).
   Animated "..." after "Đang gửi" cho file bubble trong khi worker dispatch.
   Pattern: ::after pseudo-element với steps() animation đảo 4 trạng thái
   (rỗng / "." / ".." / "...") loop 1.5s. Inherit color → fit với
   .msg-bubble-owner gradient (white text). */
.msg-sending-dots::after {
  content: '';
  display: inline-block;
  min-width: 1.5em;
  text-align: left;
  animation: msgSendingDots 1.5s steps(4, end) infinite;
}
@keyframes msgSendingDots {
  0%   { content: ''; }
  25%  { content: '.'; }
  50%  { content: '..'; }
  75%, 100% { content: '...'; }
}

/* Bubble failed state — red border + opacity. Worker dispatch permanent
   fail (4xx khác 429, FB block, max retries). Mark qua _markBubbleFailed
   khi SSE attachment_failed về. */
.msg-bubble-failed {
  opacity: 0.75;
}
.msg-bubble-failed .msg-bubble-owner {
  background: linear-gradient(135deg, #ef4444, #dc2626);
  box-shadow: 0 2px 8px rgb(239 68 68 / 0.30);
}

/* ═══════════════════════════════════════════════════════════════════════════
   TOOLTIP SYSTEM — 4 directions (LAYOUT.md §7.18)
   Usage: class="tooltip tooltip-{top|bottom|left|right}" data-tooltip="Text"
   Default (no direction class) = tooltip-top
   ═══════════════════════════════════════════════════════════════════════════ */
.tooltip { position: relative; }

/* Base pseudo-elements (shared by all directions) */
.tooltip::after {
  content: attr(data-tooltip);
  position: absolute;
  padding: 5px 10px;
  border-radius: 8px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  background: rgb(var(--color-surface-800));
  color: white;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
  z-index: 20;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.tooltip::before {
  content: '';
  position: absolute;
  border: 5px solid transparent;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
  z-index: 20;
}
.tooltip:hover::after { opacity: 1; }
.tooltip:hover::before { opacity: 1; }
/* Empty tooltip (data-tooltip="" from conditional Alpine bindings, or attr absent) →
   don't render an empty black box on hover. */
.tooltip[data-tooltip=""]::after,
.tooltip[data-tooltip=""]::before,
.tooltip:not([data-tooltip])::after,
.tooltip:not([data-tooltip])::before { display: none; }
/* Tooltip DÀI (mô tả giao dịch bị cắt «…»...): nowrap gốc đẩy chuỗi 60+ ký tự
   tràn màn hình → biến thể này cho xuống dòng trong bề ngang tối đa. */
.tooltip.tooltip-dai::after {
  white-space: normal;
  width: max-content;
  max-width: 320px;
}

/* --- TOP (default) — tooltip appears above element --- */
.tooltip:not(.tooltip-bottom):not(.tooltip-left):not(.tooltip-right)::after,
.tooltip.tooltip-top::after {
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
}
.tooltip:not(.tooltip-bottom):not(.tooltip-left):not(.tooltip-right)::before,
.tooltip.tooltip-top::before {
  bottom: calc(100% + 3px);
  left: 50%;
  transform: translateX(-50%);
  border-top-color: rgb(var(--color-surface-800));
}
.tooltip:not(.tooltip-bottom):not(.tooltip-left):not(.tooltip-right):hover::after,
.tooltip.tooltip-top:hover::after {
  transform: translateX(-50%) translateY(0);
}

/* --- BOTTOM — tooltip appears below element --- */
.tooltip.tooltip-bottom::after {
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(-4px);
}
.tooltip.tooltip-bottom::before {
  top: calc(100% + 3px);
  left: 50%;
  transform: translateX(-50%);
  border-bottom-color: rgb(var(--color-surface-800));
}
.tooltip.tooltip-bottom:hover::after {
  transform: translateX(-50%) translateY(0);
}

/* --- LEFT — tooltip appears to the left of element --- */
.tooltip.tooltip-left::after {
  right: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%) translateX(4px);
}
.tooltip.tooltip-left::before {
  right: calc(100% + 3px);
  top: 50%;
  transform: translateY(-50%);
  border-left-color: rgb(var(--color-surface-800));
}
.tooltip.tooltip-left:hover::after {
  transform: translateY(-50%) translateX(0);
}

/* --- RIGHT — tooltip appears to the right of element --- */
.tooltip.tooltip-right::after {
  left: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%) translateX(-4px);
}
.tooltip.tooltip-right::before {
  left: calc(100% + 3px);
  top: 50%;
  transform: translateY(-50%);
  border-right-color: rgb(var(--color-surface-800));
}
.tooltip.tooltip-right:hover::after {
  transform: translateY(-50%) translateX(0);
}

/* Dark mode tooltip */
.dark .tooltip::after {
  background: rgb(var(--color-surface-600));
}
.dark .tooltip:not(.tooltip-bottom):not(.tooltip-left):not(.tooltip-right)::before,
.dark .tooltip.tooltip-top::before {
  border-top-color: rgb(var(--color-surface-600));
}
.dark .tooltip.tooltip-bottom::before {
  border-bottom-color: rgb(var(--color-surface-600));
}
.dark .tooltip.tooltip-left::before {
  border-left-color: rgb(var(--color-surface-600));
}
.dark .tooltip.tooltip-right::before {
  border-right-color: rgb(var(--color-surface-600));
}
/* Floating tooltip — for elements inside overflow containers (escapes clipping) */
.msg-float-tooltip {
  position: fixed;
  z-index: 50;
  padding: 5px 10px;
  border-radius: 8px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  background: rgb(var(--color-surface-800));
  color: white;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  pointer-events: none;
  opacity: 0;
  transform: translateX(-50%) translateY(calc(-100% + 4px));
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.msg-float-tooltip.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(-100%);
}
/* Arrow pointing down (tooltip appears above) */
.msg-float-tooltip::before {
  content: '';
  position: absolute;
  bottom: -5px;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: rgb(var(--color-surface-800));
}
.dark .msg-float-tooltip {
  background: rgb(var(--color-surface-600));
}
.dark .msg-float-tooltip::before {
  border-top-color: rgb(var(--color-surface-600));
}
/* Sidebar floating tooltip — position:fixed to escape overflow-y:auto clipping.
   Uses CSS transition (not Alpine x-transition) to avoid transform conflict with translateY(-50%). */
.sidebar-float-tip {
  position: fixed;
  left: 80px; /* sidebar collapsed 72px + 8px gap */
  z-index: 60;
  padding: 5px 10px;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  background: rgb(var(--color-surface-800));
  color: white;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  pointer-events: none;
  opacity: 0;
  transform: translateY(-50%);
  transition: opacity 0.1s ease;
}
.sidebar-float-tip.is-visible {
  opacity: 1;
}
/* Arrow pointing left */
.sidebar-float-tip::before {
  content: '';
  position: absolute;
  left: -5px;
  top: 50%;
  transform: translateY(-50%);
  border: 5px solid transparent;
  border-right-color: rgb(var(--color-surface-800));
}
.dark .sidebar-float-tip {
  background: rgb(var(--color-surface-600));
}
.dark .sidebar-float-tip::before {
  border-right-color: rgb(var(--color-surface-600));
}

/* Script editor floating tooltip — escape sidebar overflow-y-auto clipping.
   Pattern: LAYOUT.md §7.19 Floating Tooltip. Direction: right (arrow ←).
   z-index 70 > editor overlay z-60. */
.se-float-tip {
  position: fixed;
  z-index: 70;
  padding: 5px 10px;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.4;
  white-space: normal;
  max-width: 260px;
  word-break: break-word;
  background: rgb(var(--color-surface-800));
  color: white;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  pointer-events: none;
  opacity: 0;
  transform: translateY(-50%);
  transition: opacity 0.15s ease;
}
.se-float-tip.is-visible { opacity: 1; }
.se-float-tip::before {
  content: '';
  position: absolute;
  left: -5px;
  top: 50%;
  transform: translateY(-50%);
  border: 5px solid transparent;
  border-right-color: rgb(var(--color-surface-800));
}
.dark .se-float-tip { background: rgb(var(--color-surface-600)); }
.dark .se-float-tip::before { border-right-color: rgb(var(--color-surface-600)); }
/* tip-flip: tip nằm BÊN TRÁI element (khi hết chỗ phải) → mũi tên sang phải, trỏ phải */
.se-float-tip.tip-flip::before { left: auto; right: -5px; border-right-color: transparent; border-left-color: rgb(var(--color-surface-800)); }
.dark .se-float-tip.tip-flip::before { border-left-color: rgb(var(--color-surface-600)); }

/* Sidebar content — only animate padding-left (for collapse/expand).
   !important overrides Tailwind CDN's runtime-generated .transition-all { transition-property: all }
   which otherwise causes a 300ms opacity fade when body.ready removes FOUC opacity:0.
   padding-right added for smooth chat panel toggle at xl+ (sidebar-content gets pr-[400px]). */
.sidebar-content {
  transition-property: padding-left, padding-right !important;
}

/* AI Selector Glow — rotating gradient border around assistant selector button */
.ai-selector-glow {
  position: relative;
  padding: 1.5px;
  border-radius: 0.5rem;
  overflow: hidden;
}
.ai-selector-glow::before {
  content: '';
  position: absolute;
  inset: -100%;
  background: conic-gradient(
    from 0deg,
    transparent 0%,
    rgb(var(--color-primary-300) / 0.6) 8%,
    rgb(var(--color-primary-500)) 15%,
    rgb(var(--color-primary-300) / 0.6) 22%,
    transparent 30%
  );
  animation: ai-glow-spin 4s linear infinite;
}
.ai-selector-glow > * {
  position: relative;
  z-index: 1;
}
.dark .ai-selector-glow::before {
  background: conic-gradient(
    from 0deg,
    transparent 0%,
    rgb(var(--color-primary-400) / 0.3) 8%,
    rgb(var(--color-primary-400) / 0.7) 15%,
    rgb(var(--color-primary-400) / 0.3) 22%,
    transparent 30%
  );
}
@keyframes ai-glow-spin {
  to { transform: rotate(360deg); }
}

/* Bong bóng khách tối = đổi BIẾN (để style .chat-style-X vẫn override được
   trong dark; nếu set thẳng .dark .msg-bubble-customer sẽ đè mất style). */
.dark {
  --msg-in-bg: rgb(var(--color-surface-700));
  --msg-in-fg: rgb(var(--color-surface-200));
}
/* msg scrollbar dark mode — handled by global .dark * rule */

/* ═══════════════════════════════════════════════════════════════════════════
   DARK MODE — .dark prefix variants
   Strategy: Add dark variants to component classes here (DRY).
   Layout/page structural elements use inline dark: Tailwind classes.
   ═══════════════════════════════════════════════════════════════════════════ */

/* --- Base --- */
.dark ::selection {
  background-color: rgb(var(--color-primary-800));
  color: rgb(var(--color-primary-100));
}

/* --- Buttons --- */
/* .btn-primary keeps indigo — works on dark bg */
.dark .btn-secondary {
  background-color: rgb(var(--color-surface-800));
  color: rgb(var(--color-surface-200));
  border-color: rgb(var(--color-surface-600));
}
.dark .btn-secondary:hover:not(:disabled) {
  background-color: rgb(var(--color-surface-700));
  border-color: rgb(var(--color-surface-500));
}
.dark .btn-ghost {
  color: rgb(var(--color-surface-400));
}
.dark .btn-ghost:hover:not(:disabled) {
  background-color: rgb(var(--color-surface-800));
  color: rgb(var(--color-surface-200));
}

/* --- Inputs --- */
.dark .input {
  background-color: rgb(var(--color-surface-800));
  border-color: rgb(var(--color-surface-600));
  color: rgb(var(--color-surface-100));
}
.dark .input::placeholder {
  color: rgb(var(--color-surface-500));
}
.dark .input:hover {
  border-color: rgb(var(--color-surface-500));
}
.dark .input:focus {
  border-color: rgb(var(--color-primary-500));
  box-shadow: 0 0 0 3px rgb(var(--color-primary-500) / 0.2);
}
.dark .input-label {
  color: rgb(var(--color-surface-300));
}
.dark .input-hint {
  color: rgb(var(--color-surface-400));
}
.dark .input-error-text {
  color: rgb(var(--color-error-500));
}

/* --- Date Input Override --- */
/* Style native date picker to match design system */
input[type="date"].input {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  cursor: pointer;
}

input[type="date"].input::-webkit-calendar-picker-indicator {
  cursor: pointer;
  opacity: 0.5;
  filter: none;
  transition: opacity 0.15s;
}

input[type="date"].input::-webkit-calendar-picker-indicator:hover {
  opacity: 0.8;
}

.dark input[type="date"].input {
  color-scheme: dark;
}

.dark input[type="date"].input::-webkit-calendar-picker-indicator {
  filter: invert(0.7);
  opacity: 0.6;
}

.dark input[type="date"].input::-webkit-calendar-picker-indicator:hover {
  opacity: 0.9;
}

/* --- Checkbox --- */
.dark .checkbox {
  background-color: rgb(var(--color-surface-700));
  border-color: rgb(var(--color-surface-500));
}
.dark .checkbox:hover {
  border-color: rgb(var(--color-primary-400));
}
.dark .checkbox:focus {
  border-color: rgb(var(--color-primary-500));
  box-shadow: 0 0 0 3px rgb(var(--color-primary-500) / 0.2);
}
.dark .checkbox:checked {
  background-color: rgb(var(--color-primary-600));
  border-color: rgb(var(--color-primary-600));
}
.dark .checkbox:checked:hover {
  background-color: rgb(var(--color-primary-500));
  border-color: rgb(var(--color-primary-500));
}
.dark .checkbox:indeterminate {
  background-color: rgb(var(--color-primary-600));
  border-color: rgb(var(--color-primary-600));
}

/* .radio dark — mirror .dark .checkbox */
.dark .radio {
  background-color: rgb(var(--color-surface-700));
  border-color: rgb(var(--color-surface-500));
}
.dark .radio:hover { border-color: rgb(var(--color-primary-400)); }
.dark .radio:focus {
  border-color: rgb(var(--color-primary-500));
  box-shadow: 0 0 0 3px rgb(var(--color-primary-500) / 0.2);
}
.dark .radio:checked {
  background-color: rgb(var(--color-primary-600));
  border-color: rgb(var(--color-primary-600));
}
.dark .radio:checked:hover {
  background-color: rgb(var(--color-primary-500));
  border-color: rgb(var(--color-primary-500));
}

/* --- Cards --- */
.dark .card {
  background-color: rgb(var(--color-surface-800));
}
.dark .card-hover:hover {
  box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.4);
}

/* --- Links --- */
.dark .link {
  color: rgb(var(--color-primary-400));
}
.dark .link:hover {
  color: rgb(var(--color-primary-300));
}

/* --- Spinner --- */
.dark .spinner {
  border-color: rgb(var(--color-surface-700));
  border-top-color: rgb(var(--color-primary-400));
}

/* --- Alerts --- */
.dark .alert-info {
  background-color: rgb(30 64 175 / 0.15);
  color: rgb(147 197 253);
}
.dark .alert-success {
  background-color: rgb(var(--color-accent-900) / 0.3);
  color: rgb(var(--color-accent-300));
}
.dark .alert-warning {
  background-color: rgb(146 64 14 / 0.15);
  color: rgb(253 186 116);
}
.dark .alert-error {
  background-color: rgb(153 27 27 / 0.15);
  color: rgb(var(--color-error-300));
}

/* --- OTP Input --- */
.dark .otp-input {
  background: rgb(var(--color-surface-800));
  border-color: rgb(var(--color-surface-600));
  color: rgb(var(--color-surface-100));
}

/* --- Industry Card (Onboarding) --- */
.dark .industry-card {
  border-color: rgb(var(--color-surface-600));
  background: rgba(var(--color-surface-800), 0.6);
}
.dark .industry-card:hover {
  border-color: rgb(var(--color-primary-500));
  background: rgba(var(--color-primary-900), 0.3);
  box-shadow: 0 4px 12px rgb(var(--color-primary-500) / 0.15);
}
.dark .industry-card.selected {
  border-color: rgb(var(--color-primary-500));
  background: rgba(var(--color-primary-900), 0.3);
  box-shadow:
    0 0 0 3px rgb(var(--color-primary-500) / 0.2),
    0 4px 16px rgb(var(--color-primary-500) / 0.15);
}

/* --- Chat Bubbles --- */
/* .chat-bubble-user keeps gradient — fine for dark */
.dark .chat-bubble-assistant {
  background: rgb(var(--color-surface-800));
  color: rgb(var(--color-surface-200));
  border-color: rgb(var(--color-surface-700));
}
.dark .typing-dot {
  background: rgb(var(--color-surface-500));
}

/* --- Chat Markdown (assistant messages) --- */
.chat-md p { margin: 0; }
.chat-md p + p { margin-top: 0.5em; }
.chat-md strong { font-weight: 600; }
.chat-md em { font-style: italic; }
.chat-md ul, .chat-md ol { margin: 0.375em 0; padding-left: 1.25em; }
.chat-md li { margin: 0.125em 0; }
.chat-md li::marker { color: rgb(var(--color-surface-400)); }
.chat-md code {
  font-size: 0.8125em;
  padding: 0.125em 0.375em;
  border-radius: 0.25rem;
  background: rgb(var(--color-surface-100));
  color: rgb(var(--color-primary-700));
  font-family: ui-monospace, SFMono-Regular, monospace;
}
.dark .chat-md code {
  background: rgb(var(--color-surface-700));
  color: rgb(var(--color-primary-300));
}
.chat-md pre {
  margin: 0.5em 0;
  padding: 0.625em 0.75em;
  border-radius: 0.5rem;
  background: rgb(var(--color-surface-100));
  overflow-x: auto;
  font-size: 0.8125em;
}
.dark .chat-md pre {
  background: rgb(var(--color-surface-900));
}
.chat-md pre code {
  padding: 0;
  background: transparent;
  color: inherit;
}
.chat-md blockquote {
  margin: 0.375em 0;
  padding-left: 0.75em;
  border-left: 3px solid rgb(var(--color-primary-300));
  color: rgb(var(--color-surface-600));
}
.dark .chat-md blockquote {
  border-left-color: rgb(var(--color-primary-600));
  color: rgb(var(--color-surface-400));
}
.chat-md a {
  color: rgb(var(--color-primary-600));
  text-decoration: underline;
  text-underline-offset: 2px;
}
.dark .chat-md a { color: rgb(var(--color-primary-400)); }
.chat-md h1, .chat-md h2, .chat-md h3, .chat-md h4 {
  font-weight: 600;
  margin: 0.5em 0 0.25em;
  line-height: 1.3;
}
.chat-md h1 { font-size: 1.125em; }
.chat-md h2 { font-size: 1em; }
.chat-md h3 { font-size: 0.9375em; }
.chat-md hr {
  margin: 0.5em 0;
  border: none;
  border-top: 1px solid rgb(var(--color-surface-200));
}
.dark .chat-md hr { border-top-color: rgb(var(--color-surface-700)); }

/* --- Assistant Card (Selector) --- */
.dark .assistant-card {
  border-color: rgb(var(--color-surface-600));
  background: rgb(var(--color-surface-800));
}
.dark .assistant-card:hover {
  border-color: rgb(var(--color-primary-500));
  background: rgb(var(--color-primary-900) / 0.2);
}
.dark .assistant-card.selected {
  border-color: rgb(var(--color-primary-500));
  background: rgb(var(--color-primary-900) / 0.2);
}

/* --- Chat Dashboard --- */
.dark .chat-textarea-container {
  border-color: rgb(var(--color-surface-600));
  background: rgb(var(--color-surface-800));
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
}
.dark .chat-textarea-container:focus-within {
  border-color: rgb(var(--color-surface-500));
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
}
.dark .chat-textarea {
  color: rgb(var(--color-surface-100));
}
.dark .chat-textarea::placeholder {
  color: rgb(var(--color-surface-500));
}
.dark .chat-top-btn {
  color: rgb(var(--color-surface-400));
}
.dark .chat-top-btn:hover {
  background: rgb(var(--color-surface-800));
  color: rgb(var(--color-surface-200));
}
.dark .chat-top-btn-primary {
  background: rgb(var(--color-surface-800));
  color: rgb(var(--color-surface-300));
}
.dark .chat-top-btn-primary:hover {
  background: rgb(var(--color-surface-700));
}
.dark .chat-send-btn {
  background: rgb(var(--color-surface-700));
  color: rgb(var(--color-surface-500));
}
/* 🔴 BẪY ĐỘ ƯU TIÊN (anh bắt 2026-08-07): comment cũ ghi "active keeps
   primary-600 — fine for dark" là LỜI HỨA SAI. `.dark .chat-send-btn` (0,2,0)
   CÙNG specificity với `.chat-send-btn.active` (0,2,0) nhưng đứng SAU trong
   file → đè luôn trạng thái active → dark mode gõ chữ xong nút vẫn xám-trên-xám
   (đo: bg surface-700 + icon surface-500 ở CẢ 3 trạng thái). Phải override
   TƯỜNG MINH như dưới — thêm trạng thái mới cho nút này thì thêm bản .dark
   tương ứng, đừng tin thứ tự file. */
.dark .chat-send-btn.active {
  background: rgb(var(--color-primary-600));
  color: white;
}
.dark .chat-send-btn.active:hover {
  /* dark hover SÁNG lên (primary-500) — nền tối mà còn tối đi là mất phản hồi */
  background: rgb(var(--color-primary-500));
}
.dark .chat-attach-btn {
  color: rgb(var(--color-surface-500));
}
.dark .chat-attach-btn:hover {
  background: rgb(var(--color-surface-700));
  color: rgb(var(--color-surface-300));
}
.dark .chat-assistant-compact {
  border-color: rgb(var(--color-surface-600));
}
.dark .chat-assistant-compact:hover {
  border-color: rgb(var(--color-surface-500));
  background: rgb(var(--color-surface-800));
}
.dark .chat-quick-card {
  border-color: rgb(var(--color-surface-700));
  background: rgb(var(--color-surface-800));
}
.dark .chat-quick-card:hover {
  border-color: rgb(var(--color-surface-600));
  background: rgb(var(--color-surface-700));
}
.dark .chat-history-drawer {
  background: rgb(var(--color-surface-800));
  border-color: rgb(var(--color-surface-700));
  box-shadow: -4px 0 24px rgba(0, 0, 0, 0.3);
}
.dark .chat-history-item:hover {
  background: rgb(var(--color-surface-700));
}
/* dark chat-messages scrollbar — handled by global .dark * rule */

/* ═══════════════════════════════════════════════════════════════════════════
   CUSTOMER CONFIG — Color Dot Palette (12 colors, max visual distinction)
   Hue-spaced: slate→red→orange→gold→lime→green→teal→blue→indigo→purple→pink→brown
   Used by: stages, groups, tags color picker
   ═══════════════════════════════════════════════════════════════════════════ */

.cust-dot-slate    { background: linear-gradient(135deg, #94a3b8, #64748b); } /* neutral gray     */
.cust-dot-red      { background: linear-gradient(135deg, #f87171, #dc2626); } /* hue 0°   — red   */
.cust-dot-orange   { background: linear-gradient(135deg, #fb923c, #ea580c); } /* hue 30°  — orange*/
.cust-dot-gold     { background: linear-gradient(135deg, #fbbf24, #d97706); } /* hue 45°  — gold  */
.cust-dot-lime     { background: linear-gradient(135deg, #a3e635, #65a30d); } /* hue 85°  — lime  */
.cust-dot-green    { background: linear-gradient(135deg, #4ade80, #16a34a); } /* hue 140° — green */
.cust-dot-teal     { background: linear-gradient(135deg, #2dd4bf, #0d9488); } /* hue 170° — teal  */
.cust-dot-blue     { background: linear-gradient(135deg, #60a5fa, #2563eb); } /* hue 215° — blue  */
.cust-dot-indigo   { background: linear-gradient(135deg, #818cf8, #4f46e5); } /* hue 240° — indigo*/
.cust-dot-purple   { background: linear-gradient(135deg, #c084fc, #9333ea); } /* hue 275° — purple*/
.cust-dot-pink     { background: linear-gradient(135deg, #f472b6, #db2777); } /* hue 320° — pink  */
.cust-dot-brown    { background: linear-gradient(135deg, #d97706, #78350f); } /* warm earthy brown*/

/* Customer Detail Page — full-height scrollable layout */
.cust-detail-wrapper {
  position: relative;
  margin: -4.5rem -1rem -1rem -1rem;
  padding-top: 3.5rem;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: clip;
}
@media (min-width: 640px) {
  .cust-detail-wrapper { margin: -4.5rem -1.5rem -1.5rem -1.5rem; }
}
@media (min-width: 1024px) {
  .cust-detail-wrapper { margin: -2rem; padding-top: 0; }
}

/* n8n-style dot grid canvas for customer journey */
.journey-canvas {
  background-color: #f8fafc;
  background-image: radial-gradient(circle, #cbd5e1 1px, transparent 1px);
  background-size: 24px 24px;
}
.dark .journey-canvas {
  background-color: #0f172a;
  background-image: radial-gradient(circle, #1e293b 1px, transparent 1px);
}

/* Timeline dot ripple effect for today */
.timeline-dot-today::before,
.timeline-dot-today::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 9999px;
  border: 2px solid rgb(99 102 241 / 0.5);
  animation: timeline-ripple 2s ease-out infinite;
}
.timeline-dot-today::after {
  animation-delay: 1s;
}
@keyframes timeline-ripple {
  0% { transform: scale(1); opacity: 0.6; }
  100% { transform: scale(1.8); opacity: 0; }
}

/* Upload preview thumbnail — hover-only remove button.
   Tailwind precompiled không có group-hover:opacity-100 rule, define
   custom class. Anh chốt 2026-05-22: nút × chỉ hiện khi hover thumbnail. */
.msg-upload-thumb .msg-upload-thumb-remove {
  opacity: 0;
  transition: opacity 150ms ease;
}
.msg-upload-thumb:hover .msg-upload-thumb-remove,
.msg-upload-thumb:focus-within .msg-upload-thumb-remove {
  opacity: 1;
}

/* File row trong "Thư viện đính kèm" — hover-only download icon (anh chốt
   2026-05-26). Cùng pattern .msg-upload-thumb-remove vì Tailwind precompiled
   không có group-hover:opacity rule. */
.msg-file-row .msg-file-download {
  opacity: 0;
  transition: opacity 150ms ease;
}
.msg-file-row:hover .msg-file-download,
.msg-file-row:focus-within .msg-file-download {
  opacity: 1;
}

/* UI Events Realtime Refresh — smooth transition khi HTMX swap #main-content */
#main-content.htmx-swapping {
  opacity: 0.85;
  transition: opacity 150ms ease-out;
}
#main-content.htmx-settling {
  opacity: 1;
  transition: opacity 150ms ease-in;
}
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 200ms;
}

/* Kich thuoc tien ich 3/4 (size 9): col-span-9 KHONG co trong tailwind build san
   (chi 3-4-6-12) — tu khai de moc 3/4 co width dung (gotcha class-thieu, nhu w-2-5). */
/* app.css nap TRUOC tailwind (shared.templ) → can !important de thang sm:col-span-6
   (tailwind, nap sau) o breakpoint lg. Chi lg: dung (getColClass: col-span-12 sm:6 lg:N). */
@media (min-width: 1024px) {
  .lg\:col-span-8 { grid-column: span 8 / span 8 !important; }
  .lg\:col-span-9 { grid-column: span 9 / span 9 !important; }
}

/* ═══ Widget drawer: Coloris (vendor tự host — anh chốt 2026-07-25).
   Mockup màu cạnh ô hex TỰ VẼ bằng Alpine trong templ (input render muộn
   trong x-if nên không dùng wrap của Coloris) — chỉ cần popup của thư viện. ═══ */

/* ═══ Biểu đồ dựng sẵn ở máy chủ: chọn bản SÁNG/TỐI bằng CSS (anh chốt 2026-08-04).
   Máy chủ dựng CẢ HAI bản; class `dark` trên <html> (đặt bởi script đầu trang,
   TRƯỚC lần vẽ đầu) quyết định bản nào hiện → không đoán theme qua cookie nữa
   (đã dính: cookie ghi trước khi app chốt theme → SVG sai màu trên nền tối).
   KHÔNG dùng dark:hidden/dark:block của Tailwind — không có trong build sẵn
   (bẫy class ma). Có test gác: TestLopSangToiCoTrongCSS. ═══ */
.sb-svg-light { display: block; }
.dark .sb-svg-light { display: none; }
.sb-svg-dark { display: none; }
.dark .sb-svg-dark { display: block; }
