/*
  labc-system.css — Line Art by Charles design-system override layer.

  WHAT: One additive stylesheet that raises the whole site to a comfortable
  reading-and-tapping baseline: 18px body copy, a 16px floor under every
  other line of text, font weights limited to 400/600/700, near-black text
  on white, 48px+ tap targets with real gaps, an 8px spacing grid, and
  short/reduced motion. It does not redesign layout. It corrects contrast,
  size, weight, spacing and touch-target gaps in markup this file does not
  own or edit.

  WHY (buyer profile): the actual Line Art by Charles buyer skews 55-70,
  is frequently on an older iPad (iOS Safari 12-15), arrives cold from an
  Etsy/Pinterest/TikTok link, is not a designer, and may have presbyopia
  or reduced contrast sensitivity. Small print, thin gray "muted" text,
  cramped tap targets and long sliding animations cost this buyer sales
  and cost Charlie support messages. Nothing here should read as
  "accessibility-ugly" — the goal is a page that is simply easy to read
  and easy to tap on the first try, on a device that may be several years
  old.

  LOAD ORDER THIS FILE ASSUMES (last wins ties):
    1. The page's own inline <style> block (per-page hero/brand rules)
    2. css/product-page.css   — shared product-page conversion layer,
                                 opted into via the <!--gen:ux--> marker
    3. css/labc-system.css    — THIS FILE. Loads last in <head>, so a
                                 plain-specificity rule here beats an
                                 equal-specificity rule anywhere earlier,
                                 including inside another file's own
                                 @media block (a later unconditional rule
                                 still wins a same-specificity fight with
                                 an earlier conditional one, regardless of
                                 viewport). !important is used ONLY where
                                 (a) the rule being overridden is itself
                                 !important (see product-page.css), or
                                 (b) markup is injected by JS at runtime
                                 AFTER this stylesheet has already parsed
                                 — js/cart.js's #labc-cookie-banner and
                                 js/product-page.js's .labc-buybar both
                                 append their own <style> tag to <head> on
                                 the fly, later in the DOM than this file,
                                 so same-specificity rules there would
                                 otherwise win by source order.

  This file does not edit HTML, other CSS, or JS — additive rules only,
  every selector below was confirmed against the real markup, not guessed.
  See the closing report for the handful of selectors flagged as "found
  by class-name search, declared styles not directly inspected."
*/

/* ============================================================
   TYPOGRAPHY — base
   ============================================================ */

body {
  font-size: 18px;
  line-height: 1.5;
  color: #1a1a1a;
  font-weight: 400;
  text-align: left; /* explicit invariant: no justified text anywhere.
    A repo-wide search found zero `text-align: justify` rules today —
    this line exists to keep it that way, not to fix a live bug. */
}

h1, h2, h3, h4, h5, h6 { font-weight: 700; }
strong, b { font-weight: 700; }

/* ---- font-weight is 400 or 600/700 only: fix every 300/500/900 found ---- */
.faq-answer,
.product-description p,
.listing-details p,
.section-subtitle,
.story-prose p,
.brand-pillar-text,
.brand-close p,
.review-text {
  font-weight: 400;
}
.pillars-intro,
.brand-pillar-title {
  font-weight: 600; /* was 500, not an allowed weight; 600 keeps the
    intended heading emphasis without leaving the 400/600/700 set */
}
.error-code {
  font-weight: 700; /* was 900 (404 page) */
}

/* ---- no italic paragraphs: real reading text only, short UI labels
   (numerals, eyebrow tags) are left alone ---- */
.review-text {
  font-style: normal; /* was italic, on every product page's review quotes */
}

/* ============================================================
   TYPOGRAPHY — page/product/price scale
   ============================================================ */

.product-h1 { font-size: clamp(24px, 4vw, 28px); }
.section-title { font-size: clamp(32px, 4vw, 36px); }
.product-price,
.labc-buybar-price {
  font-size: clamp(22px, 3vw, 24px);
  font-weight: 700;
  color: #1a1a1a; /* price/totals: never render in muted gray */
}
.size-price,
.product-sale-price {
  color: #1a1a1a; /* defensive: any price-role class stays near-black */
}

/* ============================================================
   TYPOGRAPHY — 16px small-print floor
   Audited: product-page.css, goldendoodle-sculpture/index.html (full
   read), index.html + sculptures/index.html (header/footer/grid skim).
   Every selector below carried an explicit sub-16px font-size somewhere
   in the cascade (page inline <style>, product-page.css, or a mobile
   @media block). Uppercase short labels keep their uppercase treatment;
   only size changes here.
   ============================================================ */

