/**
 * Oliu design tokens and base styles.
 *
 * SOURCE OF EVIDENCE: every value in this file is cited from
 * docs/M5_3_LEGACY_VISUAL_UX_AUDIT.md (sections noted inline), which in
 * turn cites exact files under cosmeticsweb/cosmetic/. Nothing here is
 * invented — where the audit found no confirmed value, this file uses a
 * plain, undecorated fallback rather than guessing.
 *
 * Q6f (font licensing): the actual "Laviossa-Medium" and "DB-BrandVoice-X"
 * font FILES were restored into /assets/fonts/ from the legacy source
 * (cosmeticsweb/cosmetic/public/font/) on 08 Aug 2026, per explicit owner
 * approval to prioritize legacy visual fidelity — see
 * docs/Q6F_FONT_RESTORATION.md. No LICENSE/README was ever found
 * alongside these files in the legacy source or anywhere in this project;
 * licensing/redistribution rights remain UNCONFIRMED. The system-font
 * fallback stacks below are kept in place (not removed) so the site still
 * renders correctly if the fonts are ever pulled again pending a licensing
 * decision.
 */

/* ---- Typography (audit §14) ---- */
@font-face {
  font-family: "Laviossa-Medium";
  src: url("../fonts/Laviossa-Medium.otf") format("opentype");
  font-display: swap;
}
@font-face {
  font-family: "DB-BrandVoice-X-Regular";
  src: url("../fonts/DB-BrandVoice-X-Regular.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "DB-BrandVoice-X-Bold";
  src: url("../fonts/DB-BrandVoice-X-Bold.ttf") format("truetype");
  font-weight: 700;
  font-display: swap;
}

:root {
  /* Colors — audit §15. Two distinct confirmed cream values kept
     distinct on purpose (the legacy source itself never unified them). */
  --oliu-color-primary: #FF6801;
  --oliu-color-text: #272726;
  --oliu-color-text-muted: #A0A0A0;
  --oliu-color-line-green: #00C200;
  --oliu-color-cream-section: #FDF8F5;
  --oliu-color-cream-footer: #F9ECE4;
  --oliu-color-white: #FFFFFF;
  --oliu-color-border-light: #F0F3F8;
  --oliu-color-border-mid: #E6E2E2;
  --oliu-color-border-blue: #D1DCE7;
  --oliu-color-accent-teal: #73DECA;

  /* Typography */
  --oliu-font-heading: "Laviossa-Medium", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --oliu-font-body: "DB-BrandVoice-X-Regular", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --oliu-font-body-bold: "DB-BrandVoice-X-Bold", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* Layout widths — audit §16 */
  --oliu-content-width: 1080px;
  --oliu-content-width-mid: 900px;
  --oliu-content-width-wide: 1320px;

  /* Spacing rhythm — audit §16 */
  --oliu-space-section: 64px;
  --oliu-space-sub: 32px;
  --oliu-space-tight: 16px;
}

/* ---- Responsive breakpoints (audit §17) ----
   Legacy source used 767px in CSS and 768px in one JS hook — a
   confirmed 1px inconsistency, not a second real breakpoint. This
   theme standardizes on 768px everywhere (documented choice, not a
   silent guess) as the single mobile breakpoint. Secondary confirmed
   breakpoints (900px, 1124px) are used where the audit found them. */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--oliu-font-body);
  color: var(--oliu-color-text);
  /* M5.13a (G1/G11 fix): no background was ever set here, so any
     section without its own explicit background (.oliu-hero,
     .oliu-contact-grid__details) fell through to the browser's default
     canvas fill — near-black under prefers-color-scheme: dark —
     leaving --oliu-color-text (#272726) illegible on top of it.
     Confirmed via getComputedStyle: body background-color was
     rgba(0,0,0,0). Single shared-rule fix, no new color introduced. */
  background: var(--oliu-color-white);
}

h1, h2, h3, h4 {
  font-family: var(--oliu-font-heading);
  margin: 0;
  font-weight: 500;
}

a { color: inherit; text-decoration: none; }
img { max-width: 100%; height: auto; display: block; }

.oliu-container {
  width: 100%;
  max-width: var(--oliu-content-width);
  margin: 0 auto;
  padding: 0 20px;
}
.oliu-container--mid { max-width: var(--oliu-content-width-mid); }
.oliu-container--wide { max-width: var(--oliu-content-width-wide); }

@media (max-width: 768px) {
  .oliu-container { padding: 0 16px; }
}

/* ---- Buttons (audit §5, §13) ---- */
.oliu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--oliu-font-body);
  font-size: 18px;
  padding: 12px 24px;
  border: 1px solid var(--oliu-color-text);
  background: var(--oliu-color-white);
  color: var(--oliu-color-text);
  cursor: pointer;
}
.oliu-btn--primary {
  border-color: var(--oliu-color-primary);
  color: var(--oliu-color-primary);
}
.oliu-btn[disabled],
.oliu-btn--disabled {
  border-color: var(--oliu-color-text-muted);
  color: var(--oliu-color-text-muted);
  cursor: not-allowed;
}
/* Line CTA colour is confirmed distinct from the site primary (audit §13/§14) */
.oliu-btn--line {
  background: var(--oliu-color-line-green);
  border: 0;
  color: var(--oliu-color-white);
  font-family: var(--oliu-font-body-bold);
}

