/* ============================================================
   Luxshine — Hair Extensions hub (hair-extensions.html), tier 1.
   Product groups with their lines and a WhatsApp button per line.
   Headings, table, steps, FAQ and form reuse the site's blocks
   (sections.css / info.css); this file adds the group grid.
   Tokens only (tokens.css). Motion: --ease-soft (shop.css),
   transform + opacity only.
   ============================================================ */

.hub { padding-bottom: 0; }
.hub-sec { padding-block: var(--space-24); }
.hub-sec .sec-head { margin-bottom: var(--space-12); }
/* The first section follows the breadcrumb: the trail keeps 16px above and below, and the title starts 32px under it. */
.hub__sheet > .breadcrumb { padding-block: var(--space-4); }
.hub-sec.hub-cats { padding-top: var(--space-8); }
/* A title with no lead under it sits closer to what it heads: title, tabs and table are evenly spaced. */
.hub-sec.hub-fit .sec-head { margin-bottom: var(--space-6); }
.hub-sec__lead { max-width: 60ch; font-size: var(--body-size); line-height: var(--body-lh); color: var(--color-font); text-wrap: balance; }

/* ---------- Banner: the photo is the whole block, and the header sits on it ---------- */
/* shop.css draws .shop-banner as a beige field with the photo fading in on its right half.
   On a page marked [data-header-over] (the Hair Extensions hub, the product-group page and the two
   shop-by pages) the photo fills the block edge to edge; the copy sits on the photo's bright wall. */
[data-header-over] .shop-banner { background: var(--primary-50); }
[data-header-over] .shop-banner__media { inset: 0; width: auto; }
[data-header-over] .shop-banner__media::before { content: none; }
/* Anchored right: when the block is taller than the photo, the crop comes off the empty wall. */
[data-header-over] .shop-banner__media img { object-position: right top; }
[data-header-over] .shop-banner__content { max-width: min(568px, 40vw); }
[data-header-over] .shop-banner__title { text-wrap: balance; }
/* A product group's banner (category.html): its product photo fills the whole block too (user 2026-10-14:
   "ảnh ở đây là ảnh fullscreen chứ ko phải ảnh cắt 1 bên"), with nothing laid over it — no wash under the copy,
   no fade under the header (user 2026-10-14: "hình banner ở đây bị dính lớp phủ màu vàng nè. bỏ hết lớp phủ đi"). */
[data-header-over] .shop-banner[data-t2-banner] .shop-banner__media { inset: 0; width: auto; }
[data-header-over] .shop-banner[data-t2-banner] .shop-banner__media::before { content: none; }
[data-header-over] .shop-banner[data-t2-banner] .shop-banner__media img { object-position: center; }

/* A page marked [data-header-over] starts with a see-through header over its banner.
   js/main.js measures the header (--header-over-h) and adds .is-solid once the page is scrolled. */
/* The one-row header is 72px on desktop (css/sections.css); these hold until main.js has measured it. */
[data-header-over] { --header-over-h: 72px; --header-h: 72px; }
/* The stretch of page top that counts as "not scrolled yet"; main.js watches it. */
.header-over-mark { position: absolute; top: 0; left: 0; width: 1px; height: var(--space-12); pointer-events: none; }
@media (min-width: 1025px) {
  [data-header-over] .site-header { background: transparent; box-shadow: none; transition: var(--header-slide), background-color .25s ease, box-shadow .25s ease; }
  [data-header-over] :is(.navbar, .menubar) { background: transparent; }
  /* Solid again when scrolled, under the pointer, or holding focus, so menus always open on white. */
  [data-header-over] .site-header:is(.is-solid, :hover, :focus-within) { background: var(--color-white); box-shadow: var(--shadow-md); }
  [data-header-over] .shop-banner { margin-top: calc(-1 * var(--header-over-h)); }
  /* The block grows with the screen (30vw of photo, 488px to 576px), so a wide screen shows the hair down to the shoulder, not a face crop. */
  [data-header-over] .shop-banner__inner { min-height: calc(var(--space-16) + clamp(488px, 30vw, 576px)); padding-top: calc(var(--header-over-h) + var(--space-16)); }
  /* The photo starts under the logo row and fades in, so the menu row lies on its wall and the model's head stays below the header.
     Past 1920px it stops growing and keeps to the right. */
  [data-header-over] .shop-banner__media { top: var(--space-16); left: auto; width: min(100%, 1920px); }
  [data-header-over] .shop-banner__media::before { content: ""; background: linear-gradient(to bottom, var(--primary-50), transparent var(--space-12)); }
  /* The product photo runs under the see-through header too, edge to edge, with no overlay. */
  [data-header-over] .shop-banner[data-t2-banner] .shop-banner__media { top: 0; left: 0; width: auto; }
  [data-header-over] .shop-banner[data-t2-banner] .shop-banner__media::before { content: none; }
  /* A product group whose photo is dark behind the copy (data-tone="dark", js/category.js): the copy and the
     see-through header turn white, the logo takes its white-lettered version; nothing is laid over the photo
     (user 2026-10-14, chosen from four ways to keep the copy legible without an overlay). */
  [data-header-over] .shop-banner[data-tone="dark"] :is(.shop-banner__title, .shop-banner__text, .shop-banner__text p) { color: var(--color-white); }
  [data-header-over]:has(.shop-banner[data-tone="dark"]:not([hidden])) .site-header:not(.is-solid, :hover, :focus-within) :is(.menutab, .lang__current) { color: var(--color-white); }
  [data-header-over]:has(.shop-banner[data-tone="dark"]:not([hidden])) .site-header:not(.is-solid, :hover, :focus-within) :is(.lang__globe, .lang__chev, .icon-btn img) { filter: brightness(0) invert(1); }
  [data-header-over]:has(.shop-banner[data-tone="dark"]:not([hidden])) .site-header:not(.is-solid, :hover, :focus-within) .navbar__logo img { content: url("../assets/images/footer-logo.png"); }
  /* A page whose banner is switched off (category.html listing every product) keeps its solid header. */
  [data-header-over]:has(.shop-banner[hidden]) .site-header { background: var(--color-white); box-shadow: var(--shadow-md); }
  [data-header-over]:has(.shop-banner[hidden]) :is(.navbar, .menubar) { background: var(--color-white); }
}
/* Narrow desktop: the copy column reaches the hair, so the photo fades out under it. */
@media (min-width: 1025px) and (max-width: 1279px) {
  [data-header-over] .shop-banner__media::before { background: linear-gradient(to bottom, var(--primary-50), transparent var(--space-12)), linear-gradient(to right, var(--primary-50) 34%, transparent 54%); }
}
@media (min-width: 1921px) {
  [data-header-over] .shop-banner__media::before { background: linear-gradient(to bottom, var(--primary-50), transparent var(--space-12)), linear-gradient(to right, var(--primary-50), transparent calc(var(--space-16) * 2)); }
}
/* The page slides over the banner: on a desktop screen tall enough to show the whole banner, the banner
   holds its place and everything after it (.hub__sheet) scrolls up over it. Not with reduced motion. */