.size-option .size-sub,      /* product-page.css defines this as a 2-class
                                 compound selector; must match its specificity
                                 or a later-loaded plain .size-sub loses */
.size-picker-label,          /* short uppercase UI label — size only, see below */
.product-proof,
.spec-label,
.spec-value,
.checkout-trust,
.product-shipping,
.product-rating .count,
.product-breadcrumb,
.review-author,
.section-label,               /* short uppercase eyebrow — size only */
.filter-btn,
.header-nav a,
.header-tagline,              /* short uppercase wordmark line — size only */
.footer-col h4,               /* short uppercase heading — size only */
.footer-col a,
.footer-brand p,
.footer-bottom,
.announcement-bar,
.trust-item,
.product-type,
.product-type-sale,
.product-original-price,
.overlay-btn,
.product-name,
.brand-pillar-cta,
.brand-pillar-num,
.pillars-intro-sub,           /* short uppercase label — size only */
.form-error,
.msg-error,
.msg-success {
  font-size: 16px;
}

/* Real reading paragraphs get the 18px body size, not just the 16px floor */
.product-description p,
.listing-details p,
.faq-answer,
.faq-question,
.section-subtitle,
.review-text,
article p,
section p {
  font-size: 18px;
}

/* ============================================================
   TYPOGRAPHY — paragraph measure (65ch) so lines stay easy to track
   ============================================================ */

.product-description p,
.faq-answer,
.listing-details p,
article p,
section p {
  max-width: 65ch;
}

/* ============================================================
   TYPOGRAPHY — links: always underlined inside reading content,
   never on buttons or nav items styled as buttons
   ============================================================ */

.product-description a,
.listing-details a,
.faq-answer a,
.faq-question a,
footer a,
article a,
section p a {
  text-decoration: underline;
}
/* Buttons and nav keep their own treatment — not touched here:
   .add-to-cart-btn, .buy-btn-secondary, .header-nav a, .overlay-btn,
   .filter-btn, .labc-buybar-btn, #labc-cookie-ok are all styled as
   buttons/nav, so they are excluded from the rule above by not being
   selected, not by an override. */

/* ============================================================
   COLOR & CONTRAST
   Body/description text is already #1a1a1a or darker via the site's own
   --dark/--labc-ink tokens (~17-17.4:1 on white, well over 7:1). The
   gap was content that reads as body copy but was sitting on the
   secondary --text-gray token: FAQ answers and description paragraphs
   are promoted to full near-black here. Genuinely secondary metadata
   (proof line, spec labels, breadcrumb, byline, eyebrow tags) is
   standardized on #595651 (~7.9:1 on white) — darker than the "never
   lighter than #6b6763" (~5.6:1) floor the brief sets, so it clears
   the required 4.5:1 with real margin.
   ============================================================ */

.faq-answer,
.product-description p,
.listing-details p {
  color: #1a1a1a;
}

.size-option .size-sub,
.product-proof,
.spec-label,
.product-breadcrumb,
.review-author,
.section-label,
.section-subtitle,
.product-type,
.trust-item,
.product-shipping,
.checkout-trust,
.product-rating .count,
.pillars-intro-sub,
.footer-col a,
.footer-brand p,
.footer-bottom {
  color: #595651;
}

.product-original-price {
  color: #595651; /* was #bbb (~1.6:1) — strikethrough still carries the
    "old price" meaning, color no longer needs to do that job alone */
}

/* ---- never color-only signalling: error/success also get a glyph ---- */
.msg-error,
.form-error {
  position: relative;
}
.msg-error::before,
.form-error::before {
  content: "\2715  "; /* ✕ */
  font-weight: 700;
}
.msg-success::before {
  content: "\2713  "; /* ✓ */
  font-weight: 700;
}

/* ============================================================
   SPACING — 8px grid, ~24px between unrelated elements, content
   capped at 1200px. Most section-level spacing on this site is
   already 40-72px (already compliant); the rules below tighten the
   few spots that were sitting under 24px, on grid values.
   ============================================================ */

.product-rating,
.product-shipping,
.checkout-trust {
  margin-bottom: 24px;
}
.spec-row {
  padding: 16px 0; /* was 8px 0 */
}

/* ============================================================
   BUTTONS & TOUCH
   ============================================================ */

