/* ---------------------------------------------------------------------------
   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%)}
/* 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}
/* ⚠️ 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}
/* 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)}

/* ---------- 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}
.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)}
.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. */
.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)}
.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}
.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}
.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}

/* ---------- 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:860px){
  .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)}
@media (max-width:1024px){
  .ub-image--rw{width:var(--iw-md,var(--iw))}
  .ub-image-media--h{height:var(--ih-md,var(--ih))}
  .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-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`. */
.ub-products--cart-text .hdnt-pc{position:relative}
.ub-products--cart-text .hdnt-pc-media{position:static}
.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)}

/* ---------- 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}

/* ---------- 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:1100px){
  .ub-logos:not(.ub-logos--run){grid-template-columns:repeat(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:860px){
  /* ⚠️ Ngưỡng 860 này 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 trước đây 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). Nay `--ub-span-md` được
     ưu tiên nếu có; không có thì rơi về `--ub-span-sm` rồi mới tới 12, nên
     nội dung đã lưu chạy y như cũ. Nấc điện thoại thật nằm ở khối 620px dưới. */
  .ub-col{grid-column:span var(--ub-span-md,var(--ub-span-sm,12))}
  /* Ô ghép đổi bề ngang ở ĐÚNG ngưỡng của cột — hai lưới cùng một nhịp thì
     trang không có dải bề ngang nào mà bên này đã xuống hàng, bên kia chưa.
     Chiều cao hàng cũng phải hạ: ô rộng cả màn mà vẫn cao 190px là một dải
     băng ngang, ảnh nền trong đó gần như không thấy gì. */
  .ub-cell{grid-column:span var(--ub-span-md,var(--ub-span-sm,12))}
  .ub-mosaic{grid-auto-rows:var(--ub-mo-rh-sm,150px)}
  /* Đầ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}
  .ub-stat-n{font-size:34px}
  .ub-slider-t{grid-auto-columns:calc((100% - 16px) / min(var(--ub-per,1),2))}
}

@media (max-width:620px){
  /* 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-column:span var(--ub-span-sm,12)}
  /* 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-stat-n{font-size:28px}
  .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(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)}

/* ---------------------------------------------------------------------------
   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:861px){
  .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:1100px){ .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)}
@media (max-width:860px){
  /* Khổ hẹp: khách vuốt tay là chính, nút to đè lên ảnh chỉ vướng. */
  .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}
.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}

/* ---------- 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`). */
.ub-col > .ub-imgbox,.ub-col > .ub-ibox{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)}
.ub-heading[style*="--ub-h-ss"] .ub-heading-sub{font-size:var(--ub-h-ss)}

/* 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)}

/* ---------------------------------------------------------------------------
   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}
.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}
.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}
/* 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ữ". */
.ub-dark .ub-form-i,.ub-section--dark .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)}
.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ó test quét: khối nào khai `background:var(--paper` mà không nằm
   trong danh sách này là đỏ — nhờ vậy khối thêm sau không lặng lẽ vô hình.
--------------------------------------------------------------------------- */
.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-plan{
  color:#161310;
  --ink:#161310;
  --muted:#6B7280;
  --line:#EAECEF;
  --line-strong:#D8DCE2;
}

.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:1100px){
  .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}

/* ==========================================================================
   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…).
   ========================================================================== */
.ub-rd-pad{padding:var(--ubr-pad)}
.ub-rd-mgn{margin:var(--ubr-mgn)}
.ub-rd-mt{margin-top:var(--ubr-mt)}
.ub-rd-mb{margin-bottom:var(--ubr-mb)}
.ub-rd-sa{margin-left:var(--ubr-ml);margin-right:var(--ubr-mr)}
.ub-rd-bgc{background-color:var(--ubr-bgc)}
.ub-rd-brad{border-radius:var(--ubr-brad)}
.ub-rd-bsh{box-shadow:var(--ubr-bsh)}
.ub-rd-maxw{max-width:var(--ubr-maxw)}

@media (max-width:1024px){
  .ub-rt-pad{padding:var(--ubr-pad-md)}
  .ub-rt-mgn{margin:var(--ubr-mgn-md)}
  .ub-rt-mt{margin-top:var(--ubr-mt-md)}
  .ub-rt-mb{margin-bottom:var(--ubr-mb-md)}
  .ub-rt-sa{margin-left:var(--ubr-ml-md);margin-right:var(--ubr-mr-md)}
  .ub-rt-bgc{background-color:var(--ubr-bgc-md)}
  .ub-rt-brad{border-radius:var(--ubr-brad-md)}
  .ub-rt-bsh{box-shadow:var(--ubr-bsh-md)}
  .ub-rt-maxw{max-width:var(--ubr-maxw-md)}
}
@media (max-width:620px){
  .ub-rm-pad{padding:var(--ubr-pad-sm)}
  .ub-rm-mgn{margin:var(--ubr-mgn-sm)}
  .ub-rm-mt{margin-top:var(--ubr-mt-sm)}
  .ub-rm-mb{margin-bottom:var(--ubr-mb-sm)}
  .ub-rm-sa{margin-left:var(--ubr-ml-sm);margin-right:var(--ubr-mr-sm)}
  .ub-rm-bgc{background-color:var(--ubr-bgc-sm)}
  .ub-rm-brad{border-radius:var(--ubr-brad-sm)}
  .ub-rm-bsh{box-shadow:var(--ubr-bsh-sm)}
  .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);
  border-radius:999px;background:var(--ub-dot-c,var(--accent));
  flex:none
}
/* 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{
  position:absolute;inset:0;border-radius:999px;
  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%))))
  }
}
