/* custom.css — overrides only — each rule cites the vendor line (decisions.md §4). */

/* news.css:36–37, :42–61 — menu hover/current colours gated by body:not(.dark) #header:not(.dark); re-scoped to the dark
   header. R6 (decisions.md §17, T22): category colours only on category badges → every menu item (home + the categories)
   hovers / marks current in the theme colour (bg 5.56 · bgOffset 4.75). Menu links beat style.css:17795 / :17798
   (.menu-item:hover / .current > .menu-link) by specificity, no !important */
#header.dark .menu-container > .menu-item[class*="menu-color-"]:hover > .menu-link,
#header.dark .menu-container > .menu-item[class*="menu-color-"].current > .menu-link { color: var(--cnvs-themecolor); }
/* news.css:96–111 — .menu-color-<c> > a::before category colour bar (!important) → theme colour (R6, T22) */
.menu-item[class*="menu-color-"] > a::before { background: var(--cnvs-themecolor) !important; }

/* news.css:98 .bg-tech, :103 .bg-fashion, :108 .bg-sports — white / text-light badge text 3.98 / 3.82 / 4.29:1 → bg 80%
   toward black (--cnvs-white / --cnvs-black, style.css:13289–13290). Fixed fill, both themes (T18: was .dark-scoped toward
   --cnvs-contrast-0, black under .dark). bg-travel unchanged. bg-sports added T05b.
   Home tabs (.bg-color-<c>.nav-link :hover/.active): the primary fill since T22 (R6), tab rule below */
.bg-tech { background: color-mix(in srgb, var(--cnvs-color-tech) 80%, var(--cnvs-black)) !important; }
.bg-fashion { background: color-mix(in srgb, var(--cnvs-color-fashion) 80%, var(--cnvs-black)) !important; }
.bg-sports { background: color-mix(in srgb, var(--cnvs-color-sports) 80%, var(--cnvs-black)) !important; }

/* news.css:259 — .navbar-nav.nav-sm .nav-link rest colour dark grey literal, invisible on navy (home tabs); light: the same
   literal → --cnvs-contrast-800 (#343A40, T18). Hover/active white (news.css:84–85) kept */
.navbar-nav.nav-sm .nav-link:not(:hover):not(.active) { color: var(--cnvs-contrast-800); }

/* ---- Colour census (T17): vendor colour literals that render → same colour through a custom property ---- */

/* news.css:84–85 — tab hover/active text #FFF on the category fill; news.css:96–111 .bg-color-<c> fill (!important).
   R6 (T22): category colours only on category badges → the home tabs hover / active = the primary fill + ctaLabel
   (5.56:1), both themes */
.navbar-nav.nav-sm .nav-link:hover,
.navbar-nav.nav-sm .nav-link.active { color: var(--xb-cta-label); background: var(--xb-primary) !important; }
/* news.css:192–193 — category badge text #FFF !important on the category fill */
.entry-categories a { color: var(--cnvs-white) !important; }
/* news.css:211–212 — entry meta links #AAA at rest */
.entry-meta li a:not(:hover) { color: var(--xb-meta-text); }
/* style.css:25812–25814 — social icon hover glyph #FFF on the network fill */
.social-icon i:last-child { color: var(--cnvs-white); }
/* style.css:31731 — 404 number #ddd */
.error404 { color: var(--xb-error404); }
/* style.css:31729 — 404 number 18vw (252px at 1400): capped so the cluster fits the first screen (1400×800, 375×667; T22) */
.error404 { font-size: min(18vw, 10rem); }
/* style.css:36214–36215 — footer widget links rgba(255, 255, 255, .75); --cnvs-contrast-rgb = 255, 255, 255 under .dark (:36194) */
.dark#footer .footer-widgets-wrap a, .dark #footer .footer-widgets-wrap a { color: rgba(var(--cnvs-contrast-rgb), .75); }

/* news.css:276, :289 — .line-xs light grey literal, harsh on navy → --xb-line (bgOffset; light: the TPL value, T18) */
.line-xs { border-bottom-color: var(--xb-line); }
.line-xs::after { border-top-color: var(--xb-line); }

/* news.css:441 — .owl-dot demo orange literal */
.owl-carousel .owl-dots .owl-dot { background-color: var(--cnvs-themecolor); }