.hub__sheet { position: relative; z-index: 1; background: var(--color-white); }
@media (min-width: 1025px) and (min-height: 680px) and (prefers-reduced-motion: no-preference) {
  [data-header-over] .shop-banner { position: sticky; top: calc(var(--header-h) - var(--header-over-h)); z-index: 0; }
}
/* Marks where the sheet meets the header's lower edge; main.js watches it too. */
.header-over-mark--sheet { top: calc(-1 * var(--header-h)); height: 1px; }
/* One entrance, once: the photo settles, then the copy rises. */
@media (prefers-reduced-motion: no-preference) {
  [data-header-over] .shop-banner__media img { animation: hub-banner-photo 1.4s var(--ease-soft) both; }
  [data-header-over] .shop-banner__title { animation: hub-banner-rise .7s var(--ease-soft) .1s both; }
  [data-header-over] .shop-banner__text { animation: hub-banner-rise .7s var(--ease-soft) .22s both; }
}
@keyframes hub-banner-photo { from { transform: scale(1.04); } }
@keyframes hub-banner-rise { from { opacity: 0; transform: translateY(16px); } }

/* ---------- Product groups: the two best sellers large, the other four compact ---------- */
.hub-cats__grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-6); }
/* The base card is a row: square photo on the left; name, copy, lines and buttons on the right.
   The four smaller groups are rows on a tablet and a phone; the variants below reshape the card.
   The photo sits inside the row's padding, so a body taller than it leaves no stray band. */
/* The photo runs to the card's top, left and bottom edges; the padding belongs to the copy.
   It is square while the copy is shorter than it, and grows with the card (cropped) when the copy is taller. */
.hub-cat { display: grid; grid-template-columns: 368px minmax(0, 1fr); align-items: start; min-width: 0; background: var(--surface-beige); }
.hub-cat__media { align-self: stretch; display: block; max-width: 100%; overflow: hidden; aspect-ratio: var(--ty-le-san_pham); }
.hub-cat__media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .7s var(--ease-soft); }
@media (hover: hover) { .hub-cat__media:hover img { transform: scale(1.04); } }
.hub-cat__body { align-self: stretch; display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; padding: var(--space-6) var(--space-6) var(--space-6) var(--space-8); }
/* Head row: the group's name (and tag) on the left, its two actions on the right. */
.hub-cat__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3) var(--space-6); }
.hub-cat__title { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.hub-cat__name { font-family: var(--font-heading); font-weight: 400; font-size: var(--h4-size); line-height: var(--h4-lh); text-transform: uppercase; color: var(--color-black); }
.hub-cat__name a { color: inherit; }
.hub-cat__tag { flex: none; padding: var(--space-1) var(--space-2); background: var(--color-primary); color: var(--color-black); font-size: var(--caption-size); line-height: var(--caption-lh); font-weight: 600; }
.hub-cat__desc { font-size: var(--body-size); line-height: var(--body-lh); color: var(--color-font); text-wrap: pretty; }

/* A product line: its name, and a WhatsApp button (.btn--outline) with the line pre-filled.
   As many columns as fit, so a name and its button stay together. */
.hub-cat__lines { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); gap: 0 var(--space-8); }
/* The rule under a line ties its name to its WhatsApp link, which sits at the far end of the row. */
.hub-line { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: 44px; border-bottom: 1px solid var(--primary-400); }
.hub-line__name { font-size: var(--body-size); line-height: var(--body-lh); font-weight: 600; color: var(--color-black); }
.hub-line__name:hover, .hub-line__name:focus-visible { text-decoration: underline; text-underline-offset: 4px; }
/* Every link in a card ("See more", and each line's WhatsApp link) is the DS label link (.label-link).
   Under the pointer its text and icon turn gold and nothing moves: the gap stays as it is. */
.hub-cat .label-link { flex: none; min-height: 44px; }
.hub-cat .label-link .wa-ico { width: 16px; height: 16px; }
.hub-cat .label-link:hover, .hub-cat .label-link:focus-visible { gap: var(--space-2); color: var(--color-primary); }
.hub-cat a:focus-visible { outline-color: var(--color-black); }
.wa-ico { flex: none; width: 20px; height: 20px; background: currentColor; -webkit-mask: url("../assets/icons/whatsapp.svg") no-repeat center / contain; mask: url("../assets/icons/whatsapp.svg") no-repeat center / contain; transition: transform .32s var(--ease-soft); }
@media (hover: hover) {
  .btn:hover .wa-ico { transform: translate(2px, -2px) scale(1.08); }
}

/* The group's two buttons share one width: that of the longer label. */
/* The primary button (ask on WhatsApp), a short upright rule, then the link to the group's page. */
.hub-cat__actions { display: flex; flex: none; flex-wrap: wrap; align-items: center; gap: 0 var(--space-12); }
.hub-cat__actions .btn { min-height: 44px; padding: var(--space-2) var(--space-6); }
.hub-cat__actions .btn + .label-link { position: relative; }
.hub-cat__actions .btn + .label-link::before { content: ""; position: absolute; top: 50%; left: calc(var(--space-6) * -1); width: 1px; height: 24px; margin-top: calc(var(--space-3) * -1); background: var(--color-black); }
/* 8px block padding: the 20px icon then fits inside the 44px minimum, so both buttons match in height too. */
.hub .btn:active { transform: scale(.98); }

