/* The card's size row (Elementor .rasvo-card__sizes) is a flex row, so the
   block would otherwise shrink to its content. Fill the row instead of
   restyling the Elementor container — template 268 stays untouched.

   Doubled class = specificity 0,2,0. Elementor's post-7.css ships
   `.rasvo-card__sizes span{display:inline}` at 0,1,1, which outranks a single
   class and silently flattens both wrappers back to inline (min-height then
   does nothing, since it does not apply to inline boxes). Doubling beats it
   without !important and without depending on the Elementor wrapper's name. */
.rasvo-qb.rasvo-qb{display:block;position:relative;z-index:2;flex:1 1 100%;width:100%;margin:0}
.rasvo-qb__chips.rasvo-qb__chips{display:flex;flex-wrap:wrap;gap:4px;align-items:center}
.rasvo-card__sizes .rasvo-qb__chips{margin:0}

/* Pin quick-buy to the bottom of the card so BUY NOW lines up across a grid
   row no matter what sits above it — a wrapped 2-line product name, or the
   extra meta line shorts carry that tees do not. Equalising the causes one by
   one is whack-a-mole; pinning the block is one rule that covers all of them,
   including whatever gets added later.

   The grid already gives every card in a row equal height, but Elementor's
   inner containers are flex-grow:0, so they hug their content and the spare
   height never reaches the sizes row — margin-top:auto alone does nothing.
   Stretch the ancestors that actually contain the row, then pin.
   :has() scopes all of this to cards that have a quick-buy. CSS only —
   template 268 untouched. */
.elementor-268.e-loop-item:has(.rasvo-qb) > .e-con{height:100%}
.elementor-268.e-loop-item:has(.rasvo-qb) .e-con:has(.rasvo-card__sizes){flex:1 1 auto}
/* !important because Elementor emits the per-element margin as
   `.elementor-268 .elementor-element.elementor-element-b439510{margin-top:6px}`
   (0,3,0) from post-7.css, which outranks any selector that does not hardcode
   that generated element id — and hardcoding it would break on re-export. */
.rasvo-card__sizes:has(.rasvo-qb){margin-top:auto!important}

/* Card gutter — one vertical rhythm (Review 5 issue #3).
   The media is full-bleed to the card (measured 56→365 at 1440), but three
   separate insets pushed everything below it off that edge:
     .rasvo-card__body  padding 12px 2px 0   →  everything +2px
     .rasvo-card__row   padding 4px 10px 10px →  name +10px, price −10px
     .rasvo-qb          margin-right 9.6px    →  CTA −9.6px on the RIGHT ONLY
   Net effect: card left 142 / pills+CTA 144 / title 154, and the CTA sat 2px
   from the left edge but 12px from the right — visibly off-centre in its own
   card, on every card, on every grid (Home, Shop, Related, Cart-Recommended).
   Zero the horizontal insets; vertical padding is intentional and stays.

   !important on the two Elementor containers because Elementor emits these as
   `.elementor-268 .elementor-element.elementor-element-{id}{padding:…}` (0,3,0)
   and hardcoding those generated ids breaks on re-export — same reasoning as
   the margin-top rule above. The .rasvo-qb margin needs no !important: the
   doubled class (0,2,0) already outranks whatever set it. */
.rasvo-card__body{padding-left:0!important;padding-right:0!important}
.rasvo-card__row{padding-left:0!important;padding-right:0!important}

/* Chips inherit the card's mono type; buttons need their UA styling stripped. */
.rasvo-qb__chips button.is-available{
  font:inherit;letter-spacing:inherit;text-transform:inherit;color:inherit;
  background:transparent;border:1px solid transparent;border-radius:2px;
  min-width:30px;min-height:30px;padding:0 5px;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  position:relative;
  transition:border-color .2s ease,color .2s ease,background .2s ease}
