/* catalogue.css — product listing: cards, swatches, qty stepper, filters
   (split from storefront.css, move-only). */

.detail-photo { aspect-ratio:1/1; background:var(--kraft); object-fit:contain; width:100%; border-radius:var(--radius-lg); }

/* product cards read as clickable/buyable */
.card--product { transition:transform .15s, box-shadow .15s; }
.card--product:hover { transform:translateY(-3px); box-shadow:0 12px 26px rgba(0,0,0,.13); }

/* card media + description overlay */
.card__media { position:relative; background:var(--green-tint); }
.card__desc { position:absolute; inset:0; padding:1rem; overflow:auto; font-size:.85rem;
  line-height:1.45; color:#fff;
  background:linear-gradient(rgba(43,43,40,.78), rgba(43,43,40,.92));
  opacity:0; visibility:hidden; transition:opacity .2s; }
.card__desc.on { opacity:1; visibility:visible; }
@media (hover:hover) { .card__media:hover .card__desc { opacity:1; visibility:visible; } }
.card__info-btn { position:absolute; top:.6rem; right:.6rem; z-index:2; width:28px; height:28px;
  border:0; border-radius:999px; background:rgba(250,247,242,.92); color:var(--green);
  font-family:var(--font-head); font-weight:700; cursor:pointer; box-shadow:var(--shadow); }

/* I-2 (reviewer, 2026-07-26): on a merged card, the title itself follows
   the selected handle (the two live names differ in length -- « Sac à
   Poignées Plates » vs « Sac à Poignées Torsadées »); a reserved 2-line box
   keeps the card body from reflowing when a pill tap swaps the text. `em`
   units so it scales with the mobile font-size override below. */
.card__title { display:block; line-height:1.25; min-height:2.5em; }