/* The two best sellers (Weft Hair, Tape-In) get a large card each (.hub-cat--lead): a square photo across
   the top, then name, copy and lines, with the two actions at the card's foot, so the pair lines up
   although one group has eight lines and the other three. Side by side from 641px, stacked on a phone. */
.hub-cat.hub-cat--lead { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; gap: 0; }
.hub-cat--lead .hub-cat__media { grid-row: auto; }
.hub-cat--lead .hub-cat__body { display: flex; padding: var(--space-6); }
.hub-cat--lead .hub-cat__head { display: contents; }
.hub-cat--lead .hub-cat__title { align-self: auto; flex-flow: row wrap; align-items: center; gap: var(--space-2) var(--space-3); padding: 0; }
.hub-cat--lead .hub-cat__name { font-size: var(--h4-size); line-height: var(--h4-lh); }
.hub-cat--lead .hub-cat__desc, .hub-cat--lead .hub-cat__lines { padding: 0; }
.hub-cat--lead .hub-cat__actions { order: 1; margin-top: auto; padding: var(--space-4) 0 0; }
@media (min-width: 641px) {
  .hub-cats__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .hub-cat { grid-column: 1 / -1; }
  .hub-cat.hub-cat--lead { grid-column: span 2; }
}
/* Half a tablet's width, or a phone: the button, then "See more" under it, and no rule between them. */
@media (max-width: 1024px) {
  .hub-cat--lead .hub-cat__actions { flex-direction: column; align-items: flex-start; }
  .hub-cat--lead .hub-cat__actions .btn { flex: none; }
  .hub-cat--lead .hub-cat__actions .btn + .label-link::before { content: none; }
}
@media (max-width: 640px) {
  .hub-cat--lead .hub-cat__body { padding: var(--space-4); }
  .hub-cat--lead .hub-cat__actions { align-items: stretch; }
}
/* Desktop: the four other groups (Keratin Tip, Clip-In, Halo & Ponytail, Bulk Hair) share one row of
   compact cards (.hub-cat--compact): a square photo on top, then a smaller name, the actions stacked, copy and lines. */
@media (min-width: 1025px) {
  .hub-cat--lead .hub-cat__name { font-size: var(--h3-size); line-height: var(--h3-lh); }
  .hub-cat--lead .hub-cat__lines { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--space-6); }
  .hub-cat.hub-cat--compact { --hub-pad: var(--space-4); grid-column: span 1; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; }
  .hub-cat--compact .hub-cat__body { padding: var(--hub-pad); }
  .hub-cat--compact .hub-cat__head { flex-direction: column; align-items: flex-start; }
  .hub-cat--compact .hub-cat__name { font-size: var(--h5-size); line-height: var(--h5-lh); }
  /* A narrow card: the button, then "See more" under it, and no rule between them. */
  .hub-cat--compact .hub-cat__actions { flex-direction: column; align-items: flex-start; }
  .hub-cat--compact .hub-cat__actions .btn { padding-inline: var(--space-4); }
  .hub-cat--compact .hub-cat__actions .btn + .label-link::before { content: none; }
  .hub-cat--compact .hub-cat__lines { grid-template-columns: minmax(0, 1fr); }
  /* The four cards share their rows (subgrid), so names, actions, copy and lines start level across the row.
     The body and head stop being boxes for that, and each part carries the card's padding itself. */
  @supports (grid-template-rows: subgrid) {
    .hub-cats__grid { row-gap: 0; }
    .hub-cat.hub-cat--lead { margin-bottom: var(--space-6); }
    .hub-cat.hub-cat--compact { grid-row: span 5; grid-template-rows: subgrid; }
    .hub-cat--compact .hub-cat__body, .hub-cat--compact .hub-cat__head { display: contents; }
    .hub-cat--compact .hub-cat__title { padding: var(--hub-pad) var(--hub-pad) 0; }
    .hub-cat--compact .hub-cat__actions { padding: var(--space-3) var(--hub-pad) 0; }
    .hub-cat--compact .hub-cat__desc { padding: var(--space-4) var(--hub-pad) 0; }
    .hub-cat--compact .hub-cat__desc:last-child { padding-bottom: var(--hub-pad); }
    .hub-cat--compact .hub-cat__lines { padding: var(--space-4) var(--hub-pad) var(--hub-pad); }
  }
}
/* Below 1280px a line's WhatsApp link keeps its icon and drops its word (the link is named by aria-label),
   so a long line name stays on one row; the icon keeps a 44px target. */
@media (min-width: 1025px) and (max-width: 1279px) {
  .hub-cat--lead .hub-wa__label, .hub-cat--compact .hub-wa__label { display: none; }
  .hub-cat--lead .hub-wa, .hub-cat--compact .hub-wa { min-width: 44px; justify-content: flex-end; }
}
@media (min-width: 1440px) {
  .hub-cat--lead .hub-cat__body { padding: var(--space-8); }
  .hub-cat.hub-cat--compact { --hub-pad: var(--space-6); }
}

/* ---------- Product groups in a grid of wide cards (user 2026-10-15: no section scrolls sideways; before, one
   row that scrolled sideways, chosen 2026-10-14 in comment cmv7ma6hv3b80 because "1 view màn hình ko thể nhìn hết
   được 1 - 2 danh mục"). Two cards to a row on a desktop, so each row of two still fits one screen; one to a row
   below 1025px. The same .hub-cat card, all six alike (layout below). ---------- */
.hub-cats__grid--wide { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6); }
/* The card (2026-10-14 night, comment cmv7uyxufks61 "ảnh đang bị nhỏ quá. bố cục layout trong từng card chưa đẹp",
   after the evening's small-photo card, cmv7sce2cycqq / cmv7sdxr0cx4f "ko nằm trong 1 view màn hình"): two cards
   to a row on a desktop, so each card is wide enough for a large square photo with the name, tag and
   copy beside it; the lines in two columns and the two actions run the full width below. The card's two columns are
   the lines' two columns (same 24px gap), so the photo is exactly as wide as the first column of lines under it
   (user 2026-10-14: "cho ảnh scale ra to bằng width của cụm content ở dưới"). A row
   of cards stays one screen tall. The card is one grid; the body and head wrappers step aside (display: contents) so
   their parts take its cells. */
