/* ═══════════════════════════════════════════════════════════════════════════════════════════════════
   SHOP PRODUCT GRID — from ~/Downloads/shop-grid-mockup.html, 22 Sep 2026.

   SCOPED TO `.pt-sg`. The mockup uses names generic enough to collide with GeneratePress and with this
   plugin's own `.pt-` classes — `.card`, `.grid`, `.img`, `.title`, `.price` — so every rule is prefixed
   `sg-` AND sits inside the wrapper. The wrapper is also what lifts these above the theme's bare-element
   styles without reaching for !important. [[reference-css-specificity-theme-override]]

   The mockup's `@container` queries become `@media` at the same widths, 1100 and 640, per the brief.
   Tokens come from css/header-footer-global.css — nothing is redeclared here. The mockup's `--pt-sunk`
   was marked ASSUMED; `--pt-fill-control` is this system's equivalent and is used instead.
   ═══════════════════════════════════════════════════════════════════════════════════════════════════ */
/* FULL BLEED, then measure inside. The theme wraps content in its own narrower container, so a
   `max-width:1320px` on `.wrap` was being clipped to whatever the theme allowed — the grid measured
   185px columns on a 1440px screen. The wrapper breaks out to the viewport and `.wrap` sets the real
   measure. `overflow-x: clip` rather than `hidden` so the sticky filter column still sticks.
   Same pattern as the homepage; it is the only way to own a full-width band inside this theme. */
/* 🔴 IT IS A FLEX ITEM, SO WIDTH ALONE DOES NOT SETTLE IT (Leon spotted the cause, 22 Sep).
   `.site.grid-container` is capped at 1100px and `.site-content` is `display:flex`. Negative margins
   alone pulled the left edge out to 0 but the item still sized itself from its CONTENT — 1320px — so the
   band started at the viewport edge and stopped 120px short of the right one. That is the white strip.
   `flex: 0 0 100vw` is the part that was missing: a flex item ignores `width` unless its basis says so. */
.pt-sg{
    flex:0 0 100vw;
    width:100vw;
    max-width:100vw;
    margin-left:calc(50% - 50vw);
    margin-right:calc(50% - 50vw);
    overflow-x:clip;
}
.pt-sg{background:var(--pt-surface-dim);color:var(--pt-ink);font-size:15px;line-height:1.5}

/* SENTENCE CASE, as the brief asks for throughout the store. The theme sets `text-transform:capitalize`
   on headings, so "Best sellers" reached the screen as "Best Sellers" — the same rule that was
   title-casing eleven headings on the homepage this morning. Named elements, not inheritance. */
.pt-sg h1,.pt-sg h2,.pt-sg h3{text-transform:none}
.pt-sg *{box-sizing:border-box}
.pt-sg a{color:inherit;text-decoration:none}
.pt-sg .mono{font-family:var(--pt-mono);font-variant-numeric:tabular-nums}
/* 1560, not 1320 (Leon, 22 Sep): five cards per row need the room, and the sidebar does not shrink.
   260 sidebar + 32 gap + 64 gutters leaves 1204 for five cards and their four 16px gaps — ~229 each,
   which is the same card width the four-up had at 1320. Wider page, same product. */
.pt-sg .wrap{max-width:1560px;margin:0 auto;padding:0 32px}

/* ── A1. sub-nav ── */
.pt-sg .sg-subnav{background:var(--pt-surface);border-bottom:1px solid var(--pt-border)}
.pt-sg .sg-subnav .in{display:flex;gap:28px;justify-content:center;padding:0 32px;overflow-x:auto;scrollbar-width:none}
.pt-sg .sg-subnav .in::-webkit-scrollbar{display:none}
.pt-sg .sg-subnav a{font-size:14px;font-weight:500;color:var(--pt-ink-2);padding:14px 0 12px;border-bottom:2px solid transparent;white-space:nowrap}
/* Ink, never red: red on this site means a dispute. [[feedback-status-colour-system]] */
.pt-sg .sg-subnav a.on{color:var(--pt-ink);font-weight:600;border-bottom-color:var(--pt-ink)}
.pt-sg .sg-subnav a:hover{color:var(--pt-ink)}