.add-to-cart-btn {
  min-height: 52px;
  font-size: 17px;
  font-weight: 700;
  text-transform: none; /* sentence case: stop CSS-forcing "ADD TO CART" */
  letter-spacing: normal;
}
.buy-btn-secondary {
  min-height: 52px;
  font-size: 17px;
  font-weight: 700;
  text-transform: none;
  letter-spacing: normal;
  background: #fff;         /* the ONE primary is .add-to-cart-btn; this
    stays visually weaker: outlined, not filled */
  color: #1a1a1a;
  border: 2px solid #1a1a1a;
}
.buy-btn-secondary:hover {
  background: #f8f5f1;
}
@media (max-width: 768px) {
  .add-to-cart-btn,
  .buy-btn-secondary {
    width: 100%; /* product-page.css already stacks .buy-buttons to one
      column here (grid-template-columns:1fr), which stretches these to
      full width on its own; this is a defensive restatement, not a fix */
  }
}

.labc-buybar-btn {
  min-height: 48px;
}
#labc-cookie-ok {
  /* cart.js injects its own <style> for #labc-cookie-banner at runtime,
     appended to <head> AFTER this stylesheet has already parsed, so an
     equal-specificity ID rule there would otherwise win by source
     order — !important is the deliberate, narrow exception noted in
     the file header. */
  min-height: 48px !important;
  font-size: 16px !important;
  padding: 12px 22px !important;
}
#labc-cookie-banner {
  font-size: 16px !important;
}

