/* =============================================================================
 * bulbal-media.css — Phase 01 (đợt 3): media trong chat — ảnh/video toàn màn,
 * thẻ tài liệu, tệp đính kèm trong bong bóng, trình phát/ghi âm, tiến trình tải.
 *
 * Nạp SAU css/base.css + bulbal-tokens.css + bulbal-messages.css (thứ tự do
 * phase 06 quyết định trong index.html). CHỈ overlay trình bày — KHÔNG thêm/
 * xoá/đổi thứ tự phần tử DOM. Mọi màu PHẢI qua var(--bb-*) khai báo ở
 * bulbal-tokens.css — cấm hardcode hex/rgba, cấm !important (trừ trường hợp
 * base.css đã dùng !important ở ĐÚNG thuộc tính đó — có ghi rõ dòng bên dưới).
 *
 * Vùng sở hữu độc quyền (xem plan.md + phase-01-media.md):
 *   #image-preview, #preview-caption-panel, #image-preview-container,
 *   #image-preview-footer, .image-preview (ảnh/video xem toàn màn — DÙNG CHUNG
 *   bởi CẢ 3 widget image-preview.jsx/video-preview.jsx/doc-preview.jsx, xem
 *   mục 1), .doc-card, .attachment, .audio-player, .audio, .inline-image,
 *   .rounded-container, .uploader, .play-control, .inline-video,
 *   i.material-icons.white.x-big.
 * KHÔNG đụng: #scroller .message-content img, video, .bubble (mọi biến thể),
 * .timestamp, .menuTrigger (bulbal-messages.css) · .contact-card, .the-card
 * (mọi biến thể), .quoted, .chip, .lettertile (phase 03) · .alert (mọi biến
 * thể), .cropper, ul.menu, .info-box (mọi biến thể) (phase 02, đợt 2) ·
 * .pinned (mọi biến thể), .call- (mọi biến thể) (phase 04) · .panel-form-
 * (mọi biến thể), .dialog-buttons, button.primary, button.outline
 * (bulbal-forms.css, bulbal-panels.css).
 *
 * Số đo tương phản WCAG (relative luminance chuẩn) cho MỌI cặp nền/chữ mới ở
 * đây: xem plans/20260729-1740-bulbal-chat-ui-dot3/reports/phase-01-media.md.
 * ============================================================================= */

/* ═══════════════════════════════════════════════════════════════════════════
 * MỤC 1 — Khung xem toàn màn (ảnh/video/tài liệu)
 * Widget nguồn: src/widgets/image-preview.jsx, video-preview.jsx,
 * doc-preview.jsx — CẢ 3 file render CHUNG 1 bộ ID (#image-preview bọc ngoài,
 * #preview-caption-panel thanh trên, #image-preview-container vùng giữa,
 * #image-preview-footer thanh dưới — CHỈ image/video-preview có, doc-preview
 * không render div này vì đã có SendMessage thay thế).
 * ═══════════════════════════════════════════════════════════════════════════ */

/* Khung ngoài: nền tối mờ phủ toàn màn hình (mockup: photo viewer luôn tối bất
 * kể theme sáng/tối của app, để ảnh/video nổi bật).
 * THIẾU TOKEN: chưa có --bb-scrim trong bulbal-tokens.css → dùng fallback
 * upstream --clr-overlay-white-strong (base.css:90, đã tự tối ở chế độ tối:
 * rgba(0,0,0,.9)) để không hồi quy hành vi hiện tại. Yêu cầu phase 06 bổ sung
 * --bb-scrim (đề xuất: light-dark(rgba(15,23,42,.78), rgba(2,6,15,.86)) — tối
 * đều 2 chế độ, xem report). */
#image-preview {
  background-color: var(--bb-scrim, var(--clr-overlay-white-strong));
}

/* Vùng giữa: bỏ khối nền xám đặc (--clr-gray-medium-bg) của bản gốc, để ảnh/
 * video/thẻ tài liệu nổi trên đúng 1 lớp nền tối của #image-preview — đỡ rối
 * mắt hơn bản gốc (2 lớp nền chồng nhau). */
#image-preview-container {
  background-color: transparent;
}

/* Thanh trên: tên tệp/nút tải/đóng. */
#preview-caption-panel {
  background-color: var(--bb-bg-bar);
  color: var(--bb-txt-1);
  box-shadow: var(--bb-sh-1);
}

