/* ===== frontend-elements.css — OKAMI CMS Builder block-render classes (V.104 CSS split) =====
   Split out of the former app.css. Every .builder-* class here renders
   real page CONTENT on the public site (carousel, stats bar, testimonials,
   product/category cards, FAQ, reviews, gallery, video, downloads, inquiry
   form, frontend nav-menu rendering — distinct from the admin menu-items
   editor in admin-shell.css). Load on every frontend page. */

/* V.103 (DECISIONS.md #111) — the Phase 09 "Universal Builder editor"
   3-panel CSS (palette/canvas/settings) that used to live here was
   confirmed 100% dead: zero references anywhere in app/, resources/, or
   assets/js/ outside this stylesheet itself. The real page builder that
   shipped (assets/js/page-builder.js + resources/views/admin/
   page-settings/builder.php) uses an entirely different `pb-*` class
   prefix — this was a first, abandoned implementation attempt,
   superseded before ever being wired to a real screen. Removed rather
   than kept as unreachable dead weight.
*/

/* Real rendered builder output (admin Preview route today; Phase 11's
   public frontend from then on — BuilderElements::render() is the single
   source for both). */
/* Round M mobile audit — `PageTemplates::columns()` (used by Homepage's
   feature row, Product/Service's main+sticky-quote-form layout, and
   Contact's info+form layout) had NO responsive collapse at all, and CSS
   grid items default to `min-width:auto` — meaning a column whose content
   (a wide form, an unbreaking breadcrumb) has an intrinsic width wider
   than its `1fr` track refuses to shrink, forcing the whole grid, and the
   page, wider than the viewport. Confirmed via real screenshots at 375px:
   Homepage/Product/Contact all overflowed horizontally; Shop/Category
   didn't (`.shop-layout` already had its own mobile collapse — this is
   the same fix, extended to every other 2/3/4-column layout that shares
   this one class). `min-width:0` lets a column shrink to fit its track at
   any width; the ≤700px collapse to a single stacked column (this
   codebase's Columns Builder block already stacks by default on mobile —
   same convention, not a new one) is what actually makes cramped 2-3
   narrow columns readable again, not just non-overflowing. */
.builder-columns { display: grid; gap: 24px; }
.builder-columns--2 { grid-template-columns: repeat(2, 1fr); }
.builder-columns--3 { grid-template-columns: repeat(3, 1fr); }
.builder-columns--4 { grid-template-columns: repeat(4, 1fr); }
.builder-columns > .builder-column { min-width: 0; }
@media (max-width: 700px) {
  .builder-columns--2, .builder-columns--3, .builder-columns--4 { grid-template-columns: 1fr; }
}
.builder-gallery { display: grid; gap: 12px; }
.builder-gallery--2 { grid-template-columns: repeat(2, 1fr); }
.builder-gallery--3 { grid-template-columns: repeat(3, 1fr); }
.builder-gallery--4 { grid-template-columns: repeat(4, 1fr); }
/* Round M mobile audit — a 3/4-column gallery never overflowed (images
   already shrink via `max-width:100%`) but got cramped to tiny thumbnails
   on a phone; capping at 2 columns below 560px matches the same
   card-grid convention used everywhere else in this audit. */
@media (max-width: 560px) {
  .builder-gallery--3, .builder-gallery--4 { grid-template-columns: repeat(2, 1fr); }
}
.builder-gallery img, .builder-image img { max-width: 100%; display: block; }
/* V.110 orphan cleanup — `.builder-video--empty` dropped from this
   selector list: verified zero references anywhere in resources/views,
   app/**/*.php, or assets/js/**/*.js. `PageParts::productVideo()` (the
   only place that could emit it) returns an empty string when there is no
   video, rather than a wrapper with this class — so it was never
   reachable. `.builder-image--empty`/`.builder-gallery--empty` are real
   (PageParts::currentImage()/gallery()/productGalleryHero()) and stay. */
.builder-image--empty, .builder-gallery--empty { color: var(--muted); font-size: var(--fs-12); padding: 20px; text-align: center; border: 1px dashed var(--line); border-radius: var(--rs); }

/* V.072 — real Video/Downloads/Certificates tabs (PageParts::productVideo()/
   productDownloads()/productCertificates()) — real data that had zero
   frontend surface before this round. */
.builder-video-list { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.builder-video-item video { width: 100%; display: block; border-radius: var(--r); background: #000; }
.builder-video-item iframe { width: 100%; aspect-ratio: 16/9; display: block; border: 0; border-radius: var(--r); }
.builder-video-item--link { display: inline-flex; align-items: center; gap: 8px; padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--rs); text-decoration: none; color: var(--ink); font-weight: 700; font-size: var(--fs-13); }
.builder-video-item--link svg { width: 16px; height: 16px; color: var(--accent); }
.builder-video-item--link:hover { border-color: var(--accent); }
.builder-document-list { display: flex; flex-direction: column; gap: 8px; }
.builder-document-item { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--rs); text-decoration: none; color: var(--ink); font-weight: 700; font-size: var(--fs-13); transition: border-color .12s ease; }
.builder-document-item:hover { border-color: var(--accent); }
.builder-document-item svg { width: 18px; height: 18px; color: var(--accent); flex: none; }
.builder-button { display: inline-block; padding: 10px 20px; border-radius: var(--rs); text-decoration: none; font-weight: 700; font-size: var(--fs-13); }
.builder-button--primary { background: var(--accent); color: #fff; }
.builder-button--secondary { background: var(--soft); color: var(--accent); }
.builder-button--outline { background: transparent; border: 1px solid var(--accent); color: var(--accent); }
.builder-video video, .builder-video iframe { max-width: 100%; width: 100%; aspect-ratio: 16/9; border: 0; }

/* Real rendered output — same admin-only Preview route as Phase 09.
   .builder-product-card itself is defined once, further down (the later
   `display: flex; flex-direction: column` version with the hover
   transition — the more complete, later-authored version; the earlier
   `display: block` version is dropped, not carried forward). */
.builder-product-card__media img { width: 100%; display: block; aspect-ratio: 4/3; object-fit: cover; }
.builder-product-card__noimage { aspect-ratio: 4/3; background: var(--soft); }
.builder-product-card__kind { font-size: var(--fs-10); text-transform: uppercase; color: var(--muted); }
.builder-product-card__body h4 { margin: 4px 0; font-size: var(--fs-14); }
.builder-product-card__desc { font-size: var(--fs-12); color: var(--muted); margin: 0 0 6px; }
.builder-product-card__price { font-size: var(--fs-12); font-weight: 700; color: var(--accent); }
.builder-product-grid, .builder-related-items { display: grid; gap: 16px; }
.builder-product-grid--2 { grid-template-columns: repeat(2, 1fr); }
.builder-product-grid--3 { grid-template-columns: repeat(3, 1fr); }
.builder-product-grid--4 { grid-template-columns: repeat(4, 1fr); }
.builder-related-items { grid-template-columns: repeat(3, 1fr); }
/* V.096 — mobile: every product grid becomes 2-per-row (never a single
   full-width stacked column) — same mobile-2col pattern as the category
   grid above, matches the approved mockup. List/Compact view toggles
   (below) already have their own rules and are left untouched. */
@media (max-width: 640px) {
  .builder-product-grid--2, .builder-product-grid--3, .builder-product-grid--4, .builder-related-items {
    grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px;
  }
}
/* .builder-repeater (base) removed here — confirmed zero references
   anywhere in resources/views/**/*.php or assets/js/**/*.js (dead class).
   The unrelated .builder-repeater--empty selector two lines below (part of
   a combined selector list with other real classes) is left untouched. */
.builder-product-grid--empty, .builder-product-card--empty, .builder-product-variants--empty,
.builder-taxonomy-list--empty, .builder-repeater--empty, .builder-attribute-value--empty,
.builder-related-items--empty { color: var(--muted); font-size: var(--fs-12); padding: 20px; text-align: center; border: 1px dashed var(--line); border-radius: var(--rs); }
.builder-product-variants { list-style: none; margin: 0; padding: 0; }
.builder-product-variants li { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid var(--line); }
/* Ad-hoc V.016 — a real "Browse by Category" card grid (was a plain,
   unlinked bullet list; see BuilderElements::renderTaxonomyList()). */
.builder-taxonomy-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 14px; }
.builder-taxonomy-list li { display: contents; }
.builder-taxonomy-list__link {
  display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center;
  border: 1px solid var(--line); border-radius: var(--r); background: #fff; padding: 22px 12px;
  color: var(--ink); transition: border-color .12s ease, box-shadow .12s ease;
}
.builder-taxonomy-list__link:hover { border-color: var(--accent); box-shadow: var(--shadow); }
.builder-taxonomy-list__link svg { width: 30px; height: 30px; color: var(--accent); }
.builder-taxonomy-list__link .term-name { font-weight: 700; font-size: var(--fs-13); }
.builder-taxonomy-list__link .term-count { color: var(--muted); font-size: var(--fs-11); }
.builder-attribute-value { display: flex; gap: 6px; font-size: var(--fs-13); }
.builder-attribute-value .attr-label { color: var(--muted); }