/* grid-column: auto undoes the full-row span of the six-rows layout (.hub-cat, desktop), so two cards share a row. */
.hub-cats__grid--wide > .hub-cat { grid-column: auto; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: auto minmax(0, 1fr) auto; gap: var(--space-6); align-items: start; margin: 0; padding: var(--space-6); }
.hub-cats__grid--wide .hub-cat__body, .hub-cats__grid--wide .hub-cat__head { display: contents; }
.hub-cats__grid--wide .hub-cat__media { grid-column: 1; grid-row: 1; width: 100%; }
/* Name, tag and copy are one block beside the photo; the copy starts on its own line. */
.hub-cats__grid--wide .hub-cat__title { grid-column: 2; grid-row: 1; flex-flow: row wrap; align-items: center; gap: var(--space-2) var(--space-3); padding: 0; }
.hub-cats__grid--wide .hub-cat__title .hub-cat__desc { flex: 0 0 100%; margin-top: var(--space-2); padding: 0; }
/* The lines sit right under the photo; the row takes any spare height so the actions line up across the cards. */
.hub-cats__grid--wide .hub-cat__lines { grid-column: 1 / -1; grid-row: 2; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--space-6); padding: 0; }
/* The two actions on one row (button, upright rule, "See more"), at the card's foot. */
.hub-cats__grid--wide .hub-cat__actions { grid-column: 1 / -1; grid-row: 3; margin: 0; padding: 0; }
/* A line's WhatsApp link keeps its icon only (named by its aria-label), so a name and its link share a narrow column. */
.hub-cats__grid--wide .hub-wa__label { display: none; }
.hub-cats__grid--wide .hub-wa { min-width: 44px; justify-content: flex-end; }
/* Colour groups (shop-by-color.html, user 2026-10-14 cmv7wrujr093g): the photo frame holds four of the group's
   own swatches, 2 x 2 (a swatch is 100px, too small to fill the frame alone); a group with one shade shows it whole.
   Its shades are tiles, four to a row: the swatch and the code, each leading to the listing in that shade. */
.hub-cat__media--swatches { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr)); }
.hub-cat__media--swatches.is-one { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
.hub-cats__grid--wide .hub-cat__lines--shades { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.hub-shade { display: flex; align-items: center; gap: var(--space-2); min-height: 44px; padding: var(--space-1) var(--space-2) var(--space-1) var(--space-1); border: 1px solid var(--primary-400); color: var(--color-black); font-size: var(--body-size); line-height: var(--body-lh); font-weight: 600; transition: border-color .2s ease, background-color .2s ease; }
.hub-shade img { flex: none; width: 32px; height: 32px; object-fit: cover; }
@media (hover: hover) { .hub-shade:hover { border-color: var(--color-primary); background: var(--color-white); } }
.hub-shade:focus-visible { outline: 2px solid var(--color-black); outline-offset: 2px; }
/* The title above the cards. */
.hub-cats__head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-6); margin-bottom: var(--space-12); }
.hub-sec .hub-cats__head .sec-head { margin-bottom: 0; }
/* Tablet: one card to a row, the same layout. */
@media (max-width: 1024px) {
  .hub-cats__grid--wide { grid-template-columns: minmax(0, 1fr); }
}
/* Phone: the photo (40% of the card, at least 96px) with the name at its top and the tag at its foot; the copy,
   the lines (one column) and the actions run the full width below, the lines' row taking any spare height. */
@media (max-width: 767px) {
  .hub-cats__grid--wide { gap: var(--space-4); }
  .hub-cats__grid--wide > .hub-cat { grid-template-columns: minmax(96px, 40%) minmax(0, 1fr); grid-template-rows: auto auto minmax(0, 1fr) auto; gap: var(--space-3) var(--space-4); padding: var(--space-4); }
  .hub-cats__grid--wide .hub-cat__title { display: contents; }
  .hub-cats__grid--wide .hub-cat__name { grid-column: 2; grid-row: 1; align-self: start; font-size: var(--h5-size); line-height: var(--h5-lh); }
  .hub-cats__grid--wide .hub-cat__tag { grid-column: 2; grid-row: 1; align-self: end; justify-self: start; }
  .hub-cats__grid--wide .hub-cat__title .hub-cat__desc { grid-column: 1 / -1; grid-row: 2; margin-top: 0; }
  .hub-cats__grid--wide .hub-cat__lines { grid-row: 3; grid-template-columns: minmax(0, 1fr); }
  .hub-cats__grid--wide .hub-cat__lines--shades { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The full-width WhatsApp button, then "See more" centred under it (comment cmvkoj41qmrop, 2026-10-23: "Căn giữa"). */
  .hub-cats__grid--wide .hub-cat__actions { grid-row: 4; justify-content: center; }
}

/* ---------- Photo cards borrowed from the Remy page (remy-v2.css: .rv-card, .rv-shot, .rv-swatches) ----------
   Those molds read their easing and entrance delays from an .rv ancestor; the hub and group pages
   have none, so their sections carry the same values. */
:is(.hub-sec, .info-sec, .qs-make) { --rv-ease: cubic-bezier(.16, 1, .3, 1); }
:is(.hub-sec, .info-sec, .qs-make) [data-reveal-delay="1"] { --rv-d: .12s; }
:is(.hub-sec, .info-sec, .qs-make) [data-reveal-delay="2"] { --rv-d: .24s; }
:is(.hub-sec, .info-sec, .qs-make) [data-reveal-delay="3"] { --rv-d: .36s; }
/* Four points a row (the Remy page has three): a step down the type scale so each name keeps to two or three lines.
   The column counts sit in width ranges and name both classes: remy-v2.css loads after this file, and below 900px
   its own one-column layout (photo beside copy, then over it) must still win. */
.rv-cards--4 .rv-card__title { padding: var(--space-6) var(--space-6) 0; font-size: var(--h5-size); line-height: var(--h5-lh); }
.rv-cards--4 .rv-card__text { padding: var(--space-3) var(--space-6) var(--space-6); }
@media (min-width: 1025px) {
  .rv-cards.rv-cards--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* Laptops and landscape tablets: still four across (two rows of square photos would be twice a screen tall), names a step smaller. */
@media (min-width: 1025px) and (max-width: 1280px) {
  .rv-cards--4 .rv-card__title { font-size: var(--h6-size); line-height: var(--h6-lh); }
  .rv-cards--4 .rv-card__text { font-size: var(--body-size); line-height: var(--body-lh); }
}
@media (min-width: 900px) and (max-width: 1024px) {
  .rv-cards.rv-cards--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--space-6); }
}

