/* Shop pages — category nav, filter toolbar, and the three additions to the product card.
 *
 * Deliberately small. The generated cards reuse .piece / .frame / .meta / .nm / .pr /
 * .tags from site.css unchanged, so the grid looks exactly like the hand-built one it
 * replaced. Only what genuinely did not exist before is styled here.
 *
 * Contrast was checked against --void (#4A4433), the same bar site.css holds itself to:
 *   --bone-dim  #D6CFB8  6.32:1   the Kč price, the favourite count, filter labels
 *   --magenta   #E8A93C  4.77:1   the euro price, the active category
 * Both clear AA for normal text. --saffron/--olive are lighter still.
 */

/* ---------- category nav ---------- */

/* Horizontal scroll rather than wrap on a phone: with seven categories a wrapping row
   becomes three lines and pushes the actual products below the fold. */
/* Wraps. It used to scroll sideways with a fade mask over the right edge, and the mask was
   a patch over the real problem: this is the primary navigation of the shop, and half of it
   sat behind a gesture. Horizontal scroll gives no overview of how many shelves exist, is
   close to undiscoverable with a mouse, and the counts — "Fashion 57" is where someone
   learns where the depth is — were part of what got cut off.
   The chips are short. Eight of them fit one row on a desktop and two or three on a phone,
   and two rows on a phone is not a cost. Hidden shelves are. */
.cat-nav{
  display:flex;flex-wrap:wrap;gap:8px;
  margin-bottom:20px;
}
.cat-nav a{
  flex:0 0 auto;display:flex;align-items:center;gap:6px;
  min-height:44px;padding:0 14px;
  border:1px solid rgba(242,239,230,.22);
  font-size:var(--fs-xs);letter-spacing:.05em;text-transform:uppercase;
  color:var(--bone-dim);white-space:nowrap;
  transition:border-color .15s,color .15s,background .15s;
}
/* opacity .6 measured 3.33:1 against the ground and failed AA — the count is small text,
   so it needs 4.5:1. .8 de-emphasises it just as well and measures 4.67:1. */
.cat-nav a b{font-weight:400;opacity:.8;font-size:var(--fs-2xs)}

/* Wrapping costs vertical space, and on a 375px phone the strip was landing two chips to a
   row and eight rows deep, which pushed the first product to y=1055 — worse than the
   sideways scroll it replaced, on the one measurement that matters most.
   The fix is horizontal, not vertical: the chips were spending 28px of every row on inset
   padding they do not need at this width. Tightening it fits three to a row. The 44px
   minimum height is untouched — that is the touch target, and it is not negotiable. */
@media (max-width:520px){
  .cat-nav{gap:6px}
  .cat-nav a{padding:0 9px;letter-spacing:.02em;font-size:var(--fs-2xs)}
  .cat-nav a b{font-size:11px}
}
.cat-nav a:hover{color:var(--bone);border-color:var(--bone)}
.cat-nav a[aria-current="page"]{
  background:var(--magenta);border-color:var(--magenta);color:var(--void);
}
.cat-nav a[aria-current="page"] b{opacity:.75}

/* ---------- filter toolbar ---------- */

/* Ships with the `hidden` attribute and is revealed by shop.js, so a visitor without
   JavaScript is never shown a control that cannot do anything. */
.shop-bar{display:flex;flex-direction:column;gap:10px;margin-bottom:24px}
.shop-bar[hidden]{display:none}
.sb-top{display:flex;flex-direction:column;gap:10px}
/* Two columns, but only where two columns can hold a label.
 *
 * The controls are 16px now (see the note on the input rule below), and 16px of monospace
 * is wide: "Any condition" is thirteen characters at 9.6px each, so it needs 125px of text
 * width, and a select spends another 44px on its inset padding and arrow. Two columns only
 * clear that from about 386px up. Below it the label was being cut off mid-word by the
 * arrow — "Any conditio▾" — which is worse than the row of extra height single-column
 * costs. */
.sb-controls{display:grid;grid-template-columns:1fr 1fr;gap:8px}
@media (max-width:399px){.sb-controls{grid-template-columns:1fr}}
/* Availability takes the full width of the grid. Its default option names its own filter —
   "Availability All" — which measures ~165px of text and needs ~210px with the inset padding
   and the arrow. A half-width track at these viewports is about 180px, so in a two-column
   grid the word "All" was being cut off by the arrow. A row of its own, rather than a label
   that lies about what is selected. */
.sb-avail{grid-column:1/-1}

