/* ============================================================
   BUNDLE PDP — v13 LIGHT register (rhinory/bundle-pdp block).
   Ported from prototypes/13/19-bundle-pdp.html (view-source-is-contract).
   Brief: docs/briefs/2026-08-16-bundle-pdp.md (Build 3).

   SCOPING DISCIPLINE (guardrail, mirrors merch-pdp.css / wine-pdp.css): the
   prototype scopes every light token to `.bpdp13`, NOT :root, so the light
   register never re-skins the dark site chrome (nav / mega-menu / campaign
   bar / footer). This block's own wrapper — `.rhinory-bundle-pdp`, emitted by
   get_block_wrapper_attributes() in render.php — IS that scope boundary;
   there is no separate `.bpdp13` class. Generic names that collide with the
   dark chrome's globals in style.css (`.wrap`, `.eyebrow`, `.section`) keep
   the prototype's `.wp-wrap` / `.wp-eyebrow` / `.wp-sec` renames.

   TOKEN SELF-SUFFICIENCY: the prototype inherits `--paper` / `--card` /
   `--ink-warm` / `--rhino-warm` / `--type-*` from `.light-warm` in
   v13-shared.css, which this theme does NOT ship. So the block scope declares
   the full set locally, exactly as .rhinory-merch-pdp does — values copied
   from v13-shared.css §type-scale and the prototype's own .bpdp13 block, not
   re-derived.

   NO variant-selector.css: a bundle is a single SKU, so the `.pv` component
   the merch and gift-card PDPs draw has nothing to render here.

   Section inventory: hero (supplied product photography + breadcrumb + title
   bands + teaser + conservation line + buy row with the "Members from" mark)
   -> what's inside (generic head + product body + count-adaptive component
   grid) -> Conservation Circle panel with the hairline tier ladder -> the
   shared cross-page closer.

   2026-08-22 design refresh (docs/_cowork-prompts/2026-08-22-cc-bundle-pdp-
   design-refresh.md) re-synced this file to the corrected
   prototypes/13/19-bundle-pdp.html: count-adaptive grid, long-title bands,
   generic "what's inside" head + bound product body, hairline member rows,
   the top-of-page member mark, and the shared closer band.
   ============================================================ */

