/* ==========================================================================
   BOCSite homepage UI/UX refresh (2026-09) — T7.
   Loaded only via Magento_Theme/layout/cms_index_index.xml, so every rule
   here only ever matches on the homepage. Still scoped to
   `body.cms-index-index` per the uiux-2026-09 selector contract
   (docs/design/uiux-2026-09/tests/HOOKS.md) so nothing here can leak onto
   another page if this stylesheet is ever loaded elsewhere by mistake.
   All new markup hooks use the `boc-home-` prefix. No global bare
   selectors (no `.column.main h2`, no `[data-content-type]`, etc.).
   ========================================================================== */

:root {
  --boc-home-cookie-offset: 0px;
}

/* T10 staging fix round 2 (2026-09-17): `header.page-header`'s placeholder
   height (Magento_Theme/web/tailwind/theme/components/header.css, a
   Tailwind source file this task cannot touch: `h-[104px] lg:h-[161px]`) is
   a static value sized for the ORIGINAL header content, not a live
   JS-measured sync -- so any real content-height change has to be
   reflected here by hand.
   Round 1 used 108/169px, derived from a local Playwright fixture rebuilt
   from a real DOM dump; that fixture's logo placeholder didn't reproduce
   the real logo's rendered height precisely, leaving a 4.75px (mobile) /
   5.625px (desktop) residual. Re-measured directly against live staging
   (https://test.bestonlinecabinets.com) this round, no fixture: isolated
   the discrepancy by toggling `display:none` on each direct child of
   `#header > div.bg-white .container` one at a time and re-reading the
   row's getBoundingClientRect().height -- only the logo wrapper
   (`.flex.items-center.mr-auto...`) changes it (removing it drops the row
   to exactly its 64px min-height floor at both viewports; every icon
   button, the search container, and the mobile nav toggle contribute 0).
   The logo is untouched by this task and was already this size before any
   C1 change, so this is a real pre-existing height this task's icon-target
   work never touched, not a new regression to chase further -- syncing the
   placeholder to it is the correct fix per this round's dispatch. Real
   totals confirmed via direct getBoundingClientRect() on #header itself at
   all three acceptance-test viewports: 112.75px (390x844) and 174.625px
   (1280x800 and 1440x900, identical -- nothing here has a breakpoint
   between 1024px and 1440px). */
body.cms-index-index header.page-header {
  height: 112.75px;
}
@media (min-width: 1024px) {
  body.cms-index-index header.page-header {
    height: 174.625px;
  }
}

/* ===========================================================================
   C1 — top bar / header / nav (design-direction.md C1). `.cmsbNN-*` classes
   are the theme's per-CMS-block compiled utility prefix (theme-sources.md;
   the number is not a DB block id) — cmsb49/cmsb54 were independently
   re-verified against the live homepage during the 2026-09-16 design mock
   (docs/design/uiux-2026-09/homepage/CHANGES.md "现网核验" #1).
   =========================================================================== */
body.cms-index-index #header [class~="cmsb54-min-h-\[36px\]"] {
  min-height: 44px;
}
/* T10 staging fix (2026-09-17): tel:/mailto: used to get `min-height:44px;
   display:inline-flex;align-items:center`, which genuinely grows the row
   these links sit in (measured +28px on the top bar alone). A first fix
   attempt tried plain top/bottom padding on the theory that inline padding
   doesn't affect line-box height -- but these links live inside
   `<li class="cmsb54-flex cmsb54-items-center">`/`<ul class="cmsb54-flex
   ...">`, i.e. a FLEX context, not normal inline flow: flex blockifies
   inline children, so their full padding box does count toward the flex
   item's (and therefore the row's) height after all. An absolutely
   positioned ::before overlay is genuinely removed from flex/flow layout
   regardless of container type, so it's the only technique that reliably
   gives a 44px-tall click target with zero height contribution. Width
   matches the link's own rendered width (100%) with a 44px floor, so the
   already-wide text links don't get their horizontal target shrunk to a
   centered square. font-size/weight/line-height/color are unchanged (still
   the design-direction.md values) -- only the invisible hit-area changes. */
body.cms-index-index #header a[href^="tel:"],
body.cms-index-index #header a[href^="mailto:"] {
  position: relative;
}
body.cms-index-index #header a[href^="tel:"]::before,
body.cms-index-index #header a[href^="mailto:"]::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  min-width: 44px;
  height: 44px;
}
body.cms-index-index #header a[href^="tel:"] {
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  color: #fff;
}
body.cms-index-index #header a[href^="mailto:"] {
  font-size: 12px;
  font-weight: 400;
  line-height: 16px;
  color: #D1D5DB;
}
/* Sale / Professional Discount demoted to ink; Free Design keeps its own
   deep-orange menu trigger untouched (no href/behavior change here). */
body.cms-index-index #header .cmsb49-text-primary {
  color: #111111;
}
body.cms-index-index #header .cmsb49-text-primary svg * {
  stroke: #111111;
}
/* The real main-header row (logo + search/wishlist/account/cart icons) --
   confirmed selector via a live staging DOM dump, see T7 report. */