/* ---------- Origin: the title and its line, then the four points as photo cards (user 2026-10-21) ---------- */
.hub-origin .sec-head__title { margin-bottom: var(--space-6); }
.hub-origin__lead { max-width: 65ch; }
.hub-origin .rv-cards { margin-top: var(--space-12); }
.hub-origin__more { margin-top: var(--space-12); }
/* Hair Extensions hub: the title and its line on the centre line, above the cards, with no button below them
   (user 2026-10-21 "căn giữa", "bỏ nút"); the Wigs hub keeps the left-aligned block and its button. */
.hub-origin--center .sec-head__title { text-align: center; }
.hub-origin--center .hub-origin__lead { margin-inline: auto; text-align: center; text-wrap: balance; }

/* ---------- Why salons work with Luxshine: one square photo beside a ruled list ---------- */
/* The home page's block, laid out plainly here. The photo is a square out to the left edge of the
   screen and as tall as the section; the reasons are rows between hairlines (no filled boxes), one
   open at a time. Three tracks: the photo, the copy, and a right margin equal to the page frame's. */
/* Split on the same line as the origin block above (user 2026-10-14: "cụm ở đây cũng cần chia đúng"): the photo
   ends 32px before the page's middle and the copy starts 32px after it, so both blocks share one 64px gutter. */
.hub-diff__inner { display: grid; grid-template-columns: calc(50% - var(--space-8)) minmax(0, 1fr) max(var(--space-6), calc((100% - var(--container-max)) / 2 + var(--space-6))); }
/* Square by its width; if the copy runs taller than that, the photo grows with the section (cropped). */
.hub-diff .diff__media { align-self: stretch; max-width: 100%; aspect-ratio: var(--ty-le-khoi_vuong); }
.hub-diff .diff__media img { height: 100%; object-fit: cover; }
.hub-diff__content { display: flex; flex-direction: column; justify-content: center; gap: var(--space-8); min-width: 0; padding: var(--space-16) 0 var(--space-16) var(--space-16); }
.hub-diff__head { display: grid; gap: var(--space-4); }
.hub-diff__lead { max-width: 56ch; font-size: var(--body-size); line-height: var(--body-lh); color: var(--color-black); text-wrap: pretty; }
.hub-diff .diff__accordion { gap: 0; border-bottom: 1px solid var(--color-black); }
/* White, not see-through: a row gliding to its new place covers the copy that is fading in above it. */
.hub-diff .diff-item, .hub-diff .diff-item[open] { position: relative; background: var(--color-white); border-top: 1px solid var(--color-black); }
/* The open row is marked by a gold rule drawn across its top edge. */
.hub-diff .diff-item::before { content: ""; position: absolute; top: -1px; left: 0; right: 0; height: 2px; background: var(--color-primary); transform: scaleX(0); transform-origin: left; transition: transform .6s var(--ease-soft); }
.hub-diff .diff-item[open]::before { transform: none; }
.hub-diff .diff-item__summary { grid-template-columns: var(--space-12) minmax(0, 1fr) auto; column-gap: 0; padding: var(--space-6) 0; }
/* An open row: its answer starts 8px under the title, so the two read as one group. */
.hub-diff .diff-item[open] .diff-item__summary { padding-bottom: var(--space-2); }
.hub-diff .diff-item__num { font-size: var(--h6-size); line-height: var(--h6-lh); color: var(--color-black); }
.hub-diff .diff-item__title { transition: color .25s ease; }
.hub-diff .diff-item__chev img { transition: transform .45s var(--ease-soft); }
.hub-diff .diff-item__body { padding: 0 var(--space-12) var(--space-6); }
.hub-diff .diff-item__body p { max-width: 64ch; }
/* Under the pointer (or holding focus) a row's title turns gold. */
@media (hover: hover) { .hub-diff .diff-item__summary:hover .diff-item__title { color: var(--color-primary); } }
.hub-diff .diff-item__summary:focus-visible .diff-item__title { color: var(--color-primary); }
/* Opening a row: its copy fades in and settles. The rows below glide down (js/main.js: diffGlide). */
@media (prefers-reduced-motion: no-preference) {
  .hub-diff .diff-item[open] .diff-item__body { animation: hub-diff-open .45s var(--ease-soft) .1s both; }
}
@keyframes hub-diff-open { from { opacity: 0; transform: translateY(-8px); } }

/* ---------- Which line fits: a tab per group, the group's lines as a table ---------- */
.hub-fit { background: var(--surface-beige); }
/* Group tabs: native buttons in a tablist (js/main.js: tabs). The selected group is the gold one. */
.hub-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-6); }
/* The tabs stay in view while the long table under them scrolls (user 2026-10-14: "cụm này sẽ fix nếu content
   dưới quá dài"): stuck to the top, or under the header (--header-h, measured by js/main.js) whenever it is
   showing; on the section's beige. */
.hub-fit .hub-tabs { position: sticky; top: 0; z-index: 2; margin-inline: calc(var(--space-6) * -1); padding: var(--space-3) var(--space-6); background: var(--surface-beige); }
body:has(.site-header:not(.is-away)) .hub-fit .hub-tabs { top: var(--header-h); }
/* Phone: the stuck tabs wrap instead of scrolling sideways (user 2026-10-15: nothing scrolls sideways); with 12px
   sides the six fit two rows from 375px up. */