/* ---- every tappable target >=48px, with real gaps ---- */
.thumb,
.size-option {
  min-height: 48px;
}
.header-nav a {
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  padding: 0 4px;
}
.footer-col a {
  min-height: 48px;
  display: flex;
  align-items: center;
}
.filter-btn,
.overlay-btn {
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* No accordion pattern exists in the read scope for this file (product
   page, index, sculptures listing) — .faq-question is static copy, not
   a collapsible trigger, so nothing to size here. Flag if one gets
   added later. */

/* ============================================================
   MOTION — capped duration everywhere; property scoped to
   opacity/transform on the primary shopping controls named in the
   brief. Capping duration site-wide is safe for CSS *transitions*
   (things just settle faster, nothing breaks). It is deliberately NOT
   applied to @keyframes *animations* here: a few pages outside this
   file's read scope use looping animations (review/index.html's
   0.8s loading spinner, gift/thank-you's 2-2.3s pulse/paw-rise cues) —
   forcing those to a 200ms duration would make a spinner spin
   frantically and turn a gentle pulse into a strobe, which is worse
   for this buyer, not calmer. Those stay at their authored speed for
   users with no motion preference; the prefers-reduced-motion block
   below still turns them off entirely, per spec, for anyone who asks.
   ============================================================ */

*, *::before, *::after {
  transition-duration: 200ms !important;
}

.add-to-cart-btn,
.buy-btn-secondary,
.labc-buybar-btn,
.thumb,
.size-option {
  transition-property: opacity, transform;
  transition-duration: 200ms;
  transition-timing-function: ease;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  html {
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   CLS / IMAGES — reserve a box before the image loads. HTML is not
   touched; every fix here is a CSS-only aspect-ratio + object-fit.
   .main-media itself is left alone: product-page.css already reserves
   its box (.product-media-main { aspect-ratio: 1 !important }) and
   deliberately uses object-fit: contain so the sculpture is never
   cropped — switching that to cover here would crop the product photo,
   which is a regression, not a fix.
   ============================================================ */

img, video {
  max-width: 100%;
  height: auto;
}

.thumb {
  aspect-ratio: 1;
}
.thumb img,
.thumb video {
  object-fit: cover;
}

.product-image-wrap {
  aspect-ratio: 1; /* layered on top of the existing padding-top:100%
    hack already in index.html/sculptures — modern browsers use
    aspect-ratio, older ones keep working off padding-top, so this is
    additive, not a replacement */
}
.product-image-wrap img {
  object-fit: cover;
}

.customer-photo img {
  object-fit: cover;
  height: 100%;
}

/* ============================================================
   OLD SAFARI SAFETY (iOS Safari 12-15)
   No :has(), no @container, no CSS nesting, no flex `gap` introduced
   without a fallback anywhere in this file. product-page.css's
   `.size-option:has(input:checked)` rule is the progressive
   enhancement for browsers that support :has(); the block below is
   the fallback for browsers that do not. The markup this targets
   (confirmed in goldendoodle-sculpture/index.html):
     <label class="size-option">
       <input type="radio" ...>
       <span class="size-name">...</span>
       <span class="size-sub">...</span>
       <span class="size-price">...</span>
     </label>
   The input precedes the spans as a sibling, so `~` reaches all three
   without :has(). What :has() would additionally give you — shading
   the whole label's background/border on selection — cannot be
   reproduced without a parent selector; :focus-within covers the
   moment of keyboard/tap interaction instead, and the native radio
   dot continues to carry the checked state at every other time.
   ============================================================ */

.size-option input:checked ~ .size-name,
.size-option input:checked ~ .size-price {
  color: #1a1a1a;
  font-weight: 700;
}
.size-option:focus-within {
  outline: 3px solid #1a1a1a;
  outline-offset: 2px;
  background: #f8f5f1;
}

/* ---------------------------------------------------------------------
   Desktop header: one row, not three.

   Measured 2026-08-02: the header stood 292px tall (44px announcement +
   146px logo block + a nav row + a search row), all of it sticky. That is
   a third of a laptop screen permanently occupied, and it pushed the
   product gallery so far down that the photos ran off the bottom.

   Logo left, nav and search sharing the row on the right. Phones keep the
   stacked layout, where vertical space is cheap and the nav is a menu.
   --------------------------------------------------------------------- */
@media (min-width: 981px) {
  .header-inner {
    flex-direction: row !important;
    align-items: center !important;
    justify-content: space-between !important;
    flex-wrap: nowrap !important;
    gap: 24px;
  }
  .header-logo-link {
    height: auto !important;
    flex: 0 0 auto;
  }
  .header-logo-link img,
  .header-logo-link svg { max-height: 68px !important; width: auto !important; }
  .header-nav {
    margin-top: 0 !important;
    flex: 1 1 auto;
    justify-content: center;
  }
  .ls-search-root {
    margin-top: 0 !important;
    flex: 0 1 300px;
    max-width: 300px;
  }
  /* Nav links must not wrap or clip against the search box. */
  .header-nav a { white-space: nowrap; }
  .header-nav { gap: 22px !important; }
  /* The tagline is decoration and it was forcing the logo block taller than
     the row; the <title> and the page already say what the shop sells. */
  .header-tagline, .header-inner .tagline, .site-tagline { display: none !important; }
}

/* ---------------------------------------------------------------------
   Mobile first screen: get to the product.

   Measured on a 393x852 iPhone (2026-08-02): a shopper scrolled past 842px
   of chrome - one entire screen - before the first sculpture appeared.
   Announcement 44 + logo block 146 + tagline 20 + search 48 + trust bar 197
   + section heading 108 + filters 48. Every one of those is something she
   did not come for. Compressed below so products start around the 400px
   mark and the first row is visible without scrolling.
   --------------------------------------------------------------------- */
@media (max-width: 980px) {
  /* Logo: recognisable, not a billboard. */
  .header-logo-link { height: auto !important; padding: 10px 0 !important; }
  .header-logo-link img,
  .header-logo-link svg { max-height: 62px !important; width: auto !important; }

  /* The tagline was being clipped mid-sentence ("MINIMALIST SCULPTURES &").
     A cut-off brand line reads as a broken page, so let it fit: smaller,
     tighter tracking, and allowed to wrap rather than truncate. */
  .header-tagline {
    font-size: 11px !important;
    letter-spacing: .12em !important;
    white-space: normal !important;
    overflow: visible !important;
    text-overflow: clip !important;
    line-height: 1.35 !important;
    margin: 2px 0 0 !important;
    padding: 0 12px !important;
  }

  /* Trust bar: same three proofs, one compact strip instead of 197px. */
  .trust-bar { padding: 8px 0 !important; }
  .trust-bar-inner {
    display: flex !important;
    flex-wrap: wrap !important;
    justify-content: center !important;
    gap: 6px 14px !important;
  }
  .trust-item {
    font-size: 13px !important;
    line-height: 1.3 !important;
    margin: 0 !important;
    gap: 5px !important;
  }
  .trust-item svg { width: 14px !important; height: 14px !important; }

  /* Collection heading: one line of intent, not a title page. */
  .section-head { margin-bottom: 10px !important; }
  .section-label, .section-eyebrow { display: none !important; }
  .section-title { font-size: 26px !important; margin: 6px 0 8px !important; }
  .section-subtitle { display: none !important; }

  /* Filters stay tappable but stop eating two full rows of space. */
  .filter-btn { min-height: 44px !important; padding: 8px 14px !important; font-size: 14px !important; }
}