/* ── A2. head ── */
.pt-sg .sg-head{padding:28px 0 24px}
.pt-sg .crumb{font-size:13px;color:var(--pt-ink-3);margin:0 0 18px;line-height:1.4}
.pt-sg .crumb a{color:var(--pt-accent)}
.pt-sg .crumb span{margin:0 6px;color:var(--pt-border-strong)}
.pt-sg .head-row{display:flex;justify-content:space-between;align-items:flex-end;gap:32px}
.pt-sg .head-copy{min-width:0}
.pt-sg .sg-brandlogo{max-height:40px;width:auto;display:block;margin:0 0 10px}
.pt-sg h1{font-size:40px;font-weight:800;letter-spacing:-.03em;line-height:1.05;margin:0 0 8px;color:var(--pt-ink)}
.pt-sg .head-copy p{margin:0;color:var(--pt-ink-2);font-size:16px;max-width:560px}
.pt-sg .assure{display:flex;gap:18px;flex-wrap:wrap;font-size:13px;color:var(--pt-ink-2);flex:none}
.pt-sg .assure span{display:flex;align-items:center;gap:6px}
.pt-sg .assure svg{width:14px;height:14px;flex:none}

.pt-sg .subcats{display:flex;gap:8px;margin-top:22px;overflow-x:auto;scrollbar-width:none}
.pt-sg .subcats::-webkit-scrollbar{display:none}
.pt-sg .subcats a{flex:none;font-size:13.5px;padding:7px 12px;border:1px solid var(--pt-border-strong);background:var(--pt-surface)}
.pt-sg .subcats a.on{background:var(--pt-ink);color:var(--pt-on-primary);border-color:var(--pt-ink)}
.pt-sg .subcats a .mono{font-size:12px;color:var(--pt-ink-3);margin-left:4px}
.pt-sg .subcats a.on .mono{color:#BDB8AE}

/* ── A3. layout ── */
.pt-sg .sg-layout{display:grid;grid-template-columns:260px minmax(0,1fr);gap:32px;padding:8px 0 80px;border-top:1px solid var(--pt-border)}
.pt-sg .sg-filters{padding-top:20px;align-self:start;position:sticky;top:0;min-width:0}
.pt-sg .sg-results{padding-top:20px;min-width:0}

/* ── B1. results bar ── */
.pt-sg .sg-bar{display:flex;justify-content:space-between;align-items:center;gap:16px;padding-bottom:14px;border-bottom:1px solid var(--pt-border)}
.pt-sg .count{font-size:14px;color:var(--pt-ink-2)}
.pt-sg .count .mono{color:var(--pt-ink);font-weight:600}
.pt-sg .bar-r{display:flex;gap:10px;align-items:center}
.pt-sg .sg-sort{margin:0}
/* 🔴 `color` STATED, not inherited. The Filters control is a real `<button>`, and the theme styles bare
   buttons with their own colour and background — so the label came out white on white and the control
   looked like an empty box. `.sel` is used by a <label> and a <button>, and only one of them was
   inheriting ink. [[reference-css-specificity-theme-override]] */
.pt-sg .sel{display:flex;align-items:center;gap:8px;height:38px;padding:0 12px;border:1px solid var(--pt-border-strong);background:var(--pt-surface);color:var(--pt-ink);font:inherit;font-size:14px;cursor:pointer;border-radius:0}
.pt-sg button.sel:hover{background:var(--pt-surface);color:var(--pt-ink)}
.pt-sg .sel small{color:var(--pt-ink-3);font-size:13px}
.pt-sg .sel select{font:inherit;font-size:14px;border:0;background:none;color:var(--pt-ink);padding:0;margin:0;cursor:pointer;height:auto;min-height:0;line-height:1.4}
.pt-sg .sel:focus-within{outline:2px solid var(--pt-accent);outline-offset:2px}
.pt-sg .sg-go{height:38px;padding:0 14px;border:1px solid var(--pt-ink);background:var(--pt-ink);color:#fff;font:inherit;font-size:14px}
/* The Filters button belongs to the sheet, and the sheet only exists once the sidebar has gone. */
.pt-sg .sg-fbtn{display:none}

/* ── B4. grid + cards ──
   🔴 THE CARD IS SCOPED `.pt-shop`, NOT `.pt-sg`. It is used on the shop LANDING as well as the grid,
   and every card rule was written `.pt-sg .sg-card` — so on /shop/ the markup arrived with no styles at
   all and the rails rendered as a stack of raw text. A partial shared between two pages cannot be
   scoped to one of them. Both wrappers now carry `pt-shop` alongside their own class. */
/* SEPARATE CARDS, not a mesh (Leon, 22 Sep). Shared borders read as a table once the rows get long;
   a gap lets each product be its own object. Each card has to close all four sides now — with a gap,
   three of them would otherwise simply vanish. */
.pt-sg .sg-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:16px;margin-top:18px}
.pt-shop .sg-card{background:var(--pt-surface);border:1px solid var(--pt-border);padding:16px 16px 18px;display:flex;flex-direction:column;position:relative}
.pt-shop .sg-img{display:block;aspect-ratio:1/1;background:var(--pt-fill-control);margin:0 0 14px;position:relative;overflow:hidden}
/* CONTAIN, never crop, with the photo inset so a white-background cut-out and a full-bleed photo sit on
   the same visual footing. The brief is explicit and it is the difference between a grid and a collage. */
/* THE PHOTO USES THE WHOLE TILE, AND KEEPS THE WHOLE PRODUCT (Leon, 22 Sep — twice).
   First it was inset 12% and contained, which drew a second box inside the first. Then `cover`, which
   filled the tile edge to edge and CROPPED — a tall bench drill lost its top and bottom.
   Those are the only two outcomes unless the photo happens to be square, so this takes the third
   option: `contain` across the full tile. The picture is never cut, and where the ratios disagree the
   tile's own background shows rather than a second panel. A catalogue of mixed crops needs the product
   intact more than it needs a perfect rectangle. */
.pt-shop .sg-img img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;transition:transform .25s ease}
.pt-shop .sg-card:hover .sg-img img{transform:scale(1.03)}
.pt-shop .sg-card.is-out .sg-img img{opacity:.45}