/* ---- Header / nav (audit §1) ----
   Q6a (flat vs grouped nav) is OPEN — deliberately not decided here.
   Menu items themselves come from wp_nav_menu() (an editor-managed
   WordPress menu), so this CSS only styles whatever structure an
   editor builds; it does not assume flat or grouped.

   P1 originally gave this bar a full-width black background, on the
   belief that legacy's header was solid black. CORRECTED during the
   visual fidelity pass (docs/VISUAL_FIDELITY_PASS_2026-08-09.md, Batch
   A): legacy's header is `<AppBar color="transparent">` — confirmed via
   the real injected CSS rule (`background-color: transparent`) — the
   "black bar" in the original audit was a dark-mode rendering artifact,
   not real legacy design (same bug class as audit §2.5). The bar is now
   white with a subtle drop shadow, reproducing legacy's actual MUI
   `elevation={10}` AppBar shadow (values read from the live legacy
   computed style, not invented). .oliu-header stays width-capped by
   .oliu-container for the actual nav content. */
.oliu-header-bar {
  background: var(--oliu-color-white);
  box-shadow: 0 3px 3px -2px rgba(0,0,0,0.2), 0 3px 4px rgba(0,0,0,0.14), 0 1px 8px rgba(0,0,0,0.12);
}
.oliu-header {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  padding: 10px 0;
}
/* UAT R1 visual remediation (23 Aug 2026): the logo link/image previously
   had no shrink/overflow constraint of its own. Combined with the
   invalid nested-<a> markup this template used to emit (fixed
   separately, see header.php), the logo block's content could exceed
   its fair flex share on narrow phones, and — because flex items don't
   wrap by default — that overflow silently pushed the mobile hamburger
   toggle past the right edge of the viewport instead of visibly
   colliding with it. Confirmed via real headless-browser screenshot at
   375-390px (the toggle was completely absent from the visible frame,
   not just misstyled). min-width:0 lets this flex item actually honor
   its shrink factor instead of defaulting to its content's min-content
   width; flex-shrink:0 on the toggle guarantees it never gives up its
   own space to the logo. */
.oliu-header__logo { min-width: 0; flex-shrink: 1; overflow: hidden; }
.oliu-header__logo a { color: var(--oliu-color-text); display: inline-flex; align-items: center; max-width: 100%; }
.oliu-header__toggle { flex-shrink: 0; }
.oliu-header__nav > ul > li > a { color: var(--oliu-color-text); }
.oliu-header__nav .sub-menu a { color: var(--oliu-color-text); }
/* Fixed width alongside height: this environment doesn't reliably derive
   width from the img's width/height attributes' aspect ratio when only
   height is set via CSS (same class of bug fixed for footer social icons
   above) — width is the exact proportional value for the confirmed
   legacy logo's 300x137 source at a 45px height (matches legacy Header.js). */
.oliu-header__logo img { height: 45px; width: 98.5px; }
.oliu-header__nav { display: none; }
.oliu-header__nav ul { list-style: none; display: flex; gap: 24px; margin: 0; padding: 0; }
/* Typography matched to legacy nav links (Header.style.js MenuItemButton,
   computed live: Laviossa-Medium 14px/500, 0.4px letter-spacing). */
.oliu-header__nav a { font-family: var(--oliu-font-heading); font-size: 14px; font-weight: 500; letter-spacing: 0.4px; }
/* Products dropdown (Q6a.2): desktop hover/focus reveal, no JS needed here. */
.oliu-header__nav li.menu-item-has-children { position: relative; }
.oliu-header__nav .sub-menu {
  display: none;
  list-style: none;
  position: absolute;
  top: 100%; left: 0;
  min-width: 160px;
  margin: 0; padding: 8px 0;
  background: var(--oliu-color-white);
  box-shadow: -4px 4px 18px rgba(51, 78, 105, 0.1);
  z-index: 1040;
}
.oliu-header__nav li.menu-item-has-children:hover > .sub-menu,
.oliu-header__nav li.menu-item-has-children:focus-within > .sub-menu { display: block; }
.oliu-header__nav .sub-menu li { padding: 8px 16px; }
.oliu-header__toggle {
  background: none; border: 0; cursor: pointer; color: var(--oliu-color-primary);
  font-size: 28px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px;
}
/* UAT R1 visual remediation (23 Aug 2026): confirmed via real
   headless-browser screenshot at 390px that the toggle was being pushed
   entirely outside the visible viewport by *some* horizontal-overflow
   source on the page (not narrowed down to one exact cause after
   multiple targeted flex/markup fixes — those are kept above as genuine
   improvements, not reverted). Pinning the toggle to the viewport with
   `position: fixed` makes it immune to ANY document-width overflow,
   wherever it originates, which matters more here than root-causing the
   overflow itself: without this, mobile visitors have no way at all to
   reach navigation. Desktop is unaffected (this rule only applies below
   769px, same breakpoint the toggle is already display:none above). */