#preview-caption-panel a {
  color: var(--bb-txt-2);
  /* Nút đóng/tải về: vòng tròn chạm được ≥44px theo yêu cầu mobile-ux. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--bb-touch);
  min-height: var(--bb-touch);
  border-radius: var(--bb-r-pill);
  transition: background-color var(--bb-dur) var(--bb-ease), color var(--bb-dur) var(--bb-ease);
}

#preview-caption-panel a:hover {
  color: var(--bb-brand);
  background-color: var(--bb-hover);
}

/* Thanh dưới: bảng thông tin tệp (chỉ image/video-preview). */
#image-preview-footer {
  background-color: var(--bb-bg-bar);
  color: var(--bb-txt-1);
}

#image-preview-footer b {
  color: var(--bb-txt-2);
}

/* Ảnh/video xem toàn màn: bo góc theo ngôn ngữ thiết kế chung. Base gộp chung
 * `.inline-image, .image-preview { border-radius: 0.1rem }` (base.css:2601) —
 * ở đây ghi đè RIÊNG .image-preview (không đụng .inline-image, xem mục 6). */
.image-preview {
  border-radius: var(--bb-r-bub);
}

/* =============================================================================
 * MỤC 2 — Thẻ tài liệu (doc-preview.jsx)
 * LƯU Ý (Key Insight #3 của phase file): `.doc-card` còn được the-card-preview.jsx
 * (danh thiếp, phase 03) dùng lại y nguyên tên class. Theo quyết định đã chốt ở
 * plan.md, phase 01 SỞ HỮU TOÀN BỘ `.doc-card` (kể cả khi nó xuất hiện trong
 * bong bóng chat qua the-card-preview.jsx) — phase 03 KHÔNG được viết `.doc-card`
 * mà chỉ thêm class riêng (`.avatar-box`…) cho nội dung danh thiếp bên trong.
 * Vì vậy card ở đây tự có nền/chữ RIÊNG (không ăn theo màu bong bóng cha) — an
 * toàn tương phản dù đứng trong bong bóng xanh (li.right) hay trắng (li.left).
 * ============================================================================= */
.doc-card {
  color: var(--bb-txt-1);
  box-shadow: var(--bb-sh-2);
  gap: 0.35rem;
}

/* base.css:2035 `#image-preview-container .doc-card` đặt background-color +
 * border-radius với specificity (1 id + 1 class) CAO HƠN bare `.doc-card` (1
 * class) — nếu chỉ viết `.doc-card {background-color:...}` sẽ bị base thắng
 * (không phải do !important, do specificity). Phải bám đúng ngữ cảnh
 * #image-preview-container để ghi đè được (specificity bằng nhau, file này
 * nạp sau nên thắng). */
#image-preview-container .doc-card {
  background-color: var(--bb-bub-in);
  border-radius: var(--bb-r-bub);
}

/* Nhãn in đậm ("File name:", "Content type:", "Size:") mờ hơn giá trị, tạo
 * phân cấp — vẫn đủ tương phản trên nền .doc-card (đo: xem report P3). */
.doc-card b {
  color: var(--bb-txt-2);
  font-weight: 600;
}

/* Icon loại tệp: đặt trong ô vuông nền nhạt (--bb-chip) theo ngôn ngữ thiết kế
 * "icon trong ô vuông nền nhạt" — base.css:2031 đã set font-size:6rem cho icon
 * này (không đụng), ở đây chỉ thêm khung vuông bao quanh. */
.doc-card > i.material-icons.gray {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 6.5rem;
  height: 6.5rem;
  align-self: center;
  margin-bottom: 0.4rem;
  background-color: var(--bb-chip);
  border-radius: var(--bb-r-bub);
}

/* =============================================================================
 * MỤC 3 — Tệp đính kèm trong bong bóng (attachment.jsx)
 * ĐÂY LÀ LỖ HỔNG ĐÃ XÁC NHẬN: `.attachment` chưa hề được bulbal-messages.css
 * (đợt 1) hay bất kỳ partial nào chạm tới — trước phase này nó hiển thị y
 * nguyên CSS gốc Tinode (#2094f3), lạc tông với giao diện mới.
 * DOM thật (đọc từ attachment.jsx):
 *   div.attachment > div (không class, bọc icon) > i.material-icons.big.gray
 *                  > div.flex-column > div (tên tệp + span.small.gray cỡ tệp)
 *                                    > div (a[href] i.material-icons + "save"
 *                                           HOẶC span.light-gray khi vô hiệu
 *                                           HOẶC FileProgress khi đang tải/tải
 *                                           xuống — xem mục 7 .uploader)
 * ============================================================================= */
