/*! webme.vn theme — giao diện thương mại, xem tệp LICENSE ở gốc. © 2026 webme.vn */
/* ---------------------------------------------------------------------------
   Kiểu dáng các phần tử của trình dựng trang (front-end).

   Chỉ nạp trên trang CÓ nội dung dựng bằng trình dựng (xem hdnt_ub_assets),
   nên trang thường không phải tải thêm gì.

   Quy ước media query của theme: mỗi breakpoint gom vào ĐÚNG MỘT khối, đặt ở
   cuối file. Rải rác mỗi nơi một ít là cách chắc chắn nhất để rule sau âm thầm
   đè rule trước mà không ai nhận ra.
   Breakpoint dùng chung với theme: 1240 · 1100 · 860 · 620 · 380.
--------------------------------------------------------------------------- */

/* ---------- khung ---------- */
/* Trang dựng bằng trình dựng: không giới hạn bề ngang ở đây. Mỗi khối tự quyết
   bề ngang nội dung của nó (theo lưới trang, hẹp, hay tràn màn hình). */
.ub-page{width:100%;max-width:none;margin:0}

.ub-section{position:relative;background-repeat:no-repeat;background-size:cover}
.ub-section--img{background-position:center}
/* Nền chuyển sắc. Ảnh nền in `background-image` NỘI TUYẾN nên nó thắng quy tắc
   này — chọn cả hai thì ảnh nằm trên dải màu, đúng thứ tự người dựng mong đợi. */
.ub-section--grad{background-image:var(--ub-bg-grad)}
.ub-section--tall{display:flex;flex-direction:column}
.ub-section--vcenter{justify-content:center}
.ub-section--vend{justify-content:flex-end}
.ub-section-overlay{position:absolute;inset:0;background:#14120F;pointer-events:none}
/* Khung nội dung — dựng ĐÚNG cái hộp mà trang chủ đang dùng
   (`mx-auto max-w-container px-6` = max-width 1320px + lề trong 24px), để dải
   dựng kéo thả và dải dựng sẵn thẳng mép nhau. Trước đây khai
   `width:min(100% - 40px, 1406px)`: rộng hơn 86px mà lề trong lại 0 → chữ trong
   khối lệch khỏi chữ của trang chủ, chủ site nhìn ra ngay là "hai kiểu layout".

   ⚠️ `--wrap` là bề ngang phần NỘI DUNG, KHÔNG tính hai lề — 1272px, đúng con
   số đo được trên trang chủ (hộp 1320 của `max-w-container` trừ đi 2 × 24px lề
   trong của `px-6`). `box-sizing:border-box` bật toàn cục nên khung phải tự
   CỘNG BÙ lề vào `max-width`, không thì nội dung hẹp hơn đúng hai lần lề.
   Cùng quy ước ấy cho "khung hẹp" bên dưới: 760px là phần nội dung. */
.ub-section-in{position:relative;width:100%;max-width:calc(var(--wrap,1272px) + 2 * var(--ub-gutter,24px));padding-inline:var(--ub-gutter,24px);margin-inline:auto}
/* Dải có khối con "đặt tự do": khung nội dung mượn đúng "Chiều cao tối thiểu"
   của dải. Khối đặt tự do không chiếm chỗ nào nên khung vốn cao 0, và mọi toạ độ
   dọc tính theo phần trăm của 0 đều ra 0 (đo được: đặt 20% ra 0%). */
.ub-section--hasfree > .ub-section-in{min-height:inherit}
.ub-section-in--narrow{max-width:calc(760px + 2 * var(--ub-gutter,24px))}
.ub-section-in--full{max-width:none;padding-inline:0}

/* Chữ sáng trên nền tối. Đặt màu ở biến để mọi phần tử con thừa hưởng, thay vì
   phải viết lại từng selector cho bản nền tối. */
.ub-dark{--ink:#fff;--muted:rgba(255,255,255,.72);--muted-2:rgba(255,255,255,.6);--line:rgba(255,255,255,.18);--line-strong:rgba(255,255,255,.3);--card:rgba(255,255,255,.08);color:#fff}
.ub-dark h1,.ub-dark h2,.ub-dark h3,.ub-dark h4,.ub-dark p,.ub-dark li,.ub-dark dt,.ub-dark dd,.ub-dark summary{color:inherit}

/*
 * ⚠️ Khe NGANG phải kẹp theo phần trăm bề ngang hàng. Lưới 12 rãnh + `gap` thì
 * một cột span 12 ôm trọn **11 khe** — rãnh `minmax(0,1fr)` co được về 0 nhưng
 * KHE THÌ KHÔNG. Hàng hẹp hơn 11 × khe là cột rộng hơn cả hàng và trang tràn
 * ngang. Đo thật ở 414px với khe 40px: hàng 366px, cột 440px, tràn 50px — trên
 * máy tính không thấy gì vì hàng lúc nào cũng rộng hơn 440px.
 * `min(khe, 5%)` là chốt TOÁN HỌC chứ không phải áng chừng: 11 × 5% = 55% < 100%
 * nên không khổ nào tràn được. Màn rộng thì 5% luôn lớn hơn khe người dùng đặt,
 * nên số họ chọn giữ nguyên.
 */
.ub-row{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));row-gap:var(--ub-rgap,24px);column-gap:min(var(--ub-rgap,24px),5%)}
/* Chia đều N cột — đường thoát cho những con số lưới 12 không chia hết (5, 7).
   ⚠️ Cột con phải trả `grid-column` về `auto`: chúng đang mang `span var(--ub-span)`
   với span tính theo lưới 12, mà span 6 trong lưới 5 rãnh là tràn ra ngoài hàng. */
.ub-row--even{grid-template-columns:repeat(var(--ub-even,3),minmax(0,1fr))}
.ub-row--even > .ub-col{grid-column:auto}
/* Căn dọc của hàng — qua LỚP chứ không style nội tuyến, để khổ hẹp trả về được
   `stretch`. Xem chú thích ở `hdnt_ub_render_row()`. */
.ub-row--vstart{align-items:flex-start}
.ub-row--vcenter{align-items:center}
.ub-row--vend{align-items:flex-end}
/* Kiểu xếp "Vừa đủ nội dung" — ô "Kiểu xếp cột" của Hàng.
   Hàng thành khối flex TỰ XUỐNG DÒNG: mỗi cột rộng đúng bằng thứ bên trong,
   còn chỗ thì nằm cùng dòng, hết chỗ thì rớt xuống dòng dưới. Dùng cho dải
   "nhãn + mấy nút nhỏ" — lưới 12 phần cho bề ngang cố định nên dải ấy chỉ đẹp ở
   đúng một khổ, còn ép `span_sm="12"` thì mỗi nút chiếm trọn một hàng.
   ⚠️ `grid-column` của Cột hết nghĩa trong khối flex — đánh đổi đã biết, ô mô
   tả nói thẳng. Phải khai `flex:0 1 auto` chứ không để mặc định `0 1 auto` của
   trình duyệt tự lo: `.ub-col` khai `min-width:0` nên không có `max-width:100%`
   thì một cột nội dung dài hơn hàng sẽ kéo giãn cả hàng thay vì xuống dòng. */
.ub-row--fit{display:flex;flex-wrap:wrap;align-items:center}
.ub-row--fit > .ub-col{flex:0 1 auto;max-width:100%}
/* ---------------------------------------------------------------------------
   ĐỆM TRÊN/DƯỚI của Khối · KHE và KIỂU XẾP của Hàng — theo KHỔ MÀN HÌNH

   ⚠️ Đệm đi qua BIẾN + LỚP chứ không `padding-top:` nội tuyến: style nội tuyến
   thắng mọi quy tắc trong tệp nên media query của khổ nhỏ không bao giờ ăn.
   Bộ render chỉ chuyển sang lối này khi có nấc nhỏ, nên trang chỉ đặt khổ máy
   tính vẫn in nội tuyến y như cũ.

   ⚠️ Khe hàng phải giữ nguyên công thức kẹp `min(…, 5%)` ở CẢ ba nấc — đó là
   chốt toán học chống tràn ngang, xem chú thích ở `.ub-row`. Bỏ nó ở nấc nhỏ là
   đúng chỗ nó cần nhất.

   ⚠️ `--grid-md`/`--grid-sm` phải đứng SAU `.ub-row--fit` trong tệp: cùng hạng
   (0,1,0) nên chỉ thắng nhờ vị trí. Đó là lý do cụm này nằm ngay dưới.
--------------------------------------------------------------------------- */
.ub-section--pt{padding-top:var(--ub-spt)}
.ub-section--pb{padding-bottom:var(--ub-spb)}
@media (max-width:1024px){
  .ub-section--ptmd{padding-top:var(--ub-spt-md)}
  .ub-section--pbmd{padding-bottom:var(--ub-spb-md)}
  .ub-row{
    row-gap:var(--ub-rgap-md,var(--ub-rgap,24px));
    column-gap:min(var(--ub-rgap-md,var(--ub-rgap,24px)),5%)
  }
  .ub-row--fit-md{display:flex;flex-wrap:wrap;align-items:center}
  .ub-row--fit-md > .ub-col{flex:0 1 auto;max-width:100%}
  .ub-row--grid-md{display:grid;grid-template-columns:repeat(12,minmax(0,1fr))}
  .ub-row--grid-md > .ub-col{flex:none;max-width:none}
}
@media (max-width:620px){
  .ub-section--ptsm{padding-top:var(--ub-spt-sm)}
  .ub-section--pbsm{padding-bottom:var(--ub-spb-sm)}
  .ub-row{
    row-gap:var(--ub-rgap-sm,var(--ub-rgap-md,var(--ub-rgap,24px)));
    column-gap:min(var(--ub-rgap-sm,var(--ub-rgap-md,var(--ub-rgap,24px))),5%)
  }
  .ub-row--fit-sm{display:flex;flex-wrap:wrap;align-items:center}
  .ub-row--fit-sm > .ub-col{flex:0 1 auto;max-width:100%}
  .ub-row--grid-sm{display:grid;grid-template-columns:repeat(12,minmax(0,1fr))}
  .ub-row--grid-sm > .ub-col{flex:none;max-width:none}
}
/* ⚠️ Cột KHÔNG được `position:relative`.
   Khối "đặt tự do" bám vào khối chứa ĐÃ ĐỊNH VỊ gần nhất, nên cột định vị là nó
   bị nhốt trong đúng cái cột ấy: nút trong một cột 6/12 của hàng nút chỉ nhúc
   nhích được vài chục pixel, kéo ra giữa Slider hay lên góc phải là không tới.
   Để cột đứng ngoài thì mốc rơi về Trang chiếu / Banner / Khối — tức là cả vùng
   người dựng đang nhìn, đúng thứ họ định kéo tới. */
.ub-col{grid-column:span var(--ub-span,12);min-width:0}
/* Cắt phần tràn ra ngoài — bật bằng ô "Cắt phần tràn" của Cột và Khối.
   ⚠️ `hidden` chứ không `clip`: `clip` chặn luôn `position:sticky` của khối con,
   mà đầu trang dính là thứ có thật trong theme. */
.ub-ovh{overflow:hidden}
/* Khổ máy tính bảng: khai riêng thì mới có ba nấc thật (máy tính · tablet ·
   điện thoại) — thiếu nấc giữa thì trang nhảy thẳng từ 4 cột xuống 1. */
@media (max-width:1024px){
  .ub-col{grid-column:span var(--ub-span-md,var(--ub-span,12))}
}
.ub-col--filled,.ub-col--bordered{border-radius:var(--radius,16px)}
.ub-col--bordered{border:1px solid var(--line)}
/* DỒN NỘI DUNG THEO CHIỀU DỌC trong một cột (ô "Dồn nội dung theo chiều dọc").
   Khác ô "Căn dọc" của Hàng: ô kia xếp các CỘT trong lưới, ô này xếp các khối
   con bên trong MỘT cột — chỉ thấy khác khi cột cao hơn nội dung, mà ca hay gặp
   nhất là cột đã bật "Đặt tự do" (hộp `absolute` cao theo ô Chiều cao %).
   ⚠️ Bật `display:flex` thì con của cột hết là khối chảy dọc thông thường: lề
   dưới của khối cuối không còn "sập" vào nhau nữa. Đó là lý do quy tắc này chỉ
   sinh ra khi người dựng thật sự chọn một nấc, không phải mặc định của mọi cột.
   Ba nấc theo đúng thứ tự máy tính → bảng → điện thoại. */
.ub-cvd-start,.ub-cvd-center,.ub-cvd-end{display:flex;flex-direction:column}
.ub-cvd-start{justify-content:flex-start}
.ub-cvd-center{justify-content:center}
.ub-cvd-end{justify-content:flex-end}
@media (max-width:1024px){
  .ub-cvt-start,.ub-cvt-center,.ub-cvt-end{display:flex;flex-direction:column}
  .ub-cvt-start{justify-content:flex-start}
  .ub-cvt-center{justify-content:center}
  .ub-cvt-end{justify-content:flex-end}
}
@media (max-width:620px){
  .ub-cvm-start,.ub-cvm-center,.ub-cvm-end{display:flex;flex-direction:column}
  .ub-cvm-start{justify-content:flex-start}
  .ub-cvm-center{justify-content:center}
  .ub-cvm-end{justify-content:flex-end}
}

/* ---------- lưới ghép ----------
 * Cùng lưới 12 cột với .ub-row nên `span` ở hai khối nghĩa y như nhau, và span
 * không bao giờ vượt số cột — không có đường nào tràn ngang.
 * Khác Hàng ở `grid-auto-rows`: có chiều cao hàng cố định thì ô mới CAO ĐƯỢC
 * NHIỀU HÀNG, đó là toàn bộ lý do khối này tồn tại.
 * Khe cũng kẹp theo % như .ub-row: một ô span 12 ôm trọn 11 khe, mà rãnh co về
 * 0 được còn khe thì không.
 */
.ub-mosaic{
  display:grid;grid-template-columns:repeat(12,minmax(0,1fr));
  grid-auto-rows:var(--ub-mo-rh,190px);
  row-gap:var(--ub-mo-gap,16px);column-gap:min(var(--ub-mo-gap,16px),5%)
}
.ub-cell{
  position:relative;overflow:hidden;min-width:0;
  grid-column:span var(--ub-span,4);grid-row:span var(--ub-tall,1);
  display:flex;flex-direction:column;padding:22px;
  border-radius:var(--ub-mo-rad,var(--radius,16px))
}
.ub-cell--vstart{justify-content:flex-start}
.ub-cell--vcenter{justify-content:center}
.ub-cell--vend{justify-content:flex-end}
/* Ảnh nền là thẻ RIÊNG chứ không phải background của ô: phóng nhẹ khi rê chuột
   thì chỉ ảnh động, chữ đứng yên — phóng cả ô là chữ cũng to lên rồi nhoè. */
