/* catalog.css — how a product card and the filter rail look, on /shop AND
   /build. One owner since Sept 13, 2026 (see catalog.js for why). Page-only
   styling (the cart drawer, the box tray) stays in each page. Every colour
   here falls back to a literal so a page that has not declared a variable
   still renders on-brand. */

/* 🔴 GLOBAL [hidden] guard. The HTML `hidden` attribute is only a UA
   `display:none`, so ANY author display rule outranks it -- .cols below sets
   display:block/grid, which once left 8 loading skeletons permanently on
   /shop (Aug 20-26, 2026). Written global on purpose. */
[hidden]{display:none !important;}

/* ── layout: rail + results ─────────────────────────────────────────────── */
.cols{display:block;}
@media(min-width:900px){ .cols{display:grid;grid-template-columns:224px minmax(0,1fr);gap:30px;align-items:start;} }

/* The rail is a working part of the page, so it is set in the UI sans rather
   than the body serif. */
#rail{background:var(--white,#fff);border:1px solid var(--rule,#eadbe1);border-radius:16px;padding:18px;margin:0 0 18px;
  font-family:var(--ui,system-ui,sans-serif);}
/* Parks under the header while it shows and at the top while it is away, the
   same --hdr-off every other sticky thing on the site uses. */
@media(min-width:900px){ #rail{position:sticky;top:calc(var(--hdr-off,57px) + 16px);transition:top .22s ease;margin:0;} }
.grp{margin-bottom:18px;}
.grp:last-child{margin-bottom:0;}
.lab{letter-spacing:.06em;font-size:11px;font-weight:700;color:var(--muted,#7a6470);margin:0 0 8px;}
.frow{display:flex;align-items:center;gap:9px;font-size:13.5px;color:var(--text,#3a2a32);padding:0;
  cursor:pointer;min-height:34px;width:100%;background:none;border:none;font-family:inherit;text-align:left;}
.frow.off{color:#b9aab1;cursor:default;}
.fbox{width:17px;height:17px;border-radius:4px;border:1.5px solid rgba(74,33,56,.28);background:#fff;
  flex:0 0 auto;display:flex;align-items:center;justify-content:center;}
.frow.on .fbox{background:var(--plum,#4a2138);border-color:var(--plum,#4a2138);}
.frow.off .fbox{border-color:rgba(74,33,56,.12);background:#f5efec;}
.fn{margin-left:auto;font-size:12px;color:var(--muted,#7a6470);opacity:.8;}
.railhead{display:flex;align-items:center;justify-content:space-between;padding-bottom:11px;
  margin-bottom:15px;border-bottom:1px solid var(--line,rgba(74,33,56,.11));}
.railhead b{font-family:var(--head,Georgia,serif);font-weight:700;font-size:19px;color:var(--plum,#4a2138);}
#reset{background:none;border:none;font-family:inherit;font-size:12.5px;color:var(--muted,#7a6470);cursor:pointer;
  text-decoration:underline;padding:6px;min-height:34px;}

/* ── the Filters button + phone sheet ───────────────────────────────────── */
#filtOpen{flex:0 0 auto;min-height:48px;padding:10px 20px;border-radius:999px;font-size:14px;
  font-family:var(--ui,system-ui,sans-serif);font-weight:700;color:#fff;background:var(--plum,#4a2138);
  border:none;cursor:pointer;white-space:nowrap;}
@media(max-width:899px){
  #rail{position:fixed;left:0;right:0;bottom:0;z-index:70;max-height:72vh;overflow-y:auto;
    background:var(--white,#fff);border-radius:18px 18px 0 0;padding:16px 18px 22px;
    box-shadow:0 -12px 34px rgba(74,33,56,.18);margin:0;
    transform:translateY(110%);transition:transform .22s ease;}
  #rail.open{transform:none;}
  .railclose{display:inline-flex;}
}
/* NO FLOATING FILTERS PILL. Tried Sept 16, 2026 (bottom centre, phones only)
   and rejected the same evening: on a two-column card grid a floating control
   ALWAYS lands on something, and at bottom centre it sat on the left card's Add
   button, the primary action. Same lesson nav.js records for the back-to-top.
   Each page keeps Filters reachable in its own always-visible bar instead:
   /shop makes the search row sticky under the header; /build puts a Filters
   button inside its sticky tray. */
#filtScrim{position:fixed;inset:0;background:rgba(58,42,50,.4);z-index:60;opacity:0;
  pointer-events:none;transition:opacity .2s ease;}
#filtScrim.on{opacity:1;pointer-events:auto;}
/* Geometry here; visibility below. This base rule must not carry display:none
   or it re-hides the button the <900px media rule just showed. */
.railclose{align-items:center;justify-content:center;width:36px;height:36px;
  border-radius:50%;border:1px solid var(--line,rgba(74,33,56,.11));background:var(--white,#fff);font-size:20px;
  color:var(--plum,#4a2138);cursor:pointer;}
@media(min-width:900px){ .railclose{display:none;} }
@media(prefers-reduced-motion:reduce){ #rail, #filtScrim{transition:none;} }

/* ── product grid + card ─────────────────────────────────────────────────── */
/* Two columns on a phone, measured: one column made the 118-product shelf
   three screens longer, and the card is legible at 170px wide. */
.pgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;}
@media(min-width:620px){ .pgrid{grid-template-columns:repeat(3,minmax(0,1fr));} }
@media(min-width:900px){ .pgrid{grid-template-columns:repeat(4,minmax(0,1fr));gap:18px;} }
.pcard{background:var(--white,#fff);border:1px solid var(--rule,#eadbe1);border-radius:16px;overflow:hidden;
  display:flex;flex-direction:column;}
.pcard.picked{border:2px solid var(--rose,#d46a8f);}
/* Out of stock dims the face and quietens the words. The row below it carries
   "Out of stock" in place of the price and no button at all: a dead button
   explains nothing. */
.pcard.pout .pthumb{opacity:.5;}
.pcard.pout .pbrand,.pcard.pout .pname{color:var(--muted,#6e5864);}
.pcard .add:disabled,.pcard .pick:disabled{opacity:1;background:#e9dfe4;color:#8a7580;cursor:not-allowed;white-space:nowrap;}
.pbody{padding:14px 16px 16px;display:flex;flex-direction:column;gap:6px;flex-grow:1;}
.pbrand{font-family:var(--ui,system-ui,sans-serif);font-weight:700;font-size:1rem;color:var(--plum,#4a2138);}
.pname{font-size:.95rem;color:var(--ink,#3a2a32);line-height:1.4;margin:0;max-width:none;}
.prow{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;
  margin-top:auto;padding-top:12px;}
.pprice{font-family:var(--ui,system-ui,sans-serif);font-weight:700;font-size:1.3rem;color:var(--plum,#4a2138);
  font-variant-numeric:lining-nums tabular-nums;}
.pcard.pout .pprice{font-size:.95rem;color:var(--muted,#6e5864);}
/* Only Premium is ever labelled: Standard and Deluxe cost a subscriber the
   same, and a badge nobody explains reads as a price difference. */
.ptier{font-family:var(--ui,system-ui,sans-serif);font-size:.8rem;font-weight:700;color:var(--muted,#6e5864);}
.ptier.premium{color:var(--line-statement,#8e2f56);}

/* The face. A real photo when one exists; otherwise the count set large, the
   format written out, and the absorbency word. Each format keeps its own soft
   tint so a grid of 90 cards reads as a sorted shelf, not one repeated tile. */
.pthumb{display:flex;align-items:baseline;flex-wrap:wrap;gap:0 6px;padding:18px 16px 14px;
  background:var(--cream2,#f7ebe4);border-bottom:1px solid rgba(74,33,56,.06);}
.pthumb.fmt-pad{background:#f7e3ea;}
.pthumb.fmt-tampon{background:#efe2ec;}
.pthumb.fmt-liner{background:#f6ece4;}
.pthumb.fmt-care{background:#e8ece3;}
.pthumb.fmt-cup,.pthumb.fmt-disc{background:#e6e1f3;}
.pthumb.fmt-incontinence{background:#e3e9f0;}
.pthumb.photo{display:block;background:#fff;padding:0;aspect-ratio:1/1;overflow:hidden;}
.pthumb img{display:block;width:100%;height:100%;object-fit:contain;}
.pcount{font-family:var(--ui,system-ui,sans-serif);font-weight:700;font-size:2.4rem;line-height:1;
  letter-spacing:-.03em;color:var(--plum,#4a2138);}
.punit{font-family:var(--ui,system-ui,sans-serif);font-weight:600;font-size:.9rem;color:var(--plum,#4a2138);}
.pmeta{margin-left:auto;display:flex;flex-direction:column;align-items:flex-end;text-align:right;
  font-family:var(--ui,system-ui,sans-serif);font-weight:600;font-size:.82rem;line-height:1.3;
  color:var(--plum,#4a2138);min-height:2.6em;}
.pkind{opacity:.85;}
.pfact{opacity:.6;}
.ptag{flex:0 0 100%;margin-top:8px;font-family:var(--ui,system-ui,sans-serif);font-weight:700;
  font-size:.75rem;color:var(--plum,#4a2138);opacity:.8;}

.skel{height:190px;border-radius:16px;background:linear-gradient(90deg,#f4ece8,#faf4f1,#f4ece8);}

/* ── TOUCH TARGETS ── keyed to a THUMB (pointer: coarse), not to width. */
@media (pointer: coarse) {
  .frow{min-height:44px;}
  #reset{min-height:44px;padding:10px 8px;}
}