.attachment {
  align-items: center;
  gap: 0.65rem;
  padding: 0.3rem 0.15rem;
  border-radius: var(--bb-r-bub);
  min-height: var(--bb-touch);
}

/* Ô vuông icon loại tệp — div đầu tiên không có class riêng, phải bám cấu
 * trúc thật (div đầu tiên trong .attachment). Nền nhạt --bb-chip KHÔNG đổi
 * theo bong bóng trái/phải → icon .gray (--bb-txt-2) luôn đủ tương phản trên
 * đó dù đứng trong bong bóng xanh li.right (đo: report P1, 4.76:1 / 5.71:1). */
.attachment > div:first-child {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  background-color: var(--bb-chip);
  border-radius: var(--bb-r-bub);
}

.attachment .flex-column {
  gap: 0.15rem;
  min-width: 0; /* bắt buộc để ellipsis hoạt động trong flex item */
}

/* Dòng tên tệp + cỡ tệp: cắt bằng ellipsis khi quá dài. */
.attachment .flex-column > div:first-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.attachment .small.gray {
  font-size: var(--bb-fs-meta);
}

/* Trong bong bóng CỦA MÌNH (li.right, nền gradient xanh --bb-bub-out), 2 class
 * .small.gray (cỡ tệp) và .light-gray (nhãn "save" khi vô hiệu) đang bị ép màu
 * xám bởi base.css (color:...!important — .gray dòng 759-761, .light-gray dòng
 * 763-764) — xám trên nền xanh TRƯỢT AA. Đã thử color-mix() trắng 70-90%: cao
 * nhất chỉ 4.49:1 (report P4) — KHÔNG đạt 4.5. Phải dùng trắng đặc (trùng với
 * cặp trắng/gradient đã đo đạt AA ở đợt 2: 4.81:1 sáng / 5.17:1 tối), chấp
 * nhận mất phân cấp sáng/mờ giữa 2 dòng chữ trong ngữ cảnh này.
 * !important: khớp thuộc tính `color` !important của base.css:759-761 (.gray)
 * và :763-764 (.light-gray) — không thể thắng cascade nếu không có !important. */
#scroller > li.right .attachment .small.gray,
#scroller > li.right .attachment .light-gray {
  color: var(--bb-txt-on-brand) !important;
}

/* =============================================================================
 * MỤC 4 — Trình phát âm thanh (audio-player.jsx)
 * Widget này render CẢ 2 nơi: (a) lồng trong audio-recorder.jsx lúc nghe lại
 * bản ghi (prop short=true, không có class "large") và (b) trực tiếp làm nội
 * dung tin nhắn thoại đã gửi (prop short=false/undefined, có class "large") —
 * nằm thẳng trong bong bóng #scroller > li.left/right, nên cũng dính rủi ro
 * tương phản trên nền gradient như .attachment ở mục 3.
 * Waveform (canvas) và chấm phát (thumb) được VẼ BẰNG JS (BAR_COLOR/
 * BAR_COLOR_DARK/THUMB_COLOR hardcode trong audio-player.jsx) — KHÔNG thể đổi
 * màu qua CSS. Xem report mục "Giới hạn CSS" — cần sửa JS (ngoài phạm vi CSS
 * của phase này) nếu muốn đổi theo bong bóng/theme.
 * ============================================================================= */
.audio-player {
  gap: 0.6rem;
}

/* Nút play/pause: bọc thành nút tròn --bb-brand + icon trắng, đúng ngôn ngữ
 * thiết kế "nút play tròn --bb-brand + --bb-txt-on-brand". Test cả 2 rule vì
 * base.css có 2 mức specificity khác nhau cho icon: bare (short mode, không
 * class .large) và .material-icons.large (full mode) — base.css:3444 không có
 * !important nên ghi đè được mà không cần !important (specificity bằng nhau,
 * file này nạp sau nên thắng). */
.audio-player a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--bb-r-pill);
  background-color: var(--bb-brand);
  transition: background-color var(--bb-dur) var(--bb-ease);
}