body.cms-index-index #header > div.bg-white {
  min-height: 64px;
}
@media (min-width: 1024px) {
  body.cms-index-index #header nav a.level-0 {
    font-size: 16px;
    font-weight: 400;
    line-height: 24px;
  }
}
/* T10 staging fix (2026-09-17): the previous `#header button` rule matched
   every button in the header indiscriminately (search/cart/hamburger/
   account), forcing each from its native 24x24 (mobile) / 32x32 (>=768px)
   box straight to 44x44 -- confirmed via a live staging DOM dump to be the
   dominant cause of the ~28-34px header-height regression (all 4 sit in
   the same flex row, so the row's height tracks its tallest child). These
   are precise real selectors (search/cart/account are IDs; hamburger's
   aria-label and .wishlist-link were confirmed against the live DOM too),
   and only the vertical box grows -- width/horizontal layout is completely
   untouched, so this can't affect the gap-6 spacing between these icons.
   box-sizing:border-box (global preflight) means padding alone would eat
   into the existing fixed h-6/h-8 height instead of expanding it, so
   height is set explicitly too; the negative margin retracts the added
   padding from the flex row's own height calculation, leaving the row's
   participating height exactly as it was before this rule existed. */
@media (max-width: 767.98px) {
  body.cms-index-index #menu-search-icon,
  body.cms-index-index #menu-cart-icon,
  body.cms-index-index #customer-menu,
  body.cms-index-index #header .wishlist-link,
  body.cms-index-index #header [aria-label="Open menu"] {
    height: 44px;
    padding-top: 10px;
    padding-bottom: 10px;
    margin-top: -10px;
    margin-bottom: -10px;
    box-sizing: border-box;
  }
}
@media (min-width: 768px) {
  body.cms-index-index #menu-search-icon,
  body.cms-index-index #menu-cart-icon,
  body.cms-index-index #customer-menu,
  body.cms-index-index #header .wishlist-link,
  body.cms-index-index #header [aria-label="Open menu"] {
    height: 44px;
    padding-top: 6px;
    padding-bottom: 6px;
    margin-top: -6px;
    margin-bottom: -6px;
    box-sizing: border-box;
  }
}

/* C1 — header search icon: vertically center the submit button inside the
   44px-tall search input instead of relying on a fixed top-2 offset. */
body.cms-index-index #header #search_mini_form button[type="submit"] {
  top: 50%;
  transform: translateY(-50%);
}

/* ===========================================================================
   C2 — hero banner slider (Aureatelabs_Banner::banners.phtml)
   =========================================================================== */
@media (min-width: 768px) {
  /* The row is what needs the fixed height; the image wrapper keeps its
     existing `relative` positioning and is stretched to match via the
     slide's default flex align-items:stretch (`.splide__slide` carries
     `md:flex`) — no position:absolute on the wrapper itself. Doing that
     instead (tried during the design mock) triggers CSS2.1 over-constraint
     with the wrapper's own non-!important `md:w-1/2` and pins it to the
     left, on top of the text column. */
  body.cms-index-index #banner-slider .splide__slide {
    min-height: 440px;
  }
  body.cms-index-index #banner-slider .boc-home-hero-media {
    padding-bottom: 0;
    height: auto;
  }
  body.cms-index-index #banner-slider .boc-home-hero-media img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
  }
  /* Desktop text panel: force the solid dark background regardless of
     whether the existing `md:bg-[#313131]` Tailwind utility actually made
     it into the compiled bundle (unverified locally — see T7 report). */
  body.cms-index-index #banner-slider .boc-home-hero-text {
    background-color: #313131;
  }
}
@media (max-width: 767.98px) {
  body.cms-index-index #banner-slider .splide__slide {
    min-height: 400px;
  }
  /* Mobile text panel sits on top of the photo; add the 65% black scrim
     (replaces the template's own bg-black/50). */
  body.cms-index-index #banner-slider .boc-home-hero-text {
    background-color: rgba(0, 0, 0, 0.65);
  }
}
/* CTA link: markup/copy/href come from the CMS-authored banner description
   (`getCmsFilterContent`), so it can only be reached structurally via the
   .boc-home-hero-text-inner wrapper — no assumption is made about its own
   class names. */
body.cms-index-index #banner-slider .boc-home-hero-text-inner a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 10px 24px;
  border-radius: 8px;
  background-color: #C2410C;
  color: #fff;
}
body.cms-index-index #banner-slider .boc-home-hero-text-inner a:hover {
  background-color: #9A3412;
}
/* Pagination dots: 44x44 click target, 8px visible dot on ::before. The
   button itself must stay transparent with no min-width/min-height, or the
   enlarged hit area paints as a big visible circle. */
body.cms-index-index #banner-slider .splide__pagination__page {
  width: 44px;
  height: 44px;
  min-width: 0;
  min-height: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