/* ---------- refine disclosure ----------
 *
 * See the long note in shop.js. The five refinement selects live inside a <details> so a
 * phone shows a product on its first screen; the summary is the control, and it disappears
 * from 721px up where the controls are simply always visible. */
.sb-refine{border:0}
.sb-refine > summary{
  display:flex;align-items:center;gap:8px;
  min-height:44px;padding:0 14px;
  border:1px solid rgba(242,239,230,.22);
  font-size:var(--fs-xs);letter-spacing:.05em;text-transform:uppercase;
  color:var(--bone-dim);cursor:pointer;list-style:none;
  transition:border-color .15s,color .15s;
}
.sb-refine > summary::-webkit-details-marker{display:none}
.sb-refine > summary:hover{color:var(--bone);border-color:var(--bone)}
.sb-refine > summary:focus-visible{outline:2px solid var(--magenta);outline-offset:2px}
/* A chevron drawn in CSS rather than a glyph, so it can rotate on open. */
.sb-refine > summary::after{
  content:"";margin-left:auto;width:8px;height:8px;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:translateY(-2px) rotate(45deg);transition:transform .2s;
}
.sb-refine[open] > summary::after{transform:translateY(2px) rotate(-135deg)}
.sb-refine[open] > summary{margin-bottom:8px}
/* How many refinements are set, so nothing is silently hidden behind a closed summary. */
.sb-refine-n{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:20px;height:20px;padding:0 5px;
  background:var(--magenta);color:var(--void);
  font-size:var(--fs-2xs);font-weight:600;line-height:1;
}
.sb-refine-n[hidden]{display:none}

@media (min-width:721px){
  /* No disclosure on a wide screen — the controls are the toolbar. */
  .sb-refine > summary{display:none}
}
.sb-status{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:space-between}

.shop-bar input,.shop-bar select{
  width:100%;min-height:44px;padding:0 12px;
  background:var(--void-2);color:var(--bone);
  border:1px solid rgba(242,239,230,.22);border-radius:0;
  /* 1rem is load-bearing, not a type-scale preference. Safari on iOS zooms the whole
     viewport whenever a focused field is under 16px, so at the old 12.5px every tap on
     the search box or a filter select threw the visitor into a zoomed page they then had
     to pinch back out of — on the most-used control on the site. */
  font-family:inherit;font-size:var(--fs-m);
  appearance:none;
}
.shop-bar input::placeholder{color:var(--bone-dim);opacity:.7}
.shop-bar input:focus,.shop-bar select:focus{outline:2px solid var(--magenta);outline-offset:-1px}

/* The native arrow disappears with appearance:none, so draw one back. Encoded --bone-dim. */
.shop-bar select{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23D6CFB8'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 12px center;
  padding-right:32px;
}

.sb-count{
  font-size:var(--fs-2xs);letter-spacing:.06em;text-transform:uppercase;
  color:var(--bone-dim);align-self:center;
}

.sb-empty{
  padding:40px 0;text-align:center;
  font-family:var(--sans);font-size:var(--fs-m);color:var(--bone-dim);
}
.sb-empty[hidden]{display:none}
.sb-empty button{
  background:none;border:none;padding:0;
  font-family:inherit;font-size:inherit;
  color:var(--magenta);text-decoration:underline;cursor:pointer;
}

/* One row on a wide screen: search, then every filter, then sort.
 *
 * The three wrappers the markup needs on a phone — .sb-top holding the search, the <details>
 * holding the refinements, and .sb-controls inside it — all become `display:contents` here,
 * so every control is a direct flex child of .shop-bar and they lay out as one line. The
 * wrappers still exist in the DOM, and on a phone they still do their jobs; on a desktop
 * they simply stop being boxes.
 *
 * The summary is already hidden at this width (see above), and shop.js keeps the <details>
 * open whenever the viewport is wide — which matters more than it looks, because a closed
 * <details> hides its children whatever their display value, and there would be no summary
 * left to reopen it with.
 *
 * flex-wrap, not a fixed column count: which controls exist varies by page, since a control
 * with nothing to filter is never rendered. Seven controls fit one row at 1280px and fall to
 * two rows below that, rather than being clipped.
 *
 * 150px basis with room to grow. The longest label any select carries is "Any condition",
 * which needs 125px of text plus padding and arrow — flex-grow covers the difference on any
 * real width, and shrinking below the basis is disabled so nothing gets clipped. */