@media (max-width: 767px) {
  .hub-fit .hub-tab { padding-inline: var(--space-3); }
}
.hub-tab { min-height: 44px; padding: var(--space-2) var(--space-4); border: 1px solid var(--color-black); background: transparent; color: var(--color-black); font-family: var(--font-body); font-weight: 600; font-size: var(--body-size); line-height: var(--body-lh); cursor: pointer; transition: background-color .2s ease, color .2s ease; }
.hub-tab[aria-selected="true"] { background: var(--color-primary); color: var(--color-black); }
/* Hover is a paler gold than the selected tab, so the two never read the same. */
@media (hover: hover) { .hub-tab:not([aria-selected="true"]):hover { background: var(--primary-300); } }
/* Each panel: the group's lines as a table. One row per line, one column per question (what it is,
   its strengths, who it is for), so the eye compares down a column. The four columns fit the page:
   nothing scrolls sideways. Without the script all six tables stay visible.
   The look is the site's shared table (.dtable, css/sections.css): no frame, column heads 24/32,
   line names 20/28, a hairline per row, and "Best for", the column a buyer decides on, in gold
   (.dtable__best). */
.hub-pick[hidden] { display: none; }
.hub-pick:not([hidden]) + .hub-pick:not([hidden]) { margin-top: var(--space-12); }
.hub-lines { table-layout: fixed; }
.hub-lines thead th:nth-child(1) { width: 20%; }
.hub-lines thead th:nth-child(2) { width: 30%; }
.hub-lines thead th:nth-child(3) { width: 28%; }
.hub-lines tbody th a { color: inherit; }
.hub-lines tbody th a:hover, .hub-lines tbody th a:focus-visible { text-decoration: underline; text-underline-offset: 4px; }
.hub-lines__pros { display: grid; gap: var(--space-1); margin: 0; padding: 0; list-style: none; }
/* A strength is ticked: the project's check icon alone, no box. */
.hub-lines__pros li { display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: var(--space-2); color: var(--color-black); }
.hub-lines__pros li::before { content: ""; width: 20px; height: 20px; margin-top: 2px; background: url("../assets/icons/check.svg") center / 16px no-repeat; }
/* Opening a tab deals its rows in, one after the other (same move as the price table). */
@media (prefers-reduced-motion: no-preference) {
  .hub-lines tbody tr { animation: hub-rate-row .5s var(--ease-soft) both; }
  .hub-lines tbody tr:nth-child(2) { animation-delay: .05s; }
  .hub-lines tbody tr:nth-child(3) { animation-delay: .1s; }
  .hub-lines tbody tr:nth-child(4) { animation-delay: .15s; }
  .hub-lines tbody tr:nth-child(5) { animation-delay: .2s; }
  .hub-lines tbody tr:nth-child(n+6) { animation-delay: .25s; }
}
/* Price guide: its head and its note sit on the page's centre line (user 2026-10-14); the table spans the frame. */
.hub-price .sec-head { align-items: center; text-align: center; }
.hub-price .info-note { margin-inline: auto; text-align: center; }

/* ---------- Price guide ---------- */
/* The line a group's figures come from, under the group name */
.hub-price__line { display: block; font-family: var(--font-body); font-weight: 400; font-size: var(--body-size); line-height: var(--body-lh); text-transform: none; color: var(--color-font); }
/* The table (the shared .dtable look): one row per group, with its photo, its name and four prices. */
.hub-rates { table-layout: fixed; }
/* 8px above and below the 48px photo: six rows, the heads, the title and the note stay within one screen. */
.hub-rates tbody th, .hub-rates tbody td { padding-block: var(--space-2); vertical-align: middle; }
.hub-rates thead th:first-child { width: 28%; }
.hub-rates tbody th { font-weight: 400; }
.hub-rates__name { font-weight: 600; }
.hub-rates__group { display: flex; align-items: center; gap: var(--space-4); }
.hub-rates__thumb { flex: none; width: 48px; height: 48px; overflow: hidden; }
.hub-rates__thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease-soft); }
.hub-rates__fig { display: block; font-size: var(--h6-size); line-height: var(--h6-lh); font-weight: 600; font-variant-numeric: tabular-nums lining-nums; white-space: nowrap; color: var(--color-black); }
@media (hover: hover) {
  .hub-rates tbody tr:hover .hub-rates__thumb img { transform: scale(1.12); }
}
/* Coming into view (js/main.js adds .is-in): the rows land one after another. */
@media (prefers-reduced-motion: no-preference) {
  .hub-price__table.is-in .hub-rates tbody tr { animation: hub-rate-row .5s var(--ease-soft) both; }
  .hub-price__table.is-in .hub-rates tbody tr:nth-child(2) { animation-delay: .05s; }
  .hub-price__table.is-in .hub-rates tbody tr:nth-child(3) { animation-delay: .1s; }
  .hub-price__table.is-in .hub-rates tbody tr:nth-child(4) { animation-delay: .15s; }
  .hub-price__table.is-in .hub-rates tbody tr:nth-child(5) { animation-delay: .2s; }
  .hub-price__table.is-in .hub-rates tbody tr:nth-child(6) { animation-delay: .25s; }
}
@keyframes hub-rate-row { from { opacity: 0; transform: translateY(8px); } }
/* Two levels: the note belongs to the title (16px under it); the table is its own block (32px on). */
.hub-sec.hub-price .sec-head { margin-bottom: var(--space-4); }
.hub-price .info-note { max-width: 80ch; margin-top: 0; margin-bottom: var(--space-8); }
.hub-price__ask { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-4); max-width: 60ch; }
.hub-price__ask[hidden], .hub-price__table[hidden] { display: none; }

/* ---------- Production process: the home page's five steps as overlap cards ---------- */
/* Centred: the title sits above, and the cards take the page's full width, photo and copy half each.
   Same mechanics as the home page's OEM cards (.oem__stack): each card sticks one notch lower than
   the one before, so the next card slides over it. --header-h is the sticky header's height
   (js/main.js: headerOver).
   --hub-step-size is the side of the square photo, and so the height of a card. A stuck card has to fit in
   what the screen leaves under the header, or the foot of its photo is never seen; the media queries below
   work the side out from the screen's height. --hub-stack-top is where the first card sticks. */