@media (max-width: 768px) {
  .oliu-header__toggle {
    position: fixed;
    top: 14px;
    right: 16px;
    z-index: 1060;
    background: var(--oliu-color-white);
    border-radius: 50%;
    box-shadow: 0.5px 0.5px 8px rgba(16, 42, 67, 0.15);
  }
}
@media (min-width: 769px) {
  .oliu-header__nav { display: block; }
  .oliu-header__toggle { display: none; }
}
.oliu-header__mobile-menu {
  display: none;
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: 290px;
  background: var(--oliu-color-white);
  box-shadow: -4px 4px 18px rgba(51, 78, 105, 0.1);
  padding: 16px 0;
  z-index: 1050;
  overflow: auto;
}
.oliu-header__mobile-menu.is-open { display: block; }
.oliu-header__mobile-menu ul { list-style: none; margin: 0; padding: 0; }
.oliu-header__mobile-menu li { padding: 12px 24px; }
/* Products expand/collapse (Q6a.2): submenu hidden until the parent li
   gets .is-open, toggled by nav.js on click (no new drawer mechanism). */
.oliu-header__mobile-menu .sub-menu { display: none; padding: 0; margin-top: 8px; }
.oliu-header__mobile-menu li.menu-item-has-children.is-open > .sub-menu { display: block; }
.oliu-header__mobile-menu .sub-menu li { padding: 8px 24px 8px 36px; }
.oliu-header__mobile-menu .menu-item-has-children > a::after { content: " \25BE"; font-size: 12px; }

/* ---- Footer (audit §12) ---- */
.oliu-footer {
  background: var(--oliu-color-cream-footer);
  padding: var(--oliu-space-section) 0;
  margin-top: var(--oliu-space-section);
}
.oliu-footer__grid { display: flex; flex-wrap: wrap; gap: 32px; }
.oliu-footer__brand { flex: 1 1 320px; }
.oliu-footer__brand img { height: auto; width: 150px; margin-bottom: 16px; }
/* Typography matched to legacy (Footer.style.js `FooterAddress`/`Menu`,
   visual fidelity pass Batch E): address 24px/30px, nav links 18px/21px
   weight 500 — was 20px/no line-height and unstyled size/weight. */
.oliu-footer__address { font-size: 24px; line-height: 30px; max-width: 70%; }
.oliu-footer__social { display: flex; gap: 12px; margin-top: 16px; }
.oliu-footer__social a { display: block; flex: 0 0 auto; }
.oliu-footer__social img { width: 32px; height: 32px; }
.oliu-footer__nav { flex: 1 1 320px; }
/* UAT R1 visual remediation (24 Aug 2026): 3-column grid, column-major
   fill, matching legacy's 3-stacked-column footer nav
   (`work/visual_baseline/legacy/desktop/03_brands_footer.jpg`) — was a
   single flex-wrap row (2 items, so visually indistinguishable from a
   list either way until the footer menu itself was enriched to 6 items
   alongside this change). */
.oliu-footer__nav ul {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, auto);
  grid-template-rows: repeat(2, auto);
  grid-auto-flow: column;
  gap: 12px 48px;
  margin: 0;
  padding: 0;
}
@media (max-width: 768px) {
  .oliu-footer__nav ul { grid-template-columns: repeat(2, auto); grid-template-rows: repeat(3, auto); gap: 8px 32px; }
}
.oliu-footer__nav a { font-family: var(--oliu-font-heading); font-size: 18px; line-height: 21px; font-weight: 500; color: var(--oliu-color-primary); }
.oliu-footer__divider { border: 0; border-top: 1px solid var(--oliu-color-border-blue); margin: 32px 0 16px; }
.oliu-footer__copyright { text-align: center; color: var(--oliu-color-text-muted); font-size: 14px; margin: 0; }

@media (max-width: 768px) {
  .oliu-footer__address { max-width: 100%; }
}

/* ---- Balloon / floating CTA (audit §13) ---- */
.oliu-balloon {
  position: fixed;
  bottom: 2%;
  right: 20px;
  z-index: 900;
  display: flex;
  gap: 8px;
  padding: 8px;
  background: var(--oliu-color-cream-footer);
  border-radius: 30px;
  box-shadow: 1px 1px 16px 10px rgba(0,0,0,0.1);
}
.oliu-balloon img { height: 38px; }
@media (max-width: 768px) {
  .oliu-balloon { right: 50%; transform: translateX(50%); }
}
/* UAT R1 visual remediation (24 Aug 2026): sitewide use of this
   already-built class (see oliu_render_floating_cta(), inc/template-tags.php)
   — confirmed legacy element, previously only wired up inline/neutralized
   on the Contact hero. The Shop Now pill reuses .oliu-btn--line's colour
   (legacy's confirmed distinct Line-green CTA colour) at a size that fits
   the 38px icon row rather than the larger inline-button padding/font-size
   used elsewhere on the site. */