@media (min-width:721px){
  .shop-bar{flex-direction:row;flex-wrap:wrap;align-items:center;gap:10px}
  .sb-top,.sb-refine,.sb-controls{display:contents}
  /* Chrome wraps a <details>'s children in a ::details-content box that is a block
     container, so display:contents on the <details> itself is not enough — without this the
     five selects stack in a column inside that box instead of joining the row. Browsers
     without the pseudo-element ignore the rule and were already correct. */
  .sb-refine::details-content{display:contents}
  /* Search leads and takes the slack: it is the one control whose content is unbounded. */
  .sb-search{flex:2 1 220px}
  .sb-sel{flex:1 0 150px}
  /* 218px, not 150: see the note on .sb-avail above — this is the one control whose closed
     label carries its own name, and it has to fit. */
  .sb-avail{flex:1 0 218px}
  /* Chips and the result count are a line of their own, under the controls. */
  .sb-status{flex:1 0 100%}
}


/* ---------- availability segmented control ---------- */

/* Availability is a MODE, not another attribute — you are browsing what you can buy, or
   looking at what has already gone. A segmented control says that at a glance; a fifth
   dropdown would bury it among the filters. */
.sb-seg{display:flex;flex:0 0 auto;border:1px solid rgba(242,239,230,.22)}
.sb-seg button{
  flex:1 1 auto;min-height:44px;padding:0 14px;
  background:none;border:none;border-right:1px solid rgba(242,239,230,.22);
  font-family:inherit;font-size:var(--fs-2xs);letter-spacing:.06em;text-transform:uppercase;
  color:var(--bone-dim);cursor:pointer;white-space:nowrap;
  transition:background .15s,color .15s;
}
.sb-seg button:last-child{border-right:none}
.sb-seg button:hover{color:var(--bone)}
.sb-seg button[aria-pressed="true"]{background:var(--magenta);color:var(--void);font-weight:600}

/* ---------- active filter chips ---------- */

/* The single biggest usability gap in a filtered grid is not knowing WHY it is short.
   Each chip names the filter and removes it on click; "Clear all" appears once more than
   one is active. */
.sb-chips{display:flex;flex-wrap:wrap;gap:6px}
.sb-chips[hidden]{display:none}
.chip{
  display:inline-flex;align-items:center;gap:6px;
  min-height:32px;padding:0 10px;
  background:var(--void-2);border:1px solid var(--magenta);
  font-family:inherit;font-size:var(--fs-2xs);color:var(--bone);
  cursor:pointer;transition:background .15s,color .15s;
}
.chip i{font-style:normal;color:var(--bone-dim);text-transform:uppercase;font-size:var(--fs-2xs);letter-spacing:.05em}
.chip span{color:var(--magenta);font-size:var(--fs-m);line-height:1}
.chip:hover{background:var(--magenta);color:var(--void)}
.chip:hover i,.chip:hover span{color:var(--void)}
.chip.clear-all{border-color:rgba(242,239,230,.35);color:var(--bone-dim)}
.chip.clear-all:hover{background:var(--bone);color:var(--void)}

/* ---------- progressive reveal ---------- */

/* A real button, not a bare scroll sentinel. Pure infinite scroll cannot be reached by
   keyboard and keeps the footer permanently out of reach; shop.js presses this for you
   when you scroll near it, so both interaction models work. */
.sb-more{
  display:block;width:100%;margin:28px 0 0;min-height:48px;
  background:none;border:1px solid var(--saffron);
  font-family:inherit;font-size:var(--fs-xs);letter-spacing:.06em;text-transform:uppercase;
  color:var(--saffron);cursor:pointer;transition:background .15s,color .15s;
}
.sb-more:hover{background:var(--saffron);color:var(--void)}
.sb-more[hidden]{display:none}

/* ---------- card: carousel ---------- */

/* Load-bearing. site.css sets `.piece{display:flex}`, and a class selector beats the user
   agent's `[hidden]{display:none}` — so without this rule shop.js hiding a card would have
   no visible effect and every filter would silently do nothing. */
.piece[hidden]{display:none}

/* A scroll-snap track, which is what makes touch swipe and trackpad scroll work with no
   JavaScript involved in the scrolling at all — the buttons only exist for mouse and
   keyboard. Photos 2 and 3 are appended here by shop.js on first interaction. */
.frame .shots{
  position:absolute;inset:0;z-index:1;
  display:flex;overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;
  scrollbar-width:none;-ms-overflow-style:none;
  overscroll-behavior-x:contain;
}
.frame .shots::-webkit-scrollbar{display:none}
.frame .shot{position:relative;flex:0 0 100%;scroll-snap-align:center;height:100%}
.frame .shots .ph{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}