.hub-process { --hub-vh: 100vh; --hub-step-size: 448px; --hub-stick: calc(var(--header-h) + var(--space-4)); --hub-stack-top: var(--hub-stick); }
@supports (height: 100svh) { .hub-process { --hub-vh: 100svh; } }
.hub-process__inner { display: flex; flex-direction: column; align-items: center; gap: var(--space-12); }
.hub-process__intro { text-align: center; }
.hub-process__stack { display: flex; flex-direction: column; gap: var(--space-8); width: 100%; margin: 0; padding: 0; list-style: none; }
/* A step: a square photo on the left, as wide as the card is tall; step number, title and copy on the right. */
.hub-step { position: sticky; top: calc(var(--hub-stack-top) + var(--hub-step-n, 0) * var(--space-4)); display: grid; grid-template-columns: var(--hub-step-size) minmax(0, 1fr); min-height: var(--hub-step-size); background: var(--color-white); border-top: 2px solid var(--color-primary); box-shadow: var(--shadow-md); }
.hub-step:nth-child(1) { z-index: 1; }
.hub-step:nth-child(2) { --hub-step-n: 1; z-index: 2; }
.hub-step:nth-child(3) { --hub-step-n: 2; z-index: 3; }
.hub-step:nth-child(4) { --hub-step-n: 3; z-index: 4; }
.hub-step:nth-child(5) { --hub-step-n: 4; z-index: 5; }
/* Under the header: 16px, the first card, four 16px steps of the stack, 8px to spare. */
@media (min-width: 1025px) and (min-height: 680px) {
  .hub-process { --hub-step-size: min(640px, 42vw, calc(var(--hub-vh) - var(--hub-stack-top) - var(--space-16) - var(--space-2))); }
}
/* A taller screen also keeps the title in view: it sticks under the header and the cards stack 24px below it.
   The title's bottom margin reaches down to the foot of the last stuck card, and a sticky box is held inside
   its container by its margin edge, so the title lets go at the same moment as that card and the two leave
   together. The stack takes the same length back, which keeps their gap in the flow. */
@media (min-width: 1025px) and (min-height: 776px) {
  .hub-process { --hub-stack-top: calc(var(--hub-stick) + var(--h2-lh) + var(--space-6)); --hub-step-size: min(640px, 42vw, calc(var(--hub-vh) - var(--hub-stack-top) - var(--space-16) - var(--space-6))); }
  .hub-process__intro { position: sticky; top: var(--hub-stick); margin-bottom: calc(var(--space-6) + var(--space-16) + var(--hub-step-size)); }
  .hub-process__stack { margin-top: calc(-1 * (var(--space-6) + var(--space-16) + var(--hub-step-size))); }
}
/* On a short screen a 448px card and its offset do not fit under the header: no sticking there. */
@media (max-height: 679px) { .hub-step { position: static; } }
.hub-step__media { position: relative; overflow: hidden; aspect-ratio: var(--ty-le-khoi_vuong); }
/* Cropped from the top: the faces stay in, and the caption printed along the bottom of one photo stays out. */
.hub-step__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center top; }
.hub-step__body { display: flex; flex-direction: column; justify-content: center; gap: var(--space-4); min-width: 0; padding: var(--space-16); }
/* Gold 600, not the home page's pale gold: on a white card the pale one falls under 3:1. */
.hub-step__num { font-family: var(--font-heading); font-size: var(--h4-size); line-height: var(--h4-lh); color: var(--primary-600); }
.hub-step__title { font-family: var(--font-heading); font-weight: 400; font-size: var(--h3-size); line-height: var(--h3-lh); text-transform: uppercase; color: var(--color-black); }
.hub-step__text { max-width: 44ch; font-size: var(--h6-size); line-height: var(--h6-lh); color: var(--color-black); text-wrap: pretty; }

/* ---------- FAQ: the first question starts right at the block's own gap ---------- */
.hub .faq-item:first-child > .faq-item__summary { padding-top: 0; }

/* ---------- Contact: WhatsApp beside the form ---------- */
.hub-contact__wa { align-self: flex-start; }