body.cms-index-index #banner-slider .splide__pagination__page::before {
  content: "";
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: transparent;
  border: 1px solid #fff;
  box-sizing: border-box;
}
body.cms-index-index #banner-slider .splide__pagination__page.is-active::before {
  background: #fff;
  border-color: #fff;
}

/* ===========================================================================
   C3 — cookie / chat / back-to-top vertical stacking
   #boc-chat-launcher is injected at runtime by the live-chat snippet (not a
   theme template); its own resting bottom is 15px (see
   docs/ops/live-chat-migration-2026-09/freshworks-live-chat-snippet.v2-identity-gate.html).
   #back-to-top is Magento_Theme::html/footer.phtml, left untouched — the ID
   selector is already a sufficient hook so no class was added there.
   =========================================================================== */
body.cms-index-index #boc-chat-launcher {
  bottom: calc(15px + var(--boc-home-cookie-offset, 0px));
}
body.cms-index-index #back-to-top {
  width: 44px;
  height: 44px;
  right: 24px;
  bottom: calc(88px + var(--boc-home-cookie-offset, 0px));
}

/* ===========================================================================
   C4 — six style circles (#home-categories-slider CMS content, T8-owned).
   Visual only: fixed circle diameter decoupled from whatever width Splide
   computes for the slide itself (T8's JS config), plus a clear focus style.
   No !important, no touching the slide width/gap.
   =========================================================================== */
body.cms-index-index #home-categories-slider .product-item-photo {
  padding-bottom: 0;
  margin-left: auto;
  margin-right: auto;
}
@media (min-width: 768px) {
  body.cms-index-index #home-categories-slider .product-item-photo {
    width: 112px;
    height: 112px;
  }
}
@media (max-width: 767.98px) {
  body.cms-index-index #home-categories-slider .product-item-photo {
    width: 80px;
    height: 80px;
  }
}
body.cms-index-index #home-categories-slider label {
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
}
body.cms-index-index #home-categories-slider a:focus-visible {
  outline: 3px solid #9A3412;
  outline-offset: 2px;
  border-radius: 8px;
}

/* ===========================================================================
   C5 — intro / features / Refresh / Milestones (design-direction.md C5
   table). Container hooks (.boc-home-intro, .boc-home-chips/-chip/-label/
   -body, .boc-home-features, .boc-home-refresh-caption,
   .boc-home-milestones) are added to the CMS block content by T8
   (docs/ops/home-uiux-2026-09/update_home_blocks.php); this is the
   corresponding visual layer only.
   =========================================================================== */
/* T-fix (2026-09-17): Matt's staging screenshot review flagged the intro's
   two stacked kitchen photos and the "Buy Kitchen Cabinets Online" copy as
   touching with almost no breathing room. update_home_blocks.php's
   transformIntroChips() confirms the real wrapper class this hook lands on
   is `flex py-50 md:flex-nowrap flex-wrap items-center boc-home-intro` --
   i.e. `.boc-home-intro` IS the flex row (image column + text column as
   direct flex children, wrapping to stacked on mobile via the bare
   `flex-wrap`, single row from Tailwind's default md=768px breakpoint via
   `md:flex-nowrap`) with no gap declared anywhere, which is exactly why the
   two columns sit flush. A `gap` on the flex container spaces every direct
   child uniformly in both the wrapped (mobile, vertical) and unwrapped
   (desktop, horizontal) cases, so one property covers both viewports.
   32px/48px chosen to sit inside this same rule's existing padding-top/
   bottom values (a similar mobile/desktop scaling to the file's other C5
   spacing pairs, e.g. .boc-home-features 24/32, .boc-home-milestones
   32/64). NOTE: could not get a fresh Playwright screenshot against
   https://test.bestonlinecabinets.com/ to pixel-verify this round --
   staging basic-auth credential resolution via --auth-from-handoff still
   fails for the reason docs/design/uiux-2026-09/tests/README.md's "Known
   gap" describes, and no BOC_AUTH was available either; re-check visually
   before merge. */
body.cms-index-index .boc-home-intro {
  padding-top: 36px;
  padding-bottom: 36px;
  gap: 32px;
}
body.cms-index-index .boc-home-intro h1 {
  color: #111111;
  font-size: 28px;
  font-weight: 700;
  line-height: 34px;
}
/* Real intro fixture is h1, h2, p (docs/ops/home-uiux-2026-09/
   test_update_home_blocks.php $FIXTURE_INTRO) -- an h2 sits between the h1
   and the paragraph, so `h1 + p` never matches. Target the paragraph
   directly (the block's content has exactly one). */
body.cms-index-index .boc-home-intro p {
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  color: #111111;
  margin-top: 16px;
}
@media (min-width: 1024px) {
  body.cms-index-index .boc-home-intro {
    padding-top: 48px;
    padding-bottom: 48px;
    gap: 48px;
  }
  body.cms-index-index .boc-home-intro h1 {
    font-size: 32px;
    line-height: 38px;
  }
}
body.cms-index-index .boc-home-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}
/* T-fix (2026-09-17, round 3) retired: .boc-home-chips is now reparented
   into the intro's text column by JS in home-uiux-2026-09.phtml, so it no
   longer needs a positioning hack here. */