/* Icon là ĐỒ HỌA (không phải chữ) → ngưỡng WCAG 1.4.11 (3:1), không phải 1.4.3
 * (4.5:1 cho text). Trắng/--bb-brand = 3.68:1, ĐẠT ngưỡng đồ họa (report P10). */
.audio-player .material-icons,
.audio-player .material-icons.large {
  color: var(--bb-txt-on-brand);
}

/* Trạng thái chưa sẵn sàng (.disabled): base.css:1911 có rule TOÀN CỤC
 * `.material-icons.disabled { color: var(--clr-text-tertiary) !important }`
 * (đã bridge sẵn về --bb-txt-3 ở bulbal-tokens.css) nên LUÔN thắng bất kể CSS
 * ở đây viết gì cho icon — không cần đụng màu icon. Chỉ đổi nền nút sang màu
 * trung tính (--bb-chip) để icon xám còn đọc được (report P11: 4.76:1/5.71:1),
 * thay vì xám-trên-xanh-brand dễ chìm. */
.audio-player a:has(.material-icons.disabled) {
  background-color: var(--bb-chip);
}

/* Waveform (canvas) — "nền rãnh --bb-line" theo ngôn ngữ thiết kế. Áp dụng cho
 * CẢ canvas lúc ghi âm trực tiếp (audio-recorder.jsx, không có class riêng,
 * xem mục 5) LẪN canvas.playback của audio-player — 1 rule chung, tự động giải
 * quyết luôn vấn đề tương phản vạch âm thanh xám-nhạt trên nền gradient xanh
 * (li.right) vì rãnh nền không đổi theo bong bóng. */
.audio canvas,
.audio-player .playback {
  background-color: var(--bb-line);
  border-radius: var(--bb-r-pill);
}

.audio-player .timer {
  color: var(--bb-txt-2);
  font-size: var(--bb-fs-meta);
}

/* Trong bong bóng CỦA MÌNH (li.right): .timer mặc định --bb-txt-2 (xám) trượt
 * AA trên nền gradient xanh. Dùng trắng đặc (đã đo đạt AA — report P5, trùng
 * cặp "Bong bóng gradient" đợt 2: 4.81:1/5.17:1). */
#scroller > li.right .audio-player .timer {
  color: var(--bb-txt-on-brand);
}

/* =============================================================================
 * MỤC 5 — Ghi âm (audio-recorder.jsx)
 * DOM: div.audio > a(xoá) > i.material-icons.gray | canvas (không class, lúc
 * đang ghi) HOẶC AudioPlayer short=true (lúc nghe lại) | div.duration | a(tạm
 * dừng/tiếp tục) > i.material-icons.red|gray | a(gửi) > i.material-icons.
 * Widget này CHỈ hiện lúc soạn tin (composer), KHÔNG BAO GIỜ nằm trong bong
 * bóng #scroller > li.left/right — không có rủi ro tương phản kiểu mục 3/4.
 * ============================================================================= */
.audio {
  background-color: var(--bb-chip);
  color: var(--bb-txt-1);
  padding: 0 0.75rem;
  gap: 0.5rem;
}

.audio .duration {
  color: var(--bb-txt-2);
}

/* Chấm đỏ "đang ghi": base.css:750-752 định nghĩa .red dùng --clr-danger-text
 * (màu cũ, KHÔNG được bridge sang hệ token mới) — đổi sang --bb-danger (token
 * nguy hiểm đã có sẵn từ đợt 2, xem bulbal-tokens.css mục 1b). Scope dưới
 * .audio để không đụng .red ở nơi khác ngoài phạm vi widget này.
 * !important: khớp thuộc tính `color` !important của base.css:750-752 (.red). */
.audio .material-icons.red {
  color: var(--bb-danger) !important;
}

