/* ============================================================
   2BE CLOTHING WORLD — PHASE 2 COMMERCE STYLES
   Loaded after site.css on /shop/* only. Same tokens, same rules:
   radius 0, hairlines, 8px grid, 300–500ms ease-out (guide §01–05).
   White commerce surface; premium items carry the dark card treatment
   (guide §01 surface roles).
   ============================================================ */

/* ============ page chrome ============ */
/* Shop pages have no dark hero under the fixed nav — the bar is solid from
   the start (same surface as its scrolled state). */
/* .nav--solid moved to site.css for the same reason as .band-head —
   three non-shop pages use it and don't load this file. */
.shopmain{ padding-top:var(--nav-h); }
/* .band-head moved to site.css — it is used by page families that do
   not load this file (/account/, /find-us/, /2be-trade/), which left
   their content sliding under the fixed nav. */

/* ============ PLP: sticky filter bar ============ */
.shopbar{
  position:sticky; top:var(--nav-h); z-index:var(--z-sticky);
  background:var(--light); border-bottom:1px solid var(--hairline);
}
.shopbar__row{
  display:flex; align-items:center; gap:8px;
  padding-block:12px;
  overflow-x:auto; overscroll-behavior-x:contain;
  scrollbar-width:none;

  /* SAY THAT THIS ROW SCROLLS.
     Six filter groups fit at desktop widths, so the hidden scrollbar above
     costs nothing there. At 360px the row is 722px wide inside a 360px
     window and Color, Price and Line sit off the right edge — with the
     scrollbar suppressed there was no scrollbar, no fade and no cue of any
     kind that half the filters existed. They were not hard to reach; they
     were invisible.
     This is the background-attachment scroll-shadow: the two `local` layers
     are painted in the row's own background colour and travel WITH the
     content, so they sit exactly over the true start and end; the two
     `scroll` layers are the shadows and stay pinned to the visible edges.
     At either end the matching cover lands on top of the shadow and hides
     it, so a shadow appears only on a side that genuinely has more to show.
     No JS, and nothing to keep in sync with the scroll position.
     Kept off the pointer entirely — these are background layers, so they
     cannot intercept a tap on the buttons underneath. */
  background:
    linear-gradient(to right, var(--light), rgba(255,255,255,0)) 0 0 / 28px 100% no-repeat local,
    linear-gradient(to left,  var(--light), rgba(255,255,255,0)) 100% 0 / 28px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%,    rgba(17,17,17,.16), rgba(17,17,17,0)) 0 0 / 14px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(17,17,17,.16), rgba(17,17,17,0)) 100% 0 / 14px 100% no-repeat scroll;
}
.shopbar__row::-webkit-scrollbar{ display:none; }
.fgroup{ position:relative; flex:none; }
.fgroup__btn{
  /* 40px was under the 44px floor — see the tap-target note in site.css. */
  min-height:44px; padding:0 20px; cursor:pointer;
  background:transparent; color:var(--ink);
  border:1px solid var(--hairline);
  font-size:12px; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
  transition:border-color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease),
             color var(--t-fast) var(--ease);
  white-space:nowrap;
}
.fgroup__btn[aria-expanded="true"],
.fgroup__btn.is-active{ background:var(--ink); color:var(--bg); border-color:var(--ink); }
.fgroup__panel{
  position:absolute; left:0; top:calc(100% + 8px); z-index:var(--z-sticky-panel);
  display:none; min-width:200px; padding:8px;
  background:var(--light); border:1px solid var(--hairline);
  box-shadow:0 16px 40px rgba(17,17,17,.12);
}
.fgroup__btn[aria-expanded="true"] + .fgroup__panel{ display:grid; gap:2px; }
.fopt{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  min-height:40px; padding:0 12px; cursor:pointer; text-align:left;
  background:transparent; border:0; font-size:14px; color:var(--ink);
  transition:background-color var(--t-fast) var(--ease);
}
.fopt:hover{ background:rgba(17,17,17,.05); }
.fopt[aria-pressed="true"]{ background:var(--ink); color:var(--bg); }
.shopbar__clear{
  margin-left:auto; flex:none;
  background:transparent; border:0; cursor:pointer;
  font-size:12px; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted); padding:0 8px; height:40px;
  text-decoration:underline; text-underline-offset:4px;
}
.shopbar__count{ flex:none; color:var(--muted); padding-inline:8px; }