body.cms-index-index .boc-home-chip {
  flex: 0 0 auto;
  background: #F9FAFB;
  border: 1px solid #E5E7EB;
  border-radius: 8px;
  padding: 8px 12px;
}
body.cms-index-index .boc-home-chip-label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  color: #111111;
}
body.cms-index-index .boc-home-chip-body {
  display: block;
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  color: #6B7280;
}
body.cms-index-index .boc-home-features {
  margin-top: 24px;
  padding-top: 12px;
  padding-bottom: 12px;
}
@media (min-width: 1024px) {
  body.cms-index-index .boc-home-features {
    margin-top: 32px;
  }
}
body.cms-index-index #home-kitchen-space h2 {
  font-size: 24px;
  font-weight: 700;
  line-height: 30px;
  color: #111111;
}
@media (min-width: 1024px) {
  body.cms-index-index #home-kitchen-space h2 {
    font-size: 28px;
    line-height: 34px;
  }
}
/* styles.css's base .splide__arrow{border:0} has no !important, but it does
   reset border-width/style to nothing -- setting only border-color isn't
   enough to actually paint a border. Likewise .splide__arrow svg{fill:#000}
   is a literal SVG fill, not `currentColor`, so `color` alone doesn't
   recolor the icon; fill has to be set explicitly (same pitfall the C1
   header rules above handle via `svg * { stroke }`). */
body.cms-index-index #home-kitchen-space .splide__arrow {
  width: 44px;
  height: 44px;
  border-width: 1px;
  border-style: solid;
  border-color: #8A8F98;
  background: #fff;
}
body.cms-index-index #home-kitchen-space .splide__arrow svg {
  fill: #C2410C;
}
/* Refresh card caption: solid dark strip guarantees the contrast regardless
   of the underlying photo. */
body.cms-index-index .boc-home-refresh-caption {
  background: #313131;
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  padding: 8px 12px;
}
body.cms-index-index .boc-home-milestones {
  margin-top: 32px;
  margin-bottom: 40px;
  padding: 24px;
}
@media (min-width: 1024px) {
  body.cms-index-index .boc-home-milestones {
    margin-bottom: 64px;
    padding: 48px;
  }
}
body.cms-index-index .boc-home-milestones h2 {
  font-size: 24px;
  font-weight: 700;
  line-height: 30px;
}
@media (min-width: 1024px) {
  body.cms-index-index .boc-home-milestones h2 {
    font-size: 28px;
    line-height: 34px;
  }
}
/* T-fix (2026-09-17, round 3): flat bg-black/bg-opacity-80 tint replaced
   with a left-to-right gradient per Matt's staging feedback ("遮罩半透明都
   比现在好看" -- the flat, uniformly-opaque rectangle read as generic/
   undesigned, not a deliberate treatment). The tint lives on the SAME
   element that lays out the section's two columns (confirmed real markup:
   docs/ops/home-uiux-2026-09/apply-stage-20260916-220940-milestones.txt) --
   `<div class="md:flex bg-black bg-opacity-80 p-6 items-center md:px-100
   md:py-20 z-[1] relative">`, the direct child of `#counter.boc-home-
   milestones` sitting over the absolutely-positioned background photo.
   There is no separate overlay div and no dedicated hook class on it, so
   this keys off its own always-present (non-arbitrary) `bg-black`/
   `bg-opacity-80` utility classes as the selector; the compound 3-class
   selector already outranks Tailwind's own single-class `.bg-black`/
   `.bg-opacity-80` rules on specificity alone, so no !important is needed.
   Direction is left-to-right (text column is left, stat numbers are right,
   per the markup order above) so the photo shows through more on the
   right, near the numbers, while the left stays dark enough for the
   title/subtitle to stay fully readable. */
body.cms-index-index .boc-home-milestones .bg-black.bg-opacity-80 {
  background: linear-gradient(
    to right,
    rgba(17, 17, 17, 0.92) 0%,
    rgba(17, 17, 17, 0.75) 55%,
    rgba(17, 17, 17, 0.35) 100%
  );
}
/* T8's transform keeps the 4 stat numbers' existing class
   (text-themePrimary mb-2) and only swaps their tag from <h2> to <p> --
   there is no separate boc-home-milestone-stat class on them. text-shadow
   added (round 3) to keep the stat numbers legible now that the mask
   fades toward transparent on the right, where they sit. */