/* color swatches */
.swatch-row { display:flex; align-items:center; gap:.45rem; }
.swatch { width:26px; height:26px; border-radius:6px; border:2px solid transparent;
  cursor:pointer; padding:0; box-shadow:inset 0 0 0 2px #fff; }
.swatch.on { border-color:var(--green); }
.swatch--marron { background:#9a6a38; }
.swatch--blanc { background:#f6f1e7; box-shadow:inset 0 0 0 2px #fff, inset 0 0 0 3px var(--line); }
.swatch--noir { background:#2b2b28; }
.swatch--autre { background:var(--kraft); }
.swatch-hint { font-size:.8rem; color:var(--muted); font-weight:700; text-transform:capitalize; }

/* handle selector (poignées torsadées / plates / sans poignée) -- same
   on/off idiom as .swatch above, but a text pill: a handle isn't a colour,
   so it needs a legible French label, and (client note 5, 2026-07-25)
   >=44px touch target rather than the smaller swatch dot. */
.handle-row { display:flex; align-items:center; gap:.4rem; flex-wrap:wrap; }
.handle-btn { min-height:44px; padding:.4rem .85rem; border-radius:999px;
  border:2px solid var(--line); background:#fff; color:var(--ink);
  font-family:var(--font-head); font-weight:700; font-size:.8rem; cursor:pointer; }
.handle-btn.on { border-color:var(--green); background:var(--green-tint); color:#3f7a12; }
@media (max-width:640px) {
  .handle-btn { font-size:.74rem; padding:.35rem .7rem; }
}

/* quantity stepper -- owner ask (2026-08-17 follow-up): NOT reactive to
   what's typed ("rather than adapting to what the number is, make it the
   maximum it can be on screen, and just leave it like that"), but still
   responsive to the CARD's own size across breakpoints ("still needs to be
   dynamic and mobile friendly... based off the card itself, stretched to
   the maximum inside the card"). That's a CSS sizing question, not a
   digit-counting one: `.qty-row` always spans its container's full width
   (a plain block div, so this holds regardless of the ANCESTOR's own
   flex-direction -- the cart page's `.cart-line__actions` is a column flex
   context on desktop, a row on mobile; `width:100%` fills correctly either
   way, where `flex-grow` alone would not). Inside `.qty-row`, which is
   itself ALWAYS a row (display:flex, no direction override), `.qty-stepper`
   uses `flex:1 1 auto` to take whatever space `.qty-pack` doesn't need --
   the actual maximum available on that card, at that viewport, full stop.
   `.qty-stepper` still needs max-width:100% + min-width:0 to stay inside
   its card (a flex item defaults to min-width:auto, which is exactly what
   lets a growing child force its row to overflow instead of wrapping), and
   the input keeps a small min-width floor so a single digit doesn't look
   like an empty box. */
.qty-row { display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; width:100%; }
.qty-stepper { display:inline-flex; align-items:center; border:1px solid var(--line);
  border-radius:999px; overflow:hidden; max-width:100%; min-width:0; flex:1 1 auto; }
.qty-stepper button { width:36px; height:36px; border:0; background:var(--green-tint);
  color:var(--ink); font-size:1.1rem; font-weight:700; cursor:pointer; flex:none; }
.qty-stepper input { min-width:4ch; flex:1 1 auto; width:auto; border:0; text-align:center;
  font-family:var(--font-body); font-size:.95rem; -moz-appearance:textfield; }
.qty-stepper input::-webkit-outer-spin-button,
.qty-stepper input::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }

/* personalization pill */
.custom-pill { display:inline-flex; align-items:center; gap:.45rem; font-size:.85rem;
  font-weight:700; color:#3f7a12; background:var(--green-tint); padding:.35rem .7rem;
  border-radius:999px; cursor:pointer; align-self:flex-start; }

/* pack-of-1000 suffix next to quantity steppers */
.qty-pack { font-size:.85rem; font-weight:700; color:var(--muted); white-space:nowrap; }
.qty-hint { font-size:.72rem; color:var(--muted); }
.qty-trash { border:0; background:none; color:var(--muted); cursor:pointer; padding:.35rem;
  border-radius:8px; }
.qty-trash svg { width:20px; height:20px; fill:none; stroke:currentColor; stroke-width:1.7;
  stroke-linecap:round; stroke-linejoin:round; }
.qty-trash:hover { color:var(--action); background:var(--green-tint); }

/* catalogue header band + group filters */
.catalogue-head { background:var(--kraft); border-bottom:1px solid var(--line);
  padding:2rem 0 1.5rem; }
.catalogue-head .wrap { display:flex; flex-direction:column; align-items:flex-start;
  gap:.4rem; }
.catalogue-head__title { display:inline-flex; align-items:center; gap:.6rem; margin:0;
  font-size:clamp(1.6rem,3.4vw,2.4rem); color:var(--green); }
.catalogue-head__title svg { width:1.1em; height:1.1em; fill:currentColor; }
.catalogue-head__sub { margin:0; color:var(--muted); font-size:1rem; }
.catalogue-head__count { font-family:var(--font-head); font-weight:700; font-size:.78rem;
  color:#3f7a12; background:var(--green-tint); padding:.2rem .6rem; border-radius:999px; }

.filterbar { position:sticky; top:73px; z-index:20; background:var(--paper);
  border-bottom:1px solid var(--line); }
.filterbar__inner { display:flex; gap:.6rem; padding-top:.8rem; padding-bottom:.8rem;
  overflow-x:auto; scrollbar-width:none; }
.filterbar__inner::-webkit-scrollbar { display:none; }
.filter-btn { display:inline-flex; align-items:center; gap:.45rem; white-space:nowrap;
  font-family:var(--font-head); font-weight:700; font-size:.95rem; padding:.6rem 1.1rem;
  border-radius:999px; border:2px solid var(--green); color:var(--green); background:#fff;
  transition:transform .12s, background .12s, color .12s; }
.filter-btn svg { width:20px; height:20px; fill:currentColor; }
.filter-btn:hover { transform:translateY(-1px); background:var(--green-tint); }
.filter-btn.on { background:var(--green); color:#fff; }
@media (max-width:640px) {
  .catalogue-head { padding:1.5rem 0 1.1rem; }
  .filter-btn { font-size:.9rem; padding:.55rem .9rem; }
}

/* --- Mobile: two product cards per row (was one) ---
   Scoped to .grid--cards so the cart's own stepper/controls are untouched.
   At 375px this gives ~163px per card (~131px inner), so the stepper is
   tightened from 136px to 112px to fit with room to spare. */
@media (max-width:640px) {
  .grid--cards { grid-template-columns:repeat(2, 1fr); }
  .grid--cards .card__body { padding:.7rem; gap:.35rem; }
  .grid--cards .card__body strong { font-size:.95rem; }
  .grid--cards .price-tag { font-size:.85rem; }
  .grid--cards .qty-stepper button { width:34px; }
  .grid--cards .qty-stepper input { min-width:3ch; font-size:.9rem; }
  .grid--cards .swatch { width:22px; height:22px; }
  .grid--cards .swatch-hint { font-size:.72rem; }
  .grid--cards .custom-pill { font-size:.78rem; }
  .grid--cards .btn--action { font-size:.85rem; }
}

/* --- Personalization warning overlay (_size_card.html) ---
   Owner instruction (2026-07-26): the blocking « Personnalisation à partir de
   N sacs » warning is stated OVER the page, not appended inline below the
   fold where the tap that triggered it can't see it. z-index 60 clears the
   cart FAB (50), the offcanvas (40) and the sticky header (30), and stays
   under the first-paint loading screen (1000) so it can never cover it.
   `position:fixed` + `inset:0` means the dialog holds the viewport centre
   even if the page behind it scrolls; `overscroll-behavior:contain` keeps a
   scroll gesture inside the box from chaining to the page underneath. */
.perso-modal { position:fixed; inset:0; z-index:60; display:flex;
  align-items:center; justify-content:center; padding:1rem;
  background:rgba(43,43,40,.5); }
.perso-modal__box { width:100%; max-width:26rem; max-height:calc(100dvh - 2rem);
  overflow:auto; overscroll-behavior:contain; background:#fff;
  border:1px solid var(--line); border-radius:var(--radius); padding:1.15rem;
  box-shadow:0 18px 48px rgba(0,0,0,.28); display:flex;
  flex-direction:column; gap:.7rem; }
.perso-modal__title { margin:0; font-weight:800; font-size:1.05rem;
  color:var(--ink); }
/* The card grid shrinks its buttons at 640px (two cards per row); the dialog
   is full-width at any size, so it opts out of that tightening. */
@media (max-width:640px) {
  .grid--cards .perso-modal .btn--action,
  .grid--cards .perso-modal .btn--ghost { font-size:1rem; }
}

/* --- "Voir plus" load-more control (scalability round, 2026-08-17) --- */
.load-more-row { display:flex; justify-content:center; padding-top:0; }
/* `.btn--line` already exists, but only in manage.css, which the storefront
   never loads (same fact nudges.css hit first -- see its own comment on
   `.nudge-modal__second`). Same rule, so a bordered/outline button reads the
   same way here as it does on /gerer/'s own pager. */
.btn--line { background:#fff; border:1px solid var(--line); color:var(--ink); }
.btn--line:hover { filter:none; border-color:var(--green); color:var(--green); }