/* style.css:36221–36224 — #copyrights has no background var (rgba literal over the footer) → bgDeep */
.dark #copyrights, #copyrights.dark { background-color: var(--xb-bg-deep); }

/* style.css:23797–23798 — primary-filled .button: white text on themecolor = 2.37:1 → ctaLabel.
   Variants with their own colours (:23822 dark, :23825 light, :23892 border) and :hover (:23829) excluded. */
.button:not(.button-dark):not(.button-light):not(.button-border):not(:hover) { color: var(--xb-cta-label); }
/* style.css:23829–23831 — .button:hover gray-900 fill + white text (T15 ruling 4): primary-filled buttons (post CTA, search
   submit) and the border button (:23901–23905 themecolor fill + white, 2.37:1) hover to --xb-primary-hover + ctaLabel.
   Footer links' white hover (style.css:36217) would win on the app buttons → same rule, footer specificity */
.button:not(.button-dark):not(.button-light):hover,
.dark #footer .footer-widgets-wrap a.button:not(.button-dark):not(.button-light):hover { background-color: var(--xb-primary-hover); color: var(--xb-cta-label); }

/* style.css:23892–23896 — .button-border border + text = --cnvs-btn-color-dark (gray-900): invisible on navy (footer app
   buttons, TPL button-light/bg-white swapped, T15) → light contrast value */
.dark .button.button-border { --cnvs-btn-border-color: var(--cnvs-contrast-800); }

/* style.css:13525 — --cnvs-link-hover-color = var(--cnvs---cnvs-heading-color) (invalid) → a:hover (:13811) falls back
   to the inherited near-white text. Primary links (rest --cnvs-link-color, :13524) → --xb-primary-hover (tokens.css) */
.dark { --cnvs-link-hover-color: var(--xb-primary-hover); }

/* news.css:211 / style.css:21807 — entry meta rest = light grey literal via a:not(:hover); hover had no own rule → themecolor */
.dark .entry-meta li a:hover { color: var(--cnvs-themecolor); }

/* style.css:21719–21723 — entry title hover themecolor loses to the !important .text-light (:8782) / .text-white (:8797)
   utilities of TPL markup (carousel overlay, footer W1) */
.dark .entry-title a.text-light:hover,
.dark .entry-title a.text-white:hover { color: var(--cnvs-themecolor) !important; }

/* style.css:36217 — footer widget links hover → white; light rest (:36214) → themecolor. App buttons excluded */
.dark #footer .footer-widgets-wrap a:not(.button):hover { color: var(--cnvs-themecolor); }

/* style.css:32692 — copyrights menu hover = opacity only; light rest (:32639 contrast-500) → themecolor */
.copyright-links a:hover { color: var(--cnvs-themecolor); }

/* style.css:13805 — text-decoration: none !important on every link → body links colour-only (WCAG 1.4.1).
   Same selector under .entry-content outranks it; the vendor shorthand is !important, so each longhand is too.
   Option B (decisions.md §4): rest 1px underline at 70% currentColor; hover/focus 2px solid (colour = --xb-primary-hover).
   .button excluded (cta block, style.css:23797) */
.entry-content a:not(.btn-link):not(.text-decoration-underline):not(.more-link):not(.button) {
  text-decoration-line: underline !important;
  text-decoration-thickness: 1px !important;
  text-decoration-color: color-mix(in srgb, currentColor 70%, transparent) !important;
  text-underline-offset: .25em;
}
.entry-content a:not(.btn-link):not(.text-decoration-underline):not(.more-link):not(.button):hover,
.entry-content a:not(.btn-link):not(.text-decoration-underline):not(.more-link):not(.button):focus-visible {
  text-decoration-color: currentColor !important;
  text-decoration-thickness: 2px !important;
}

/* Lighthouse color-contrast (decisions.md §4, T04b). style.css:36222 — .dark #copyrights text rgba(255,255,255,.4)
   on bgDeep 3.69:1 → contrast-500 7.62:1 */
.dark #copyrights, #copyrights.dark { color: var(--cnvs-contrast-500); }

/* ==== T05 spacing standard (decisions.md §13) — Canvas variables first, then cited rules. sm = < 992px (T05b) ==== */