body.cms-index-index .boc-home-milestones p.text-themePrimary {
  font-size: 32px;
  font-weight: 700;
  line-height: 38px;
  color: #fff;
  display: block;
  margin: 0 0 8px;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
/* Astra design-fix (2026-09-17): "looks like a table" redesign. The stat
   grid's real markup (block 46 / hyva_boch_home_quality_of_work, a CMS
   block -- see docs/ops/home-uiux-2026-09/apply-stage-20260916-220940-
   milestones.txt for the exact applied HTML) is
   `<div class="... grid grid-cols-2 border-secondary-darker border">` with
   each of the 4 stat cells adding its own border-secondary-darker plus
   border-l/border-t for the white divider lines, and each label is
   `<p class="text-white mb-0">...</p>` -- there is no dedicated label class,
   and .text-white is shared with the section's own <h2> title, so the type
   selector (p.text-white, not h2.text-white) is required to avoid touching
   the heading. This content lives in a Magento CMS block content column,
   not a git-tracked template, so the fix targets the classes that content
   already carries rather than restructuring markup. */
body.cms-index-index .boc-home-milestones .border-secondary-darker {
  border-width: 0;
}
body.cms-index-index .boc-home-milestones .grid.grid-cols-2 {
  column-gap: 16px;
  row-gap: 24px;
}
body.cms-index-index .boc-home-milestones p.text-white {
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  color: #D1D5DB;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
/* xl = 1280px in tailwind.config.js -- the design's explicit "desktop"
   threshold for this section (this file's other C5 rules use the 1024px
   lg breakpoint instead, so 1280px here is intentional, not a typo). Below
   this the 2x2 grid above stays, just without the old table-style borders.
   Verification note (static-fixture check, see task report): the stats
   grid only occupies the right half of this section (pre-existing
   md:w-1/2 split against the left text column, untouched here), so a
   42px-bold 6-digit stat in a true one-row layout has little headroom on
   a 1280-1440px viewport. flex-wrap:wrap (not nowrap) is a deliberate
   safety net -- it still renders one row wherever the content fits, and
   degrades to wrapping instead of clipping/overflowing if a given
   viewport is tighter than expected; worth a real staging check once
   deployed. */
@media (min-width: 1280px) {
  /* T-fix (2026-09-17, staging screenshot review): the CMS block's own
     markup carries a fixed `h-[380px]` + `overflow-hidden` on this exact
     element (confirmed live: getComputedStyle height=380px, overflow=
     hidden). That budget was sized for the original single-row-of-labels
     table layout; once the 4 stats wrap to two rows at this breakpoint
     (see the flex-wrap note above -- the stats column is only the right
     half of this box, ~600px, too narrow for four 42px-bold 6-digit
     numbers in one true row), the second row's content is pushed past
     380px and the top row is clipped clean off (verified: "Kitchen
     Design"/"Kitchens Served" numbers were invisible above the box's top
     edge). Overriding to `height:auto` + a taller `min-height` lets the
     box grow to fit two full rows plus the heading; the background image
     is `absolute;inset-0;object-cover` (confirmed in the applied CMS
     content), so it stretches to cover whatever height results --
     `overflow:hidden` is left untouched to keep the intended rounded-xl
     image crop. */
  body.cms-index-index .boc-home-milestones {
    height: auto;
    min-height: 460px;
    padding: 64px 48px;
  }
  body.cms-index-index .boc-home-milestones .grid.grid-cols-2 {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 48px;
  }
  body.cms-index-index .boc-home-milestones p.text-themePrimary {
    font-size: 42px;
    line-height: 48px;
  }
  body.cms-index-index .boc-home-milestones p.text-white {
    font-size: 16px;
    line-height: 22px;
  }
}

/* ===========================================================================
   C6 — product cards under #landing-category-wrapper
   (Aureatelabs_FurnitureLayout::category-slider.phtml, homepage only)
   =========================================================================== */
/* Price-area min-height (label20 + gap4 + price34/38 + gap4 + reg20) keeps
   every card in a row the same height whether or not it has a discount, so
   the action buttons below stay aligned -- design-direction.md:114. */
/* Astra design-fix (2026-09-17, round 3): "Basic Kitchen Starting From"
   (category-slider.phtml's bare `<div><?= $priceLabel ?></div>`, no class
   hook, no margin) and the price row below it had zero gap between them --
   margin-top here gives the requested 8-12px breathing room. */
body.cms-index-index #landing-category-wrapper .price-label {
  min-height: 82px;
  margin-top: 10px;
}
@media (min-width: 1024px) {
  body.cms-index-index #landing-category-wrapper .price-label {
    min-height: 86px;
  }
}
/* Astra design-fix (2026-09-17): the actual-price size/weight now applies
   whether or not a card has a discount -- category-slider.phtml's
   non-discount branch is also gated onto .boc-home-price-now (isHomepage
   only) so both branches share one visual weight. Default color is the
   no-discount case (#111111); the discount case is reached below via the
   adjacent-sibling selector, since .boc-home-price-reg only precedes
   .boc-home-price-now in the DOM when a discount exists. */