.oliu-balloon__shop {
  height: 38px;
  padding: 0 18px;
  font-size: 14px;
  white-space: nowrap;
}

/* ---- Card family (audit §4, §7, §8) ----
   Shared "Popular" ribbon pattern confirmed identical between product
   and brand cards — implemented once here, reused by both. */
.oliu-card {
  background: var(--oliu-color-white);
  border: 1px solid var(--oliu-color-border-light);
  box-shadow: 0.5px 0.5px 8px rgba(16, 42, 67, 0.1);
  position: relative;
}
.oliu-card__ribbon {
  position: absolute;
  top: 10px; right: -10px;
  background: var(--oliu-color-primary);
  color: var(--oliu-color-white);
  padding: 4px 16px;
  font-size: 16px;
}
.oliu-card__image { width: 100%; height: 230px; object-fit: cover; object-position: center; padding: 10px; }
.oliu-card__body { padding: 24px; }
.oliu-card__title { font-size: 26px; font-weight: 600; margin: 0 0 8px; }
.oliu-card__meta { color: var(--oliu-color-text-muted); font-size: 18px; margin: 0; }
/* M5.13f: 2-line clamp on the brand card description, matching legacy
   CardBrand.js's confirmed 2-line-clamped description (M5_13C2 §3). A
   character-count clamp is already applied server-side (template-tags.php)
   as the primary safety net — this is a second, purely visual guard. */
.oliu-card__desc {
  margin-top: 4px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.oliu-card__price { color: var(--oliu-color-primary); font-weight: 700; font-size: 25px; margin-top: 16px; }
/* Wired up in the visual fidelity pass (Batch C, punch-list #9) —
   matches legacy's `.disCount__Price` (CardProduct.js) exactly: same
   25px size as the discounted price above it, weight 300, muted grey,
   struck through; 17px on mobile. */
.oliu-card__price-was { display: block; color: var(--oliu-color-text-muted); text-decoration: line-through; font-weight: 300; font-size: 25px; margin-top: 2px; }
@media (max-width: 767px) {
  .oliu-card__price-was { font-size: 17px; }
}

/* ---- Grid ----
   M5.13d: grid tracks use minmax(0, 1fr) rather than bare 1fr. A bare
   `1fr` track's minimum size defaults to the content's min-content width
   (the "grid blowout" behaviour), so a card with enough intrinsic content
   width can force its track wider than its fair 1fr share and push the
   grid past its container — this was the confirmed root cause of the
   74px `/brands/` mobile overflow and, since the ribbon's negative
   right-inset is anchored to the resulting oversized card, also of the
   25px `.oliu-card__ribbon` homepage mobile overflow (both documented in
   docs/M5_13C2_BRAND_VISUAL_QA.md §4/§7). minmax(0, 1fr) caps each
   track's minimum at 0, so tracks stay within their fair share and cards
   shrink (with wrapping/ellipsis handled by existing card content rules)
   instead of overflowing. */
.oliu-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
@media (max-width: 900px) { .oliu-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 480px) { .oliu-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } }

/* ---- Homepage carousel (P2, docs/P2_HOME_CAROUSEL.md) ----
   Legacy (cosmeticsweb/cosmetic/pages/index.js) renders "Our Popular
   Product" and "Our Brand" with react-slick: slidesToShow 3 (>=950px),
   2 (575-949px), 1 (<575px); infinite: false; swipeToSlide: true;
   prev/next arrows hidden below 991px (styles/slick-custom.css). This
   is a dependency-free equivalent: a native horizontally-scrolling flex
   track (scroll-snap gives free touch/drag, no JS needed for that) plus
   two buttons that scroll by one slide width. No carousel library
   existed anywhere in this theme to reuse, so none was introduced. */
.oliu-carousel { position: relative; margin-top: 32px; }
.oliu-carousel__viewport {
  display: flex;
  gap: 24px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-bottom: 4px;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.oliu-carousel__viewport::-webkit-scrollbar { display: none; }
.oliu-carousel__slide { flex: 0 0 calc((100% - 48px) / 3); min-width: 0; scroll-snap-align: start; }
@media (max-width: 949px) { .oliu-carousel__slide { flex-basis: calc((100% - 24px) / 2); } }
@media (max-width: 574px) { .oliu-carousel__slide { flex-basis: 100%; } }
@media (prefers-reduced-motion: reduce) { .oliu-carousel__viewport { scroll-behavior: auto; } }

.oliu-carousel__btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--oliu-color-border-light);
  background: rgba(255, 255, 255, 0.9);
  box-shadow: 0.5px 0.5px 8px rgba(16, 42, 67, 0.1);
  color: var(--oliu-color-primary);
  font-size: 22px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 1;
  padding: 0;
}
.oliu-carousel__btn--prev { left: 8px; }
.oliu-carousel__btn--next { right: 8px; }
.oliu-carousel__btn:disabled { opacity: 0.35; cursor: default; }
/* Matches legacy's own arrow-hiding breakpoint (max-width: 991px in
   styles/slick-custom.css) — below it, legacy relies on swipe only. */