@media (max-width: 1024px) {
  /* Banner stacks under a solid header: the photo is a band across the top (the whole picture down
     to 640px, then its right side with the model), and the copy sits below on the plain field. */
  [data-header-over] .shop-banner__media { display: block; position: relative; height: clamp(200px, 62vw, 320px); }
  [data-header-over] .shop-banner__inner { min-height: 0; padding-block: var(--space-12); }
  [data-header-over] .shop-banner__content { max-width: 64ch; }
  [data-header-over] .shop-banner[data-t2-banner] .shop-banner__media { width: auto; }
  [data-header-over] .shop-banner[data-t2-banner] .shop-banner__media::before { content: none; }
  /* The photo shrinks beside the name and copy; lines and buttons take the full row. */
  .hub-cat { grid-template-columns: 160px minmax(0, 1fr); grid-template-rows: auto 1fr; gap: var(--space-4) var(--space-6); }
  .hub-cat__body { display: contents; }
  .hub-cat__media { grid-row: span 2; }
  /* The body is gone as a box here, so each part of the copy carries the card's padding itself. */
  .hub-cat__lines { grid-column: 1 / -1; padding: 0 var(--space-6) var(--space-6); }
  .hub-cat__desc { padding-right: var(--space-6); }
  .hub-cat__desc:last-child { padding-bottom: var(--space-6); }
  /* Beside the small photo the actions sit under the name, in every card alike. */
  .hub-cat__head { flex-direction: column; align-items: flex-start; padding: var(--space-6) var(--space-6) 0 0; }
}
@media (max-width: 1024px) {
  /* Process: no sticking, as the home page does with its OEM cards; one step down in size. */
  .hub-process { --hub-step-size: max(320px, 42vw); }
  .hub-process__inner { gap: var(--space-8); }
  .hub-step { position: static; }
  .hub-step__num { font-size: var(--h5-size); line-height: var(--h5-lh); }
  .hub-process__stack { gap: var(--space-4); }
  .hub-step__body { gap: var(--space-2); padding: var(--space-8); }
  .hub-step__title { font-size: var(--h4-size); line-height: var(--h4-lh); }
  /* Why salons: the photo becomes a band across the top, edge to edge; the list takes the page frame. */
  .hub-diff__inner { grid-template-columns: minmax(0, 1fr); }
  .hub-diff .diff__media { aspect-ratio: auto; height: clamp(240px, 56vw, 420px); }
  .hub-diff__content { padding: var(--space-12) var(--space-6) var(--space-16); }
  /* Price table on a tablet: tighter cells and body-size figures so four price columns still fit. */
  .hub-rates th, .hub-rates td { padding-inline: var(--space-2); }
  .hub-rates tr > :first-child { padding-left: 0; }
  .hub-rates thead th:first-child { width: 34%; }
  .hub-rates__fig { font-size: var(--body-size); line-height: var(--body-lh); }
  .hub-rates__group { gap: var(--space-3); }
  .hub-fit__terms { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); margin-top: var(--space-8); }
}
@media (max-width: 768px) {
  .hub-sec { padding-block: var(--space-16); }
  .hub-sec .sec-head { margin-bottom: var(--space-8); }
  .hub-cats__head { margin-bottom: var(--space-8); }
  .hub-cats__grid { gap: var(--space-4); }
  .hub-cat__name { font-size: var(--h5-size); line-height: var(--h5-lh); }
}
@media (max-width: 640px) {
  [data-header-over] .shop-banner__content { max-width: 100%; }
  /* The photo moves above the copy. */
  .hub-step { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .hub-step__media { height: auto; }
  /* Why salons: the photo is a full-width square again. */
  .hub-diff .diff__media { height: auto; aspect-ratio: var(--ty-le-khoi_vuong); }
  .hub-step__body { padding: var(--space-6); }
  .hub-step__num { font-size: var(--h6-size); line-height: var(--h6-lh); }
  .hub-step__title { font-size: var(--h5-size); line-height: var(--h5-lh); }
  .hub-step__text { font-size: var(--body-size); line-height: var(--body-lh); }
  /* Why salons: a narrower number column and smaller row titles. */
  .hub-diff .diff-item__summary { grid-template-columns: var(--space-8) minmax(0, 1fr) auto; padding: var(--space-4) 0; }
  .hub-diff .diff-item__title { font-size: var(--h6-size); line-height: var(--h6-lh); }
  .hub-diff .diff-item__body { padding: 0 0 var(--space-4) var(--space-8); }
  /* Thumbnail beside the name; copy, lines and buttons below, full width. */
  .hub-cat { grid-template-columns: 96px minmax(0, 1fr); grid-template-rows: auto; column-gap: var(--space-4); }
  .hub-cat__media { grid-row: auto; }
  /* The name stays beside the thumbnail; the two actions take the card's full width below. */
  .hub-cat__head { display: contents; }
  .hub-cat__title { align-self: center; flex-direction: column; align-items: flex-start; gap: var(--space-2); padding: var(--space-4) var(--space-4) var(--space-4) 0; }
  .hub-cat__actions { grid-column: 1 / -1; justify-content: space-between; column-gap: var(--space-4); padding-inline: var(--space-4); }
  .hub-cat__actions .btn { flex: 1 1 auto; }
  .hub-cat__actions .btn + .label-link::before { content: none; }
  .hub-cat__desc { grid-column: 1 / -1; padding-inline: var(--space-4); }
  .hub-cat__desc:last-child { padding-bottom: var(--space-4); }
  .hub-cat__lines { padding: 0 var(--space-4) var(--space-4); }
  /* Two full-width rows: side by side the labels would wrap inside the buttons. */
}
@media (prefers-reduced-motion: reduce) {
  .hub-cat__media img, .wa-ico, .hub-rates__thumb img, .hub-diff .diff-item::before, .hub-diff .diff-item__title, .hub-diff .diff-item__chev img, .hub-cat .label-link { transition: none; }
}

/* ---------- Phone (user comments 2026-10-14 at 375px) ---------- */
@media (max-width: 767px) {
  /* Line table: each line of hair becomes a white card, one under another (user 2026-10-15: nothing scrolls
     sideways). "Best for", the answer a buyer looks for, leads on gold; then what it is and its strengths,
     each under a readable label. The explicit ARIA roles in the markup keep it a table for assistive tech. */
  .hub-pick:focus-visible { outline: 2px solid var(--color-black); outline-offset: 2px; }
  .hub-lines, .hub-lines tr, .hub-lines th, .hub-lines td { display: block; }
  .hub-lines thead { display: none; }
  .hub-lines tbody { display: grid; gap: var(--space-4); }
  .hub-lines tbody tr { display: flex; flex-direction: column; background: var(--color-white); }
  .hub-lines tbody th, .hub-lines tbody td { border-top: 0; }
  .hub-lines tbody th { order: 0; padding: var(--space-4) var(--space-4) var(--space-3); font-size: var(--h6-size); line-height: var(--h6-lh); }
  .hub-lines tbody td { order: 2; padding: var(--space-3) var(--space-4); font-size: var(--body-size); line-height: var(--body-lh); color: var(--color-black); }
  .hub-lines tbody td.dtable__best { order: 1; padding-block: var(--space-3); font-weight: 600; }
  .hub-lines tbody td:last-child:not(.dtable__best) { padding-bottom: var(--space-4); }
  .hub-lines td::before { content: attr(data-label); display: block; margin-bottom: var(--space-1); font-size: var(--body-size); line-height: var(--body-lh); font-weight: 600; color: var(--color-font); }
  .hub-lines td.dtable__best::before { color: var(--color-black); font-weight: 400; }
  .hub-lines td:empty { display: none; }
  /* Price table: each group is a block, its photo and name on top and its four lengths in a 2 x 2 grid,
     the length above the price, so the whole table is about half as tall and lengths compare side by side. */
  .hub-rates, .hub-rates tbody, .hub-rates th, .hub-rates td { display: block; }
  .hub-rates thead { display: none; }
  .hub-rates tbody tr { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); padding-block: var(--space-4); border-top: 1px solid var(--dtable-line); }
  .hub-rates tbody th, .hub-rates tbody td { border-top: 0; }
  .hub-rates tbody th { grid-column: 1 / -1; padding: 0 0 var(--space-3); }
  .hub-rates tbody td { padding: var(--space-2) 0; }
  .hub-rates tbody td:nth-of-type(even) { padding-left: var(--space-4); border-left: 1px solid var(--dtable-line); }
  .hub-rates tbody td:nth-of-type(n+3) { border-top: 1px solid var(--dtable-line); }
  .hub-rates td::before { content: attr(data-label); display: block; font-size: var(--body-size); line-height: var(--body-lh); color: var(--color-font); }
  .hub-rates .hub-rates__fig { font-size: var(--h6-size); line-height: var(--h6-lh); }
}