body.cms-index-index #landing-category-wrapper .boc-home-price-now {
  font-size: 28px;
  font-weight: 800;
  line-height: 34px;
  color: #111111;
}
@media (min-width: 1024px) {
  body.cms-index-index #landing-category-wrapper .boc-home-price-now {
    font-size: 32px;
    line-height: 38px;
  }
}
body.cms-index-index #landing-category-wrapper .boc-home-price-reg {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: #6B7280;
}
/* The visible price digits live on a nested <span class="price text-16
   lg:text-18 ... font-medium"> one level inside .boc-home-price-reg (see
   category-slider.phtml), not on .boc-home-price-reg itself -- a rule on
   the wrapper can't override a property the nested span's own matched
   Tailwind classes already set. Target that inner span directly instead.
   color is needed too: the inner span's own text-666666 Tailwind class
   sets color directly on itself, which an inherited color from the
   wrapper can never beat -- only an explicit rule on the span itself can. */
body.cms-index-index #landing-category-wrapper .boc-home-price-reg .price {
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  color: #6B7280;
}
/* Astra design-fix (2026-09-17): this selector only matches the discounted
   final price (.boc-home-price-reg is only rendered right before it in the
   discount branch of category-slider.phtml) -- no second modifier class
   needed on the template side. */
body.cms-index-index #landing-category-wrapper .boc-home-price-reg + .boc-home-price-now {
  color: #B91C1C;
}
/* Astra design-fix (2026-09-17): the discount badge was pulled out of flow
   on mobile via the template's own max767:absolute/top-1/right-1 utility
   classes (max767 = max-width:767px, tailwind.config.js screens), which is
   what made it float in the card's corner instead of following the price.
   This rule is unconditional (no media query) and beats those utilities on
   specificity alone (body.cms-index-index + #landing-category-wrapper +
   .discount-percentage outranks any single Tailwind utility class), so the
   badge stays in the .price-label flex flow at every width; desktop's own
   inline placement (md:ml-auto, not absolute) is untouched. Once it's a
   normal flex item it would otherwise stretch to the height of whichever
   row it wraps onto (.price-label has no align-items, so flex's stretch
   default applies) -- align-self keeps it a compact pill instead. */
body.cms-index-index #landing-category-wrapper .discount-percentage {
  position: static;
  display: inline-block;
  align-self: center;
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  color: #C2410C;
  background: #FFF7F1;
  padding: 4px 8px;
  border-radius: 8px;
  border: 0;
}
/* Astra design-fix (2026-09-17, round 3): the badge previously sat inline
   with the price digits inside .price-label's flex-wrap row, which made
   the row's line height uneven ("挤在一起" per Matt's round-3 feedback).
   .discount-percentage-row is a new wrapping <span> (category-slider.phtml)
   whose only job is to force a flex-wrap line break so the badge lands on
   its own row below the price; .discount-percentage's own declared rules
   (above) are untouched. Its EFFECTIVE position is not unaffected, though:
   wrapping it removes it from being a direct flex item of .price-label, so
   its own `md:ml-auto` (a flex-context auto-margin, round-2's deliberate
   flush-right placement on desktop) would resolve to 0 in plain block flow
   -- code-reviewer round 1 caught this. The rule below restores that same
   right-alignment at the same >=768px breakpoint the template's `md:`
   prefix already targets, now on the wrapping row instead. */
body.cms-index-index #landing-category-wrapper .discount-percentage-row {
  flex: 1 0 100%;
  margin-top: 8px;
}
@media (min-width: 768px) {
  body.cms-index-index #landing-category-wrapper .discount-percentage-row {
    display: flex;
    justify-content: flex-end;
  }
}

/* ===========================================================================
   C7 — testimonials. `.home-faqs` is the wrapping CMS block (T8:
   c7_faq_hooks, docs/ops/home-uiux-2026-09/update_home_blocks.php) that
   contains the eyebrow/heading/rating-bar and embeds the
   Magebees_Testimonial::testimonial.phtml widget (#home-faqs id) beneath
   it — two different elements sharing a similar name.
   =========================================================================== */
body.cms-index-index .home-faqs {
  padding-top: 40px;
  padding-bottom: 40px;
}
@media (min-width: 1024px) {
  body.cms-index-index .home-faqs {
    padding-top: 64px;
    padding-bottom: 64px;
  }
}
body.cms-index-index .boc-home-faq-eyebrow {
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  color: #6B7280;
}
body.cms-index-index .boc-home-faq-title {
  color: #111111;
  font-size: 24px;
  font-weight: 700;
  line-height: 30px;
}
@media (min-width: 1024px) {
  body.cms-index-index .boc-home-faq-title {
    font-size: 28px;
    line-height: 34px;
  }
}
/* The real rating-bar markup (T8's transformFaqHooks only adds the
   .boc-home-faq-rating class; every <a>/<span> inside it keeps its
   pre-existing inline style="..." from the CMS content -- see
   docs/ops/home-uiux-2026-09/dry-run-stage-20260916-214324.txt). Inline
   styles beat any non-!important class, so every override below needs
   !important. Also: don't set font-size on the anchor itself -- the value
   span's own inline font-size is `em`-relative to the anchor, so bumping
   the anchor's font-size would inflate the value far past the intended
   28px. Target the value span (2nd of the 3 children: star, value,
   source) directly with an absolute px !important instead. */