/* ---------- WHY THE FILTER DROPDOWN DISAPPEARED ----------
   Reported as "the nav sits over the filter bar". It is not the nav and it
   is not the z-scale: --z-nav (100) already outranks --z-sticky (60) by
   design, and the bar sticks to top:var(--nav-h) so the two never meet.

   The panel was being CLIPPED. `.shopbar__row` carries overflow-x:auto for
   the chip scroller, and per CSS a `visible` value on the other axis
   computes to `auto` when its partner is not visible — so the row is a
   scroll container in BOTH directions. `.fgroup` was position:relative,
   which put the panel's containing block INSIDE that container, and a
   310px dropdown was cut to the 4px of row below the button. What showed
   through underneath was the product grid, which reads exactly like
   something covering the bar.

   The mobile branch below never had the bug, and that is the clue: it sets
   `.fgroup{position:static}`, so the panel resolves against `.shopbar`
   instead — outside the scroll container, and therefore unclipped.

   Two rules, at the cause:
     - below 900px the panel is a full-width sheet anchored to .shopbar,
       exactly as it already was on phones, extended to cover the 768-899
       range where the chips still scroll;
     - at 900px and up nothing overflows — rails.css already asserts this by
       switching its scroll-fade off at the same breakpoint — so the row
       stops being a scroll container at all and the per-button dropdown has
       nothing left to clip it. */
@media (max-width:899px){
  .fgroup{ position:static; }
  .fgroup__panel{ left:var(--pad-x); right:var(--pad-x); min-width:0; }
}
@media (min-width:900px){
  .shopbar__row{ overflow:visible; }
}

/* ============ PLP grid (2-col mobile / 4-col desktop) ============ */
.shopgrid{ display:grid; gap:48px 24px; grid-template-columns:repeat(2,1fr); padding-block:48px 96px; }
@media (min-width:1024px){ .shopgrid{ grid-template-columns:repeat(4,1fr); } }
/* grid items must be allowed to shrink below content min-width, and the
   unbreakable [PLACEHOLDER-COPY:…] token must wrap, or 2-col mobile overflows */
.shopgrid > *{ min-width:0; }
.card__body > div{ min-width:0; }

/* PLP card images are wrapped in a PDP link — carry the frame contract
   (cover fit + hover zoom, guide §05) through the anchor */
.frame > a{ position:absolute; inset:0; display:block; }
.frame > a > img{
  width:100%; height:100%; object-fit:cover;
  transform:scale(1); transition:transform 400ms var(--ease);
}
.card:hover .frame > a > img,
.card:focus-within .frame > a > img{ transform:scale(1.04); }
.card__name a{ text-decoration:none; }

/* quick-add size selector — replaces the quick-add bar in place */
.card__sizes{
  position:absolute; left:0; right:0; bottom:0; z-index:3;
  display:none; flex-wrap:wrap; gap:1px;
  background:var(--bg); border-top:1px solid var(--hairline);
  padding:8px;
}
.card.is-sizing .card__sizes{ display:flex; }
.card.is-sizing .card__quick{ transform:translateY(100%); }
.card__sizes .szbtn{ flex:1 0 56px; }
.szbtn{
  min-width:48px; min-height:44px; padding:0 8px; cursor:pointer;
  background:transparent; color:var(--ink);
  border:1px solid var(--hairline);
  font-size:12px; font-weight:600; letter-spacing:.08em;
  transition:background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease);
}
.szbtn:hover:not(:disabled),.szbtn[aria-pressed="true"]{ background:var(--ink); color:var(--bg); border-color:var(--ink); }
.szbtn:disabled{ color:var(--muted); cursor:not-allowed; text-decoration:line-through; }