/* ALWAYS VISIBLE (Leon, 22 Sep). A control that only exists on hover cannot be found by anyone who is
   not already pointing at it — and on a touch screen there is no hover at all, so it was only ever
   half-shipped. It sits on the photo now, so it carries its own white chip to stay legible. */
.pt-shop .sg-wish{position:absolute;top:10px;right:10px;width:30px;height:30px;background:var(--pt-surface);border:1px solid var(--pt-border);display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0;border-radius:0;z-index:2}
/* Matches the marketplace heart's own geometry: 16px on a 24 viewBox at stroke-width 2. `currentColor`
   rather than a token, so the glyph takes the button's colour exactly as it does in the marketplace. */
.pt-shop .sg-wish{color:var(--pt-ink)}
.pt-shop .sg-wish svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2}
.pt-shop .sg-wish.is-on svg{fill:currentColor}
.pt-shop .sg-wish.is-on svg{fill:var(--pt-ink)}

/* The empty brand line is kept, not removed: it is what keeps every title in the row on one baseline. */
.pt-shop .sg-brand{font-size:12px;color:var(--pt-ink-3);margin:0 0 2px;min-height:18px}
.pt-shop .sg-title{font-size:14.5px;font-weight:600;line-height:1.35;margin:0 0 12px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:39px}
.pt-shop .sg-title:hover{text-decoration:underline}
.pt-shop .sg-price{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-top:auto;padding-top:10px;border-top:1px solid var(--pt-border)}
.pt-shop .sg-price .now{font-family:var(--pt-mono);font-size:16px;font-weight:600}
.pt-shop .sg-price .was{font-family:var(--pt-mono);font-size:13px;color:var(--pt-ink-3);text-decoration:line-through}
/* The ONE red on this page, and it is marketing type rather than a status. */
.pt-shop .sg-price .sale{font-size:12px;font-weight:600;color:var(--pt-brand)}
.pt-shop .sg-stock{font-size:12.5px;color:var(--pt-ink-3);margin:6px 0 0;min-height:19px}

.pt-shop .sg-add{margin-top:12px;height:38px;border:1px solid var(--pt-ink);background:var(--pt-surface);color:var(--pt-ink);font:inherit;font-size:13.5px;font-weight:600;cursor:pointer;width:100%;display:flex;align-items:center;justify-content:center;border-radius:0}
/* HOVER MATCHES THE MARKETPLACE SAVE HEART (Leon, 22 Sep) — `--pt-fill-control`, the same token
   `.pt-listing-card__save:hover` uses (marketplace-discover.css:1804). The button dims rather than
   recolouring, which is the site-wide rule; a full ink flip made it read as the primary action on a card
   whose primary action is the product itself. [[feedback-button-hover-noir]]
   The TOKEN, not the hex: one change to `--pt-fill-control` and both controls still agree. */