body.cms-index-index .boc-home-faq-rating a {
  color: #111111 !important;
  min-height: 44px;
}
body.cms-index-index .boc-home-faq-rating a span:first-child {
  color: #C2410C !important;
}
body.cms-index-index .boc-home-faq-rating a span:nth-child(2) {
  font-size: 28px !important;
  font-weight: 700 !important;
  line-height: 34px !important;
}
body.cms-index-index .boc-home-faq-rating a span:last-child {
  font-size: 14px !important;
  font-weight: 400 !important;
  color: #6B7280 !important;
}
/* Card: white bg, 1px line, radius8 already present on .testimonial-col;
   remove the shared .home-interior-col grid's forced 275px min-height so
   the card follows its own natural/slider-row height instead. */
body.cms-index-index .testimonial-col.home-interior-col {
  min-height: 0;
}
body.cms-index-index .testimonial-col p {
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  color: #111111;
}
body.cms-index-index .clientname p {
  font-size: 16px;
  font-weight: 600;
  line-height: 24px;
}
body.cms-index-index #home-faqs .boc-home-avatar {
  width: 40px;
  height: 40px;
}

/* ===========================================================================
   C8 — footer. .boc-home-footer-chip is prepended (default display:none via
   an unscoped <style> tag baked into the shared hyva_boch_footer content) by
   T8 (docs/ops/home-uiux-2026-09/update_home_blocks.php,
   transformFooterChip) specifically so this homepage-scoped rule is what
   makes it visible — every other page keeps display:none. .page-footer and
   .footer-title are confirmed real selectors (HOOKS.md "already exists"
   list); the remaining footer selectors below were validated against the
   live homepage during the 2026-09-16 design mock (CHANGES.md item 5) but
   not independently re-verified here — see the T7 report.
   =========================================================================== */
/* T-fix (2026-09-17): Matt's staging screenshot review flagged this as a
   small, left-aligned, light-bordered card floating alone in open space
   between the testimonials section and the dark footer -- reads as
   unfinished, not a deliberate trust badge. update_home_blocks.php's
   transformFooterChip() confirms this markup is inserted immediately
   before the rest of the hyva_boch_footer block's own content (the chip is
   NOT nested inside that content), and
   docs/design/uiux-2026-09/homepage/CHANGES.md's "实施踩坑" #2 confirms the
   real footer content further down is `.footer-wrap` (a `flex flex-wrap`
   row containing `.footer-left`'s ~500px logo/social/contact/phone
   column) -- the original max-width:400px/align-self:flex-start compact-
   card treatment was chosen there specifically to avoid the chip being
   flex-stretched to that row's height, not to solve today's
   "looks disconnected from the footer" complaint. Restyled as a
   full-width dark bar that reuses this file's existing #313131 dark tone
   (see .boc-home-hero-text / .boc-home-refresh-caption above) and sits
   flush against whatever immediately follows it (margin-bottom:0
   replacing the old 24/32px gap), so it reads as the top edge of the
   footer rather than an orphaned card, per the task's "跟深色 footer 有视觉
   连接...横跨容器宽度的条状" options. align-self:flex-start is kept as a
   defensive no-op in case this still lands as a flex child somewhere,
   matching the documented pitfall above. Copy and hook classes are
   unchanged -- visual presentation only.
   NOTE: could not get a fresh Playwright screenshot against
   https://test.bestonlinecabinets.com/ to pixel-verify this round --
   staging basic-auth credential resolution via --auth-from-handoff still
   fails for the reason docs/design/uiux-2026-09/tests/README.md's "Known
   gap" describes, and no BOC_AUTH was available either; re-check visually
   before merge, especially whether this chip is currently a flex child of
   `.footer-wrap` or a preceding sibling of it. */
body.cms-index-index .boc-home-footer-chip {
  display: flex;
  flex: 1 1 100%;
  width: 100%;
  max-width: none;
  align-self: flex-start;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  column-gap: 8px;
  row-gap: 2px;
  text-align: center;
  background: #313131;
  border: 0;
  border-radius: 0;
  padding: 20px 24px;
  margin-bottom: 0;
}
@media (min-width: 1024px) {
  body.cms-index-index .boc-home-footer-chip {
    padding: 24px 48px;
  }
}
body.cms-index-index .boc-home-footer-chip-label {
  display: inline;
  font-size: 16px;
  font-weight: 600;
  line-height: 24px;
  color: #fff;
}
body.cms-index-index .boc-home-footer-chip-body {
  display: inline;
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  color: #D1D5DB;
  margin-top: 0;
}
body.cms-index-index .footer-title {
  color: #fff;
  font-size: 16px;
  font-weight: 700;
  line-height: 24px;
}
body.cms-index-index .page-footer .footer-links a,
body.cms-index-index .page-footer .footer-col a {
  color: #D1D5DB;
  font-size: 14px;
  font-weight: 400;
  line-height: 22px;
}
body.cms-index-index .page-footer .footer-links a:hover,
body.cms-index-index .page-footer .footer-col a:hover {
  color: #fff;
  text-decoration: underline;
}
/* T-fix (2026-09-17, round 10): Matt flagged the bordered-pill treatment
   here as looking worse than the footer's original plain phone number,
   while separately asking for the number to stand out more -- drop the
   box (border/padding/radius) and let it stand out via size/weight
   against the surrounding text instead. No `color` override (the anchor
   already carries Tailwind's `text-white`/`hover:text-themePrimary`; a
   `color` here would out-specificity the hover utility and kill the
   existing hover-to-orange feedback). The 44px touch target stays --
   this isn't incidental C1 scaffolding, it's the shipped A17/C8 audit
   item (docs/design/home-audit-2026-09/audit.md, "电话点击" metric), a
   product decision this task doesn't have standing to retire. */