/* style.css:13302–13304 — --cnvs-content-padding 5rem (content-wrap :20946, footer widgets :32661, copyrights :32665)
   and --cnvs-section-margin 4rem (.section :13651–13652, .line :13961) → 64 · 48 (sm) and 64 · 36 (sm) */
:root { --cnvs-content-padding: 4rem; }
@media (max-width: 991.98px) {
  :root { --cnvs-content-padding: 3rem; --cnvs-section-margin: 2.25rem; }
}

/* style.css:748–758 — .container gutter 1.5rem → 12px side padding; §13 ≥ 16 on mobile */
@media (max-width: 991.98px) {
  .container { --bs-gutter-x: 2rem; }
}

/* style.css:32661 — footer widgets bottom padding = content padding; §13 48 · 32 (sm) */
#footer .footer-widgets-wrap { padding-bottom: 3rem; }
@media (max-width: 991.98px) {
  #footer .footer-widgets-wrap { padding-bottom: 2rem; }
}

/* style.css:32731 — --cnvs-widget-margin 3rem; titled widgets add the heading's half-leading → 44 · 36 (sm) */
.widget { --cnvs-widget-margin: 2.75rem; }
@media (max-width: 991.98px) {
  .widget { --cnvs-widget-margin: 2.25rem; }
  /* news.css:275 — .line-xs bottom margin 20px → section heading → content 16 on mobile */
  .line-xs { margin-bottom: .875rem; }
}

/* style.css:21634 — --cnvs-post-content-margin 2rem (cards: /1.5, :21823) → card meta → excerpt 12 */
#posts, .posts, .entry, [class*=entry-] { --cnvs-post-content-margin: .75rem; }

/* style.css:21697 — .posts-md .entry-image + .entry-title .875rem → card image → title 12 on mobile */
@media (max-width: 991.98px) {
  .posts-md .entry-image + .entry-title { margin-top: .5rem; }
}

/* style.css:15289–15295 — card lists (.col-mb-30 / .col-mb-50 rows, col padding-bottom :15283) → row gap 40 · 32 (sm),
   text half-leading included. Only rows that hold cards. */
#content .row.col-mb-30:has(.entry), #content .row.col-mb-50:has(.entry) { --cnvs-col-mb: 2.25rem; }
@media (max-width: 991.98px) {
  #content .row.col-mb-30:has(.entry), #content .row.col-mb-50:has(.entry) { --cnvs-col-mb: 1.75rem; }
}
/* style.css:15279–15281 — the grid row's `mb-0` (src/category.njk, for the pager below) keeps the last cards' col
   padding; a card grid that ends its column (search results, T13) gets the negative row margin back */
#content .col-lg-8 > .row.col-mb-50.mb-0:last-child:has(> .col-md-6) { margin-bottom: calc(var(--cnvs-col-mb) * -1) !important; }

/* style.css:13845 (h1–h3 margin 0 0 30px), :13937 (p, ul, … margin-bottom 30px) — post body headings:
   the block before a heading drops its bottom margin, the heading carries the space → h2 48/16 · h3 32/12,
   sm h2 40/16 · h3 24/12 (half-leading included) */
.entry-content > :has(+ h2), .entry-content > :has(+ h3) { margin-bottom: 0; }
.entry-content > h2 { margin: 2.5rem 0 .5rem; }
.entry-content > h3 { margin: 1.75rem 0 .25rem; }
@media (max-width: 991.98px) {
  .entry-content > h2 { margin-top: 2.125rem; }
  .entry-content > h3 { margin-top: 1.25rem; }
}

/* Post body: a block utility (cta partial .mb-5, style.css:8100-range utilities) must not stack on the heading's margin;
   the last block leaves the post-item margin alone */
.entry-content > :has(+ h2), .entry-content > :has(+ h3), .entry-content > :last-child { margin-bottom: 0 !important; }
/* style.css:13926–13937 — paragraph gap 30px, zeroed on the body's last child (rule above) → the block after the body
   takes it: contact line (page), tag list (post) (T12), brand box (post, T16) */
.entry-content + address, .entry-content + div:has(> .tagcloud), .entry-content + .promo { margin-top: 30px; }

/* style.css:1935 — .table margin-bottom 1rem inside the body table wrapper (div.table-responsive): the wrapper drops its
   margin before a heading (rule above), the table's own 1rem stayed → h2/h3 top +16 (T11, /chinh-sach-bao-mat/) */