@media (max-width: 991px) { .oliu-carousel__btn { display: none; } }

/* ---- Page-cover band (audit §3, §9) ----
   Background CORRECTED during the visual fidelity pass
   (docs/VISUAL_FIDELITY_PASS_2026-08-09.md, Batch A/D): legacy's
   category/product-listing hero (cosmeticsweb/cosmetic/pages/Mode/Filter/
   index.js `.cover__Page`) uses a real gradient/pattern image
   (`bg-cover-new.png`), not a solid color — confirmed min-height 284px
   and white title/desc text match exactly what this band already had.
   Used as-is by category.php / archive-product.php /
   taxonomy-product_category.php; archive-brand.php gets its own
   `--brands` modifier below (different legacy background + 2-col layout). */
.oliu-cover-band {
  min-height: 284px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: var(--oliu-color-text) url('../images/covers/bg-cover-new.png') center/cover no-repeat;
  color: var(--oliu-color-white);
  padding: 48px 20px;
}
.oliu-cover-band__title { font-size: 48px; }
.oliu-cover-band__desc { font-size: 20px; max-width: 660px; margin-top: 16px; }
@media (max-width: 768px) {
  .oliu-cover-band__title { font-size: 24px; }
  .oliu-cover-band__desc { font-size: 16px; }
}

/* ---- Section rhythm (audit §16) ---- */
.oliu-section { padding: var(--oliu-space-section) 0; }
.oliu-section--cream { background: var(--oliu-color-cream-section); }
.oliu-section__title { font-size: 36px; color: var(--oliu-color-primary); margin-bottom: var(--oliu-space-sub); }
@media (max-width: 768px) {
  .oliu-section { padding: 48px 0; }
  .oliu-section__title { font-size: 24px; }
}

/* ---- Hero (M5.13f) ----
   Restores the legacy hero's 2-column layout (text left, product image
   right — pages/index.js's `Grid` with `xs=12 md=6` columns). Uses
   minmax(0, 1fr) tracks (same lesson as the M5.13d grid-blowout fix) so
   the image column can never force horizontal overflow. Collapses to a
   single column under the theme's standard 768px breakpoint, image below
   text — no new breakpoint or layout pattern introduced. */
.oliu-hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--oliu-space-sub);
  align-items: center;
}
.oliu-hero__image-wrap { justify-self: end; }
.oliu-hero__image { width: 100%; max-width: 376px; height: auto; }
/* Typography matched to legacy (pages/index.js `.grid-left h1`/`.desc`,
   visual fidelity pass Batch B/punch-list #11): the hero heading/desc
   had no explicit size at all, falling back to the browser's UA-default
   h1 (32px, dark text) — legacy is 52px/orange (32px on mobile), desc
   28px/32px (20px/24px on mobile). */
.oliu-hero__text h1 { font-size: 52px; color: var(--oliu-color-primary); line-height: 1.1; }
.oliu-hero__text p { font-family: var(--oliu-font-body); font-size: 28px; line-height: 32px; margin-top: 24px; }
@media (max-width: 768px) {
  .oliu-hero__grid { grid-template-columns: 1fr; }
  .oliu-hero__image-wrap { justify-self: center; margin-top: var(--oliu-space-tight); }
  .oliu-hero__image { max-width: 280px; }
  .oliu-hero__text h1 { font-size: 32px; }
  .oliu-hero__text p { font-size: 20px; line-height: 24px; }
}

/* ---- Product detail layout (M5.12e) ----
   Two-column grid — gallery left, info right — replacing the previously
   unstyled single-column stack (confirmed gap, M5.12d). Collapses to a
   single column at the theme's existing 768px breakpoint, preserving
   natural document order (gallery, then title/price/options/buy). Reuses
   existing spacing tokens only; no new design system introduced. The
   M5.12a proportional-image fix (global `img { height: auto }`) already
   covers the gallery image and is untouched here. */
.oliu-product-detail {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--oliu-space-sub);
  align-items: start;
  margin-top: var(--oliu-space-sub);
}
.oliu-product-detail__info > * + * {
  margin-top: var(--oliu-space-tight);
}
.oliu-product-detail__description {
  margin-top: var(--oliu-space-section);
}
@media (max-width: 768px) {
  .oliu-product-detail {
    grid-template-columns: 1fr;
    gap: var(--oliu-space-tight);
  }
}