body.cms-index-index .footer-left a[href^="tel:"] {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: 16px;
  font-weight: 700;
  line-height: 24px;
}
body.cms-index-index .footer-left .social-icon a {
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* T-fix (2026-09-17, round 4): round 3's equal-flex fallback (`flex: 1 1 0`
   on every .footer-col) evenly split the row when the CMS content's
   Tailwind arbitrary-value utilities failed to compile (see round 3's
   prefix-drift note above, still the reason this fallback exists at all),
   but Matt flagged the result as visibly worse than the original -- the
   real production footer (bestonlinecabinets.com/kitchen-cabinets.html,
   compiled prefix intact) is NOT four equal columns. DOM measurement at
   1280px against that reference confirmed: col1 is a fixed 18.5%, col2 is
   25% at 1024-1279px narrowing to 19.5% at >=1280px, col3/col4 are
   `width:auto` (content-sized, not stretched) with the row's own
   `justify-content:space-between` turning their slack into gap instead of
   width, and the second row's trailing `.footer-col-last` is ~41% capped
   at a max-width (310px / 360px at the same breakpoint). The rules below
   replace the equal-split with `:nth-child` position hooks that recreate
   those real proportions directly -- each column's flex-basis is pinned to
   a value that can't be hijacked by prefix drift: an explicit percentage
   for the columns with a real competing Tailwind width utility to lose
   (including `.footer-col-last`'s 41%, clamped down by `max-width`), or
   `auto` for col3/col4, which have no such utility to begin with (they're
   genuinely content-sized in production). None of them fall back to
   reading the underlying, non-`!important` Tailwind `width` utility as
   their flex-basis, so the whole block stays correct even when that
   utility fails to compile. */
@media (min-width: 1024px) {
  body.cms-index-index .footer-wrap {
    display: flex !important;
    flex-wrap: nowrap !important;
  }
  body.cms-index-index .footer-wrap > .footer-left {
    width: 23% !important;
    flex: 0 0 23% !important;
    padding-right: 20px !important;
    box-sizing: border-box !important;
  }
  body.cms-index-index .footer-wrap > .footer-right {
    width: 77% !important;
    flex: 1 1 auto !important;
    min-width: 0 !important;
  }
  body.cms-index-index .footer-wrap > .footer-right .footer-row {
    display: flex !important;
    flex-wrap: nowrap !important;
    justify-content: space-between !important;
  }
  body.cms-index-index .footer-wrap > .footer-right .footer-row-second {
    display: flex !important;
    flex-wrap: wrap !important;
    justify-content: space-between !important;
  }
  body.cms-index-index .footer-wrap > .footer-right .footer-row > .footer-col:nth-child(1),
  body.cms-index-index .footer-wrap > .footer-right .footer-row-second > .footer-col:nth-child(1) {
    flex: 0 0 18.5% !important;
  }
  body.cms-index-index .footer-wrap > .footer-right .footer-row > .footer-col:nth-child(2),
  body.cms-index-index .footer-wrap > .footer-right .footer-row-second > .footer-col:nth-child(2) {
    flex: 0 0 25% !important;
  }
  body.cms-index-index .footer-wrap > .footer-right .footer-row > .footer-col:nth-child(3),
  body.cms-index-index .footer-wrap > .footer-right .footer-row > .footer-col:nth-child(4) {
    flex: 0 0 auto !important;
  }
  body.cms-index-index .footer-wrap > .footer-right .footer-row-second > .footer-col-last {
    flex: 0 1 41% !important;
    max-width: 310px !important;
  }
}
@media (min-width: 1280px) {
  body.cms-index-index .footer-wrap > .footer-right .footer-row > .footer-col:nth-child(2),
  body.cms-index-index .footer-wrap > .footer-right .footer-row-second > .footer-col:nth-child(2) {
    flex: 0 0 19.5% !important;
  }
  body.cms-index-index .footer-wrap > .footer-right .footer-row-second > .footer-col-last {
    max-width: 360px !important;
  }
}