.ub-cell-bg{position:absolute;inset:0;background-size:cover;background-position:50% 50%;transition:transform .5s ease}
.ub-cell--zoom:hover .ub-cell-bg{transform:scale(1.06)}
.ub-cell-ov{position:absolute;inset:0;background:#161310}
/* Liên kết cả ô: thẻ <a> RỖNG phủ kín, nằm DƯỚI nội dung. Bấm chỗ trống là theo
   liên kết của ô, bấm đúng cái nút thì theo nút — không thẻ nào lồng thẻ nào.
   (<a> lồng <a> thì trình duyệt tự tách thành hai thẻ anh em và bố cục vỡ.) */
.ub-cell-link{position:absolute;inset:0;z-index:1}
.ub-cell-in{position:relative;z-index:2;min-width:0}
.ub-cell--light,.ub-cell--light .ub-heading-t,.ub-cell--light .ub-text{color:#fff}
.ub-cell--light .ub-eyebrow{color:#fff;opacity:.82}
@media (max-width:1024px){
  .ub-cell{grid-column:span var(--ub-span-md,var(--ub-span,4))}
}

.ub-gap{width:100%}
.ub-divider{border:0;border-top:1px solid var(--line);margin-block:24px}

.ub-tleft{text-align:left}
.ub-tcenter{text-align:center}
.ub-tright{text-align:right}

/* ---------- chữ ---------- */
.ub-heading{margin:0 0 20px}
/* Dòng nhỏ phía trên ăn màu NHẤN, không phải xám: ở trang chủ nó là nhãn dẫn
   dắt ("BỘ SƯU TẬP"), xám thì chìm hẳn vào phần mô tả ngay dưới. */
/* Giãn cách chữ đi qua biến; dự phòng .08em = đúng dáng cũ. */
.ub-eyebrow{margin:0 0 8px;font-size:13px;font-weight:700;letter-spacing:var(--ub-eb-ls,.08em);text-transform:uppercase;color:var(--accent)}
/* Tiêu đề dùng phông tiêu đề của Giao diện chung — trước đây thừa hưởng phông
   nội dung nên hai dải cạnh nhau ra hai kiểu chữ. */
.ub-heading-t{margin:0;font-family:var(--font-head,inherit);font-weight:var(--head-weight,700);font-size:34px;line-height:1.18;letter-spacing:-.01em}
.ub-heading--small .ub-heading-t{font-size:24px}
.ub-heading--large .ub-heading-t{font-size:44px}
.ub-heading--huge .ub-heading-t{font-size:56px;line-height:1.08}
.ub-heading-sub{margin:14px 0 0;max-width:64ch;color:var(--muted);font-size:16px;line-height:1.65}
.ub-tcenter .ub-heading-sub{margin-inline:auto}
.ub-tright .ub-heading-sub{margin-left:auto}

.ub-text{margin:0 0 20px;line-height:1.7}
.ub-text p{margin:0 0 1em}
.ub-text p:last-child{margin-bottom:0}
/* Trang dựng bằng khối KHÔNG còn bọc `.hdnt-content` (xem page.php), nên phần
   chữ tự lo định dạng cơ bản của nó. Thiếu mấy dòng này thì danh sách gõ trong
   ô soạn thảo mất hẳn dấu chấm đầu dòng — trình duyệt bỏ `list-style` theo
   preflight của Tailwind. */
.ub-text ul,.ub-text ol{margin:0 0 1em;padding-left:1.25rem}
.ub-text ul{list-style:disc}
.ub-text ol{list-style:decimal}
.ub-text li{margin-top:.25rem}
.ub-text strong,.ub-text b{font-weight:700}
.ub-text em,.ub-text i{font-style:italic}
.ub-text img{max-width:100%;height:auto}
.ub-text--lead{font-size:19px;line-height:1.65}
.ub-text--small{font-size:14px}
.ub-tcenter.ub-text{margin-inline:auto}

/* ---------------------------------------------------------------------------
   ⚠️ Khối dựng có thể nằm TRONG `.hdnt-content` (bài viết, mô tả sản phẩm), mà
   lớp đó khai `a{color:…;text-decoration:underline}`. Nên MỌI liên kết trong
   khối bị gạch chân — kể cả những chỗ không phải "link đọc được": nguyên thẻ
   sản phẩm là một <a>, nút bấm cũng là <a>, ô danh mục, thẻ bài viết. Chủ site
   báo bằng ảnh chụp: tên sản phẩm · giá · sao · nhãn nút đều gạch chân.

   Phần MÀU đã chữa tận gốc ở `src/input.css`: quy tắc kia nay bọc `:where()`
   nên chỉ còn (0,0,1) và nhường mọi lớp có màu riêng.
   ⚠️ Đừng khai `color:inherit` ở đây: nó sẽ là (0,2,1), cao hơn
   `.ub-btn--primary{color:#fff}` (0,1,0) → nhãn MỌI nút đổ về màu chữ của
   trang. Đã vấp thật, đo được `rgb(55,65,81)` trên cả nút màu tự chọn.

   Còn `text-decoration` thì vẫn phải tắt ở đây, vì nó KHÔNG phải thuộc tính kế
   thừa thường: gạch chân do thẻ cha vẽ, con không "ghi đè" được bằng cách khai
   màu của mình. Tắt theo chiều NGƯỢC (tắt hết trong khối rồi bật lại ở chỗ cần)
   chứ không liệt kê từng khối cần tắt: liệt kê thì khối viết sau này lại lặng
   lẽ rơi vào nhánh sai — đúng lỗi mà tài liệu của repo đã cảnh báo.
--------------------------------------------------------------------------- */
.hdnt-content [class^="ub-"] a,
.hdnt-content [class*=" ub-"] a,
.hdnt-content a[class^="ub-"],
.hdnt-content a[class*=" ub-"]{text-decoration:none}

/* Bật lại ở phần CHỮ do người dùng gõ — ở đó link vẫn phải trông như link.
   Cùng độ ưu tiên (0,2,1) với quy tắc trên nhưng đứng sau nên thắng. */
.hdnt-content .ub-text a,
.hdnt-content .ub-qa-a a,
.hdnt-content .ub-html a,
.hdnt-content .ub-card-x a{color:var(--accent);text-decoration:underline;text-underline-offset:3px}
/* Kiểu nút "Chữ có gạch chân" là kiểu DUY NHẤT cố ý giữ gạch chân. Phải viết
   `a.ub-btn--link` để đủ (0,2,1) — bằng quy tắc tắt ở trên rồi thắng nhờ đứng
   sau; `.hdnt-content .ub-btn--link` chỉ (0,2,0) nên vẫn thua. */
.hdnt-content a.ub-btn--link{text-decoration:underline;text-underline-offset:4px}

/* ---------- nút ---------- */
.ub-btn-wrap{margin:0 0 16px}
.ub-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:13px 26px;border-radius:var(--btn-radius,999px);border:1px solid transparent;font-weight:600;font-size:15px;line-height:1;text-decoration:none;cursor:pointer;transition:opacity .18s,background-color .18s,color .18s}
.ub-btn:hover{opacity:.86}
/*
 * Nút chính lấy dáng của nút CTA trang chủ: chuyển sắc từ màu nhấn sang một sắc
 * đậm hơn, kèm bóng cùng tông. Nút phẳng một màu đặt cạnh nút thật ở dải CTA là
 * nhìn ra ngay hai kiểu nút trên cùng một trang.
 * `color-mix` không hiểu thì `background` vẫn là màu nhấn phẳng — vẫn dùng được.
 */
.ub-btn--primary{
  background:var(--accent);
  background:linear-gradient(to right,var(--accent),color-mix(in srgb,var(--accent) 78%,#000));
  color:#fff;
  box-shadow:0 10px 24px -12px color-mix(in srgb,var(--accent) 70%,transparent)
}
.ub-btn--primary:hover{opacity:1;filter:brightness(1.06)}
.ub-btn--primary:active{transform:scale(.97)}
.ub-btn--soft{background:var(--soft);color:var(--ink)}
.ub-btn--outline{border-color:currentColor;color:inherit;background:transparent}
.ub-btn--link{padding:0;border-radius:0;background:none;color:inherit;text-decoration:underline;text-underline-offset:4px}
/* "Chữ trơn": như kiểu gạch chân nhưng KHÔNG gạch, và tắt luôn hiệu ứng mờ khi
   rê chuột — thứ ấy là ngôn ngữ của một cái nút, mà đây không còn là nút.
   ⚠️ `.hdnt-content` khai `a{text-decoration:underline}` ở (0,2,1) cho vùng nội
   dung, nên phải có bản `a.ub-btn--plain` đủ hạng ấy, đứng SAU. Thiếu nó thì
   kiểu này vẫn gạch chân đúng ở chỗ hay dùng nhất. */
.ub-btn--plain{padding:0;border-radius:0;background:none;border-color:transparent;color:inherit;text-decoration:none}
.ub-btn--plain:hover{opacity:1}
.hdnt-content a.ub-btn--plain{text-decoration:none}
.ub-btn--small{padding:9px 18px;font-size:14px}
.ub-btn--large{padding:16px 34px;font-size:16px}
.ub-btn--block{display:flex;width:100%}
.ub-btn .kico{width:18px;height:18px;background:currentColor}
/* Màu tự chọn đè lên kiểu dựng sẵn. Dùng biến chứ không viết thẳng vào thuộc
   tính style của thẻ: nhờ vậy trạng thái rê chuột / kiểu "Viền" vẫn mượn được
   đúng màu ấy mà không phải sinh thêm CSS nội tuyến. */
.ub-btn[style*="--btn-bg"]{background:var(--btn-bg);border-color:var(--btn-bg)}
.ub-btn[style*="--btn-fg"]{color:var(--btn-fg)}
.ub-btn--outline[style*="--btn-bg"]{background:transparent;border-color:var(--btn-bg);color:var(--btn-bg)}
.ub-btn--outline[style*="--btn-fg"]{color:var(--btn-fg)}
.ub-btn--link[style*="--btn-fg"]{color:var(--btn-fg);background:none}
/* Chữ hoa: kèm giãn chữ, không thì chữ hoa tiếng Việt dính vào nhau và các dấu
   thanh chồng lên nhau ở cỡ nhỏ. */
.ub-btn--upper{text-transform:uppercase;letter-spacing:.04em}
/* Lề trong · bóng đổ tự chọn. `--btn-radius` thì `.ub-btn` đã đọc sẵn nên không
   cần quy tắc riêng.
   ⚠️ Phải có giá trị dự phòng: `[style*="--btn-shadow"]` khớp CẢ khi chỉ đặt
   `--btn-shadow-h`, lúc đó biến kia chưa có giá trị. */
.ub-btn[style*="--btn-pad"]{padding:var(--btn-pad)}
/* Cỡ chữ và đệm của nút theo KHỔ MÀN HÌNH.
   ⚠️ Tên biến của hai nấc nhỏ (`--btnpad-*`, `--btnfz-*`) KHÔNG được là mở rộng
   của `--btn-pad`: cổng gác ngay trên bắt bằng chuỗi con, nên `--btn-pad-sm` sẽ
   bật quy tắc ấy với một biến chưa khai và nút mất luôn đệm ở khổ máy tính.
   ⚠️ Gác bằng LỚP, cùng lý do. Chưa đặt nấc nào thì không có lớp nào → kế thừa
   khổ lớn hơn.
   ⚠️ Khối `sm` đứng SAU khối `md`: hai khối chồng nhau ở dải nhỏ. */
.ub-btn--fz{font-size:var(--btnfz)}
@media (max-width:1024px){
  .ub-btn--bpmd{padding:var(--btnpad-md)}
  .ub-btn--fzmd{font-size:var(--btnfz-md)}
}
@media (max-width:620px){
  .ub-btn--bpsm{padding:var(--btnpad-sm)}
  .ub-btn--fzsm{font-size:var(--btnfz-sm)}
}
.ub-btn[style*="--btn-shadow"]{box-shadow:var(--btn-shadow,none)}
.ub-btn[style*="--btn-shadow-h"]:hover{box-shadow:var(--btn-shadow-h,none)}

/* ---------- danh sách, thẻ, bước ---------- */
/* Cỡ chữ do người dựng đặt; để trống = kế thừa như cũ. Dấu đầu dòng khai theo
   `em` nên tự co theo con số này. */
/* ⚠️ `margin:0;padding:0;list-style:none` là BẮT BUỘC, không phải cho đẹp. Đây
   là thẻ `<ul>`, mà mọi `<ul>` khác trong tệp này đều đã tự reset — riêng nó bị
   quên. Hậu quả: dấu chấm đen mặc định của trình duyệt hiện ở đầu MỖI mục, ngay
   cạnh icon tick, cộng thêm 40px thụt lề trái mà không ai đặt. Chủ site báo
   "chọn Dấu phân cách: Không có vẫn hiện bullet" (2026-08-12) — ô ấy vô can,
   dấu chấm là của trình duyệt. */
.ub-checks{display:grid;grid-template-columns:repeat(var(--ub-cols,1),minmax(0,1fr));gap:var(--ub-chk-gap,10px) 28px;font-size:var(--ub-chk-fs,inherit);margin:0;padding:0;list-style:none}
.ub-cards{display:grid;grid-template-columns:repeat(var(--ub-cols,3),minmax(0,1fr));gap:20px}
/* Trượt ngang chỉ bật ở khổ hẹp (xem media query bên dưới); ở máy tính đây vẫn
   là lưới. Con của dải cần điểm bám cuộn — nhắm `> *` vì Lưới thẻ nhận CẢ `card`
   lẫn `ibox`, khai theo một lớp con là lỡ mất loại kia. */
.ub-cards--slider-d > *,.ub-cards--slider-t > *,.ub-cards--slider-m > *{scroll-snap-align:start}
/* Dải trượt ở khổ MÁY TÍNH. Ba nấc độc lập nhau, nên mỗi nấc phải tự khai đủ
   cả dáng lưới lẫn dáng trượt — nấc dưới còn phải HOÀN TÁC nấc trên khi người
   dựng chọn ngược lại (máy tính trượt, bảng lưới chẳng hạn). */
.ub-cards.ub-cards--slider-d{grid-auto-flow:column;grid-auto-columns:calc((100% - (var(--ub-cols,3) - 1) * 20px) / var(--ub-cols,3));grid-template-columns:none;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:thin;padding-bottom:6px}

/*
 * ⚠️ `.ub-card` · `.ub-steps` · `.ub-faq` · `.ub-specs` TRƯỚC ĐÂY KHÔNG CÓ MỘT
 * DÒNG CSS NÀO ở theme này. Hàm render in ra `class="pg-card ub-card"`, mà
 * `.pg-card` là lớp của theme NGUỒN — theme này dùng Tailwind nên không có.
 * Hệ quả: kéo khối "Thẻ" hay "Các bước" ra thì được một khối chữ trần, không
 * nền, không viền, không khoảng cách. Đây là bài học ".shop-grid / .pcard-*
 * không có ở đây" lặp lại lần thứ tư — nay mỗi khối tự khai đủ phần của nó.
 *
 * Dáng bám theo đúng ngôn ngữ của trang chủ (`template-parts/tkbx/*`): nền
 * trắng, bo `--radius`, viền mảnh `--line`, bóng nhạt, rê chuột thì nhấc lên.
 */
.ub-card{
  display:flex;flex-direction:column;gap:10px;
  padding:24px 22px;
  background:var(--paper,#fff);
  border:1px solid var(--line,#EAECEF);
  border-radius:var(--radius,14px);
  box-shadow:0 6px 20px rgba(0,0,0,.05)
}
.ub-card-ico{
  display:grid;place-items:center;width:44px;height:44px;
  border-radius:999px;font-size:20px;
  color:var(--accent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--accent) 40%,transparent)
}
.ub-card-ico .kico{width:1em;height:1em}
.ub-card h3{margin:4px 0 0;font-size:17px;font-weight:700;line-height:1.35}
.ub-card p{margin:0;color:var(--muted);font-size:14px;line-height:1.65}
.ub-card--link{text-decoration:none;color:inherit;transition:transform .18s,box-shadow .18s}
.ub-card--link:hover{transform:translateY(-4px);box-shadow:0 10px 30px rgba(0,0,0,.08)}

/*
 * Các bước: vòng tròn nền kem có viền vàng + số thứ tự do CSS đếm — chèn/xoá
 * một bước không phải đánh số lại. Cùng khuôn với dải Quy trình ở trang chủ.
 */
.ub-steps{
  counter-reset:ub-step;
  display:grid;grid-template-columns:repeat(var(--ub-cols,3),minmax(0,1fr));
  gap:32px var(--ub-cgap,20px)
}
/* Huy hiệu số cũng phải co theo vòng tròn: để cứng 26px thì vòng tròn 40px bị
   nó che gần hết, còn vòng 96px thì nó thành một chấm nhỏ lạc lõng. Tỉ lệ .41
   giữ đúng kích thước cũ ở cỡ mặc định 64px (26,24px). */
.ub-step{
  position:relative;text-align:center;padding:0 6px;
  counter-increment:ub-step;--ub-step-b:calc(var(--ub-step-s,64px) * .41)
}
.ub-step::before{
  content:counter(ub-step);
  display:grid;place-items:center;
  width:var(--ub-step-s,64px);height:var(--ub-step-s,64px);margin:0 auto 16px;
  border-radius:999px;
  background:var(--band,#F6F1E7);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--accent) 40%,transparent);
  color:var(--accent);font-size:calc(var(--ub-step-s,64px) * .34);font-weight:800;line-height:1
}
/* Có icon riêng thì icon thay chỗ con số, và số lùi thành huy hiệu góc — giữ
   được cả hai thông tin mà không chồng lên nhau.
   ⚠️ Cỡ chữ bên trong tính THEO đường kính vòng tròn, không phải một con số cố
   định: nới vòng tròn lên 100px mà số vẫn 22px thì nó lọt thỏm giữa mảng nền. */
.ub-step-ico{
  position:relative;display:grid;place-items:center;
  width:var(--ub-step-s,64px);height:var(--ub-step-s,64px);margin:0 auto 16px;
  border-radius:999px;font-size:calc(var(--ub-step-s,64px) * .41);
  background:var(--band,#F6F1E7);
  color:var(--accent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--accent) 40%,transparent)
}
.ub-step-ico .kico{width:1em;height:1em}
/* Có icon thì vòng tròn số nhường chỗ, số lùi thành huy hiệu góc.
   ⚠️ Huy hiệu vẽ trên `.ub-step-ico::after`, KHÔNG phải `.ub-step::before`: nó
   phải neo vào chính VÒNG TRÒN. Neo vào khối bước thì hễ bước có đệm trong là
   icon tụt vào trong mà huy hiệu vẫn dính mép trên thẻ — hai thứ rời hẳn nhau.
   Nhờ vậy cũng bỏ được cái mẹo `left:50%` của bố cục icon-trái. */
.ub-step:has(.ub-step-ico)::before{display:none}
.ub-step-ico::after{
  content:counter(ub-step);
  position:absolute;top:calc(var(--ub-step-b) * -.15);
  left:calc(100% - var(--ub-step-b) * .69);
  display:grid;place-items:center;
  width:var(--ub-step-b);height:var(--ub-step-b);
  border-radius:999px;
  font-size:calc(var(--ub-step-b) * .46);font-weight:800;line-height:1;
  background:var(--accent);color:#fff
}
.ub-step h3{margin:0;font-size:var(--ub-step-t,14px);font-weight:700;line-height:1.4;letter-spacing:.02em;text-transform:uppercase}

/* Căn TRÁI trong bước — bố cục "số ở trên, chữ căn trái" của dải Quy trình.
   ⚠️ Chỉ đổi căn chữ, KHÔNG đụng `display`: bố cục `side` cũng căn trái nhưng
   bằng flex, gộp hai luật lại là bật `talign` trên dải `side` thì hỏng hàng.
   ⚠️ Vòng tròn số neo bằng `margin-inline:auto`, nên phải gỡ ở đây, bằng không
   chữ dạt trái mà cái vòng vẫn đứng giữa thẻ. */
.ub-steps--tal .ub-step{text-align:left}
.ub-steps--tal:not(.ub-steps--side) .ub-step::before,
.ub-steps--tal:not(.ub-steps--side) .ub-step-ico{margin-inline:0}

/* Tên bước chữ thường. Tách riêng khỏi `--tal`: hai thứ độc lập, gộp vào một
   lớp thì ai muốn căn trái mà GIỮ in hoa sẽ không có đường. */
.ub-steps--noup .ub-step h3{text-transform:none;letter-spacing:0}
.ub-step p{margin:6px 0 0;color:var(--muted);font-size:var(--ub-step-d,13px);line-height:1.65}

/* Bố cục "Icon trái, chữ phải" — hợp với dải một cột hoặc hai cột, và với bước
   có mô tả dài (kiểu trên–dưới đẩy chữ xuống rất sâu). */
.ub-steps--side .ub-step{
  /* Khe co theo cỡ icon: huy hiệu số nhô ra khỏi vòng tròn đúng .31 bề ngang
     của nó, nên khe cố định 14px thì icon 96px là huy hiệu gần chạm chữ (đo
     được hở 2px). Theo tỉ lệ thì hở 9px, bằng nhịp của cỡ mặc định. */
  display:flex;align-items:flex-start;text-align:left;padding:0;
  gap:max(14px,calc(var(--ub-step-s,64px) * .22))
}
.ub-steps--side .ub-step::before,
.ub-steps--side .ub-step-ico{margin:0;flex:none}
/* Cột chữ phải co được, không thì tên bước dài đẩy tràn ngang.
   ⚠️ `min-width:0` một mình là CHƯA đủ: nó cho cái hộp co lại, nhưng CHỮ bên
   trong vẫn tràn ra ngoài hộp nếu có từ dài hơn hộp. Đo ở 420px với icon 96px:
   hộp 56,9px mà chữ rộng 68px — tràn 11px, mà nhìn thì tưởng lỗi bố cục. */
.ub-steps--side .ub-step > div{min-width:0;overflow-wrap:break-word}

/* ── Đường nối giữa các bước ─────────────────────────────────────────────────
   Vẽ bằng `::after` của chính bước (`::before` là số thứ tự, `.ub-step-ico::after`
   là huy hiệu — chỗ này còn trống), nên HTML không thêm thẻ nào.
   Nằm ngang TÂM vòng tròn: .5 đường kính, trừ nửa chiều cao của chính mũi tên.
   ⚠️ Chỉ hiện ở khổ máy tính. Khổ hẹp các bước xuống nhiều hàng mà CSS không
   biết bước nào đứng cuối hàng (số cột là một BIẾN, `:nth-child()` không nhận
   biến), nên mũi tên ở cuối hàng sẽ chỉ vào chỗ trống. Dải Quy trình của giao
   diện cũng làm đúng vậy (`hidden xl:block`). */
.ub-steps--link-arrow .ub-step:not(:last-child)::after,
.ub-steps--link-line .ub-step:not(:last-child)::after{
  content:"";position:absolute;pointer-events:none;
  top:calc(var(--ub-step-s,64px) * .5 - 11px);
  left:calc(100% - var(--ub-cgap,20px) * .5 - 11px);
  width:22px;height:22px;
  background:color-mix(in srgb,var(--accent) 55%,transparent)
}
.ub-steps--link-arrow .ub-step:not(:last-child)::after{
  -webkit-mask:var(--i-arrow-right) center/contain no-repeat;
  mask:var(--i-arrow-right) center/contain no-repeat
}
/* Gạch ngang: một vạch mảnh giữa khe, không cần icon nào. */
.ub-steps--link-line .ub-step:not(:last-child)::after{
  top:calc(var(--ub-step-s,64px) * .5 - 1px);
  left:calc(100% - var(--ub-cgap,20px) * .5 - 11px);
  height:2px;border-radius:2px
}
/* Bố cục "icon trái" xếp dọc từng bước — nối ngang ở đó chỉ trỏ vào lề. */
.ub-steps--side .ub-step::after{display:none}

/* Câu hỏi thường gặp: mỗi câu là một thẻ có viền, mở bằng <details> nên chạy
   được cả khi JS lỗi và Ctrl+F vẫn tìm được chữ trong phần đang đóng. */
.ub-faq{display:flex;flex-direction:column;gap:10px}
.ub-qa{
  border:1px solid var(--line,#EAECEF);
  border-radius:var(--radius,14px);
  background:var(--paper,#fff);
  overflow:hidden
}
.ub-qa > summary{
  display:flex;align-items:center;gap:10px;
  padding:16px 20px;cursor:pointer;list-style:none;
  font-weight:700;font-size:15px;line-height:1.45
}
.ub-qa > summary::-webkit-details-marker{display:none}
/* Mũi tên vẽ bằng ::after của chính summary — không thêm thẻ nào vào HTML, và
   xoay khi mở nên không phải nuôi hai hình. */
.ub-qa > summary::after{
  content:"";flex:none;margin-left:auto;
  width:9px;height:9px;
  border-right:2px solid var(--accent);border-bottom:2px solid var(--accent);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform .2s
}
.ub-qa[open] > summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
.ub-qa[open] > summary{background:var(--band,#F6F1E7)}
.ub-qa-a{padding:4px 20px 18px;color:var(--muted);font-size:15px;line-height:1.7}
.ub-qa-a p{margin:0 0 .8em}
.ub-qa-a p:last-child{margin-bottom:0}

/* Bảng thông số: hai cột nhãn/giá trị, kẻ ngang mảnh — dễ đọc hơn hẳn bảng có
   đủ bốn cạnh viền. */
.ub-specs{display:table;width:100%;border-collapse:collapse}
.ub-specs > div{display:table-row}
.ub-specs > div > *{display:table-cell;margin:0;padding:12px 4px;border-bottom:1px solid var(--line,#EAECEF);vertical-align:top}
/* Cỡ chữ · độ đậm · màu nhãn đều đọc từ BIẾN nên chỉnh ở bảng thuộc tính là ăn,
   không phải quay ra ô "CSS riêng". */
.ub-specs > div > dt{width:38%;color:var(--ub-sp-lc,var(--muted));font-size:var(--ub-sp-ls,14px)}
.ub-specs > div > dd{font-weight:600;font-size:var(--ub-sp-vs,15px)}
/* Độ đậm nhãn: (0,2,2) nên thắng quy tắc gốc (0,1,2) ở trên. */
.ub-specs--lw-500 > div > dt{font-weight:500}
.ub-specs--lw-600 > div > dt{font-weight:600}
.ub-specs--lw-700 > div > dt{font-weight:700}
/* Icon trước nhãn. `<dt>` là ô của bảng (display:table-cell) nên KHÔNG flex được
   — icon để inline và căn theo đường giữa chữ.
   Icon khai `1em` ở `.kico` nên tự co theo cỡ chữ nhãn: đổi cỡ chữ một chỗ là
   cả dòng cân lại, không phải nhớ chỉnh thêm ô thứ hai. */
.ub-specs-i{display:inline-block;margin-right:8px;color:var(--accent);vertical-align:-.12em}
.ub-specs > div:last-child > *{border-bottom:0}
/* Đường kẻ DỌC giữa hai cột: vẽ bằng `border-right` của ô nhãn (`<dt>`).
   ⚠️ Ô nhãn có lề trong `padding:12px 4px` mà đường kẻ nằm ở MÉP ô, nên phải
   nới lề phải của nhãn / lề trái của giá trị cho chữ không dính vào vạch. */
.ub-specs--vline > div > dt{border-right:1px solid var(--line,#EAECEF);padding-right:20px}
.ub-specs--vline > div > dd{padding-left:20px}
/* Tắt kẻ ngang: bảng gọn, không vạch nào. Vẫn giữ được kẻ dọc nếu bật cả hai. */
.ub-specs--nohline > div > *{border-bottom:0}

/* ---------- số liệu ---------- */
.ub-stats{display:grid;grid-template-columns:repeat(var(--ub-cols,3),minmax(0,1fr));gap:20px;text-align:center}
/* Đường kẻ dọc giữa các ô số liệu.
   ⚠️ Kẻ ở mép TRÁI của từng ô rồi bỏ ô đầu đi — không dùng `:nth-child()` được
   vì số cột là một BIẾN, CSS không đọc được biến trong `:nth-child()`. */
.ub-stats--vline > *{border-inline-start:1px solid var(--line,#EAECEF)}
.ub-stats--vline > :first-child{border-inline-start:0}
.ub-stat{padding:20px 12px}
.ub-stat-n{margin:0;font-size:44px;font-weight:700;line-height:1;letter-spacing:-.02em}
.ub-stat-s{font-size:24px;margin-left:2px;color:var(--accent)}
.ub-stat-l{margin:10px 0 0;color:var(--muted);font-size:14px}
/* Cỡ chữ SỐ và NHÃN của dải Số liệu — biến khai trên khối chứa nên cả dải ăn
   theo, không phải chỉnh từng ô.
   ⚠️ `var(--ub-sn, 44px)` giữ nguyên con số cũ làm dự phòng, nên dải chưa đặt gì
   render y hệt từng pixel. */
.ub-stats .ub-stat-n{font-size:var(--ub-sn,44px)}
.ub-stats .ub-stat-l{font-size:var(--ub-sl,14px)}

/* ---------- thông số ---------- */
.ub-specs{margin:0}

/* ---------- lời khen ---------- */
.ub-quotes{display:grid;grid-template-columns:repeat(var(--ub-cols,2),minmax(0,1fr));gap:var(--ub-cgap,20px)}
/* Trượt ngang chỉ bật ở khổ ≤1100px (xem media query bên dưới); ở máy tính đây
   vẫn là lưới. Con của dải cần điểm bám cuộn. */
.ub-quotes--slider-sm > .ub-quote{scroll-snap-align:start}
.ub-quote{position:relative;margin:0;padding:26px;border:1px solid var(--line);border-radius:var(--radius,16px);background:var(--paper,#fff);box-shadow:0 1px 2px rgba(8,28,69,.05);display:flex;flex-direction:column;gap:16px}
/* Dấu nháy trang trí ở đầu thẻ — CHỈ ở bố cục mặc định (bố cục "người nói ở
   trên" giấu nó đi, không thì nó chen giữa người nói và nội dung). Là một flex
   item nên nằm đúng đầu thẻ mà không cần định vị tuyệt đối. */
.ub-quote::before{content:"\201C";color:var(--accent);font:800 40px/.5 Georgia,"Times New Roman",serif}
.ub-quote blockquote{margin:0;font-size:16px;line-height:1.7}
/* Khối người nói: avatar · tên/mô tả · sao (đẩy về mép phải). `margin-top:auto`
   ghim nó xuống CHÂN thẻ ở bố cục mặc định. */
.ub-quote-cap{display:flex;align-items:center;gap:12px;margin-top:auto}
.ub-quote-cap span{display:flex;flex-direction:column;min-width:0}
.ub-quote-cap small{color:var(--muted);font-size:13px}
.ub-quote-av{width:46px;height:46px;border-radius:50%;object-fit:cover;flex:none}
/* Sao vàng. `hdnt_stars()` in ra chuỗi ★ trần nên phải tô màu ở đây;
   `letter-spacing` cho năm ngôi sao thở ra như thẻ đánh giá mẫu. Màu đọc từ
   biến để người dựng đổi được ở bảng thuộc tính; mặc định vàng như ảnh mẫu. */
.ub-quote-stars{color:var(--ub-q-star,#f5a623);font-size:15px;line-height:1;letter-spacing:2px;white-space:nowrap}
/* Bản sao ở CHÂN thẻ — ngang hàng với tên, đẩy về mép phải. Bản ở ĐỈNH ẩn sẵn,
   chỉ bố cục "sao ở trên" mới bật. */
.ub-quote-stars--cap{margin-left:auto;flex:none}
.ub-quote-stars--top{display:none;margin-bottom:2px}
/* Bố cục "người nói + sao ở TRÊN, nội dung ở dưới": đảo thứ tự bằng `order`. */
.ub-quotes--head .ub-quote::before{display:none}
.ub-quotes--head .ub-quote-cap{order:-1;margin-top:0}
/* Bố cục "sao ở TRÊN · nội dung trong ngoặc · người nói ở chân": bật sao đỉnh,
   tắt sao chân, và thêm dấu ngoặc kép ôm nội dung. */
.ub-quotes--stars .ub-quote::before{display:none}
.ub-quotes--stars .ub-quote-stars--top{display:block}
.ub-quotes--stars .ub-quote-stars--cap{display:none}
.ub-quotes--stars blockquote::before{content:"\201C"}
.ub-quotes--stars blockquote::after{content:"\201D"}

/* ---------- ảnh ---------- */
.ub-image{margin:0 0 20px}
.ub-image-media{display:block;overflow:hidden}
.ub-image-media img{display:block;width:100%;height:auto}
/* Chiều cao cố định: ảnh phủ kín khung rồi cắt cho vừa. Đặt SAU quy tắc gốc để
   thắng `height:auto` ở trên (cùng độ ưu tiên, thắng theo thứ tự nạp). */
.ub-image-media--h img{height:100%;object-fit:cover}
.ub-image figcaption{margin-top:10px;font-size:13px;color:var(--muted)}
.ub-tcenter.ub-image{margin-inline:auto}
.ub-tright.ub-image{margin-left:auto}

/* Khung tỉ lệ cố định. Ảnh đặt absolute để KHÔNG đẩy giãn khung: để trong dòng
   chảy thì ảnh nguồn cao hơn tỉ lệ khung sẽ phá vỡ aspect-ratio (đã vấp ở
   .pcard-media của cửa hàng). */
.ub-ratio{position:relative;display:block}
.ub-ratio img,.ub-ratio iframe{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.ub-ratio--1-1{aspect-ratio:1/1}
.ub-ratio--4-3{aspect-ratio:4/3}
.ub-ratio--3-2{aspect-ratio:3/2}
.ub-ratio--16-9{aspect-ratio:16/9}
.ub-ratio--21-9{aspect-ratio:21/9}
.ub-ratio--3-4{aspect-ratio:3/4}
.ub-ratio--9-16{aspect-ratio:9/16}

.ub-gallery{display:grid;grid-template-columns:repeat(var(--ub-cols,3),minmax(0,1fr));gap:var(--ub-gap,12px)}
.ub-gallery-i{display:block;overflow:hidden;border-radius:var(--ub-radius,12px)}
.ub-gallery-i img{display:block;width:100%;height:auto}
.ub-gallery-a{display:block;transition:opacity .18s}
.ub-gallery-a:hover{opacity:.88}

/* ---------- biển lớn ---------- */
.ub-banner{position:relative;display:flex;min-height:var(--ub-h,480px);overflow:hidden;background-size:cover;background-position:center;color:#fff;isolation:isolate}
.ub-banner-ov{position:absolute;inset:0;background:#14120F;z-index:0}
.ub-banner-in{position:relative;z-index:1;width:100%;max-width:calc(var(--wrap,1272px) + 2 * var(--ub-gutter,24px));margin-inline:auto;padding:48px var(--ub-gutter,24px);display:flex;flex-direction:column;justify-content:center}
.ub-banner--vstart .ub-banner-in{justify-content:flex-start}
.ub-banner--vend .ub-banner-in{justify-content:flex-end}
.ub-banner--center .ub-banner-in{align-items:center;text-align:center}
.ub-banner--right .ub-banner-in{align-items:flex-end;text-align:right}
.ub-banner .ub-eyebrow{color:var(--ub-bn-ec,rgba(255,255,255,.82))}
.ub-banner-t{margin:0;font-size:52px;line-height:1.08;letter-spacing:-.02em;max-width:18ch;color:var(--ub-bn-tc,inherit)}
/* Mỗi dòng tiêu đề một thẻ khối (thay cho <br>). Tô màu riêng từng đoạn thì gõ
   thẳng thẻ HTML vào ô Tiêu đề — không cần ô "màu dòng nhấn" riêng. */
.ub-banner-tl{display:block}
.ub-banner-x{margin:16px 0 0;max-width:52ch;font-size:17px;line-height:1.6;color:var(--ub-bn-xc,rgba(255,255,255,.88))}
.ub-banner-b{display:flex;flex-wrap:wrap;gap:12px;margin:26px 0 0}
.ub-banner-b--s .ub-btn{padding:9px 18px;font-size:13px}
.ub-banner-b--l .ub-btn{padding:16px 34px;font-size:17px}
.ub-banner .ub-btn--outline{border-color:rgba(255,255,255,.7);color:#fff}
/* Khối con của biển: nằm trong cùng cột chữ, ngay dưới cụm nút. */
.ub-banner-c{margin:26px 0 0;width:100%}
.ub-banner--center .ub-banner-c{text-align:center}
.ub-banner--right .ub-banner-c{text-align:right}

/* ---------- Text Box: hộp chữ ĐẶT TỰ DO trong biển ----------
   Lớp `--free` phủ kín CẢ biển và nằm ngoài `.ub-banner-in`, nên toạ độ % tính
   theo cả tấm biển chứ không theo cột chữ (cột ấy có lề trong và bề ngang tối đa
   nên kéo hộp ra sát mép là không tới được).
   ⚠️ Lớp phủ tự nó KHÔNG bắt sự kiện chuột (`pointer-events:none`) — nó trải kín
   biển nên để bắt thì nó nuốt mọi cú bấm vào nút và chữ bên dưới; từng hộp con
   mới bật lại. Ngoài Banner thì Text Box không có `position:absolute` nào, nó là
   một khối bình thường trong dòng chảy. */
.ub-banner-c--free{position:absolute;inset:0;margin:0;z-index:3;pointer-events:none}
/* `.ub-free` = cùng cách đặt ấy nhưng cho MỌI phần tử (Tiêu đề · Đoạn chữ · Nút ·
   Ảnh…) và ở mọi khối chứa (Khối · Slider · Cột · Ô ghép), không cần lớp phủ
   riêng: khối tự nhấc khỏi dòng chảy và bám vào khối chứa gần nhất có
   `position:relative`.
   ⚠️ Khối chứa phải có chiều cao của riêng nó — mọi con đều "đặt tự do" thì nó
   cao 0. Khối có ô "Chiều cao tối thiểu"; Banner và Slider vốn đã có. */
/* Cụm đặt tự do dời xuống CUỐI tệp — xem mục "Đặt tự do" ở đó. */
.ub-textbox{box-sizing:border-box;min-width:40px;min-height:24px}
.ub-textbox--c{color:var(--tb-c)}
/* Vị trí · kích thước RIÊNG theo khổ màn hình — cùng lối var() lồng nhau với ô
   Chiều cao của Banner (điện thoại ← bảng ← máy tính). */
/* Liên kết cả biển khi CÓ khối con: thẻ <a> rỗng phủ kín nằm DƯỚI nội dung —
   bọc quanh nội dung là dựng lại cảnh <a> lồng <a> (khối con hay là một cái nút). */
.ub-banner-link{position:absolute;inset:0;z-index:1}
.ub-banner-in{position:relative;z-index:2}

/* ---------- cỡ chữ do người dựng đặt ----------
   Canh cửa bằng LỚP (`--tsz`/`--xsz`/`--esz`), KHÔNG phải [style*="--ub-bn-ts"]:
   chuỗi ấy khớp cả khi chỉ đặt `--ub-bn-ts-md` rồi `var(--ub-bn-ts)` hỏng ở mức
   tính giá trị (bẫy `--btn-shadow`). Lớp cho độ ưu tiên (0,2,0) nên thắng bộ cỡ
   chữ dựng sẵn (0,1,0) ở MỌI khổ mà không phải xếp lại thứ tự trong tệp; các nấc
   dưới đây xếp từ rộng đến hẹp nên nấc hẹp hơn luôn đè được nấc rộng.
   Không đặt gì = không có lớp = bộ cỡ chữ cũ giữ nguyên từng pixel. */
.ub-banner--tsz .ub-banner-t{font-size:var(--ub-bn-ts,52px)}
.ub-banner--xsz .ub-banner-x{font-size:var(--ub-bn-xs,17px)}
.ub-banner--esz .ub-eyebrow{font-size:var(--ub-bn-es,13px)}
@media (max-width:1240px){
  .ub-banner--tsz .ub-banner-t{font-size:var(--ub-bn-ts,44px)}
}
@media (max-width:1024px){
  .ub-banner--tsz .ub-banner-t{font-size:var(--ub-bn-ts-md,var(--ub-bn-ts,44px))}
  .ub-banner--xsz .ub-banner-x{font-size:var(--ub-bn-xs-md,var(--ub-bn-xs,17px))}
}
@media (max-width:1024px){
  .ub-banner--tsz .ub-banner-t{font-size:var(--ub-bn-ts-md,var(--ub-bn-ts,34px))}
  .ub-banner--xsz .ub-banner-x{font-size:var(--ub-bn-xs-md,var(--ub-bn-xs,16px))}
}
@media (max-width:620px){
  .ub-banner--tsz .ub-banner-t{font-size:var(--ub-bn-ts-sm,var(--ub-bn-ts-md,var(--ub-bn-ts,34px)))}
  .ub-banner--xsz .ub-banner-x{font-size:var(--ub-bn-xs-sm,var(--ub-bn-xs-md,var(--ub-bn-xs,16px)))}
}
@media (max-width:380px){
  .ub-banner--tsz .ub-banner-t{font-size:var(--ub-bn-ts-sm,var(--ub-bn-ts-md,var(--ub-bn-ts,29px)))}
}

/* ---------- video, bản đồ ---------- */
.ub-video{position:relative;overflow:hidden;background:#14120F;cursor:pointer}
.ub-video img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.ub-video-play{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:72px;height:72px;border:0;border-radius:50%;background:rgba(255,255,255,.94);cursor:pointer;display:grid;place-items:center;transition:transform .18s}
.ub-video-play:hover{transform:translate(-50%,-50%) scale(1.06)}
.ub-video-play span{width:0;height:0;margin-left:5px;border-style:solid;border-width:11px 0 11px 18px;border-color:transparent transparent transparent #14120F}
.ub-video iframe{border:0}

.ub-map{position:relative;height:var(--ub-h,360px);overflow:hidden;background:var(--card);display:grid;place-items:center;gap:10px;align-content:center}
/* Bản đồ hiện sẵn: bỏ nền kem và cách căn giữa của khung chờ. Để nguyên thì
   trong lúc iframe chưa tải xong khung loé lên một mảng kem rồi mới thành bản
   đồ — nhìn như trang chớp một cái ở đúng chỗ mắt đang nhìn. */
.ub-map--on{background:none;display:block}
.ub-map iframe{position:absolute;inset:0;width:100%;height:100%;border:0}

/* ---------- kích thước theo KHỔ MÀN HÌNH ----------
   Ảnh (rộng/cao) · Banner · Bản đồ đặt kích thước RIÊNG cho máy tính · máy tính
   bảng · điện thoại. Mỗi khổ một biến; khổ để trống KẾ THỪA khổ lớn hơn nhờ
   var() lồng nhau (điện thoại ← bảng ← máy tính). Media query đọc BIẾN chứ không
   ghim số, và đứng SAU quy tắc gốc để đè được (cùng độ ưu tiên, thắng theo thứ tự
   nạp). Ngưỡng 1024/620 khớp bề ngang khổ xem trước (bảng 834 · điện thoại 414).
   ⚠️ Chiều cao ảnh: base RỖNG mà khổ nhỏ có đặt thì `height:var(--ih)` vô hiệu →
   tỉ lệ khung (.ub-ratio) thắng ở máy tính, khổ nhỏ mới đè chiều cao cố định. */
.ub-image--rw{width:var(--iw);max-width:100%}
.ub-image-media--h{height:var(--ih)}
/* Chiều cao theo PHẦN TRĂM: vỏ <figure> phải cao theo khối chứa, không thì
   `height:var(--ih)` ở trên quy chiếu vào một vỏ cao AUTO và rơi về tỉ lệ ảnh
   gốc — ô "Chiều cao = 100%" chọn ở bảng trái sẽ không bao giờ ăn.
   Lớp `--fh` chỉ gắn khi người dựng nhập phần trăm (xem `hdnt_ub_render_image`).
   Khối chứa cao tự do thì `100%` thành `auto`, tức không đổi gì. */
.ub-image--fh{height:100%}
.ub-image--fh > a{display:block;height:100%}
/* LỚP PHỦ ẢNH — ô "Độ đậm lớp phủ" · "Màu lớp phủ" · "Kiểu phủ" của khối Ảnh.
   Màu (hoặc dải chuyển sắc) đi nội tuyến trên chính thẻ phủ; ĐỘ ĐẬM thì đi bằng
   biến theo khổ, y hệt --iw/--ih ở trên và vì đúng lý do đó: đặt thẳng `opacity`
   nội tuyến cho khổ máy tính là khổ nhỏ không đè lại được.
   ⚠️ `border-radius:inherit` — khung ảnh bo góc bao nhiêu thì lớp phủ bo bấy
   nhiêu; thiếu nó là bốn góc vuông thò ra ngoài tấm ảnh đã bo. */
.ub-image-media--ov{position:relative}
.ub-image-ov{position:absolute;inset:0;z-index:2;pointer-events:none;border-radius:inherit;opacity:var(--ub-iov,0)}
@media (max-width:1024px){
  .ub-image--rw{width:var(--iw-md,var(--iw))}
  .ub-image-media--h{height:var(--ih-md,var(--ih))}
  .ub-image-ov{opacity:var(--ub-iov-md,var(--ub-iov,0))}
  .ub-banner{min-height:var(--ub-h-md,var(--ub-h,480px))}
  .ub-map{height:var(--ub-h-md,var(--ub-h,360px))}
}
@media (max-width:620px){
  .ub-image--rw{width:var(--iw-sm,var(--iw-md,var(--iw)))}
  .ub-image-media--h{height:var(--ih-sm,var(--ih-md,var(--ih)))}
  .ub-image-ov{opacity:var(--ub-iov-sm,var(--ub-iov-md,var(--ub-iov,0)))}
  .ub-banner{min-height:var(--ub-h-sm,var(--ub-h-md,var(--ub-h,480px)))}
  .ub-map{height:var(--ub-h-sm,var(--ub-h-md,var(--ub-h,360px)))}
}
.ub-map-load{padding:12px 24px;border:1px solid var(--line-strong);border-radius:999px;background:var(--paper,#fff);font:inherit;font-weight:600;cursor:pointer}
.ub-map-q{margin:0;color:var(--muted);font-size:14px;text-align:center;padding-inline:20px}

/* ---------- logo ---------- */
.ub-logos{display:grid;grid-template-columns:repeat(var(--ub-cols,5),minmax(0,1fr));gap:24px;align-items:center}
.ub-logo{display:grid;place-items:center}
.ub-logo img{max-width:100%;width:auto;height:var(--ub-logo-h,40px);object-fit:contain}
.ub-logos--gray .ub-logo img{filter:grayscale(1);opacity:.62;transition:filter .2s,opacity .2s}
.ub-logos--gray .ub-logo img:hover{filter:none;opacity:1}
/* Dải logo chạy vô tận — CSS thuần, không một dòng JS.
   Danh sách in HAI lần rồi chạy đúng 50% bề ngang dải: khung hình cuối trùng
   khít khung hình đầu nên mắt không thấy điểm nối. Chạy hết MỘT bản rồi nhảy về
   đầu là có một nhịp giật thấy rõ.
   ⚠️ Chạy bằng `translate` (thuộc tính riêng), KHÔNG phải `transform`: hiệu ứng
   rê chuột của bộ thuộc tính chung dùng `transform`, mà animation luôn thắng
   khai báo thường — chạy bằng `transform` là mọi hiệu ứng ấy chết câm ở đây. */
.ub-logos--run{display:block;overflow:hidden}
/* ⚠️ `min-width:200%` là chốt TOÁN HỌC của "không bao giờ hở".
   Dải rộng đúng bằng nội dung (`max-content`), nên vài logo nhỏ chỉ được vài
   trăm pixel — hẹp hơn khung — và khách thấy một mảng TRỐNG chạy theo sau. Đo
   được ở khung 1320px: dải nhân đôi chỉ 480px, sau khi lặp thêm vẫn 1440px, tức
   mỗi bản 720px < 1320px. Ép dải rộng tối thiểu GẤP ĐÔI khung là mỗi bản luôn
   ≥ khung, mà `translate:-50%` thì đúng bằng MỘT bản — nên khung hình cuối trùng
   khít khung hình đầu ở mọi số lượng logo. Hai bản cùng `flex:1 0 auto` nên chia
   đôi phần dư bằng nhau; thiếu chỗ ấy thì hai bản lệch bề ngang và mối nối lộ. */
.ub-logos--run .ub-logos-t{display:flex;width:max-content;min-width:200%;animation:ub-run var(--ub-run-s,30s) linear infinite}
.ub-logos--run .ub-logos-s{flex:1 0 auto;display:flex;align-items:center;justify-content:space-around;gap:var(--ub-run-g,56px);padding-right:var(--ub-run-g,56px)}
.ub-logos--run .ub-logo{flex:none}
.ub-logos--run-r .ub-logos-t{animation-direction:reverse}
/* Dừng khi rê chuột: dải đang chạy thì không đọc nổi tên thương hiệu nào, mà
   chính ô "xám hoá" lại hứa hiện màu khi rê tới. */
.ub-logos--run:hover .ub-logos-t{animation-play-state:paused}
@keyframes ub-run{from{translate:0}to{translate:-50%}}

/* ---------- cửa hàng ---------- */
.ub-products-wrap{margin:0}
/* ⚠️ PHẢI tự khai display:grid. Lớp `.shop-grid` là của theme NGUỒN (nơi bộ dựng
   này được bê sang) — theme này dùng Tailwind, không có quy tắc nào đặt
   display:grid cho nó. Thiếu dòng này thì grid-template-columns vô nghĩa, mỗi
   thẻ là một khối và lưới thành MỘT sản phẩm mỗi hàng, tràn hết bề ngang: đúng
   triệu chứng "số cột không nhận". */
.ub-products{
  display:grid;
  grid-template-columns:repeat(var(--ub-cols,4),minmax(0,1fr));
  gap:var(--ub-pgap,24px);
  padding:0
}
/* Ba nấc cột thật (máy tính · máy tính bảng · điện thoại). Thiếu nấc giữa thì
   lưới nhảy thẳng từ 4 cột xuống 2, khổ tablet lãng phí nửa bề ngang.
   ⚠️ KHÔNG chèn thêm quy tắc cột nào ở các @media khác: chúng đứng sau trong tệp
   nên sẽ đè, và lựa chọn của người dựng lại thành vô nghĩa (đã vấp — có ba dòng
   khai cứng 4/3/2 ở 1240/1100/860px, gỡ rồi). */
@media (max-width:1024px){
  .ub-products{grid-template-columns:repeat(var(--ub-cols-md,3),minmax(0,1fr))}
}
@media (max-width:620px){
  .ub-products{grid-template-columns:repeat(var(--ub-cols-sm,2),minmax(0,1fr))}
}
/* Bật/tắt từng phần của thẻ bằng LỚP trên lưới, không sửa template thẻ — thẻ đó
   dùng chung với trang Cửa hàng, đụng vào là đổi luôn cả trang kia.
   Móc `hdnt-pc-*` gắn thêm trong product-card.php, thuần bổ sung. */
.ub-products.ub-no-title .hdnt-pc-title,
.ub-products.ub-no-rate .hdnt-pc-rate,
.ub-products.ub-no-price .hdnt-pc-price,
.ub-products.ub-no-cart .hdnt-pc-cart{display:none}
/* Tỉ lệ khung ảnh do người dựng chọn. Thẻ vốn khai aspect-square bằng Tailwind
   (0,1,0) — hai lớp ở đây là đủ thắng, không cần !important. */
.ub-products.ub-pr-1-1 .hdnt-pc-media{aspect-ratio:1/1}
.ub-products.ub-pr-4-3 .hdnt-pc-media{aspect-ratio:4/3}
.ub-products.ub-pr-3-4 .hdnt-pc-media{aspect-ratio:3/4}
.ub-products.ub-pr-16-9 .hdnt-pc-media{aspect-ratio:16/9}
/* ⚠️ aspect-ratio trong flex CỘT phải khai luôn width, không thì Safari coi bề
   ngang là suy ra được từ tỉ lệ và thôi kéo giãn — ô phình to hơn cả màn hình. */
.ub-products[class*="ub-pr-"] .hdnt-pc-media{width:100%;max-width:100%}
.ub-products.ub-pr-fit .hdnt-pc-img{object-fit:contain}
.ub-products .hdnt-pc{border-radius:var(--ub-prad,12px)}
/* Lề trong của khối chữ. Thẻ sản phẩm dùng chung với trang Cửa hàng nên ô chữ
   KHÔNG có lớp riêng — nhắm bằng :last-child, đừng thêm lớp vào product-card.php.
   (0,3,0) nên thắng lớp tiện ích `p-4` (0,1,0) của thẻ mà không cần !important. */
.ub-products .hdnt-pc > div:last-child{padding:var(--ub-cpad,16px)}
/* Nút mua đặt TRONG KHỐI CHỮ, mép phải hàng giá / đánh giá.
 *
 * Không sửa `product-card.php` để dời thẻ: tệp đó dùng chung với trang Cửa
 * hàng, đụng vào là đổi luôn cả trang kia. Nút vốn `position:absolute` neo vào
 * khung ẢNH — bỏ mốc neo ấy đi thì nó tự rơi xuống neo vào cả THẺ, tức là góc
 * dưới bên phải, đúng ngang hàng giá/sao. Một lớp CSS, không đổi một dòng HTML.
 *
 * ⚠️ `.hdnt-pc-media` mang `relative` của Tailwind (0,1,0) nên phải nhắm hai lớp
 * (0,2,0) mới gỡ được; và thẻ phải tự làm mốc neo vì nó không khai `relative`. */
/* ⚠️ KHÔNG gỡ mốc neo của khung ảnh — xem chú thích cùng chỗ ở `src/input.css`:
   gỡ là mọi con `absolute` (nhãn giảm giá, nút Xem nhanh) rơi theo. Nút giỏ
   rời khỏi ảnh bằng MARKUP trong `product-card.php`. */
.ub-products--cart-text .hdnt-pc-body{position:relative}
.ub-products--cart-text .hdnt-pc-cart{
  top:auto;left:auto;
  bottom:var(--ub-cpad-b,16px);right:var(--ub-cpad-r,16px)
}
.ub-products-more{margin:28px 0 0;text-align:center}

.ub-cats{display:grid;grid-template-columns:repeat(var(--ub-cols,4),minmax(0,1fr));gap:var(--ub-cgap,16px)}
.ub-cat{position:relative;display:flex;flex-direction:column;gap:10px;text-decoration:none;color:inherit;box-shadow:var(--ub-cshadow,none);border-radius:var(--ub-crad,var(--radius,16px));transition:box-shadow .2s,transform .2s}
.ub-cat:hover{box-shadow:var(--ub-cshadow-h,var(--ub-cshadow,none))}
/* Khung ảnh bọc riêng để `overflow:hidden` cắt được phần ảnh phóng to lúc rê
   chuột — đặt trên chính thẻ thì nó cắt luôn cả nhãn chữ đè lên. */
.ub-cat img{display:block;width:100%;aspect-ratio:1/1;object-fit:cover;border-radius:var(--ub-crad,var(--radius,16px));background:var(--card);transition:transform .35s,opacity .25s}
.ub-cat span{position:relative;font-weight:600;font-size:15px;text-align:center}
.ub-cat-n{display:block;font-style:normal;font-weight:500;font-size:12px;color:var(--muted);margin-top:2px}

/* Tỉ lệ ảnh do người dựng chọn. Khai trên .ub-cats rồi truyền xuống ảnh: đặt
   thẳng lên <img> thì mỗi biến thể phải viết lại cả bốn quy tắc. */
.ub-cats.ub-cr-1-1 .ub-cat img{aspect-ratio:1/1}
.ub-cats.ub-cr-4-3 .ub-cat img{aspect-ratio:4/3}
.ub-cats.ub-cr-3-4 .ub-cat img{aspect-ratio:3/4}
.ub-cats.ub-cr-16-9 .ub-cat img{aspect-ratio:16/9}

/* Lớp phủ: ::after trên chính thẻ, KHÔNG phải nền của <span> — phủ phải nằm
   trên ẢNH, mà span còn phải đọc được ở trên cùng. */
.ub-cats[style*="--ub-covl"] .ub-cat::after{content:"";position:absolute;inset:0;background:var(--ub-covl);opacity:var(--ub-covl-o,.35);border-radius:var(--ub-crad,var(--radius,16px));pointer-events:none}
.ub-cats--overlay .ub-cat::after,.ub-cats--badge .ub-cat::after,.ub-cats--oband .ub-cat::after{bottom:auto;height:100%}

/* Chữ ĐÈ lên ảnh */
.ub-cats--overlay .ub-cat,.ub-cats--badge .ub-cat,.ub-cats--oband .ub-cat{overflow:hidden}
.ub-cats--overlay .ub-cat span,.ub-cats--badge .ub-cat span,.ub-cats--oband .ub-cat span{position:absolute;left:0;right:0;z-index:1;color:#fff;text-shadow:0 1px 10px rgba(0,0,0,.5)}
.ub-cats--overlay .ub-cat span,.ub-cats--oband .ub-cat span{top:50%;transform:translateY(-50%);font-size:17px;letter-spacing:.02em;text-transform:uppercase}
.ub-cats--badge .ub-cat span{bottom:0;padding:12px 10px;background:rgba(22,19,16,.82);text-shadow:none;text-transform:uppercase;letter-spacing:.04em;font-size:14px}
/* "Chữ đè giữa ảnh, NHÃN MỜ": vẫn nằm giữa ảnh nhưng có dải nền mờ sau chữ như
   kiểu nhãn đáy ảnh. Chữ trắng trần trên ảnh bếp sáng màu gần như không đọc
   được — bóng chữ chỉ cứu được ảnh sẫm.
   ⚠️ Dải nền chạy hết bề ngang (left/right:0 kế thừa ở trên) chứ không bó sát
   chữ: tên danh mục dài ngắn khác nhau thì mỗi thẻ một bề ngang nhãn, cả dải
   nhìn lộn xộn. */
.ub-cats--oband .ub-cat span{padding:12px 10px;background:rgba(22,19,16,.82);text-shadow:none}
.ub-cats--overlay .ub-cat-n,.ub-cats--badge .ub-cat-n,.ub-cats--oband .ub-cat-n{color:rgba(255,255,255,.78)}

/* ---------- kiểu "Nút tròn nhỏ" ----------
 * Hàng nút bo tròn, ảnh thu về một chấm nhỏ bên trái tên. Đây là kiểu để dẫn
 * hướng nhanh (như dải lọc), không phải để khoe ảnh — nên KHÔNG nhận tỉ lệ
 * khung ảnh, không nhận bố cục so le.
 * ⚠️ Phải huỷ `grid-template-columns` của `.ub-cats`, và hai media query số cột
 * cũng phải trừ kiểu này ra — không thì xuống 1100px là hàng nút vỡ trở lại
 * thành lưới (đúng bẫy đã gặp với dải trượt). */
.ub-cats--pill{display:flex;flex-wrap:wrap;grid-template-columns:none;gap:var(--ub-cgap,12px)}
.ub-cats--pill .ub-cat{
  flex:0 0 auto;flex-direction:row;align-items:center;gap:10px;
  /* min-height = 34px ảnh + 2×6px đệm. Khai tường minh vì nút KHÔNG có ảnh
     (danh mục chưa đặt ảnh đại diện) sẽ thấp hơn hẳn — mà chỉ lộ ra khi nó rơi
     xuống hàng riêng, cùng hàng thì flex kéo giãn cho bằng nhau nên nhìn vẫn
     đều. Đo được: 46px khi cùng hàng, 28px khi xuống hàng. */
  min-height:46px;
  padding:6px 16px 6px 6px;border-radius:999px;
  background:var(--paper,#fff);box-shadow:0 0 0 1px var(--line,#EAECEF)
}
/* Ảnh là một chấm tròn nhỏ — khai đủ width/height nên không dính tỉ lệ khung. */
.ub-cats--pill .ub-cat img{width:34px;height:34px;aspect-ratio:1;border-radius:50%;flex:none}
.ub-cats--pill .ub-cat span{font-size:14px;text-align:left;white-space:nowrap}
.ub-cats--pill .ub-cat-n{display:inline;margin:0 0 0 6px;font-size:12px}
/* Không có ảnh thì nút vẫn phải cân: bù lại phần lề trái đã chừa cho chấm. */
.ub-cats--pill .ub-cat:not(:has(img)){padding-left:16px}

/* Rê chuột — NHẤC CẢ THẺ với MỌI lựa chọn.
 *
 * ⚠️ Trước đây chỉ hai lựa chọn `card`/`lift` mới nhấc, còn `zoom` (mặc định cũ,
 * và là giá trị module của site từng ghim) chỉ phóng mỗi phần ẢNH — nửa trên
 * động, nửa dưới đứng im, nhìn như thẻ bị tách đôi. Đổi mặc định không chữa
 * được những trang đã lưu `zoom` vào nội dung, nên phần NHẤC phải là nền chung
 * của mọi lựa chọn; ô "Rê chuột" chỉ còn quyết định chuyện xảy ra với tấm ảnh.
 *
 * Bốn con số SAO Y `.card-hover` trong `src/input.css` — thứ thẻ sản phẩm đang
 * dùng, để hai dải cạnh nhau cùng một nhịp. Có TEST đối chiếu.
 * ⚠️ Không mượn thẳng lớp `.card-hover`: nó là (0,1,0), hoà với `.ub-cat` và
 * thua theo thứ tự nạp (ub.css nạp sau theme.css) nên transition không ăn.
 * ⚠️ Bóng đổ vào BIẾN chứ không khai `box-shadow`: kiểu "Thẻ bo khối" có
 * `.ub-cats--boxed .ub-cat:hover` cũng (0,2,0) và đứng SAU trong tệp nên nó
 * thắng — đo được bóng vẫn là bóng cũ dù luật mới đã chạy. Qua biến thì mọi
 * kiểu thẻ đều nhận, mà ô "Bóng khi rê chuột" của người dựng vẫn đè lại được.
 */
.ub-cats[class*="ub-cats--hv-"]{--ub-cshadow-h:0 24px 50px rgba(8,28,69,.12)}
.ub-cats[class*="ub-cats--hv-"] .ub-cat{overflow:hidden;transition:transform .35s cubic-bezier(.2,.7,.3,1),box-shadow .35s ease}
.ub-cats[class*="ub-cats--hv-"] .ub-cat:hover{transform:translateY(-6px)}
.ub-cats[class*="ub-cats--hv-"] .ub-cat img{transition:transform .35s cubic-bezier(.2,.7,.3,1),opacity .25s}

/* Phần ẢNH thì tuỳ lựa chọn. Ảnh phóng bên trong khung nên khung phải cắt tràn
   — đã khai ở quy tắc chung trên. */
.ub-cats--hv-card .ub-cat:hover img,
.ub-cats--hv-zoom .ub-cat:hover img{transform:scale(1.06)}
.ub-cats--hv-fade .ub-cat:hover img{opacity:.82}

@media (prefers-reduced-motion:reduce){
  .ub-cats[class*="ub-cats--hv-"] .ub-cat:hover{transform:none}
  .ub-cats--hv-card .ub-cat:hover img,
  .ub-cats--hv-zoom .ub-cat:hover img{transform:none}
}

/* Trượt ngang: cùng bề ngang ô như lưới, chỉ khác là xếp trên một hàng cuộn
   được. `scroll-snap` để mỗi lần vuốt dừng đúng đầu một ô.
   ⚠️ Bề ngang ô tính THEO KHUNG chứ không phải 1fr — để `1fr` thì có 2 danh mục
   là mỗi ô phình ra nửa dải. */
.ub-cats--slider{display:grid;grid-auto-flow:column;grid-auto-columns:calc((100% - (var(--ub-cols,4) - 1) * var(--ub-cgap,16px)) / var(--ub-cols,4));grid-template-columns:none;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:thin;padding-bottom:6px}
.ub-cats--slider .ub-cat{scroll-snap-align:start}

.ub-pricing{display:grid;grid-template-columns:repeat(var(--ub-cols,3),minmax(0,1fr));gap:20px;align-items:start}
.ub-plan{position:relative;padding:30px 26px;border:1px solid var(--line);border-radius:var(--radius,16px);background:var(--paper,#fff);display:flex;flex-direction:column;gap:16px}
.ub-plan--hi{border-color:var(--accent);box-shadow:0 18px 44px -28px rgba(20,18,15,.5)}
.ub-plan-badge{position:absolute;top:-11px;left:26px;padding:4px 12px;border-radius:999px;background:var(--accent);color:#fff;font-size:12px;font-weight:600}
.ub-plan-n{margin:0;font-size:17px}
/* Mô tả ngắn dưới tên gói. Khe của .ub-plan là 16px nên phải kéo nó áp sát
   tên, không thì dòng mô tả trôi lửng giữa tên và giá. */
.ub-plan-x{margin:-12px 0 0;font-size:13px;color:var(--muted)}
.ub-plan-p{margin:0;font-size:32px;font-weight:700;letter-spacing:-.02em}
.ub-plan-p span{font-size:14px;font-weight:400;color:var(--muted);margin-left:4px}
.ub-plan-f{margin:0}
.ub-plan .ub-btn{margin-top:auto}

.ub-posts{display:grid;grid-template-columns:repeat(var(--ub-cols,3),minmax(0,1fr));gap:22px}
.ub-post{display:flex;flex-direction:column;gap:12px;text-decoration:none;color:inherit}
.ub-post-m{display:block;overflow:hidden;border-radius:var(--radius,16px)}
.ub-post-m img{display:block;width:100%;aspect-ratio:16/10;object-fit:cover;transition:transform .3s}
.ub-post:hover .ub-post-m img{transform:scale(1.03)}
.ub-post-b{display:flex;flex-direction:column;gap:6px}
.ub-post-d{font-size:13px;color:var(--muted)}
/* Là thẻ <h3> (khớp trang PHP) nên phải tự trung hoà lề + cỡ chữ mặc định của
   trình duyệt, không thì mỗi thẻ dôi một khoảng trắng ở trên. */
.ub-post-t{margin:0;font-size:17px;font-weight:600;line-height:1.35}
/* Cắt tiêu đề còn N dòng. `-webkit-box` là cách DUY NHẤT mọi trình duyệt hiện
   nay hiểu (`line-clamp` chuẩn còn chưa phổ biến), nên khai cả hai — bản chuẩn
   đứng sau để trình duyệt mới dùng nó. Canh cửa bằng LỚP: chỉ khi người dựng
   thật sự đặt, không thì tiêu đề hiện đủ như cũ. */
.ub-posts--clamp .ub-post-t{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--ub-post-tc,2);line-clamp:var(--ub-post-tc,2);overflow:hidden}

/* ---------- tab ---------- */
.ub-tabs{border-top:1px solid var(--line)}
.ub-tab{border-bottom:1px solid var(--line)}
.ub-tab-h{margin:0;padding:16px 0;font-size:16px}
.ub-tab-b{padding-bottom:20px}
/* Khi JS chạy được thì đổi sang dạng tab thật. Không có JS thì các phần nằm
   xuôi xuống dưới nhau như trên — nội dung vẫn đọc được đủ. */
.ub-tabs.is-ready{border-top:0}
.ub-tabs.is-ready .ub-tab-h{display:none}
.ub-tabs.is-ready .ub-tab{border-bottom:0;display:none}
.ub-tabs.is-ready .ub-tab.is-active{display:block}
.ub-tabs-nav{display:flex;flex-wrap:wrap;gap:4px;border-bottom:1px solid var(--line);margin-bottom:24px}
.ub-tabs-nav button{padding:12px 20px;border:0;border-bottom:2px solid transparent;background:none;font:inherit;font-weight:600;color:var(--muted);cursor:pointer}
.ub-tabs-nav button[aria-selected="true"]{color:var(--ink);border-bottom-color:var(--accent)}
/* Dải thẻ ở khổ điện thoại: MỘT hàng, trượt ngang nếu không đủ chỗ.
   `flex-wrap:wrap` + padding 20px nghĩa là ba thẻ chữ tiếng Việt luôn rớt xuống
   hai hàng dưới 620px — đo trên trang thật: nav 366px mà ba thẻ cộng lại 446px,
   nên gạch chân của thẻ đang chọn nằm lửng giữa khối và nhìn như lỗi.
   ⚠️ `nowrap` PHẢI đi kèm `overflow-x:auto`. Chỉ `nowrap` thì thẻ tràn ra ngoài
   và kéo cả trang cuộn ngang — đổi một lỗi lấy một lỗi nặng hơn. Có `auto` thì
   trường hợp xấu nhất (nhiều thẻ, chữ dài) vẫn là một hàng vuốt được.
   ⚠️ Giấu thanh cuộn nhưng KHÔNG dùng `overflow:hidden`: `hidden` cũng cắt, mà
   lại chặn luôn thao tác vuốt, tức thẻ cuối không cách nào tới được. */
@media (max-width:620px){
.ub-tabs-nav{flex-wrap:nowrap;overflow-x:auto;gap:2px;scrollbar-width:none;-ms-overflow-style:none}
.ub-tabs-nav::-webkit-scrollbar{display:none}
.ub-tabs-nav button{padding:10px 12px;font-size:14px;white-space:nowrap}
}

/* ---------- trình chiếu ---------- */
.ub-slider{position:relative}
.ub-slider-t{display:grid;grid-auto-flow:column;grid-auto-columns:calc((100% - (var(--ub-per,1) - 1) * 16px) / var(--ub-per,1));gap:16px;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.ub-slider-t::-webkit-scrollbar{display:none}
/* Điểm bám cuộn cho MỌI ô con trực tiếp — nay slider nhận thẳng Ảnh/Biển lớn
   làm trang chiếu, không chỉ `.ub-slide`. `min-width:0` cho ô co được trong
   grid, không thì ảnh rộng đẩy tràn. `margin:0` xoá lề dưới của Ảnh (20px) để
   nó không đội cao cả dải trượt ngang. */
.ub-slider-t > *{scroll-snap-align:start;min-width:0;margin:0}
/* Một trang chiếu là khối chứa có `position:relative` để Text Box "đặt tự do"
   bám được vào chính nó.
   ⚠️ Text Box đặt tự do phải nằm TRONG một trang chiếu (hoặc một Banner) chứ
   không phải con trực tiếp của Slider: dải trượt khai `overflow-x:auto` nên nó
   xén mọi thứ nằm ngoài khung cuộn — hộp sẽ mất một phần hoặc mất hẳn. */
.ub-slide{position:relative}
/* Căn ô ở GIỮA khung thay vì mép trái — hợp khi mỗi lượt xem nhiều ô. */
.ub-slider--center .ub-slider-t{scroll-snap-align:center}
.ub-slider--center .ub-slider-t > *{scroll-snap-align:center}

/* ---------- Kiểu chuyển xếp chồng (mờ · trượt chồng · phóng to) ----------
   Bốn kiểu này xếp MỌI trang chiếu vào CÙNG một ô lưới (`grid-area:1/1`) rồi
   đổi bằng transition. Dùng lưới một ô chứ không `position:absolute`: lưới giữ
   chiều cao bằng ô CAO NHẤT, còn absolute thì khối chứa cao 0 và phải tự đo
   chiều cao bằng JS.
   ⚠️ Tắt hẳn cuộn (`overflow:hidden` + bỏ scroll-snap): còn cuộn thì lúc kéo
   tay dải vẫn dịch được vài pixel trong khi ô không đổi — nhìn như dải hỏng. */
.ub-slider--fx .ub-slider-t{
  grid-auto-flow:row;grid-template-columns:100%;grid-auto-columns:auto;gap:0;
  overflow:hidden;scroll-snap-type:none
}
.ub-slider--fx .ub-slider-t > *{
  grid-area:1/1;opacity:0;pointer-events:none;scroll-snap-align:none;
  transition:opacity var(--ub-fxms,600ms) ease,transform var(--ub-fxms,600ms) ease
}
/* ⚠️ CHƯA chạy JS thì trang chiếu ĐẦU phải hiện sẵn. Không có luật này thì mọi
   ô đều `opacity:0`, dải trắng trơn cho tới khi JS gắn `is-on` — hero mở trang
   ra là một mảng trống rồi mới mờ dần hiện lên (đo được 80–90% ở mốc 500ms),
   mà đây đúng là thứ khách nhìn thấy đầu tiên. Máy tắt JS thì cũng còn thấy
   một ảnh thay vì không có gì.
   Canh cửa bằng `:not([data-ub-ready])` — JS đặt cờ ấy NGAY đầu hàm rồi mới gắn
   `is-on` trong cùng một nhịp, nên không có khung hình nào ở giữa để nháy. */
.ub-slider--fx:not([data-ub-ready]) .ub-slider-t > :first-child{opacity:1;transform:none}
/* Mờ chồng — đúng kiểu hero của trang chủ (assets/js/tkbx.js): chỉ đổi độ mờ,
   không dịch chuyển. */
.ub-slider--fx-fade .ub-slider-t > *{transform:none}
/* Trượt chồng: ô chờ ở phía SẼ đi vào, ô đi ra trượt về phía đối diện. Chiều do
   lớp `.is-bwd` trên dải quyết định (JS đặt trước khi đổi ô). */
.ub-slider--fx-slide .ub-slider-t > *{transform:translateX(100%)}
.ub-slider--fx-slide .ub-slider-t.is-bwd > *{transform:translateX(-100%)}
.ub-slider--fx-slide .ub-slider-t > .is-out{opacity:1;transform:translateX(-100%)}
.ub-slider--fx-slide .ub-slider-t.is-bwd > .is-out{transform:translateX(100%)}
.ub-slider--fx-vslide .ub-slider-t > *{transform:translateY(100%)}
.ub-slider--fx-vslide .ub-slider-t.is-bwd > *{transform:translateY(-100%)}
.ub-slider--fx-vslide .ub-slider-t > .is-out{opacity:1;transform:translateY(-100%)}
.ub-slider--fx-vslide .ub-slider-t.is-bwd > .is-out{transform:translateY(100%)}
/* Phóng to mờ dần: ô vào lớn dần từ 1.08, ô ra nhỏ lại — không cần biết chiều. */
.ub-slider--fx-zoom .ub-slider-t > *{transform:scale(1.08)}
.ub-slider--fx-zoom .ub-slider-t > .is-out{transform:scale(.94)}
/* ⚠️ Quy tắc ô ĐANG HIỆN phải đứng SAU cả bốn kiểu trên: nó cùng độ ưu tiên
   (0,3,0) với `.ub-slider--fx-slide .ub-slider-t.is-bwd > *`, nằm trước là hoà
   rồi thua theo thứ tự nạp — ô đang hiện bị đẩy ra khỏi khung và dải trắng
   trơn. */
.ub-slider--fx .ub-slider-t > .is-on{opacity:1;transform:none;pointer-events:auto;z-index:1}
/* ⚠️ Ô "Thêm phần tử" của khối chứa RỖNG cũng là con trực tiếp của dải, nên nó
   dính luôn `opacity:0` ở trên — Slider rỗng đặt kiểu mờ chồng thì không nhìn
   thấy mà cũng không bấm được, tức là không thêm nổi trang chiếu ĐẦU TIÊN. Đặt
   sau `.is-on` để cùng (0,3,0) thì thắng theo thứ tự nạp. */
.ub-slider--fx .ub-slider-t > .ub-ph{opacity:1;transform:none;pointer-events:auto}
/* Tắt hiệu ứng đúng một khung hình, để JS trả ô vừa đi ra về vị trí chờ mà
   không có gì bay ngang qua khung. Ba lớp (0,3,0) nên thắng quy tắc khai
   transition ở trên (0,2,0) mà không cần `!important`. */
.ub-slider--fx .ub-slider-t > .is-snap{transition:none}
/* Cùng công tắc ấy nhưng đặt trên CẢ DẢI: lúc đổi chiều, mọi ô phải nhảy sang
   vị trí chờ mới tức thì. Để nguyên hiệu ứng thì ô sắp vào bắt đầu chạy về phía
   kia rồi bị `is-on` đổi đích — bấm "lùi" mà ảnh vẫn trượt như bấm "tới". */
.ub-slider--fx .ub-slider-t.is-snap > *{transition:none}
/* Tôn trọng "giảm chuyển động": bỏ phần DI CHUYỂN, giữ mờ dần — tắt hết thì ô
   nhảy phát một, khách không kịp nhận ra dải vừa đổi ảnh. */
@media (prefers-reduced-motion:reduce){
  .ub-slider--fx .ub-slider-t > *{transform:none!important;transition:opacity var(--ub-fxms,600ms) ease}
}
/* Nút tối cho ảnh sáng màu: nền sẫm, mũi tên trắng. */
.ub-slider--navdark .ub-slider-n{background:rgba(22,19,16,.82);border-color:transparent}
.ub-slider--navdark .ub-slider-n::before{border-color:#fff}
/* Kéo bằng chuột: đổi con trỏ để biết cầm được, và khoá chọn chữ khi đang kéo. */
.ub-slider[data-drag] .ub-slider-t{cursor:grab}
.ub-slider[data-drag] .ub-slider-t.is-drag{cursor:grabbing;scroll-snap-type:none;user-select:none}
.ub-slider-n{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:44px;border:1px solid var(--line);border-radius:50%;background:rgba(255,255,255,.92);cursor:pointer;display:grid;place-items:center;z-index:2}
.ub-slider-n:disabled{opacity:.35;cursor:default}
.ub-slider-n--prev{left:10px}
.ub-slider-n--next{right:10px}
.ub-slider-n::before{content:"";width:9px;height:9px;border-top:2px solid var(--ink);border-right:2px solid var(--ink)}
.ub-slider-n--prev::before{transform:rotate(-135deg);margin-left:3px}
.ub-slider-n--next::before{transform:rotate(45deg);margin-right:3px}
/* Kiểu nút VUÔNG: bỏ bo tròn (giữ nền + viền) — "không bo tròn thành hình tròn". */
.ub-slider--navsquare .ub-slider-n{border-radius:6px}
/* MŨI TÊN TRẦN: bỏ hẳn nền + viền, chỉ còn dấu ‹ › đậm hơn, kèm bóng nhẹ để đọc
   được trên mọi ảnh. KHÔNG khai màu ở đây → mũi tên vẫn theo base (--ink) hoặc
   nút tối (#fff), nên "Mũi tên trần" ghép được với cả "Màu nút · Tối". */
.ub-slider--navbare .ub-slider-n{background:none;border:0}
.ub-slider--navbare .ub-slider-n::before{width:12px;height:12px;border-top-width:3px;border-right-width:3px;filter:drop-shadow(0 1px 2px rgba(0,0,0,.45))}

/* ---------- bản tin ---------- */
.ub-nl{border-radius:var(--radius,16px);background:var(--band);padding:40px 32px}
.ub-nl.ub-dark{background:#14120F}
.ub-nl-in{max-width:620px;margin-inline:auto;text-align:center}
.ub-nl-t{margin:0;font-size:26px}
.ub-nl-x{margin:10px 0 0;color:var(--muted)}
.ub-nl-f{display:flex;gap:10px;margin-top:22px}
.ub-nl-f input{flex:1;min-width:0;padding:13px 18px;border:1px solid var(--line);border-radius:999px;background:var(--paper,#fff);font:inherit}

/* ---------------------------------------------------------------------------
   Đầu trang / chân trang dựng bằng kéo thả
--------------------------------------------------------------------------- */

/* .site-header của theme đã lo sticky, nền mờ và viền dưới — ở đây chỉ gỡ phần
   đệm dành cho bản dựng sẵn để dải tự dựng làm chủ khoảng cách của nó. */
.ub-header{padding:0}
.ub-bar-row{position:relative;display:flex;align-items:center}
.ub-bar-row--line{border-bottom:1px solid var(--line)}
.ub-bar-in{
  width:100%;max-width:calc(var(--wrap,1272px) + 2 * var(--ub-gutter,24px));padding-inline:var(--ub-gutter,24px);margin-inline:auto;
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:16px
}
.ub-bar-in--full{max-width:none}
.ub-zone{display:flex;align-items:center;min-width:0}
.ub-zone--left{justify-content:flex-start}
.ub-zone--center{justify-content:center}
.ub-zone--right{justify-content:flex-end}

.ub-logo-el{flex:none}
.ub-logo-el .ub-logo-img{height:var(--ub-logo-h,26px);width:auto;max-width:none}
.ub-logo-t{white-space:nowrap}

.ub-menu{position:static;min-width:0}
.ub-menu-h{margin:0 0 12px;font-size:14px;font-weight:600}
.ub-menu-list{display:flex;gap:24px;margin:0;padding:0;list-style:none;font-size:14px}
.ub-menu--col .ub-menu-list{flex-direction:column;gap:9px}
.ub-menu--row .ub-menu-list{align-items:center;flex-wrap:wrap}
.ub-menu-list a{white-space:nowrap}

/* Menu con: hiện khi rê chuột HOẶC khi focus bằng bàn phím (:focus-within) —
   chỉ dùng :hover thì người dùng bàn phím không bao giờ mở được menu con. */
.ub-menu--row .ub-menu-list li{position:relative}
.ub-menu--row .sub-menu{
  position:absolute;top:100%;left:-14px;z-index:40;display:none;
  min-width:200px;flex-direction:column;gap:0;margin:0;padding:8px 0;list-style:none;
  background:var(--paper,#fff);border:1px solid var(--line);border-radius:12px;
  box-shadow:0 18px 44px -22px rgba(20,18,15,.4)
}
.ub-menu--row .sub-menu a{display:block;padding:8px 16px}
.ub-menu--row li:hover > .sub-menu,
.ub-menu--row li:focus-within > .sub-menu{display:flex}

.ub-hicon{display:flex;align-items:center;gap:7px;position:relative;flex:none}
.ub-hicon-t{font-size:14px;white-space:nowrap}
.ub-htext{display:flex;align-items:center;gap:7px;min-width:0}
.ub-htext .kico{width:16px;height:16px;flex:none;background:currentColor}
.ub-htext span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.ub-search--box form{display:flex;gap:8px}
.ub-search--box input{
  width:min(46vw,260px);height:38px;padding:0 14px;font:inherit;font-size:14px;
  border:1px solid var(--line);border-radius:10px;background:var(--paper,#fff);color:var(--ink)
}
.ub-search--box button{
  height:38px;padding:0 16px;border:0;border-radius:10px;
  background:var(--accent);color:#fff;font:inherit;font-size:14px;font-weight:600;cursor:pointer
}
.ub-social{display:flex;align-items:center;gap:12px}

/* Nút ba gạch: chỉ hiện ở màn hình hẹp, xem khối breakpoint bên dưới. */
.ub-menu--burger .hdr-burger{display:none}
.ub-menu .hdr-panel{
  position:absolute;top:100%;left:0;right:0;z-index:50;display:none;
  padding:10px 20px 14px;background:var(--paper,#fff);border-bottom:1px solid var(--line);
  box-shadow:0 18px 40px -24px rgba(20,18,15,.45)
}
.ub-menu .hdr-panel.is-open{display:block}
.ub-menu .hdr-panel .ub-menu-list{flex-direction:column;gap:2px;font-size:16px}
.ub-menu .hdr-panel a{display:block;padding:9px 2px}

/* ---------- mega menu ---------- */
.ub-mega{position:static;min-width:0}
.ub-mega-bar{display:flex;align-items:center;gap:var(--ub-mega-gap,8px);margin:0;padding:0;list-style:none}
.ub-mega-item{position:static}
.ub-mega-t{
  display:block;padding:8px 16px;border-radius:999px;
  font-size:15px;font-weight:500;white-space:nowrap;transition:background-color .15s
}
/* Nền bo tròn cho mục đang mở. Dùng cả :hover và :focus-within để người dùng
   bàn phím Tab tới cũng thấy đúng trạng thái như người dùng chuột. */
.ub-mega--pill .ub-mega-item:hover > .ub-mega-t,
.ub-mega--pill .ub-mega-item:focus-within > .ub-mega-t,
.ub-mega--pill .ub-mega-item.is-current > .ub-mega-t{background:var(--card)}

/* Bảng xổ rộng hết dải. Đặt absolute theo .ub-bar-row (position:relative) nên
   nó tràn ngang bất kể mục cha nằm ở cột nào của lưới đầu trang. */
/* ⚠️ Bảng tự khai NỀN thì phải tự khai luôn MÀU CHỮ. Đầu trang đè lên ảnh bìa
   đặt `color:#fff` cho cả `.site-header`, mà bảng xổ là con của nó — không khai
   lại thì chữ trắng nằm trên nền trắng, cả bảng biến mất. Đã vấp thật. */
/* Nền xám LẠNH và hơi trong, không phải chuyển sắc xám-ấm → trắng như trước.
   Trên trang chủ bảng nằm đè lên ảnh bìa nên `backdrop-filter` cho ảnh mờ hiện
   qua — bảng thuộc về trang chứ không phải một tấm thẻ dán lên trên. Dòng
   `background` đục ở trên là bản dự phòng cho trình duyệt không có
   `backdrop-filter`: thiếu nó thì chữ đọc thẳng trên ảnh. */
.ub-mega-panel{
  position:absolute;top:100%;left:0;right:0;z-index:60;display:none;
  background:var(--mega-bg,rgb(237,238,240));
  background:var(--mega-bg-t,rgba(237,238,240,.97));
  backdrop-filter:blur(28px);
  color:var(--ink);
  border-bottom:1px solid var(--line);
  box-shadow:0 24px 48px -32px rgba(20,18,15,.35)
}
.ub-mega-item:hover > .ub-mega-panel,
.ub-mega-item:focus-within > .ub-mega-panel{display:block}

/* Nhịp cầu vô hình nối mục menu với bảng xổ.
   Mục menu chỉ cao bằng chữ (~36px) trong khi bảng bắt đầu ở ĐÁY đầu trang, nên
   giữa hai thứ có một dải trống. Chuột đi qua dải đó là rời khỏi .ub-mega-item →
   :hover tắt → bảng biến mất trước khi tới nơi. Cầu này lấp đúng dải trống ấy.
   ⚠️ Chỉ dựng khi ĐANG rê chuột: để thường trực thì nó là một dải trong suốt
   nằm đè lên nội dung ngay dưới đầu trang, nuốt mất cú bấm của khách.
   Cao theo --hdr-h (JS đo) chứ không phải số cứng — đầu trang kéo thả cao 1–3
   dải, số cứng đúng ở bản này là sai ở bản kia. Thừa cũng không sao: phần thừa
   rơi xuống dưới đáy đầu trang, chỗ đó bảng xổ (z-index 60) đã che sẵn.
   ⚠️ Cầu đặt trên NÚT (.ub-mega-t), không phải trên .ub-mega-item. Cho item
   `position:relative` là biến nó thành khối chứa của bảng xổ, bảng co lại đúng
   bề ngang một mục menu (đo được 80px) thay vì trải hết trang — đã vấp thật. */
.ub-mega-t{position:relative}
.ub-mega-item--mega:hover > .ub-mega-t::after,
.ub-mega-item--mega:focus-within > .ub-mega-t::after,
.ub-mega-item--drop:hover > .ub-mega-t::after,
.ub-mega-item--drop:focus-within > .ub-mega-t::after{
  content:'';position:absolute;left:0;right:0;top:100%;
  height:calc(var(--hdr-h,64px) / 2)
}

/* Bảng xổ neo theo DẢI MENU, không theo mép trang.
   --ub-mega-l / --ub-mega-r / --ub-mega-pr do theme.js đo (xem khối cuối tệp).
   Không đo được thì rơi về 20px, tức đúng hành vi cũ — bảng vẫn dùng được.
   Việc mở/đóng vẫn hoàn toàn bằng CSS :hover/:focus-within, JS chỉ lo canh lề. */
.ub-mega-in{
  width:auto;margin-inline:0;
  padding-left:var(--ub-mega-l,20px);
  padding-right:var(--ub-mega-pr,20px);
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:40px;
  padding-block:34px 46px;align-items:start
}
/* Cụm bên phải: mép PHẢI nội dung thẳng hàng với mục menu cuối, cột trải sang
   trái. Một cột auto chứ không phải minmax(0,1fr) — 1fr sẽ căng nội dung ra hết
   bề ngang rồi lại dán vào mép trái, đúng thứ đang cần sửa. */
.ub-mega--end .ub-mega-in{
  grid-template-columns:auto;justify-content:end;
  padding-left:20px;padding-right:var(--ub-mega-r,20px)
}
.ub-mega-cols{display:flex;gap:56px;flex-wrap:wrap}
.ub-mega-col{min-width:150px}
/* Tiêu đề cột ngả LẠNH, không dùng `--muted` (xám ấm): trên nền xám lạnh của
   bảng, xám ấm lộ ra một sắc vàng lệch tông. */
.ub-mega-h{
  display:block;margin:0 0 14px;font-size:13px;font-weight:600;
  letter-spacing:.01em;color:var(--mega-head,oklch(0.58 0.026 254))
}
.ub-mega-col ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:14px}
.ub-mega-col a{font-size:17px;line-height:1.25;display:inline-block}
/* Rê chuột ra GẠCH CHÂN, giữ nguyên màu chữ — không đổi sang màu nhấn.
   Màu nhấn do chủ site chọn (site này để xanh), mà đổi màu cả dòng thì mục đang
   rê trông như mục "đang chọn", còn dòng đang chọn thật thì lẫn vào. Gạch chân
   chỉ nói "con trỏ đang ở đây" nên không tranh nghĩa với trạng thái nào cả.
   `text-underline-offset` để nét gạch không dính vào chân chữ có dấu tiếng Việt. */
.ub-mega-col a:hover,
.ub-mega-col a:focus-visible{
  color:inherit;text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:5px
}

/* Ô sản phẩm bên phải */
/* Dải ô nằm sát mép PHẢI của bảng (cột `auto` trong lưới), nên muốn nó bắt đầu
   xa hơn về bên trái thì phải nới chính dải ra — tăng khoảng cách giữa hai cột
   của lưới không dịch được ô nào, nó chỉ bóp cột liên kết bên trái.
   3 × 200 + 2 × 56 = 712px, rộng hơn bản cũ (604px) 108px → mép trái dải lùi
   sang trái đúng 108px, đúng khoảng cách của bản tham chiếu. */
.ub-mega-feat{display:flex;gap:56px}
.ub-mega-p{display:flex;flex-direction:column;align-items:center;gap:6px;width:200px;text-align:center}
/* Khung ảnh CỐ ĐỊNH cho mọi ô, không để ảnh tự quyết.
   Bảng mega trộn hai loại ảnh: hàng hoá tách nền (vuông-ish, nhỏ) và ảnh chụp
   mẫu (16:9, rộng). Để ảnh tự co trong một khung chỉ khai chiều CAO thì ảnh
   chụp mẫu bè ra hết bề ngang còn ảnh hàng hoá teo lại giữa ô — ba ô cạnh nhau
   ra ba kích cỡ khác hẳn, đúng chỗ đang lệch so với bản tham chiếu.
   Khung 4/3 + contain cho mọi ảnh cùng chiếm một ô như nhau, và ảnh nào cũng
   hiện trọn vẹn (cover sẽ cắt mất hai đầu gọng — bài học #4). */
/* ⚠️ Ảnh đặt ABSOLUTE, không để trong dòng chảy — cùng cách đã dùng ở
   `.ub-ratio` và `.pplain-media`, và vì đúng một lý do.
   Để ảnh trong dòng chảy thì `max-height:100%` / `height:100%` của nó quy chiếu
   vào hàng lưới, mà hàng lưới lại tự co theo chính ảnh → phần trăm không có gì
   để quy chiếu nên trình duyệt BỎ QUA, chỉ còn `max-width` ăn. Ảnh vuông
   300×300 khi đó render 180×180 trong khung 180×135 rồi bị `overflow:hidden`
   xén mất 45px dưới đáy. Đã vấp thật với ảnh bộ kính Quest, và thử sửa bằng
   `height:100%` cũng KHÔNG ăn vì vòng phụ thuộc vẫn nguyên.
   `inset:0` cho hộp ảnh ôm đúng khung bất kể chuyện đó, `object-fit` lo phần
   thu vừa và căn giữa. */
.ub-mega-p-m{
  position:relative;display:block;width:100%;aspect-ratio:4/3;
  border-radius:12px;overflow:hidden
}
/* Ô KHÔNG có nền riêng: ảnh hàng hoá tách nền vốn đã có sẵn nền sáng trong tệp,
   đặt thêm một mảng xám sau lưng chỉ tạo ra một hình chữ nhật lệch tông ngay
   quanh món hàng — đúng chỗ nền ảnh và nền ô đá nhau. Để trong suốt thì ảnh
   nằm thẳng trên nền bảng, giống bản tham chiếu.
   Ảnh phủ kín thì tự nó lấp hết ô nên cũng không cần nền. */
.ub-mega-p-m img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}
/* Sản phẩm chưa có ảnh hàng hoá thì ô đành dùng ảnh chụp mẫu — và ảnh chụp mẫu
   phải PHỦ KÍN ô. Thu vừa một tấm 16:9 vào khung 4/3 để lại hai dải trắng trên
   dưới, ba ô cạnh nhau ra ba kiểu khác nhau. Bo góc đặt trên chính <img> vì
   overflow:hidden của ô cắt theo mép NGOÀI, ảnh vẫn thò góc vuông ra (bài #11). */
.ub-mega-p-m--fill img{width:100%;height:100%;max-width:none;max-height:none;object-fit:cover;border-radius:12px}
.ub-mega-p-c{font-size:13px;color:var(--muted)}
.ub-mega-p-t{font-size:14px;font-weight:600;line-height:1.3}
/* Ô sản phẩm theo cùng một lối với liên kết bên trái: gạch chân, không đổi màu.
   Một bảng mà hai kiểu phản hồi thì khách phải học hai lần.
   ⚠️ Phải chặn ngay ở chính thẻ `<a>` (`.ub-mega-p`), không chỉ ở dòng chữ bên
   trong: quy tắc chung `a:hover{color:var(--accent)}` tô màu cả ô, rồi
   `color:inherit` của dòng chữ lại kế thừa đúng màu vừa tô — đo được vẫn xanh. */
.ub-mega-p:hover{color:inherit}
.ub-mega-p:hover .ub-mega-p-t{
  color:inherit;text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:5px
}

/* Mục có con nhưng không bật mega: xổ dọc như menu thường. */
.ub-mega-item--drop{position:relative}
.ub-mega-drop{
  position:absolute;top:100%;left:0;z-index:60;display:none;
  min-width:210px;margin:0;padding:8px 0;list-style:none;
  background:var(--paper,#fff);border:1px solid var(--line);border-radius:12px;
  box-shadow:0 18px 44px -22px rgba(20,18,15,.4)
}
.ub-mega-item--drop:hover > .ub-mega-drop,
.ub-mega-item--drop:focus-within > .ub-mega-drop{display:block}
.ub-mega-drop a{display:block;padding:8px 16px;font-size:15px}

/* Bản điện thoại: nút ba gạch + bảng trượt phẳng hai tầng. */
.ub-mega .hdr-burger{display:none}
.ub-mpanel ul{margin:0;padding:0;list-style:none}
.ub-mpanel .ub-mpanel-top > a{display:block;padding:11px 2px;font-size:17px;font-weight:600}
.ub-mpanel .ub-mpanel-top ul{padding:0 0 10px 14px;border-left:1px solid var(--line);margin-left:2px}
.ub-mpanel .ub-mpanel-top ul a{display:block;padding:8px 2px;font-size:15px}
.ub-mpanel .ub-mpanel-leaf a{padding-left:14px;color:var(--muted);font-size:14px}

/* ---------- chọn ngôn ngữ ---------- */
.ub-lang{display:flex;align-items:center;gap:7px;flex:none}
.ub-lang-i{flex:none;opacity:.9}
.ub-lang-list{display:flex;align-items:center;gap:2px;margin:0;padding:0;list-style:none}
.ub-lang-a{
  display:block;padding:4px 8px;border-radius:7px;
  font-size:14px;font-weight:500;color:var(--muted);white-space:nowrap
}
.ub-lang-a:hover{color:var(--ink);background:var(--card)}
.ub-lang-a.is-on{color:var(--ink);font-weight:700}

.ub-footer{padding:0}

/* ---------- khối chưa có dữ liệu (chỉ hiện khi đang dựng) ---------- */
.ub-ph{padding:28px;border:1px dashed var(--line-strong);border-radius:var(--radius,16px);text-align:center;color:var(--muted);font-size:14px;background:var(--card)}

/* ---------- ẩn theo màn hình ---------- */
/* ⚠️ CÔNG TẮC NGẮT, nên phải `!important`. Đây là quy tắc (0,1,0) nằm ở GIỮA
   tệp, mà rất nhiều phần tử khai `display` của riêng chúng ở PHÍA SAU (nhãn nổi,
   chấm lan toả… thêm sau này đều nằm cuối tệp) — hoà độ ưu tiên rồi thua theo
   thứ tự nạp, tức ô "Ẩn trên …" bấm vào KHÔNG làm gì cả. Đo được: đặt "Ẩn trên
   máy tính bảng" cho nhãn nổi mà ở 834px nó vẫn hiện và đè lên tiêu đề.
   Không liệt kê từng phần tử để trừ ra: phần tử thêm sau nữa lại quên. */
.ub-hide-desk{display:none !important}

/* ---------- KHE HỞ theo khổ màn hình ----------
   Định nghĩa lại CHÍNH biến gốc thay vì thêm biến mới cho từng nấc: khe hở được
   đọc ở hơn hai mươi chỗ, kể cả trong `calc()` tính bề ngang ô của dải trượt.
   Đổi ở gốc thì mọi chỗ đọc tự đi theo, không phải sửa một dòng nào phía tiêu thụ
   — mà sửa tay hơn hai mươi chỗ thì kiểu gì cũng sót một, và chỗ sót ấy làm bề
   ngang ô lệch khỏi khe hở, dải trượt lộ ra một mẩu ô kế tiếp.

   ⚠️ Lớp chỉ có mặt KHI biến `--ub-gapv-*` đã được in ra (PHP lo, xem
   `hdnt_ub_gap_resp`). Thiếu chốt ấy thì `var(--ub-gapv-md)` trỏ vào biến chưa
   khai, cả khai báo thành "guaranteed-invalid" và biến gốc bị XOÁ về giá trị kế
   thừa — khe hở khổ máy tính biến mất thay vì giữ nguyên. */
@media (max-width:1024px){
  .ub-gap-md{
    --ub-cgap:var(--ub-gapv-md) !important;
    --ub-pgap:var(--ub-gapv-md) !important;
    --ub-gap:var(--ub-gapv-md) !important;
    --ub-sgap:var(--ub-gapv-md) !important;
    --ub-mgap:var(--ub-gapv-md) !important;
    --ub-chk-gap:var(--ub-gapv-md) !important;
    --ub-ib-gap:var(--ub-gapv-md) !important;
    --ub-cd-gap:var(--ub-gapv-md) !important;
    --ub-mo-gap:var(--ub-gapv-md) !important
  }
}
@media (max-width:620px){
  .ub-gap-sm{
    --ub-cgap:var(--ub-gapv-sm) !important;
    --ub-pgap:var(--ub-gapv-sm) !important;
    --ub-gap:var(--ub-gapv-sm) !important;
    --ub-sgap:var(--ub-gapv-sm) !important;
    --ub-mgap:var(--ub-gapv-sm) !important;
    --ub-chk-gap:var(--ub-gapv-sm) !important;
    --ub-ib-gap:var(--ub-gapv-sm) !important;
    --ub-cd-gap:var(--ub-gapv-sm) !important;
    --ub-mo-gap:var(--ub-gapv-sm) !important
  }
}

/* ---------- phông chữ riêng của khối ----------
   Đi kèm ba biến do PHP in ra ngay trên khối: `--ub-ff` (dùng ở đây),
   `--font-head` và `--font-body` (khai lại để CẢ CÂY CON đổi theo).

   ⚠️ Vì sao cần cả ba: tiêu đề trong khối KHÔNG kế thừa `font-family` của cha —
   `.ub-heading-t` khai thẳng `font-family:var(--font-head,inherit)`, tức nó đọc
   biến của giao diện chung. Chỉ đặt `font-family` trên khối thì chữ thường đổi
   mà tiêu đề đứng im, nhìn ra thành "chọn phông không ăn".

   ⚠️ Dùng LỚP chứ không phải `[style*="--ub-ff"]`: bộ chọn theo chuỗi style còn
   khớp cả `--ub-ff-md` nếu sau này thêm biến thể theo khổ, và khi ấy quy tắc
   máy tính bật lên với một biến chưa định nghĩa — cả khai báo hỏng lặng lẽ.
   Đúng bẫy `--btn-shadow` đã ghi. */
.ub-ff{font-family:var(--ub-ff)}

/* ---------- hiệu ứng xuất hiện ---------- */
.ub-anim{opacity:0;transition:opacity .6s ease,transform .6s cubic-bezier(.22,.61,.36,1)}
.ub-anim--up{transform:translateY(24px)}
.ub-anim--left{transform:translateX(-24px)}
.ub-anim--right{transform:translateX(24px)}
.ub-anim--zoom{transform:scale(.96)}
.ub-anim.is-in{opacity:1;transform:none}

/* Người đã bật "giảm chuyển động" trong hệ điều hành: hiện thẳng, không animate.
   Phải để opacity:1 chứ không chỉ tắt transition — nếu không, JS chưa kịp chạy
   là khối nằm vô hình vĩnh viễn. */
@media (prefers-reduced-motion:reduce){
  .ub-anim{opacity:1;transform:none;transition:none}
}

/* ---------- hiệu ứng rê chuột (mọi phần tử, mặc định TẮT) ----------
 * Chỉ khai khi người dựng CHỌN: khối không có lớp `ub-hv` thì không có một dòng
 * transition nào — trang không phải vẽ lại thứ gì lúc chuột đi ngang qua.
 *
 * ⚠️ Đổ bóng đi qua BIẾN `--ub-hv-sh`, không viết thẳng vào từng nhánh: khối đã
 * tự khai `box-shadow` ở style nội tuyến (ô "Đổ bóng khối") thì nhánh rê chuột
 * phải THAY được nó, mà style nội tuyến thắng mọi quy tắc trong tệp — nên bóng
 * lúc rê chuột phải nằm ở một quy tắc :hover riêng, không phải style nội tuyến.
 */
.ub-hv{transition:transform .22s ease,box-shadow .22s ease,opacity .22s ease,border-color .22s ease,background-color .22s ease}
.ub-hv--lift:hover{transform:translateY(-6px);box-shadow:var(--ub-hv-sh,0 14px 30px rgba(22,19,16,.13))}
.ub-hv--shadow:hover{box-shadow:var(--ub-hv-sh,0 14px 30px rgba(22,19,16,.13))}
/* Phóng cả khối chứ không riêng ảnh: đây là hiệu ứng CHUNG cho mọi phần tử,
   nhiều khối không có ảnh nào. 1.02 là đủ thấy — 1.06 như thẻ ảnh thì với một
   dải rộng cả trang là nó tràn ra ngoài mép. */
.ub-hv--zoom:hover{transform:scale(1.02)}
/* Viền: khối chưa có viền thì `border-color` không hiện gì, nên phải tự vẽ vành
   bằng box-shadow — cách này cũng KHÔNG cộng thêm 2px vào kích thước khối như
   `border` (thẻ mua ở site anh em đã vấp: nút cao lên 2px). */
.ub-hv--border:hover{box-shadow:0 0 0 2px var(--accent,#C49B40)}
/* Viền NỔI: viền mảnh + bóng toả + nhấc 3px — dáng thẻ "đang chọn" hay gặp ở
   dải sản phẩm/khoá học. Vành vẫn vẽ bằng box-shadow (không cộng kích thước),
   và nhấc ít hơn `lift` để thẻ nổi lên mà hàng không nhảy nhịp. */
.ub-hv--ring:hover{
  box-shadow:0 0 0 1.5px var(--accent,#C49B40),var(--ub-hv-sh,0 18px 40px -18px rgba(8,28,69,.28));
  transform:translateY(-3px)
}
/* Đổi màu nền: dáng "dòng đang trỏ tới" của bảng xếp hạng / danh sách. Mặc định
   là màu nhấn pha RẤT nhạt để chữ sẫm bên trên vẫn đọc được — đổi nền mà không
   đổi màu chữ là bài học "bảng nào tự khai nền thì phải tự khai luôn màu chữ",
   nên ở đây chọn nền nhạt thay vì nền đậm. */
.ub-hv--bg:hover{background-color:var(--ub-hv-bg,color-mix(in srgb,var(--accent,#C49B40) 9%,#fff))}
.ub-hv--fade:hover{opacity:.86}

/* ⚠️ Cùng luật với hiệu ứng xuất hiện: người đã bật "giảm chuyển động" thì bỏ
   phần DI CHUYỂN, nhưng vẫn giữ bóng và màu — chúng không gây chóng mặt, mà bỏ
   hết thì khối bấm được trông y hệt khối không bấm được. */
@media (prefers-reduced-motion:reduce){
  .ub-hv{transition:box-shadow .22s ease,border-color .22s ease,background-color .22s ease}
  .ub-hv--lift:hover,.ub-hv--zoom:hover,.ub-hv--ring:hover{transform:none}
}

/* ---------------------------------------------------------------------------
   Breakpoint — mỗi mốc đúng một khối
--------------------------------------------------------------------------- */

@media (max-width:1240px){
  .ub-heading--huge .ub-heading-t{font-size:46px}
  .ub-banner-t{font-size:44px}
}

@media (max-width:1024px){
  /* ⚠️ Dự phòng 4 chứ KHÔNG rơi về `--ub-cols` của khổ máy tính: dải 8 logo mà
     giữ 8 cột xuống máy tính bảng là mỗi logo còn một mẩu. Chủ site đặt riêng
     thì `--ub-cols-md` thắng. */
  .ub-logos:not(.ub-logos--run){grid-template-columns:repeat(var(--ub-cols-md,4),minmax(0,1fr))}
  /* ⚠️ ĐỌC TỪ BIẾN, đừng viết cứng một con số. Trước đây khai `repeat(3,…)` ở
     đây: người dựng chọn 4 cột mà khung hẹp hơn 1100px là ra 3 — và khung xem
     trước của trình dựng hẹp hơn ngưỡng ấy, nên chủ site báo "số cột chưa
     nhận". Cùng lỗi đã gặp với lưới sản phẩm.
     ⚠️ KHÔNG dùng `repeat(min(var(--x),3),…)`: min() không hợp lệ cho số lần
     lặp của repeat(), cả quy tắc bị bỏ và lưới vỡ về một cột. Phải là biến
     riêng, do PHP tính sẵn. */
  /* ⚠️ `:not()` là BẮT BUỘC: dải trượt và bố cục so le khai `grid-template-columns:none`
     ở quy tắc gốc, mà quy tắc này đứng SAU nên nó dựng lại các cột cố định. Hệ
     quả đo được: lưới sinh 6 track (3 cột rỗng 0px + 3 ô thật), mỗi bước cuộn
     chỉ nhích 20px vì ô đo ra bề ngang BẰNG KHÔNG. Nút chuyển vẫn bấm được nên
     nhìn thì tưởng chạy — phải ĐO mới thấy. */
  .ub-cats:not(.ub-cats--slider):not(.ub-cats--masonry):not(.ub-cats--pill){grid-template-columns:repeat(var(--ub-cols-md,3),minmax(0,1fr))}
  .ub-posts:not(.ub-posts--slider){grid-template-columns:repeat(var(--ub-cols-md,2),minmax(0,1fr))}
  .ub-gallery:not(.ub-gallery--slider):not(.ub-gallery--masonry){grid-template-columns:repeat(var(--ub-cols-md,3),minmax(0,1fr))}
  .ub-gallery--masonry{column-count:var(--ub-cols-md,3)}
  .ub-hide-tablet{display:none !important}
}

@media (max-width:1024px){
  /* ⚠️ Khối này TRƯỚC 2026-08-12 nằm ở ngưỡng 860px — một mốc thứ ba nằm giữa
     hai nấc thật, và nó đã cắn: 860 nằm TRÊN bề ngang khổ máy tính bảng (834px)
     và đứng SAU khối 1024px trong tệp, nên nó nuốt luôn nấc máy tính bảng — đặt
     "Bề ngang · máy tính bảng" xong vẫn ra bố cục điện thoại, ở khung xem trước
     LẪN trên iPad thật (iPad dọc đúng 834px). Vá lần đó chỉ chữa phần đọc biến;
     đợt này gom hẳn mốc về 1024 nên cả dải 861–1024px cũng hành xử như máy tính
     bảng thay vì nửa nọ nửa kia.
     ⚠️ Ba quy tắc TỪNG nằm ở đây đã GỠ trong cùng đợt, vì gom mốc xong chúng
     thành bản sao xấu hơn của quy tắc có sẵn: `.ub-col` và `.ub-cell` đã có bản
     ở khối 1024px phía trên tệp với chuỗi dự phòng ĐÚNG
     (`var(--ub-span-md,var(--ub-span,12))`). Bản ở đây rơi về `--ub-span-sm`, tức
     nấc máy tính bảng đi ăn theo nấc ĐIỆN THOẠI — chỉ hợp lý hồi mốc 860 còn
     nhập nhèm hai khổ. Nấc trên KHÔNG BAO GIỜ được kế thừa từ nấc dưới. */
  /* Ô ghép: nấc chiều cao theo khổ, để trống thì rơi về khổ lớn hơn. */
  .ub-cell{grid-row:span var(--ub-tall-md,var(--ub-tall,1))}
  /* Chia đều theo khổ: nấc để trống thì rơi về nấc khổ lớn hơn, đúng nếp mọi ô
     "theo khổ" khác. Cột con vẫn phải giữ `auto` — quy tắc ngay trên đặt lại
     `grid-column` cho MỌI cột nên phải nhắc lại ở đây, nếu không span lưới 12
     quay về và hàng chia đều vỡ ở đúng khổ máy tính bảng. */
  .ub-row--even{grid-template-columns:repeat(var(--ub-even-md,var(--ub-even,3)),minmax(0,1fr))}
  .ub-row--even > .ub-col{grid-column:auto}
  /* Chỉ đặt chia đều cho khổ BẢNG, khổ máy tính vẫn dùng lưới 12 của từng cột.
     ⚠️ Phải là LỚP RIÊNG chứ không dùng chung `.ub-row--even`: lớp chung có mặt
     ở mọi khổ, nên chỉ đặt cho khổ nhỏ mà dùng nó thì khổ MÁY TÍNH cũng bị ép về
     `repeat(3, …)` và mọi ô "Bề ngang" của từng cột mất tác dụng. */
  .ub-row--evmd{grid-template-columns:repeat(var(--ub-even-md,3),minmax(0,1fr))}
  .ub-row--evmd > .ub-col{grid-column:auto}
  /* Đầu trang trên màn hình hẹp: menu ngang thu vào nút ba gạch. Menu nào tắt
     tuỳ chọn đó thì thành dải cuộn ngang thay vì biến mất. */
  .ub-menu--burger .hdr-burger{
    display:flex;align-items:center;justify-content:center;width:34px;height:34px;
    padding:0;border:0;background:transparent;color:inherit;cursor:pointer
  }
  .ub-menu--burger .ub-menu-main{display:none}
  .ub-menu--row:not(.ub-menu--burger) .ub-menu-list{
    flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none
  }
  .ub-menu--row:not(.ub-menu--burger) .ub-menu-list::-webkit-scrollbar{display:none}
  .ub-bar-in{gap:12px}
  .ub-search--box input{width:min(52vw,200px)}
  /* Bảng tìm kiếm neo right:0 theo chính icon, mà icon nằm gần mép phải nên
     bảng thò hẳn ra ngoài mép TRÁI (đo được -39px ở 414px). Cho nó neo theo
     cả dải thay vì theo icon. */
  .ub-search{position:static}
  .ub-search .hdr-search-panel{left:12px;right:12px;width:auto}
  .ub-search .hdr-search-panel form{width:100%}
  .ub-search .hdr-search-panel input{width:100%;min-width:0}
  /* Mega menu: bảng nhiều cột không có chỗ trên màn hình hẹp — thu về nút ba
     gạch, nội dung chuyển sang bảng trượt phẳng. */
  .ub-mega .ub-mega-nav{display:none}
  .ub-mega .hdr-burger{
    display:flex;align-items:center;justify-content:center;width:34px;height:34px;
    padding:0;border:0;background:transparent;color:inherit;cursor:pointer
  }
  .ub-mega-in{grid-template-columns:minmax(0,1fr);gap:28px;padding:24px 0 30px}
  .ub-mega-cols{gap:32px}
  .ub-mega-feat{gap:18px;overflow-x:auto}
  /* ⚠️ `align-items:stretch` là BẮT BUỘC. Xếp dọc rồi thì trục ngang thành trục
     PHỤ, mà `align-items:center` ở đó co từng cột về bề ngang nội dung — cột ảnh
     lấy bề ngang từ khung tỉ lệ nên co về ĐÚNG 0px và ảnh biến mất (đo được ở
     834 và 620px: cột chữ 569, cột ảnh 0). Căn dọc cũng chẳng còn nghĩa gì khi
     mỗi cột một hàng. Quy tắc này đứng SAU `.ub-row--vcenter` trong tệp nên hoà
     rồi thắng theo thứ tự nạp — không cần !important. */
  .ub-row--rev{display:flex;flex-direction:column-reverse;align-items:stretch}
  .ub-heading-t{font-size:28px}
  .ub-heading--large .ub-heading-t{font-size:32px}
  .ub-heading--huge .ub-heading-t{font-size:36px}
  .ub-banner-t{font-size:34px}
  .ub-banner-x{font-size:16px}
  /* Ba lưới "dữ liệu thật" (.ub-cats · .ub-posts · .ub-gallery) KHÔNG có mặt ở
     đây: số cột của chúng do người dựng đặt, đọc từ biến ở hai khối 1100 và 620.
     Thêm một nấc viết cứng nữa là quay lại đúng lỗi "số cột không nhận". */
  /* ⚠️ Đọc BIẾN, không ghim số: Lưới thẻ có ô "Số cột" theo khổ như mọi lưới
     khác — ghim 2 là chọn 3 vẫn ra 2 rồi tưởng cài đặt không ăn.
     ⚠️ Trừ dải trượt ra (`:not`) — để `repeat()` đè lên `grid-auto-flow:column`
     là dải vỡ về lưới (bẫy đã gặp ở .ub-cats/.ub-posts/.ub-quotes). */
  /* Máy tính bảng LƯỚI: phải hoàn tác mọi thuộc tính của dải trượt khổ trên,
     không thì chọn Lưới ở đây mà nó vẫn cuộn. */
  .ub-cards:not(.ub-cards--slider-t){grid-auto-flow:row;grid-template-columns:repeat(var(--ub-cols-md,2),minmax(0,1fr));grid-auto-columns:auto;overflow-x:visible;scroll-snap-type:none;padding-bottom:0}
  /* Máy tính bảng: các thẻ xếp trên MỘT hàng cuộn được, mỗi lượt xem đúng số ô
     của "Số cột" khổ ấy. Bề ngang ô tính THEO KHUNG (`calc`), không `1fr` — để
     `1fr` thì có 2 thẻ là mỗi ô phình ra nửa dải. */
  .ub-cards.ub-cards--slider-t{grid-auto-flow:column;grid-auto-columns:calc((100% - (var(--ub-cols-md,2) - 1) * 20px) / var(--ub-cols-md,2));grid-template-columns:none;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:thin;padding-bottom:6px}
  .ub-pricing{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ub-checks{grid-template-columns:1fr}
  /* Lời khen tách khỏi hàng viết cứng ở trên: số cột do người dựng đặt (tới 4
     cột trên máy tính), đọc từ biến. Dự phòng 2 = con số cũ.
     ⚠️ Trừ dải trượt ra (`:not`) — nó có luật riêng ngay dưới, để `repeat()`
     đè lên `grid-auto-flow:column` là dải vỡ về lưới (đúng bẫy đã gặp ở dải
     trượt của .ub-cats/.ub-posts). */
  .ub-quotes:not(.ub-quotes--slider-sm){grid-template-columns:repeat(var(--ub-cols-md,2),minmax(0,1fr))}
  /* Máy tính bảng: xếp các thẻ trên MỘT hàng cuộn được, mỗi lượt xem đúng số ô
     của "Cột · máy tính bảng". Cùng cơ chế với dải trượt của Danh mục/Bài viết:
     overflow + scroll-snap, JS chỉ thêm nút bấm cho chuột. */
  .ub-quotes--slider-sm{grid-auto-flow:column;grid-auto-columns:calc((100% - (var(--ub-cols-md,2) - 1) * var(--ub-cgap,20px)) / var(--ub-cols-md,2));grid-template-columns:none;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:thin;padding-bottom:6px}
  /* ⚠️ Đọc từ BIẾN, không viết cứng: khoá chết 2 cột ở đây thì người dựng chọn 5
     cột vẫn thấy 3 trong khung xem trước (khung hẹp hơn ngưỡng này) rồi tưởng
     cài đặt không ăn — đúng lỗi đã gỡ ở .ub-cats/.ub-posts/.ub-gallery.
     Giá trị dự phòng 2 = đúng con số viết cứng cũ, nên trang lưu từ trước chạy
     y hệt. */
  .ub-stats{grid-template-columns:repeat(var(--ub-cols-md,2),minmax(0,1fr))}
  .ub-steps{grid-template-columns:repeat(var(--ub-cols-md,3),minmax(0,1fr))}
  /* Xem ghi chú ở chỗ khai `.ub-steps--link-*`: khổ hẹp các bước xuống nhiều
     hàng, mũi tên ở cuối hàng sẽ chỉ vào chỗ trống. */
  .ub-steps--link-arrow .ub-step::after,
  .ub-steps--link-line .ub-step::after{display:none}
  /* Nấc thu nhỏ sẵn có giữ nguyên làm DỰ PHÒNG; chủ site đặt riêng thì thắng. */
  .ub-stats .ub-stat-n{font-size:var(--ub-sn-md,34px)}
  .ub-stats .ub-stat-l{font-size:var(--ub-sl-md,var(--ub-sl,14px))}
  .ub-slider-t{grid-auto-columns:calc((100% - 16px) / min(var(--ub-per,1),2))}
}

@media (max-width:620px){
  /* Chia đều N cột ở khổ ĐIỆN THOẠI.
     ⚠️ Trước 2026-08-12 khối 620px KHÔNG có quy tắc nào đọc `--ub-even-sm`, nên ô
     "Chia đều số cột · điện thoại" là một ô bấm vào KHÔNG LÀM GÌ — đúng lớp lỗi
     "một ô không thấy gì đổi còn tệ hơn không có". Chỉ lộ ra khi dựng một trang
     demo rồi đo `gridTemplateColumns` bằng trình duyệt thật: đặt 3/2/1 mà ở 414px
     vẫn ra 2 cột. */
  .ub-row--even{grid-template-columns:repeat(var(--ub-even-sm,var(--ub-even-md,var(--ub-even,3))),minmax(0,1fr))}
  .ub-row--evsm{grid-template-columns:repeat(var(--ub-even-sm,var(--ub-even-md,3)),minmax(0,1fr))}
  .ub-row--evsm > .ub-col{grid-column:auto}

  /* Nấc ĐIỆN THOẠI thật của Cột / Ô ghép. Khối 860px ở trên nay lo nấc MÁY TÍNH
     BẢNG (iPad dọc 834px rơi vào đó), nên nấc điện thoại phải khai riêng ở đây —
     và phải đứng SAU khối ấy trong tệp thì mới đè được (cùng độ ưu tiên). */
  .ub-col{grid-column:span var(--ub-span-sm,12)}
  .ub-cell{grid-row:span var(--ub-tall-sm,var(--ub-tall-md,var(--ub-tall,1)))}
  .ub-cell{grid-column:span var(--ub-span-sm,12)}
  /* ⚠️ Chiều cao hàng của Ghép ảnh CHUYỂN XUỐNG ĐÂY trong đợt gom mốc
     (2026-08-12). Ô của nó tên là "Chiều cao hàng · ĐIỆN THOẠI" mà quy tắc lại
     nằm ở ngưỡng 860 cũ, tức nó hạ luôn chiều cao của máy tính bảng — đặt cho
     điện thoại mà tablet cũng lùn theo. Nay ô nói gì thì làm đúng thế; máy tính
     bảng giữ chiều cao của khổ máy tính (Ghép ảnh chưa có ô riêng cho tablet). */
  .ub-mosaic{grid-auto-rows:var(--ub-mo-rh-sm,150px)}
  /* Lề trong giữ nguyên 24px như `px-6` của trang chủ — khối dựng kéo thả và
     dải dựng sẵn phải thẳng mép nhau ở MỌI khổ, không riêng khổ rộng. */
  .ub-cards:not(.ub-cards--slider-m){grid-auto-flow:row;grid-template-columns:repeat(var(--ub-cols-sm,1),minmax(0,1fr));grid-auto-columns:auto;overflow-x:visible;scroll-snap-type:none;padding-bottom:0}
  /* Điện thoại: cả hai kiểu trượt đều chạy ở đây. `--slider-m` mới bật từ nấc
     này (ở khổ bảng nó vẫn là lưới) nên phải khai ĐỦ, không chỉ đổi bề ngang ô.
     ⚠️ Phải viết HAI LỚP. `:not()` mang theo độ ưu tiên của đối số, nên quy tắc
     lưới ở khổ bảng `.ub-cards:not(.ub-cards--slider-sm)` là (0,2,0) — nó VẪN
     khớp khối `--slider-m` (chỉ trừ `--slider-sm` ra) và thắng một lớp đơn
     (0,1,0) dù nằm trước trong tệp. Đo được: lưới sinh 5 track
     `0px 0px 0px 342px 342px`, ô rộng ĐÚNG 30px — dải trượt vỡ mà vẫn cuộn
     được nên nhìn lướt tưởng chạy. */
  .ub-cards.ub-cards--slider-m{grid-auto-flow:column;grid-auto-columns:calc((100% - (var(--ub-cols-sm,1) - 1) * 20px) / var(--ub-cols-sm,1));grid-template-columns:none;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:thin;padding-bottom:6px}
  .ub-pricing{grid-template-columns:1fr}
  /* Dự phòng 2 = "mặc định 2x2" chủ site chốt: dải số liệu xếp mỗi hàng một ô
     trên điện thoại thì cao lê thê, phải cuộn mới hết mấy con số vốn để liếc
     một cái là thấy. Hạ về 1 cột vẫn được ở ô "Cột · điện thoại". */
  .ub-stats{grid-template-columns:repeat(var(--ub-cols-sm,2),minmax(0,1fr))}
  /* ⚠️ Hai ô cạnh nhau trên điện thoại thì ô chỉ còn ~146px ở màn 360px, mà cỡ
     34px của khổ bảng làm "12.000+" tràn ra khỏi ô (đo được 3px). Hạ thêm một
     nấc và thu lề ngang — đúng nếp 44px → 34px đã có ở khổ bảng. */
  .ub-stat{padding:16px 6px}
  .ub-stats .ub-stat-n{font-size:var(--ub-sn-sm,var(--ub-sn-md,28px))}
  .ub-stats .ub-stat-l{font-size:var(--ub-sl-sm,var(--ub-sl-md,var(--ub-sl,14px)))}
  .ub-stat-s{font-size:17px}
  .ub-quotes:not(.ub-quotes--slider-sm){grid-template-columns:repeat(var(--ub-cols-sm,1),minmax(0,1fr))}
  /* Điện thoại: dải trượt hiện đúng số ô của "Cột · điện thoại" (mặc định 1). */
  .ub-quotes--slider-sm{grid-auto-columns:calc((100% - (var(--ub-cols-sm,1) - 1) * var(--ub-cgap,20px)) / var(--ub-cols-sm,1))}
  .ub-steps{grid-template-columns:repeat(var(--ub-cols-sm,2),minmax(0,1fr))}
  /* Ba khối lấy dữ liệu thật vẫn theo biến của người dựng — trên điện thoại
     hai ô danh mục cạnh nhau là bình thường, ép về một cột là quyết định thay
     họ. Mặc định vẫn là 1 nếu không đặt gì. */
  .ub-cats:not(.ub-cats--slider):not(.ub-cats--masonry):not(.ub-cats--pill){grid-template-columns:repeat(var(--ub-cols-sm,2),minmax(0,1fr))}
  .ub-posts:not(.ub-posts--slider){grid-template-columns:repeat(var(--ub-cols-sm,1),minmax(0,1fr))}
  .ub-gallery:not(.ub-gallery--slider):not(.ub-gallery--masonry){grid-template-columns:repeat(var(--ub-cols-sm,2),minmax(0,1fr))}
  .ub-logos:not(.ub-logos--run){grid-template-columns:repeat(var(--ub-cols-sm,var(--ub-cols-md,3)),minmax(0,1fr))}
  .ub-nl-f{flex-direction:column}
  .ub-nl-f .ub-btn{width:100%}
  .ub-nl{padding:32px 22px}
  .ub-slider-t{grid-auto-columns:100%}
  .ub-slider-n{display:none}
  .ub-hide-mobile{display:none !important}
  .ub-hide-desk{display:revert !important}
}

@media (max-width:380px){
  .ub-heading--huge .ub-heading-t,.ub-banner-t{font-size:29px}
  .ub-gallery{grid-template-columns:1fr}
  .ub-btn{padding:12px 20px}
}

/* Dải trình chiếu: khe và số ô nhìn thấy do người dựng đặt. */
.ub-slider-t{gap:var(--ub-sgap,16px)}
@media (max-width:620px){
  .ub-slider-t > *{flex:0 0 calc((100% - (var(--ub-per-sm,1) - 1) * var(--ub-sgap,16px)) / var(--ub-per-sm,1))}
}
/* Nút để BÊN NGOÀI dải: chừa chỗ hai bên, không đè lên nội dung ô đầu/ô cuối. */
.ub-slider--navout{padding-inline:44px}
.ub-slider--navout .ub-slider-n--prev{left:0}
.ub-slider--navout .ub-slider-n--next{right:0}
/* Chấm chỉ mục. Vòng ngoài của chấm đang chọn vẽ bằng box-shadow chứ không phải
   border — border ăn vào kích thước nên chấm đang chọn lại NHỎ hơn các chấm kia. */
.ub-slider-d{display:flex;justify-content:center;gap:8px;margin-top:14px}
.ub-slider-d button{
  width:8px;height:8px;padding:0;border:0;border-radius:50%;
  background:var(--line-strong,#c9ccd0);cursor:pointer
}
.ub-slider-d button.is-on{background:var(--accent,#0073eb);box-shadow:0 0 0 3px rgba(0,115,235,.22)}

/* Kiểu "Gạch dài": mỗi chỉ mục là một vạch ngắn, ô đang xem dài và đậm hơn.
   Cùng độ ưu tiên với quy tắc gốc (0,1,1) nên phải đứng SAU để thắng. */
.ub-slider-d--bar button{width:22px;height:4px;border-radius:99px}
.ub-slider-d--bar button.is-on{width:34px;box-shadow:none}

/* Kiểu "Số thứ tự": nút mang số 1..N, ô đang xem tô màu nhấn. */
.ub-slider-d--num{gap:6px}
.ub-slider-d--num button{
  width:auto;height:auto;min-width:22px;padding:2px 7px;border-radius:6px;
  font:600 12px/1.35 inherit;color:var(--muted,#6b7280);background:var(--soft,#f1f3f5)
}
.ub-slider-d--num button.is-on{color:#fff;background:var(--accent,#0073eb);box-shadow:none}

/* Vị trí "Trong ảnh": chỉ mục đè lên đáy dải, căn giữa ngang. Chấm sáng + bóng
   để đọc được trên MỌI tấm ảnh — chấm xám nhạt chìm hẳn trên nền sáng màu. */
.ub-slider-d--in{position:absolute;left:0;right:0;bottom:12px;margin-top:0;z-index:3}
.ub-slider-d--in button{background:rgba(255,255,255,.6);box-shadow:0 1px 3px rgba(0,0,0,.35)}
.ub-slider-d--in button.is-on{background:#fff;box-shadow:0 0 0 3px rgba(255,255,255,.45)}
/* Số thứ tự đè trên ảnh: viên nền tối mờ cho chữ trắng nổi rõ (đủ (0,2,1) để
   thắng cả .ub-slider-d--num button lẫn .ub-slider-d--in button ở trên). */
.ub-slider-d--in.ub-slider-d--num button{color:#fff;background:rgba(0,0,0,.45);box-shadow:0 1px 3px rgba(0,0,0,.3)}
.ub-slider-d--in.ub-slider-d--num button.is-on{background:var(--accent,#0073eb)}
/* ẨN chỉ mục theo khổ (ô "Ẩn chỉ mục trên").
   ⚠️ Lớp nằm trên chính DẢI, không trên thanh chỉ mục — thanh ấy do JS dựng sau
   khi trang tải xong, nên quy tắc nhắm thẳng vào nó chỉ ăn muộn một nhịp và
   khách kịp thấy chỉ mục loé lên rồi biến mất.
   ⚠️ Cần (0,2,0) để thắng `.ub-slider-d{display:flex}` ở trên — đủ, vì đây là
   hai lớp; nếu chỉ một lớp thì hoà rồi phải trông vào thứ tự tệp.
   Ngưỡng khớp đúng ba nấc của trình dựng: bảng ≤1024 · điện thoại ≤620 · máy
   tính là phần còn lại. */
@media (min-width:1025px){
  .ub-slider--dh-desk .ub-slider-d{display:none}
}
@media (max-width:1024px){
  .ub-slider--dh-tablet .ub-slider-d{display:none}
}
@media (max-width:620px){
  .ub-slider--dh-mobile .ub-slider-d{display:none}
}

/* ---------------------------------------------------------------------------
   Dãy liên kết (khối "Dãy liên kết")
--------------------------------------------------------------------------- */
.ub-links{margin:0 0 20px}
.ub-links-list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ub-mgap,20px);margin:0;padding:0;list-style:none}
.ub-links--col .ub-links-list{flex-direction:column;align-items:stretch}
.ub-links.ub-tcenter .ub-links-list{justify-content:center}
.ub-links.ub-tright .ub-links-list{justify-content:flex-end}
.ub-links--col.ub-tcenter .ub-links-list{align-items:center}
.ub-links--col.ub-tright .ub-links-list{align-items:flex-end}
.ub-links-i{margin:0;padding:0;min-width:0}
/* Cỡ chữ · màu chữ · màu khi rê chuột đi qua BIẾN, khai được ở HAI tầng: cả dãy
   (`.ub-links`) hoặc riêng một mục (`.ub-links-i`). Biến CSS kế thừa nên mục nào
   tự khai thì thắng cho đúng nhánh của nó — một bộ tên, không phải hai bộ quy tắc. */
.ub-links-a{display:inline-flex;align-items:center;gap:8px;color:var(--ub-lnk-c,inherit);text-decoration:none;font-weight:var(--ub-lnk-w,600);font-size:var(--ub-lnk-fs,15px);line-height:1.4;transition:color .15s}
.ub-links-a:hover{color:var(--ub-lnk-hc,var(--accent))}
/* ⚠️ "Chữ hoa" hạ cỡ chữ xuống 13px, nên nó phải NHƯỜNG con số người dựng đặt —
   không thì chọn cỡ chữ xong bật Chữ hoa là con số vừa chọn biến mất. */
.ub-links--upper .ub-links-a{text-transform:uppercase;letter-spacing:.04em;font-size:var(--ub-lnk-fs,13px)}
.ub-links-a .kico{width:1.15em;height:1.15em}
.ub-links-badge{font-style:normal;font-size:10px;font-weight:700;line-height:1;padding:3px 6px;border-radius:999px;background:var(--accent);color:#fff;text-transform:uppercase;letter-spacing:.03em}
/* Dấu phân cách vẽ bằng ::before của mục THỨ HAI trở đi, không phải viền của
   từng mục: viền thì mục cuối cũng có một vạch thừa ở mép. */
.ub-links--sep-line .ub-links-i + .ub-links-i,
.ub-links--sep-dot .ub-links-i + .ub-links-i{display:flex;align-items:center;gap:var(--ub-mgap,20px)}
.ub-links--sep-line .ub-links-i + .ub-links-i::before{content:"";width:1px;align-self:stretch;min-height:1em;background:var(--line)}
.ub-links--sep-dot .ub-links-i + .ub-links-i::before{content:"";width:4px;height:4px;border-radius:999px;background:var(--line-strong,var(--line))}
.ub-links--col.ub-links--sep-line .ub-links-i + .ub-links-i{flex-direction:column;align-items:stretch}
.ub-links--col.ub-links--sep-line .ub-links-i + .ub-links-i::before{width:auto;height:1px;min-height:0}

/* ---------------------------------------------------------------------------
   Lưới sản phẩm kiểu DANH SÁCH — ảnh nhỏ bên trái, chữ bên phải.
   Vẫn đúng thẻ sản phẩm của trang Cửa hàng, chỉ đổi cách xếp bên trong.
--------------------------------------------------------------------------- */
.ub-products--list{grid-template-columns:repeat(var(--ub-cols,1),minmax(0,1fr))}
.ub-products--list .hdnt-pc{display:grid;grid-template-columns:minmax(0,120px) minmax(0,1fr);align-items:center;gap:16px}
.ub-products--list .hdnt-pc-media{aspect-ratio:1/1}
/* ⚠️ Ô chữ của thẻ không có lớp riêng — nó là con thứ hai của thẻ. Nhắm bằng
   :last-child chứ đừng thêm lớp vào product-card.php: tệp đó dùng chung với
   trang Cửa hàng, đổi ở đó là đổi luôn cả trang kia. */
.ub-products--list .hdnt-pc > div:last-child{padding:0 16px 0 0}
@media (max-width:620px){
  .ub-products--list .hdnt-pc{grid-template-columns:minmax(0,92px) minmax(0,1fr);gap:12px}
}

/* ---------------------------------------------------------------------------
   Hình thức thanh toán
--------------------------------------------------------------------------- */
.ub-pay{display:flex;flex-wrap:wrap;align-items:flex-start;gap:14px 26px;margin:0 0 20px;padding:0;list-style:none}
.ub-pay.ub-tcenter{justify-content:center}
.ub-pay.ub-tright{justify-content:flex-end}
.ub-pay-i{display:flex;flex-direction:column;align-items:center;gap:6px;margin:0;text-align:center;color:var(--muted)}
.ub-pay-i .kico{width:var(--ub-pay-size,26px);height:var(--ub-pay-size,26px);color:var(--ink)}
.ub-pay-i span{font-size:12px;line-height:1.3;max-width:9em}

/* Căn chữ + cỡ chữ của thẻ danh mục. Năm nấc XS…XL như thói quen sẵn có. */
.ub-cats.ub-ct-left .ub-cat span{text-align:left}
.ub-cats.ub-ct-center .ub-cat span{text-align:center}
.ub-cats.ub-ct-right .ub-cat span{text-align:right}
.ub-cats.ub-cs-xs .ub-cat span{font-size:12px}
.ub-cats.ub-cs-s .ub-cat span{font-size:13px}
.ub-cats.ub-cs-m .ub-cat span{font-size:15px}
.ub-cats.ub-cs-l .ub-cat span{font-size:18px}
.ub-cats.ub-cs-xl .ub-cat span{font-size:22px}

/* ---------------------------------------------------------------------------
   Biển lớn — bổ sung 2026-08-07: video nền · hiệu ứng · hình cắt mép · bấm cả biển
--------------------------------------------------------------------------- */
/* ⚠️ Video nằm DƯỚI lớp phủ và dưới chữ, nên phải z-index thấp hơn .ub-banner-in
   (đang z-index:1). Không khai thì nó là con cuối trong dòng chảy và đè lên chữ. */
.ub-banner-vid{position:absolute;inset:0;z-index:0;width:100%;height:100%;object-fit:cover;border-radius:inherit;pointer-events:none}

/* Hiệu ứng nền. "Đứng yên khi cuộn" cố ý KHÔNG bật ở khổ hẹp: iOS bỏ qua
   background-attachment:fixed và render ảnh sai tỉ lệ, mà đó lại là nửa số
   khách. */
.ub-banner--fx-zoom{overflow:hidden}
.ub-banner--fx-zoom{animation:ub-bzoom 18s ease-in-out infinite alternate}
@keyframes ub-bzoom{from{background-size:100%}to{background-size:112%}}
.ub-banner--fx-hzoom{transition:background-size .6s ease}
.ub-banner--fx-hzoom:hover{background-size:108%}
@media (min-width:1025px){
  .ub-banner--fx-fixed{background-attachment:fixed}
}
@media (prefers-reduced-motion:reduce){
  .ub-banner--fx-zoom{animation:none}
}

/* Hình cắt mép: nằm đè lên hai mép, cao 60px và co lại ở khổ hẹp. */
/* Hình cắt mép — dùng chung cho Banner và Khối.
   ⚠️ KHÔNG khai z-index. Hình cắt in ra ngay TRƯỚC khung nội dung trong DOM, nên
   với z-index tự động nó đã nằm trên nền + lớp phủ mà vẫn dưới chữ — đúng ở cả
   hai khối. Khai z-index:2 như bản đầu thì trong Khối nó ĐÈ LÊN chữ, vì
   `.ub-section-in` không đặt z-index (khác `.ub-banner-in`). */
.ub-shape{position:absolute;left:0;right:0;line-height:0;pointer-events:none}
.ub-shape svg{display:block;width:100%;height:60px}
.ub-shape--top{top:0;transform:rotate(180deg)}
.ub-shape--bottom{bottom:0}

/* Cả biển là một liên kết: bỏ gạch chân và giữ nguyên màu chữ bên trong. */
.ub-banner--link{display:block;text-decoration:none;color:inherit}

/* Khổ điện thoại: TẮT hẳn video nền. Ảnh `poster` vẫn hiện nên biển không
   trống, mà khách dùng 3G không phải tải một tệp vài MB. */
@media (max-width:620px){
  .ub-banner-vid{display:none}
  .ub-shape svg{height:34px}
}

/* Khung chữ trong biển lớn + chữ màu sẫm cho nền sáng.
   ⚠️ `.ub-banner` khai `color:#fff` nên nhánh sáng phải trả màu về sẫm cho CẢ
   chữ lẫn nút kiểu "Viền" (nút ấy mượn currentColor làm màu vành). */
.ub-banner--box-light .ub-banner-in,
.ub-banner--box-dark .ub-banner-in,
.ub-banner--box-round .ub-banner-in{max-width:min(560px,100%);padding:34px 36px;flex:none}
.ub-banner--box-light .ub-banner-in{background:rgba(255,255,255,.94);color:var(--ink);border-radius:var(--radius,16px)}
.ub-banner--box-dark .ub-banner-in{background:rgba(22,19,16,.86);color:#fff;border-radius:var(--radius,16px)}
.ub-banner--box-round .ub-banner-in{background:rgba(22,19,16,.86);color:#fff;border-radius:999px;aspect-ratio:1/1;display:flex;flex-direction:column;justify-content:center;text-align:center;padding:12%;max-width:min(420px,100%)}
/* ⚠️ Hai nhánh nền sáng có độ ưu tiên (0,2,0), cao hơn `.ub-banner-x` (0,1,0),
   nên chúng cũng phải đọc biến màu của người dựng — không thì chọn màu chữ xong
   bật "Chữ màu sẫm" là màu vừa chọn biến mất mà không rõ vì sao. */
.ub-banner--box-light .ub-banner-x{color:var(--ub-bn-xc,var(--muted))}
.ub-banner--box-light .ub-eyebrow{color:var(--ub-bn-ec,var(--muted))}
.ub-banner--light{color:var(--ink)}
.ub-banner--light .ub-banner-x{color:var(--ub-bn-xc,var(--muted))}
.ub-banner--light .ub-eyebrow{color:var(--ub-bn-ec,var(--muted))}
/* Hộp căn theo cạnh mà người dựng chọn — `margin-inline:auto` của khung nội
   dung sẽ luôn kéo nó về giữa nếu không khai lại. */
.ub-banner--box-light.ub-banner--left .ub-banner-in,
.ub-banner--box-dark.ub-banner--left .ub-banner-in,
.ub-banner--box-round.ub-banner--left .ub-banner-in{margin-inline:var(--ub-gutter,24px) auto}
.ub-banner--box-light.ub-banner--right .ub-banner-in,
.ub-banner--box-dark.ub-banner--right .ub-banner-in,
.ub-banner--box-round.ub-banner--right .ub-banner-in{margin-inline:auto var(--ub-gutter,24px)}

/* Tiêu đề + biểu tượng của khối Câu hỏi thường gặp */
.ub-faq-h{margin:0 0 18px;font-size:24px;line-height:1.25}
.ub-faq summary .kico{width:1.05em;height:1.05em;margin-right:8px;vertical-align:-2px}

/* ---------------------------------------------------------------------------
   Lưới bài viết — bổ sung 2026-08-07
--------------------------------------------------------------------------- */
.ub-posts{gap:var(--ub-cgap,24px)}
.ub-post{box-shadow:var(--ub-cshadow,none);border-radius:var(--ub-crad,var(--radius,16px));transition:box-shadow .2s,transform .2s}
.ub-post:hover{box-shadow:var(--ub-cshadow-h,var(--ub-cshadow,none))}
.ub-post-m{display:block;overflow:hidden;border-radius:var(--ub-crad,var(--radius,16px))}
.ub-post-m img{display:block;width:100%;height:100%;object-fit:cover;transition:transform .35s,opacity .25s}
.ub-post-meta{display:flex;flex-wrap:wrap;align-items:center;gap:10px;font-size:12px;color:var(--muted)}
/* Nhãn chuyên mục là VIÊN THUỐC có nền, đúng dáng thẻ bài của trang chủ
   (`template-parts/tkbx/news.php`). Chữ gold trần trên nền trắng nhìn lẫn với
   dòng ngày ngay cạnh — mà đây là thứ phân loại bài, phải tách ra thấy ngay.
   Nền lấy từ `--soft` (bảng màu Giao diện chung) nên đổi tông một chỗ là cả
   site theo, không gõ cứng mã màu. */
.ub-post-c{display:inline-flex;align-items:center;height:24px;padding:0 10px;border-radius:999px;background:var(--soft,#F6F1E7);font-weight:700;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--accent)}
.ub-post-x{display:block;margin-top:6px;font-size:14px;line-height:1.6;color:var(--muted)}
.ub-post-more{display:inline-block;margin-top:10px;font-weight:600;font-size:14px;color:var(--accent)}

/* Tỉ lệ ảnh dùng lại đúng bộ lớp ub-pr-* của lưới sản phẩm — một quy ước, hai
   khối, không phải nhớ hai bộ tên. */
.ub-posts.ub-pr-1-1 .ub-post-m{aspect-ratio:1/1}
.ub-posts.ub-pr-4-3 .ub-post-m{aspect-ratio:4/3}
.ub-posts.ub-pr-3-4 .ub-post-m{aspect-ratio:3/4}
.ub-posts.ub-pr-16-9 .ub-post-m{aspect-ratio:16/9}

.ub-posts.ub-ct-left .ub-post-b{text-align:left}
.ub-posts.ub-ct-center .ub-post-b{text-align:center}
.ub-posts.ub-ct-center .ub-post-meta{justify-content:center}
.ub-posts.ub-ct-right .ub-post-b{text-align:right}
.ub-posts.ub-ct-right .ub-post-meta{justify-content:flex-end}
.ub-posts.ub-cs-xs .ub-post-t{font-size:14px}
.ub-posts.ub-cs-s .ub-post-t{font-size:15px}
.ub-posts.ub-cs-m .ub-post-t{font-size:17px}
.ub-posts.ub-cs-l .ub-post-t{font-size:20px}
.ub-posts.ub-cs-xl .ub-post-t{font-size:24px}

/* ---------------------------------------------------------------------------
   Thẻ bài viết có NỀN — dáng mặc định, đúng nhịp các dải dựng sẵn ở trang chủ.
   Không có nó thì khối chữ nằm trần: chữ dính sát mép trái và không có gì đỡ ở
   đáy, mà `gap` của thẻ lại tạo một khe hở giữa ảnh và chữ.
--------------------------------------------------------------------------- */
.ub-posts--boxed .ub-post{
  gap:0;overflow:hidden;background:var(--paper,#fff);
  border-radius:var(--ub-crad,var(--radius,16px));
  box-shadow:var(--ub-cshadow,0 10px 30px -18px rgba(22,19,16,.35))
}
.ub-posts--boxed .ub-post:hover{box-shadow:var(--ub-cshadow-h,0 18px 40px -20px rgba(22,19,16,.45))}
/* Ảnh bo góc theo THẺ, không tự bo: `overflow:hidden` của thẻ đã cắt hai góc
   trên; để ảnh giữ bo góc riêng thì mép dưới ảnh cũng cong và hở ra hai khe
   trắng ngay giữa thẻ (đúng bài học đã gặp ở dải danh mục kiểu "Thẻ bo khối"). */
.ub-posts--boxed .ub-post-m{border-radius:0}
/* `flex:1` để khối chữ ăn hết chỗ dư — thiếu nó thì nền trắng dừng ngay dưới
   dòng cuối và thẻ nào tiêu đề ngắn cũng hở một mảng ở đáy. */
.ub-posts--boxed .ub-post-b{flex:1;padding:var(--ub-cpad,18px 22px 22px)}
/* Dáng "danh sách": ảnh trái · chữ phải, nên lề trong chỉ đặt ở phần chữ. */
.ub-posts--boxed.ub-posts--list .ub-post{gap:0}

.ub-posts--hv-zoom .ub-post-m{overflow:hidden}
.ub-posts--hv-zoom .ub-post:hover img{transform:scale(1.06)}
.ub-posts--hv-fade .ub-post:hover img{opacity:.82}
.ub-posts--hv-lift .ub-post:hover{transform:translateY(-4px)}

/* Danh sách: ảnh trái, chữ phải. Trượt ngang: cùng khuôn với dải danh mục. */
.ub-posts--list{grid-template-columns:repeat(var(--ub-cols,1),minmax(0,1fr))}
.ub-posts--list .ub-post{display:grid;grid-template-columns:minmax(0,200px) minmax(0,1fr);align-items:center;gap:18px}
.ub-posts--slider{display:grid;grid-auto-flow:column;grid-auto-columns:calc((100% - (var(--ub-cols,3) - 1) * var(--ub-cgap,24px)) / var(--ub-cols,3));grid-template-columns:none;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:thin;padding-bottom:6px}
.ub-posts--slider .ub-post{scroll-snap-align:start}
@media (max-width:620px){
  .ub-posts--list .ub-post{grid-template-columns:minmax(0,110px) minmax(0,1fr);gap:12px}
}

/* ---------------------------------------------------------------------------
   Thư viện ảnh — bổ sung 2026-08-07: chú thích · bố cục · xem phóng to
--------------------------------------------------------------------------- */
.ub-gallery-i{position:relative;box-shadow:var(--ub-cshadow,none);transition:box-shadow .2s,transform .2s}
.ub-gallery-i img{transition:transform .35s,opacity .25s}
.ub-gallery-a{position:relative;display:block;border-radius:var(--ub-radius,12px);overflow:hidden}
.ub-gallery-a:hover .ub-gallery-i{box-shadow:var(--ub-cshadow-h,var(--ub-cshadow,none))}
/* Lớp phủ vẽ bằng ::after trên khung ảnh: phủ phải nằm TRÊN ảnh mà chữ vẫn phải
   đọc được ở trên cùng. */
.ub-gallery[style*="--ub-covl"] .ub-gallery-i::after{content:"";position:absolute;inset:0;background:var(--ub-covl);opacity:var(--ub-covl-o,.35);border-radius:inherit;pointer-events:none}

/* Chú thích */
.ub-gallery-c{display:block;font-size:14px;line-height:1.45}
.ub-gallery--cap-below .ub-gallery-c{margin-top:8px;color:var(--muted)}
.ub-gallery--cap-overlay .ub-gallery-a,.ub-gallery--cap-shade .ub-gallery-a,.ub-gallery--cap-label .ub-gallery-a{position:relative}
.ub-gallery--cap-overlay .ub-gallery-c,
.ub-gallery--cap-shade .ub-gallery-c,
.ub-gallery--cap-label .ub-gallery-c{position:absolute;left:0;right:0;z-index:1;color:#fff;padding:12px 14px}
.ub-gallery--cap-overlay .ub-gallery-c{top:50%;transform:translateY(-50%);text-align:center;text-shadow:0 1px 10px rgba(0,0,0,.55);font-weight:600}
.ub-gallery--cap-shade .ub-gallery-c{bottom:0;background:linear-gradient(to top,rgba(22,19,16,.78),transparent)}
.ub-gallery--cap-label .ub-gallery-c{bottom:0;background:rgba(22,19,16,.82)}

/* Bố cục */
.ub-gallery--masonry{display:block;column-count:var(--ub-cols,3);column-gap:var(--ub-gap,12px)}
.ub-gallery--masonry > *{break-inside:avoid;margin-bottom:var(--ub-gap,12px)}
.ub-gallery--slider{display:grid;grid-auto-flow:column;grid-auto-columns:calc((100% - (var(--ub-cols,3) - 1) * var(--ub-gap,12px)) / var(--ub-cols,3));grid-template-columns:none;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:thin;padding-bottom:6px}
.ub-gallery--slider > *{scroll-snap-align:start}

/* Rê chuột */
.ub-gallery--hv-zoom .ub-gallery-i{overflow:hidden}
.ub-gallery--hv-zoom .ub-gallery-a:hover img{transform:scale(1.06)}
.ub-gallery--hv-fade .ub-gallery-a:hover img{opacity:.82}
.ub-gallery--hv-lift .ub-gallery-a:hover{transform:translateY(-4px)}

.ub-gallery.ub-ct-left .ub-gallery-c{text-align:left}
.ub-gallery.ub-ct-center .ub-gallery-c{text-align:center}
.ub-gallery.ub-ct-right .ub-gallery-c{text-align:right}
.ub-gallery.ub-cs-xs .ub-gallery-c{font-size:12px}
.ub-gallery.ub-cs-s .ub-gallery-c{font-size:13px}
.ub-gallery.ub-cs-m .ub-gallery-c{font-size:14px}
.ub-gallery.ub-cs-l .ub-gallery-c{font-size:16px}
.ub-gallery.ub-cs-xl .ub-gallery-c{font-size:19px}

/* ---------- khung xem ảnh phóng to ---------- */
/* z-index cao hơn đầu trang sticky (z-50 của Tailwind) — thấp hơn thì đầu trang
   nằm đè lên khung xem ảnh, mà đó lại là chỗ có nút Đóng. */
.ub-lb{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;gap:8px;padding:24px;background:rgba(12,10,8,.92)}
.ub-lb[hidden]{display:none}
body.ub-lb-on{overflow:hidden}
.ub-lb-f{margin:0;max-width:100%;max-height:100%;display:flex;flex-direction:column;align-items:center;gap:12px;min-width:0}
.ub-lb-f img{display:block;max-width:100%;max-height:82vh;width:auto;height:auto;border-radius:8px}
.ub-lb-f figcaption{color:rgba(255,255,255,.86);font-size:14px;text-align:center;max-width:60ch}
.ub-lb-f figcaption[hidden]{display:none}
.ub-lb button{flex:none;display:grid;place-items:center;width:44px;height:44px;border:0;border-radius:999px;background:rgba(255,255,255,.14);color:#fff;font-size:26px;line-height:1;cursor:pointer;transition:background-color .15s}
.ub-lb button:hover{background:rgba(255,255,255,.26)}
.ub-lb button[hidden]{display:none}
.ub-lb-x{position:absolute;top:16px;right:16px;font-size:30px}
@media (max-width:620px){
  .ub-lb{padding:12px;gap:4px}
  .ub-lb button{width:38px;height:38px;font-size:22px}
  .ub-gallery--masonry{column-count:var(--ub-cols-sm,2)}
}

/* Hình thức thanh toán · kiểu "icon trái · chữ phải".
   Mỗi mục thành một hàng ngang, và cả dải xuống dòng theo cột — hợp khi chữ dài
   ("Thanh toán khi nhận hàng" xếp dọc dưới icon thì cao gấp đôi). */
.ub-pay--row .ub-pay-i{flex-direction:row;align-items:center;gap:9px;text-align:left}
.ub-pay--row .ub-pay-i span{max-width:none}
/* Dòng chú thích cuối dải: không có biểu tượng, chữ nhạt hơn — nó không phải
   một cách thanh toán thứ năm. */
.ub-pay-i--note{align-self:center}
.ub-pay-i--note span{color:var(--muted-2,var(--muted));font-size:13px;max-width:none}

/* ---------------------------------------------------------------------------
   Dấu đầu dòng của danh sách (Danh sách tích · Gói giá)

   Gạch ngang · chấm tròn · số thứ tự vẽ bằng `li::before` chứ không phải SVG:
   chúng co theo cỡ chữ nên không bao giờ lệch dòng, và số thứ tự do `counter`
   tự đánh — chèn/xoá một dòng không phải đánh số lại.
--------------------------------------------------------------------------- */
/* ⚠️ `.pg-check` của theme này KHÔNG khai `display:flex` cho <li> — nó chỉ tô
   màu icon. Icon và chữ xếp inline nên tạm ổn, nhưng dấu vẽ bằng ::before thì
   cần một trục để bám. Khai flex ở đây cho cả hai danh sách: icon thẳng hàng
   với dòng đầu, chữ nhiều dòng thụt đều theo lề trái. */
.ub-checks > li,
.ub-plan-f > li{display:flex;align-items:flex-start;gap:9px}
/* Dấu đầu dòng co THEO cỡ chữ. ⚠️ `.pg-check .kico` khai `font-size:20px` (icon
   rộng `1em` của CHÍNH nó), nên phải đè đúng `font-size` chứ không phải width —
   khai `1.25em` là nhân lên trên 20px và ra 25px, tức đổi cả dáng mặc định.
   Dự phòng 20px = đúng con số cũ, không đặt cỡ chữ thì không lệch một pixel. */
.ub-checks > li > .kico{font-size:var(--ub-chk-fs,20px)}
.ub-mk-dash > li::before,
.ub-mk-dot > li::before{content:"";flex:none;background:currentColor;opacity:.55}
.ub-mk-dash > li::before{width:.7em;height:2px;margin-top:.62em;border-radius:2px}
.ub-mk-dot > li::before{width:.4em;height:.4em;margin-top:.52em;border-radius:999px}
.ub-mk-num{counter-reset:ub-mk}
.ub-mk-num > li{counter-increment:ub-mk}
.ub-mk-num > li::before{content:counter(ub-mk) ".";flex:none;min-width:1.3em;font-weight:700;color:var(--accent)}

/* ---------------------------------------------------------------------------
   Dải danh mục · kiểu "Thẻ bo khối": cả ô là một tấm nền trắng bo góc, ảnh nằm
   trên, chữ nằm trong lề của thẻ.
--------------------------------------------------------------------------- */
/* Dáng NGHỈ sao y thẻ sản phẩm: `shadow-soft` + vành 1px `ring-bordr` của theme
   (`0 10px 30px rgba(0,0,0,.06)` + `#EAECEF`). Bóng cũ đậm gấp mấy lần nên thẻ
   danh mục lúc nào cũng nổi lù lù dù chủ site chưa bật ô "Đổ bóng" nào — mà hai
   dải này nằm cạnh nhau trên cùng một trang.
   ⚠️ Vành vẽ bằng chính chuỗi box-shadow (như Tailwind `ring-`), không phải
   `border`: thêm border là cộng 2px vào kích thước thẻ và lệch lưới. */
.ub-cats--boxed .ub-cat{
  gap:0;overflow:hidden;background:var(--paper,#fff);
  border-radius:var(--ub-crad,18px);
  box-shadow:var(--ub-cshadow,0 0 0 1px var(--line,#EAECEF),0 10px 30px rgba(0,0,0,.06))
}
/* Lúc rê chuột lấy đúng bóng của thẻ sản phẩm, vẫn giữ vành 1px cho khỏi hụt
   mép. Giá trị đến từ biến nên ô "Bóng khi rê chuột" của người dựng đè lại được. */
.ub-cats--boxed .ub-cat:hover{box-shadow:0 0 0 1px var(--line,#EAECEF),var(--ub-cshadow-h,0 18px 40px -20px rgba(22,19,16,.45))}
/* Ảnh bo góc theo THẺ, không tự bo: `overflow:hidden` của thẻ đã cắt hai góc
   trên; để ảnh giữ bo góc riêng thì mép dưới ảnh cũng cong, hở ra hai khe trắng
   ngay giữa thẻ. */
.ub-cats--boxed .ub-cat img{border-radius:0}
/* Chữ căn TRÁI trong lề thẻ — thẻ có nền riêng nên căn giữa làm khối chữ trôi
   lửng giữa mảng trắng, không bám vào đâu. */
.ub-cats--boxed .ub-cat span{padding:var(--ub-cpad,16px 18px 18px);text-align:left}
.ub-cats--boxed.ub-ct-center .ub-cat span{text-align:center}
.ub-cats--boxed.ub-ct-right .ub-cat span{text-align:right}
.ub-cats--boxed .ub-cat-n{margin-top:6px}
/* Mũi tên vẽ bằng ::after của dòng số lượng — không thêm thẻ nào vào HTML, và
   kiểu khác không dùng thì cũng không phải gỡ đi. */
.ub-cats--boxed .ub-cat-n::after{content:"→";margin-left:8px;font-weight:600;color:var(--accent);transition:transform .18s;display:inline-block}
.ub-cats--boxed .ub-cat:hover .ub-cat-n::after{transform:translateX(4px)}

/* ---------------------------------------------------------------------------
   Dải danh mục · bổ sung 2026-08-07: kiểu phủ mờ · so le · tràn màn hình ·
   hiệu ứng rê chuột · nút chuyển của dải trượt
--------------------------------------------------------------------------- */
/* "Phủ mờ dần": nền chuyển sắc từ đáy lên, khác "Nhãn đáy ảnh" ở chỗ không có
   mép cắt ngang — ảnh và chữ hoà vào nhau. */
.ub-cats--shade .ub-cat{overflow:hidden}
.ub-cats--shade .ub-cat span{
  position:absolute;left:0;right:0;bottom:0;z-index:1;padding:16px 14px 14px;
  color:#fff;background:linear-gradient(to top,rgba(22,19,16,.86),rgba(22,19,16,.55) 45%,transparent)
}
.ub-cats--shade .ub-cat-n{color:rgba(255,255,255,.8)}

/* So le: giữ nguyên tỉ lệ từng ảnh, không cắt. */
.ub-cats--masonry{display:block;column-count:var(--ub-cols,4);column-gap:var(--ub-cgap,16px)}
.ub-cats--masonry > *{break-inside:avoid;margin-bottom:var(--ub-cgap,16px)}
@media (max-width:1024px){ .ub-cats--masonry{column-count:var(--ub-cols-md,3)} }
@media (max-width:620px){ .ub-cats--masonry{column-count:var(--ub-cols-sm,2)} }

/* Tràn màn hình: kéo dải ra sát hai mép bằng lề âm — không phải đoán đúng lề
   của khung rồi trừ tay, thứ sẽ sai ngay lần ai đó đổi lề. */
.ub-full{margin-inline:calc(50% - 50vw);padding-inline:var(--ub-gutter,24px)}

/* ---------- nút chuyển dùng chung cho mọi dải trượt ---------- */
.ub-sld{position:relative}
/* Nút neo GIỮA theo chiều dọc của cả vỏ, đè lên mép dải. `hidden` do PHP đặt
   sẵn, JS chỉ bật lên khi dải thật sự dài hơn khung — trang không JS thì không
   hiện hai cái nút bấm chẳng làm gì. */
.ub-sld-b{
  position:absolute;top:calc(50% - 22px);z-index:2;display:grid;place-items:center;
  width:44px;height:44px;border:0;border-radius:999px;cursor:pointer;
  background:var(--paper,#fff);color:var(--ink);font-size:24px;line-height:1;
  box-shadow:0 6px 18px -6px rgba(22,19,16,.45);transition:opacity .15s,background-color .15s
}
.ub-sld-b:hover{background:var(--soft,#f2efe9)}
.ub-sld-b[disabled]{opacity:.35;cursor:default}
.ub-sld-b[hidden]{display:none}
.ub-sld-b--prev{left:-14px}
.ub-sld-b--next{right:-14px}
.ub-sld-dots{display:flex;justify-content:center;gap:8px;margin-top:14px}
.ub-sld-dots[hidden]{display:none}
.ub-sld-dot{
  width:8px;height:8px;padding:0;border:0;border-radius:999px;cursor:pointer;
  background:var(--line-strong,#c9c3b6);transition:background-color .15s,transform .15s
}
.ub-sld-dot.is-on{background:var(--accent);transform:scale(1.35)}
/* Nút ‹ › của dải trượt: giấu khi thiết bị KHÔNG có con trỏ chính xác, chứ không
   giấu theo bề ngang.

   ⚠️ Đây là ngoại lệ có chủ ý trong đợt gom mốc breakpoint (2026-08-12), và lý do
   là câu ghi trong chú thích cũ: *"khách vuốt tay là chính"* — tức điều kiện thật
   sự là CẢM ỨNG, không phải màn hình hẹp. Cứ để nó ở mốc bề ngang thì gom về
   1024px sẽ giấu mất hai cái nút trên laptop nhỏ, nơi người dùng chỉ có chuột và
   không vuốt được. Còn ở mốc 860px cũ thì nó là mốc thứ ba, phá đúng cái luật
   "hai mốc" mà đợt này lập ra. Truy vấn cảm ứng trả lời đúng câu hỏi. */
@media (hover:none) and (pointer:coarse){
  .ub-sld-b{display:none}
}

/* ---------------------------------------------------------------------------
   Ô icon · Ô ảnh — hai khối "icon/ảnh + chữ" dùng CHUNG bộ tên `-b` · `-s` ·
   `-t` · `-x` cho khối chữ, nên phần chữ chỉ khai một lần ở dưới cùng.
--------------------------------------------------------------------------- */
.ub-ibox,.ub-imgbox{display:flex;gap:var(--ub-ib-gap,14px);color:inherit;text-decoration:none}
/* ⚠️ `color:inherit` ngay trên ĐÈ `.ub-dark{color:#fff}`: hai luật CÙNG độ đặc
   hiệu (0,1,0) mà `.ub-dark` khai ở đầu tệp, nên luật đứng sau thắng. Hậu quả:
   bật ô "Chữ sáng màu" cho Icon Box trên dải SÁNG thì `--ink` đổi thành trắng
   nhưng `color` bị kéo về màu mực của dải — tiêu đề đen trên nền #161310, nhìn
   ra là một cái hộp trống. Đúng triệu chứng chủ site báo ở hộp "Giờ làm việc"
   trang Liên hệ.
   ⚠️ KHÔNG phải chuyện của `[ub_section dark]`: ở đó `inherit` lấy đúng màu
   trắng của dải, nên bọ chỉ lộ khi `ub-dark` nằm trên CHÍNH khối — cũng là lý
   do phép kiểm dạng "có chuỗi" xanh hết mà trang vẫn hỏng.
   Dùng `var(--ink)` chứ không viết cứng `#fff`: cùng một mã màu với mọi thứ
   khác trong `.ub-dark`, đổi bảng màu là đổi một chỗ. */
.ub-ibox.ub-dark{color:var(--ink)}
.ub-ibox--top,.ub-imgbox--top{flex-direction:column}
/* Icon/ảnh bên PHẢI đổi THỨ TỰ TRONG HTML, không phải `flex-direction` đảo
   chiều ở CSS — cùng luật đã áp cho icon trong nút: đảo bằng CSS thì thứ tự
   người ta NGHE khác thứ tự người ta THẤY, và bôi đen bằng chuột cũng nhảy
   lung tung. Nhờ vậy chỉ có MỘT quy tắc xếp hàng ngang cho cả hai phía. */
.ub-ibox--left,.ub-ibox--right,
.ub-imgbox--left,.ub-imgbox--right{flex-direction:row;align-items:flex-start}
.ub-ibox-b,.ub-imgbox-b{display:flex;flex-direction:column;min-width:0;flex:1}
/* Icon căn GIỮA khối chữ (tiêu đề + mô tả) thay vì ngang hàng tiêu đề.
   ⚠️ Phải đủ (0,2,0) mới thắng `.ub-ibox--left` (0,1,0) ở dòng trên — cùng độ
   ưu tiên là hoà rồi thua theo thứ tự nạp, mà quy tắc kia đứng trước.
   Chỉ áp cho icon trái/phải: icon nằm TRÊN thì trục dọc là trục chính, đặt
   `align-items` vào đó là kéo giãn theo chiều ngang chứ không căn dọc. */
.ub-ibox--ia-center.ub-ibox--left,
.ub-ibox--ia-center.ub-ibox--right{align-items:center}
/* Tinh chỉnh vị trí icon sau khi đã chọn mốc căn. Dùng `transform` chứ không
   phải `margin-top`: margin đẩy lại cả khối chữ và làm hàng cao thêm đúng bấy
   nhiêu, còn transform chỉ dời phần nhìn thấy nên bố cục đứng yên. */
.ub-ibox--left > .ub-ibox-i,
.ub-ibox--right > .ub-ibox-i{transform:translateY(var(--ub-ib-off,0px))}

/* --- Khung icon --- */
.ub-ibox-i{
  display:grid;place-items:center;flex:none;
  color:var(--ub-ib-c,var(--accent));
  font-size:var(--ub-ib-i,22px)
}
/* Có khung thì mới chiếm chỗ vuông; không khung thì icon co đúng bằng chính nó,
   không để lại một ô trống 56px bên trái chữ. */
.ub-ibox--circle .ub-ibox-i,.ub-ibox--square .ub-ibox-i{
  width:var(--ub-ib-box,56px);height:var(--ub-ib-box,56px);
  border:var(--ub-ib-bd,1px) solid currentColor;
  background:var(--ub-ib-bg,transparent)
}
.ub-ibox--circle .ub-ibox-i{border-radius:999px}
.ub-ibox--square .ub-ibox-i{border-radius:12px}
/* `.kico` cao 1em nên cỡ icon đi theo font-size của khung — đặt width/height
   thẳng lên nó thì mọi icon phải khai lại hai con số. */
.ub-ibox-i .kico{width:1em;height:1em}
/* Ảnh thay icon: dùng chính khung icon. `contain` chứ không `cover` — logo mà bị
   cắt mất một góc thì hỏng hẳn thương hiệu (bài học "đoán sai về phía phủ kín
   thì mất một phần sản phẩm"). Khung không khai `shape` thì ảnh vuông theo cỡ khung. */
.ub-ibox-i--img{overflow:hidden;padding:0}
/* ⚠️ Ảnh 404 thì trình duyệt in chữ ALT ra chỗ tấm ảnh — cỡ chữ của khối cha nên
   nó tràn hẳn ra ngoài khung 48px và đè lên tiêu đề bên cạnh (đã gặp với logo
   Eurogold mất tệp). Thu chữ alt lại và cắt theo khung. */
.ub-ibox-i--img img{width:100%;height:100%;object-fit:contain;display:block;font-size:9px;line-height:1.1;color:var(--muted);overflow:hidden}
.ub-ibox--circle .ub-ibox-i--img img{border-radius:999px}
/* Icon nằm TRÊN mà chữ căn giữa thì khung icon cũng phải vào giữa, không thì
   một vòng tròn lệch trái bên trên khối chữ căn giữa. */
.ub-ibox--top.ub-ct-center .ub-ibox-i{margin-inline:auto}
.ub-ibox--top.ub-ct-right .ub-ibox-i{margin-left:auto}

/* --- Ảnh của Ô ảnh --- */
.ub-imgbox-m{position:relative;display:block;flex:none;overflow:hidden;border-radius:var(--ub-ib-rad,0);box-shadow:var(--ub-cshadow,none);transition:box-shadow .25s,transform .25s}
.ub-imgbox--top .ub-imgbox-m{width:var(--ub-ib-iw,100%)}
.ub-imgbox--left .ub-imgbox-m,.ub-imgbox--right .ub-imgbox-m{width:var(--ub-ib-iw,40%)}
.ub-imgbox-m img{display:block;width:100%;height:auto;transition:transform .35s,opacity .25s}
/* Khung có tỉ lệ thì ảnh phải phủ kín — `.ub-ratio img` đã lo, nhưng ảnh trong
   dòng chảy (không tỉ lệ) vẫn cần `height:auto` ở trên. */
.ub-imgbox-m.ub-ratio img{height:100%}
/* Lớp phủ: ::after trên chính khung ảnh, và độ đậm đi qua biến riêng vì ô màu
   không diễn tả được độ trong suốt. */
.ub-imgbox[style*="--ub-covl"] .ub-imgbox-m::after{content:"";position:absolute;inset:0;background:var(--ub-covl);opacity:var(--ub-covl-o,.35);pointer-events:none}
.ub-imgbox--hv-zoom:hover .ub-imgbox-m img{transform:scale(1.06)}
.ub-imgbox--hv-fade:hover .ub-imgbox-m img{opacity:.82}
.ub-imgbox--hv-lift{transition:transform .25s}
.ub-imgbox--hv-lift:hover{transform:translateY(-4px)}
.ub-imgbox:hover .ub-imgbox-m{box-shadow:var(--ub-cshadow-h,var(--ub-cshadow,none))}
/* Nền của khối chữ chỉ dựng khi chủ site chọn màu — mặc định chữ nằm thẳng
   trên nền dải, không có mảng trắng thừa. */
.ub-imgbox[style*="--ub-ib-tbg"] .ub-imgbox-b{background:var(--ub-ib-tbg);border-radius:var(--ub-ib-rad,0)}
.ub-imgbox-b{padding:var(--ub-cpad,0)}
/* Tông chữ sáng dùng cho ảnh nền tối. Khai cả `--muted` chứ không chỉ `color`:
   dòng mô tả lấy màu từ biến ấy, quên là mô tả vẫn xám sẫm trên nền tối. */
.ub-imgbox--light{color:#fff;--muted:rgba(255,255,255,.78)}

/* --- Khối chữ dùng chung --- */
.ub-ibox-s,.ub-imgbox-s{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--accent)}
.ub-ibox-t,.ub-imgbox-t{margin:0;font-size:17px;font-weight:700;line-height:1.35}
.ub-ibox-t a,.ub-imgbox-t a{color:inherit;text-decoration:none}
.ub-ibox-t a:hover,.ub-imgbox-t a:hover{text-decoration:underline;text-underline-offset:3px}
.ub-ibox-x,.ub-imgbox-x{color:var(--muted);font-size:15px;line-height:1.65}
.ub-ibox-x p,.ub-imgbox-x p{margin:6px 0 0}
.ub-ibox-x p:first-child,.ub-imgbox-x p:first-child{margin-top:4px}
.ub-ibox.ub-ct-left,.ub-imgbox.ub-ct-left{text-align:left}
.ub-ibox.ub-ct-center,.ub-imgbox.ub-ct-center{text-align:center}
.ub-ibox.ub-ct-right,.ub-imgbox.ub-ct-right{text-align:right}
/* XXS = 13px: bậc nhỏ nhất trước đây là 14px, mà dải cam kết của chính giao diện
   dùng 13px. Trong ô hẹp, 1px ấy đúng bằng MỘT DÒNG chữ (đo ở khối chữ 72px:
   "Thiết kế miễn phí 3D" 3 dòng ở 14px, 2 dòng ở 13px). */
.ub-ibox.ub-cs-xxs .ub-ibox-t,.ub-imgbox.ub-cs-xxs .ub-imgbox-t{font-size:13px}
.ub-ibox.ub-cs-xs .ub-ibox-t,.ub-imgbox.ub-cs-xs .ub-imgbox-t{font-size:14px}
.ub-ibox.ub-cs-s .ub-ibox-t,.ub-imgbox.ub-cs-s .ub-imgbox-t{font-size:15px}
.ub-ibox.ub-cs-m .ub-ibox-t,.ub-imgbox.ub-cs-m .ub-imgbox-t{font-size:17px}
.ub-ibox.ub-cs-l .ub-ibox-t,.ub-imgbox.ub-cs-l .ub-imgbox-t{font-size:20px}
.ub-ibox.ub-cs-xl .ub-ibox-t,.ub-imgbox.ub-cs-xl .ub-imgbox-t{font-size:24px}
/* Cỡ chữ MÔ TẢ tách riêng khỏi tiêu đề (`ub-ds-*`). `m` = 15px = cỡ cũ cố định. */
.ub-ibox.ub-ds-xxs .ub-ibox-x,.ub-imgbox.ub-ds-xxs .ub-imgbox-x{font-size:11px}
.ub-ibox.ub-ds-xs .ub-ibox-x,.ub-imgbox.ub-ds-xs .ub-imgbox-x{font-size:13px}
.ub-ibox.ub-ds-s .ub-ibox-x,.ub-imgbox.ub-ds-s .ub-imgbox-x{font-size:14px}
.ub-ibox.ub-ds-m .ub-ibox-x,.ub-imgbox.ub-ds-m .ub-imgbox-x{font-size:15px}
.ub-ibox.ub-ds-l .ub-ibox-x,.ub-imgbox.ub-ds-l .ub-imgbox-x{font-size:17px}
.ub-ibox.ub-ds-xl .ub-ibox-x,.ub-imgbox.ub-ds-xl .ub-imgbox-x{font-size:19px}
/* Lưới thẻ dùng CHUNG thang trên — không đẻ thang thứ hai. Thang này nhắm
   `.ub-card h3` / `.ub-card p` (Lưới thẻ không có lớp `-t`/`-x` riêng như Icon
   Box). ⚠️ Không thêm lớp nào thì hai quy tắc gốc 17px/14px vẫn nguyên, nên mọi
   trang đã dựng không đổi một pixel. */
.ub-cards.ub-cs-xxs .ub-card h3{font-size:13px}
.ub-cards.ub-cs-xs .ub-card h3{font-size:14px}
.ub-cards.ub-cs-s .ub-card h3{font-size:15px}
.ub-cards.ub-cs-m .ub-card h3{font-size:17px}
.ub-cards.ub-cs-l .ub-card h3{font-size:20px}
.ub-cards.ub-cs-xl .ub-card h3{font-size:24px}
.ub-cards.ub-ds-xxs .ub-card p{font-size:11px}
.ub-cards.ub-ds-xs .ub-card p{font-size:13px}
.ub-cards.ub-ds-s .ub-card p{font-size:14px}
.ub-cards.ub-ds-m .ub-card p{font-size:15px}
.ub-cards.ub-ds-l .ub-card p{font-size:17px}
.ub-cards.ub-ds-xl .ub-card p{font-size:19px}

/* ---------- Thẻ lấp đầy ô chứa, để một hàng cao bằng nhau ----------
   `.ub-col` là ô lưới nên nó tự kéo giãn bằng nhau, nhưng Ô ảnh / Icon Box nằm
   TRONG nó là khối thường, cao theo nội dung — mô tả xuống thêm một dòng là thẻ
   ấy cao hơn hẳn thẻ bên cạnh (đo ở 834px: 453 · 473 · 459 · 408, lệch 65px).
   ⚠️ `height:100%` chỉ ăn khi cha có chiều cao XÁC ĐỊNH; `.ub-col` là ô lưới đã
   kéo giãn nên có. Nằm trong khối cao tự do thì nó thành `auto`, tức là không
   đổi gì — an toàn cho mọi chỗ khác đang dùng.
   Khối chữ vốn đã `flex:1` nên nền trắng chạy hết thẻ; thiếu là nền dừng ngay
   dưới dòng cuối và hở một mảng ở đáy (bài học `.ub-posts--boxed .ub-post-b`). */
/* ⚠️ `:only-child` là BẮT BUỘC (thêm 2026-08-15). Luật này sinh ra cho ca "mỗi
   cột đúng MỘT thẻ, các cột cạnh nhau phải cao bằng nhau". Không rào thì xếp
   BỐN Icon Box dọc trong một cột là mỗi cái nhận `height:100%` của cột —
   bốn cái cùng cao bằng cả cột, chồng lên nhau rồi tràn hẳn ra ngoài, trôi
   xuống tận dưới chân trang. Đo thật ở trang Liên hệ: cột cao 481px mà mỗi
   thẻ cũng 481px, thẻ cuối nằm ở y=3008 trong khi cột kết thúc ở y=1433.
   Nhìn từ phía chủ site là "lỗi layout" không đoán được nguyên nhân.
   `:only-child` giữ NGUYÊN ca một-thẻ-một-cột, chỉ thôi ép khi có nhiều thẻ. */
.ub-col > .ub-imgbox:only-child,.ub-col > .ub-ibox:only-child{height:100%}
/* Thẻ cao bằng nhau thì phần dư phải rơi vào đâu đó — dồn LÊN TRÊN dòng cuối
   (thường là "Xem chi tiết") để nó ghim đáy thẻ và thẳng hàng với các thẻ bên
   cạnh, thay vì thõng xuống thành một mảng trắng dưới chân chữ.
   ⚠️ `:not(:first-child)` là bắt buộc: thẻ chỉ có mỗi tiêu đề thì dòng cuối
   CHÍNH LÀ dòng đầu, `margin-top:auto` đẩy nó rơi xuống đáy ô.
   Không có chỗ dư thì quy tắc này không làm gì — an toàn cho thẻ cao tự do. */
.ub-imgbox-b > :last-child:not(:first-child),
.ub-ibox-b > :last-child:not(:first-child){margin-top:auto}
/* ⚠️ Và cắt lề DƯỚI của phần tử cuối. Trong flex cột lề KHÔNG tự triệt tiêu, nên
   một khối như Dãy liên kết (`margin:20px 0`) nằm cuối thẻ là cộng thẳng 20px
   trống vào đáy — đo được chữ cách đáy thẻ 47px trong khi dải thật chỉ 23px. */
.ub-imgbox-b > :last-child,.ub-ibox-b > :last-child{margin-bottom:0}

@media (max-width:620px){
  /* Ảnh trái/phải ở khổ điện thoại: ảnh 40% bề ngang màn hình là một tấm tem,
     chữ bên cạnh chỉ còn 5–6 chữ mỗi dòng. Xuống hàng cho ảnh trải hết. */
  .ub-imgbox--left,.ub-imgbox--right{flex-direction:column}
  .ub-imgbox--left .ub-imgbox-m,.ub-imgbox--right .ub-imgbox-m{width:100%}
}

/* ---------------------------------------------------------------------------
   Đếm ngược
--------------------------------------------------------------------------- */
.ub-cd{display:flex;flex-wrap:wrap;gap:var(--ub-cd-gap,12px)}
.ub-ctr .ub-cd,.ub-cd.ub-tcenter{justify-content:center}
.ub-cd.ub-tright{justify-content:flex-end}
.ub-cd.ub-tleft{justify-content:flex-start}
.ub-cd-u{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:calc(var(--ub-cd-n,32px) * 2)}
.ub-cd-v{
  font-size:var(--ub-cd-n,32px);font-weight:800;line-height:1.1;
  color:var(--ub-cd-c,var(--ink,inherit));
  /* Chữ số ĐỀU BỀ NGANG: mặc định "1" hẹp hơn "8" nên ô giây co giãn theo từng
     nhịp đếm và cả dải nhảy qua nhảy lại mỗi giây. */
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum"
}
.ub-cd-l{font-style:normal;font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.ub-cd--box .ub-cd-u{
  padding:12px 14px;border-radius:var(--ub-cd-rad,12px);
  background:var(--ub-cd-bg,var(--card,rgba(22,19,16,.06)))
}
/* Hết giờ: lời nhắn thay chỗ cả dải ô, không chèn thêm vào dưới. */
.ub-cd.is-over{display:block}
.ub-cd.is-over .ub-cd-u{display:none}
.ub-cd-over{font-weight:700}

/* ---------------------------------------------------------------------------
   Tiêu đề: icon phía trước · gạch chân trang trí · cỡ chữ tự đặt
--------------------------------------------------------------------------- */
/* Ô px riêng ĐÈ lên bốn mức có sẵn. Đặt ở đây, SAU bốn quy tắc mức, nên chỉ cần
   cùng độ ưu tiên là thắng — không phải thêm lớp cho đủ mạnh. */
.ub-heading[style*="--ub-h-fs"] .ub-heading-t{font-size:var(--ub-h-fs)}
/* Cỡ chữ tiêu đề theo KHỔ MÀN HÌNH (ô "Cỡ chữ riêng" có `resp`).
   ⚠️ Phải đủ (0,3,0) mới hoà được với quy tắc ngay trên — `[style*=…]` tính
   ngang một lớp, nên `.ub-heading--fzsm .ub-heading-t` (0,2,0) THUA. Hoà rồi
   thắng nhờ đứng SAU trong tệp.
   ⚠️ Khối `sm` phải đứng SAU khối `md`: hai khối cùng `max-width` chồng nhau ở
   dải ≤620px, đặt ngược thì cỡ máy tính bảng đè cỡ điện thoại.
   Chưa đặt nấc nào thì không có lớp nào → kế thừa khổ lớn hơn, đúng như ô mô tả. */
@media (max-width:1024px){
  .ub-heading.ub-heading--fzmd .ub-heading-t{font-size:var(--ub-hfz-md)}
}
@media (max-width:620px){
  .ub-heading.ub-heading--fzsm .ub-heading-t{font-size:var(--ub-hfz-sm)}
}
/* MÀU CHỮ và GIÃN DÒNG của tiêu đề theo khổ (ô "Màu chữ" · "Giãn dòng").
   Gác bằng LỚP, và chỉ sinh lớp ở nấc người dựng thật sự đặt — nấc trống không
   có quy tắc nào nên tự kế thừa khổ lớn hơn.
   ⚠️ Màu đặt trên chính `.ub-heading` để nó CHẢY XUỐNG cả tiêu đề lẫn icon;
   `.ub-heading-sub` và `.ub-eyebrow` tự khai màu riêng nên không bị kéo theo —
   đúng ý, hai thứ ấy có ô màu của riêng chúng.
   ⚠️ Ba khối phải theo thứ tự máy tính → bảng → điện thoại: chúng cùng độ ưu
   tiên nên khối đứng sau thắng ở vùng chồng nhau. */
.ub-heading--c{color:var(--ub-hc)}
.ub-heading--lh .ub-heading-t{line-height:var(--ub-hlh)}
@media (max-width:1024px){
  .ub-heading--cmd{color:var(--ub-hc-md)}
  .ub-heading--lhmd .ub-heading-t{line-height:var(--ub-hlh-md)}
}
@media (max-width:620px){
  .ub-heading--csm{color:var(--ub-hc-sm)}
  .ub-heading--lhsm .ub-heading-t{line-height:var(--ub-hlh-sm)}
}
/* DÒNG NHỎ: màu + khe xuống tiêu đề, cũng theo khổ.
   ⚠️ `.ub-eyebrow` gốc khai `margin:0 0 8px` và `color:var(--accent)` — cả hai
   là (0,1,0) như quy tắc dưới đây, nên chỉ thắng nhờ đứng SAU trong tệp. Dời
   cụm này lên trước khai báo gốc là nó im lặng mất tác dụng. */
.ub-heading--ebc .ub-eyebrow{color:var(--ub-eb-c)}
.ub-heading--ebg .ub-eyebrow{margin-bottom:var(--ub-eb-g)}
@media (max-width:1024px){
  .ub-heading--ebcmd .ub-eyebrow{color:var(--ub-eb-c-md)}
  .ub-heading--ebgmd .ub-eyebrow{margin-bottom:var(--ub-eb-g-md)}
}
@media (max-width:620px){
  .ub-heading--ebcsm .ub-eyebrow{color:var(--ub-eb-c-sm)}
  .ub-heading--ebgsm .ub-eyebrow{margin-bottom:var(--ub-eb-g-sm)}
}
.ub-heading[style*="--ub-h-ss"] .ub-heading-sub{font-size:var(--ub-h-ss)}
/* Cỡ chữ + giãn dòng của MÔ TẢ theo khổ màn hình.
   ⚠️ Đủ (0,3,0) mới hoà được với quy tắc `[style*=…]` ngay trên — `[style*=…]`
   tính ngang một lớp. Hoà rồi thắng nhờ đứng SAU trong tệp.
   ⚠️ Khối `sm` phải đứng SAU khối `md`: hai khối cùng `max-width` chồng nhau ở
   dải nhỏ, đặt ngược thì cỡ máy tính bảng đè cỡ điện thoại.
   ⚠️ Giãn dòng của mô tả là biến RIÊNG (`--ub-hsl`), không dùng chung `--ub-hlh`
   của tiêu đề: hai đoạn chữ khác cỡ nên gần như luôn cần hai con số khác nhau. */
.ub-heading--sl .ub-heading-sub{line-height:var(--ub-hsl)}
@media (max-width:1024px){
  .ub-heading.ub-heading--ssmd .ub-heading-sub{font-size:var(--ub-hss-md)}
  .ub-heading--slmd .ub-heading-sub{line-height:var(--ub-hsl-md)}
}
@media (max-width:620px){
  .ub-heading.ub-heading--sssm .ub-heading-sub{font-size:var(--ub-hss-sm)}
  .ub-heading--slsm .ub-heading-sub{line-height:var(--ub-hsl-sm)}
}
/* Độ đậm riêng của tiêu đề. Gác bằng chính tên biến trong `style` nên chưa đặt
   thì quy tắc gốc `font-weight:var(--head-weight,700)` giữ nguyên.
   ⚠️ Tên biến phải KHÁC HẲN `--ub-h-fs`, không được là tiền tố của nhau:
   `[style*="--ub-h-f"]` sẽ khớp cả hai và một ô đè lên ô kia (bẫy `--btn-shadow`). */
.ub-heading[style*="--ub-h-fw"] .ub-heading-t{font-weight:var(--ub-h-fw)}
/* Kiểu chữ + giãn cách chữ của tiêu đề — thay cho việc phải gõ
   `text-transform`/`letter-spacing` vào ô "CSS riêng".
   ⚠️ `--ub-h-ls` KHÔNG được là tiền tố của nhóm gạch chân `--ub-h-lw` ·
   `--ub-h-lh` · `--ub-h-lg` · `--ub-h-lc` (và ngược lại): cổng gác bắt bằng
   chuỗi con, tên nào lồng vào tên kia là mở nhầm rồi hỏng ở mức tính giá trị.
   Quy tắc gốc `.ub-heading-t` đã khai `letter-spacing:-.01em` nên chỉ cần cùng
   độ ưu tiên và đứng SAU là thắng. */
.ub-heading--upper .ub-heading-t{text-transform:uppercase}
.ub-heading[style*="--ub-h-ls"] .ub-heading-t{letter-spacing:var(--ub-h-ls)}
/* Giãn cách chữ theo khổ. Cũng cần (0,3,0) để hoà với quy tắc `[style*=…]` trên. */
@media (max-width:1024px){
  .ub-heading.ub-heading--lsmd .ub-heading-t{letter-spacing:var(--ub-hls-md)}
}
@media (max-width:620px){
  .ub-heading.ub-heading--lssm .ub-heading-t{letter-spacing:var(--ub-hls-sm)}
}

/* Icon trước chữ: `1em` nên nó co theo đúng cỡ chữ tiêu đề khi chưa đặt cỡ
   riêng, và `vertical-align` kéo nó về ngang thân chữ thay vì tụt xuống đáy
   dòng như mặc định của inline-block. */
.ub-heading-i{
  display:inline-block;flex:none;margin-right:.4em;
  font-size:var(--ub-h-is,1em);color:var(--ub-h-ic,var(--accent))
}
.ub-heading-i .kico{width:1em;height:1em;vertical-align:-.12em}
/* Icon đặt TRÊN tiêu đề: một dòng riêng, tự bám theo cách căn chữ của cả khối. */
.ub-heading-ib{display:block;margin:0 0 10px;line-height:1}
.ub-heading-ib .ub-heading-i{margin-right:0;font-size:var(--ub-h-is,32px)}

/* Gạch chân vẽ bằng thẻ RIÊNG + `border-top`: nét đứt · nét chấm · hai nét chỉ
   có ở thuộc tính `border`, `background` không diễn tả được. Đặt border thẳng
   lên thẻ tiêu đề thì nó chạy hết bề ngang cột, không thu theo ô "Bề ngang
   gạch" được. */
.ub-heading-line{
  display:block;
  width:var(--ub-h-lw,64px);
  margin-top:var(--ub-h-lg,14px);
  border-top:var(--ub-h-lh,3px) solid var(--ub-h-lc,var(--accent))
}
.ub-heading-line--dashed{border-top-style:dashed}
.ub-heading-line--dotted{border-top-style:dotted}
/* "Hai nét" cần dày tối thiểu 3px: dưới ngưỡng ấy trình duyệt không đủ chỗ vẽ
   hai vạch và một đường `double` 1px hiện ra y hệt nét liền. */
.ub-heading-line--double{border-top-style:double;border-top-width:max(3px,var(--ub-h-lh,3px))}
/* Gạch bám theo cách căn chữ — căn giữa mà gạch vẫn dính mép trái thì nhìn như
   lỗi. `margin-top` phải khai LẠI ở đây: `margin` rút gọn ghi đè cả bốn cạnh
   nên viết `margin:0 auto` là khoảng cách với tiêu đề bị trả về 0. */
.ub-heading.ub-tcenter .ub-heading-line{margin-left:auto;margin-right:auto}
.ub-heading.ub-tright .ub-heading-line{margin-left:auto;margin-right:0}
/* Có gạch thì mô tả phải lùi xuống, không thì nó dính ngay dưới vạch. */
.ub-heading-line + .ub-heading-sub{margin-top:14px}

/* Đoạn chữ: cỡ chữ và giãn dòng tự đặt, cùng khuôn với Tiêu đề. */
.ub-text[style*="--ub-tx-fs"]{font-size:var(--ub-tx-fs)}
.ub-text[style*="--ub-tx-lh"]{line-height:var(--ub-tx-lh)}
/* Độ đậm · kiểu chữ · giãn cách chữ của đoạn văn (thêm 2026-08-12).
   Trang tham chiếu dùng `text-transform` 48 lần và `font-weight` 50 lần trên
   CHÍNH đoạn văn, nên thiếu ba thứ này là mọi đoạn viết hoa / đậm vừa đều phải
   gõ vào ô "CSS riêng". */
.ub-text[style*="--ub-tx-fw"]{font-weight:var(--ub-tx-fw)}
.ub-text[style*="--ub-tx-ls"]{letter-spacing:var(--ub-tx-ls)}
.ub-text--upper{text-transform:uppercase}
/* Cỡ chữ · giãn dòng theo KHỔ MÀN HÌNH.
   ⚠️ Tên biến KHÁC HẲN `--ub-tx-fs` / `--ub-tx-lh`, và gác bằng LỚP chứ không
   bằng `[style*=...]`: bộ chọn theo chuỗi style khớp cả tiền tố, nên
   `--ub-tx-fs-md` sẽ bật quy tắc máy tính lên với một biến chưa khai và đoạn văn
   MẤT LUÔN cỡ chữ ở khổ máy tính. Đúng bẫy `--btn-shadow`, đã vấp y hệt với
   Tiêu đề. */
@media (max-width:1024px){
  .ub-text.ub-text--fzmd{font-size:var(--ub-txfz-md)}
  .ub-text.ub-text--lhmd{line-height:var(--ub-txlh-md)}
}
@media (max-width:620px){
  .ub-text.ub-text--fzsm{font-size:var(--ub-txfz-sm)}
  .ub-text.ub-text--lhsm{line-height:var(--ub-txlh-sm)}
}
/* Màu chữ theo khổ (ô "Màu chữ" có `resp`) — gác bằng LỚP.
   ⚠️ Chỉ sinh lớp khi người dựng đặt màu cho ÍT NHẤT một khổ nhỏ; đặt mỗi khổ
   máy tính thì PHP vẫn in `color:` nội tuyến như trước, nên trang cũ không đổi
   một pixel. Xem chú thích trong `hdnt_ub_render_text()`. */
.ub-text--c{color:var(--ub-tx-c)}
@media (max-width:1024px){
  .ub-text--cmd{color:var(--ub-tx-c-md)}
}
@media (max-width:620px){
  .ub-text--csm{color:var(--ub-tx-c-sm)}
}

/* ---------------------------------------------------------------------------
   Hộp thông báo · Thành viên · Nút chia sẻ
--------------------------------------------------------------------------- */
.ub-msg{
  display:flex;gap:12px;align-items:flex-start;
  padding:16px 18px;border-radius:12px;
  /* Nền pha từ CHÍNH màu sắc thái: hai chỗ tự gõ hai mã màu là kiểu gì cũng
     lệch tông sau một đợt sửa. `color-mix` không hiểu thì rơi về nền xám nhạt,
     vẫn đọc được. */
  background:#f6f6f4;
  background:color-mix(in srgb,var(--ub-msg-c) 10%,transparent);
  border-left:var(--ub-msg-bar,4px) solid var(--ub-msg-c)
}
.ub-msg--info{--ub-msg-c:#2563EB}
.ub-msg--ok{--ub-msg-c:#16A34A}
.ub-msg--warn{--ub-msg-c:#C49B40}
.ub-msg--stop{--ub-msg-c:#DC2626}
.ub-msg-i{flex:none;font-size:20px;line-height:1;color:var(--ub-msg-c)}
.ub-msg-i .kico{width:1em;height:1em}
.ub-msg-b{min-width:0}
.ub-msg-t{display:block;margin-bottom:2px;font-weight:700}
.ub-msg-x p{margin:4px 0 0}
.ub-msg-x p:first-child{margin-top:0}

.ub-mem{display:flex;flex-direction:column}
/* ⚠️ Tỉ lệ khai ở KHUNG, ảnh định vị tuyệt đối phủ kín khung — KHÔNG khai
   `aspect-ratio` thẳng lên `<img>`. Thẻ ảnh mang sẵn thuộc tính `width`/`height`
   của WordPress, mà chiều cao đã có giá trị cụ thể thì `aspect-ratio` bị bỏ qua:
   đo được ô 160×300 thay vì 160×160, ảnh tròn thành hình trứng. Đây đúng bài
   học "đừng để kích thước ảnh phụ thuộc vào khung mà khung lại phụ thuộc vào
   ảnh" — cách chữa là khung tự quyết, ảnh chỉ việc lấp đầy.
   `width` vẫn phải khai tường minh: khung có tỉ lệ nằm trong flex CỘT thì Safari
   coi bề ngang là suy ra được và không kéo giãn nữa. */
.ub-mem-m{position:relative;display:block;overflow:hidden;width:var(--ub-mem-s,160px);max-width:100%;aspect-ratio:1/1}
.ub-mem-m img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* Bo góc chỉ đặt ở KHUNG: `overflow:hidden` của nó đã cắt rồi, để ảnh giữ bo
   góc riêng là thừa một lớp và hở khe ở mép (bài học của thẻ danh mục bo khối). */
.ub-mem--circle .ub-mem-m{border-radius:999px}
.ub-mem--round .ub-mem-m{border-radius:16px}
.ub-mem-n{margin:14px 0 0;font-size:18px;font-weight:700;line-height:1.3}
.ub-mem-r{margin:2px 0 0;font-size:14px;color:var(--accent);font-weight:600}
.ub-mem-x{margin-top:8px;color:var(--muted);font-size:15px;line-height:1.65}
/* Căn chữ phải kéo theo cả khung ẢNH — ảnh dính mép trái dưới một khối chữ căn
   giữa thì nhìn như lỗi. */
.ub-mem.ub-ct-center{align-items:center;text-align:center}
.ub-mem.ub-ct-right{align-items:flex-end;text-align:right}
.ub-mem.ub-ct-left{align-items:flex-start;text-align:left}

.ub-share{display:flex;flex-wrap:wrap;align-items:center;gap:10px}
.ub-share.ub-tcenter{justify-content:center}
.ub-share.ub-tright{justify-content:flex-end}
.ub-share-l{font-weight:600;font-size:14px;color:var(--muted)}
.ub-share-b{
  display:grid;place-items:center;flex:none;
  width:var(--ub-share-s,38px);height:var(--ub-share-s,38px);
  border-radius:999px;border:1px solid var(--line,rgba(22,19,16,.16));
  color:inherit;font-size:calc(var(--ub-share-s,38px) * .46);
  transition:background .2s,color .2s,border-color .2s
}
.ub-share-b .kico{width:1em;height:1em}
/*
 * Nút Zalo dùng ẢNH THẬT (logo xanh trên nền trắng) chứ không phải icon mặt nạ
 * — xem `hdnt_ub_render_share()`. Vì thế nút ấy KHÔNG tô nền theo màu nhấn của
 * theme ở mọi kiểu: nền vàng phủ dưới một logo xanh trắng thì nhìn ra hai lớp
 * chồng nhau. Nhân đôi tên lớp để thắng cả bốn kiểu nút khai ở dưới.
 */
.ub-share-b .ub-share-img{width:1.5em;height:1.5em;object-fit:contain;border-radius:50%}
.ub-share-b--zalo.ub-share-b--zalo{background:#fff;border-color:rgba(22,19,16,.14);padding:0}
.ub-share-b--zalo.ub-share-b--zalo:hover{background:#fff;filter:brightness(.97)}
.ub-share-b:hover{background:var(--accent);border-color:var(--accent);color:#fff}
/* Đã chép xong thì nói ra — bấm một nút mà không có gì đổi thì người ta bấm
   thêm mấy lần nữa. Chữ vẽ bằng ::after nên không phải thêm thẻ nào vào HTML. */
.ub-share-b.is-done{background:var(--accent);border-color:var(--accent);color:#fff}
/* Bốn kiểu nút của Tuỳ biến ▸ Chia sẻ. Kiểu mặc định (viền tròn) KHÔNG có lớp
   nào — dáng đang chạy phải là nhánh "không áp thêm gì", không phải một kiểu tự
   khai lại đúng bộ số ấy. Đặt SAU quy tắc gốc: cùng độ ưu tiên thì thắng theo
   thứ tự nạp. */
.ub-share--plain .ub-share-b{border-color:transparent;background:none}
.ub-share--plain .ub-share-b:hover{background:none;color:var(--accent)}
.ub-share--fill .ub-share-b{background:var(--accent);border-color:var(--accent);color:#fff}
.ub-share--fill .ub-share-b:hover{filter:brightness(1.08)}
.ub-share--square .ub-share-b{border-radius:10px;background:var(--accent);border-color:var(--accent);color:#fff}
.ub-share--square .ub-share-b:hover{filter:brightness(1.08)}
.ub-share--soft .ub-share-b{border-radius:10px;border-color:transparent;background:var(--soft,rgba(22,19,16,.06))}

/* ---------------------------------------------------------------------------
   Form nhận tư vấn
--------------------------------------------------------------------------- */
.ub-form{margin:0}
.ub-form--boxed{
  padding:26px 24px;
  background:var(--paper,#fff);
  border:1px solid var(--line,#EAECEF);
  border-radius:var(--radius,14px);
  box-shadow:0 10px 30px rgba(0,0,0,.06)
}
.ub-form-h{margin-bottom:16px}
.ub-form-t{margin:0;font-family:var(--font-head,inherit);font-size:21px;font-weight:700;line-height:1.3}
.ub-form-x{margin:6px 0 0;color:var(--muted);font-size:14px;line-height:1.6}
.ub-form-g{display:flex;flex-direction:column;gap:10px}
/* Hàng ngang: các ô chia đều, nút co đúng bằng chữ. `min-width:0` là bắt buộc —
   bề ngang tối thiểu của một ô nhập là ~180px, thiếu nó thì ba ô đẩy hàng tràn. */
.ub-form--inline .ub-form-g{flex-direction:row;flex-wrap:wrap;align-items:center}
.ub-form--inline .ub-form-i{flex:1 1 180px;min-width:0}
.ub-form--inline .ub-form-b{flex:none}
.ub-form-i{
  width:100%;padding:12px 14px;
  font:inherit;font-size:15px;color:inherit;
  background:var(--paper,#fff);
  border:1px solid var(--line,#EAECEF);
  border-radius:calc(var(--radius,14px) / 2);
  transition:border-color .15s,box-shadow .15s
}
.ub-form-i::placeholder{color:var(--muted)}
/* Vành focus vẽ bằng `box-shadow` chứ không phải `outline`: outline không bo
   theo `border-radius` ở mọi trình duyệt, ra một khung vuông quanh ô bo góc. */
.ub-form-i:focus{
  outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)
}
.ub-form-i--area{resize:vertical;min-height:88px;line-height:1.6}
/* Ô nhập "nền đặc" (ô "Kiểu ô nhập" của khối Form): nền kem, không viền, bo góc
   to hơn và cao hơn — dáng của form Liên hệ viết tay.

   ⚠️ PHẢI đặt SAU `.ub-form-i:focus` ở ngay trên. Luật focus là (0,2,0), bằng
   đúng `.ub-form--fill .ub-form-i`; đặt trước thì focus thắng và ô nhập mọc lại
   viền ngay khi bấm vào. Vì thế luật focus của kiểu này cũng phải leo lên
   (0,3,0) — cùng cái bẫy `.ub-ibox{color:inherit}` đè `.ub-dark` ở trên.

   ⚠️ `--ub-form-fill` có màu dự phòng ngay tại đây: khối không đặt "Màu nền ô
   nhập" thì biến KHÔNG được gán, mà `background:var(--x)` không dự phòng là
   hỏng ở mức tính giá trị và ô nhập mất nền hẳn. */
.ub-form--fill .ub-form-i{
  background:var(--ub-form-fill,#F4EFE6);
  border-color:transparent;
  border-radius:var(--radius,14px);
  padding:15px 18px
}
.ub-form--fill .ub-form-i:focus{
  border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)
}
.ub-form-b{width:100%}
.ub-form--inline .ub-form-b{width:auto}
.ub-form-p{margin:12px 0 0;color:var(--muted);font-size:12.5px;line-height:1.6}
.ub-form.ub-tcenter .ub-form-p,.ub-form.ub-tcenter .ub-form-h{text-align:center}
/* Ô bẫy bot — đẩy RA NGOÀI khung nhìn, KHÔNG `display:none`.

   Bot dò form loại bỏ ô `display:none` và ô `type="hidden"` từ lâu; ô vẫn nằm
   trong luồng bố cục nhưng ở toạ độ âm thì chúng vẫn điền. Người thật không
   thấy, không Tab tới được (`tabindex="-1"` ở markup), trình đọc màn hình bỏ
   qua (`aria-hidden`).

   ⚠️ Luật này CÒN được lặp lại bằng thuộc tính `style` ngay trên thẻ, và đó là
   chủ ý chứ không phải quên dọn. `ub.css` chỉ nạp khi `hdnt_ub_assets()` nhận
   ra trang có khối trình dựng — đã trượt NĂM lần với những chỗ in markup ngoài
   nội dung bài. Trượt ở đây thì ô hiện nguyên hình kèm nhãn "Để trống ô này",
   khách tử tế điền vào, và server lặng lẽ vứt đơn của họ đi. Chi phí của một
   dòng thừa nhỏ hơn nhiều lần chi phí của cái hỏng câm đó. */
.ub-hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}
/* Nền tối: ô nhập phải tự khai nền + màu chữ, không thì ô trắng chữ trắng —
   đúng luật "bảng nào tự khai nền thì phải tự khai luôn màu chữ".

   ⚠️ TRỪ form có THẺ NỀN SÁNG (`--boxed`). Thẻ ấy vẽ `background:var(--paper)`
   tức nền TRẮNG ngay giữa dải tối, nên ruột nó phải theo tông của THẺ chứ không
   theo tông của dải. Bản cũ không trừ ra: ô nhập ăn `color:#fff` +
   `background:rgba(255,255,255,.08)` rồi nằm trên thẻ trắng — trắng trên trắng,
   **cả form biến mất**. Chủ site dựng thử một trang rồi báo đúng cảnh ấy
   (2026-08-12): "form gần như không dùng được ở section tối".

   Nặng hơn ô nhập: `.ub-dark{color:#fff}` kế thừa xuống nên tiêu đề và mô tả
   của form cũng trắng trên trắng. Vì thế `.ub-form--boxed` phải vào hẳn danh
   sách ĐẢO TÔNG bên dưới, không chỉ gỡ riêng ô nhập ra. */
.ub-dark .ub-form:not(.ub-form--boxed) .ub-form-i,
.ub-section--dark .ub-form:not(.ub-form--boxed) .ub-form-i{
  background:rgba(255,255,255,.08);
  border-color:rgba(255,255,255,.22);
  color:#fff
}

@media (max-width:620px){
  /* Khổ điện thoại bỏ hàng ngang: ba ô cạnh nhau trong 366px thì mỗi ô còn
     ~100px, gõ số điện thoại không nhìn thấy hết chữ. */
  .ub-form--inline .ub-form-g{flex-direction:column;align-items:stretch}
  .ub-form--inline .ub-form-b{width:100%}
}

/* ---------------------------------------------------------------------------
   Lưới dự án đã làm — cùng bộ quy ước với ba lưới dữ liệu thật kia.
--------------------------------------------------------------------------- */
.ub-projs{display:grid;grid-template-columns:repeat(var(--ub-cols,4),minmax(0,1fr));gap:var(--ub-cgap,24px)}
/* Hàng lọc danh mục + hàng số trang — chỉ có khi bật hai công tắc tương ứng,
   nên mọi dải "Dự án nổi bật" đã dựng không đụng tới mấy luật này.
   ⚠️ `.is-on` chứ không phải `[aria-current]`: dãy nút này là LIÊN KẾT sang
   trang khác, mà `aria-current="page"` chỉ đúng cho mục đang đứng — dùng sai
   thì trình đọc màn hình đọc ra bốn "trang hiện tại" cùng lúc. */
.ub-projs-f{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 24px}
.ub-projs-c{
  display:inline-flex;align-items:center;height:36px;padding:0 16px;
  border-radius:999px;border:1px solid var(--line,#EAECEF);
  background:var(--paper,#fff);color:inherit;
  font-size:14px;font-weight:600;text-decoration:none;
  transition:background-color .15s,border-color .15s,color .15s
}
.ub-projs-c:hover{border-color:var(--accent);color:var(--accent)}
/* Nút đang chọn phải ĐẢO nền, không chỉ đổi màu chữ: đổi mỗi màu chữ thì trên
   nền kem của giao diện này gần như không phân biệt được cái nào đang bật. */
.ub-projs-c.is-on{background:var(--ink,#161310);border-color:var(--ink,#161310);color:#fff}
.ub-projs-none{margin:0;color:var(--muted);font-size:15px}
.ub-projs-p{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin:32px 0 0}
.ub-projs-p .page-numbers{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:38px;height:38px;padding:0 12px;
  border-radius:10px;border:1px solid var(--line,#EAECEF);
  background:var(--paper,#fff);color:inherit;
  font-size:14px;font-weight:600;text-decoration:none
}
.ub-projs-p .page-numbers:hover{border-color:var(--accent);color:var(--accent)}
.ub-projs-p .page-numbers.current{background:var(--ink,#161310);border-color:var(--ink,#161310);color:#fff}
.ub-projs-p .page-numbers.dots{border-color:transparent;background:none}
.ub-proj{
  display:flex;flex-direction:column;overflow:hidden;
  color:inherit;text-decoration:none;
  background:var(--paper,#fff);
  border:1px solid var(--line,#EAECEF);
  border-radius:var(--ub-crad,var(--radius,14px));
  box-shadow:var(--ub-cshadow,none);
  transition:box-shadow .25s,transform .25s
}
.ub-proj:hover{box-shadow:var(--ub-cshadow-h,var(--ub-cshadow,none))}

/* ---------------------------------------------------------------------------
   ⚠️ Khối tự khai NỀN SÁNG thì phải tự khai luôn MÀU CHỮ

   Thẻ dự án · bài viết · danh mục · lời khen · gói giá · thẻ icon đều vẽ nền
   trắng (`--paper`) nhưng để `color:inherit`. Đặt chúng vào một dải NỀN TỐI
   (`.ub-dark`) thì chúng thừa hưởng `--ink:#fff` của dải — chữ TRẮNG trên nền
   TRẮNG, mất hẳn. Chủ site báo đúng cảnh này ở dải "Dự án tiêu biểu": ảnh hiện,
   phần chữ dưới ảnh là một mảng trắng rỗng (HTML vẫn có đủ tiêu đề và link).

   Trả bộ token về tông SÁNG cho riêng cây con của thẻ, không đụng phần còn lại
   của dải.

   ⚠️ **Câu "Có test quét: khối nào khai `background:var(--paper` mà không nằm
   trong danh sách này là đỏ" từng đứng ở đây là SAI — phép kiểm ấy CHƯA BAO GIỜ
   tồn tại.** Phép duy nhất có thật chỉ đối chiếu bảy cái tên đã liệt kê nằm
   đúng một khối chọn; nó không hề dò ra khối MỚI. Cái lưới an toàn được hứa
   trong chú thích, rồi không ai viết, và mọi người sau đọc chú thích thì yên
   tâm là đã có lưới.

   Cái giá đã phải trả: `.ub-form--boxed` thêm vào sau, cũng khai
   `background:var(--paper,#fff)`, cũng không có trong danh sách — và cả form
   biến mất trên dải tối. Không lỗi, không cảnh báo. Nay phép quét đã viết thật
   (`tests/test-builder.php`, tìm "quét thẻ nền sáng").
--------------------------------------------------------------------------- */
.ub-dark .ub-proj,
.ub-dark .ub-post,
.ub-dark .ub-cat,
.ub-dark .ub-card,
.ub-dark .ub-qa,
.ub-dark .ub-quote,
.ub-dark .ub-form--boxed,
/* Nút lọc danh mục và ô số trang cũng tự khai `background:var(--paper)` tức nền
   TRẮNG, nên phải vào đây. Thiếu là trong dải tối chúng ăn `color:#fff` của
   `.ub-dark` rồi thành chữ trắng trên nền trắng — biến mất hẳn. Đúng lớp bọ mà
   `.ub-form--boxed` đã vấp, và bộ kiểm bắt được TRƯỚC khi lên site. */
.ub-dark .ub-projs-c,
.ub-dark .ub-projs-p .page-numbers,
.ub-dark .ub-plan{
  color:#161310;
  --ink:#161310;
  --muted:#6B7280;
  --line:#EAECEF;
  --line-strong:#D8DCE2;
}
/* ⚠️ TRỪ hai trạng thái "đang chọn" ra: chúng cố ý ĐẢO nền sang màu mực, nên
   `--ink:#161310` ở trên biến chúng thành nền tối chữ tối. Luật này phải nằm SAU
   và nặng hơn (0,3,0 · 0,4,0) — đúng bài học thứ tự đã vấp hai lần hôm nay. */
.ub-dark .ub-projs-c.is-on{background:#161310;border-color:#161310;color:#fff}
.ub-dark .ub-projs-p .page-numbers.current{background:#161310;border-color:#161310;color:#fff}

.ub-proj-m{display:block;overflow:hidden;background:var(--card)}
/* Ảnh bỏ bo góc riêng: `overflow:hidden` của thẻ đã cắt hai góc trên: để ảnh giữ
   bo góc của nó thì mép dưới cũng cong và hở ra hai khe ngay giữa thẻ. */
.ub-proj-m img{display:block;width:100%;height:100%;object-fit:cover;border-radius:0;transition:transform .35s,opacity .25s}
.ub-projs.ub-pr-1-1 .ub-proj-m{aspect-ratio:1/1}
.ub-projs.ub-pr-4-3 .ub-proj-m{aspect-ratio:4/3}
.ub-projs.ub-pr-3-4 .ub-proj-m{aspect-ratio:3/4}
.ub-projs.ub-pr-16-9 .ub-proj-m{aspect-ratio:16/9}
/* `flex:1` để khối chữ ăn hết chỗ dư — thiếu nó thì nền dừng ngay dưới dòng cuối
   và thẻ nào tên ngắn cũng hở một mảng ở đáy. */
.ub-proj-b{flex:1;display:flex;flex-direction:column;gap:4px;padding:var(--ub-cpad,16px 18px 18px)}
.ub-proj-t{margin:0;font-size:15px;font-weight:700;line-height:1.4}
.ub-proj-x{font-size:13px;color:var(--muted)}
.ub-projs--hv-zoom .ub-proj:hover img{transform:scale(1.06)}
.ub-projs--hv-fade .ub-proj:hover img{opacity:.85}
.ub-projs--hv-lift .ub-proj:hover{transform:translateY(-4px)}

@media (max-width:1024px){
  .ub-projs{grid-template-columns:repeat(var(--ub-cols-md,2),minmax(0,1fr))}
}
@media (max-width:620px){
  .ub-projs{grid-template-columns:repeat(var(--ub-cols-sm,2),minmax(0,1fr))}
}

/* ---------------------------------------------------------------------------
   Ảnh Trước – Sau
--------------------------------------------------------------------------- */
.ub-cmp{position:relative;overflow:hidden;border-radius:var(--ub-cmp-rad,14px);background:var(--card);touch-action:pan-y}
/* `.ub-ratio img` đã lo `position:absolute;inset:0;object-fit:cover` — không
   khai lại, để một quy ước duy nhất lo việc phủ kín khung. */
.ub-cmp-i{display:block}
/*
   ⚠️ Cắt bằng `clip-path`, KHÔNG phải `width`: thu `width` thì ảnh bên trên bị
   BÓP NGANG chứ không phải bị che, hai bên hết khớp nhau và cả khối thành vô
   nghĩa. `inset()` cắt đúng phần thừa mà ảnh vẫn giữ nguyên kích thước.
*/
.ub-cmp-i--a{clip-path:inset(0 0 0 var(--ub-cmp,50%))}
.ub-cmp-bar{
  position:absolute;top:0;bottom:0;left:var(--ub-cmp,50%);
  width:2px;margin-left:-1px;
  background:#fff;box-shadow:0 0 0 1px rgba(0,0,0,.18);
  pointer-events:none
}
.ub-cmp-grip{
  position:absolute;top:50%;left:50%;translate:-50% -50%;
  display:grid;place-items:center;
  width:38px;height:38px;border-radius:999px;
  background:#fff;box-shadow:0 4px 14px rgba(0,0,0,.28)
}
/* Hai mũi tên vẽ bằng ::before/::after của chính tay nắm — không thêm thẻ nào
   vào HTML, và chúng không bao giờ nhận được cú bấm (cha đã pointer-events:none). */
.ub-cmp-grip::before,.ub-cmp-grip::after{
  content:"";position:absolute;
  width:7px;height:7px;
  border-top:2px solid var(--ink,#161310);border-right:2px solid var(--ink,#161310)
}
.ub-cmp-grip::before{rotate:-135deg;margin-left:-9px}
.ub-cmp-grip::after{rotate:45deg;margin-left:9px}
/*
   Thanh kéo THẬT phủ kín cả khối, trong suốt hoàn toàn: kéo bằng chuột, vuốt
   bằng tay, phím mũi tên và trình đọc màn hình đều chạy sẵn nhờ trình duyệt.
   Bộ kéo tự viết phải làm lại cả bốn và thường bỏ sót ba cái sau.
*/
.ub-cmp-r{
  position:absolute;inset:0;
  width:100%;height:100%;margin:0;padding:0;
  appearance:none;-webkit-appearance:none;
  background:transparent;border:0;cursor:ew-resize
}
/* Nút gạt của range ẩn đi — phần nhìn thấy là `.ub-cmp-grip` ở trên. Vẫn phải
   khai kích thước, không thì Firefox không cho kéo. */
.ub-cmp-r::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;width:44px;height:44px;background:transparent;cursor:ew-resize}
.ub-cmp-r::-moz-range-thumb{width:44px;height:44px;background:transparent;border:0;cursor:ew-resize}
.ub-cmp-r::-moz-range-track{background:transparent;border:0}
.ub-cmp-r:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.ub-cmp-l{
  position:absolute;top:12px;z-index:1;
  padding:5px 12px;border-radius:999px;
  background:rgba(22,19,16,.72);color:#fff;
  font-size:12px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  pointer-events:none
}
.ub-cmp-l--b{left:12px}
.ub-cmp-l--a{right:12px}

/* --------------------------------------------------------------------------
   ĐIỂM NÓNG TRÊN ẢNH — không một dòng JS nào.

   Bảng chú thích mở bằng `:hover` LẪN `:focus-within` (đúng cơ chế bảng mega).
   Chấm là `<button>` nên nó nhận tiêu điểm: máy cảm ứng chạm vào là focus →
   bảng mở, chạm ra ngoài là blur → bảng đóng. Dựng bằng JS thì phải tự lo cả
   bàn phím lẫn cảm ứng, mà trình duyệt vốn đã làm sẵn cả hai.
-------------------------------------------------------------------------- */
/* ⚠️ Cỡ chấm ĐANG DÙNG là một biến THỨ HAI (`--ub-hs-s`) khai ở đây, không phải
   `--ub-hs-d` mà PHP in nội tuyến. Style nội tuyến thắng mọi quy tắc trong tệp,
   nên nếu khổ hẹp cũng đi sửa `--ub-hs-d` thì media query không bao giờ với tới
   — đo được chấm đứng nguyên 28px ở CẢ 360px. Biến in ra chỉ CẤP giá trị; quy
   tắc DÙNG nó thì nằm trong tệp và đè lại được. (Bài học `--ih`/`--ih-sm` của
   khối Ảnh, nhìn từ chiều ngược lại.) */
.ub-hs{--ub-hs-s:var(--ub-hs-d,28px)}

/* ⚠️ KHÔNG `overflow:hidden` ở khung: cắt ở đây là xén luôn bảng chú thích của
   mấy chấm sát mép (đo ở 390px: bảng của chấm x=4% thò ra 14,3px và bị cắt
   cụt). Bo góc đặt thẳng lên tấm ảnh — nó là hình chữ nhật nên không cần cắt. */
.ub-hs-media{position:relative}
.ub-hs-media > img{
  display:block;width:100%;height:100%;object-fit:cover;
  border-radius:var(--ub-hs-r,16px)
}

/* ⚠️ Vỏ điểm nóng KHÔNG sinh hộp (`display:contents`). Nếu nó là một khối
   `position:absolute` thì CHÍNH NÓ thành khối chứa của bảng chú thích — mà bề
   ngang nó chỉ bằng cái chấm, nên mọi phần trăm trong `max-width` của bảng quy
   chiếu vào 28px chứ không vào tấm ảnh, tức không cách nào kẹp bảng cho vừa
   khung. Bỏ hộp đi thì cả chấm lẫn bảng cùng neo vào `.ub-hs-media`.
   `:hover` vẫn khớp: theo đặc tả, một phần tử được coi là đang-rê khi con cháu
   nó đang-rê, và `display:contents` không đổi quan hệ cha–con. */
.ub-hs-p{display:contents}

.ub-hs-dot{
  position:absolute;left:var(--ub-hs-x,50%);top:var(--ub-hs-y,50%);
  translate:-50% -50%;z-index:2;
  width:var(--ub-hs-s);height:var(--ub-hs-s);
  padding:0;border:0;border-radius:50%;cursor:pointer;
  background:var(--ub-hs-c,var(--accent,#C49B40));
  /* Vành trắng vẽ bằng `box-shadow` chứ không `border` — border cộng thêm vào
     kích thước nên chấm 28px thành 32px và lệch khỏi toạ độ đã đặt. */
  box-shadow:0 0 0 3px rgba(255,255,255,.92),0 4px 12px rgba(22,19,16,.28)
}
/* Dấu cộng vẽ bằng CSS, không thêm thẻ nào — và không dùng ký tự "+": mỗi hệ
   điều hành vẽ một kiểu, có máy còn lệch hẳn khỏi tâm chấm. */
.ub-hs-dot::before,.ub-hs-dot::after{
  content:"";position:absolute;left:50%;top:50%;translate:-50% -50%;
  background:#fff;border-radius:2px
}
.ub-hs-dot::before{width:44%;height:2px}
.ub-hs-dot::after{width:2px;height:44%}

/* Vòng sóng: thẻ RIÊNG chứ không `::before`/`::after` của chính chấm — hai
   pseudo kia đã dành cho dấu cộng. */
.ub-hs-w{display:none}
.ub-hs--pulse .ub-hs-w{
  display:block;position:absolute;inset:0;border-radius:50%;
  background:var(--ub-hs-c,var(--accent,#C49B40));
  animation:ub-hs-wave 2s ease-out infinite
}
@keyframes ub-hs-wave{
  0%{opacity:.5;scale:1}
  100%{opacity:0;scale:2.3}
}

.ub-hs-tip{
  position:absolute;z-index:3;
  width:max-content;
  padding:10px 12px;border-radius:12px;
  background:#fff;color:#161310;
  box-shadow:0 0 0 1px rgba(22,19,16,.07),0 14px 32px rgba(22,19,16,.18);
  /* Ẩn bằng `visibility` chứ không chỉ `opacity:0`: chữ mờ 0 vẫn được trình đọc
     màn hình đọc, tức khách khiếm thị nghe hết mọi bảng chú thích cùng lúc. */
  opacity:0;visibility:hidden;
  transition:opacity .18s ease,visibility .18s ease,translate .18s ease;
  pointer-events:none
}
.ub-hs-p:hover .ub-hs-tip,
.ub-hs-p:focus-within .ub-hs-tip{opacity:1;visibility:visible;pointer-events:auto}

.ub-hs-t{display:block;font-size:14px;font-weight:700;line-height:1.35}
.ub-hs-t a{color:inherit;text-decoration:none}
.ub-hs-t a:hover{text-decoration:underline}
.ub-hs-x{display:block;margin-top:3px;font-size:13px;line-height:1.45;color:rgba(22,19,16,.66)}

/* Phía mở — suy từ chính toạ độ ở PHP, không có ô nào cho người dựng chọn:
   chấm ở mép phải mà bảng mở sang phải là nó thò ra ngoài ảnh. */
.ub-hs-p--xl .ub-hs-tip{left:calc(var(--ub-hs-x) - var(--ub-hs-s) / 2);max-width:calc(100% - var(--ub-hs-x) + var(--ub-hs-s) / 2)}
.ub-hs-p--xr .ub-hs-tip{right:calc(100% - var(--ub-hs-x) - var(--ub-hs-s) / 2);max-width:calc(var(--ub-hs-x) + var(--ub-hs-s) / 2)}
/*
 * ⚠️ Nhánh CĂN GIỮA phải tự kẹp bề ngang theo khoảng hẹp hơn trong hai bên —
 * không thì chấm ở 30% của một tấm ảnh 342px (khổ 390) có bảng 260px trải từ
 * −27px, tức thò ra ngoài cả khung LẪN khung nhìn. Nay bảng rộng nhiều nhất là
 * gấp đôi khoảng cách tới mép gần nhất, nên nó luôn nằm trọn trong ảnh.
 * Phần trăm ở đây quy chiếu vào `.ub-hs-media` — đó chính là lý do vỏ điểm nóng
 * phải `display:contents`.
 */
.ub-hs-p--xc .ub-hs-tip{
  left:var(--ub-hs-x);
  max-width:min(260px, calc(2 * var(--ub-hs-x)), calc(2 * (100% - var(--ub-hs-x))))
}

.ub-hs-p--up .ub-hs-tip{bottom:calc(100% - var(--ub-hs-y) + var(--ub-hs-s) / 2 + 10px)}
.ub-hs-p--dn .ub-hs-tip{top:calc(var(--ub-hs-y) + var(--ub-hs-s) / 2 + 10px)}

/* ⚠️ Nhánh căn giữa khai `translate` ở CẢ hai trạng thái: `translate` là thuộc
   tính ĐƠN, khai lần sau XOÁ lần trước (không cộng dồn như hai `transform`).
   Thiếu `-50%` ở một nhánh là bảng nhảy hẳn sang phải nửa bề ngang. */
.ub-hs-p--xc .ub-hs-tip{translate:-50% 0}
.ub-hs-p--xc.ub-hs-p--up .ub-hs-tip{translate:-50% 6px}
.ub-hs-p--xc.ub-hs-p--dn .ub-hs-tip{translate:-50% -6px}
.ub-hs-p--xc.ub-hs-p--up:hover .ub-hs-tip,
.ub-hs-p--xc.ub-hs-p--up:focus-within .ub-hs-tip,
.ub-hs-p--xc.ub-hs-p--dn:hover .ub-hs-tip,
.ub-hs-p--xc.ub-hs-p--dn:focus-within .ub-hs-tip{translate:-50% 0}

@media (prefers-reduced-motion:reduce){
  .ub-hs--pulse .ub-hs-w{animation:none;opacity:0}
}

/* Khổ hẹp: chấm nhỏ lại một nấc — ảnh co theo bề ngang màn hình nên chấm 28px
   giữ nguyên sẽ che mất chính chi tiết nó đang chỉ vào. Nhân theo cỡ người
   dựng đặt chứ không ghim một con số: họ chọn 48px thì ở đây phải ra 38, không
   phải 22. */
@media (max-width:620px){
  .ub-hs{--ub-hs-s:calc(var(--ub-hs-d,28px) * .8)}
  .ub-hs-tip{padding:8px 10px}
  .ub-hs-t{font-size:13px}
  .ub-hs-x{font-size:12px}
}

/* ==========================================================================
   BỘ THUỘC TÍNH CHUNG THEO TỪNG KHỔ MÀN HÌNH
   Chủ site chốt: chỉ đổi CSS riêng cho từng thiết bị, KHÔNG dựng ba bản nội
   dung. Trang vẫn xuất một bản HTML; khác biệt nằm hết ở đây.

   ⚠️ BA LỚP RIÊNG chứ không một lớp chung với `var(--x, mặc-định)`. Một lớp
   chung nghĩa là quy tắc áp cho CẢ ba khổ, nên nấc chưa đặt sẽ ghi đè bằng giá
   trị dự phòng và XOÁ MẤT style của chính khối — chỉ đặt đệm cho điện thoại thì
   máy tính bị ép `padding:0`, mất luôn phần đệm khối tự khai. Có lớp riêng thì
   nấc chưa đặt không sinh quy tắc nào, và việc "kế thừa khổ lớn hơn" tự xảy ra
   vì quy tắc khổ trên vẫn còn hiệu lực.

   ⚠️ Thứ tự trong MỖI khổ giữ đúng luật cũ: Margin bốn cạnh đặt NỀN, rồi mt/mb
   đè từng cạnh, rồi "Đặt khối về" đè margin trái/phải. Xếp ngược là Margin xoá
   mất lựa chọn của chúng.

   Ngưỡng 1024 · 620 khớp bề ngang khổ xem trước của trình dựng (bảng 834 · điện
   thoại 414) — cùng bộ ngưỡng với các ô "theo khổ" đã có (chiều cao ảnh…).
   ========================================================================== */
/* ==========================================================================
   GIÁ TRỊ PHẦN TỬ TỰ KHAI (`ub-own-*`)

   ⚠️ Vài phần tử có ô RIÊNG trùng việc với ô CHUNG: Khối/Cột/Banner/Bản đồ đều
   có "Bo góc" riêng bên cạnh "Bo góc khối" chung; Đường kẻ có "Khoảng cách" và
   "Căn" riêng bên cạnh "Khoảng cách trên/dưới" và "Đặt khối về" chung.

   Trước đây ô riêng in THẲNG vào `style=""` của thẻ. Mà style nội tuyến thắng
   MỌI quy tắc của tệp này — nhân đôi tên lớp bao nhiêu cũng vô ích — nên ô chung
   không bao giờ với tới được. Chính là lỗi "lưu được mà trang không đổi", chỉ
   khác gốc rễ với cụm `.ub-badge`/`.ub-dot`: bên kia là thứ tự tệp, bên này là
   nội tuyến.

   Nay ô riêng nhả BIẾN (`--ubo-*`) chứ không nhả thuộc tính, và mấy luật dưới
   đổ biến ra thuộc tính ở (0,1,0). Nhờ vậy:
   · chưa đặt ô chung  → ô riêng ăn, y hệt trước, không site nào đổi;
   · đã đặt ô chung    → `.ub-rd-*` (0,2,0) thắng, đúng điều người dùng vừa làm;
   · đặt ô chung ở MỘT khổ → khổ ấy theo ô chung, khổ còn lại vẫn giữ ô riêng.
     Nhánh thứ ba này là lý do phải đi đường biến thay vì chỉ "có ô chung thì
     thôi in ô riêng" — cách kia làm đặt mỗi nấc điện thoại là máy tính mất
     trắng bo góc.
   ========================================================================== */
.ub-own-bgc{background-color:var(--ubo-bgc)}
.ub-own-brad{border-radius:var(--ubo-brad)}
.ub-own-mt{margin-top:var(--ubo-mt)}
.ub-own-mb{margin-bottom:var(--ubo-mb)}
.ub-own-sa{margin-left:var(--ubo-ml);margin-right:var(--ubo-mr)}

/* ⚠️ TÊN LỚP NHÂN ĐÔI — cố ý, đừng "dọn cho gọn".
   Một lớp đơn là (0,1,0), y hệt CSS gốc của từng phần tử, nên luật nào đứng SAU
   trong tệp thì thắng. Mà `.ub-badge` (dòng ~3040) và `.ub-dot` (~3080) đứng sau
   khối này, nên người dùng đặt "Màu nền khối"/"Bo góc khối"/"Bề ngang tối đa"
   cho hai phần tử ấy thì biến `--ubr-*` có mặt đủ trong `style=""` mà KHÔNG AI
   ĐỌC: trình dựng báo lưu thành công, mở lại vẫn thấy giá trị mình đặt, trang
   thật không đổi một pixel. Không có dấu hiệu nào cho người dùng biết.
   `.ub-nl.ub-dark` còn nặng hơn — (0,2,0) thì thắng bất kể thứ tự.

   Nhân đôi lên (0,2,0) là thắng được cả hai cảnh trên. KHÔNG dùng `!important`:
   nó chặn luôn ô "CSS riêng" của người dùng, tức vá một lỗi bằng cách dựng lỗi
   khác. Và vẫn nhường `.ub-free` (cũng (0,2,0), đứng CUỐI tệp) — "Đặt tự do" thì
   phải thắng, đúng như trước.

   ⚠️ Giá trị phần tử tự khai đi đường `.ub-own-*` ngay dưới, cũng (0,1,0), nên
   khối này thắng nó — đó chính là điều ta muốn: ô chung đặt rồi thì ô chung nói. */
.ub-rd-pad.ub-rd-pad{padding:var(--ubr-pad)}
.ub-rd-mgn.ub-rd-mgn{margin:var(--ubr-mgn)}
.ub-rd-mt.ub-rd-mt{margin-top:var(--ubr-mt)}
.ub-rd-mb.ub-rd-mb{margin-bottom:var(--ubr-mb)}
.ub-rd-sa.ub-rd-sa{margin-left:var(--ubr-ml);margin-right:var(--ubr-mr)}
.ub-rd-bgc.ub-rd-bgc{background-color:var(--ubr-bgc)}
.ub-rd-brad.ub-rd-brad{border-radius:var(--ubr-brad)}
.ub-rd-bsh.ub-rd-bsh{box-shadow:var(--ubr-bsh)}
.ub-rd-maxw.ub-rd-maxw{max-width:var(--ubr-maxw)}

@media (max-width:1024px){
  .ub-rt-pad.ub-rt-pad{padding:var(--ubr-pad-md)}
  .ub-rt-mgn.ub-rt-mgn{margin:var(--ubr-mgn-md)}
  .ub-rt-mt.ub-rt-mt{margin-top:var(--ubr-mt-md)}
  .ub-rt-mb.ub-rt-mb{margin-bottom:var(--ubr-mb-md)}
  .ub-rt-sa.ub-rt-sa{margin-left:var(--ubr-ml-md);margin-right:var(--ubr-mr-md)}
  .ub-rt-bgc.ub-rt-bgc{background-color:var(--ubr-bgc-md)}
  .ub-rt-brad.ub-rt-brad{border-radius:var(--ubr-brad-md)}
  .ub-rt-bsh.ub-rt-bsh{box-shadow:var(--ubr-bsh-md)}
  .ub-rt-maxw.ub-rt-maxw{max-width:var(--ubr-maxw-md)}
}
@media (max-width:620px){
  .ub-rm-pad.ub-rm-pad{padding:var(--ubr-pad-sm)}
  .ub-rm-mgn.ub-rm-mgn{margin:var(--ubr-mgn-sm)}
  .ub-rm-mt.ub-rm-mt{margin-top:var(--ubr-mt-sm)}
  .ub-rm-mb.ub-rm-mb{margin-bottom:var(--ubr-mb-sm)}
  .ub-rm-sa.ub-rm-sa{margin-left:var(--ubr-ml-sm);margin-right:var(--ubr-mr-sm)}
  .ub-rm-bgc.ub-rm-bgc{background-color:var(--ubr-bgc-sm)}
  .ub-rm-brad.ub-rm-brad{border-radius:var(--ubr-brad-sm)}
  .ub-rm-bsh.ub-rm-bsh{box-shadow:var(--ubr-bsh-sm)}
  .ub-rm-maxw.ub-rm-maxw{max-width:var(--ubr-maxw-sm)}
}

/* Cỡ chữ Dãy liên kết theo từng khổ. Nấc nhỏ chưa đặt thì `var()` lồng nhau rơi
   về nấc trên — không sinh quy tắc thừa nào. */
@media (max-width:1024px){
  .ub-links-a{font-size:var(--ub-lnk-fs-md,var(--ub-lnk-fs,15px))}
  .ub-links--upper .ub-links-a{font-size:var(--ub-lnk-fs-md,var(--ub-lnk-fs,13px))}
}
@media (max-width:620px){
  .ub-links-a{font-size:var(--ub-lnk-fs-sm,var(--ub-lnk-fs-md,var(--ub-lnk-fs,15px)))}
  .ub-links--upper .ub-links-a{font-size:var(--ub-lnk-fs-sm,var(--ub-lnk-fs-md,var(--ub-lnk-fs,13px)))}
}

/* ---------------------------------------------------------------------------
   NHÃN NỔI · CHẤM LAN TOẢ · CHUYỂN ĐỘNG LẶP

   Bộ trang trí quanh ảnh hero: thẻ nhỏ "Giao diện đẹp / Hiện đại, tinh tế" thả
   nổi cạnh ảnh, chấm tròn có sóng lan trên vòng quỹ đạo, và chuyển động chạy
   liên tục cho cả hai.
--------------------------------------------------------------------------- */

/* ---- Nhãn nổi ---- */
.ub-badge{
  display:inline-flex;align-items:center;gap:12px;
  padding:12px 18px 12px 12px;
  border-radius:16px;
  background:#fff;color:var(--ink);
  box-shadow:0 14px 34px -12px rgba(8,28,69,.28),0 0 0 1px rgba(255,255,255,.6);
  text-decoration:none;
  /* Không cho co lại theo cột: nhãn nổi thường nằm cạnh ảnh, co là chữ gãy dòng. */
  max-width:100%
}
a.ub-badge:hover{color:var(--ink)}
.ub-badge-i{
  display:grid;place-items:center;flex:none;
  width:var(--ub-bdg-b,40px);height:var(--ub-bdg-b,40px);
  border-radius:12px;
  background:var(--ub-bdg-ib,color-mix(in srgb,var(--accent) 14%,#fff));
  color:var(--ub-bdg-ic,var(--accent));
  font-size:calc(var(--ub-bdg-b,40px) * .5)
}
.ub-badge-i .kico{width:1em;height:1em}
.ub-badge-b{display:flex;flex-direction:column;min-width:0}
.ub-badge-t{font-weight:700;font-size:15px;line-height:1.35}
.ub-badge-x{color:var(--muted);font-size:13px;line-height:1.4}
.ub-badge--small{padding:9px 14px 9px 9px;border-radius:13px;gap:9px;--ub-bdg-b:31px}
.ub-badge--small .ub-badge-t{font-size:13px}
.ub-badge--small .ub-badge-x{font-size:11.5px}
.ub-badge--large{padding:15px 22px 15px 15px;border-radius:19px;gap:14px;--ub-bdg-b:50px}
.ub-badge--large .ub-badge-t{font-size:17px}
.ub-badge--large .ub-badge-x{font-size:14px}
/* Nền tối: nhãn tự khai nền thì phải tự khai luôn màu chữ — không thì chữ sẫm
   nằm trên nền sẫm và cả nhãn biến mất. */
.ub-badge--dark{background:var(--ink,#161310);color:#fff;box-shadow:0 14px 34px -12px rgba(0,0,0,.5)}
.ub-badge--dark .ub-badge-x{color:rgba(255,255,255,.72)}
.ub-badge--dark .ub-badge-i{background:rgba(255,255,255,.14);color:#fff}
a.ub-badge--dark:hover{color:#fff}

/* ---- Chấm lan toả ---- */
.ub-dot{
  position:relative;display:inline-block;vertical-align:middle;
  width:var(--ub-dot-s,12px);height:var(--ub-dot-s,12px);
  /* ⚠️ Phải có CẢ `min-*`: bật "Đặt tự do" thì cụm `.ub-free` ở cuối tệp khai
     `width:var(--tb-w,auto)` và thắng (0,2,0 · đứng sau) — chấm rỗng ruột nên
     `auto` co về ĐÚNG 0px, biến mất mà không báo gì. `min-*` thì cụm ấy không
     đụng tới. */
  min-width:var(--ub-dot-s,12px);min-height:var(--ub-dot-s,12px);
  background:var(--ub-dot-c,var(--accent));
  flex:none
}
/* BUG-02 — bo góc của chấm nay là MẶC ĐỊNH CỦA PHẦN TỬ, do
   `hdnt_ub_render_dot()` nhả ra `--ubo-brad:999px` + lớp `.ub-own-brad`. Nhờ vậy
   ô chung "Bo góc khối" đè lên một MẶC ĐỊNH chứ không phải phá một luật cứng.

   ⚠️ Luật dưới đây là lối về cho markup do bản CŨ sinh ra — trang nằm trong bộ
   nhớ đệm (plugin cache) vẫn là HTML cũ, không có `.ub-own-brad`, trong khi CSS
   đã là bản mới; thiếu luật này thì chấm hoá VUÔNG cho tới lần xoá đệm kế tiếp,
   tức bản cập nhật đổi dáng site đang chạy.

   ⚠️ `:where()` chứ không `:not()` trần: `:where()` có độ ưu tiên BẰNG KHÔNG nên
   cả selector vẫn đúng (0,1,0) y hệt `.ub-dot` cũ. Dùng `:not()` trần là (0,2,0)
   — ngang `.ub-rd-brad.ub-rd-brad` mà lại đứng SAU trong tệp, tức trên trang còn
   đệm thì nó cướp mất giá trị người dùng đã đặt. Đúng lớp lỗi đang vá, chỉ đảo
   chiều.

   ⚠️ KHÔNG viết `border-radius:var(--ubo-brad,999px)` cho `.ub-dot`: biến CSS
   KẾ THỪA, nên chấm nằm trong một Khối có "Bo góc" riêng sẽ ăn luôn con số của
   Khối ấy. Lớp `.ub-own-brad` chỉ gắn khi CHÍNH phần tử tự khai nên không dính
   bẫy này. */
.ub-dot:where(:not(.ub-own-brad)){border-radius:999px}
/* Vành ngoài: điểm trên vòng quỹ đạo. `box-shadow` chứ không `border` — border
   cộng thêm vào kích thước, chấm 12px đặt cạnh nhau sẽ lệch nhau vài pixel. */
.ub-dot--ring{box-shadow:0 0 0 3px #fff,0 0 0 5px color-mix(in srgb,var(--ub-dot-c,var(--accent)) 45%,transparent)}
.ub-dot-w{
  /* Sóng ôm theo bo góc của chính chấm (`inherit`), không tự khai 999px: đặt
     "Bo góc khối" = 4px mà quầng sóng vẫn tròn vành vạnh thì ô ấy chỉ có tác
     dụng một nửa. Mặc định chấm vẫn 999px nên site đang chạy không đổi. */
  position:absolute;inset:0;border-radius:inherit;
  background:var(--ub-dot-c,var(--accent));
  animation:ub-dot-wave var(--ub-dot-t,2s) ease-out infinite;
  pointer-events:none
}
@keyframes ub-dot-wave{
  0%{transform:scale(1);opacity:.45}
  100%{transform:scale(2.9);opacity:0}
}

/* ---- Chuyển động lặp ----
   ⚠️ Chạy bằng `translate`/`rotate`/`scale` — thuộc tính RIÊNG, không phải
   `transform`. Hiệu ứng rê chuột (nhấc lên, phóng nhẹ) dùng `transform`, mà
   animation luôn thắng khai báo thường: gộp chung một thuộc tính là bật chuyển
   động lặp xong "Nhấc lên khi rê chuột" im lặng mất tác dụng. */
.ub-mo{animation-duration:var(--ub-mo-s,4s);animation-timing-function:ease-in-out;animation-iteration-count:infinite;will-change:translate,rotate,scale}
.ub-mo--float{animation-name:ub-mo-float}
.ub-mo--sway{animation-name:ub-mo-sway}
.ub-mo--drift{animation-name:ub-mo-drift}
.ub-mo--pulse{animation-name:ub-mo-pulse}
@keyframes ub-mo-float{50%{translate:0 -12px}}
@keyframes ub-mo-sway{50%{translate:14px 0}}
@keyframes ub-mo-drift{
  33%{translate:9px -10px;rotate:1.4deg}
  66%{translate:-7px -4px;rotate:-1.2deg}
}
@keyframes ub-mo-pulse{50%{scale:1.055}}

/* Người đã bật "giảm chuyển động" trong hệ điều hành: tắt hẳn cả hai. Chấm vẫn
   là một chấm, nhãn vẫn ở đúng chỗ — chỉ không nhúc nhích nữa. */
@media (prefers-reduced-motion:reduce){
  .ub-mo,.ub-dot-w,.ub-logos--run .ub-logos-t{animation:none}
  .ub-dot-w{opacity:0}
}

/* ---------------------------------------------------------------------------
   ĐẶT TỰ DO — để CUỐI tệp là có chủ ý.

   Khối đặt tự do phải thắng mọi quy tắc bố cục mà chính phần tử ấy khai. Ví dụ
   đã vấp: `.ub-col > .ub-imgbox{height:100%}` là (0,2,0), cao hơn `.ub-free`
   (0,1,0) — kéo mép TRÊN/DƯỚI của một Ô ảnh trong Cột thì không co giãn được
   gì, trong khi kéo mép trái/phải vẫn chạy (bề ngang không ai đè).

   Hai lớp trùng tên (`.ub-free.ub-free`) cho (0,2,0), cộng vị trí CUỐI tệp là
   thắng mọi quy tắc cùng hạng đứng trước — không cần `!important`.
--------------------------------------------------------------------------- */
.ub-banner-c--free > .ub-textbox,
.ub-free.ub-free{
  position:absolute;
  left:var(--tb-x,0%);top:var(--tb-y,0%);
  width:var(--tb-w,auto);height:var(--tb-h,auto);
  max-width:calc(100% - var(--tb-x,0%));
  pointer-events:auto;
  z-index:3;
  /* Lề của khối sẽ cộng thêm vào toạ độ — đặt tự do thì toạ độ phải là thứ duy
     nhất quyết định chỗ đứng, nếu không kéo một quãng lại lệch một quãng. */
  margin:0
}
@media (max-width:1024px){
  .ub-banner-c--free > .ub-textbox,
  .ub-free.ub-free{
    left:var(--tb-x-md,var(--tb-x,0%));top:var(--tb-y-md,var(--tb-y,0%));
    width:var(--tb-w-md,var(--tb-w,auto));height:var(--tb-h-md,var(--tb-h,auto));
    max-width:calc(100% - var(--tb-x-md,var(--tb-x,0%)))
  }
}
@media (max-width:620px){
  .ub-banner-c--free > .ub-textbox,
  .ub-free.ub-free{
    left:var(--tb-x-sm,var(--tb-x-md,var(--tb-x,0%)));top:var(--tb-y-sm,var(--tb-y-md,var(--tb-y,0%)));
    width:var(--tb-w-sm,var(--tb-w-md,var(--tb-w,auto)));height:var(--tb-h-sm,var(--tb-h-md,var(--tb-h,auto)));
    max-width:calc(100% - var(--tb-x-sm,var(--tb-x-md,var(--tb-x,0%))))
  }
}

/* ---------------------------------------------------------------------------
   GIỮ CHỖ CŨ — nửa còn thiếu của "Đặt tự do"

   `position:absolute` nhấc khối khỏi dòng chảy, nên mọi khối đứng SAU nó dồn
   lên lấp chỗ trống. Chủ site báo: *"kéo thả các phần tử khác không được phép
   nhảy lên chồng chéo nhau — phần tử nào di chuyển riêng của phần tử đó"*.

   `position:relative` giữ khối TRONG dòng chảy: chỗ cũ vẫn được giữ, hàng xóm
   đứng yên, mà khối vẫn đi tới đâu tuỳ ý.

   ⚠️ Đứng SAU cụm `.ub-free.ub-free` ở trên và cũng dùng hai lớp trùng tên để
   được (0,2,0) — cùng hạng thì quy tắc đứng sau thắng, không cần `!important`.

   ⚠️ `max-width` phải trả về `100%`. Cụm ở trên tính `calc(100% - var(--tb-x))`
   cho đúng nghĩa "cách mép trái bao nhiêu"; ở đây `--tb-x` là ĐỘ DỜI nên công
   thức ấy bóp khối lại một cách vô cớ.

   ⚠️ Không đặt `margin:0` như cụm trên: khối vẫn ở trong dòng chảy nên lề của
   nó là thứ giữ khoảng cách với hàng xóm — xoá đi là dính sát nhau.
--------------------------------------------------------------------------- */
.ub-free--keep.ub-free--keep{
  position:relative;
  max-width:100%;
  margin:revert
}
@media (max-width:1024px){
  .ub-free--keep.ub-free--keep{max-width:100%}
}
@media (max-width:620px){
  .ub-free--keep.ub-free--keep{max-width:100%}
}

/* ---------------------------------------------------------------------------
   HERO KIỂU NỘI THẤT GỖ — bật bằng lớp `ub-hero-n01` ở ô "Lớp CSS thêm" của dải.

   Ba chi tiết của bản gốc mà trình dựng KHÔNG có ô nào diễn tả được. Đo trên
   trang thật (1440px) rồi chép lại đúng cơ chế, không phải áng chừng:

   ① Ảnh mờ dần sang trái 96px để hoà vào nền kem. Bản gốc dùng MỘT thẻ
      `bg-gradient-to-r from-cream to-transparent` rộng 96px đè lên mép trái ảnh
      — KHÔNG phải mask, không filter, và KHÔNG có gì ở mép phải (đã soi cả
      `mask-image`, `filter`, `::before`, `::after` của cả ảnh lẫn khung: đều
      `none`). Đừng thêm lớp mờ bên phải cho "cân" — bản gốc không có.
   ② Nút CTA: 13px/700, giãn chữ .12em, bo 4px, bóng đổ BA lớp. `.ub-btn` tự
      khai `font-weight:600;font-size:15px` nên ô "CSS riêng" của khối (đặt lên
      thẻ BỌC) không với tới — phải nhắm thẳng `.ub-btn`.
   ③ Chỉ số 01/02/03 nằm ở CỘT CHỮ dưới nút, gạch chân — không phải viên số nổi
      trên ảnh. Chấm của Slider vì thế được nhấc ra khỏi khung ảnh và neo vào
      `.ub-section-in` (đã `position:relative`), nên ba khối trung gian phải trả
      về `static`. Số "0" đứng trước do `::before` — JS của Slider chỉ ghi 1/2/3.

   Bề ngang hai cột do ô `span` của Cột quyết định như mọi hàng khác — KHÔNG
   ghim bằng CSS, để chủ site còn kéo mép cột được trên layout.
   ------------------------------------------------------------------------ */
/* @ub-helper ub-hero-n01 | Hero kiểu nội thất gỗ | Dải mờ mép trái tấm ảnh, nút CTA đậm, chỉ số 01/02/03 nhấc xuống dưới cột chữ. Dán vào chính DẢI chứa hero. | section */
.ub-hero-n01 .ub-btn{
  font-size:13px;font-weight:700;letter-spacing:.12em;
  padding:0 28px;min-height:48px;border-radius:4px;
  box-shadow:0 2px 4px rgba(43,33,24,.20),0 10px 22px -6px rgba(43,33,24,.30),0 26px 52px -14px rgba(43,33,24,.34)
}

/* ⚠️ NGƯỠNG LÀ 1025, KHÔNG PHẢI 1024 — phải khớp mép với nấc "máy tính bảng"
   của trình dựng, vốn là `max-width:1024px`. Khai 1024 thì ở ĐÚNG khổ 1024px cả
   hai khối cùng chạy: nấc bảng cho cột span 12 (hai cột xếp chồng) trong khi
   khối này ép `.ub-col{position:static}` — cột chữ hết `absolute`, nhảy lên
   thành một khối THẬT nằm TRÊN tấm ảnh, mang màu chữ TRẮNG của nấc bảng đặt
   trên nền kem #F0E9DD. Đo được: hero cao 982 thay vì 540, chữ trắng trên kem
   coi như vô hình. Hở đúng một pixel bề ngang, nhưng iPad Pro dọc là 1024.
   Cùng ngưỡng với ô "Ẩn chỉ mục trên · Máy tính" ở trên. */
@media (min-width:1025px){
  /* ① dải mờ 96px BÁM THEO mép trái của chính tấm ảnh.
        ⚠️ Đừng ghim `grid-template-columns` cho hàng rồi đặt dải mờ ở một mốc
        phần trăm cố định. Làm vậy là `grid-column:span var(--ub-span)` của Cột
        hết tác dụng — kéo mép cột trên layout vẫn ghi ra `span` mới nhưng KHÔNG
        có gì nhúc nhích, người dựng tưởng ô chữ và cái nút đang chặn. Bám vào
        chính `.ub-image` thì dải mờ tự đi theo mọi bề ngang cột. */
  .ub-hero-n01 .ub-image{position:relative}
  .ub-hero-n01 .ub-image::after{
    content:"";position:absolute;inset:0 auto 0 0;width:96px;
    z-index:2;pointer-events:none;
    background:linear-gradient(to right,var(--hero-fade,#F0E9DD),rgba(240,233,221,0))
  }

  /* ③ nhấc chỉ số ra khỏi ảnh, đặt xuống cột chữ */
  .ub-hero-n01 .ub-row,
  .ub-hero-n01 .ub-col,
  .ub-hero-n01 .ub-slider{position:static}
  .ub-hero-n01 .ub-slider-d{
    position:absolute;z-index:3;margin:0;
    left:max(24px,calc((100vw - 1320px) / 2 + 24px));bottom:79px;
    justify-content:flex-start;align-items:flex-end;gap:20px
  }
  .ub-hero-n01 .ub-slider-d button{
    display:block;width:44px;height:30px;padding:0 0 10px;
    background:none;box-shadow:none;border:0;border-radius:0;position:relative;
    color:rgba(43,33,24,.35);font-size:13px;font-weight:600;letter-spacing:.08em;text-align:left
  }
  .ub-hero-n01 .ub-slider-d button::before{content:"0"}
  .ub-hero-n01 .ub-slider-d button::after{
    content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:rgba(43,33,24,.18)
  }
  .ub-hero-n01 .ub-slider-d button.is-on{color:#2B2118;background:none;box-shadow:none}
  .ub-hero-n01 .ub-slider-d button.is-on::after{background:var(--accent,#84613C)}
}

/* ⚠️ DƯỚI 1024px — chỗ này TỪNG có 11 dòng CSS riêng cho hero: lớp phủ ảnh,
   đảo chữ sang sáng, căn giữa cột chữ, giấu số thứ tự, nới khe dòng nhỏ. Cần,
   vì cột chữ khai `free="1"` nên ở khổ hẹp nó nằm ĐÈ lên tấm ảnh — chữ sẫm
   trên ảnh phòng khách sáng, đo được 1,3:1.

   Nay KHÔNG còn dòng nào: cả 11 dòng đã thành Ô trong bảng thuộc tính, đặt
   riêng cho từng khổ ngay trong trình dựng (chủ site chốt 2026-08-12 —
   *"thuộc tính nào chưa có thì thêm luôn vào nâng cấp UX Builder"*):
     · lớp phủ ảnh   → Ảnh ▸ Độ đậm lớp phủ · Màu lớp phủ · Kiểu phủ
     · chữ sáng      → Tiêu đề / Đoạn chữ ▸ Màu chữ (nay theo khổ)
     · giãn dòng     → Tiêu đề ▸ Giãn dòng (nay theo khổ)
     · dòng nhỏ      → Tiêu đề ▸ Màu dòng nhỏ · Khe xuống tiêu đề
     · căn giữa dọc  → Cột ▸ Dồn nội dung theo chiều dọc
     · giấu chỉ số   → Slider ▸ Ẩn chỉ mục trên

   ⚠️ Đừng thêm quy tắc `.ub-hero-n01` cho khổ nhỏ vào đây nữa. Thêm là chủ
   site mất đường sửa: ô trong bảng thuộc tính không đè được CSS trong tệp khi
   cả hai cùng nhắm một thuộc tính, và họ không sửa được tệp này từ wp-admin.
   Thiếu ô thì THÊM Ô, đúng như sáu dòng trên. */

/* ---------------------------------------------------------------------------
   THẺ TRẮNG KIỂU NỘI THẤT GỖ — lớp `ub-n01-card` / `ub-n01-card--sm`.

   Vành 1px và bo góc do ô "Viền" + "Bo góc khối" của chính khối lo; đây chỉ
   thêm ĐỔ BÓNG, vì thang bóng dựng sẵn của trình dựng (`hdnt_ub_depth_shadow`)
   là MỘT lớp alpha 26–50%, đậm hơn hẳn token của bản gốc — ba lớp 4/6/8%.
   Hai mức dưới đây chép đúng `shadow-soft` và `shadow-softer` trong
   `tailwind.config.js` của noithat01.

   ⚠️ Bo góc phải đặt bằng ô CHUNG "Bo góc khối" (`brad`), KHÔNG dùng ô `radius`
   riêng của Cột: ô riêng chỉ in `border-radius` trần, còn ô chung mới kèm
   `overflow:hidden` — thiếu nó thì ảnh trong thẻ vẫn vuông góc, chỉ cái vỏ bo.
   ------------------------------------------------------------------------ */
/* @ub-helper ub-n01-card | Thẻ trắng nội thất gỗ | Đổ bóng mức ĐẬM theo token của bản gốc. Dùng cho khối Xưởng sản xuất và thẻ Công trình. Vành và bo góc vẫn đặt ở ô "Viền" + "Bo góc khối". */
.ub-n01-card{box-shadow:0 1px 2px rgba(36,27,17,.04),0 4px 10px -3px rgba(36,27,17,.06),0 12px 26px -8px rgba(36,27,17,.08)}
/* @ub-helper ub-n01-card--sm | Thẻ trắng nội thất gỗ · nhẹ | Đổ bóng mức NHẸ, cho thẻ Danh mục, Sản phẩm và Tin tức — những thẻ xếp thành lưới nhiều ô. */
.ub-n01-card--sm{box-shadow:0 1px 2px rgba(36,27,17,.04),0 2px 8px -2px rgba(36,27,17,.05)}