/* V.070 — Kit V12 §04 Category Card System: real 6-style Category Grid
   Builder block, replacing the old fixed `.builder-taxonomy-list` card
   grid on the Homepage "Browse by Category" block (`.builder-taxonomy-
   list` above is untouched — the Sidebar Category Filter widget still
   uses it for its own, deliberately different link-list look). */
.builder-category-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
.builder-category-grid--empty { color: var(--muted); font-size: var(--fs-12); padding: 20px; text-align: center; border: 1px dashed var(--line); border-radius: var(--rs); }
/* V.096 — mobile: 2-per-row instead of the auto-fill grid collapsing to a
   single column below ~460px (220px min card width doesn't fit twice past
   that). Matches the approved mockup's mobile-2col pattern. */
@media (max-width: 640px) {
  .builder-category-grid:not(:has(.builder-category-card--pill)) { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
}
.builder-category-card { display: block; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; text-decoration: none; color: inherit; background: var(--surface); transition: box-shadow .15s, transform .15s; }
.builder-category-card:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.builder-category-card__media { position: relative; display: block; }
.builder-category-card__media img { width: 100%; display: block; aspect-ratio: 4/3; object-fit: cover; }
.builder-category-card__noimage { aspect-ratio: 4/3; background: var(--soft); display: flex; align-items: center; justify-content: center; }
.builder-category-card__noimage svg { width: 36px; height: 36px; color: var(--accent); }
.builder-category-card__body { padding: 14px; display: flex; flex-direction: column; gap: 6px; }
.builder-category-card__body h4 { margin: 0; font-size: var(--fs-15); }
.builder-category-card__desc { font-size: var(--fs-12_5); color: var(--muted); margin: 0; }
.builder-category-card__link { font-size: var(--fs-12_5); font-weight: 700; color: var(--accent); }
.builder-category-card__count { font-size: var(--fs-11_5); color: var(--muted); }

/* Image — full-bleed media with a name/count overlay pinned to the bottom.
   The dark gradient + white text only makes sense over a real photo. */
.builder-category-card--image .builder-category-card__overlay {
  position: absolute; inset: auto 0 0 0; padding: 14px;
  background: linear-gradient(to top, rgba(0,0,0,.65), transparent);
  display: flex; flex-direction: column; gap: 2px;
}
.builder-category-card--image .builder-category-card__name { color: #fff; font-weight: 700; font-size: var(--fs-15); }
.builder-category-card--image .builder-category-card__count { color: rgba(255,255,255,.85); }
/* V.097 — self-caught bug (DECISIONS.md #105): a term with no real photo
   falls back to `__noimage` (a plain icon on a light `--soft` tile), but
   the dark gradient + white name/count overlay from the rule above still
   rendered on top of it — a dark smear over a light box with the name
   text barely legible and visually colliding with the centered icon.
   Confirmed via the owner's own screenshot of the real live category
   grid. When there's no photo, drop the overlay treatment entirely and
   render the name/count as normal static text under the icon instead —
   same layout the "Icon" card style already uses successfully. */
.builder-category-card--image:has(.builder-category-card__noimage) .builder-category-card__overlay {
  position: static; background: none; padding: 12px 14px 14px;
}
.builder-category-card--image:has(.builder-category-card__noimage) .builder-category-card__name { color: var(--ink); }
.builder-category-card--image:has(.builder-category-card__noimage) .builder-category-card__count { color: var(--muted); }
.builder-category-card--image:has(.builder-category-card__noimage) { display: flex; flex-direction: column; }
.builder-category-card--image:has(.builder-category-card__noimage) .builder-category-card__noimage { aspect-ratio: 16/10; }

/* Compact — small icon-left row, arrow affordance. */
.builder-category-card--compact { display: flex; flex-direction: row; align-items: center; padding: 10px 12px; gap: 10px; }
.builder-category-card--compact .builder-category-card__icon { flex: 0 0 auto; display: flex; color: var(--accent); }
.builder-category-card--compact .builder-category-card__icon svg { width: 22px; height: 22px; }
.builder-category-card--compact .builder-category-card__body { flex: 1; padding: 0; flex-direction: column; gap: 1px; }
.builder-category-card--compact .builder-category-card__name { font-weight: 700; font-size: var(--fs-13); }
.builder-category-card--compact .builder-category-card__arrow { flex: 0 0 auto; color: var(--muted); }

/* Icon — centered icon tile, no photo. */
.builder-category-card--icon { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 8px; padding: 24px 12px; }
.builder-category-card--icon .builder-category-card__icon-wrap { width: 52px; height: 52px; border-radius: 50%; background: var(--soft); display: flex; align-items: center; justify-content: center; color: var(--accent); }
.builder-category-card--icon .builder-category-card__icon-wrap svg { width: 26px; height: 26px; }
.builder-category-card--icon h4 { margin: 0; font-size: var(--fs-14); }
/* V.096 — decorative tint cycling on the icon chip only (4-color rotation,
   position-based via nth-child so no PHP/data change is needed) — matches
   the "colorful but tasteful icon circles" look from the approved mockup. */
.builder-category-grid--icon .builder-category-card:nth-child(4n+1) .builder-category-card__icon-wrap,
.builder-category-grid--compact .builder-category-card:nth-child(4n+1) .builder-category-card__icon { background: var(--tint-a-bg); color: var(--tint-a-ink); }
.builder-category-grid--icon .builder-category-card:nth-child(4n+2) .builder-category-card__icon-wrap,
.builder-category-grid--compact .builder-category-card:nth-child(4n+2) .builder-category-card__icon { background: var(--tint-b-bg); color: var(--tint-b-ink); }
.builder-category-grid--icon .builder-category-card:nth-child(4n+3) .builder-category-card__icon-wrap,
.builder-category-grid--compact .builder-category-card:nth-child(4n+3) .builder-category-card__icon { background: var(--tint-c-bg); color: var(--tint-c-ink); }
.builder-category-grid--icon .builder-category-card:nth-child(4n+4) .builder-category-card__icon-wrap,
.builder-category-grid--compact .builder-category-card:nth-child(4n+4) .builder-category-card__icon { background: var(--tint-d-bg); color: var(--tint-d-ink); }
.builder-category-grid--compact .builder-category-card__icon { width: 34px; height: 34px; border-radius: 50%; justify-content: center; }

/* Pill — small inline chip (no card chrome, wraps in a flex row). */
.builder-category-grid:has(.builder-category-card--pill) { grid-template-columns: none; display: flex; flex-wrap: wrap; }
.builder-category-card--pill {
  display: inline-flex; align-items: center; gap: 7px; border-radius: 999px; border: 1px solid var(--line);
  background: #fff; padding: 8px 16px 8px 12px; width: auto;
}
.builder-category-card--pill svg { width: 16px; height: 16px; color: var(--accent); }
.builder-category-card--pill .builder-category-card__name { font-weight: 700; font-size: var(--fs-12_5); }
.builder-category-card--pill .builder-category-card__count { background: var(--soft); border-radius: 999px; padding: 1px 8px; font-size: var(--fs-11); }

/* CTA — media + description + a real "Browse" button. The button label
   includes the real term name ("Browse Access Platforms"), which can run
   long — overrides the app-wide `.btn { white-space: nowrap }` so it
   wraps inside the card instead of overflowing past the card edge. */
.builder-category-card--cta { border: 1px solid var(--line); }
.builder-category-card--cta .builder-category-card__body .btn { align-self: stretch; margin-top: 4px; white-space: normal; text-align: center; justify-content: center; }

/* Ad-hoc V.021 (CATEGORY-FILTER-POLISH-REPORT.md) — Sidebar Category
   Filter widget only (BuilderRenderer::renderCategoryFilterWidget()). The
   real dataset has 100+ Product Category terms; `.builder-taxonomy-list`
   above (unchanged, still used unpaginated by the Homepage `category`
   block) made this narrower 280px Sidebar widget ~11,000px tall. This
   wraps that exact same real `<ul>` in a compact search box + a
   bounded-height scrollable panel — the fix is presentation-only, no new
   query, no change to `.builder-taxonomy-list` itself. */
.builder-category-filter { display: flex; flex-direction: column; gap: 10px; }
.builder-category-filter__search-wrap { display: block; }
.builder-category-filter__search {
  width: 100%; box-sizing: border-box; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: var(--rs); font: inherit; font-size: var(--fs-13); color: var(--ink); background: #fff;
}
.builder-category-filter__search:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Bounded height + scroll is what keeps the sidebar compact — the ~11,000px
   page-height regression this whole fix exists to solve. */
.builder-category-filter__list { max-height: 360px; overflow-y: auto; overflow-x: hidden; padding-right: 2px; }
.builder-category-filter__list .builder-taxonomy-list { grid-template-columns: 1fr; gap: 8px; }
.builder-category-filter__list .builder-taxonomy-list__link { flex-direction: row; justify-content: flex-start; text-align: left; padding: 10px 12px; gap: 10px; }
.builder-category-filter__list .builder-taxonomy-list__link svg { width: 18px; height: 18px; flex: none; }
.builder-category-filter__list .builder-taxonomy-list__link .term-name { flex: 1 1 auto; }
.builder-category-filter__list .builder-taxonomy-list__link--active { border-color: var(--accent); background: var(--soft); font-weight: 700; }
/* JS-driven hide/show only — never removes the real `<a>` from the DOM
   (frontend-category-filter.js). `[hidden]` needs this explicit override
   because `.builder-taxonomy-list li { display: contents; }` above would
   otherwise out-specificity the UA stylesheet's own `[hidden]` rule. */
.builder-category-filter__list .builder-taxonomy-list li[hidden] { display: none; }
.builder-category-filter__empty { margin: 0; padding: 10px 12px; font-size: var(--fs-13); color: var(--muted); border: 1px dashed var(--line); border-radius: var(--rs); text-align: center; }
.builder-category-filter__empty[hidden] { display: none; }
/* V.075 — Sidebar Widgets: "Featured Products" widget (real, live latest-
   or category-scoped products, reusing the existing compact product-card
   style — BuilderRenderer::renderFeaturedWidget()). A vertical stack, not
   a grid, since it lives in a narrow sidebar column. */
.builder-featured-widget { display: flex; flex-direction: column; gap: 10px; }
.builder-featured-widget .builder-product-card--compact { border-radius: var(--rs); }
/* No dedicated 900px/560px override needed — `box-sizing: border-box` on
   the search input and `overflow-x: hidden` on the scroll panel above
   already prevent horizontal overflow at every width, including the
   existing `.pb-layout` mobile stack (BuilderRenderer::renderLayout()). */

/* V.078 — real sidebar "Price Filter" / "Attribute Filter" widgets
   (BuilderRenderer::renderPriceFilterWidget()/renderAttributeFilterWidget()).
   Same narrow-sidebar-column assumption as `.builder-featured-widget`
   above; matches `.shop-filters`' own visual language (label/value rows,
   muted counts, accent-colored active state) so these widgets don't look
   like a foreign control bolted onto the existing filter sidebar. */
.builder-price-filter { display: flex; flex-direction: column; gap: 10px; }
.builder-price-filter__row { display: flex; gap: 10px; }
.builder-price-filter__row label { flex: 1 1 0; display: flex; flex-direction: column; gap: 4px; font-size: var(--fs-12); font-weight: 700; color: var(--muted); }
.builder-price-filter__row input { width: 100%; box-sizing: border-box; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--rs); background: var(--surface); color: var(--ink); font: inherit; font-size: var(--fs-13_5); }
.builder-price-filter__row input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.builder-price-filter__clear { margin-left: 8px; font-size: var(--fs-12_5); color: var(--muted); text-decoration: underline; }
.builder-price-filter__clear:hover { color: var(--ink); }
.builder-attribute-filter h4 { margin: 0 0 10px; font-size: var(--fs-13); font-weight: 800; }
.builder-attribute-filter ul { list-style: none; margin: 0 0 8px; padding: 0; }
.builder-attribute-filter li { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.builder-attribute-filter li a { display: block; flex: 1 1 auto; padding: 8px 4px; font-size: var(--fs-13_5); color: var(--ink); text-decoration: none; border-radius: var(--rs); }
.builder-attribute-filter li a:hover { background: var(--bg); }
.builder-attribute-filter li.is-active a { color: var(--accent); font-weight: 800; background: var(--soft); }
.builder-attribute-filter__count { color: var(--muted); font-size: var(--fs-11_5); }
.builder-attribute-filter__clear { display: inline-block; font-size: var(--fs-12_5); color: var(--muted); text-decoration: underline; }
.builder-attribute-filter__clear:hover { color: var(--ink); }
/* No dedicated 900px/560px override needed — same reasoning as the
   Featured widget above; these widgets already use `box-sizing:
   border-box`/flex-wrap-safe layout and stack cleanly at every width. */

/* ===== Phase 11 — Frontend Templates (PHASE-11-PLAN.md §4, §5) ===== */
.builder-attribute-list { display: flex; flex-direction: column; gap: 4px; margin: 12px 0; }
.builder-attribute-list .builder-attribute-value { padding: 6px 0; border-bottom: 1px solid var(--line); }
/* V.110 orphan cleanup — `.builder-dynamic-field--description` removed
   here: verified zero references anywhere in resources/views,
   app/**/*.php, or assets/js/**/*.js. See `app/Support/UiKitAudit.php`'s
   live orphan report. */
.builder-post-grid { display: grid; gap: 16px; grid-template-columns: repeat(3, 1fr); }
/* Round M mobile audit — no responsive breakpoint existed at all (unlike
   `.builder-product-grid`, which the real Product Grid block already
   overrides per-instance with its own scoped column CSS at every save).
   3 fixed equal columns squeezed unreadably narrow at phone widths —
   confirmed via real screenshot. Same 900px/560px breakpoints this
   codebase already uses everywhere else for card grids. */
@media (max-width: 900px) { .builder-post-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .builder-post-grid { grid-template-columns: repeat(1, 1fr); } }
.builder-post-card { display: block; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; text-decoration: none; color: inherit; background: var(--surface); transition: box-shadow .15s, transform .15s; }
/* V.103 (DECISIONS.md #111) — blog cards never got the V.096 premium
   hover-lift/shadow treatment product and category cards already have —
   same rule, ported as-is for visual parity. */
.builder-post-card:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.builder-post-card__media img { width: 100%; display: block; aspect-ratio: 16/9; object-fit: cover; }
.builder-post-card__noimage { aspect-ratio: 16/9; background: var(--soft); }
.builder-post-card__body { padding: 12px; }
.builder-post-card__body h4 { margin: 4px 0; font-size: var(--fs-14); }
.builder-post-card__desc { font-size: var(--fs-12); color: var(--muted); margin: 0; }
.builder-search-results { display: grid; gap: 16px; grid-template-columns: repeat(3, 1fr); }
/* V.103 (DECISIONS.md #111) — this grid had no breakpoint at all, unlike
   every other product-card grid on the site — squeezed 3 cards into
   375-390px. Same mobile-2col treatment .builder-product-grid already
   uses. Placed right after the base rule (rather than grouped with the
   product-grid mobile rules earlier in the file) because an equal-
   specificity rule earlier in source order loses to this one regardless
   of media-query nesting — confirmed the hard way via a real Playwright
   screenshot that still showed 3 columns until moved here. */
@media (max-width: 640px) {
  .builder-search-results { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
}
.builder-post-grid--empty, .builder-search-results--empty { color: var(--muted); font-size: var(--fs-13); padding: 24px; text-align: center; border: 1px dashed var(--line); border-radius: var(--rs); grid-column: 1 / -1; }

/* ---- frontend nav-menu rendering (Global Header/Footer Layout menu block) ----
   .builder-menu--vertical removed from the two selectors below — confirmed
   zero references anywhere in resources/views/**/*.php or assets/js/**/*.js
   (dead modifier class). */
.builder-menu__list { list-style: none; display: flex; gap: 20px; margin: 0; padding: 12px 24px; align-items: center; flex-wrap: wrap; }
.builder-menu__item { position: relative; }
.builder-menu__link { font-weight: 700; font-size: var(--fs-13); text-decoration: none; color: inherit; }
.builder-menu__link:hover { text-decoration: underline; }
/* V.110 orphan cleanup — `.builder-menu__submenu` (and its two compound
   rules that only existed to style it) removed here: verified zero
   references anywhere in resources/views, app/**/*.php, or assets/js/**/
   *.js — including the UI Kit style-guide demo markup, which shows
   `.builder-menu__item--has-children` but never pairs it with a
   `.builder-menu__submenu` element. `PageParts::menu()` (the only real
   renderer of `.builder-menu__*`) has no children/dropdown concept at
   all; the real site header's own dropdown (`PageParts::menuItemsHtml()`)
   uses a completely different, real `.sub-menu` class instead. See
   `app/Support/UiKitAudit.php`'s live orphan report. */

/* ---- generic icon-glyph utility (used across header/footer/every element) ----
   .builder-icon-link removed — confirmed zero references anywhere (dead class). */
.builder-icon { display: inline-flex; }
.builder-icon svg { width: 1em; height: 1em; }
.builder-icon--sm { font-size: 18px; } /* icon glyph box size, not reading text — not part of the type scale */
.builder-icon--md { font-size: 28px; }
.builder-icon--lg { font-size: 40px; }
.builder-icon--xl { font-size: 56px; }

/* ---- carousel (hero slider) ---- */
.builder-carousel { position: relative; border-radius: var(--r); overflow: hidden; background: var(--dark); }
.builder-carousel__track { position: relative; }
.builder-carousel__slide {
  display: none; grid-template-columns: 1.1fr 1fr; align-items: center; gap: 32px;
  min-height: 420px; padding: 48px clamp(24px, 5vw, 64px);
}
.builder-carousel__slide.is-active { display: grid; }
.builder-carousel__media { position: relative; order: 2; }
.builder-carousel__media img { width: 100%; border-radius: var(--r); display: block; object-fit: cover; aspect-ratio: 4/3; }
.builder-carousel__badge {
  position: absolute; bottom: 16px; right: 16px; background: #fff; border-radius: var(--rs);
  box-shadow: var(--shadow); padding: 12px 16px; text-align: left; min-width: 140px;
}
.builder-carousel__badge strong { display: block; font-size: var(--fs-22); color: var(--ink); }
.builder-carousel__badge span { font-size: var(--fs-11); color: var(--muted); }
/* V.108 — Homepage redesign: right column becomes a genuine "stat callout
   card" when a slide has no image (see PageParts::carousel()'s docblock),
   instead of a small overlay badge floating over nothing. Static-positioned
   (not the corner-overlay `.builder-carousel__badge` above), centered in
   the media column, its own icon + larger value/label. */
.builder-carousel__media:not(:has(img)) {
  display: flex; align-items: center; justify-content: center; min-height: 220px;
}
.builder-carousel__badge--standalone {
  position: static; width: 100%; max-width: 260px; text-align: center;
  padding: 32px 24px; border-top: 4px solid var(--accent);
}
.builder-carousel__badge--standalone .builder-icon {
  width: 44px; height: 44px; border-radius: 50%; background: var(--soft); color: var(--accent);
  display: flex; align-items: center; justify-content: center; margin: 0 auto 14px;
}
.builder-carousel__badge--standalone .builder-icon svg { width: 22px; height: 22px; }
.builder-carousel__badge--standalone strong { font-size: var(--fs-28); }
.builder-carousel__badge--standalone span { display: block; margin-top: 4px; font-size: var(--fs-12); text-transform: uppercase; letter-spacing: .04em; font-weight: 700; }
@media (max-width: 780px) {
  .builder-carousel__media:not(:has(img)) { min-height: 0; padding-bottom: 8px; }
}
.builder-carousel__body { order: 1; color: #fff; }
.builder-carousel__eyebrow { display: block; font-size: var(--fs-11); font-weight: 800; text-transform: uppercase; letter-spacing: .14em; color: var(--accent-2); margin-bottom: 10px; }
.builder-carousel__body h2 { font-size: clamp(26px, 3.4vw, 42px); line-height: 1.15; margin: 0 0 14px; }
.builder-carousel__body p { color: #c7d0da; font-size: var(--fs-15); max-width: 46ch; margin: 0 0 22px; }
.builder-carousel__ctas { display: flex; gap: 12px; flex-wrap: wrap; }
.builder-carousel__ctas .btn.outline { background: transparent; border-color: rgba(255,255,255,.4); color: #fff; }
.builder-carousel__nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 5; width: 40px; height: 40px; border-radius: 999px; border: none; background: rgba(255,255,255,.15); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.builder-carousel__nav svg { width: 18px; height: 18px; }
.builder-carousel__nav:hover { background: rgba(255,255,255,.3); }
.builder-carousel__nav--prev { left: 16px; }
.builder-carousel__nav--next { right: 16px; }
.builder-carousel__dots { position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%); display: flex; gap: 8px; z-index: 5; }
.builder-carousel__dot { width: 8px; height: 8px; border-radius: 999px; border: none; background: rgba(255,255,255,.4); cursor: pointer; padding: 0; }
.builder-carousel__dot.is-active { background: #fff; width: 22px; border-radius: 999px; }
.builder-carousel--empty { display: none; }
@media (max-width: 780px) {
  .builder-carousel__slide { grid-template-columns: 1fr; min-height: 0; padding: 32px 20px; }
  .builder-carousel__media, .builder-carousel__body { order: 0; }
}

/* ---- stats_bar ---- */
.builder-stats-bar { background: var(--dark); color: #fff; border-radius: var(--r); padding: 24px 28px; }
.builder-stats-bar__heading { margin: 0 0 14px; font-size: var(--fs-18); }
/* V.096 — dashboard-style stat chips inside the dark band: label above the
   big value, icon in its own soft circle. Same visual language as the
   category/stat cards elsewhere on the page, just tuned for a dark
   background (semi-opaque white circle instead of a light tint). Mobile
   goes 2-per-row instead of wrapping to whatever fits. */
.builder-stats-bar__items { display: flex; flex-wrap: wrap; gap: 24px; }
.builder-stats-bar__item { display: flex; flex-direction: column; gap: 2px; min-width: 90px; }
.builder-stats-bar__item strong { font-size: var(--fs-26); font-weight: 900; overflow-wrap: anywhere; }
.builder-stats-bar__item span { font-size: var(--fs-12); color: #aeb8c2; text-transform: uppercase; letter-spacing: .04em; font-weight: 700; order: -1; margin-bottom: 6px; }
.builder-stats-bar__icon { width: 38px; height: 38px; border-radius: 50%; background: rgba(255,255,255,.1); color: var(--accent-2); margin-bottom: 4px; display: flex; align-items: center; justify-content: center; }
.builder-stats-bar__icon svg { width: 20px; height: 20px; }
.builder-stats-bar--empty { display: none; }
@media (max-width: 640px) {
  .builder-stats-bar__items { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 16px; }
  .builder-stats-bar__item { min-width: 0; }
  /* Font-size no longer overridden here — .builder-stats-bar__item strong's
     own var(--fs-26) is already fluid (V.103, DECISIONS.md #111), so it
     scales down smoothly instead of jumping at this breakpoint. */
}

/* ---- trust_band ("Why [Business]", V.108 Homepage redesign) ----
   Full-bleed dark band (PageParts::trustBand()) — same `var(--dark)`/white-
   text language `.builder-carousel`/`.builder-stats-bar` already use, so it
   reads as one consistent "dark section" system rather than a one-off. Not
   wrapped in `.builder-container` by the caller (full width edge-to-edge),
   so the band supplies its own inner max-width/centering. */
.builder-trust-band { background: var(--dark); color: #fff; padding: 56px clamp(20px, 5vw, 64px); text-align: center; }
.builder-trust-band__heading { margin: 0 0 10px; font-size: var(--fs-h2); }
.builder-trust-band__text { margin: 0 auto 36px; max-width: 62ch; color: #c7d0da; font-size: var(--fs-16); }
.builder-trust-band__items { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 32px; max-width: 1100px; margin: 0 auto; }
.builder-trust-band__item .builder-icon {
  width: 52px; height: 52px; border-radius: 50%; background: rgba(255,255,255,.1); color: var(--accent-2);
  margin: 0 auto 14px; align-items: center; justify-content: center; font-size: 22px;
}
.builder-trust-band__item h4 { margin: 0 0 6px; font-size: var(--fs-18); color: #fff; }
.builder-trust-band__item p { margin: 0; font-size: var(--fs-14); color: #aeb8c2; line-height: 1.5; }
@media (max-width: 640px) {
  .builder-trust-band { padding: 40px 20px; }
  .builder-trust-band__items { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px 16px; text-align: left; }
  .builder-trust-band__item .builder-icon { margin: 0 0 10px; }
}
@media (max-width: 460px) {
  .builder-trust-band__items { grid-template-columns: 1fr; text-align: center; }
  .builder-trust-band__item .builder-icon { margin: 0 auto 10px; }
}

/* ---- logo_strip ---- */
.builder-logo-strip { text-align: center; padding: 8px 0; }
.builder-logo-strip__heading { font-size: var(--fs-13); text-transform: uppercase; letter-spacing: .1em; color: var(--muted); margin: 0 0 18px; }
.builder-logo-strip__items { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 36px; }
.builder-logo-strip__item img { max-height: 34px; max-width: 130px; object-fit: contain; filter: grayscale(1); opacity: .65; transition: opacity .15s ease, filter .15s ease; }
.builder-logo-strip__item:hover img { filter: none; opacity: 1; }
.builder-logo-strip--empty { display: none; }

/* ---- testimonials ---- */
.builder-testimonials { text-align: center; }
.builder-testimonials__heading { font-size: var(--fs-26); margin: 0 0 24px; }
.builder-testimonials__slider { position: relative; max-width: 720px; margin: 0 auto; }
.builder-testimonials__item { display: none; flex-direction: column; align-items: center; gap: 14px; padding: 8px 12px 44px; }
.builder-testimonials__item.is-active { display: flex; }
.builder-testimonials__item svg { width: 30px; height: 30px; color: var(--accent); }
.builder-testimonials__item blockquote { margin: 0; font-size: var(--fs-19); line-height: 1.6; color: var(--ink); font-weight: 600; }
.builder-testimonials__item cite { font-style: normal; color: var(--muted); font-size: var(--fs-13); }
.builder-testimonials--empty { display: none; }
/* V.103 — the item also carries .builder-carousel__slide so the existing
   generic frontend-slider.js (built for the Hero Carousel) drives this
   slider too; this resets the carousel slide's own grid/min-height so it
   doesn't leak into the testimonials layout. Two classes = higher
   specificity than either rule alone, so it wins regardless of source
   order. */
.builder-testimonials__item.builder-carousel__slide { min-height: 0; grid-template-columns: none; }
.builder-testimonials__item.builder-carousel__slide.is-active { display: flex; }

/* ---- tabs / tab_panel ---- */
.builder-tabs__nav { display: flex; gap: 4px; flex-wrap: wrap; border-bottom: 1px solid var(--line); margin-bottom: 20px; }
.builder-tabs__nav-btn { border: none; background: transparent; padding: 12px 18px; font: inherit; font-weight: 700; font-size: var(--fs-13); color: var(--muted); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.builder-tabs__nav-btn.is-active { color: var(--accent); border-bottom-color: var(--accent); }
.builder-tab-panel { display: block; margin-bottom: 20px; }
.has-js-tabs > .builder-tab-panel { display: none; margin-bottom: 0; }
.has-js-tabs > .builder-tab-panel.is-active { display: block; }
.builder-tabs--empty { display: none; }

/* ---- faq_list (reused, now with a real public element) ---- */
.builder-faq-list { display: flex; flex-direction: column; gap: 8px; }
.builder-faq-list__item { border: 1px solid var(--line); border-radius: var(--rs); padding: 12px 16px; background: var(--surface); }
.builder-faq-list__item summary { cursor: pointer; font-weight: 700; font-size: var(--fs-14); list-style: none; }
.builder-faq-list__item summary::-webkit-details-marker { display: none; }
.builder-faq-list__item summary::before { content: '+'; display: inline-block; width: 18px; color: var(--accent); font-weight: 900; }
.builder-faq-list__item[open] summary::before { content: '\2212'; }
.builder-faq-list__answer { padding: 10px 0 0 18px; color: var(--muted); font-size: var(--fs-13); line-height: 1.6; }
.builder-faq-list--empty { display: none; }

/* ---- reviews ---- */
.builder-reviews__summary { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.builder-reviews__average { font-size: var(--fs-26); font-weight: 900; }
.builder-reviews__stars { display: inline-flex; gap: 2px; color: var(--accent-2); }
.builder-reviews__stars svg { width: 16px; height: 16px; }
.builder-reviews__star-empty { color: var(--line); }
.builder-reviews__count, .builder-reviews__none { color: var(--muted); font-size: var(--fs-13); }
.builder-reviews__list { display: flex; flex-direction: column; gap: 12px; }
.builder-reviews__item { border: 1px solid var(--line); border-radius: var(--rs); padding: 14px 16px; background: var(--surface); }
.builder-reviews__item-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.builder-reviews__date { color: var(--muted); font-size: var(--fs-11); margin-left: auto; }
.builder-reviews__item p { margin: 0; font-size: var(--fs-13); color: var(--ink); line-height: 1.6; }
/* V.110 orphan cleanup — `.builder-reviews--empty` removed here: verified
   zero references anywhere in resources/views, app/**/*.php, or
   assets/js/**/*.js. `PageParts::reviews()` renders `.builder-reviews__none`
   (a real, styled class elsewhere in this file) for the no-reviews case,
   never this class. See `app/Support/UiKitAudit.php`'s live orphan report. */

/* ---- inquiry_form ---- */
.builder-inquiry-form { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--shadow); padding: 24px; display: flex; flex-direction: column; gap: 14px; }
.builder-inquiry-form h3 { margin: 0; font-size: var(--fs-19); }
.builder-inquiry-form__sub { margin: -8px 0 0; color: var(--muted); font-size: var(--fs-13); }
.builder-inquiry-form__product { background: var(--soft); color: var(--accent); border-radius: var(--rs); padding: 8px 12px; font-size: var(--fs-13); }
.builder-inquiry-form label { display: flex; flex-direction: column; gap: 5px; font-size: var(--fs-12); font-weight: 700; color: var(--ink); }
.builder-inquiry-form .req { color: var(--danger); }
.builder-inquiry-form input, .builder-inquiry-form select, .builder-inquiry-form textarea { font: inherit; font-weight: 400; border: 1px solid var(--line); border-radius: var(--rs); padding: 10px 12px; background: var(--surface); }
.builder-inquiry-form__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.builder-inquiry-form__interest select { width: 100%; }
.builder-inquiry-form__contact-method { display: flex; align-items: center; gap: 14px; font-size: var(--fs-12); }
.builder-inquiry-form__contact-method-label { font-weight: 700; color: var(--muted); }
.builder-inquiry-form__contact-method label { flex-direction: row; align-items: center; gap: 6px; font-weight: 500; }
.builder-inquiry-form__hp { position: absolute !important; opacity: 0; height: 0; width: 0; padding: 0; border: 0; pointer-events: none; }
/* V.104 hotfix — Send Inquiry + WhatsApp Us used to stack as two
   full-width blocks that didn't visually match each other. Now a real
   2-column row via .builder-inquiry-form__actions, each button filling
   its cell so they match; collapses to one full-width column only below
   420px, where two half-width buttons stop being comfortably tappable. */
.builder-inquiry-form__actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.builder-inquiry-form__actions .btn { width: 100%; margin: 0; }
@media (max-width: 420px) {
  .builder-inquiry-form__actions { grid-template-columns: 1fr; }
}
/* V.073 — real "WhatsApp Us" action alongside Send Inquiry (PageParts::
   whatsappInquiryLink()). Only ever present in the markup at all when a
   real WhatsApp number is configured. */
.builder-inquiry-form__whatsapp { display: flex; align-items: center; justify-content: center; gap: 8px; background: #25d366; border-color: #25d366; color: #fff; }
.builder-inquiry-form__whatsapp:hover { background: #20bd5a; border-color: #20bd5a; color: #fff; }
.builder-inquiry-form__whatsapp .builder-icon, .builder-inquiry-form__whatsapp svg { width: 16px; height: 16px; }
.builder-inquiry-form__status { font-size: var(--fs-12); min-height: 1em; }
.builder-inquiry-form__status.is-success { color: var(--success); }
.builder-inquiry-form__status.is-error { color: var(--danger); }
.builder-inquiry-form--sticky { position: sticky; top: 24px; }
/* Round M mobile audit — once `.builder-columns` stacks to one column
   (≤700px, see that rule above), a `position:sticky` quote form pins
   itself near the top of the viewport as the visitor scrolls the (now
   much taller, single-column) page, overlapping content below it instead
   of sitting in its own place in the stack. Static on mobile — same
   `.shop-filters` precedent (that sidebar drops its own sticky the same
   way at its own mobile breakpoint). */
@media (max-width: 700px) {
  .builder-inquiry-form--sticky { position: static; top: auto; }
}
@media (max-width: 560px) {
  .builder-inquiry-form__grid { grid-template-columns: 1fr; }
}

/* ---- product_grid view toggle (real Grid/List/Compact, Ad-hoc V.016 §2 —
   deliberately not a "popularity" sort/heat-map, see the plan doc) ---- */
.builder-view-toggle { display: flex; justify-content: flex-end; gap: 6px; margin-bottom: 14px; }
.builder-view-toggle__btn { border: 1px solid var(--line); background: var(--surface); border-radius: var(--rs); padding: 6px 12px; font-size: var(--fs-12); font-weight: 700; color: var(--muted); cursor: pointer; }
.builder-view-toggle__btn.is-active { background: var(--soft); border-color: #cbd8ff; color: var(--accent); }
.builder-product-grid[data-view="list"] { display: flex; flex-direction: column; gap: 12px; }
.builder-product-grid[data-view="list"] .builder-product-card { display: grid; grid-template-columns: 180px 1fr; }
.builder-product-grid[data-view="list"] .builder-product-card__media img { aspect-ratio: 1/1; height: 100%; }
.builder-product-grid[data-view="compact"] { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; }
.builder-product-grid[data-view="compact"] .builder-product-card__body { padding: 8px; }
.builder-product-grid[data-view="compact"] .builder-product-card__desc { display: none; }

/* ===== V.027 — Frontend redesign pass (DECISIONS.md #046) =====
   New/enhanced frontend components only — every selector below is either
   brand new (pp-feature had zero rules before this; shop-filters/product
   card actions/spec-grid/contact cards did not exist at all) or a pure
   additive extension of an existing block (productCard gained a spec row
   + a real two-button actions row, previously the whole card was one
   link). Nothing here removes or restyles admin-side (.app-*) rules. */

/* Homepage/feature "trust row" icon callouts — PageParts::iconFeature()
   has emitted this markup since Phase 09/V.016 but app.css never had a
   matching rule, so it rendered as unstyled stacked text. */
.pp-feature { display: flex; gap: 12px; align-items: flex-start; }
.pp-feature .builder-icon {
  width: 40px; height: 40px; border-radius: 50%; background: var(--soft);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--accent);
}
.pp-feature .builder-icon svg { width: 20px; height: 20px; }
.pp-feature h4 { margin: 0 0 2px; font-size: var(--fs-14); }
.pp-feature .builder-text { margin: 0; font-size: var(--fs-12_5); color: var(--muted); }
/* V.096 — same decorative tint rotation as the category icon cards, keyed
   off the feature item's own position in its `.builder-columns` wrapper. */
.builder-columns > .pp-feature:nth-child(4n+1) .builder-icon { background: var(--tint-a-bg); color: var(--tint-a-ink); }
.builder-columns > .pp-feature:nth-child(4n+2) .builder-icon { background: var(--tint-b-bg); color: var(--tint-b-ink); }
.builder-columns > .pp-feature:nth-child(4n+3) .builder-icon { background: var(--tint-c-bg); color: var(--tint-c-ink); }
.builder-columns > .pp-feature:nth-child(4n+4) .builder-icon { background: var(--tint-d-bg); color: var(--tint-d-ink); }

/* Product card — spec row + two-button actions row (Get Quote / View
   Details), replacing the old "whole card is one <a>" pattern so a quote
   click and a details click can go to different real destinations. */
.builder-product-card { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; background: var(--surface); transition: box-shadow .15s, transform .15s; }
.builder-product-card__media { display: block; }
.builder-product-card:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
/* .builder-product-card__body merged (padding from the Phase 10 rule + layout from the V.027 rule) */
.builder-product-card__body { padding: 12px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
.builder-product-card__specs { display: flex; flex-wrap: wrap; gap: 8px 12px; font-size: var(--fs-11_5); color: var(--muted); margin: 2px 0 4px; }
.builder-product-card__specs span { display: inline-flex; align-items: center; gap: 4px; }
.builder-product-card__specs svg { width: 12px; height: 12px; color: var(--accent); flex-shrink: 0; }
.builder-product-card__actions { display: flex; gap: 8px; margin-top: auto; padding-top: 4px; }
.builder-product-card__actions .btn { flex: 1; justify-content: center; padding: 8px 10px; font-size: var(--fs-12_5); text-decoration: none; }
.builder-product-card__actions .btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.builder-product-card__actions .view-link { flex: none; display: inline-flex; align-items: center; gap: 3px; font-size: var(--fs-12_5); font-weight: 700; color: var(--accent); padding: 8px 2px; text-decoration: none; }
.builder-product-card__actions .view-link:hover { color: var(--accent-dark); }
/* V.096 — 2-per-row mobile cards (see .builder-product-grid mobile rule
   above) are too narrow for "Get Quote" + "View Details →" side by side;
   stack them instead of letting the link text get clipped. */
@media (max-width: 640px) {
  .builder-product-card__actions { flex-direction: column; align-items: stretch; gap: 4px; }
  .builder-product-card__actions .view-link { justify-content: center; padding: 6px 2px; }
}
/* V.110 orphan cleanup — `.builder-product-card__link` removed here:
   verified zero references anywhere in resources/views, app/**/*.php, or
   assets/js/**/*.js. Every real `PageParts::productCard()` style variant
   uses `.builder-product-card__media`/plain `<a>` tags with inline
   `style="text-decoration:none;color:inherit;"`, never this class. See
   `app/Support/UiKitAudit.php`'s live orphan report. */

/* V.069 — Kit V12 §03 Product/Service Card System: CSS for the 9 additional
   named card styles PageParts::productCard() now renders (`standard` above
   stays the unmodified base look). Each is a `.builder-product-card--*`
   modifier layered on the same base rules above — same border/radius/hover
   lift, only the internals differ. */
.builder-product-card__pills { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0 4px; }
.builder-product-card__pills .pill { font-size: var(--fs-11); padding: 3px 9px; }
.builder-product-card__arrow { display: flex; align-items: center; justify-content: center; padding: 0 12px; font-size: 18px; /* decorative arrow glyph */ color: var(--muted); flex-shrink: 0; }
.builder-product-card .view-link { display: inline-flex; align-items: center; gap: 3px; font-size: var(--fs-12_5); font-weight: 700; color: var(--accent); text-decoration: none; }
.builder-product-card .view-link:hover { color: var(--accent-dark); }
.builder-save-toggle { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; width: 40px; padding: 0; }
.builder-save-toggle svg { width: 16px; height: 16px; }
.builder-save-toggle[aria-pressed="true"] { color: var(--danger); border-color: var(--danger); }
.builder-save-toggle[aria-pressed="true"] svg { fill: currentColor; }

/* 02 Minimal — image, title, one View action, nothing else. */
.builder-product-card--minimal .builder-product-card__body { padding: 12px; gap: 4px; }
.builder-product-card--minimal .btn { align-self: flex-start; margin-top: 4px; }

/* 03 Featured — "Featured" pill badge + arrow explore link, no price row. */
.builder-product-card--featured { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }
.builder-product-card--featured .builder-product-card__body { padding: 14px; gap: 6px; }
.builder-product-card--featured .pill.active { background: var(--accent); color: #fff; border-color: var(--accent); align-self: flex-start; }
.builder-product-card--featured .view-link { margin-top: 4px; }

/* 04 Specs — spec pills instead of a description paragraph. */
.builder-product-card--specs .builder-product-card__body { padding: 12px; gap: 8px; }

/* 05 Editorial — description + a text-style "Explore" pill link. */
.builder-product-card--editorial .builder-product-card__body { padding: 14px; gap: 6px; }
.builder-product-card--editorial .pill { align-self: flex-start; background: transparent; border: none; padding: 0; color: var(--accent); font-weight: 700; }

/* 06 Horizontal — list-row layout: media left, body middle, actions right. */
.builder-product-card--horizontal { flex-direction: row; align-items: stretch; }
.builder-product-card--horizontal .builder-product-card__media { flex: 0 0 160px; }
.builder-product-card--horizontal .builder-product-card__media img { height: 100%; aspect-ratio: 1/1; }
.builder-product-card--horizontal .builder-product-card__body { padding: 12px 14px; }
.builder-product-card--horizontal .builder-product-card__actions { flex-direction: column; justify-content: center; margin: 0; padding: 12px; flex: 0 0 140px; }
@media (max-width: 640px) {
  .builder-product-card--horizontal { flex-direction: column; }
  .builder-product-card--horizontal .builder-product-card__media { flex: none; }
  .builder-product-card--horizontal .builder-product-card__actions { flex-direction: row; padding: 0 12px 12px; }
}

/* 07 Compact — small row card, arrow affordance instead of a button.
   Unlike Horizontal/Standard/etc., Compact and Dense (below) put the image
   directly inside the card `<a>` (no `.builder-product-card__media`
   wrapper — see PageParts::productCard()), so their image/placeholder is
   targeted directly here, with an explicit flex-basis so it doesn't
   collapse to 0 width in a flex row. */
.builder-product-card--compact { flex-direction: row; align-items: center; }
.builder-product-card--compact > img,
.builder-product-card--compact > picture,
.builder-product-card--compact > .builder-product-card__noimage { flex: 0 0 64px; width: 64px; aspect-ratio: 1/1; object-fit: cover; overflow: hidden; }
.builder-product-card--compact > picture > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.builder-product-card--compact .builder-product-card__body { padding: 8px 10px; gap: 2px; }
.builder-product-card--compact .builder-product-card__body h4 { font-size: var(--fs-13); }
.builder-product-card--compact .builder-product-card__desc { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }

/* 08 Compare — two real actions (View Details / Get Quote), no price row. */
.builder-product-card--compare .builder-product-card__body { padding: 12px; gap: 8px; }

/* 09 Conversion — Add Inquiry + save/wishlist heart toggle. */
.builder-product-card--conversion .builder-product-card__body { padding: 12px; gap: 8px; }
.builder-product-card--conversion .builder-product-card__actions .btn.primary { flex: 1; }

/* 10 Dense — tightest grid card, for high-count listings. Same unwrapped
   image markup as Compact above — targeted directly, not via
   `.builder-product-card__media` (see the note on Compact). */
.builder-product-card--dense { flex-direction: row; align-items: center; }
.builder-product-card--dense > img,
.builder-product-card--dense > picture,
.builder-product-card--dense > .builder-product-card__noimage { flex: 0 0 48px; width: 48px; aspect-ratio: 1/1; object-fit: cover; overflow: hidden; }
.builder-product-card--dense > picture > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.builder-product-card--dense .builder-product-card__body { padding: 6px 8px; gap: 0; }
.builder-product-card--dense .builder-product-card__body h4 { font-size: var(--fs-12_5); margin: 0; }
.builder-product-card--dense .builder-product-card__desc { display: none; }


/* Shop page — real category filter sidebar (DECISIONS.md #046: only the
   category filter is wired to a real query; capacity/mounting-type/height
   are intentionally not shown since there's no generic attribute-range
   query layer yet — see FrontendController::shop() doc comment). */
.shop-layout { display: grid; grid-template-columns: 250px 1fr; gap: 28px; align-items: flex-start; }
/* V.073 (task #135 follow-up, "Builder is King" mockup sign-off) — the
   real Shop/Category filter sidebar now stays pinned while the product
   grid scrolls past it, same as the approved mockup's sticky sidebar.
   Offset matches `.site-header`'s own sticky `top` (0, or 40px with
   `.has-adminbar` — see the block above) plus the header's real
   rendered height, so the sidebar never slides under it. Capped to the
   viewport height (minus that same offset) with its own scroll, so a
   sidebar taller than the viewport never gets stuck unreachable. */
.shop-filters { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 18px; position: sticky; top: 84px; max-height: calc(100vh - 100px); overflow-y: auto; }
.frontend-body.has-adminbar .shop-filters { top: 124px; max-height: calc(100vh - 140px); }
.shop-filters h4 { margin: 0 0 12px; font-size: var(--fs-13); font-weight: 800; }
.shop-filters ul { list-style: none; margin: 0; padding: 0; }
.shop-filters li a { display: flex; justify-content: space-between; gap: 8px; padding: 8px 4px; font-size: var(--fs-13_5); color: var(--ink); text-decoration: none; border-radius: var(--rs); }
.shop-filters li a:hover { background: var(--bg); }
.shop-filters li.is-active a { color: var(--accent); font-weight: 800; background: var(--soft); }
.shop-filters li .count { color: var(--muted); font-size: var(--fs-11_5); }
@media (max-width: 860px) {
  .shop-layout { grid-template-columns: 1fr; }
  /* Stacked single-column mobile layout — a sticky sidebar above the grid
     would just eat scroll space with no room to show the grid at the same
     time, so it reverts to normal flow below this width. */
  .shop-filters { position: static; max-height: none; overflow-y: visible; }
}

/* V.067 — Universal UI/UX Kit V12 consolidated round (DECISIONS.md #089):
   taxonomy "dropdown" display style. */
.shop-filters__select { width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--rs); background: var(--surface); color: var(--ink); font: inherit; font-size: var(--fs-13_5); }

/* Contact page — real info cards (address/phone/email/hours), each backed
   by an actual PageSettings/Settings field, no placeholder copy. */
.contact-info-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
.contact-info-card { display: flex; gap: 14px; align-items: flex-start; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 18px; box-shadow: var(--shadow); }
.contact-info-card .builder-icon { width: 44px; height: 44px; border-radius: 50%; background: var(--soft); display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--accent); }
.contact-info-card .builder-icon svg { width: 20px; height: 20px; }
.contact-info-card h4 { margin: 0 0 3px; font-size: var(--fs-13_5); }
.contact-info-card p { margin: 0; font-size: var(--fs-12_5); color: var(--muted); line-height: 1.55; }
/* V.099 — same colored-circle tint cycling as the Category icon grid and
   the .pp-feature columns (DECISIONS.md #107), so the Contact page's
   WhatsApp/Call/Email/Hours cards match the mockup's premium pattern
   instead of the old flat-square accent icon. */
.contact-info-row .contact-info-card:nth-child(4n+1) .builder-icon { background: var(--tint-a-bg); color: var(--tint-a-ink); }
.contact-info-row .contact-info-card:nth-child(4n+2) .builder-icon { background: var(--tint-b-bg); color: var(--tint-b-ink); }
.contact-info-row .contact-info-card:nth-child(4n+3) .builder-icon { background: var(--tint-c-bg); color: var(--tint-c-ink); }
.contact-info-row .contact-info-card:nth-child(4n+4) .builder-icon { background: var(--tint-d-bg); color: var(--tint-d-ink); }

/* Product/Service detail — real spec grid (from the Attribute Engine,
   frontend_visible attributes only), sitting above the description. */
.pp-spec-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin: 4px 0 22px; }
.pp-spec-grid .spec { border: 1px solid var(--line); border-radius: var(--rs); padding: 12px 14px; }
.pp-spec-grid .spec-label { font-size: var(--fs-11); color: var(--muted); margin-bottom: 2px; }
.pp-spec-grid .spec-value { font-size: var(--fs-14); font-weight: 800; }

/* Owner-approved mockups — kind-aware attribute rendering (product/service
   detail page tabs: Specifications | Key Features | Applications |
   Industries | FAQ | Reviews). `.pp-spec-group-title` is the small heading
   between the Attributes/Specifications blocks in the 'grouped' spec
   display style (Settings > Theme). Chip row is the 'chips' (default)
   chip_display_style — reuses the shared `.pill`/`.badge` shape (tokens.css)
   with a kind-specific soft tint (`--tint-b/c/d`) instead of inventing a
   new chip component. */
.pp-spec-group-title { font-size: var(--fs-13); font-weight: 800; color: var(--ink); margin: 18px 0 6px; }
.pp-spec-group-title:first-child { margin-top: 0; }
.builder-attribute-list--grouped .builder-attribute-list { margin: 0 0 4px; }

.builder-attribute-chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0; }
.builder-attribute-chip {
  display: inline-flex; align-items: center; border-radius: 999px;
  padding: 7px 14px; font-size: var(--fs-13); font-weight: 700;
  border: 1px solid transparent;
}
.builder-attribute-chip.tint-b { background: var(--tint-b-bg); color: var(--tint-b-ink); }
.builder-attribute-chip.tint-c { background: var(--tint-c-bg); color: var(--tint-c-ink); }
.builder-attribute-chip.tint-d { background: var(--tint-d-bg); color: var(--tint-d-ink); }

/* chip_display_style: 'icon_grid' */
.builder-kind-grid--icon { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin: 4px 0 22px; }
.builder-kind-icon-card { border: 1px solid var(--line); border-radius: var(--rs); padding: 14px; display: flex; flex-direction: column; gap: 6px; }
.builder-kind-icon-card__icon {
  width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.builder-kind-icon-card__icon svg { width: 16px; height: 16px; }
.builder-kind-icon-card__icon.tint-b { background: var(--tint-b-bg); color: var(--tint-b-ink); }
.builder-kind-icon-card__icon.tint-c { background: var(--tint-c-bg); color: var(--tint-c-ink); }
.builder-kind-icon-card__icon.tint-d { background: var(--tint-d-bg); color: var(--tint-d-ink); }
.builder-kind-icon-card__label { font-weight: 700; font-size: var(--fs-14); }
.builder-kind-icon-card__value { font-size: var(--fs-13); color: var(--muted); }

/* chip_display_style: 'icon_list' */
.builder-kind-list { list-style: none; margin: 4px 0 22px; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.builder-kind-list__item { display: flex; align-items: baseline; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--line); }
.builder-kind-list__dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.builder-kind-list__dot.tint-b { background: var(--tint-b-ink); }
.builder-kind-list__dot.tint-c { background: var(--tint-c-ink); }
.builder-kind-list__dot.tint-d { background: var(--tint-d-ink); }

/* Small "eyebrow" section labels, orange highlight accent — used on the
   Homepage hero (already existed via .builder-carousel__eyebrow) and now
   also the Contact page intro, both driven by --accent-2. */
.pp-eyebrow { display: block; font-size: var(--fs-11); font-weight: 800; text-transform: uppercase; letter-spacing: .14em; color: var(--accent-2); margin-bottom: 8px; }

/* ---- Round O — Add to Inquiry button (renders on the product card itself; the
   inquiry-cart drawer/badge widget it opens lives site-wide in frontend-body.css) ---- */
.builder-add-to-inquiry {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; width: 100%;
  justify-content: center;
}
/* Real bug found via mobile screenshot review: Icons::svg() returns a
   bare <svg> with no width/height (every other caller — e.g.
   .builder-save-toggle svg above — constrains it in CSS, same as this
   needed and was missing), so the "+" rendered at the browser's default
   intrinsic SVG size (huge) instead of a small inline icon. */
.builder-add-to-inquiry svg { width: 16px; height: 16px; flex: 0 0 auto; }
.builder-add-to-inquiry.is-added { background: var(--success, #1a9c5e); color: #fff; border-color: transparent; }
.builder-add-to-inquiry--detail { margin-top: 0; margin-bottom: 14px; }

/* ---- V.109 — Product/Service detail page redesign (owner: old layout "not
   good enough anymore"). Reuses every real data source already in place
   (cover/gallery media, spec grid, tabs, variants, related items, inquiry
   form) — this block is layout/visual only, no new fields. ---- */

/* Gallery + info hero, side by side on desktop, stacked on mobile. Kept as
   its own grid (not `.builder-columns`) since it needs a different, wider
   breakpoint than the generic 2-column helper (a real gallery needs more
   room before stacking than a text column does). */
.builder-product-hero {
  display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 40px; align-items: start; margin-bottom: 8px;
}
@media (max-width: 860px) {
  .builder-product-hero { grid-template-columns: 1fr; gap: 24px; }
}

/* Large selected image + thumbnail rail (PageParts::productGalleryHero()).
   Replaces the old "cover image, then small gallery thumbnails stacked
   below with no visible relationship to it" layout. */
.builder-product-gallery__main {
  border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; background: var(--surface);
  box-shadow: var(--shadow); aspect-ratio: 1 / 1;
}
.builder-product-gallery__main-img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.builder-product-gallery__thumbs {
  display: flex; gap: 10px; margin-top: 12px; overflow-x: auto; padding-bottom: 2px;
}
.builder-product-gallery__thumb {
  flex: 0 0 auto; width: 72px; height: 72px; padding: 0; border-radius: var(--rs); overflow: hidden;
  border: 2px solid transparent; background: var(--surface); cursor: pointer; opacity: .72;
  transition: opacity .15s ease, border-color .15s ease;
}
.builder-product-gallery__thumb:hover { opacity: 1; }
.builder-product-gallery__thumb.is-active { opacity: 1; border-color: var(--accent); }
.builder-product-gallery__thumb-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.builder-product-gallery--empty .builder-image--empty { aspect-ratio: 1 / 1; display: flex; align-items: center; justify-content: center; }

/* Builder `product-gallery` block — thumbnail-strip variant, sized
   (small/medium/large) via a modifier class the renderer applies, width
   constrained by an inline style per its widthMode/customPct setting. */
.pb-product-gallery-strip { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; }
.pb-product-gallery-strip__thumb { position: relative; flex: 0 0 auto; border-radius: var(--rs); overflow: hidden; background: var(--surface); }
.pb-product-gallery-strip__img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pb-product-gallery-strip--small .pb-product-gallery-strip__thumb { width: 56px; height: 56px; }
.pb-product-gallery-strip--medium .pb-product-gallery-strip__thumb { width: 84px; height: 84px; }
.pb-product-gallery-strip--large .pb-product-gallery-strip__thumb { width: 112px; height: 112px; }
.pb-product-gallery-strip__more { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.55); color: #fff; font-weight: 700; font-size: var(--fs-13); }

/* Info column — category eyebrow, H1, short description, spec grid as a
   tighter card (was plain bordered boxes sitting directly on the page
   background; now a single elevated card matching the rest of the page's
   card-based language). */
.builder-product-hero__info h1 { margin: 0 0 12px; }
/* V.110 — real SKU (task #2, "SKU is never shown as visible text"): small
   muted text right under the H1, only rendered for a product that actually
   has one (`PageTemplates::productOrService()` skips this entirely
   otherwise — no empty "SKU:" label). */
.builder-product-hero__sku { color: var(--muted); font-size: var(--fs-12); margin: 0 0 10px; font-weight: 600; }
/* V.110 — Vehicle Type / Tags taxonomy pills (task #2, "Vehicle Type and
   Tags taxonomy never render anywhere"). Reuses the existing global
   .pill chip component (tokens.css) — no new chip style invented. */
.builder-product-hero__tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 14px; }
/* V.110 — real price on the detail page itself (task #2, "Price never
   shows on the product/service DETAIL page"). Same text `PageParts::
   pricingLabel()` already renders on every product card, just larger/bolder
   here since it's the page's own primary price display, not a card chip. */
.builder-product-hero__price { font-size: var(--fs-20); font-weight: 800; color: var(--accent); margin: 0 0 16px; }
.builder-product-hero__desc { color: var(--muted); font-size: var(--fs-14); line-height: 1.65; margin: 0 0 20px; }
.builder-product-hero__info .pp-spec-grid {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--shadow);
  padding: 16px; margin: 0; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}
.builder-product-hero__info .pp-spec-grid .spec { border: none; border-radius: 0; padding: 4px 10px; border-left: 2px solid var(--line); }

/* Tabs wrapped in a card (was bare on the page background). Functional
   tab-switching JS (assets/js/frontend-tabs.js) is untouched. */
.builder-product-tabs-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--shadow);
  padding: 24px; margin-top: 32px;
}
.builder-product-tabs-card .builder-tabs--empty { display: none; }
/* Nav strip modernized: was a plain flex-wrap row (already broke to a new
   line on narrow content); this makes it scroll horizontally instead of
   wrapping, so the active underline row never gets tall on a phone, and
   gives the active tab a filled pill instead of only an underline. */
.builder-product-tabs-card .builder-tabs__nav {
  flex-wrap: nowrap; overflow-x: auto; gap: 6px; margin: -4px -4px 20px; padding: 4px 4px 12px;
}
.builder-product-tabs-card .builder-tabs__nav-btn {
  flex: 0 0 auto; border-radius: 999px; padding: 10px 16px; margin-bottom: 0; border-bottom: none;
}
.builder-product-tabs-card .builder-tabs__nav-btn.is-active { background: var(--soft); color: var(--accent); }
@media (max-width: 640px) {
  .builder-product-tabs-card { padding: 18px; }
}

/* Variants + Related Products sections (V.105) — visual weight matched to
   the rest of the redesigned page: Variants restyled from a plain
   bordered `<ul>` list into the same card-grid language Related Products
   already uses, no markup change (PageParts::productVariants() still emits
   a plain <ul><li><a>>). */
.builder-product-variants {
  display: flex; flex-wrap: wrap; gap: 10px; list-style: none; margin: 0; padding: 0;
}
.builder-product-variants li {
  border: none; padding: 0;
}
.builder-product-variants li a {
  display: inline-flex; align-items: center; gap: 8px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 999px; padding: 10px 18px; font-weight: 700; font-size: var(--fs-13);
  transition: border-color .15s ease, color .15s ease;
}
.builder-product-variants li a:hover { border-color: var(--accent); color: var(--accent); }

/* Asymmetric main/sidebar ratio for the product detail page only — every
   other 2-column `.builder-columns--2` section (Contact, etc.) keeps the
   shared 1fr/1fr default. Scoped so `PageTemplates::columns()` itself
   stays untouched. Only applies above the existing 700px stack breakpoint
   (frontend-elements.css line ~42) — mobile stacking is unaffected. */
@media (min-width: 701px) {
  .builder-product-detail .builder-columns--2 { grid-template-columns: 1.7fr 1fr; }
}

/* V.110 — Blog post detail page: real excerpt lead paragraph + author
   byline (field-render-gap fix, task #5). Both render only when the real
   data is present (`PageTemplates::blogPost()`) — this is presentation
   only, no placeholder text baked into the CSS. */
.builder-blog-post__byline { color: var(--muted); font-size: var(--fs-13); margin: 0 0 18px; }
.builder-blog-post__byline-sep { margin: 0 2px; }
.builder-blog-post__excerpt { font-size: var(--fs-18); color: var(--muted); line-height: 1.6; margin: 0 0 24px; font-weight: 500; }