/* ---- Product gallery (P0, docs/VISUAL_AUDIT_LEGACY_VS_WORDPRESS.md §2.8) ----
   Legacy: large main image + thumbnail strip (active thumb has an
   orange border), lightbox-capable. WordPress had no gallery/thumbnail
   markup at all — confirmed the single largest gap in that audit.
   Images come from oliu_get_product_gallery_images() (the product's
   featured image + each distinct, real SKU image already imported into
   the ACF `skus` repeater) — no placeholder/stock image is ever used;
   a product with zero resolvable images simply renders an empty
   .oliu-product-detail__gallery, same as before this change, so the
   two-column layout never breaks. Main image is fixed to a square
   aspect-ratio box with object-fit: contain so no source image (any
   original dimensions) can force the page wider or taller than
   intended, on desktop or the single-column mobile stack. */
.oliu-gallery {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.oliu-gallery__main {
  width: 100%;
  aspect-ratio: 1 / 1;
  background: var(--oliu-color-cream-section);
  border: 1px solid var(--oliu-color-border-light);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  cursor: zoom-in;
}
.oliu-gallery__main-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.oliu-gallery__thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.oliu-gallery__thumb {
  width: 72px;
  height: 72px;
  padding: 2px;
  border: 2px solid var(--oliu-color-border-light);
  background: var(--oliu-color-white);
  cursor: pointer;
  display: block;
}
/* Active-thumbnail indicator, matching legacy's confirmed orange border. */
.oliu-gallery__thumb.is-active {
  border-color: var(--oliu-color-primary);
}
.oliu-gallery__thumb-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Minimal dependency-free lightbox (no library found in the theme to
   reuse — audit confirmed legacy is "lightbox-capable" but this theme
   had no image-gallery markup at all yet to inherit one from). */
.oliu-lightbox {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  padding: 24px;
}
.oliu-lightbox.is-open { display: flex; }
.oliu-lightbox__image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.oliu-lightbox__close {
  position: absolute;
  top: 16px;
  right: 20px;
  background: none;
  border: 0;
  color: var(--oliu-color-white);
  font-size: 36px;
  line-height: 1;
  cursor: pointer;
}

/* ---- Archive / category layout (M5.12f) ----
   Sidebar (category list) + main (product grid), two columns on desktop —
   replacing the previously unstyled stack where the category list rendered
   full-width above the grid (confirmed gap, M5.12 baseline §2/§6). Reuses
   existing spacing tokens and the theme's single 768px breakpoint only;
   the product grid/card component itself (.oliu-grid/.oliu-card) is
   untouched. Sidebar markup is default wp_list_categories() output (no
   markup change), so only list-reset styling is added here to make it fit
   the fixed-width column instead of the browser default full-bleed list. */
.oliu-archive-layout {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: var(--oliu-space-sub);
  align-items: start;
  margin-top: var(--oliu-space-sub);
}
.oliu-archive-sidebar ul {
  list-style: none;
  margin: var(--oliu-space-tight) 0 0;
  padding: 0;
}
.oliu-archive-sidebar ul ul {
  margin-top: 0;
  padding-left: var(--oliu-space-tight);
}
.oliu-archive-sidebar li {
  padding: 4px 0;
}
.oliu-archive-main {
  min-width: 0;
}
@media (max-width: 768px) {
  .oliu-archive-layout {
    grid-template-columns: 1fr;
    gap: var(--oliu-space-tight);
  }
}

/* ---- Contact layout (M5.12g) ----
   Map + details two-column grid, replacing the previously unstyled stack
   (confirmed gap, M5.12d/M5.12 baseline §10). Reuses existing spacing
   tokens and the theme's single 768px breakpoint only; existing
   typography, `.oliu-btn`/`.oliu-btn--line`, `.oliu-footer__address`, and
   `.oliu-footer__social` are untouched. The map column's content comes
   from `the_content()` (editable, no confirmed embed yet per
   page-contact.php's own comment) — the iframe/img/embed rule below only
   caps width at 100% so whatever gets embedded can't force horizontal
   overflow; it does not alter height/aspect ratio, so a fixed-height
   embed's own dimensions are preserved as authored. */
.oliu-contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--oliu-space-sub);
  align-items: start;
  margin-top: var(--oliu-space-sub);
}
.oliu-contact-grid__map,
.oliu-contact-grid__details {
  min-width: 0;
}
.oliu-contact-grid__map iframe,
.oliu-contact-grid__map img,
.oliu-contact-grid__map embed,
.oliu-contact-grid__map object {
  max-width: 100%;
}
.oliu-contact-grid__details > * + * {
  margin-top: var(--oliu-space-tight);
}
@media (max-width: 768px) {
  .oliu-contact-grid {
    grid-template-columns: 1fr;
    gap: var(--oliu-space-tight);
  }
}