/* S41 · invisible tap-target extension (design review 2026-08-02). The chips
   measured 24-44 x 30px on mobile — above the WCAG 2.5.8 24px floor but under
   the 44px platform guidelines, and they feed a straight-to-checkout link.
   Growing the boxes was rejected in this file already (fold budget on 667px
   phones, documented above); a ::after overlay adds ±7px of vertical hit area
   with ZERO layout change: 30px visual -> 44px effective. Horizontal reach is
   untouched — chips sit flush side by side and must not steal each other's
   taps. The 7px below stays inside BUY NOW's 8px top margin; the 7px above
   overlaps only the non-interactive tail of the name/price row.
   ponytail: visual 44px chips if the fold budget is ever re-negotiated. */
.rasvo-qb__chips button.is-available::after{
  content:'';position:absolute;left:0;right:0;top:-7px;bottom:-7px}
.rasvo-qb__chips button.is-available:hover{border-color:rgba(12,20,31,.3);color:var(--rv-ink,#141A23)}
.rasvo-qb__chips button.is-available:focus-visible{
  outline:2px solid var(--rasvo-terracotta,#C2542B);outline-offset:1px}
.rasvo-qb__chips button.is-selected{
  border-color:var(--rv-ink,#141A23);color:var(--rv-ink,#141A23);
  background:rgba(12,20,31,.05)}
.rasvo-qb__chips .is-oos{
  min-height:30px;display:inline-flex;align-items:center;padding:0 5px}

/* Terracotta, not navy. Navy repeated the colour of the product imagery
   itself (dark tees on cream), so the button read as another dark slab in the
   grid rather than an action. Terracotta is the brand's designated CTA colour
   and separates cleanly from both the cream card and the dark garment.

   OUTLINE, not solid fill (Review 5 issue #6). The trade-off flagged below —
   "spends the reserved accent 16x on a shop grid ... if it reads loud, the fix
   is this one rule" — was called in review: 16 solid terracotta bars made the
   grid read as discount retail and out-shouted the product. Terracotta now
   marks the button as an OUTLINE and only fills on intent (hover/focus).

   Why the label is ink and not terracotta: terracotta TEXT on cream measures
   4.34:1 and fails AA at this size (the reason the original chose a solid
   fill). Ink #141A23 on #FAF9F6 is ~15:1, and the filled hover state keeps
   cream-on-terracotta at 4.53:1. Both states pass; the accent survives as the
   border, which is what carried the "this is the buy action" meaning anyway.

   No hover-reveal: touch has no hover, so the CTA must be legible at rest.

   `.rasvo-qb a.rasvo-qb__btn` (0,2,1), not `.rasvo-qb__btn` (0,1,0): the theme
   ships `:root body a{color:var(--rasvo-navy)}` at 0,1,2, which otherwise wins
   and paints navy text on the button. */
.rasvo-qb a.rasvo-qb__btn{
  display:flex;align-items:center;justify-content:center;
  width:100%;margin-top:10px;min-height:40px;padding:0 12px;
  background:transparent;color:var(--rv-ink,#141A23);
  border:1px solid var(--rasvo-terracotta,#C2542B);border-radius:0;
  font-family:"Geist Mono",ui-monospace,monospace;font-size:11px;
  letter-spacing:.18em;text-transform:uppercase;text-decoration:none;
  transition:background .25s ease,border-color .25s ease,color .25s ease}
.rasvo-qb a.rasvo-qb__btn:hover,.rasvo-qb a.rasvo-qb__btn:focus-visible{
  background:var(--rasvo-terracotta,#C2542B);
  border-color:var(--rasvo-terracotta,#C2542B);
  color:var(--rasvo-paper,#FFFEFA);text-decoration:none}
.rasvo-qb__btn:focus-visible{outline:2px solid var(--rv-ink,#141A23);outline-offset:2px}
.rasvo-qb__btn.is-busy{pointer-events:none;opacity:.7}

/* Mobile: the 2-up card gives the chip row 138px. At min-width:30 a 5-size run
   needs 166px and wrapped to two rows, which pushed BUY NOW below the fold on
   mid-size phones. 24px is the WCAG 2.5.8 floor, so 5x24 + 4x3 = 132 fits one
   row with margin to spare — height stays 30px, so the target is 24x30, above
   the minimum on both axes. Reclaims 34px of card height.
   No min-height needed: the block is bottom-pinned (margin-top:auto), so BUY
   NOW lands at the same y even if a future 6-size run wraps. */
@media (max-width:767px){
  /* Zero gap, not 3px. The 2-up grid gives the chip row 122px at a 360px
     device and 129px at 412px; five chips at the 24px WCAG floor need exactly
     120px, so ANY gap tips the 5-size runs onto a second row — which is what
     was orphaning "XXL" on its own line. Chips flex to share the slack, so
     3-size products fill the row evenly instead of huddling left, and the
     selected chip's border keeps them legible without gutters. */
  .rasvo-qb__chips.rasvo-qb__chips{gap:0}
  /* max-width is the load-bearing part. `flex:1 1 auto` lets chips share the
     row so a 3-size run fills a 122px 2-up card evenly — but the SAME rule on
     the PDP, where the grid is 1 column and the card is 375px wide, stretched
     each chip to 65-82px. A single letter in an 82px box is what read as
     "disoriented". Capping at 44px keeps the 2-up behaviour identical
     (122/5 = 24.4px, well under the cap) while the wide 1-up card gets
     natural-sized chips that stay left-aligned with the rest of the card. */
  .rasvo-qb__chips button.is-available{min-width:24px;max-width:44px;flex:1 1 auto;padding:0}
  .rasvo-qb__chips .is-oos{min-width:24px;max-width:44px;flex:1 1 auto;padding:0;justify-content:center}
  .rasvo-qb a.rasvo-qb__btn{margin-top:8px}
}
@media (min-width:768px){
  .rasvo-qb__chips{gap:5px}
  .rasvo-qb__chips button.is-available{min-width:32px;min-height:32px;padding:0 7px}
  .rasvo-qb__btn{min-height:42px;font-size:11.5px}
}
@media (prefers-reduced-motion:reduce){
  .rasvo-qb__chips button.is-available,.rasvo-qb__btn{transition:none}
}

/* ---- Fit/GSM as an image label -------------------------------------------
   Moves the card's spec line (.rasvo-card__fit) out of the text stack and onto
   the product image, which frees its full block height below the image — the
   remaining mobile-fold win — and reads as a garment tag rather than a caption.

   Positioned against .e-loop-item, which is already position:relative and whose
   top-left corner coincides exactly with .rasvo-card__media (measured: delta
   0,0 on every card, every breakpoint). So top/left alone place it inside the
   image with no height math and nothing to re-tune per viewport.

   CRITICAL — this RELOCATES the existing element, it does not re-derive the
   values. The line already respects the Session 21 "B2" guard in
   rasvo-catalogue-scope.php, which blanks `gsm` for shorts because 240 GSM is
   the combed-cotton JERSEY spec and was blanket-written onto all 25 products
   including the 10 shorts, whose real weight is unconfirmed. Moving the node
   inherits that guard: tees render "REGULAR • 240 GSM", shorts "REGULAR".
   Re-querying the meta here would resurrect the false figure on shorts.

   pointer-events:none — the whole card is one tap target via a stretched link;
   the label must never swallow that click.
   Type stays 12px: Session 33 set a hard 12px floor sitewide (106 nodes fixed
   at source) and exempted only the numeric cart badge as a count chip. A spec
   label is reading text, so it does not qualify. Tracking is tightened instead. */
.elementor-268.e-loop-item .rasvo-card__fit{
  position:absolute;top:10px;left:10px;z-index:3;
  margin:0!important;width:auto;max-width:calc(100% - 20px);
  padding:3px 6px;
  background:rgba(250,249,246,.92);
  color:var(--rv-ink,#141A23)!important;
  font-size:12px!important;letter-spacing:.02em!important;line-height:1.22!important;
  white-space:normal;
  pointer-events:none}
/* One term per line. As a single "FIT • GSM" string at the 12px floor this ran
   93% of card width on a phone — a full-bleed banner across the garment's
   shoulders, and wrapping at the bullet left it dangling at the end of line 1.
   Split into stacked terms it is as wide as its longest word (~53% of the
   card) and reads as a woven garment tag. Shorts have no GSM, so they render
   a single line with no empty second row. */
.elementor-268.e-loop-item .rasvo-card__fit .rasvo-fit__t{display:block}
/* Second term de-emphasised with opacity only — NOT a smaller size. The 12px
   floor is sitewide (Session 33) and a spec label is reading text. ink at .66
   over this paper composites to 5.49:1, clear of AA at 12px. */
.elementor-268.e-loop-item .rasvo-card__fit .rasvo-fit__t + .rasvo-fit__t{opacity:.66}

/* GSM now lives in the always-visible image tag, so drop it from the hover
   spec overlay — it was stating the same figure twice on one card, and on
   touch (where the overlay is tap-triggered) both showed at once.
   Positional: the dl is always GSM, Wash, Print in that order, so the first
   dt/dd pair is the GSM row. Shorts already have an empty GSM dd hidden by
   rasvo-catalogue-scope, so hiding the pair there is a harmless no-op. */
.elementor-268.e-loop-item .rasvo-card__spec dl > dt:nth-child(1),
.elementor-268.e-loop-item .rasvo-card__spec dl > dd:nth-child(2){display:none!important}

/* Name and price share one flex row that was allowed to wrap, so a long name
   ("Bug Free Zone") pushed the price onto its own line while a short one
   ("Ctrl Alt Chill") kept it inline — the row read differently card to card.
   Pin them to one line and let the NAME truncate.
   Chose ellipsis over a PHP character limit: it is width-aware (a 4-up desktop
   card and a 2-up phone card have very different room), it never mutates the
   product title that also feeds search/SEO/cart, and it needs no per-name
   tuning. The full name stays in the DOM and in the link's accessible name.
   :has(>) targets the row via its own child, not the generated element id,
   which would break on re-export. */
.elementor-268.e-loop-item :has(> .rasvo-card__name){flex-wrap:nowrap!important}
/* flex:1, so the name claims ALL the width the price does not need and
   truncates only when it genuinely cannot fit. At 0 1 auto it settled at 88px
   inside a ~141px row and clipped names that had room to spare. */
.elementor-268.e-loop-item .rasvo-card__name{
  min-width:0;flex:1 1 auto;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Every descendant, not just the direct child: the title markup is
   widget > heading > a, and the <a> is the element that actually carries the
   text. Truncating only the outer box left the inline <a> overflowing it, so
   the name was hard-clipped mid-word with NO ellipsis — the parent's
   scrollWidth equalled its clientWidth, so the browser never drew one. The
   link must be a block with its own overflow for "…" to appear. */
.elementor-268.e-loop-item .rasvo-card__name *{
  display:block;max-width:100%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.elementor-268.e-loop-item .rasvo-card__price{flex:0 0 auto;white-space:nowrap}

/* ---- Archive header rhythm + fit pills (mobile) --------------------------
   Earlier this block trimmed the title padding to 2px to win the fold. Moving
   the spec line onto the image later freed 41px, so that borrowed space is
   given back: the eyebrow sat ~2px under a 93px header, which read cramped.

   Budget is real, not eyeballed — BUY NOW's bottom edge was 595px against 608
   usable on a 667px phone (844 − 59px tab bar → 785; 667 − 59 → 608). Adding
   16px of title padding and taking 9px back off the pills nets +7px, so the
   button still clears the fold on every phone down to 667.

   Selectors use the semantic classes the template already ships
   (.rasvo-filterbar, .rasvo-fitpill) rather than generated element ids, which
   this file previously hardcoded and which break on re-export. */
@media (max-width:767px){
  /* 16px, not 18: at 18 the button's bottom edge measured 609px against the
     608px usable on a 667 phone — over by one pixel. 16 lands it at 605 with
     a 3px margin, and is still 8x the 2px it was borrowed down to. */
  .archive .e-con.e-parent:has(h1){padding-top:16px!important}
  .archive .rasvo-filterbar{padding-bottom:0!important}
  .archive .elementor-loop-container{margin-top:0!important}

  /* Pills started at x=30 while the H1 sat at x=20 — the scroll row carries a
     10px padding-left of its own on top of the section's 20px gutter. Zero it
     so the first pill is flush with the heading; keep padding on the right so
     the last pill has room to scroll clear of the edge. */
  .archive .rasvo-filterbar :has(> .rasvo-fitpill){
    padding-left:0!important;padding-right:14px}

  /* 41px tall for a 12px label is a lot of chrome. 7px vertical padding brings
     it to ~33px — still well over the 24px tap-target floor — and tightening
     the tracking from 1.4px recovers ~8px per pill, which is what was pushing
     the row into a horizontal scroll and clipping "REGULAR — 20" mid-word.
     Type stays 12px (sitewide floor). */
  .archive .rasvo-fitpill .elementor-button{
    padding:6px 10px!important;letter-spacing:.8px!important}
}

/* ---- Loop grid overflowing the footer on mobile (client bug report) -------
   Symptom: scroll a PDP to the bottom on a phone and the last related-product
   cards render THROUGH the footer — cut in half, or the footer sitting on top
   of them.

   Measured at 390px: the loop-grid widget reported offsetHeight **1474px**
   while its own content was **2529px**, so the footer was positioned ~980px too
   early and the remaining cards painted over it. Confirmed genuine layout, not
   a measurement artefact — offsetHeight matched getBoundingClientRect and no
   ancestor carried a transform.

   PRE-EXISTING, not introduced here: reproduced with this file's stylesheet
   emptied AND every .rasvo-qb node removed from the DOM — the 982px overlap was
   identical. The quick-buy work only changes its magnitude.

   Cause: `.e-con-inner` is a COLUMN flex container, and Elementor chains a
   percentage height down it (`.e-con{height:var(--height)}` ->
   `.e-con-inner{height:100%}` -> `.elementor-widget-container{height:100%}`).
   With a 1-column mobile grid the widget's flex base size resolves against a
   circular height and lands at the 2-column figure. It is immune to `height`,
   `min-height` and `flex` overrides — all three were tested with !important and
   changed nothing, because the wrong size comes from the parent's formatting
   context, not from any property on the widget.

   Fix: give that one container a block formatting context so the widget sizes
   to its content normally. Only containers that actually hold a loop grid are
   touched, and only under 768px. `display:block` drops the flex `gap`, so it is
   re-added as PADDING (padding does not collapse with the sibling margins the
   way a replacement margin would) using Elementor's own inherited `--row-gap`.
   Verified: heading-to-grid spacing is 42px before and after. */
@media (max-width:767px){
  .e-con > .e-con-inner:has(> .elementor-widget-loop-grid){display:block!important}
  .e-con > .e-con-inner:has(> .elementor-widget-loop-grid) > * + *{
    padding-top:var(--row-gap,20px)}
}

/* ---- Related-products grid ran edge-to-edge on mobile ---------------------
   The "COMPLETE THE DROP" container had zero horizontal padding, so its cards
   sat flush against both screen edges (measured left 0 / right 375 on a 375px
   viewport) while every other block on the page — gallery frame, eyebrow, H1,
   price — sits on a 10px gutter. The grid was the only element breaking the
   page's vertical edge line.
   Matched to the page's own 10px rather than inventing a new value, so the
   cards now align with the image frame and the copy above them. */
@media (max-width:767px){
  .single-product .e-con:has(> .e-con-inner > .elementor-widget-loop-grid){
    padding-left:10px!important;padding-right:10px!important}
}

/* ---- PDP mobile: reclaim the dead space above the gallery -----------------
   Measured at 397x915: the header ends at 93 but the image did not start until
   179 — 86px of nothing (40px outer container + 10px inner + 36px frame
   padding), on the one screen where vertical space is scarcest. The whole
   first viewport was image and cream.

   Trimming the frame's own padding (36 -> 12 top, 36 -> 16 bottom) and the two
   container gaps pulls the title, price and size selector up by ~90px. The
   IMAGE ITSELF IS NOT SHRUNK — it is the product, and on a PDP that is the
   point; only the empty margin around it goes. */
@media (max-width:767px){
  .single-product .woocommerce-product-gallery{margin-bottom:0!important}
  /* Classes applied at runtime by rasvo-experience-polish.php — the gallery is
     not a direct child of its frame, so `:has(> …)` cannot reach it, and the
     only other handle is a generated element id that breaks on re-export. */
  /* The outer container is e-con-BOXED, so Elementor puts its 40px padding on
     the generated .e-con-inner, not on the container itself — a rule on the
     container alone silently does nothing. 12px keeps the page off the header
     without a desktop-sized void above the breadcrumb. */
  .single-product .rasvo-pdp-outer,
  .single-product .rasvo-pdp-outer > .e-con-inner{padding-top:12px!important}
  .single-product .rasvo-pdp-col{padding-top:0!important}

  /* THE big one. The content column carries `row-gap:40px`, and it applies
     twice in the first screen — once between breadcrumb and gallery, once
     between gallery and summary. That is the empty space above and below the
     breadcrumb; 80px of it, on the most space-starved screen in the store.
     A desktop-scale rhythm does not translate to a 397px viewport. */
  .single-product .rasvo-pdp-col{row-gap:16px!important}

  /* Frame: 12/16 still left ~37px above the image and ~42px below once its own
     20px gap was counted. The image is not shrunk — only the margin around it. */
  /* Side padding was 36px on a 375px screen, inside a frame already inset 10px
     — 92px of the viewport (24%) spent on cream margin, leaving the product
     itself only 283px. 14px gives it 327px (+15%) without touching the image's
     own scale or the frame's identity. Desktop keeps its 36px. */
  .single-product .rasvo-pdp-galleryframe{
    padding-top:8px!important;padding-bottom:10px!important;
    padding-left:14px!important;padding-right:14px!important;row-gap:12px!important}

  /* Breadcrumb: the full trail wrapped to two lines (36px) because
     "HOME / T-SHIRTS / OVERSIZED T-SHIRTS / CTRL ALT CHILL" cannot fit 397px.
     One line with an ellipsis — the trail stays complete in the DOM for
     assistive tech and SEO, it just stops eating a second row. */
  .single-product .rasvo-pdp-breadcrumb-moved{
    margin:0!important;padding:0!important;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
    /* min-width:0 is load-bearing. As a flex item it defaults to
       min-width:auto, which refuses to shrink below the content's intrinsic
       size — with nowrap that made the element 477px wide inside a 397px
       viewport, so it spilled past the right gutter and the ellipsis never
       engaged. Without this line the two properties above do nothing. */
    min-width:0;max-width:100%}

  /* Eyebrow -> title carried 10px margin-bottom AND 10px margin-top for a 12px
     label. 20px between two lines of type is a desktop interval. */
  .single-product .rasvo-pdp-eyebrow{margin-bottom:4px!important}
  .single-product .rasvo-pdp-eyebrow + .elementor-widget{margin-top:4px!important}
}