.shot-nav{
  position:absolute;top:50%;transform:translateY(-50%);z-index:4;
  width:32px;height:44px;display:flex;align-items:center;justify-content:center;
  background:rgba(10,9,8,.55);border:1px solid rgba(242,239,230,.28);
  color:var(--bone);font-size:20px;line-height:1;cursor:pointer;
  opacity:0;transition:opacity .15s,background .15s;
}
.shot-nav.prev{left:0;border-left:none}
.shot-nav.next{right:0;border-right:none}
.shot-nav:hover{background:var(--magenta);color:var(--void)}
.piece:hover .shot-nav,.piece:focus-within .shot-nav{opacity:1}
/* No hover on touch, so the arrows would never appear — but the track swipes there, and
   the counter below still says how many photos there are. */
@media (hover:none){.shot-nav{opacity:.85}}
.shot-nav:focus-visible{opacity:1}

/* Bottom-right, beside the external-link mark. Every overlay now owns one corner and
   cannot collide with another:
       top-left      SOLD / Reserved flag
       top-right     favourites badge
       bottom-left   brand or category caption
       bottom-right  external-link mark, with the photo counter to its left
   The blurb slides over the bottom edge on hover and deliberately covers both. */
.shot-count{
  position:absolute;bottom:9px;right:40px;z-index:3;
  background:rgba(10,9,8,.62);color:var(--bone);
  font-size:var(--fs-2xs);letter-spacing:.05em;padding:3px 7px;
}

/* site.css puts this mark top-right, which is now the favourites badge's corner. */
.piece .ext{top:auto;bottom:9px;right:8px}

