/* ═══════════════════════════════════════════════════════════════════
   Product grid and card

   One card, every surface: home rails, shop grid, search results.
   The 1:1 media box is load-bearing — the Store API publishes no
   intrinsic image dimensions, so nothing else reserves the space.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Product grid & cards ───────────────────────────────────────── */
/* Every row the same height, so cards line up down the page whatever
   the product name wraps to and whether or not a spec line is present.
   Measured before this: rows of 414, 436 and 446px in one grid. */
/* Rows size to their own content. `grid-auto-rows:1fr` made EVERY row as
   tall as the tallest card in the whole grid, so one power module with a
   SAVE badge and two rows of spec chips inflated a screwdriver four rows
   below it — and because .card-price pins to the bottom, all of that
   slack landed as one void between "In stock" and the price.
   Grid's default `align-items:stretch` still equalises the cards WITHIN a
   row, which is the alignment the eye actually compares. */
.prod-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-3)}
.card{
  position:relative;      /* the quick-view button anchors to THIS, not the page */
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  overflow:hidden;display:flex;flex-direction:column;min-width:0;height:100%;
  transition:border-color .15s,box-shadow .15s,transform .15s;
}
.card:hover{border-color:var(--brand);box-shadow:var(--shadow-md);transform:translateY(-2px)}
.card-media{position:relative;padding:var(--sp-2) var(--sp-2) 0}
/* The 1:1 box is load-bearing — the Store API publishes no intrinsic
   image dimensions, so nothing else reserves the space before decode.
   What changed is the GROUND: it used to be --surface-2, a visible grey
   tile, so a 500x404 photo showed its letterbox bars and read as a
   smaller image than the square one beside it. On the card's own white
   the bars are invisible and every tile looks the same size, which is
   as even as `contain` can be without cropping a component photo.
   A uniform inset stops photos that carry no built-in margin from
   looking larger than the ones that do. */
.media{
  aspect-ratio:1/1;background:var(--surface);border-radius:var(--r-sm);
  overflow:hidden;display:flex;align-items:center;justify-content:center;
  padding:var(--sp-2);
}
.media img{width:100%;height:100%;object-fit:contain}
.media .ph{width:38%;height:38%;color:var(--ink-3);stroke-width:1.2}
/* Every product image opens the zoomable lightbox — CLAUDE.md §9.4 */
.zoomable{cursor:zoom-in}
/* Badges sit in the body, never on the photograph: the images come from
   WooCommerce, many already carry the manufacturer's own text, and we
   cannot guarantee contrast against a picture we do not control. */
.badges{display:flex;flex-wrap:wrap;z-index:1;
  display:flex;flex-direction:column;gap:var(--sp-1)}
.badge{padding:3px var(--sp-2);border-radius:var(--r-sm);
  font-size:.75rem;font-weight:700;letter-spacing:.02em;width:fit-content;line-height:1.3}
.badge-save{background:var(--green);color:var(--on-fill)}
/* The urgency badge. Icon + word, not colour alone (§9.2), and never a
   quantity (§9.6) — "ALMOST GONE" is what WooCommerce's low-stock flag
   actually tells us. "Selling fast" would claim a velocity nobody here
   measures, which is the same class of untrue trust signal as §9.7's
   testing claim. */
.badge-low{background:var(--red);color:var(--on-fill);
  display:inline-flex;align-items:center;gap:4px}
.badge-low .ico{width:1.1em;height:1.1em;flex:none}
.badge-pick{background:var(--brand);color:var(--logo-ink)}
.badge-new{background:var(--ink-2);color:var(--on-fill)}
.card-body{padding:var(--sp-3);display:flex;flex-direction:column;
  gap:var(--sp-1);flex:1 1 auto;min-width:0}
.card-cat{font-size:.75rem;font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--brand-ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card-name{
  font-size:.9375rem;font-weight:600;color:var(--ink);line-height:1.35;
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;min-height:2.5em;overflow-wrap:anywhere;
}
.card-price{display:flex;align-items:baseline;gap:var(--sp-2);
  flex-wrap:wrap;padding-top:var(--sp-1)}
/* Only inside a grid card: it pins the price to the bottom so prices line
   up across a row. Anywhere else — quick view, cart row, hero pick — the
   same `auto` swallows the free space and opens a hole. */
.card-body .card-price{margin-top:auto}
.card-price .now{font-size:1.125rem;font-weight:800;color:var(--ink);
  font-variant-numeric:tabular-nums}
.card-price .was{font-size:.875rem;color:var(--ink-3);text-decoration:line-through;
  font-variant-numeric:tabular-nums}
.card-stock{display:flex;align-items:center;gap:var(--sp-1);
  font-size:.8125rem;font-weight:600;color:var(--green)}
.card-stock.low{color:var(--red)}
.card-stock .ico{width:18px;height:18px}
/* On a phone the label states the fact and stops: "Low stock". The
   "order soon" tail returns from 768px, where the row has room for it. */
.stock-more{display:none}
@media (min-width:768px){ .stock-more{display:inline} }
/* §9.12 — always visible; mobile has no hover. */
.card .btn{margin:0 var(--sp-3) var(--sp-3)}


/* ── Spec chips — the extracted electrical summary ────────────────
   Was one nowrap line with an ellipsis, which clipped 12 of 18 lines at
   360px and 4 even at 1280px — a 399px string in a 190px box. Chips
   wrap instead, so a tile shows the whole specification or grows a line;
   it never shows half a word. See specChips() in public/js/cards.js. */
.spec-chips{display:flex;flex-wrap:wrap;gap:4px;margin-top:var(--sp-1)}
.spec-chip{
  font-family:var(--font-mono);font-size:.75rem;line-height:1.35;
  color:var(--ink-2);background:var(--surface-2);
  border:1px solid var(--line-soft);border-radius:var(--r-sm);
  padding:2px 6px;white-space:nowrap;
}
/* The first chip is the topology — the fact that decides whether the
   part is even a candidate — so it carries the amber ink. Weight and
   colour, never colour alone (§9.2). */
.spec-chip:first-child{color:var(--brand-ink);font-weight:700}


@media (min-width:640px){ .prod-grid{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media (min-width:768px){ .prod-grid{grid-template-columns:repeat(4,minmax(0,1fr))} }
@media (min-width:900px){ .prod-grid{grid-template-columns:repeat(5,minmax(0,1fr))} }
@media (min-width:1200px){ .prod-grid{gap:var(--sp-4)} }