.rhinory-bundle-pdp{
  /* light-warm register (prototype .light-warm + .bpdp13 locals) */
  --paper:#FBF7EE;
  /* catalog card hover bed — the shop-all unit's --paper-2 (v13 light register) */
  --paper-2:#F7F1E5;
  --card:#F4EEE0;
  --card-2:#EAE1CE;
  --ink-warm:#2D2926;
  --ink-warm-2:#3A352F;
  --rhino-warm:#717271;
  --rule-warm:rgba(45,41,38,0.10);
  --rule-warm-2:rgba(45,41,38,0.18);
  --burgundy:#520000;
  --burgundy-2:#3d0000;
  --rule-burg:rgba(82,0,0,0.24);
  --acacia:#B97727;
  /* type scale — v13-shared.css :root, copied because the theme has none */
  --type-hero:clamp(46px,6vw,84px);
  --type-h2:clamp(40px,5.4vw,76px);
  --type-h3:clamp(26px,2.6vw,34px);
  --type-lede:18px;
  --type-body-editorial:18px;
  --type-eyebrow:11px;
  --ff-display:"Cormorant Garamond",Georgia,serif;
  --ff-label:"Josefin Sans",system-ui,sans-serif;
  --ff-body:"Lora",Georgia,serif;
  display:block;
  background:var(--paper);
  color:var(--ink-warm);
  font-family:var(--ff-body);
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
.rhinory-bundle-pdp *{ box-sizing:border-box }
.rhinory-bundle-pdp a{ color:var(--burgundy); text-decoration:none }
.rhinory-bundle-pdp a:hover{ color:var(--burgundy-2) }
.rhinory-bundle-pdp .wp-wrap{ width:100%; max-width:1280px; margin:0 auto; padding:0 56px }
.rhinory-bundle-pdp .wp-eyebrow{
  font-family:var(--ff-label); font-size:var(--type-eyebrow); font-weight:600;
  letter-spacing:0.18em; text-transform:uppercase; color:var(--burgundy);
  display:inline-flex; align-items:center; gap:12px; margin:0;
}
.rhinory-bundle-pdp .wp-eyebrow::before{ content:""; width:24px; height:1px; background:var(--burgundy) }
.rhinory-bundle-pdp section{ scroll-margin-top:96px }

/* ---- HERO — supplied product photography left (flush to the page edge),
   product block right. Nothing is printed over the photo.
   Ported from prototypes/13/19-bundle-pdp.html § .bh-hero.

   This REPLACES Build 3's `.hp-lineup` figure, which composed a side-by-side
   row of the bundle's child bottle shots. That collage pre-dated the
   2026-08-21 hero pass and substituted theme-drawn artwork for the packshot
   the operator uploaded to Commerce7 — which, for Rider's Reserve, already IS
   the three bottles together. ---- */
.rhinory-bundle-pdp .bh-hero{
  display:grid; grid-template-columns:minmax(0,1.06fr) minmax(0,0.94fr);
  align-items:start; border-bottom:1px solid var(--rule-warm-2);
}
/* The gallery column is deliberately OUTSIDE `.wp-wrap` — it runs to x=0 so
   the photo meets the dark nav with no paper gutter above it. */
.rhinory-bundle-pdp .bh-gallery{ position:relative; background:var(--card-2); border-right:1px solid var(--rule-warm-2) }
/* 3 high by 4 wide — the client-specified lifestyle crop, not a tall bottle crop. */
.rhinory-bundle-pdp .bh-frame{ position:relative; aspect-ratio:4/3; overflow:hidden; background:var(--card-2) }
/* The <picture> wrapper the image pipeline emits must stay transparent to the
   box model, or the absolutely-positioned shots below lose their frame. */
.rhinory-bundle-pdp .bh-frame picture{ display:contents }
/* Every supplied shot is stacked in the frame and cross-faded, so a rail click
   swaps opacity on an already-decoded image instead of rewriting src. */
.rhinory-bundle-pdp .bh-frame .bh-shot{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center;
  opacity:0; transition:opacity .9s cubic-bezier(.2,.7,.2,1); filter:saturate(.94) contrast(1.03);
}
.rhinory-bundle-pdp .bh-frame .bh-shot.is-on{ opacity:1 }
/* A cut-out packshot sits ON the warm card ground rather than filling the
   frame — see render.php § Hero gallery for how the two are told apart. */
.rhinory-bundle-pdp .bh-frame .bh-shot.is-packshot{ object-fit:contain; padding:26px 0; background:var(--card) }
/* Thumbnail rail below the photo, on paper, never over it. It renders only
   when there is a second shot to switch to, so there is no empty-rail state
   to style away. */
.rhinory-bundle-pdp .bh-thumbs{ display:flex; gap:8px; padding:8px 8px 0 0; background:var(--paper) }
.rhinory-bundle-pdp .bh-thumb{
  flex:1 1 0; min-width:0; aspect-ratio:4/3; padding:0; border:1px solid var(--rule-warm-2); border-radius:1px;
  background-size:cover; background-position:center; background-color:var(--card); cursor:pointer;
  filter:saturate(.86) brightness(.94); transition:filter .3s ease, box-shadow .25s ease, transform .25s ease;
}
.rhinory-bundle-pdp .bh-thumb:hover{ filter:saturate(1) brightness(1) }
.rhinory-bundle-pdp .bh-thumb.is-on{ filter:saturate(1) brightness(1); box-shadow:0 0 0 2px var(--burgundy); transform:translateY(-2px) }
.rhinory-bundle-pdp .bh-thumb:focus-visible{ outline:2px solid var(--burgundy); outline-offset:2px }
@media (prefers-reduced-motion:reduce){
  .rhinory-bundle-pdp .bh-frame .bh-shot{ transition:none }
  .rhinory-bundle-pdp .bh-thumb{ transition:none }
}

/* ---- ONE-IMAGE HERO (`.bh-hero.is-solo`, set off the supplied photo count).
   Ported from prototypes/13/19-bundle-pdp.html § ONE-IMAGE HERO.

   With 3+ shots the thumb rail adds height and the two columns balance. With
   ONE shot there is no rail, so the photo column ran short of the detail column
   and the hero sat on a band of dead paper (measured ~340px at 1440, and EVERY
   live bundle is that case — see render.php § Hero gallery). In the solo state
   the gallery column stretches to the detail column's height and the photo
   fills it outright — no caption mat: there is no per-image caption data to
   maintain, and an empty card-2 band under the photo read as a mistake rather
   than a frame. The crop guard stays (object-position weighted up) so a
   landscape lifestyle shot does not centre-crop into its dullest patch even at
   a tall column height.

   The 3+-image hero is untouched: every rule here is gated on `.is-solo`. ---- */
.rhinory-bundle-pdp .bh-hero.is-solo{ align-items:stretch }
.rhinory-bundle-pdp .bh-hero.is-solo .bh-gallery{ display:flex; flex-direction:column; min-height:0 }
.rhinory-bundle-pdp .bh-hero.is-solo .bh-frame{ flex:1 1 auto; aspect-ratio:auto; min-height:0 }
/* Crop guard: filling a taller-than-4:3 box off a landscape shot takes the crop
   off centre, so it is biased up to where the subject sits. */
.rhinory-bundle-pdp .bh-hero.is-solo .bh-frame img{ object-position:center 30% }

/* Operator has uploaded no photography yet — the hatched stand-in, never a broken <img>. */
.rhinory-bundle-pdp .img-slot{
  background:repeating-linear-gradient(-45deg, rgba(45,41,38,0.05) 0 10px, transparent 10px 20px), var(--card-2);
  display:flex; align-items:center; justify-content:center; text-align:center; width:100%; height:100%; min-height:inherit;
}
.rhinory-bundle-pdp .img-slot span{ font-family:var(--ff-label); font-size:10px; font-weight:600; letter-spacing:0.16em; text-transform:uppercase; color:var(--rhino-warm); padding:12px; line-height:1.7; max-width:22ch }

.rhinory-bundle-pdp .bh-detail{ display:flex; flex-direction:column; padding:26px 56px 72px 56px; max-width:660px }
/* The breadcrumb moved INTO the detail column with the hero re-skin: the
   gallery now runs flush to x=0, so a full-width crumb strip above it would
   float over a photo edge it no longer belongs to. */
.rhinory-bundle-pdp .bh-crumb{ display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin:0 0 30px 0; font-family:var(--ff-label); font-size:11px; font-weight:600; letter-spacing:0.14em; text-transform:uppercase }
.rhinory-bundle-pdp .bh-crumb a{ color:var(--rhino-warm); transition:color .2s }
.rhinory-bundle-pdp .bh-crumb a:hover{ color:var(--burgundy) }
.rhinory-bundle-pdp .bh-crumb .sep{ color:var(--rule-warm-2) }
/* The crumb tail TRUNCATES rather than wrapping the breadcrumb onto three rows —
   a real C7 bundle name is long enough to do that on its own. The H1 below is
   where the whole name reads; this is the navigational echo of it. */
.rhinory-bundle-pdp .bh-crumb .current{ color:var(--burgundy); max-width:42ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }

/* ---- H1: the WHOLE Commerce7 name, in one field ----
   Nothing is lifted out of a bundle title (it has no vintage of its own and can
   hold several), so the long-title problem is solved by type rather than by
   splitting the field. Two moves, both from the prototype:
   1. the display size steps down in two bands by how many lines the name
      actually RENDERS to — measured in view.js § bootTitleFit, off a
      character-count estimate served by render.php § Long-title display band so
      the common cases are already banded in the first paint;
   2. the wrap is balanced and the measure capped, so no line is left holding a
      single word. */
.rhinory-bundle-pdp .hp-name{
  font-family:var(--ff-display); font-weight:500; font-size:var(--type-hero);
  line-height:0.96; letter-spacing:-0.008em; color:var(--ink-warm); margin:14px 0 0 0;
  text-wrap:balance; overflow-wrap:break-word; max-width:22ch;
}
.rhinory-bundle-pdp .hp-name.is-long{ font-size:calc(var(--type-hero) * 0.80); line-height:1.0; max-width:24ch }
.rhinory-bundle-pdp .hp-name.is-xlong{ font-size:calc(var(--type-hero) * 0.64); line-height:1.04; letter-spacing:-0.004em; max-width:26ch }

/* Teaser (C7 short description) and the conservation line below it are SIBLING
   editorial paragraphs and share one size and leading — the conservation line
   used to sit at the foot of the buy block in a smaller italic, which read as a
   caption rather than as the second half of the same thought. */
.rhinory-bundle-pdp .hp-teaser{
  font-family:var(--ff-body); font-size:var(--type-body-editorial); line-height:1.7;
  color:var(--ink-warm-2); margin:24px 0 0 0; padding:24px 0 0 0;
  border-top:1px solid var(--rule-warm-2); max-width:54ch;
}
.rhinory-bundle-pdp .hp-mission{
  font-family:var(--ff-body); font-size:var(--type-body-editorial); line-height:1.7;
  color:var(--ink-warm-2); margin:18px 0 0 0; max-width:54ch;
}
.rhinory-bundle-pdp .hp-mission em{ font-style:italic; color:var(--burgundy) }

/* ---- buy row (shared grammar with the wine + merch PDPs) ---- */
.rhinory-bundle-pdp .hp-buy{ margin-top:30px; padding-top:28px; border-top:1px solid var(--rule-warm-2); display:flex; flex-wrap:wrap; align-items:baseline; gap:20px 28px }
/* The price block is a COLUMN of lines, mirroring the wine PDP's member
   treatment (assets/css/wine-pdp.css § member club price): the member state
   adds a context eyebrow above the figure line and a savings badge below it.
   The ANONYMOUS paint is unchanged from Build 3 — one baseline row holding the
   set price and its unit, because everything member-only server-renders
   `hidden`. */
.rhinory-bundle-pdp .hp-price{ display:flex; flex-direction:column; align-items:flex-start }
.rhinory-bundle-pdp .hp-figure{ display:flex; align-items:baseline; gap:10px; flex-wrap:wrap }
/* Wrapper around the frozen base marker so "reg." can sit beside it and the
   pair can take its own line when demoted. Inline-flex takes its baseline from
   the amount, so the anonymous row still baseline-aligns with the unit. */
.rhinory-bundle-pdp .hp-retail{ display:inline-flex; align-items:baseline; gap:8px }
.rhinory-bundle-pdp .hp-price .amt{ font-family:var(--ff-display); font-weight:500; font-size:58px; line-height:1; letter-spacing:-0.01em; color:var(--ink-warm); font-variant-numeric:lining-nums; font-feature-settings:"lnum" 1 }
.rhinory-bundle-pdp .hp-price .unit{ font-family:var(--ff-label); font-size:11px; font-weight:600; letter-spacing:0.18em; text-transform:uppercase; color:var(--rhino-warm); line-height:1.7 }
.rhinory-bundle-pdp .hp-price .member{ display:inline-flex; align-items:baseline; gap:10px }
.rhinory-bundle-pdp .hp-price .member strong{ font-family:var(--ff-display); font-weight:500; font-size:58px; line-height:1; letter-spacing:-0.01em; color:var(--ink-warm); font-variant-numeric:lining-nums; font-feature-settings:"lnum" 1 }
/* Context eyebrow — burgundy small-caps with a SOLID square pip: it marks a
   standing benefit, not a flagged markdown. */
.rhinory-bundle-pdp .hp-club-eyebrow{ display:inline-flex; align-items:center; gap:10px; margin-bottom:12px; font-family:var(--ff-label); font-size:11.5px; font-weight:600; letter-spacing:0.2em; text-transform:uppercase; color:var(--burgundy) }
.rhinory-bundle-pdp .hp-club-eyebrow .pip{ width:7px; height:7px; background:var(--burgundy); flex:0 0 auto }
.rhinory-bundle-pdp .hp-retail .reg{ font-family:var(--ff-label); font-size:12px; font-weight:600; letter-spacing:0.06em; text-transform:uppercase; color:var(--rhino-warm) }
/* Savings badge — additive, in the member's own voice. The figure is computed
   by view.js from the two live prices; nothing here authors an amount. */
.rhinory-bundle-pdp .hp-save{ display:inline-flex; align-items:center; gap:8px; margin-top:14px; font-family:var(--ff-label); font-size:10.5px; font-weight:600; letter-spacing:0.18em; text-transform:uppercase; color:var(--burgundy) }
.rhinory-bundle-pdp .hp-save-ic{ width:14px; height:14px; flex:0 0 auto; stroke:var(--burgundy); fill:none; stroke-width:1.5 }
/* Every member-only part server-renders `hidden`. Each display declaration
   above has equal specificity to the UA `[hidden]` rule, so each must re-assert
   it or the empty element paints anyway. */
.rhinory-bundle-pdp .hp-price .member[hidden],
.rhinory-bundle-pdp .hp-club-eyebrow[hidden],
.rhinory-bundle-pdp .hp-retail .reg[hidden],
.rhinory-bundle-pdp .hp-save[hidden]{ display:none }
/* Member promote (view.js .is-member guard): the club price becomes the primary
   figure because it is what THIS viewer is charged at checkout, and the base
   price drops to a quiet "reg. $300" on its own line. NO strikethrough — the
   strike is reserved for a genuine sale. A benefit is not a markdown. */
.rhinory-bundle-pdp .hp-price.is-member .member{ order:1 }
.rhinory-bundle-pdp .hp-price.is-member .hp-retail{ order:2; flex:1 1 100%; margin-top:10px }
/* The base line owns the unit anonymously and the club line owns it for a
   member — whichever is not the primary figure drops its unit, so "the set ·
   4 items" never reads as modifying the reference price. */
.rhinory-bundle-pdp .hp-price.is-member > .hp-figure > .unit{ display:none }
.rhinory-bundle-pdp .hp-price.is-member .amt{ font-family:var(--ff-label); font-size:12px; font-weight:600; letter-spacing:0.06em; text-transform:uppercase; color:var(--rhino-warm); text-decoration:none }
/* The availability label takes its own row ABOVE the figure rather than floating
   mid-height beside a 58px numeral. It is written after the price in source order
   (the price is the more important thing to reach first), so `order` does the move. */
.rhinory-bundle-pdp .hp-stock{ flex:0 0 100%; order:-1; margin:0 0 -4px 0; font-family:var(--ff-label); font-size:11px; font-weight:600; letter-spacing:0.18em; text-transform:uppercase; color:var(--burgundy); display:inline-flex; align-items:center; gap:9px }
.rhinory-bundle-pdp .hp-stock .dot{ width:7px; height:7px; border-radius:50%; background:var(--burgundy); box-shadow:0 0 0 3px rgba(82,0,0,0.14) }
/* A closed state must not read as an in-stock badge — same shape, muted ink. */
.rhinory-bundle-pdp .hp-stock.is-closed{ color:var(--rhino-warm) }
.rhinory-bundle-pdp .hp-stock.is-closed .dot{ background:var(--rhino-warm); box-shadow:0 0 0 3px rgba(113,114,113,0.16) }
.rhinory-bundle-pdp .hp-actions{ display:flex; flex-wrap:wrap; gap:12px; width:100%; margin-top:6px }
.rhinory-bundle-pdp .hp-add{
  display:inline-flex; align-items:center; justify-content:center; gap:10px;
  padding:16px 30px; min-height:52px; background:var(--burgundy); color:var(--paper);
  font-family:var(--ff-label); font-size:12px; font-weight:600; letter-spacing:0.16em;
  text-transform:uppercase; border:1px solid var(--burgundy); border-radius:2px;
  cursor:pointer; box-shadow:0 14px 30px -18px rgba(82,0,0,0.55);
  transition:background .25s, color .25s, transform .2s; flex:1 1 auto;
}
.rhinory-bundle-pdp .hp-add .arr{ transition:transform .25s }
.rhinory-bundle-pdp .hp-add:hover{ background:var(--burgundy-2); transform:translateY(-1px) }
.rhinory-bundle-pdp .hp-add:hover .arr{ transform:translateX(4px) }
.rhinory-bundle-pdp .hp-add[disabled]{ background:var(--card); color:var(--rhino-warm); border-color:var(--rule-warm-2); box-shadow:none; cursor:not-allowed }
.rhinory-bundle-pdp .hp-add[disabled]:hover{ background:var(--card); transform:none }
.rhinory-bundle-pdp .hp-sec{
  display:inline-flex; align-items:center; justify-content:center; gap:9px;
  padding:16px 24px; min-height:52px; background:none; color:var(--ink-warm-2);
  font-family:var(--ff-label); font-size:11px; font-weight:600; letter-spacing:0.16em;
  text-transform:uppercase; border:1px solid var(--rule-warm-2); border-radius:2px; cursor:pointer;
}
.rhinory-bundle-pdp .hp-sec:hover{ border-color:var(--burgundy); color:var(--burgundy) }
.rhinory-bundle-pdp .hp-sec .ic{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round }

/* ---- "Members from" mark IN the buy block ----
   The catalog's own member device (prototypes/13/shop-all.css § .sa-memberest on
   the shared .sa-crashsub component), ported here so a card and a PDP recruit
   with the same affordance rather than two look-alikes. One computed figure on a
   dashed explainer border; the tier ladder lives in the popover, which opens
   UPWARD so it never covers the Add-to-Bag row beneath it.

   These are the PROTOTYPE's class names, deliberately: the frozen plugin surface
   for this component is `.winery-product-card__member-preview*` and it is
   consumed read-only on the catalog, never re-used here. The box skin (border,
   shadow, arrow, type) repeats the values already shipped for that popover in
   assets/css/winery-storefront.css so the two read as one system. */
.rhinory-bundle-pdp .hp-memberest{ position:relative; flex:0 0 100%; margin:4px 0 -8px 0 }
.rhinory-bundle-pdp .sa-crashsub-mark{
  display:inline-flex; align-items:center; gap:6px; background:none; border:0; margin:0; padding:0 0 3px 0; cursor:pointer;
  border-bottom:1px dashed var(--rule-warm-2); text-align:left;
  font-family:var(--ff-body); font-size:13px; font-weight:500; letter-spacing:0.01em;
  color:var(--ink-warm-2); font-variant-numeric:tabular-nums;
  transition:color .2s, border-color .2s;
}
.rhinory-bundle-pdp .sa-crashsub-mark .e{ font-style:italic; color:var(--rhino-warm) }
.rhinory-bundle-pdp .sa-crashsub-mark b{
  font-family:var(--ff-display); font-size:19px; font-weight:600; letter-spacing:-0.01em;
  color:var(--burgundy); line-height:1; font-variant-numeric:lining-nums; font-feature-settings:"lnum" 1;
}
.rhinory-bundle-pdp .sa-crashsub-mark .m{
  font-family:var(--ff-label); font-size:9px; font-weight:600; letter-spacing:0.16em;
  text-transform:uppercase; color:var(--rhino-warm);
}
.rhinory-bundle-pdp .sa-crashsub-mark svg{ width:13px; height:13px; margin-left:1px; opacity:.55; stroke:currentColor; fill:none; stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round }
.rhinory-bundle-pdp .sa-crashsub-mark:hover,
.rhinory-bundle-pdp .sa-crashsub-mark:focus-visible,
.rhinory-bundle-pdp .sa-crashsub-mark[aria-expanded="true"]{ color:var(--burgundy); border-color:var(--rule-burg) }
.rhinory-bundle-pdp .sa-crashsub-mark:hover b,
.rhinory-bundle-pdp .sa-crashsub-mark[aria-expanded="true"] b{ color:var(--burgundy-2) }
.rhinory-bundle-pdp .sa-crashsub-mark:hover svg,
.rhinory-bundle-pdp .sa-crashsub-mark[aria-expanded="true"] svg{ opacity:1 }
.rhinory-bundle-pdp .sa-crashsub--pop .sa-crashsub-pop{
  position:absolute; left:0; bottom:calc(100% + 12px); z-index:9; width:min(300px,84vw);
  background:var(--paper); border:1px solid var(--rule-burg); border-left:2px solid var(--burgundy);
  box-shadow:0 26px 64px -26px rgba(45,41,38,.6); padding:18px 20px 18px 18px;
  opacity:0; visibility:hidden; transform:translateY(6px);
  transition:opacity .2s, transform .2s, visibility .2s;
}
.rhinory-bundle-pdp .sa-crashsub--pop .sa-crashsub-pop::after{
  content:""; position:absolute; left:20px; top:100%; width:12px; height:12px; margin-top:-6px;
  background:var(--paper); border-right:1px solid var(--rule-burg); border-bottom:1px solid var(--rule-burg); transform:rotate(45deg);
}
/* Hover + focus-within are the pointer/keyboard reveal; `.is-open` is the
   click-to-pin state view.js sets for touch. */
.rhinory-bundle-pdp .sa-crashsub--pop:hover .sa-crashsub-pop,
.rhinory-bundle-pdp .sa-crashsub--pop:focus-within .sa-crashsub-pop,
.rhinory-bundle-pdp .sa-crashsub--pop.is-open .sa-crashsub-pop{ opacity:1; visibility:visible; transform:translateY(0) }
.rhinory-bundle-pdp .sa-crashsub-pop .cs-h{ font-family:var(--ff-display); font-weight:600; font-size:19px; line-height:1.14; color:var(--ink-warm); margin:0 }
.rhinory-bundle-pdp .sa-crashsub-pop .cs-note{ font-family:var(--ff-body); font-size:13px; line-height:1.5; color:var(--ink-warm-2); margin:8px 0 0 0 }
.rhinory-bundle-pdp .sa-crashsub-pop .cs-link{
  display:inline-flex; align-items:center; gap:7px; margin:13px 0 0 0;
  font-family:var(--ff-label); font-size:10.5px; font-weight:600; letter-spacing:0.14em; text-transform:uppercase;
  color:var(--burgundy); border-bottom:1px solid var(--rule-burg); padding-bottom:2px; transition:border-color .2s;
}
.rhinory-bundle-pdp .sa-crashsub-pop .cs-link:hover{ border-color:var(--burgundy) }
.rhinory-bundle-pdp .me-tiers{ list-style:none; margin:9px 0 11px; padding:0; display:flex; flex-direction:column; gap:7px }
.rhinory-bundle-pdp .me-tiers li{ display:flex; justify-content:space-between; align-items:baseline; gap:18px; font-family:var(--ff-body); font-size:13px; line-height:1.2; color:var(--ink-warm-2) }
.rhinory-bundle-pdp .me-tiers li b{ font-family:var(--ff-label); font-size:10px; font-weight:600; letter-spacing:0.12em; text-transform:uppercase; color:var(--burgundy) }
/* A promoted member sees their own live figure in the price row, so the recruit
   mark is suppressed for them (view.js) — an estimate of what they already pay
   is noise. */
.rhinory-bundle-pdp .hp-memberest[hidden]{ display:none }

/* ---- shared section shell ---- */
.rhinory-bundle-pdp .wp-sec{ padding:104px 0; border-bottom:1px solid var(--rule-warm-2) }
.rhinory-bundle-pdp .wp-head{ max-width:920px }
.rhinory-bundle-pdp .wp-title{ font-family:var(--ff-display); font-weight:500; font-size:var(--type-h2); line-height:1.0; letter-spacing:-0.005em; color:var(--ink-warm); margin:14px 0 0 0 }
.rhinory-bundle-pdp .wp-title em{ font-style:italic; color:var(--burgundy) }
/* ---- product body field (C7 `content`) ----
   Free rich text of unknown length and unknown wrapper depth — Commerce7 exports
   arrive wrapped in their own `<div class="c7-product__content">` on some
   products and as bare `<p>`s on others. So the rhythm is set with a bottom
   margin on every descendant paragraph rather than an adjacent-sibling rule,
   which a wrapper div would silently break, and the last child's margin is
   collapsed so the flow does not add a phantom row above the cards. */
.rhinory-bundle-pdp .wp-body{ margin:22px 0 0 0; max-width:62ch }
.rhinory-bundle-pdp .wp-body p{ font-family:var(--ff-body); font-size:var(--type-lede); line-height:1.74; color:var(--ink-warm); margin:0 0 16px 0; text-wrap:pretty }
.rhinory-bundle-pdp .wp-body ul,
.rhinory-bundle-pdp .wp-body ol{ font-family:var(--ff-body); font-size:var(--type-lede); line-height:1.74; color:var(--ink-warm); margin:0 0 16px 0; padding-left:1.2em }
.rhinory-bundle-pdp .wp-body li + li{ margin-top:6px }
.rhinory-bundle-pdp .wp-body > *:last-child,
.rhinory-bundle-pdp .wp-body > *:last-child > *:last-child{ margin-bottom:0 }
.rhinory-bundle-pdp .wp-body em{ font-style:italic; color:var(--burgundy) }
.rhinory-bundle-pdp .wp-body strong{ color:var(--ink-warm); font-weight:600 }
/* The body carries operator images only by accident; keep them inside the measure. */
.rhinory-bundle-pdp .wp-body img{ max-width:100%; height:auto }

/* ---- what's inside: the shop-all CATALOG CARD unit ----
   Ported from prototypes/13/shop-all.css (.sa-grid / .sa-card / .sa-shot /
   .sa-body / .sa-buy), which is what prototypes/13/19-bundle-pdp.html loads
   for this section: "the trio's components reuse the shop-all card unit
   rather than a bespoke bundle row" (19-bundle-pdp.html head comment).
   The v13 catalog tokens are named for the shop's own scope (--rule / --ink /
   --muted); this block's scope declares the warm set instead, so each ported
   rule is restated against the local token of the same role rather than
   importing a second token vocabulary.

   Two page-level additions, both named in the prototype's own section comment
   ("the bottle-slot label in the card footer"): `.bp-slot` and the `.bp-qty`
   badge. Everything else is the catalog unit verbatim, minus the catalog-only
   affordances a component card has no data for (price, add-to-cart, lifestyle
   hover swap, member ghost price, badges other than quantity).

   `.sa-card-eyebrow` / `.sa-vintage` / `.sa-teaser` / `.sa-varietal` are the
   four slots the plugin's canonical-resolver enrichment fills (v0.105.0); each
   is emitted only when Commerce7 has a value for it, so these rules must all
   read correctly against a card that has none of them. */
.rhinory-bundle-pdp .sa-grid{
  margin-top:40px; display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:0;
  background:var(--paper); border-top:1px solid var(--rule-warm); border-left:1px solid var(--rule-warm);
}
/* Hairline separators are per-card borders over a paper bed, so a short last
   row leaves clean paper rather than a stray gray cell (shop-all.css note). */
.rhinory-bundle-pdp .sa-card{
  position:relative; background:var(--paper); display:flex; flex-direction:column;
  border-right:1px solid var(--rule-warm); border-bottom:1px solid var(--rule-warm); transition:background .22s;
}
.rhinory-bundle-pdp .sa-card::after{ content:""; position:absolute; inset:0; border:1px solid transparent; pointer-events:none; transition:border-color .22s }
.rhinory-bundle-pdp .sa-card:hover{ background:var(--paper-2) }
.rhinory-bundle-pdp .sa-card:hover::after{ border-color:var(--burgundy) }
.rhinory-bundle-pdp .sa-card:focus-within::after{ border-color:var(--burgundy) }

/* Transparent cut-out packshots sit directly on the card's paper bed. */
.rhinory-bundle-pdp .sa-shot{
  position:relative; aspect-ratio:3/3.4; background:transparent;
  display:flex; align-items:center; justify-content:center; overflow:hidden; border-bottom:1px solid var(--rule-warm);
}
.rhinory-bundle-pdp .sa-shot picture{ display:contents }
.rhinory-bundle-pdp .sa-shot img{
  height:88%; width:auto; max-width:76%; object-fit:contain; display:block;
  filter:saturate(.96) contrast(1.03) drop-shadow(0 14px 26px rgba(45,41,38,.16));
  transform-origin:50% 86%; transition:transform 2.2s cubic-bezier(.2,.7,.2,1);
}
/* No lifestyle photo exists per component, so the card takes the catalog's
   other hover branch: a slow zoom toward the bottle's label. */
.rhinory-bundle-pdp .sa-card:hover .sa-shot img{ transform:scale(1.07) }
@media (prefers-reduced-motion:reduce){ .rhinory-bundle-pdp .sa-card:hover .sa-shot img{ transform:none } }
/* A child with no image at all (every draft packaging SKU today) draws the
   catalog's hatched placeholder rather than a broken frame. */
.rhinory-bundle-pdp .sa-shot .placeholder{
  width:100%; height:100%; display:flex; align-items:center; justify-content:center; text-align:center; padding:20px;
  background:repeating-linear-gradient(-45deg, rgba(45,41,38,0.045) 0 10px, transparent 10px 20px), var(--card);
}
.rhinory-bundle-pdp .sa-shot .placeholder span{
  font-family:var(--ff-label); font-size:10.5px; font-weight:600; letter-spacing:0.14em; text-transform:uppercase;
  color:var(--rhino-warm); line-height:1.7; max-width:16ch;
}

/* badges — the component card carries exactly one chit: multiplicity. */
.rhinory-bundle-pdp .sa-badges{ position:absolute; left:14px; top:14px; z-index:2; display:flex; flex-direction:column; align-items:flex-start; gap:6px }
.rhinory-bundle-pdp .sa-badge{
  display:inline-flex; align-items:center; gap:6px; padding:5px 10px; border-radius:2px;
  font-family:var(--ff-label); font-size:9.5px; font-weight:700; letter-spacing:0.14em; text-transform:uppercase;
}
.rhinory-bundle-pdp .sa-badge.bp-qty{ background:var(--card-2); color:var(--burgundy) }

.rhinory-bundle-pdp .sa-body{ position:relative; padding:22px 22px 24px 22px; display:flex; flex-direction:column; flex:1 }
/* v13 ladder EXCEPTION carried from the catalog: product/card titles stay
   weight 600 (not the light register's 500 cap) — product names are the
   primary scannable element on a card. */
.rhinory-bundle-pdp .sa-card-title{
  font-family:var(--ff-display); font-weight:600; font-size:var(--type-h3); line-height:1.04; letter-spacing:-0.004em;
  color:var(--ink-warm); margin:0; text-wrap:pretty;
}
.rhinory-bundle-pdp .sa-card-title a{ color:inherit }
.rhinory-bundle-pdp .sa-card:hover .sa-card-title a{ color:var(--burgundy) }

/* ---- the four enrichment slots (plugin v0.105.0) ---- */
.rhinory-bundle-pdp .sa-card-eyebrow{
  font-family:var(--ff-label); font-size:10.5px; font-weight:600; letter-spacing:0.14em; text-transform:uppercase;
  color:var(--burgundy); display:flex; flex-wrap:wrap; gap:7px; align-items:center; margin:0 0 5px 0;
}
.rhinory-bundle-pdp .sa-card-eyebrow .sep{ color:var(--rule-warm-2) }
.rhinory-bundle-pdp .sa-vintage{
  position:absolute; top:35px; right:22px; z-index:3; margin:0; line-height:1;
  font-family:var(--ff-label); font-size:12px; font-weight:600; letter-spacing:0.12em; text-transform:uppercase; color:var(--ink-warm-2);
}
/* The chit is taken out of flow so it hangs beside the title's first line, as
   the prototype draws it. The prototype's component titles are short because
   its eyebrow carries the collection; a real synced child title spells the
   collection out ("2022 Keras Collection South Africa Cabernet Sauvignon") and
   would wrap straight under the chit, so a card that HAS a vintage reserves the
   gutter for it. */
.rhinory-bundle-pdp .sa-body.has-vintage .sa-card-eyebrow{ padding-right:56px }
.rhinory-bundle-pdp .sa-body.has-vintage .sa-card-title{ padding-right:56px }
/* ---- ABV spec (Google Alcohol advertising policy, eff. 2026-09-30) ----
   Ported from prototypes/13/shop-all.css:333-364. A bundle has no ABV of its own — the
   same reason it has no single vintage — so the figure is per-component and rides the
   `.sa-vintage` mark each wine card already carries: INLINE, to the LEFT of the vintage,
   so the vintage stays pinned where it ships and the mark grows leftward into empty card.
   Zero added card height and nothing new in the reading column, which is the point: this
   is a fact label, not a selling number. The group carries its own TRAILING middot, so a
   card without ABV (the gift-box component) leaves the mark byte-identical to today.
   Class is `.rhw-abv` rather than the prototype's bare `.abv` — the theme's stylesheets
   are global, and `.abv` is too generic a name to introduce into that namespace. */
.rhinory-bundle-pdp .sa-vintage{ text-align:right }
.rhinory-bundle-pdp .sa-vintage .rhw-abv{
  font-size:10px; letter-spacing:0.1em; color:var(--rhino-warm); white-space:nowrap;
  font-variant-numeric:tabular-nums lining-nums; font-feature-settings:"lnum" 1,"tnum" 1;
}
.rhinory-bundle-pdp .sa-vintage .rhw-abv .n{ letter-spacing:0.06em }
.rhinory-bundle-pdp .sa-vintage .rhw-abv .d{ margin:0 6px; opacity:.55 }
.rhinory-bundle-pdp .sa-vintage .rhw-abv:empty{ display:none }
/* The mark is absolute, so a card that actually carries an ABV widens its reserve past
   the 56px a bare vintage needs. Only those cards pay it. */
.rhinory-bundle-pdp .sa-body:has(.sa-vintage .rhw-abv) .sa-card-eyebrow,
.rhinory-bundle-pdp .sa-body:has(.sa-vintage .rhw-abv) .sa-card-title{ padding-right:96px }
/* Column-aware, like the grid itself. Below a ~332px column the inline pair stops
   clearing the eyebrow, so the mark stacks (vintage over ABV, right-aligned), the middot
   hides, and the reserve moves off the eyebrow onto the title — so the eyebrow never
   wraps at any width. */
.rhinory-bundle-pdp .sa-card{ container-type:inline-size }
@container (max-width:332px){
  .rhinory-bundle-pdp .sa-vintage{ display:flex; flex-direction:column-reverse; align-items:flex-end; gap:5px }
  .rhinory-bundle-pdp .sa-vintage .rhw-abv .d{ display:none }
  .rhinory-bundle-pdp .sa-body:has(.sa-vintage .rhw-abv) .sa-card-eyebrow{ padding-right:0 }
  .rhinory-bundle-pdp .sa-body:has(.sa-vintage .rhw-abv) .sa-card-title{ padding-right:74px }
}
.rhinory-bundle-pdp .sa-teaser{
  font-family:var(--ff-label); font-size:11px; font-weight:600; letter-spacing:0.1em; text-transform:uppercase;
  color:var(--ink-warm-2); margin:11px 0 0 0; line-height:1.5;
}
.rhinory-bundle-pdp .sa-teaser .d{ color:var(--burgundy); margin:0 3px }
.rhinory-bundle-pdp .sa-varietal{
  font-family:var(--ff-label); font-size:10.5px; font-weight:600; letter-spacing:0.12em; text-transform:uppercase;
  color:var(--rhino-warm); margin:12px 0 0 0;
}

/* ---- card footer: slot label + (linked children only) the bottle CTA ---- */
.rhinory-bundle-pdp .sa-buy{ margin-top:auto; padding-top:20px; display:flex; align-items:flex-end; justify-content:space-between; gap:12px }
.rhinory-bundle-pdp .bp-slot{
  font-family:var(--ff-label); font-size:9.5px; font-weight:600; letter-spacing:0.12em; text-transform:uppercase;
  color:var(--rhino-warm); line-height:1.6; white-space:nowrap;
}
.rhinory-bundle-pdp .sa-add{
  display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:13px 20px; min-height:46px;
  background:var(--burgundy); border:1px solid var(--burgundy); border-radius:2px; cursor:pointer; white-space:nowrap;
  font-family:var(--ff-label); font-size:11px; font-weight:600; letter-spacing:0.14em; text-transform:uppercase;
  transition:background .22s, transform .18s;
}
/* The catalog CTA is an anchor here, and the block scope's own `a` rule paints
   links burgundy — restate the ivory label at equal specificity (same fix the
   prototype makes at 19-bundle-pdp.html: `.bpdp13 .sa-add`). */
.rhinory-bundle-pdp a.sa-add,
.rhinory-bundle-pdp a.sa-add:hover{ color:var(--paper); text-decoration:none }
.rhinory-bundle-pdp .sa-add .arr{ transition:transform .22s }
.rhinory-bundle-pdp .sa-add:hover{ background:var(--burgundy-2); transform:translateY(-1px) }
.rhinory-bundle-pdp .sa-add:hover .arr{ transform:translateX(3px) }

/* ---- COMPONENT COUNT ----
   The catalog grid is auto-fill, so a bundle with one web-visible component
   stranded two empty tracks and left the section reading as broken. The grid
   adapts to the resolved child count (class set server-side in render.php
   § Component-count adaptive grid): three or more keep the catalog grid, two run
   as two equal columns, one drops the grid entirely for a single wide feature row.

   Two at full section width would blow each card out to ~600px and the bottle
   shot with it, so the pair is capped at 820px: each card sits at the scale it
   has in a 4-up catalog row, and the pair reads as two of the same thing rather
   than two banners. The single row is capped at 940px for the same reason.
   Design: prototypes/13/19-bundle-pdp.html § .bp-grid.is-pair / .is-single. */
.rhinory-bundle-pdp .bp-grid.is-pair{ grid-template-columns:repeat(2,minmax(0,1fr)); max-width:820px }
.rhinory-bundle-pdp .bp-grid.is-single{ grid-template-columns:minmax(0,1fr); max-width:940px }
/* A lone card turns on its side: photo left, the full note right. It is not a
   link target as a whole (the CTA inside it is), so it drops the pointer cursor. */
.rhinory-bundle-pdp .bp-grid.is-single .sa-card{ display:grid; grid-template-columns:minmax(0,0.42fr) minmax(0,0.58fr); align-items:stretch; cursor:default }
/* `justify-self:stretch` is load-bearing, not decoration. The shot carries an
   aspect-ratio, and CSS Align says a grid item's DEFAULT `justify-self:normal`
   behaves as `start` (not `stretch`) once the box has a preferred ratio. So the
   stretched row height became definite, the ratio transferred it back into a
   WIDTH of height x 3/3.2, and the shot spilled past its 0.42fr track — dragging
   its border-right (the card's internal divider) ~74px into the text column,
   where it crossed the .sa-buy hairline instead of meeting the card frame.
   Explicit `stretch` pins the width to the track, so the divider lands exactly on
   the column boundary. The ratio still floors the row height when the note is
   short (a body-less card measures 420px, = track width x 3.2/3), which is the
   proportion it was there to guarantee.
   Design: prototypes/13/19-bundle-pdp.html § .bp-grid.is-single. */
.rhinory-bundle-pdp .bp-grid.is-single .sa-shot{ aspect-ratio:3/3.2; justify-self:stretch; border-bottom:0; border-right:1px solid var(--rule-warm) }
.rhinory-bundle-pdp .bp-grid.is-single .sa-body{ padding:40px 44px 36px 44px; justify-content:center }
.rhinory-bundle-pdp .bp-grid.is-single .sa-card-title{ font-size:calc(var(--type-h3) * 1.1) }
.rhinory-bundle-pdp .bp-grid.is-single .sa-vintage{ top:46px; right:44px }
.rhinory-bundle-pdp .bp-grid.is-single .sa-buy{ margin-top:26px; padding-top:26px; border-top:1px solid var(--rule-warm) }
/* The tasting note only exists on the single-component row — the wide card is
   the one with room for it (render.php § Component-count adaptive grid). Clamped
   so a very long C7 note cannot outrun the bottle shot beside it. */
.rhinory-bundle-pdp .sa-blurb{
  font-family:var(--ff-body); font-size:15px; line-height:1.55; color:var(--ink-warm-2);
  margin:14px 0 0 0; max-width:44ch; text-wrap:pretty;
  display:-webkit-box; -webkit-line-clamp:5; -webkit-box-orient:vertical; overflow:hidden;
}

/* ---- club upsell: Conservation Circle panel + member tier ladder ----
   Theme garnish (ownership map Rule 2), mirroring the wine PDP's own #club
   band so a bundle and a bottle upsell identically. The member panel used to
   sit in the #details spec-table grid the v13 pass cut; it keeps its own
   styling and now rides the panel's right column. */
.rhinory-bundle-pdp .club-grid{ display:grid; grid-template-columns:1.15fr 0.85fr; gap:56px; align-items:center }
.rhinory-bundle-pdp .club-title{ font-family:var(--ff-display); font-weight:500; font-size:var(--type-h2); line-height:1.04; letter-spacing:-0.006em; color:var(--ink-warm); margin:16px 0 0 0; max-width:16ch }
.rhinory-bundle-pdp .club-title em{ font-style:italic; color:var(--burgundy) }
.rhinory-bundle-pdp .club-copy{ font-family:var(--ff-body); font-size:var(--type-body-editorial); line-height:1.7; color:var(--ink-warm-2); margin:20px 0 0 0; max-width:48ch }
.rhinory-bundle-pdp .club-cta{ display:flex; flex-direction:column; gap:26px; align-items:stretch }
.rhinory-bundle-pdp .club-cta .hp-add{ align-self:flex-start; flex:0 0 auto }

/* ---- member tier ladder — hairline rows, no box ----
   The figures are the plugin's own club previews (render.php § member preview),
   sorted ascending by discount so the rows read as the 4/8/12-bottle progression.

   What this replaced: a bordered `.mp` card that repeated "Conservation Circle"
   as a second label beside the head, restated the full club name in every row,
   and carried "(est.)" three times down the price column. The house move is
   hairline rows — the tier name carries the row with its bottle count as a
   sub-label, the saving and the estimated set price sit in two aligned
   right-hand columns, and "(est.)" is stated ONCE in the footnote.

   The two right-hand tracks are FIXED, which is the whole point of the grid: the
   saving column starts on one line whatever figure sits beside it, and the price
   column is right-aligned with tabular numerals so $189 and $178.50 land on the
   same right edge, decimals or not.
   Design: prototypes/13/19-bundle-pdp.html § .club-tiers. */
.rhinory-bundle-pdp .club-tiers-head{
  display:flex; align-items:center; gap:10px; margin:0 0 6px 0;
  font-family:var(--ff-label); font-size:10px; font-weight:600; letter-spacing:0.22em;
  text-transform:uppercase; color:var(--burgundy);
}
.rhinory-bundle-pdp .club-tiers-head::before{ content:""; width:24px; height:1px; background:currentColor; flex:none }
.rhinory-bundle-pdp .club-tiers ul{ list-style:none; margin:0; padding:0; border-top:1px solid var(--rule-warm-2) }
.rhinory-bundle-pdp .club-tiers li{
  display:grid; grid-template-columns:1fr 70px minmax(96px,auto); align-items:baseline;
  gap:6px 22px; padding:14px 0; border-bottom:1px solid var(--rule-warm-2);
}
.rhinory-bundle-pdp .club-tiers .t{ font-family:var(--ff-body); font-size:15px; line-height:1.3; color:var(--ink-warm) }
.rhinory-bundle-pdp .club-tiers .t small{
  display:block; margin-top:4px; font-family:var(--ff-label); font-size:9.5px; font-weight:600;
  letter-spacing:0.14em; text-transform:uppercase; color:var(--rhino-warm);
}
.rhinory-bundle-pdp .club-tiers .s{
  font-family:var(--ff-label); font-size:10px; font-weight:600; letter-spacing:0.16em;
  text-transform:uppercase; color:var(--rhino-warm); white-space:nowrap;
}
.rhinory-bundle-pdp .club-tiers .p{
  font-family:var(--ff-display); font-weight:500; font-size:26px; line-height:1; color:var(--burgundy);
  font-variant-numeric:lining-nums tabular-nums; font-feature-settings:"lnum" 1,"tnum" 1;
  text-align:right; white-space:nowrap;
}
.rhinory-bundle-pdp .club-fine{
  margin:16px 0 0 0; font-family:var(--ff-body); font-style:italic; font-size:13.5px;
  line-height:1.62; color:var(--rhino-warm); max-width:46ch;
}
.rhinory-bundle-pdp .club-fine a{ font-style:normal }

/* ---- public-sale treatment (prototypes/13/sale-price.css Context 1) ----
   Ported from the wine PDP's block of the same name so a bundle on sale and a
   bottle on sale read identically. Inert on a full-price bundle: `.pt-sale` is
   only added when `is_on_sale` is true, and `.pt-wasgrp` is only emitted behind
   the numeric struck-retail gate. */
.rhinory-bundle-pdp .pt-sale{ --pt-accent:#520000; --pt-was:rgba(45,41,38,0.52); --pt-was-size:20px }
.rhinory-bundle-pdp .pt-saleline{ display:flex; align-items:center; gap:10px; margin:0 0 10px; font-family:var(--ff-label); font-size:10px; font-weight:600; letter-spacing:0.22em; text-transform:uppercase; color:var(--pt-accent, var(--burgundy)) }
.rhinory-bundle-pdp .pt-saleline::before{ content:""; width:24px; height:1px; background:currentColor; flex:none }
.rhinory-bundle-pdp .pt-saleline .d{ opacity:.45 }
.rhinory-bundle-pdp .pt-saleline .pct{ opacity:.78 }
.rhinory-bundle-pdp .pt-wasgrp{ display:inline-flex; align-items:baseline; gap:6px }
.rhinory-bundle-pdp .pt-waslab{ font-family:var(--ff-label); font-size:9.5px; font-weight:600; letter-spacing:0.16em; text-transform:uppercase; color:var(--pt-was, var(--rhino-warm)); font-style:normal }
/* The rule is a centered pseudo-element, NOT text-decoration:line-through,
   which misrenders on Cormorant Garamond italic. "was" sits OUTSIDE the strike
   so the rule only crosses the figure. */
.rhinory-bundle-pdp .pt-was{ position:relative; font-family:var(--ff-display); font-size:var(--pt-was-size, 20px); font-weight:500; font-style:italic; line-height:1; color:var(--pt-was, var(--rhino-warm)); text-decoration:none; font-variant-numeric:lining-nums; font-feature-settings:"lnum" 1 }
.rhinory-bundle-pdp .pt-was::after{ content:""; position:absolute; left:-2px; right:-2px; top:50%; height:1px; background:currentColor; transform:translateY(-0.5px); pointer-events:none }

/* ---- dark closer (register returns to Reserve-cool at the foot) ----
   The SHARED cross-page band, matching the wine PDP's `#conservation` — same
   photograph, same scrim, same copy. Without the image this was a flat near-black
   strip stacked directly on the dark site footer, and the two read as a duplicated
   footer; the photograph is what makes a closer a closer. */
.rhinory-bundle-pdp .cl-close{ position:relative; overflow:hidden; background:#1A1512; color:var(--paper); text-align:center; padding:120px 0; border-bottom:none }
.rhinory-bundle-pdp .cl-rhino{ position:absolute; inset:0; z-index:0 }
/* `display:contents` on the <picture> the image pipeline emits — the wrapper must
   stay transparent to the box model or the cover image loses its frame. */
.rhinory-bundle-pdp .cl-rhino picture{ display:contents }
.rhinory-bundle-pdp .cl-rhino img{ width:100%; height:100%; object-fit:cover; object-position:center; display:block }
.rhinory-bundle-pdp .cl-scrim{
  position:absolute; inset:0; z-index:1;
  background:
    radial-gradient(120% 96% at 50% 44%, rgba(26,21,18,0.52) 0%, rgba(26,21,18,0.82) 62%, rgba(26,21,18,0.95) 100%),
    linear-gradient(180deg, rgba(26,21,18,0.72) 0%, rgba(26,21,18,0.5) 45%, rgba(26,21,18,0.9) 100%);
}
.rhinory-bundle-pdp .cl-close .wp-wrap{ position:relative; z-index:2 }
.rhinory-bundle-pdp .cl-close .body{
  font-family:var(--ff-body); font-size:var(--type-body-editorial); line-height:1.7;
  color:rgba(251,247,238,0.9); max-width:60ch; margin:0 auto 34px auto;
}
.rhinory-bundle-pdp .cl-close .wp-eyebrow{ color:rgba(251,247,238,0.72); justify-content:center }
.rhinory-bundle-pdp .cl-close .wp-eyebrow::before{ background:rgba(251,247,238,0.5) }
.rhinory-bundle-pdp .cl-close h2{ font-family:var(--ff-display); font-weight:500; font-size:var(--type-h2); line-height:1.02; letter-spacing:-0.01em; margin:18px auto 24px auto; max-width:20ch }
.rhinory-bundle-pdp .cl-close h2 em{ font-style:italic; color:var(--acacia) }
.rhinory-bundle-pdp .cl-close .sig{ font-family:var(--ff-display); font-style:italic; font-weight:500; font-size:32px; color:var(--paper); display:inline-block; padding:18px 40px; border-top:1px solid rgba(251,247,238,0.28); border-bottom:1px solid rgba(251,247,238,0.28) }
.rhinory-bundle-pdp .cl-close .triad{ margin-top:30px; font-family:var(--ff-label); font-size:11px; font-weight:600; letter-spacing:0.28em; text-transform:uppercase; color:rgba(251,247,238,0.6) }
.rhinory-bundle-pdp .cl-close .triad span{ color:#E8B4A0; margin:0 6px }

/* ---- responsive ---- */
@media (max-width:1024px){
  .rhinory-bundle-pdp .wp-wrap{ padding:0 40px }
  /* Photo above, product block below — the gallery keeps its full-bleed width
     and hands the vertical rule to a horizontal one. */
  .rhinory-bundle-pdp .bh-hero{ grid-template-columns:1fr }
  .rhinory-bundle-pdp .bh-gallery{ border-right:0; border-bottom:1px solid var(--rule-warm-2) }
  .rhinory-bundle-pdp .bh-thumbs{ padding:8px 8px 0 8px }
  /* Stacked single column: there is no second column left to match, so the solo
     photo returns to the plain 4:3 frame. */
  .rhinory-bundle-pdp .bh-hero.is-solo .bh-frame{ aspect-ratio:4/3; min-height:0 }
  .rhinory-bundle-pdp .bh-detail{ padding:34px 40px 64px 40px; max-width:none }
  .rhinory-bundle-pdp .club-grid{ grid-template-columns:1fr; gap:32px }
  .rhinory-bundle-pdp .club-cta{ align-items:flex-start }
}
@media (max-width:720px){
  /* Both count-adaptive layouts collapse to one column here — a 2-up of catalog
     cards and a side-by-side feature row both stop working below ~720px. */
  .rhinory-bundle-pdp .bp-grid.is-pair{ grid-template-columns:minmax(0,1fr) }
  .rhinory-bundle-pdp .bp-grid.is-single .sa-card{ grid-template-columns:minmax(0,1fr) }
  .rhinory-bundle-pdp .bp-grid.is-single .sa-shot{ aspect-ratio:3/3.4; min-height:0; border-right:0; border-bottom:1px solid var(--rule-warm) }
  .rhinory-bundle-pdp .bp-grid.is-single .sa-body{ padding:26px 24px 28px 24px }
  .rhinory-bundle-pdp .bp-grid.is-single .sa-vintage{ top:35px; right:24px }
  .rhinory-bundle-pdp .club-tiers li{ grid-template-columns:1fr 70px minmax(84px,auto); gap:4px 14px }
}
/* The member explainer is a popover on desktop and an INLINE reveal (a drawer)
   on a phone — a floating box anchored to a mark near the top of a 390px viewport
   has nowhere to open. Mirrors the catalog's own breakpoint behaviour
   (prototypes/13/shop-all.css § .sa-memberest @640px). Hover is dropped with it:
   on touch the reveal is the click-pin `.is-open` state view.js sets. */
@media (max-width:640px){
  .rhinory-bundle-pdp .sa-memberest.sa-crashsub--pop .sa-crashsub-pop{
    position:static; width:auto; left:auto; bottom:auto; box-shadow:none; transform:none;
    padding:0; border-left:2px solid transparent; max-height:0; overflow:hidden; opacity:0; visibility:visible;
    transition:max-height .3s ease, opacity .2s ease, padding .3s ease;
  }
  .rhinory-bundle-pdp .sa-memberest.sa-crashsub--pop .sa-crashsub-pop::after{ display:none }
  .rhinory-bundle-pdp .sa-memberest.sa-crashsub--pop:focus-within .sa-crashsub-pop,
  .rhinory-bundle-pdp .sa-memberest.sa-crashsub--pop.is-open .sa-crashsub-pop{
    max-height:320px; opacity:1; margin-top:12px; padding:13px 0 4px 16px; border-left-color:var(--burgundy);
  }
}
@media (max-width:560px){
  .rhinory-bundle-pdp .wp-wrap{ padding:0 22px }
  .rhinory-bundle-pdp .wp-sec{ padding:72px 0 }
  .rhinory-bundle-pdp .bh-detail{ padding:28px 22px 56px 22px }
  .rhinory-bundle-pdp .hp-actions{ flex-direction:column }
  .rhinory-bundle-pdp .hp-add,
  .rhinory-bundle-pdp .hp-sec{ width:100%; flex:1 1 auto }
  /* The card grid is auto-fill, so it collapses to one column on its own; the
     only phone rules the catalog keeps are the CTA's tighter footprint and a
     title step-down. */
  .rhinory-bundle-pdp .sa-card-title{ font-size:23px }
  .rhinory-bundle-pdp .sa-add{ padding:12px 14px; font-size:10px; letter-spacing:0.1em; min-width:122px }
  /* The ladder keeps its three tracks down to 390px — the row head is now a short
     tier name ("Collector") rather than the full club name, so it fits. Only the
     figure steps down. */
  .rhinory-bundle-pdp .club-tiers li{ grid-template-columns:1fr 62px minmax(74px,auto); gap:4px 12px }
  .rhinory-bundle-pdp .club-tiers .p{ font-size:22px }
}