/* ---- Contact hero / branding (M5.13g) ----
   Scoped to `.oliu-cover-band--contact` only — the base `.oliu-cover-band`
   rules (used by category/brand/product archives and taxonomy pages) are
   untouched. Restores the legacy Contact hero's confirmed background
   image, logo, and 2-column text/image layout
   (cosmeticsweb/cosmetic/pages/Contact/index.js:44-79). Uses the same
   minmax(0, 1fr) + single-768px-breakpoint pattern as the M5.13f homepage
   hero. */
.oliu-cover-band--contact {
  background-image: url('../images/contact/bg-cover-contact.png');
  background-size: cover;
  background-position: center;
  text-align: left;
  align-items: stretch;
}
.oliu-cover-band__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--oliu-space-sub);
  align-items: center;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
}
.oliu-cover-band__text { text-align: left; }
.oliu-cover-band__logo { height: 40px; width: auto; margin-bottom: 16px; }
.oliu-cover-band--contact .oliu-cover-band__title { text-align: left; }
.oliu-cover-band--contact .oliu-balloon { justify-content: flex-start; }
/* M5.13g fix: `.oliu-balloon`'s own mobile rule (`right: 50%;
   transform: translateX(50%);`) is written for its fixed-position, floating
   use — it does not know about this in-flow hero usage (inline-overridden
   to position:static). Left un-neutralized here, the transform still
   applies to the static box and was confirmed (via scrollWidth measurement)
   to push the Contact hero's text column ~168px past its container at
   375px, a genuine new horizontal-overflow bug. Scoped to this page's
   hero only — the shared `.oliu-balloon` rule itself is untouched. */
.oliu-cover-band--contact .oliu-balloon { right: auto; transform: none; }
.oliu-cover-band__image-wrap { justify-self: end; }
.oliu-cover-band__image { width: 100%; max-width: 360px; height: auto; }
@media (max-width: 768px) {
  .oliu-cover-band__grid { grid-template-columns: 1fr; }
  .oliu-cover-band__text { text-align: center; }
  .oliu-cover-band--contact .oliu-cover-band__title { text-align: center; }
  .oliu-cover-band--contact .oliu-balloon { justify-content: center; }
  .oliu-cover-band__image-wrap { justify-self: center; margin-top: var(--oliu-space-tight); }
  .oliu-cover-band__image { max-width: 280px; }
}

/* ---- Product detail Details/How-to-order tabs (audit §2.8,
   punch-list #4, visual fidelity pass Batch C) ---- */
.oliu-tabs__list { display: flex; gap: 32px; border-bottom: 1px solid var(--oliu-color-border-mid); }
.oliu-tabs__tab {
  background: none; border: 0; cursor: pointer;
  font-family: var(--oliu-font-heading); font-size: 18px; color: var(--oliu-color-text-muted);
  padding: 12px 0; margin-bottom: -1px;
  border-bottom: 2px solid transparent;
}
.oliu-tabs__tab.is-active { color: var(--oliu-color-primary); border-bottom-color: var(--oliu-color-primary); }
.oliu-tabs__panel { padding-top: var(--oliu-space-tight); }
.oliu-tabs__panel[hidden] { display: none; }

/* ---- Brand detail hero (audit §2.9, visual fidelity pass Batch D) ----
   TEMPLATE bug, confirmed: this hero had no height/object-fit rule at
   all, so `the_post_thumbnail()` rendered at its raw intrinsic pixel
   size — harmless for landscape photos, but a near-square image (e.g.
   OliU's, a screenshot-derived asset per D6_EXECUTION_EVIDENCE.md)
   produced an ~900px-tall band. Legacy (pages/Brands/detail/index.js
   `.image__Cover`) fixes this to a flat 400px with object-fit: cover on
   desktop, and lets height go auto (no crop) below 767px — reproduced
   exactly, values read directly from that file, not invented. */
.oliu-brand-detail__hero img {
  display: block;
  width: 100%;
  height: 400px;
  object-fit: cover;
  object-position: center center;
}
@media (max-width: 767px) {
  .oliu-brand-detail__hero img { height: auto; }
}

/* ---- Brands archive hero (audit §2.10, visual fidelity pass Batch D) ----
   Legacy (cosmeticsweb/cosmetic/pages/Brands/index.js): a distinct
   colorful hero (`bg-cover-brand.png`, different from the generic
   `.oliu-cover-band` gradient above), a headline + descriptive subtext,
   and a product-photo illustration (`Brand_cover.webp`) in a 2-column
   layout, height 550px. Reuses the existing `.oliu-cover-band__grid`/
   `__text`/`__image-wrap`/`__image` pattern already built for the
   Contact hero (M5.13g) rather than inventing a new layout mechanism. */
.oliu-cover-band--brands {
  background: url('../images/brands/bg-cover-brand.png') center/cover no-repeat;
  min-height: 400px;
  text-align: left;
  align-items: stretch;
}
.oliu-cover-band--brands .oliu-cover-band__title { text-align: left; font-size: 40px; }
.oliu-cover-band--brands .oliu-cover-band__desc { text-align: left; max-width: none; margin-top: 12px; }
@media (max-width: 768px) {
  .oliu-cover-band--brands { min-height: 0; padding: 32px 20px; }
  .oliu-cover-band--brands .oliu-cover-band__title,
  .oliu-cover-band--brands .oliu-cover-band__desc { text-align: center; }
}