/* A long brand name would otherwise run under the counter in the opposite corner. */
.frame > .cap:not(.flag){max-width:58%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---------- card: hover blurb ---------- */

/* The listing's own words, over the bottom of the photo on hover or keyboard focus.
   Strictly an extra: condition, colour, size and price all live in the card's text below
   the image, so a touch visitor (who has no hover) and a screen reader lose nothing. */
.frame .blurb{
  position:absolute;left:0;right:0;bottom:0;z-index:5;
  margin:0;padding:14px 12px 12px;
  background:linear-gradient(to top,rgba(10,9,8,.94),rgba(10,9,8,.86) 65%,transparent);
  /* Plex SANS here, not the site's Plex Mono. This is the only text on a shop page that is
     read rather than scanned, and a monospace face gives every letter the same width, so
     word shapes flatten and reading slows. Plex Sans is the same family's proportional
     sibling, so it sits with the rest of the page instead of fighting it.
     12.5px/1.65 rather than 11px/1.5 — small text in a dark overlay needs the extra leading
     more than body copy on a light ground does. */
  font-family:"IBM Plex Sans",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
  color:var(--bone);font-size:var(--fs-s);line-height:var(--lh-read);letter-spacing:.005em;
  opacity:0;transform:translateY(6px);
  transition:opacity .18s ease,transform .18s ease;
  pointer-events:none;
}
.piece:hover .blurb,.piece:focus-within .blurb{opacity:1;transform:none}
/* The caption sits in the same corner, so it steps aside rather than being overprinted. */
.piece:hover .frame > .cap:not(.flag),.piece:focus-within .frame > .cap:not(.flag){opacity:0}
.frame > .cap:not(.flag){transition:opacity .18s ease}
/* ---------- the description on touch (24 Aug 2026) ----------
 *
 * This line used to read `@media (hover:none){.frame .blurb{display:none}}`. On a phone the
 * blurb was not merely unreachable, it was removed: the one piece of prose on the card,
 * absent on the device most people shop from, while desktop got it for free on hover.
 *
 * The touch route is an "i" disclosure in the bottom-left corner of the photograph. Desktop
 * keeps hover exactly as it was — the two live side by side because they suit different
 * inputs: a pointer can reveal on the way past at no cost, a thumb cannot.
 *
 * WHY THE BOTTOM-LEFT CORNER IS FREE. It was the brand caption's (.cap, "Indian"). On touch
 * that caption is hidden and the toggle takes the slot: the brand is already in the card's
 * title underneath, so nothing is lost, whereas the description had nowhere else to go.
 *
 * Above .go on the z axis, like .ask and .shot-nav, or the card's stretched link would
 * swallow the tap.
 */
@media (hover:none){
  /* Present but hidden, rather than display:none — the reveal below animates it in, and a
     display:none element cannot transition. */
  .frame .blurb{display:block;opacity:0;pointer-events:none}

  .frame > .cap:not(.flag){display:none}

  .piece .info{position:absolute;left:8px;bottom:8px;z-index:7}
  .piece .info>summary{
    list-style:none;cursor:pointer;
    display:flex;align-items:center;justify-content:center;
    /* 34px visible, 44px of touch target via the padding-free min sizes below: the WCAG 2.2
       floor with a mark small enough not to sit on the garment. */
    width:34px;height:34px;min-width:34px;
    border-radius:999px;
    border:1px solid rgba(242,239,230,.42);
    background:rgba(10,9,8,.72);
    color:var(--bone);
    font-family:var(--mono);font-size:var(--fs-s);font-weight:600;line-height:1;
    font-style:italic;
  }
  .piece .info>summary::-webkit-details-marker{display:none}
  .piece .info[open]>summary{background:var(--magenta);color:var(--void);border-color:var(--magenta)}
  /* The reveal. Same overlay the pointer gets, same styling, driven by state instead of hover. */
  .piece .info[open] ~ .blurb{opacity:1;transform:translateY(0)}
  /* The open blurb must clear the toggle, or the first line sits under it. */
  .piece .info[open] ~ .blurb{padding-bottom:50px}
}

/* The toggle is touch-only: a pointer already has hover, and two ways to the same sentence
   on one card is one too many. */
@media (hover:hover){.piece .info{display:none}}

/* ---------- card: sold and reserved ---------- */

/* Top-LEFT now: the favourite badge owns the top-right corner. */
.frame .cap.flag{
  top:8px;bottom:auto;left:8px;right:auto;
  font-weight:600;letter-spacing:.1em;
}
/* --chilli, not --paprika. site.css already warns that paprika is a fill colour and not a
   text colour, but the corollary bites here too: --void ON paprika measures only 2.29:1,
   and so does --bone at 3.68:1. Chilli is the darker red in the same family and carries
   --bone at 4.76:1, so the badge stays urgent AND legible. */
.frame .cap.sold-flag{background:var(--chilli);color:var(--bone)}
.frame .cap.res{background:var(--saffron);color:var(--void)}
/* Where the piece comes from. Deliberately the quietest of the three flags: Sold and
   Reserved are about availability and have to interrupt, while the vendor mark is
   provenance — it must be readable at a glance without competing with the photograph.
   --bone on --chicory measures 10.9:1, so it stays legible at 12px over any image. */
.frame .cap.vendor-flag{background:var(--chicory);color:var(--bone);font-weight:500}
/* Sold or out-of-stock cards carry two flags in the same corner; stack rather than overlap. */
.frame .cap.flag ~ .cap.flag{top:34px}
/* A sold card carries a flag AND the counter in the top-left; push the flag clear. */
.piece.sold .shot-count{display:none}

/* Desaturated rather than faded: a sold piece should still read as a real photograph —
   it is evidence that things sell here — but must never look available at a glance. */
.piece.sold .frame .ph{filter:grayscale(.72) contrast(.92)}
.piece.sold .nm{color:var(--bone-dim)}
/* Muted rather than red, at 6.22:1. Paprika text on the ground measures 2.29:1 and fails
   outright — and the red badge on the photograph is already carrying the "gone" signal, so
   shouting it twice in the price slot would be redundant as well as unreadable. */
.piece.sold .pr.sold-pr{
  color:var(--bone-dim);font-weight:600;text-transform:uppercase;
  font-size:var(--fs-2xs);letter-spacing:.06em;
}
.piece.sold .pr .czk{text-decoration:line-through}
/* Not a link, so it must not pretend to be one. */
.piece.sold{cursor:default}
.piece.sold:hover .frame{outline:none}

/* ---------- card: price pair and favourites ---------- */

/* One currency, and big enough to be read at a glance. The koruna figure used to sit under
   it at 10.5px and split the shopper's attention across two numbers for the same thing;
   price is the second-most-scanned element on a product card after the photograph, so it
   gets the weight. site.css already sets .pr nowrap and flex:0 0 auto. */
.piece .meta .pr{
  font-size:var(--fs-xl);font-weight:600;line-height:1.2;
  color:var(--magenta);letter-spacing:-.01em;
  align-self:center;
}

/* Social proof, on the photograph rather than in the small print.
   These are real people who saved the piece on Vinted, and it is the single strongest
   reason a hesitant buyer commits — so it is roughly three times its old size and sits in
   the top-right corner, the second place the eye lands on a card after the image itself. */
.piece .fav-badge{
  position:absolute;top:8px;right:8px;z-index:4;
  display:inline-flex;align-items:center;gap:6px;
  padding:6px 11px;
  /* .82 rather than a lighter wash: this badge floats over photographs whose colour cannot
     be known in advance, so the backdrop has to do the contrast work itself. At this
     opacity the amber sits at roughly 8:1 whatever is behind it; at .66 a pale garment
     could have taken it under AA. */
  background:rgba(10,9,8,.82);
  border:1px solid rgba(232,169,60,.55);
  color:var(--magenta);
  /* 20px — near enough double the 10.5px it was as a line of tag text, and level with the
     price, which is the other number that decides a purchase. */
  font-size:var(--fs-xl);font-weight:600;line-height:1;
  font-variant-numeric:tabular-nums;
  backdrop-filter:blur(3px);
}
.piece .fav-badge svg{flex:0 0 auto;width:18px;height:18px}

/* The rule that used to sit here — Plex Sans for .lede, .sec-lead and .guide-body p —
   has moved to site.css and grown to cover the whole site. The note it carried said the
   other pages kept the all-mono setting "until they are looked at properly"; they have
   been. */

/* ---------- return to filters ---------- */

/* Appears only once the toolbar has scrolled out of view, so it never covers the controls
   it points at. Bottom-anchored rather than a second sticky strip below the header, which
   would eat the small viewport this matters most on.
   Two buttons, because they are two different intents: "narrow this down" and "start
   again". One ambiguous arrow would serve neither well. */
.sb-jump{
  position:fixed;z-index:90;
  left:50%;transform:translateX(-50%);
  bottom:calc(16px + env(safe-area-inset-bottom,0px));
  display:flex;gap:1px;
  box-shadow:0 4px 20px rgba(10,9,8,.45);
}
.sb-jump[hidden]{display:none}
.sb-jump button{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:46px;padding:0 18px;
  background:var(--magenta);color:var(--void);
  border:1px solid var(--magenta);
  font-family:inherit;font-size:var(--fs-xs);font-weight:600;
  letter-spacing:.06em;text-transform:uppercase;
  cursor:pointer;transition:background .15s,color .15s;
}
.sb-jump button:hover{background:var(--bone);border-color:var(--bone)}
.sb-jump .sb-jump-top{padding:0 14px;min-width:46px}

@media (min-width:721px){
  /* Out of the centre on a wide screen, where the middle of the viewport is content the
     visitor is reading rather than dead space. */
  .sb-jump{left:auto;right:24px;transform:none;bottom:24px}
}

/* ---------- the card's two destinations ---------- */

/* A card carries two links now — the Vinted listing and a WhatsApp message about this
 * specific piece — so it can no longer BE a link: an <a> cannot contain another <a>, and
 * browsers recover from that by splitting the markup differently per engine.
 *
 * Instead the card is a <div>, the Vinted link is stretched over all of it, and .ask sits
 * above that on the z axis. Clicking anywhere still opens the listing exactly as before;
 * the only element that behaves differently is the one that is meant to.
 */
.piece{position:relative}

/* Above everything inside .frame (shots 1, grain 2, count 3, nav 4, blurb 5) so the whole
   card surface is the listing link. The two things that must stay independently clickable
   are lifted back above it below. */
.piece .go{position:absolute;inset:0;z-index:6}
/* The carousel arrows used to be inside the card's anchor and cancelled its navigation with
   preventDefault. They are outside it now, so they simply need to be on top. */
.shot-nav{z-index:7}
/* Decorative overlays must not eat clicks meant for the card beneath them. .blurb covers the
   bottom third of the photograph on hover, which is a large dead zone if it is left
   interactive. */
.piece .blurb,.piece .grain,.piece .cap,.piece .ext,.piece .fav-badge{pointer-events:none}

/* The focus ring belongs to the card, not to the invisible rectangle on top of it. */
.piece:focus-within .frame{outline:2px solid var(--magenta);outline-offset:2px}

/* ---------- ask about this piece ---------- */

/* The single highest-value control on the page, and the site did not have it: every card's
 * only exit was Vinted, in a new tab, so a visitor with a question — does this fit, what are
 * the real measurements, do you have it in another colour — had nowhere to put it.
 *
 * WhatsApp green rather than the site's amber, because the recognition IS the affordance:
 * it says which app is about to open before you tap it. Measured 5.9:1 on --void-2, so it
 * clears AA as text without a fill.
 */
.piece .ask{
  position:relative;z-index:7;
  display:flex;align-items:center;justify-content:center;
  margin-top:8px;min-height:44px;padding:8px 10px;
  font-size:var(--fs-2xs);letter-spacing:.05em;text-transform:uppercase;
  color:#25D366;border:1px solid rgba(37,211,102,.34);
  transition:background .15s,color .15s,border-color .15s;
}
.piece .ask:hover{background:#25D366;border-color:#25D366;color:var(--void)}
.piece .ask:focus-visible{outline:2px solid #25D366;outline-offset:2px}
/* On a sold card the ask is the ONLY thing left to do, so it keeps full weight while the
   rest of the card is dimmed. */
.piece.sold .ask{color:#3BE87C;border-color:rgba(37,211,102,.45)}

/* ---------- paint budget ---------- */

/* THERE IS NO content-visibility HERE ANY MORE, AND THERE CANNOT BE. Removed 24 Aug 2026
 * because it was corrupting the card grid on every shelf page — the bug looked like the
 * photo swallowing the title, price and button below it.
 *
 * What it used to say: wardrobe.html holds 157 cards, shop.js reveals 24 at a time, and the
 * rest still cost style, layout and paint on every scroll — so `.piece:nth-child(n+7)` got
 * `content-visibility:auto` with `contain-intrinsic-size:auto 560px` to skip the off-screen
 * ones. Sound reasoning, and it worked when the card was a plain block.
 *
 * WHY IT CANNOT COEXIST WITH THIS CARD. site.css gives `.piece` `grid-template-rows:subgrid`
 * — that is what makes the frame, title, tags and button line up ACROSS the cards in a row
 * instead of each card sizing itself. content-visibility:auto applies layout containment,
 * which makes the element an independent formatting context, and a subgrid cannot inherit
 * its parent's tracks from inside one. So every card from the seventh onwards silently
 * dropped out of the subgrid: measured, its rows resolved to `2px 52.59px 25.79px 52px`
 * instead of `subgrid`, the frame's 495px track collapsed to 2px, and the card laid out at
 * 132px tall while still containing a 495px photograph. Everything below the photo drew on
 * top of it.
 *
 * `contain-intrinsic-size:auto` then made it self-sustaining: the browser remembers the size
 * an element had when it was last rendered, so once a card rendered at the broken 132px it
 * kept being 132px. Both a fixed intrinsic size and a corrected one were tried and neither
 * helped, because the intrinsic size was never the problem — the lost subgrid was.
 *
 * IF THE PAINT COST EVER NEEDS ADDRESSING AGAIN, it must not be done on `.piece`. The
 * options that do not fight subgrid: put content-visibility on a wrapper INSIDE the card,
 * or have shop.js keep the far-off cards `hidden` (it already owns that mechanism, and
 * `display:none` skips strictly more work than content-visibility does). Measure first —
 * this was an optimisation with no measurement behind it, and it cost more than it bought.
 */

/* ---------- pagination ---------- */

/* Real links to real pages. The strip sits below the grid and above the FX note, which is
   where a visitor who has reached the end of 100 cards is already looking.
   Wrapped rather than scrolled: a 16-page series on a phone is three short rows, and a
   horizontally scrolling control is one nobody discovers. */
.pager{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:8px;
  margin:28px 0 8px;font-family:var(--mono);font-size:var(--fs-xs);
}
.pg-nums{display:flex;flex-wrap:wrap;gap:6px}
.pager .pg{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:38px;height:38px;padding:0 10px;
  border:1px solid var(--band);border-radius:2px;
  color:var(--bone-dim);text-decoration:none;
  transition:background .16s ease,color .16s ease,border-color .16s ease;
}
.pager .pg:hover,.pager .pg:focus-visible{background:var(--band);color:var(--bone)}
/* The current page is a link to itself, kept in the tab order and marked for assistive
   tech with aria-current — removing it would make the strip jump as you page through. */
.pager .pg[aria-current="page"]{background:var(--saffron);color:var(--void);border-color:var(--saffron)}
.pager .pg-step{font-weight:600}
.pg-gap{padding:0 2px;color:var(--bone-dim);opacity:.6}
/* Sits inline with the centred controls, dropped to its own line on a narrow one. */
.pg-of{color:var(--bone-dim);opacity:.75}
@media (max-width:560px){
  .pg-of{width:100%;text-align:center}
  .pager .pg{min-width:34px;height:34px}
}

/* ---------- subcategory chips ---------- */

/* Real links to real pages, sitting between the shelf strip and the filter toolbar — the
   order matters: shelf, then division of shelf, then attributes of what is on it.
   Scrolls horizontally rather than wrapping: Meditation has twelve divisions, and three
   wrapped rows of chips would push the grid below the fold on a phone. The shelf strip
   above it already behaves this way, so the gesture is the one the page has taught. */
/* Wraps, for the same reason .cat-nav does — but clamped, because a shelf can carry a dozen
   divisions and wrapping all of them would push the first product a long way down on a
   phone. Two rows is the compromise: nothing is behind a gesture, and what is behind the
   clamp is behind a labelled button that says how many.
   The clamp is a max-height rather than a row count because CSS has no "two flex rows". The
   number here is a no-JS fallback only, and it is deliberately generous: shop.js measures a
   real chip on load and sets the exact two-row height, because guessing it wrong clips the
   second row THROUGH the chips rather than below them — which is what a hand-computed 72px
   did (the chips are 42px tall, not the 32px the padding arithmetic suggested). */
.subs{
  display:flex;flex-wrap:wrap;gap:8px;margin:0 0 8px;
  max-height:92px;overflow:hidden;
  font-family:var(--mono);font-size:var(--fs-2xs);
}
.subs.subs-open{max-height:none}
.subs-more{
  display:none;margin:0 0 16px;padding:6px 12px;
  background:none;border:1px dashed var(--band);border-radius:999px;
  font-family:var(--mono);font-size:var(--fs-2xs);color:var(--bone-dim);
  cursor:pointer;min-height:32px;
  transition:border-color .16s ease,color .16s ease;
}
.subs-more:not([hidden]){display:inline-flex;align-items:center;gap:6px}
.subs-more:hover,.subs-more:focus-visible{border-color:var(--bone);color:var(--bone)}
.subs-more b{font-weight:500;opacity:.6}
.subs .sub{
  flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;
  padding:7px 12px;border:1px solid var(--band);border-radius:999px;
  color:var(--bone-dim);text-decoration:none;white-space:nowrap;
  transition:background .16s ease,color .16s ease,border-color .16s ease;
}
.subs .sub b{font-weight:500;opacity:.6}
.subs .sub:hover,.subs .sub:focus-visible{background:var(--band);color:var(--bone)}
.subs .sub.on{background:var(--bone);color:var(--void);border-color:var(--bone)}
.subs .sub.on b{opacity:.55}

/* ---------- out of stock ---------- */

/* Same visual language as sold, one step lighter. Both mean "not today", so both are
   desaturated rather than faded — the photograph still has to read as a real object. Out of
   stock is the recoverable one, so it keeps more colour than sold: the piece is expected
   back, and greying it as hard as a sold item would overstate the loss. */
.piece.oos .frame .ph{filter:grayscale(.55) contrast(.95)}
.piece.oos .nm{color:var(--bone-dim)}
/* Saffron on void, 9.6:1. Not the chilli of a sold flag: this is a wait, not an ending. */
.frame .cap.oos-flag{background:var(--saffron);color:var(--void)}
/* The price stays legible and unstruck. It is still what the thing costs when it returns,
   and striking it through would read as a discount that is not on offer. */
.piece.oos .pr{opacity:.75}

/* Availability leads the refinements now, so it is a control like any other in that grid
   and wants no styling of its own — `.shop-bar select` already dresses it. It used to carry
   a bespoke block here (fixed height, its own border and font size) because it sat beside
   the search box and had to match the input rather than the filters. It sits with the
   filters now, so it should look like a filter. */

/* ---------- a fourth product column on a wide screen (24 Aug 2026) ----------
 *
 * site.css caps .grid3 at three columns from 801px up and never grows it again, so a 1920
 * display showed three cards across a 1280 wrap. With the wrap now at 1600 there is room
 * for four at ~373px, which is barely under the 396px they measure today — the row gains a
 * product without the cards losing anything.
 *
 * THIS RULE LIVES IN shop.css DELIBERATELY. .grid3 is shared with the Diaries card grids,
 * and those cards carry a title and an excerpt: four across turns an article into a tile.
 * shop.css is only ever loaded on wardrobe pages, so the split needs no extra class. */
@media (min-width:1400px){
  .grid3{grid-template-columns:repeat(4,minmax(0,1fr))}
}