.entry-content > .table-responsive:has(+ h2, + h3) > .table { margin-bottom: 0; }

/* style.css:13744–13747 (Canvas reset: ol, ul, li padding 0) cancels the reboot indent (style.css:300–303) → body list
   markers hang left of the text column, nested lists flush. Text 24px in per level; style.css:13926–13937
   (ul, ol margin-bottom 30px) also hits a nested list → 0 inside its item; style.css:20952–20954 gives the content
   leading to p only → lists too (visible p → list → block gaps = p → p). Body lists only (block lists keep theirs) */
.entry-content > :is(ul, ol) { line-height: var(--cnvs-line-height-content); }
.entry-content > :is(ul, ol), .entry-content > :is(ul, ol) :is(ul, ol) { padding-left: 1.5rem; }
.entry-content > :is(ul, ol) :is(ul, ol) { margin-bottom: 0; }

/* style.css:710–714 — blockquote attribution #6c757d (Bootstrap literal) on bg 2.81:1 → contrast-500 (light: lever value, T18) */
.blockquote-footer { color: var(--cnvs-contrast-500); }

/* style.css:21618 / :21648 — --cnvs-post-item-margin 50px (post .entry bottom) → post body → post nav 64 · 40 (sm) */
#posts, .posts, .entry, [class*=entry-] { --cnvs-post-item-margin: 3.75rem; }
@media (max-width: 991.98px) {
  #posts, .posts, .entry, [class*=entry-] { --cnvs-post-item-margin: 2.25rem; }
}

/* style.css:22070 — .post-navigation + .line margin-top = var(--cnvs-contrast-900) (invalid → 0) and the .line bottom
   section margin (:13961): divider centred between post nav and related → 64 · 40 (sm) */
.post-navigation + .line { margin: 1.75rem 0; }
@media (max-width: 991.98px) {
  .post-navigation + .line { margin: 1rem 0; }
}

/* Home major sections (§13 64 · 40 sm). The TPL utilities (img.aligncenter mb-5, col mt-4, div mt-5 — demo-news
   index.html) give 48 + 24 / 48; one own gap replaces them */