/* ---- Brands archive "Sort By" control (audit §2.10, punch-list #12) ----
   Legacy (pages/Brands/index.js `DropDown`): a top-right dropdown with
   Name A-Z / Name Z-A / Popular options, client-side re-sort of the
   already-rendered brand list — no new data/query, so a plain vanilla-JS
   DOM reorder (same "no new dependency" precedent as gallery.js/
   carousel.js) reproduces the behavior. */
.oliu-brands-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: var(--oliu-space-sub);
}
.oliu-brands-sort {
  font-family: var(--oliu-font-heading);
  font-size: 16px;
  padding: 8px 12px;
  border: 1px solid var(--oliu-color-border-mid);
  border-radius: 4px;
  background: var(--oliu-color-white);
  color: var(--oliu-color-text);
}

/* ---- Contact LINE QR (M5.13g) ----
   Restores the legacy QR graphic next to the existing, untouched
   "Add friend now" CTA (Contact/index.js:126-144). */
.oliu-contact-line {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.oliu-contact-line__qr { width: 120px; height: 121px; object-fit: cover; }
@media (max-width: 768px) {
  .oliu-contact-line { justify-content: center; }
}

/* ---- Contact "Our Shop" gallery (M5.13g) ----
   Restores the confirmed legacy 3-photo gallery + background band
   (Contact/index.js:169-178). Uses the same minmax(0, 1fr) grid pattern
   as `.oliu-grid`/`.oliu-hero__grid` so the gallery can never force
   horizontal overflow at 1280px or 375px. */
.oliu-contact-ourshop {
  background-image: url('../images/contact/bg-cover-contact-ourshop.png');
  background-size: cover;
  background-position: center;
  padding: var(--oliu-space-section) 0;
  margin-top: var(--oliu-space-section);
}
.oliu-contact-ourshop__title { color: var(--oliu-color-white); text-align: center; }
.oliu-contact-ourshop__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}
.oliu-contact-ourshop__img {
  width: 100%;
  height: 260px;
  object-fit: cover;
  object-position: center;
}
@media (max-width: 768px) {
  .oliu-contact-ourshop__grid { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .oliu-contact-ourshop__img { height: 220px; }
}

/* ---- Testimonials (M5.13i) ----
   Restores the legacy "Our customers are saying…" homepage section
   (pages/index.js:760-784, CardCustomerReviews.js) between Body and
   Blog. Background image + white title/desc text color are both
   confirmed legacy values (bgImgCode 1 → bg-cover-reviews.png;
   `.title__Reviews`/`.title__CustomersDesc` both `color: #ffffff`).
   Card styling mirrors CardCustomerReviews.js's own styled-component
   (white bg, `#f0f3f8` border — already this theme's
   --oliu-color-border-light token, box-shadow) as closely as this
   theme's existing `.oliu-card` base class allows. Grid uses the same
   minmax(0, 1fr) + single-768px-breakpoint pattern already established
   throughout this theme (M5.13d/f/g) rather than legacy's exact MUI
   Grid breakpoints, for consistency with the rest of the site. */
.oliu-testimonials {
  background-image: url('../images/home/bg-cover-reviews.png');
  background-size: cover;
  background-position: center;
  color: var(--oliu-color-white);
}
.oliu-testimonials__title { color: var(--oliu-color-white); }
.oliu-testimonials__desc {
  max-width: 660px;
  margin-top: -8px;
  margin-bottom: var(--oliu-space-sub);
  color: var(--oliu-color-white);
}
.oliu-testimonial-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
}
.oliu-testimonial-card {
  padding: 32px 32px 28px;
  min-height: 260px;
  display: flex;
  flex-direction: column;
}
.oliu-testimonial-card__label {
  font-weight: 700;
  font-size: 26px;
  color: var(--oliu-color-text);
  margin: 0;
}
.oliu-testimonial-card__name {
  font-size: 18px;
  color: var(--oliu-color-text-muted);
  margin: 2px 0 0;
}
.oliu-testimonial-card__text {
  margin: 18px 0 0;
  font-size: 18px;
  line-height: 1.4;
  color: var(--oliu-color-text);
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  flex: 1 0 auto;
}
.oliu-star-rating { display: inline-flex; gap: 2px; margin-top: 16px; }
.oliu-star-rating__star { font-size: 22px; line-height: 1; color: var(--oliu-color-border-mid); }
.oliu-star-rating__star--full,
.oliu-star-rating__star--half { color: var(--oliu-color-primary); }
@media (max-width: 768px) {
  .oliu-testimonial-grid { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .oliu-testimonial-card { min-height: 0; }
}