.pt-shop .sg-add:hover{background:var(--pt-fill-control);color:var(--pt-ink)}
/* And the heart hovers the same way, because it is the same control on the same card. */
.pt-shop .sg-wish:hover{background:var(--pt-fill-control)}
/* Sold out SUPPRESSES the control rather than greying a live one out. [[feedback-one-action-card]] */
.pt-shop .sg-add.is-off{border-color:var(--pt-border-strong);color:var(--pt-ink-3);cursor:default}
.pt-shop .sg-add.is-off:hover{background:var(--pt-surface);color:var(--pt-ink-3)}
.pt-shop .sg-add.is-done{background:var(--pt-ink);color:#fff}
.pt-shop .sg-err{margin:8px 0 0;font-size:12.5px;color:var(--pt-ink)}
.pt-shop .sg-add:focus-visible,.pt-shop .sg-wish:focus-visible{outline:2px solid var(--pt-accent);outline-offset:2px}

/* ── B7. load more ── */
.pt-sg .sg-more{display:flex;flex-direction:column;align-items:center;gap:12px;padding:40px 0 0}
/* 🔴 `display:flex` BEATS THE `hidden` ATTRIBUTE. The browser's own `[hidden]{display:none}` is (0,1,0);
   the rule above is (0,2,0), so `wrap.hidden = true` set the attribute and changed nothing — "Show 6
   more" stayed on screen after the last six had loaded. Second time today: the homepage rail arrows
   needed the same line. ANY rule that sets `display` on an element JS will hide has to say this too. */
.pt-sg .sg-more[hidden]{display:none}
.pt-sg .sg-more .mono{font-size:13px;color:var(--pt-ink-2)}
.pt-sg .prog{width:240px;height:3px;background:var(--pt-border)}
.pt-sg .prog i{display:block;height:100%;background:var(--pt-ink);transition:width .2s ease}
.pt-sg .sg-btn{display:inline-flex;align-items:center;justify-content:center;height:46px;padding:0 28px;border:1px solid var(--pt-ink);background:var(--pt-surface);color:var(--pt-ink);font:inherit;font-size:15px;font-weight:600;cursor:pointer;border-radius:0}
.pt-sg .sg-btn:hover{background:var(--pt-ink);color:#fff}

/* ── B8. empty ── */
.pt-sg .sg-empty{padding:56px 24px;text-align:center;border:1px solid var(--pt-border);background:var(--pt-surface);margin-top:18px}
.pt-sg .sg-empty h3{font-size:20px;font-weight:700;margin:0 0 6px;color:var(--pt-ink)}
.pt-sg .sg-empty p{margin:0 0 20px;color:var(--pt-ink-2)}

/* ═══════════════ TABLET ═══════════════ */
/* Between 1100 and 1400 five would be too thin, so it steps down before the sidebar leaves. */
@media (max-width:1400px){ .pt-sg .sg-grid{grid-template-columns:repeat(4,minmax(0,1fr))} }

@media (max-width:1100px){
  .pt-sg .sg-layout{grid-template-columns:1fr}
  .pt-sg .sg-filters{display:none}
  .pt-sg .sg-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
  .pt-sg .head-row{flex-direction:column;align-items:flex-start;gap:14px}
}

/* ═══════════════ PHONE ═══════════════ */
@media (max-width:640px){
  .pt-sg .wrap{padding:0 16px}
  .pt-sg .sg-subnav .in{justify-content:flex-start;padding:0 16px;gap:22px}
  .pt-sg .sg-head{padding:18px 0 16px}
  .pt-sg .crumb{margin-bottom:10px}
  .pt-sg h1{font-size:30px}
  .pt-sg .head-copy p{font-size:15px}
  .pt-sg .assure{gap:10px;font-size:12.5px}
  .pt-sg .subcats{margin:16px -16px 0;padding:0 16px}
  .pt-sg .sg-layout{padding-bottom:56px}
  .pt-sg .sg-results{padding-top:0}
  /* Sticky, because the count is the answer to "did that filter do anything". */
  /* Filters LEFT, Sort RIGHT, on one row — two equal 42px buttons (Leon, 22 Sep). The count goes on its
     own line underneath, where it can be read rather than squeezed between two controls. */
  .pt-sg .sg-bar{position:sticky;top:0;z-index:5;background:var(--pt-surface-dim);margin:0 -16px;padding:10px 16px;display:grid;grid-template-columns:1fr;gap:8px}
  .pt-sg .count{order:2;font-size:13px}
  .pt-sg .bar-r{order:1;display:grid;grid-template-columns:1fr 1fr;gap:8px;width:100%}
  .pt-sg .sel{height:42px;justify-content:center;width:100%}
  .pt-sg .sg-sort{width:100%;min-width:0}
  .pt-sg .sel small{display:none}
  .pt-sg .sg-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .pt-shop .sg-card{padding:10px 10px 12px}
  .pt-shop .sg-img{margin-bottom:10px}
  .pt-shop .sg-brand{font-size:11.5px;min-height:16px}
  .pt-shop .sg-title{font-size:13.5px;min-height:36px;margin-bottom:8px}
  .pt-shop .sg-price .now{font-size:15px}
  .pt-shop .sg-price .was{font-size:12px}
  .pt-shop .sg-stock{font-size:12px}
  .pt-shop .sg-add{height:36px;font-size:13px;margin-top:8px}
  .pt-shop .sg-wish{width:28px;height:28px;top:6px;right:6px}
  .pt-sg .sg-more .sg-btn{width:100%}
  .pt-sg .prog{width:100%}
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════════
   THE FILTER PANEL (brief §5, 22 Sep). Fresh markup from includes/shop-grid-filters.php, because the
   legacy sidebar carries its styles INLINE — `style="color:#888;text-decoration:underline"` and thirty
   more — and no stylesheet beats an inline declaration without `!important` on every rule. Restyling it
   was not available; re-rendering it was.
   ═══════════════════════════════════════════════════════════════════════════════════════════════════ */
.pt-sg .sgf{font-size:14px}
.pt-sg .sgf-top{display:flex;justify-content:space-between;align-items:baseline;padding-bottom:14px;border-bottom:1px solid var(--pt-ink)}
.pt-sg .sgf-top b{font-size:15px;font-weight:700}
.pt-sg .sgf-clear{font-size:13px;color:var(--pt-accent);text-decoration:none}
.pt-sg .sgf-clear:hover{text-decoration:underline}

.pt-sg .sgf-g{padding:16px 0;border-bottom:1px solid var(--pt-border)}
.pt-sg .sgf-h{display:flex;align-items:center;gap:6px;font-size:14px;font-weight:600;cursor:pointer;user-select:none}
/* 🔴 THE COUNT SITS AGAINST THE TITLE, not in a right-hand column (Leon, 22 Sep).
   `margin-right:auto` was on the TITLE, which pushes everything after it to the far right — so
   "Power source" and its "3" ended up at opposite ends of the row. The chevron already takes the right
   edge with its own `margin-left:auto`, so the title needs no auto margin at all and the count simply
   follows it. Reads as one label — "Power source 3" — which is what it is. */
.pt-sg .sgf-gn{font-family:var(--pt-mono);font-size:11.5px;font-weight:400;color:var(--pt-ink-3)}
/* The mockup's 12px is the GLYPH's own size in a font that draws it small; at 12px it read as a speck.
   18px in a fixed box gives the same visual weight as the mockup and a real hit area. */
.pt-sg .sgf-chev{font-style:normal;font-size:18px;line-height:1;color:var(--pt-ink-3);margin-left:auto;width:18px;text-align:right;flex:none}
.pt-sg .sgf-b{margin-top:12px}
.pt-sg .sgf-g.is-shut .sgf-b{display:none}
.pt-sg .sgf-h:focus-visible{outline:2px solid var(--pt-accent);outline-offset:2px}

/* price */
.pt-sg .sgf-prices{display:grid;grid-template-columns:1fr auto 1fr;gap:8px;align-items:center}
.pt-sg .sgf-pin{display:flex;align-items:center;height:36px;border:1px solid var(--pt-border-strong);background:var(--pt-surface);padding:0 10px;min-width:0}
.pt-sg .sgf-pin i{font-style:normal;color:var(--pt-ink-3);margin-right:4px;font-family:var(--pt-mono);font-size:13.5px}
.pt-sg .sgf-pin input{width:100%;min-width:0;border:0;background:none;padding:0;margin:0;height:auto;min-height:0;font-family:var(--pt-mono);font-size:13.5px;color:var(--pt-ink);-moz-appearance:textfield}
.pt-sg .sgf-pin input::-webkit-outer-spin-button,.pt-sg .sgf-pin input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.pt-sg .sgf-pin:focus-within{outline:2px solid var(--pt-accent);outline-offset:1px}
.pt-sg .sgf-to{font-size:12px;color:var(--pt-ink-3)}

/* Two native range inputs stacked on one track. Native, so the keyboard and screen readers get a real
   control; the visible track and fill are drawn underneath and the inputs themselves are transparent. */
.pt-sg .sgf-track{position:relative;height:2px;background:var(--pt-border-strong);margin:18px 8px 6px}
/* left/right default to 0 so the bar is full width — and therefore VISIBLE — before anything sets it.
   An absolutely positioned element with neither edge pinned has no width at all. */
.pt-sg .sgf-fill{position:absolute;top:0;bottom:0;left:0;right:0;background:var(--pt-ink)}
.pt-sg .sgf-range{position:absolute;left:-8px;right:-8px;top:-8px;width:calc(100% + 16px);height:18px;margin:0;padding:0;background:none;border:0;-webkit-appearance:none;appearance:none;pointer-events:none}
.pt-sg .sgf-range::-webkit-slider-thumb{-webkit-appearance:none;pointer-events:auto;width:16px;height:16px;background:var(--pt-surface);border:2px solid var(--pt-ink);border-radius:0;box-shadow:none;cursor:pointer}
.pt-sg .sgf-range::-moz-range-thumb{pointer-events:auto;width:16px;height:16px;background:var(--pt-surface);border:2px solid var(--pt-ink);border-radius:0;box-shadow:none;cursor:pointer}
.pt-sg .sgf-range:focus-visible::-webkit-slider-thumb{outline:2px solid var(--pt-accent);outline-offset:2px}

/* toggles */
.pt-sg .sgf-tg{display:flex;align-items:center;gap:10px;padding:4px 0;font-size:14px;cursor:pointer}
.pt-sg .sgf-tg__l{margin-right:auto}
.pt-sg .sgf-tg input{position:absolute;opacity:0;width:0;height:0}
.pt-sg .sgf-sw{width:32px;height:18px;flex:none;background:var(--pt-border-strong);position:relative;transition:background .12s}
.pt-sg .sgf-sw::after{content:"";position:absolute;left:3px;top:3px;width:12px;height:12px;background:var(--pt-surface);transition:left .12s}
.pt-sg .sgf-tg input:checked ~ .sgf-sw{background:var(--pt-ink)}
.pt-sg .sgf-tg input:checked ~ .sgf-sw::after{left:17px}
.pt-sg .sgf-tg input:focus-visible ~ .sgf-sw{outline:2px solid var(--pt-accent);outline-offset:2px}

/* option rows */
.pt-sg .sgf-opt{display:flex;align-items:center;gap:10px;padding:5px 0;font-size:14px;cursor:pointer}
.pt-sg .sgf-opt.is-extra{display:none}
.pt-sg .sgf-g.is-open-all .sgf-opt.is-extra{display:flex}
.pt-sg .sgf-opt input{position:absolute;opacity:0;width:0;height:0}
.pt-sg .sgf-box{width:16px;height:16px;flex:none;border:1.5px solid var(--pt-border-strong);background:var(--pt-surface);position:relative}
.pt-sg .sgf-opt input:checked ~ .sgf-box{background:var(--pt-ink);border-color:var(--pt-ink)}
.pt-sg .sgf-opt input:checked ~ .sgf-box::after{content:"";position:absolute;left:4px;top:0;width:4px;height:9px;border:solid var(--pt-surface);border-width:0 2px 2px 0;transform:rotate(45deg)}
.pt-sg .sgf-opt input:focus-visible ~ .sgf-box{outline:2px solid var(--pt-accent);outline-offset:2px}
.pt-sg .sgf-opt__l{margin-right:auto;min-width:0}
/* Right-aligned, mono, no parentheses — the count is data, not part of the name. */
/* One count column across every group. It used to sit wherever the row's flex left it — beside the label
   in one group and pressed against the toggle in the next — so the numbers never lined up down the
   column. Fixed width, right-aligned, identical in an option row and a toggle row. */
.pt-sg .sgf-n{font-family:var(--pt-mono);font-size:12px;color:var(--pt-ink-3);flex:none;min-width:28px;text-align:right}
.pt-sg .sgf-tg .sgf-n{margin-right:2px}
.pt-sg .sgf-more{display:inline-block;margin-top:6px;font:inherit;font-size:13px;color:var(--pt-accent);background:none;border:0;padding:0;cursor:pointer}
.pt-sg .sgf-more:hover{text-decoration:underline}
.pt-sg .sgf-go{margin-top:16px;width:100%;height:42px;border:1px solid var(--pt-ink);background:var(--pt-ink);color:#fff;font:inherit;font-size:14px;font-weight:600}

/* ── applied-filter chips above the grid ── */
.pt-sg .sg-chips{display:flex;gap:8px;flex-wrap:wrap;align-items:center;padding:14px 0 18px}
.pt-sg .sg-chip{display:inline-flex;align-items:center;gap:8px;font-size:13px;padding:5px 8px 5px 12px;background:var(--pt-bone);border:1px solid var(--pt-border-strong)}
.pt-sg .sg-chip b{font-weight:400;color:var(--pt-ink-3);font-size:15px;line-height:1}
.pt-sg .sg-chips .sg-chip:hover b{color:var(--pt-ink)}
.pt-sg .sg-chips > a.sg-clear{font-size:13px;color:var(--pt-accent);margin-left:4px;white-space:nowrap}

/* ── §4: the sidebar sticks and never shows a scrollbar ── */
.pt-sg .sg-filters{max-height:calc(100vh - 32px);overflow-y:auto;scrollbar-width:none}
.pt-sg .sg-filters::-webkit-scrollbar{display:none}

/* ── §6: no text caret in the count. The number is not editable and must not look like it is. ── */
.pt-sg .count{caret-color:transparent;user-select:none}

/* ═══════════════ THE FILTER SHEET (§8) ═══════════════
   Below 1100 the sidebar is gone, so the same panel comes back as a bottom sheet. It is a real dialog:
   it traps focus, closes on Escape, and locks the page behind it. */
@media (max-width:1100px){
  .pt-sg .sg-fbtn{display:inline-flex;align-items:center;gap:6px}
  .pt-sg .bar-r{display:flex;gap:10px}
}
.pt-sg .sg-sheet-wrap{position:fixed;inset:0;z-index:9999;display:flex;align-items:flex-end}
.pt-sg .sg-sheet-wrap[hidden]{display:none}
.pt-sg .sg-sheet-bd{position:absolute;inset:0;background:rgba(26,26,26,.5)}
.pt-sg .sg-sheet{position:relative;width:100%;max-height:calc(100vh - 120px);background:var(--pt-surface-dim);display:flex;flex-direction:column}
.pt-sg .sg-sheet-h{display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid var(--pt-border);background:var(--pt-surface);flex:none}
.pt-sg .sg-sheet-h b{font-size:16px}
.pt-sg .sg-sheet-x{font:inherit;font-size:24px;line-height:1;background:none;border:0;padding:0 4px;cursor:pointer;color:var(--pt-ink)}
.pt-sg .sg-sheet-b{flex:1;overflow-y:auto;padding:0 20px;min-height:0}
.pt-sg .sg-sheet-f{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:center;padding:14px 20px;border-top:1px solid var(--pt-border);background:var(--pt-surface);margin:0 -20px;position:sticky;bottom:0}
.pt-sg .sg-sheet-f a{font-size:14px;color:var(--pt-accent)}
.pt-sg .sg-btn.is-ink{background:var(--pt-ink);color:#fff}
.pt-sg .sg-btn.is-ink:hover{background:#1f1f1f;color:#fff}
/* The sheet's own header row is the title, so the panel does not repeat it. */
.pt-sg .sg-sheet-b .sgf-top{display:none}

/* The anchor lands BELOW the sticky header rather than under it. Without this the results bar arrives
   hidden behind the site header and it looks as though nothing scrolled. */
.pt-sg #sg-results{scroll-margin-top:110px}
@media (max-width:640px){ .pt-sg #sg-results{scroll-margin-top:72px} }

/* A group that cannot be collapsed must not look clickable either — no pointer, no hover intent. */
.pt-sg .sgf-g.is-fixed .sgf-h{cursor:default}

/* ═══ QUICK CHOICE — chips in the card ═══════════════════════════════════════
   Variations brief §B, 23 Sep 2026. Only ever rendered for a product the server decided is a small
   single choice: one attribute, six or fewer values, no per-variation photo.
   ──────────────────────────────────────────────────────────────────────────── */

/* 🔴 `display:flex` BEATS THE `hidden` ATTRIBUTE. Both of these carry `hidden` in the markup and both
   would otherwise render open — the chip row on every card in the grid, and a stray "Add to cart" under
   it. This is the third time this exact trap has cost time on this project, so it is written down here
   rather than remembered. */
.pt-shop .sg-opts[hidden],
.pt-shop .sg-add[hidden]{display:none}

/* The wrapper is now the card's last flex item, so it carries the spacing the button used to. Without
   this a chip product sits 12px shorter than the simple product beside it in the same row. */
.pt-shop .sg-choose{margin-top:12px;display:flex;flex-direction:column}
.pt-shop .sg-choose .sg-add{margin-top:0}
.pt-shop .sg-opts{display:flex;flex-direction:column;gap:8px}

/* NOT `.pt-chip`. The site's chip-row component is a FILTER row: it removes zero-count chips, grows a
   "More" past twelve and becomes a select on a phone. This row has to do the opposite on the one rule
   that matters — an unavailable size STAYS on screen, because people need to see it existed and has
   gone. Same tokens, same measurements, different job. Flagged to Leon rather than quietly forked. */
.pt-shop .sg-chips{display:flex;flex-wrap:wrap;gap:6px}
.pt-shop .sg-chip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--pt-border-strong);background:var(--pt-surface);color:var(--pt-ink);font:inherit;font-size:13px;line-height:1.2;padding:7px 12px;border-radius:0;cursor:pointer}
.pt-shop .sg-chip:hover{background:var(--pt-fill-control)}
.pt-shop .sg-chip.is-on{background:var(--pt-ink);border-color:var(--pt-ink);color:#fff}
.pt-shop .sg-chip.is-on:hover{background:var(--pt-ink)}
/* Muted and struck through, never hidden and never `disabled`: a disabled button is skipped in silence
   by a screen reader, which is the same as deleting it. `aria-disabled` keeps it reachable and audible. */
.pt-shop .sg-chip[aria-disabled="true"]{color:var(--pt-border-strong);border-color:var(--pt-border-strong);text-decoration:line-through;cursor:default}
.pt-shop .sg-chip[aria-disabled="true"]:hover{background:var(--pt-surface)}
.pt-shop .sg-chip:focus-visible{outline:2px solid var(--pt-accent);outline-offset:2px}
.pt-shop .sg-sw{width:12px;height:12px;flex:0 0 auto;border:1px solid var(--pt-border-strong)}
/* What a screen reader needs and the eye does not — the line through the chip already says it. */
.pt-shop .sg-gone-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.pt-shop .sg-cancel{align-self:flex-start;background:none;border:0;padding:0;font:inherit;font-size:12.5px;color:var(--pt-ink-3);text-decoration:underline;cursor:pointer}
.pt-shop .sg-cancel:hover{color:var(--pt-ink)}

/* ── the phone sheet (§B8) ──
   A 170px card cannot hold a chip row without it becoming a scrolling line of half-words. The SAME
   markup moves in here and back out again; a second copy of the chips is a second set of states to keep
   in step, and they only ever stay in step until the first change. */
.pt-shop .sg-vsheet{position:fixed;inset:0;z-index:1000}
.pt-shop .sg-vsheet[hidden]{display:none}
.pt-shop .sg-vsheet__bd{position:absolute;inset:0;background:rgba(26,26,26,.45)}
.pt-shop .sg-vsheet__panel{position:absolute;left:0;right:0;bottom:0;max-height:80vh;overflow:auto;padding:16px;background:var(--pt-surface);border-top:1px solid var(--pt-border)}
.pt-shop .sg-vsheet__head{display:flex;gap:12px;align-items:flex-start;margin-bottom:14px}
.pt-shop .sg-vsheet__img{width:56px;height:56px;flex:0 0 auto;object-fit:contain;background:var(--pt-surface-dim)}
.pt-shop .sg-vsheet__of{flex:1 1 auto;min-width:0}
.pt-shop .sg-vsheet__name{margin:0 0 6px;font-size:14px;font-weight:600;line-height:1.35}
/* The price in the sheet head is a heading, not the bottom of a card — it drops the card's rule and
   the auto margin that pushes it down one. */
.pt-shop .sg-vsheet__head .sg-price{margin-top:0;padding-top:0;border-top:0}
.pt-shop .sg-vsheet__x{flex:0 0 auto;background:none;border:0;padding:0 4px;font-size:24px;line-height:1;color:var(--pt-ink-3);cursor:pointer}
.pt-shop .sg-vsheet__body .sg-opts{gap:12px}
/* A full-width button at thumb height, per the brief. */
.pt-shop .sg-vsheet__body .sg-add{height:44px}

/* Seven demo products carry no price at all. Printing "£0.00" over them was a lie; printing nothing
   left a bordered empty strip that reads as a broken card. So the row keeps its silence and drops the
   divider it has nothing to divide. The products themselves are on Leon's cleanup list. */
.pt-shop .sg-price.is-none{border-top:0;padding-top:0;margin-top:auto;min-height:0}