:root { --xb-section-gap: 4rem; }
@media (max-width: 991.98px) {
  :root { --xb-section-gap: 2.5rem; }
}
/* style.css:13652 — home carousel .section bottom padding */
.section.bg-transparent:has(> .container > #oc-news) { padding-bottom: var(--xb-section-gap); }
/* T23 full-width home sections (latest, ad slots, category sections, topics = .content-wrap children): one gap after the
   latest block */
.content-wrap:has(#nav-tabContent) > .container ~ * { margin-top: var(--xb-section-gap); }
/* style.css:8076 — .mb-0 !important (TPL demo-news.html:863 `row col-mb-30 mb-0`) cancels the col-mb row margin
   (style.css:15280–15281) → the last column's padding-bottom (:15286) added 30–36px under each category section */
.content-wrap:has(#nav-tabContent) > .container > .row.col-mb-30.mb-0 { margin-bottom: calc(var(--cnvs-col-mb) * -1) !important; }
@media (min-width: 992px) {
  /* ≥ 992 a category section ends on the lead card excerpt: minus its line descent (.375rem) */
  .content-wrap:has(#nav-tabContent) > .container:has(> .row.col-mb-30) + * { margin-top: calc(var(--xb-section-gap) - .375rem); }
}

/* footer.njk utilities mb-5 (TPL demo-news index.html footer widgets) → footer widgets stacked 32 below 992 */
@media (max-width: 991.98px) {
  #footer .footer-widgets-wrap > [class*=col-]:not(:last-child) { margin-bottom: 1.625rem !important; }
}

/* style.css:20946 — static page whose column ends in body text (page.njk, T11; one column, no sidebar since T21): the
   last line's half-leading (.25rem) adds to the content padding → last block → footer 64 · 48 (sm) */
#content > .content-wrap:has(.col-lg-10 > .entry:last-child > .entry-content:last-child) { padding-bottom: calc(var(--cnvs-content-padding) - .25rem); }

/* style.css:20946 — archive page whose card grid ends the main column (no pager; topic / category with ≤ 10 posts, T22):
   the last excerpt line's half-leading (.25rem) adds to the content padding → last block → footer 64 (≥ 992; < 992 the
   sidebar ad slot is the last block) */
@media (min-width: 992px) {
  #content > .content-wrap:has(.col-lg-8 > .row.col-mb-50.mb-0:last-child) { padding-bottom: calc(var(--cnvs-content-padding) - .25rem); }
}

/* style.css:20946 — 404 content top padding: header → first block 40 · 24 (sm); last block → footer 48 on phones */
#content > .content-wrap:has(.error404) { padding-top: 2.5rem; }
/* T21 one cluster, the category links last: li padding-bottom (style.css:32770 widget_links li, 4px) + half-leading → 64 (lg) */
@media (min-width: 992px) {
  #content > .content-wrap:has(.error404) { padding-bottom: calc(var(--cnvs-content-padding) - .4375rem); }
}
@media (max-width: 991.98px) {
  #content > .content-wrap:has(.error404) { padding: 1.5rem 0 2.5rem; }
}

/* sidebar.njk / index.njk: TPL utilities mt-5 mt-lg-0 on .sticky-sidebar-wrap (48 + the content's half-leading)
   → content → stacked sidebar 48. sidebar.njk column without the Canvas JS hook since T22 → matched by its inner wrapper */
@media (max-width: 991.98px) {
  .sticky-sidebar-wrap, .col-lg-4:has(> .sticky-sidebar) { margin-top: 2.75rem; }
  /* style.css:32738 / :32754 — widget top margin, zero only on :first-child; < 992 the ad slot is the one visible widget
     (categories, latest, related topics d-none, T22) → no margin of its own on top of the column's */
  .sticky-sidebar > .widget.sticky-lg-top { margin-top: 0; }
}
/* style.css:7107–7111 — .sticky-lg-top top 0: the sidebar ad slot (T22) stops below the sticky header (#header-wrap 60px
   = --cnvs-header-height-sm, header-size-sm, style.css:17404, + its 2 × 1px borders) + the §13 widget gap 48.
   style.css:32737 — .widget position relative comes after .sticky-lg-top (same specificity) → sticky restated.
   style.css:20947 — .content-wrap overflow hidden makes it the scroll container of anything sticky inside → clip (same
   clipping, no scroll container), only on pages with the sticky ad slot */
@media (min-width: 992px) {
  .sticky-sidebar > .widget.sticky-lg-top { position: sticky; top: calc(var(--cnvs-header-height-sm) + 2px + 3rem); }
  .content-wrap:has(.sticky-sidebar > .widget.sticky-lg-top) { overflow: clip; }
}

/* style.css:21765–21784 — .entry-meta ul (flex-wrap, negative left margin for the separators) inside overflow:hidden:
   an item that wraps inside itself starts its second line under the clip (last letters only), exposed by the 16px
   mobile gutter. Items stay on one line; the flex row wraps between items */
.entry-meta li { white-space: nowrap; }

/* ---- Touch targets (decisions.md §4): ≥ 24×24; mobile nav items, buttons, pager links ≥ 44×44 ---- */

/* style.css:29435–29443 / :29526–29531 — Owl dot 8px → 24px button, 8px dot kept (content-box background) */
.owl-carousel .owl-dots .owl-dot {
  width: 1.5rem; height: 1.5rem; padding: .5rem; margin: 22px 0 0;
  background-clip: content-box;
}

/* style.css:4827–4834 — Bootstrap rounds only the first/last .page-link; Canvas circle radius (style.css:33035) → every
   item (decisions.md §6) */
.pagination-circle .page-link { border-radius: var(--bs-pagination-border-radius); }

/* news.css:191–200 — category badge 19px high → ≥ 24px */
.entry-categories a { display: inline-flex; align-items: center; min-height: 1.5rem; }

/* style.css:18899–18905 — header top-search trigger 20px → 24px box, icon size kept (T13; 44px on mobile below) */
.header-misc-icon > a { width: 1.5rem; height: 1.5rem; }

@media (max-width: 767.98px) {
  /* style.css:17670 / :17727 — menu trigger: 12px padding on the div, 16px button → padding moves to the button (44×44) */
  .primary-menu-trigger { padding: 0; }
  .primary-menu-trigger .cnvs-hamburger { box-sizing: content-box; padding: .875rem; }
  /* style.css:18899–18905 — header top-search trigger → 44px on mobile (T13) */
  .header-misc-icon > a { width: 2.75rem; height: 2.75rem; }
  /* style.css:2995–2996 (.btn padding .375rem .75rem) — header colour mode switch 42×39 → 44×44 (T18) */
  .header-misc-icon > .btn { min-width: 2.75rem; min-height: 2.75rem; }
  /* news.css:381–391, :396–398 — Owl fixed-nav arrows 26px → 44px on mobile */
  .owl-carousel.fixed-nav .owl-nav [class*=owl-],
  .owl-carousel.fixed-nav:hover .owl-nav [class*=owl-] { width: 2.75rem; height: 2.75rem; line-height: 2.625rem; right: 3rem; }
  .owl-carousel.fixed-nav .owl-nav .owl-next,
  .owl-carousel.fixed-nav:hover .owl-nav .owl-next { right: 0; }
  /* news.css:408–409 — top-nav arrows sit 44px above the carousel → 52px for the 44px button */
  .owl-carousel.fixed-nav.top-nav .owl-nav [class*=owl-],
  .owl-carousel.fixed-nav.top-nav:hover .owl-nav [class*=owl-] { top: -3.25rem; }
  /* style.css:33019 — pagination circle 35px → 44px */
  .pagination { --cnvs-pagination-circle-size: 2.75rem; }
  /* style.css:23841 — .button-small (/tim-kiem/ sort + category toggles, T21) 40px → 44px */
  .button.button-small[aria-pressed] { min-height: 2.75rem; }
  /* news.css:258–263 — home tabs .nav-sm .nav-link 25px high (2px padding + 21px line) → 44px (T23; the toggler is gone) */
  .navbar-nav.nav-sm .nav-link { padding-block: calc((2.75rem - 21px) / 2); }
}

/* ---- Focus indicators (T15 ruling 1, 5): solid 2px --xb-focus ring, no outline ---- */

/* style.css:13786–13789 — :active, :focus { outline: none !important } removes every focus ring → box-shadow ring */
:focus-visible { box-shadow: 0 0 0 2px var(--xb-focus); }
/* style.css:3871–3874 — .nav-link:focus-visible Bootstrap-blue .25 shadow (home tabs) */
.nav-link:focus-visible { box-shadow: 0 0 0 2px var(--xb-focus); }
/* style.css:3044–3049 — .btn:focus-visible box-shadow var(--bs-btn-focus-box-shadow), plain .btn has no focus rgb → none (header
   colour mode switch, T18) */
.btn:focus-visible { box-shadow: 0 0 0 2px var(--xb-focus); }
/* style.css:2204–2210 — .form-control:focus Bootstrap-blue border + .25 shadow (/tim-kiem/ input) */
.form-control:focus { border-color: var(--xb-focus); box-shadow: 0 0 0 2px var(--xb-focus); }
/* style.css:18977–18981 — .top-search-form input box-shadow none / outline 0 !important, border 0 → 2px bottom border */
.top-search-form input:focus-visible { border-bottom: 2px solid var(--xb-focus); }

/* ---- Brand tags + secondary brand CTA (decisions.md §16, T16; §17 R1, T20): outline in the theme colour at rest ---- */

/* style.css:14201 .color / :14205 .border-color (!important) at rest; hover / focus filled = the filled CTA's hover (rule
   above, style.css:23829): --xb-primary-hover + ctaLabel (dark 7.29:1). !important against style.css:33004–33007
   (.tagcloud a:hover border + text !important), :23901–23905 (.button-border:hover border transparent !important) and
   the .color / .border-color utilities */
/* /tim-kiem/ sort + category toggles (T21): same outline button, pressed = the primary fill + ctaLabel (5.56:1); hover rule
   below keeps priority (same specificity, later). !important against the same vendor rules */
.button.button-border.color[aria-pressed="true"] {
  background-color: var(--xb-primary) !important;
  border-color: var(--xb-primary) !important;
  color: var(--xb-cta-label) !important;
}
.tagcloud a.border-color:is(:hover, :focus-visible),
.button.button-border.color:is(:hover, :focus-visible) {
  background-color: var(--xb-primary-hover) !important;
  border-color: var(--xb-primary-hover) !important;
  color: var(--xb-cta-label) !important;
}
/* style.css:8096 — the last brand box disclosure line .mb-5 (48) before the tag rows → major section gap (§13 64 · 40 sm) */
.entry > .promo + p.small:has(+ div > .tagcloud) { margin-bottom: 0 !important; }
.entry > .promo + p.small + div:has(> .tagcloud) { margin-top: calc(var(--xb-section-gap) - .375rem); } /* line descent + heading half-leading */
/* style.css:8096 — brand tag page (topic.njk): disclosure line .mb-5 (48) above the grid → major section gap (§13 64 · 40 sm) */
.col-lg-8 > .promo + p.small:has(+ .row) { margin-bottom: calc(var(--xb-section-gap) - .1875rem) !important; } /* line descent */
/* style.css:9799 — rows touch (.tagcloud margin-bottom -2px :32985, heading .mb-sm-4): brand row → topic row = sidebar widget
   gap (§13 48 · 40 sm), heading half-leading included as .widget above */
.entry > div:has(> .tagcloud) + div:has(> .tagcloud) { margin-top: 2.75rem; }
@media (max-width: 991.98px) {
  .entry > div:has(> .tagcloud) + div:has(> .tagcloud) { margin-top: 2.25rem; }
}

/* ---- Light theme (T18, decisions.md §16 Theme rulings): body without .dark; header / footer stay .dark ---- */

/* style.css:17298 — #header has no background (only #header-wrap, :17350–17355): the top row (social, date) showed the
   navy body; on a light body → the header's own background */
body:not(.dark) #header.dark { background-color: var(--cnvs-header-bg); }
/* style.css:23779 (.button --cnvs-btn-color = theme colour): primary fills keep theme.primary + ctaLabel in both
   themes; the light theme colour is text primary (tokens.css) — outlines in the theme colour (brand tags, secondary brand CTA,
   T20) keep it. news.css:297 /
   :308 .line-home heading rule: decorative, the TPL value needs no contrast → theme.primary too */
body:not(.dark) .button:not(.button-border.color), body:not(.dark) .line-home { --cnvs-themecolor: var(--xb-primary); --cnvs-themecolor-rgb: var(--xb-primary-rgb); }

/* ---- Compact mobile header (T20, decisions.md §17 R7) ---- */

/* style.css:17596 — #header-wrap #logo img height = --cnvs-header-height; the < 992 logo in #header-wrap carries no #logo
   (the top-row logo keeps the id; master ruling T20) → same rule, the small header height (style.css:13313) */
#header-wrap .header-row > a[href="/"] > img { height: var(--cnvs-header-height-sm); }
/* style.css:19062–19066 — open top search fades #logo; the < 992 logo has no id → same fade */
.top-search-open .top-search-parent > a[href="/"] { opacity: 0; }

/* ---- Small-post lists (T20, decisions.md §17 R3; master ruling) ---- */

/* style.css:816–818 (.col flex 1 0 0) + flex min-width auto: the text column's min-content = its widest nowrap meta item
   (custom.css:247, T05) → at 375 the column wrapped under the thumbnail. Thumbnail left, text right at every width */
.posts-sm .grid-inner > .col { min-width: 0; }

/* ---- Post details (T20) ---- */

/* style.css:9513 (.d-sm-flex, author card body ≥ 576) + flex min-width auto: the byline text column would not shrink below
   its longest word / nowrap link → min-width 0, text wraps beside the image */
.card-body > .author-image + div { min-width: 0; }

/* ---- Logo tile (T21, decisions.md §17 T20 answers) ---- */

/* style.css:4417 / :4427 — .card background = --bs-body-bg (white in light): the light-on-dark placeholder logo vanished on
   the light card → fixed navy tile behind the logo in both themes (author card, /gioi-thieu/ operator block). Unconditional
   (T22): the card image is the site logo by definition (decisions.md §15), selected by the card layout, not its file */
.card-body.d-sm-flex > .flex-shrink-0 > img { background-color: var(--xb-bg); }

/* ---- Accents (T22, decisions.md §17 R6): brand green + navy; category colours only on category badges ---- */

/* news.css:296–318 — .line-<c> / .line-<c>::after heading rules in a category colour (!important): decorative heading lines
   (sidebar categories, home sections) → theme colour, as .line-home (news.css:297 / :308; light: theme.primary, as above) */
:is(.line-travel, .line-tech, .line-fashion, .line-sports, .line-lifestyle, .line-food, .line-market) { border-bottom-color: var(--cnvs-themecolor) !important; }
:is(.line-travel, .line-tech, .line-fashion, .line-sports, .line-lifestyle, .line-food, .line-market)::after { border-top-color: var(--cnvs-themecolor) !important; }
body:not(.dark) :is(.line-travel, .line-tech, .line-fashion, .line-sports, .line-lifestyle, .line-food, .line-market) { --cnvs-themecolor: var(--xb-primary); }

/* style.css:25865–26200 — social network fills .bg-<net> / .h-bg-<net>:hover (!important; share bar, author card, footer):
   rest = the navy surface (white glyph), hover = the primary fill + ctaLabel glyph (5.56:1; replaces the white hover glyph
   of style.css:25812 / the T17 rule above) */
.social-icon:is(.bg-facebook, .bg-x-twitter, .bg-whatsapp, .bg-email3, .bg-instagram) { background-color: var(--xb-bg-offset) !important; }
.social-icon:is(.bg-facebook, .bg-x-twitter, .bg-whatsapp, .bg-email3, .bg-instagram, .h-bg-facebook, .h-bg-x-twitter, .h-bg-instagram):hover { background-color: var(--xb-primary) !important; }
.social-icon:is(.bg-facebook, .bg-x-twitter, .bg-whatsapp, .bg-email3, .bg-instagram, .h-bg-facebook, .h-bg-x-twitter, .h-bg-instagram):hover i:last-child { color: var(--xb-cta-label); }

/* style.css:4930–4942 — .alert-success / .alert-info Bootstrap green / cyan (blocks/callout.njk tones tip / info): brand
   surface (dark: bgOffset; light: primary 10% toward white) + brand border, body text. .alert-warning keeps its colours */
.alert-success, .alert-info {
  --bs-alert-color: var(--bs-body-color);
  --bs-alert-bg: var(--xb-callout-bg);
  --bs-alert-border-color: var(--cnvs-themecolor);
  --bs-alert-link-color: var(--cnvs-themecolor);
}

/* style.css:341–345 — mark: Bootstrap highlight (dark brown / light yellow, own text colour) → lever-derived brand tint,
   text inherited (/tim-kiem/ highlights, T21) */
mark, .mark { color: inherit; background-color: var(--xb-mark-bg); }

/* style.css:7062–7068 — .fixed-bottom has no surface → review mobile CTA bar (T24) on the sticky header's navy
   (--cnvs-header-sticky-bg = bgOffset under .dark, tokens.css), both themes like the header */
.fixed-bottom.dark { background-color: var(--cnvs-header-sticky-bg); }
/* style.css:2995–2996 (.btn padding), :23775 (.button) — bar CTA + close button → 44×44 touch targets (§4, T24) */
.fixed-bottom.dark :is(.button, .btn) { min-height: 2.75rem; min-width: 2.75rem; }

/* ---- Layout reservation (T26, master ruling 2026-10-10: CLS < 0.1) ---- */

/* style.css:29181–29183 / :29219–29221 — .owl-carousel is display none until Owl init (js/news.js) → the home featured
   carousel took its box after first paint and pushed the page down (CLS 0.18 desktop / 0.43 mobile). Reserve the initialised
   box, hidden until loaded: slide 400px (inline height, TPL demo-news.html) + dots row < 992 (24px dot button + 22px top
   margin, custom.css "Touch targets"; no dots ≥ 992, js/news.js responsive) */
#oc-news:not(.owl-loaded) { display: block; visibility: hidden; overflow: hidden; height: calc(400px + 22px + 1.5rem); }
@media (min-width: 992px) { #oc-news:not(.owl-loaded) { height: 400px; } }

/* style.css:1460 (.col-lg-10, content height) — /tim-kiem/ results are rendered by /js/search.js after first paint and pushed the
   footer down (CLS 0.48 desktop). The results column (the one holding the .posts-sm template) reserves the form (input-group-lg,
   3rem) + one viewport from the first paint and keeps it (master ruling: always, also without q). Capped at 100rem: a
   viewport resized to the page height (full-page tools, audit-ui) would otherwise grow the reservation with it */
#content .row > div:has(.posts-sm > template) { min-height: min(calc(3rem + 100vh), 100rem); }