/* =============================================================================
 * MỤC 6 — Ảnh đang tải lên (uploading-image.jsx)
 * DOM: div.inline-image > img (class do formatters.js truyền qua props, KHÔNG
 * cố định — thường trùng "inline-image", xem báo cáo) > div.rounded-container
 * > FileProgress (.uploader, xem mục 7).
 * LƯU Ý TRÙNG PHẠM VI: class "inline-image" còn được formatters.js gán trực
 * tiếp lên <img> của TIN NHẮN ẢNH ĐÃ GỬI (không qua uploading-image.jsx) — tức
 * là ảnh bình thường trong bong bóng cũng mang class này. Selector TAG
 * `#scroller .message-content img` (bulbal-messages.css, đợt 1, CẤM CHẠM) có
 * specificity cao hơn class `.inline-image` trần nên LUÔN thắng trong ngữ
 * cảnh tin nhắn — rule dưới đây chỉ thực sự hiển thị rõ khi .inline-image nằm
 * NGOÀI #scroller .message-content (vd trong khung xem trước trả lời). Không
 * xoá/đổi max-width/max-height gốc của base.css (0.1rem/36rem/24rem) để tránh
 * đụng hành vi đã có — chỉ bo lại góc cho khớp ngôn ngữ thiết kế. */
.inline-image {
  border-radius: var(--bb-r-bub);
}

.inline-image > .rounded-container {
  background-color: var(--bb-chip);
  box-shadow: var(--bb-sh-2);
  border-radius: var(--bb-r-pill);
}

/* =============================================================================
 * MỤC 7 — Thanh tiến trình tải lên/xuống (file-progress.jsx, dùng chung bởi
 * attachment.jsx VÀ uploading-image.jsx qua .rounded-container ở mục 6)
 * DOM: div.uploader > div (rãnh nền) > span (thanh chạy, width theo %) ; a
 * (huỷ, chỉ hiện khi < 99.9%) HOẶC text "finishing...".
 * ============================================================================= */
.uploader {
  align-items: center;
  gap: 0.5rem;
}

/* Thanh mảnh 3px theo ngôn ngữ thiết kế (rãnh --bb-line, chạy --bb-brand). Fill
 * trên rãnh là tương phản ĐỒ HỌA (WCAG 1.4.11, ngưỡng 3:1) chứ không phải chữ
 * — đo: report P7 (3.07:1 sáng / 3.59:1 tối, đạt ngưỡng 3:1). */
.uploader > div {
  height: 3px;
  padding: 0;
  width: 5rem;
  background-color: var(--bb-line);
  border-radius: var(--bb-r-pill);
  overflow: hidden;
}

.uploader > div > span {
  display: block;
  height: 100%;
  background-color: var(--bb-brand);
  border-radius: var(--bb-r-pill);
  transition: width var(--bb-dur) var(--bb-ease);
}

@media (prefers-reduced-motion: reduce) {
  .uploader > div > span {
    transition: none;
  }
}

.uploader a {
  color: var(--bb-txt-2);
}

.uploader a:hover {
  color: var(--bb-brand);
}

/* =============================================================================
 * MỤC 8 — Video tĩnh trong bong bóng (inline-video.jsx)
 * DOM: div.inline-video > img (props, xem mục 6) > div.play-control >
 * i.material-icons.white.x-big (có onClick) HOẶC img broken_video (không) >
 * div.duration (nếu có).
 * ============================================================================= */

/* LỖI THẬT phát hiện được: .white (base.css:746-748) dùng
 * `color: var(--clr-bright) !important`, và --clr-bright đã bị bulbal-tokens.css
 * (đợt 1) bridge thành `var(--bb-bg-side)` (nền sidebar) — ở CHẾ ĐỘ TỐI
 * --bb-bg-side là #111827 (gần đen), khiến icon "play" áng ra gần như ĐEN thay
 * vì TRẮNG, gần như vô hình trên nền tròn tối rgba(0,0,0,.4) phía sau. Cố định
 * bằng --bb-txt-on-brand (luôn #FFFFFF, không đổi theo theme) — đúng ý nghĩa
 * gốc của .white cho riêng icon play này.
 * !important: khớp thuộc tính `color` !important của base.css:746-748 (.white). */
.inline-video .play-control i.material-icons.white.x-big {
  color: var(--bb-txt-on-brand) !important;
}

/* Nền vòng tròn play-control (rgba(0,0,0,.4), base.css:2684) và khung
 * .duration (rgba trắng mờ cố định, base.css:2666-2675) CỐ Ý giữ nguyên: cả 2
 * đè lên ảnh/video bất kỳ màu sắc nào, không phải lên nền app — không có token
 * --bb-* nào phù hợp mà không phá vỡ tính bất biến-theme cần thiết ở đây (xem
 * report mục "Không đổi + lý do"). */