/* premium (2Be Atelier) dark card treatment — guide §01: dark = premium */
.card--premium .card__body{
  background:var(--dark); color:var(--ink-on-dark);
  padding:16px; margin-top:0;
}
.card--premium .card__price{ color:var(--ink-on-dark); }
.card--premium .card__quick,
.card--premium .card__sizes{ background:var(--dark); color:var(--ink-on-dark); border-color:rgba(245,245,245,.16); }
.card--premium .card__sizes .szbtn{ color:var(--ink-on-dark); border-color:rgba(245,245,245,.24); }
.card--premium .card__sizes .szbtn:hover:not(:disabled),
.card--premium .card__sizes .szbtn[aria-pressed="true"]{ background:var(--ink-on-dark); color:var(--dark); border-color:var(--ink-on-dark); }
/* Brand and line occupy the same slot — one eyebrow per card, never both
   (see cardHTML). Sharing the rule keeps them from drifting apart. */
.card__line,
.card__brand{ display:block; margin-bottom:8px; color:var(--muted); }
.card--premium .card__line{ color:#C8B58E; } /* quiet atelier marker on dark [DUMMY: premium tint] */
.card__soldout{
  position:absolute; top:12px; left:12px; z-index:2;
  padding:8px 12px; background:var(--dark); color:var(--ink-on-dark);
  font-size:12px; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
}

/* no-results edge state */
.shopempty{
  grid-column:1/-1; text-align:center; padding-block:96px;
  border:1px solid var(--hairline);
}
.shopempty p{ margin-bottom:24px; color:var(--muted); }

/* ============ PDP ============ */
.pdp{ display:grid; grid-template-columns:1fr; gap:0; }
@media (min-width:1024px){
  .pdp{ grid-template-columns:7fr 5fr; gap:48px; align-items:start; }
}

/* gallery: mobile = horizontal snap rail capped in height so the purchase
   panel is reachable without scrolling on small screens (success check 7);
   desktop = stacked editorial column */
.pdp__gallery{
  display:grid; grid-auto-flow:column; grid-auto-columns:82%;
  gap:8px; overflow-x:auto; scroll-snap-type:x mandatory;
  overscroll-behavior-x:contain;
}
.pdp__gallery .frame{ scroll-snap-align:start; aspect-ratio:auto; height:clamp(140px,24svh,360px); }
@media (min-width:1024px){
  .pdp__gallery{ grid-auto-flow:row; grid-auto-columns:auto; gap:24px; overflow:visible; }
  .pdp__gallery .frame{ aspect-ratio:4/5; height:auto; }
}

.pdp__panel{ display:flex; flex-direction:column; align-items:stretch; padding-block:8px 64px; }
@media (min-width:1024px){
  .pdp__panel{ position:sticky; top:calc(var(--nav-h) + 24px); padding-block:8px 96px; }
}
/* small screens: the purchase moment (price/size/add-to-cart) must fit the
   first viewport — the stock note and accordion drop below the CTAs.
   Phase 5: .sizewa joins them — it only renders when a size is out of
   stock, and on the one demo product where that's true (suit-01, size
   46R) its extra line pushed Add-to-Cart ~8px past 640px (tests/e2e2.mjs
   check 7). A recovery link for an unavailable size is explicitly NOT
   part of the critical path a working size needs to complete. */
@media (max-width:1023px){
  .pdp__stock{ order:2; margin-block:8px 0; }
  .sizewa{ order:2; }
  .pdp__acc{ order:3; }
}
.pdp__name{ font-size:20px; font-weight:500; margin-block:8px 4px; }
.pdp__meta{ color:var(--muted); }
.pdp__pricerow{
  display:flex; align-items:baseline; gap:16px; flex-wrap:wrap;
  margin-block:8px;
}
.pdp__price{ font-size:20px; font-weight:600; }
/* :has() support is wide enough on the browsers this store targets in
   2026; if it's ever unsupported the price simply stays default-colored —
   never broken, just less emphasized. */
.pdp__pricerow:has([data-price-was]:not([hidden])) .pdp__price{ color:var(--accent); }
.pdp__price-was{ font-size:16px; color:var(--muted); text-decoration:line-through; }
.pdp__saleends{ margin-top:-4px; margin-bottom:8px; }
.pdp__usd{ color:var(--muted); font-size:14px; }
.ccy{
  background:transparent; border:1px solid var(--hairline); cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center;
  min-height:44px; min-width:44px; padding:0 12px;
  font-size:12px; font-weight:600; letter-spacing:.12em;
}
.ccy[aria-pressed="true"]{ background:var(--ink); color:var(--bg); border-color:var(--ink); }

.pdp__sizes{ margin-block:8px 4px; display:flex; flex-wrap:wrap; gap:8px; }
.pdp__sizehead{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  margin-top:8px;
}
.pdp__guide{
  background:transparent; border:0; cursor:pointer; color:var(--muted);
  display:inline-flex; align-items:center; min-height:44px;
  font-size:12px; letter-spacing:.08em; text-decoration:underline; text-underline-offset:4px;
  padding:0;
}
.pdp__stock{ min-height:1.4em; margin-block:8px; font-size:14px; color:var(--muted); }
/* STANDALONE TEXT CTAs. A link that is the whole of its own line is a
   control, whatever element wraps it, and takes the 44px floor: the
   breadcrumb back to the shop, and the out-of-stock size recovery link.
   A link sitting INSIDE a sentence is the genuine exception and is left
   alone — see the tap-target note in site.css. */
.pdp__back{ display:inline-flex; align-items:center; min-height:44px; }
.pdp__stock.is-out{ color:var(--accent); }

.pdp__cta{ display:grid; gap:8px; margin-block:8px 16px; }
.pdp__cta .btn{ width:100%; }
.btn--wa{ border:1px solid var(--ink); gap:8px; }
.btn--wa:hover{ background:var(--ink); color:var(--bg); }
.btn:disabled{ opacity:.4; cursor:not-allowed; filter:none; }

/* ============ PDP STICKY PURCHASE BAR (ship-list W2) ============
   `.pdp__panel` is position:sticky ONLY inside the min-width:1024px block
   above, so on a phone Add to Cart scrolled away the moment the visitor
   opened the accordion or read the description — on the one page whose
   entire job is that button.

   THIS ALSO REOPENS MOBILE_AUDIT M1, which was recorded as "accepted, not
   fixed". That decision was right on its own terms: getting the size picker
   and Add to Cart above the consent bar on a 360x640 first visit would have
   meant cutting the gallery to ~120px, and the photography IS the product on
   a clothing PDP. But it assumed the CTA had to stay in the document flow.
   A sticky bar needs none of the gallery.

   WHAT THIS DOES NOT DO: it stands down while the consent question is open
   (below), so it does not change M1's FIRST-VISIT screen. Stacking a ~72px
   bar on top of a consent bar that already eats 263px of a 640px screen
   would leave the gallery almost nothing and make the first screen worse,
   not better. What it fixes is every screen after the answer — which is
   where a visitor spends all of their time, and all of a returning
   visitor's. */
.buybar{
  display:none;                      /* the [hidden] attribute is the switch */
  position:fixed; left:0; right:0; bottom:0; z-index:var(--z-buybar);
  background:var(--bg,var(--light)); border-top:1px solid var(--hairline);
}
/* Shown only when JS says so AND only below the two-column breakpoint.
   Written as :not([hidden]) rather than a state class so that the [hidden]
   attribute cannot be silently defeated by this rule's own `display` — the
   exact defect class BUILD_LOG records for .acct and .drawer. */
@media (max-width:1023px){
  .buybar:not([hidden]){ display:block; }
}
.buybar__inner{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding-block:12px;
}
.buybar__info{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.buybar__price{ font-size:16px; font-weight:600; white-space:nowrap; }
.buybar__size{ overflow-wrap:anywhere; }
/* 44px floor is WCAG 2.5.8; .btn is already 48px, this only stops the
   flex row from squeezing it below that on a 320px screen. */
.buybar__btn{ flex:0 0 auto; min-height:44px; padding-inline:24px; }
/* The consent question owns the screen while it is open — same stand-down
   as the style-assistant launcher (assistant.css), keyed to the same class
   consent.js sets alongside --consent-h. */
.consent-open .buybar{ display:none; }

/* Reserve the bar's height at the END of the panel, so the last thing on
   the page — the accordion — is not sitting underneath it. Scoped to the
   panel rather than pushed onto document.body: consent.js already owns
   body's padding-bottom imperatively, and a second writer would clobber it. */
@media (max-width:1023px){
  .pdp__panel{ padding-bottom:calc(64px + var(--buybar-h,0px)); }
}

.pdp__acc{ border-top:1px solid var(--hairline); margin-top:16px; }
.pdp__acc details{ border-bottom:1px solid var(--hairline); }
.pdp__acc summary{
  display:flex; align-items:center; justify-content:space-between;
  padding-block:16px; cursor:pointer; list-style:none;
  font-size:12px; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
}
.pdp__acc summary::-webkit-details-marker{ display:none; }
.pdp__acc summary::after{ content:"+"; font-weight:400; }
.pdp__acc details[open] summary::after{ content:"–"; }
.pdp__acc .acc__body{ padding-bottom:24px; font-size:14px; }
.shiprow{
  display:flex; justify-content:space-between; gap:16px; padding-block:8px;
  border-bottom:1px dashed var(--hairline);
}
.shiprow:last-child{ border-bottom:0; }
.shiprow s{ color:var(--muted); margin-right:8px; }
.promo-note{ margin-top:8px; color:var(--muted); font-size:12px; }

/* ============ size-guide modal (category-specific) ============ */
dialog.modal{
  border:1px solid var(--hairline); border-radius:0; padding:0;
  width:min(560px, calc(100vw - 32px));
  background:var(--light); color:var(--ink-on-light);
}
dialog.modal::backdrop{ background:rgba(13,13,13,.6); }
.modal__head{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding:24px; border-bottom:1px solid var(--hairline);
}
.modal__close{ background:transparent; border:0; cursor:pointer; font-size:20px; line-height:1; padding:8px; }
.modal__body{ padding:24px; overflow:auto; max-height:70svh; }
.sizetable{ width:100%; border-collapse:collapse; font-size:14px; }
.sizetable th,.sizetable td{
  border:1px solid var(--hairline); padding:8px 12px; text-align:left;
}
.sizetable th{ font-size:12px; letter-spacing:.12em; text-transform:uppercase; }
.modal__note{ margin-top:16px; font-size:12px; color:var(--muted); }

/* ============ cart drawer ============ */
.drawer-scrim{
  position:fixed; inset:0; z-index:var(--z-drawer-scrim);
  background:rgba(13,13,13,.6); opacity:0; pointer-events:none;
  transition:opacity var(--t-fast) var(--ease);
}
.drawer[hidden]{ display:none; } /* author display:flex would defeat [hidden] otherwise */
.drawer{
  position:fixed; top:0; right:0; bottom:0; z-index:var(--z-drawer);
  width:min(440px, 100vw);
  display:flex; flex-direction:column;
  background:var(--light); color:var(--ink-on-light);
  border-left:1px solid var(--hairline);
  transform:translateX(100%);
  transition:transform var(--t-slow) var(--ease);
}
body.drawer-open .drawer{ transform:translateX(0); }
body.drawer-open .drawer-scrim{ opacity:1; pointer-events:auto; }
body.drawer-open{ overflow:hidden; }
.drawer__head{
  display:flex; align-items:center; justify-content:space-between;
  padding:24px; border-bottom:1px solid var(--hairline);
}
.drawer__lines{ flex:1; overflow-y:auto; padding:8px 24px; }
.dline{
  display:grid; grid-template-columns:72px 1fr auto; gap:16px;
  padding-block:16px; border-bottom:1px solid var(--hairline);
}
.dline__media{ width:72px; }
.dline__name{ font-size:14px; font-weight:500; }
.dline__meta{ font-size:12px; color:var(--muted); margin-top:4px; }
.dline__qty{ display:inline-flex; align-items:center; gap:0; margin-top:12px; border:1px solid var(--hairline); }
.dline__qty button{
  width:32px; height:32px; background:transparent; border:0; cursor:pointer; font-size:16px;
}
.dline__qty span{ min-width:32px; text-align:center; font-size:14px; }
.dline__price{ font-size:14px; text-align:right; }
.dline__remove{
  background:transparent; border:0; cursor:pointer; color:var(--muted);
  font-size:12px; text-decoration:underline; text-underline-offset:3px; margin-top:12px;
}
.drawer__empty{ padding:48px 0; text-align:center; color:var(--muted); }
.drawer__empty .btn{ margin-top:24px; }

.drawer__aside{ border-top:1px solid var(--hairline); padding:16px 24px; }
.drawer__aside + .drawer__aside{ padding-top:0; border-top:0; }
.memberform{ display:flex; }
.memberform input{
  flex:1; min-width:0; height:40px; padding:0 12px;
  background:transparent; border:1px solid var(--hairline); border-right:0;
  font-size:14px;
}
.memberform .btn{ height:40px; padding:0 16px; }
.member__status{ min-height:1.4em; margin-top:8px; font-size:12px; color:var(--muted); }
.zonefield{ display:grid; gap:8px; }
.zonefield select{
  height:40px; padding:0 12px; background:transparent; color:inherit;
  border:1px solid var(--hairline); border-radius:0; font:inherit; font-size:14px;
}
.drawer__sums{ border-top:1px solid var(--hairline); padding:16px 24px; display:grid; gap:8px; }
.sumrow{ display:flex; justify-content:space-between; gap:16px; font-size:14px; }
.sumrow s{ color:var(--muted); margin-right:8px; }
.sumrow--total{ font-size:16px; font-weight:600; padding-top:8px; border-top:1px solid var(--hairline); }
.drawer__foot{ padding:16px 24px 24px; }
.drawer__foot .btn{ width:100%; }
.drawer__note{ margin-top:8px; font-size:12px; color:var(--muted); text-align:center; }
.nav__bag .bagcount{ margin-left:6px; }

/* ============ checkout + order status (mock stand-ins) ============ */
.checkout{ max-width:720px; margin-inline:auto; padding-block:48px 120px; }
.mocknote{
  border:1px solid var(--hairline); border-left:2px solid var(--accent);
  padding:16px; margin-block:24px; font-size:14px; color:var(--muted);
}
.paychoice{ display:grid; gap:8px; margin-top:24px; }
.paychoice .btn{ width:100%; }
/* Review round 5 (P4): an author `display` defeats the [hidden] attribute
   — the same trap pages.css's .acct/.acct-me already document. Without
   this, JS setting .hidden=true on the mock or live payment panel does
   nothing visually; found live, screenshot-confirmed, while wiring the
   two panels' mode-based show/hide. */
.paychoice[hidden]{ display:none; }
.ordsum{ border:1px solid var(--hairline); padding:24px; margin-top:24px; }
.ordsum .sumrow{ padding-block:4px; }

.orderstatus{ max-width:720px; margin-inline:auto; padding-block:48px 120px; }
.status{
  border:1px solid var(--hairline); padding:32px 24px; margin-block:24px;
}
.status__badge{
  display:inline-block; padding:8px 16px; margin-bottom:16px;
  font-size:12px; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
  border:1px solid var(--hairline);
}
.status--pending .status__badge{ color:var(--ink); }
.status--paid .status__badge{ background:var(--ink); color:var(--bg); border-color:var(--ink); }
.status--failed .status__badge{ color:var(--accent); border-color:var(--accent); }
@keyframes statuspulse{ 0%,100%{ opacity:1; } 50%{ opacity:.5; } }
.status--pending .status__badge{ animation:statuspulse 2s var(--ease) infinite; }
@media (prefers-reduced-motion:reduce){ .status--pending .status__badge{ animation:none; } }

/* ============ BANK TRANSFER DETAILS ============
   Shown on /shop/order/ while a transfer order is awaiting payment. This is
   an instruction panel, not decoration: the accent left border matches
   .mocknote's convention for "read this", and the account numbers are the
   one thing on the page a customer has to copy accurately.

   An author `display` here would defeat the [hidden] attribute that shop.js
   toggles — the trap .paychoice above already documents. Hence
   `.bankpay:not([hidden])` rather than a bare `.bankpay{ display:... }`. */
.bankpay{
  border:1px solid var(--hairline); border-left:2px solid var(--accent);
  padding:24px; margin-block:24px;
}
.bankpay:not([hidden]){ display:block; }
.bankpay h2{ margin:0 0 8px; }
.bankpay p{ margin:0 0 16px; }
.bankpay__meta{
  display:grid; grid-template-columns:auto 1fr; gap:4px 16px;
  margin:0 0 20px; align-items:baseline;
}
.bankpay__meta dt{ color:var(--muted); }
.bankpay__meta dd{ margin:0; }

.bankpay__accounts{ display:grid; gap:12px; }
.bankpay__row{
  display:grid; grid-template-columns:auto 1fr auto; gap:4px 16px;
  align-items:baseline;
  padding:12px 16px; border:1px solid var(--hairline);
}
.bankpay__ccy{ color:var(--muted); }
/* Tabular figures and letter-spacing: an account number is read a digit at
   a time and transcribed into a banking app. Proportional digits in the
   body face make 3010012456 harder to read back than it needs to be. */
.bankpay__num{
  font-variant-numeric:tabular-nums; letter-spacing:.08em;
  font-size:18px; font-weight:600;
}
.bankpay__amt{ text-align:right; }
.bankpay__amt strong{ font-variant-numeric:tabular-nums; }

.bankpay__ref{ margin:20px 0 8px !important; }
.bankpay__ref [data-bank-ref]{
  font-variant-numeric:tabular-nums; letter-spacing:.06em; font-weight:600;
}

/* Under ~480px the three-column account row stops fitting: the amount drops
   under the number rather than squeezing either. */
@media (max-width:479px){
  .bankpay__row{ grid-template-columns:auto 1fr; }
  .bankpay__amt{ grid-column:1 / -1; text-align:left; }
}

/* ============ COMPLETE THE LOOK (Phase 4) ============
   Cross-sell rail on the PDP. Deliberately quiet: no accent fill, no badge,
   no urgency — the primary action on this page is still Add to Bag, and
   guide §02 keeps oxblood for that (study.md §4.1, the squint test). This
   rail is a helpful aside, and should look like one. */
.look{ padding-block:64px 0; }
@media (min-width:1024px){ .look{ padding-block:96px 0; } }
.look .shead{ margin-bottom:24px; }
.look__rail{
  display:grid; grid-auto-flow:column; grid-auto-columns:minmax(140px,180px);
  gap:16px; overflow-x:auto; overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory; padding-bottom:8px;
}
.look__rail > *{ scroll-snap-align:start; }
.look__item{ min-width:0; color:var(--ink); }
.look__item .frame{ margin-bottom:8px; }
.look__cat{ display:block; color:var(--muted); margin-bottom:4px; }
.look__name{
  display:block; font-size:14px; line-height:1.3;
  overflow-wrap:anywhere;            /* [PLACEHOLDER-COPY:...] is unbreakable */
}
.look__price{ display:block; font-size:14px; font-weight:600; margin-top:4px; }
.look__item:hover .frame > img,
.look__item:focus-visible .frame > img{ transform:scale(1.04); }

/* ============ LOW-STOCK BADGE (Phase 5) ============
   Real count, never synthetic urgency (study.md §4.4 — fake scarcity is a
   trust grenade in this market). Shares the accent with the primary CTA
   (guide §02/§09.1): urgency earns the same scarce color the Add-to-cart
   button uses, it doesn't get a second, competing red. Motion lives in
   motion.css, gated behind html.motion — this file only lays it out. */
.lowstock{
  display:inline-flex; align-items:center; gap:3px; flex:none;
  height:18px; padding:0 6px; margin-left:6px;
  background:var(--accent); color:#fff;
  font-size:10px; font-weight:700; line-height:1;
  vertical-align:middle;
}
.lowstock__mark{ font-weight:800; }
/* On a size chip the badge would otherwise stretch the button's own
   background; pin it to the corner instead of sitting inline. */
.szbtn{ position:relative; }
.szbtn .lowstock{
  position:absolute; top:-8px; right:-6px; margin-left:0;
  height:16px; padding:0 4px; font-size:9px;
}
.pdp__stock .lowstock{ margin-left:8px; }

/* ---------- PDP: out-of-stock size -> WhatsApp recovery (Phase 5) ---------- */
.sizewa{
  /* 8px put this link's box 3px under the size grid on a phone — close
     enough that a thumb going for the WhatsApp fallback could take a size
     instead, or the reverse, on the one row where picking wrong is a wrong
     order rather than a wrong page. The size buttons are the primary
     control; this is the recovery next to it, and the two want daylight. */
  display:block; margin-top:16px; font-size:12px; color:var(--muted);
}
.sizewa a{
  display:inline-flex; align-items:center; min-height:44px;
  text-decoration:underline; color:var(--ink);
}

/* ============ SALE (Phase 5) ============
   Reuses the PLP card component; adds the struck-original/now price and a
   loss-framed end date (study.md §4.4). Same accent-scarcity rule as the
   low-stock badge — one small tag, not a re-themed card. */
.card--sale .card__saletag{
  position:absolute; top:12px; left:12px; z-index:2;
  height:24px; padding:0 10px; display:inline-flex; align-items:center;
  background:var(--accent); color:#fff;
  /* Was 10px. Under ~12px is not readable on a phone held at arm's length,
     and this tag is doing commercial work — it is the only thing marking a
     card as reduced. Letter-spacing trimmed a touch so the wider glyphs
     still fit the same 24px pill. */
  font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
}
/* .card__body is a flex ROW and shop.css gives every child min-width:0, so
   this column — whose price and "Sale ends" line are both white-space:nowrap
   — was shrinking to 41px on a 159px card at 390px and painting its text
   straight over the product title. Two fixes, because one is not enough:
   it must never shrink below its content, AND on a card this narrow there
   is no room for two columns at all, so the body stacks. Mobile is the
   majority of this audience (study.md §4.7); an unreadable price there is
   not a small thing. */
.card__pricewrap{ display:flex; flex-direction:column; align-items:flex-end; gap:2px; flex:none; }
@media (max-width:640px){
  .card__body{ flex-direction:column; align-items:flex-start; gap:4px; }
  .card__pricewrap{ align-items:flex-start; }
}
.card__price--sale s{ color:var(--muted); font-weight:400; }
.card__price--sale{ color:var(--accent); font-weight:600; }
.card__saleends{ display:block; color:var(--muted); font-size:12px; white-space:nowrap; }

/* Light surface, not dark: oxblood-on-#0D0D0D fails for body text (the
   guide never actually reads accent-on-black as text — the nav's accent
   underline is a 2px mark, not a copy color). This keeps the same token,
   same accent-as-urgency logic, at a contrast that actually reads. */
.saleband{ padding-block:24px; text-align:center; border-bottom:1px solid var(--hairline); }
.saleband strong{ color:var(--accent); }

/* ============ PRODUCT STORY (Phase 6) ============
   A definition list, not prose: fabric/construction/care are discrete
   facts a buyer scans, and study.md §6 puts them on the PDP checklist as
   trust signal. Two columns on anything wider than a phone so the terms
   read as a spec sheet rather than a paragraph. */
.story{ display:grid; grid-template-columns:1fr; gap:2px 16px; margin:0; }
@media (min-width:480px){ .story{ grid-template-columns:auto 1fr; gap:12px 24px; } }
.story__term{ color:var(--muted); padding-top:2px; }
.story__def{ margin:0 0 12px; overflow-wrap:anywhere; }
@media (min-width:480px){ .story__def{ margin-bottom:0; } }

/* A quiet explanatory row under the member discount when part of the bag
   was excluded from it (sale items don't stack) — reads as a footnote to
   the line above, never as another charge. */
.sumrow--note{ align-items:flex-start; font-size:12px; line-height:1.4; margin-top:-4px; }

/* Recommended-for-you rail (Phase 7). Reuses .look__rail/.look__item from
   the PDP cross-sell — same component, different data source; a second set
   of near-identical card styles would just drift. */
.recs{ padding-block:32px 8px; }
.recs .shead{ margin-bottom:24px; }

/* Defensive, for the same reason .drawer[hidden] above exists: neither .look
   nor .recs currently sets `display`, so [hidden] works — but adding one
   later would silently un-hide them, and that failure is invisible to a
   property-only test (Phase 7 hit exactly that on .acct in pages.css). */
.look[hidden], .recs[hidden]{ display:none; }
