/* ============================================================
   ZILVER JEWELRY — public site base styles
   Cartier-style luxury minimal: full-bleed photography, generous
   whitespace, serif headings + clean sans body. Palette is black/white
   plus ONE flat silver accent (#b0b0b0, no gradient/shine), used only as
   a thin decorative touch (the heading underline, the WhatsApp button's
   ring) — never a button or container/section background. No gold, no
   other bright colors, no drop shadows anywhere — flat and quiet.

   WHITE BACKGROUNDS, BLACK BORDERS: every container/card/section is
   white (or transparent over white) except the black footer — separation
   between them always comes from --border-hairline (a thin black border)
   on that white background, never a gray/beige fill. Buttons follow the
   same rule: white background, black border, black text, inverting to
   solid black on hover (.btn-outline / .btn-silver below) — no filled
   buttons apart from the plain black .btn base, which no actual button
   on the site uses unmodified.

   MOBILE-FIRST: every rule below is written for phone width first: this
   is where most visitors land (Instagram/TikTok links). Tablet/desktop
   overrides live in min-width media queries layered on top, never the
   other way around.
   ============================================================ */

:root {
  /* ---- Palette ---- */
  /* The ONE black used everywhere on the site — text, borders,
     backgrounds, buttons. Previously two near-identical but distinct
     values existed (#111111 here, plus a separate --color-text: #1a1a1a
     used for body copy/headings/card names/the sort select) — close
     enough to look the same at a glance but not actually the same pixel
     value, so they'd drift if either was ever tweaked alone. Consolidated
     to this single variable; --color-text was removed. (The nav-backdrop
     scrim below is rgba(17, 17, 17, ...) rather than a var() reference —
     CSS custom properties can't be interpolated inside rgba() without
     newer relative-color syntax not yet safe to rely on — but 17,17,17 is
     this exact value and must stay in sync with it by hand.) */
  --color-black:      #111111;
  --color-white:      #ffffff;
  /* Soft warm-gray panel fill — product photo containers only (shop
     cards + product.php's gallery), a Tiffany-style tinted backdrop for
     the image instead of a bordered box. Not used for anything else. */
  --color-panel:      #f5f5f3;
  --color-gray-100:   #ececea;
  --color-gray-300:   #d8d6d2;
  --color-gray-500:   #8a8781;
  --color-gray-700:   #4a4844;

  /* Flat silver — a thin decorative accent only (the heading underline,
     the WhatsApp float's ring), NOT used for buttons or container/section
     backgrounds — those follow the white-background/black-border rule
     (see --border-hairline above and .btn-outline/.btn-silver below)
     instead. No gradient/shine. --silver-accent-hover is the slightly
     darker shade for the WhatsApp ring's hover state; --silver-flat is a
     separate, slightly lighter gray for a few small hover-only text/
     border accents elsewhere (nav underline, footer link hover, ...). */
  --silver-accent: #b0b0b0;
  --silver-accent-hover: #999999;
  --silver-flat: #b8b8b8;

  /* ---- Typography ---- */
  --font-serif: 'Playfair Display', Georgia, 'Times New Roman', serif;
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;

  /* ---- Spacing scale ---- */
  --space-xs:  8px;
  --space-sm:  16px;
  --space-md:  24px;
  --space-lg:  40px;
  --space-xl:  64px;
  --space-2xl: 96px;

  /* ---- Layout ----
     1800px, not the old 1240px: on a common 1440/1920px desktop the page
     was leaving large empty margins either side instead of using the
     screen. At any viewport <=1800px this now fills the full width (just
     the padding below as edge space); wider than that it centers with a
     modest margin rather than stretching indefinitely. */
  --container-max: 1800px;
  /* Black, not silver: definition between sections/cards/containers comes
     from a thin black border on a white background everywhere on this
     site, not a background fill or a silver line. Silver is reserved for
     the occasional genuinely decorative accent (the heading underline,
     the WhatsApp button's ring) — see --silver-accent below. */
  --border-hairline: 1px solid var(--color-black);

  /* Minimum comfortable tap target (buttons, nav links, filter chips). */
  --touch-target: 44px;

  /* Deliberately no --radius / --shadow tokens: this design language is
     flat and square-cornered, not rounded-card or drop-shadow styled. */
}

/* ---- Reset ---- */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font-sans);
  color: var(--color-black);
  background: var(--color-white);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden; /* belt-and-braces: nothing in this stylesheet
    should ever force horizontal scroll on a phone, but this keeps a
    stray oversized element from breaking the whole page if one slips
    through, rather than the visitor scrolling sideways to find it. */
  /* --banner-height is set inline on <body> (includes/header.php) only
     while the promo banner is actually showing — var()'s own 0px
     fallback means this is a no-op on every other page/state, so the
     fixed .site-banner below never hides real page content underneath it. */
  padding-block-end: var(--banner-height, 0px);
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; margin: 0; padding: 0; }
button { font-family: inherit; cursor: pointer; }

/* ---- Typography ---- */
h1, h2, h3, h4 {
  font-family: var(--font-serif);
  font-weight: 600;
  line-height: 1.25;
  margin: 0 0 var(--space-sm);
}
h1 { font-size: clamp(1.9rem, 8vw, 3.4rem); }
h2 { font-size: clamp(1.5rem, 5vw, 2.2rem); }
h3 { font-size: 1.25rem; }
p { margin: 0 0 var(--space-sm); color: var(--color-gray-700); }

.eyebrow {
  display: block;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-gray-700);
  margin-bottom: var(--space-xs);
}

/* Small decorative underline for section headings — the "accent border
   on headings" use of the flat silver accent. Used as a standalone element
   under an <h2>, not baked into h2 itself (some headings, like the hero's
   h1, don't want it). */
.accent-bar {
  display: block;
  width: 56px;
  height: 3px;
  margin: var(--space-sm) auto 0;
  background: var(--silver-accent);
}
.section-heading { text-align: center; margin-bottom: var(--space-lg); }
.section-heading h2 { margin-bottom: 0; }

/* ---- Layout helpers ---- */
.container {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-sm);
}
@media (min-width: 768px) {
  .container { padding-inline: var(--space-lg); }
}

.section { padding-block: var(--space-xl); }

/* ---- Buttons ----
   .btn: solid black, base layout/type shared by every button (never used
   unmodified — every real button on the site is .btn-outline or
   .btn-silver).
   .btn-outline and .btn-silver: same outlined style — white background,
   black border, black text, inverting to solid black on hover. They're
   kept as two class names (some markup calls the primary CTA
   "btn-silver", secondary actions "btn-outline") but deliberately share
   one rule so they can never drift apart. No silver fill on buttons
   anywhere on the site — see --silver-accent's own comment below for
   where flat silver is still used (as a thin accent only, never a
   button or container background). */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-height: var(--touch-target);
  padding: 12px 28px;
  border: 1px solid var(--color-black);
  background: var(--color-black);
  color: var(--color-white);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  transition: background-color .2s ease, color .2s ease, border-color .2s ease;
}
.btn:hover { background: var(--color-white); color: var(--color-black); }

.btn-outline,
.btn-silver {
  background: var(--color-white);
  color: var(--color-black);
  border-color: var(--color-black);
}
.btn-outline:hover,
.btn-silver:hover {
  background: var(--color-black);
  color: var(--color-white);
}

/* ============================================================
   Site chrome: header / nav
   ============================================================ */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--color-white);
  border-bottom: var(--border-hairline);
}
/* Fixed height, not min-height: the header stays one clean, contained
   strip at every width regardless of logo size — the logo fits inside
   it with a bit of padding, it never stretches the bar taller. Sized up
   from 76px/104px alongside the third, deliberately larger logo bump
   below (54px mobile / 70px desktop, up from 40px/50px) so there's real
   breathing room around it instead of the logo nearly touching the
   header's edges. align-items: center here is what centers .site-logo,
   .main-nav and .lang-switch against each other — all three are direct
   children of this one flex row, so they share one alignment axis by
   construction, not three separate ones that could drift apart. */
.site-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 84px;
  padding-block: var(--space-xs);
}
@media (min-width: 768px) {
  .site-header-inner { height: 104px; }
}

/* No extra line-height/padding here that could nudge the image off the
   row's centerline — display:flex + align-items:center is the only
   thing positioning the <img> inside this wrapper, same mechanism as
   the parent .site-header-inner above. */
.site-logo { display: flex; align-items: center; line-height: 0; }
.site-logo img {
  /* Circular emblem with "ZILVER JEWELRY" text inside. Third size pass:
     40px/50px read as too small to be legible (see the conversation) —
     this is a deliberately large jump, not another small bump. If the
     wordmark still isn't clearly readable at this size, that's the
     signal to design a simplified mark instead, not to size up again. */
  height: 54px;
  width: auto;
  /* No filter/opacity/blend-mode here or anywhere else on this image —
     it renders at full source contrast, deliberately. These two hints
     just ask supporting browsers to avoid extra smoothing when scaling a
     thin line-art logo down, so it stays a bit sharper; not universally
     supported, and not a substitute for a properly-sized source image. */
  image-rendering: -webkit-optimize-contrast;
  image-rendering: crisp-edges;
}
@media (min-width: 768px) {
  .site-logo img { height: 70px; }
}

.main-nav {
  display: none;
  align-items: center;
  gap: var(--space-lg);
}
.main-nav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-target);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-bottom: 2px solid transparent;
}
.main-nav a:hover,
.main-nav a.active {
  border-bottom-color: var(--silver-flat);
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target);
  height: var(--touch-target);
  background: none;
  border: none;
  color: var(--color-black);
  flex-shrink: 0;
}
.nav-toggle svg { width: 26px; height: 26px; }

@media (min-width: 900px) {
  .main-nav { display: flex; }
  .nav-toggle { display: none; }
}

/* ---- Language switcher (FR/AR/EN) — always visible, mobile + desktop,
   so it sits directly in the header row rather than tucked away in the
   nav drawer. Deliberately more compact than the 44px touch-target
   guideline: a secondary, rarely-used control (picked once per visit),
   not a primary action like nav links or the WhatsApp button. ---- */
.lang-switch { display: flex; gap: 2px; flex-shrink: 0; }
.lang-switch a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  padding-inline: var(--space-xs);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--color-gray-500);
  border: 1px solid transparent;
}
.lang-switch a.active { color: var(--color-black); border-color: var(--color-gray-300); }
.lang-switch a:hover { color: var(--color-black); }

/* ---- Off-canvas mobile nav drawer ----
   Slides in from the right, full height, backdrop behind it. Flat panel
   (hairline border instead of a shadow, per the no-drop-shadow rule). */
.nav-backdrop {
  position: fixed;
  inset: 0;
  z-index: 190;
  background: rgba(17, 17, 17, .55);
  display: none;
}
.nav-backdrop.is-open { display: block; }

.mobile-drawer {
  position: fixed;
  inset-block: 0;
  inset-inline-end: 0;
  z-index: 200;
  width: 82%;
  max-width: 340px;
  background: var(--color-white);
  border-inline-start: var(--border-hairline);
  transform: translateX(100%);
  transition: transform .25s ease;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}
.mobile-drawer.is-open { transform: translateX(0); }

/* transform is always a PHYSICAL axis (no logical equivalent exists in
   CSS), so even though inset-inline-end above already repositions this
   panel to the correct (left) edge in RTL, the closed-state offset still
   has to be flipped explicitly to slide off in the right direction.
   Safe to leave unscoped to a breakpoint: this panel is display:none at
   >=900px regardless of its transform. */
html[dir="rtl"] .mobile-drawer { transform: translateX(-100%); }
html[dir="rtl"] .mobile-drawer.is-open { transform: translateX(0); }

.mobile-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-sm);
  border-bottom: var(--border-hairline);
}
/* Same logo, same full-contrast/no-filter + sharpening hints as
   .site-logo img above — this instance's size is set inline in
   includes/header.php (height: 32px), not here. */
.mobile-drawer-head img {
  image-rendering: -webkit-optimize-contrast;
  image-rendering: crisp-edges;
}
.mobile-drawer-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target);
  height: var(--touch-target);
  background: none;
  border: none;
  color: var(--color-black);
}
.mobile-drawer-close svg { width: 24px; height: 24px; }

.mobile-drawer-nav { display: flex; flex-direction: column; padding-block: var(--space-xs); }
.mobile-drawer-nav a {
  display: flex;
  align-items: center;
  min-height: var(--touch-target);
  padding-inline: var(--space-sm);
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  border-bottom: 1px solid var(--color-gray-100);
}

.mobile-drawer-foot {
  margin-top: auto;
  padding: var(--space-md) var(--space-sm);
  border-top: var(--border-hairline);
}
.mobile-drawer-foot .social-icons { justify-content: flex-start; }

@media (min-width: 900px) {
  .nav-backdrop, .mobile-drawer { display: none !important; }
}

/* ============================================================
   Site chrome: footer
   ============================================================ */
.site-footer {
  background: var(--color-black);
  color: var(--color-gray-300);
  padding-block: var(--space-xl) var(--space-lg);
  /* Real bottom padding on mobile so the floating WhatsApp button never
     sits on top of the last footer line. */
  padding-bottom: calc(var(--space-lg) + 72px);
}
@media (min-width: 900px) {
  .site-footer { padding-bottom: var(--space-lg); }
}
.footer-grid {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
}
@media (min-width: 768px) {
  .footer-grid { grid-template-columns: 2fr 1fr 1fr; }
}
/* Text wordmark, not the logo image: that file has an opaque white
   background (no transparency), which would show as an ugly white box
   on this black footer. Revisit once a transparent-background version
   of the logo exists. */
.footer-logo {
  font-family: var(--font-serif);
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--color-white);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: var(--space-sm);
}
.footer-heading {
  color: var(--color-white);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: var(--space-sm);
}
.footer-links li { margin-bottom: var(--space-xs); }
.footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-target);
}
.footer-links a:hover { color: var(--silver-flat); }

.social-icons { display: flex; gap: var(--space-sm); margin-top: var(--space-sm); }
.social-icons a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target);
  height: var(--touch-target);
  border: 1px solid var(--color-gray-700);
  color: var(--color-white);
}
.social-icons a:hover { border-color: var(--silver-flat); color: var(--silver-flat); }
.social-icons svg { width: 18px; height: 18px; }

/* Same icon row, for use on a white section background (e.g. contact.php)
   instead of the footer's dark one — the default .social-icons a color is
   white, which disappears on white. */
.social-icons.on-light { justify-content: center; }
.social-icons.on-light a { border-color: var(--color-black); color: var(--color-black); }
.social-icons.on-light a:hover { border-color: var(--silver-flat); color: var(--silver-flat); }

.footer-bottom {
  margin-top: var(--space-xl);
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-gray-700);
  font-size: 0.75rem;
  color: var(--color-gray-500);
  text-align: center;
}

/* ---- Floating WhatsApp button ----
   Solid black disc with a flat silver ring (plain border — the accent is
   a flat color now, so the old two-background-layer gradient-border
   trick isn't needed here anymore). */
.whatsapp-float {
  position: fixed;
  /* Logical, not physical: in RTL (Arabic) inset-inline-end resolves to
     the LEFT edge, so the button correctly moves there instead of
     staying pinned to the physical right. */
  inset-inline-end: var(--space-sm);
  /* Same --banner-height fallback as body's own padding-block-end above —
     sits at the usual space-sm off the viewport bottom normally, and
     lifts by exactly the banner's height while it's showing, so the two
     never overlap regardless of what height an admin configures. */
  bottom: calc(var(--banner-height, 0px) + var(--space-sm));
  z-index: 150;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 2px solid var(--silver-accent);
  background-color: var(--color-black);
  color: var(--color-white);
}
.whatsapp-float:hover { background-color: var(--color-white); border-color: var(--silver-accent-hover); color: var(--color-black); }
.whatsapp-float svg { width: 26px; height: 26px; }

/* ---- Fixed/sticky promo banner (site_settings.php's "Bannière" tab) ----
   Sits below the WhatsApp float (z-index 150) and well below the nav
   drawer/backdrop (190/200) — a full-width bar at the very bottom of the
   viewport, not a modal overlay, so it should never compete with those.
   --banner-height is set inline on <body> only while showing (see
   body's own comment above) and read here too, so the two stay in sync
   from one single source of truth instead of two separate PHP values
   that could drift apart. */
.site-banner {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 140;
  height: var(--banner-height, 48px);
  background: var(--color-black);
  color: var(--color-white);
  overflow: hidden;
}
.site-banner-track {
  position: relative;
  height: 100%;
}
.site-banner-message {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding-inline: var(--space-md);
  opacity: 0;
  transition: opacity .5s ease;
  /* Only one message is ever meant to be interactive/announced at a
     time — the rest sit fully transparent underneath it (never
     display:none, so the crossfade has something to fade FROM), but
     without this they'd still be reachable by keyboard/screen reader
     while invisible. */
  visibility: hidden;
}
.site-banner-message.is-active { opacity: 1; visibility: visible; }
.site-banner-icon {
  width: calc(var(--banner-height, 48px) - var(--space-md));
  height: calc(var(--banner-height, 48px) - var(--space-md));
  object-fit: contain;
  flex-shrink: 0;
}
.site-banner-text {
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

@media (max-width: 480px) {
  /* Narrow phones: the icon (already capped to the banner's own height)
     can crowd out the text at a typical 40-48px banner height — drop it
     rather than let the text clip to nothing between two fixed-width
     neighbors. */
  .site-banner-icon { display: none; }
  .site-banner-text { font-size: 0.78rem; }
}

/* ============================================================
   Home hero (index.php) — simple centered text block on plain white,
   back to the original treatment (no split layout, no image column —
   see the conversation for why that was tried and reverted: this
   site's own rule is white backgrounds everywhere, no large fills).
   padding-block replaces the original's 78vh min-height + flex-centered
   approach, which produced far more empty space above "ZILVER JEWELRY"
   and below the buttons than the content needed; this is roughly half
   that — a deliberately tighter section so the page doesn't read as
   dead space before "Pièces à l'honneur" begins.
   ============================================================ */
.hero {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: var(--color-white);
  border-bottom: var(--border-hairline);
  padding-block: var(--space-xl);
}
.hero-content { max-width: 640px; padding-inline: var(--space-sm); }
.hero-content p {
  color: var(--color-gray-700);
  font-size: 1rem;
  margin-bottom: var(--space-md);
}
.hero-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.hero-actions .btn { width: 100%; }
@media (min-width: 480px) {
  .hero-actions { flex-direction: row; flex-wrap: wrap; justify-content: center; }
  .hero-actions .btn { width: auto; }
}

/* Generic placeholder block, used until real photography/content exists. */
.placeholder-block {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40vh;
  background: var(--color-white);
  border: var(--border-hairline);
  color: var(--color-gray-500);
  font-size: 0.85rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: center;
  padding: var(--space-md);
}

/* ============================================================
   Product grid (shop.php) + product card (includes/product-card.php).
   Mobile-first: 2 columns from phone width up (tight gutter, nearly
   edge-to-edge), 3 from tablet up — permanently, even at wide desktop
   (1200-1600px+ and beyond). Extra width on a bigger screen goes into
   making each of the 3 cards larger (bigger photo, more breathing room),
   not into cramming a 4th column in.
   ============================================================ */
.product-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-xs);
  /* Breaks out of .container's normal mobile padding (--space-sm, 16px
     each side) and re-applies a smaller one, so the 2-column grid reads
     as nearly edge-to-edge with just a small consistent gutter — same
     idea as Tiffany's mobile catalog. Only the grid itself does this;
     the heading/toolbar above it keep .container's normal padding. */
  margin-inline: calc(var(--space-sm) * -1);
  padding-inline: var(--space-xs);
}
@media (min-width: 768px) {
  .product-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-lg);
    /* Tablet/desktop: use .container's own (already wider) padding as
       the page edge — no breakout needed here. */
    margin-inline: 0;
    padding-inline: 0;
  }
}

/* No border/outline on the card itself — it "floats" on the page, with
   only the grid gutter (.product-grid's gap) separating it from its
   neighbors, no visible line between cards. The COMMANDER button below
   keeps its own outlined black border regardless (see .btn-outline /
   .btn-silver) — this only removes the border that used to wrap the
   photo/card, not the button's. */
.product-card {
  display: flex;
  flex-direction: column;
  background: var(--color-white);
}
/* Plain positioning context for the thumbnail overlay below — no visual
   styling of its own, so a single-photo card (no .product-card-thumbs
   inside it) renders identically to before this existed. */
.product-card-photo-wrap { position: relative; touch-action: pan-y; }
.product-card-photo {
  display: block;
  aspect-ratio: 1 / 1;
  background: var(--color-panel);
  overflow: hidden;
}
.product-card-photo img { width: 100%; height: 100%; object-fit: cover; }

/* ---- Grid-card thumbnail overlay (shop.php/index.php cards with more
   than one photo — see render_product_card()'s own comment) — small
   28x28px squares in the photo's bottom-left corner, always visible/
   tappable (not hover-only: this has to work by tap on mobile as the
   primary method, same as a click on desktop). box-shadow (not a
   border) keeps each thumbnail legible against any photo, light/dark
   alike, without a visible border ring around it.

   Tap target: the VISUAL image stays a small 28px square (doesn't
   dominate the card), but .product-card-thumb itself is a wider 44px
   box (Apple/Google's own minimum recommended touch-target size) with
   the image centered inside it via flex, so there's real invisible
   margin around each thumbnail for an imprecise real-finger tap to
   still land — a 28px hitbox next to another tappable element (the
   card's own photo link) is genuinely too tight for reliable mobile
   tapping, independent of anything about the click-handling JS itself.

   Active state: previously a dim dot on every thumbnail (gray) vs. a
   dark one (black) on the active thumbnail — two-tone comparisons like
   that read ambiguously at 4px, especially in a screenshot; simplified
   to presence/absence instead — the dot is only ever drawn at all under
   the active thumbnail, nothing to compare against. */
.product-card-thumbs {
  position: absolute;
  inset-block-end: 6px;
  inset-inline-start: 6px;
  z-index: 2;
  display: flex;
  gap: 2px;
}
.product-card-thumb {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  /* Physical width first, logical inline-size second (deliberately in
     this order — see .product-card-thumb img below for why): a fixed
     pixel size either way, never sized off content. */
  width: 44px;
  min-height: 44px;
  inline-size: 44px;
  min-block-size: 44px;
  flex-shrink: 0;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}
.product-card-thumb img {
  /* width/height declared BEFORE inline-size/block-size on purpose: a
     browser that understands the logical pair uses those (and mirrors
     correctly for this site's RTL Arabic pages, same as every other
     inset-inline-*/inline-size rule in this file); one that doesn't
     simply ignores lines it can't parse and keeps the physical
     width/height instead of falling through to the image's own
     intrinsic size. Either way this thumbnail is a fixed 28x28px, never
     sized off the source photo's real dimensions — max-width/max-height
     plus overflow:hidden as a second layer of the same guarantee. */
  width: 28px;
  height: 28px;
  inline-size: 28px;
  block-size: 28px;
  max-width: 28px;
  max-height: 28px;
  overflow: hidden;
  object-fit: cover;
  display: block;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}
.product-card-thumb-dot {
  inline-size: 4px;
  block-size: 4px;
  border-radius: 50%;
  background: transparent;
}
.product-card-thumb.active .product-card-thumb-dot { background: var(--color-black); }

.product-card-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-xs);
  flex: 1;
}
.product-card-name {
  font-family: var(--font-serif);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--color-black);
  /* Clamps to 2 lines so card heights stay consistent across a grid row
     regardless of name length. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Price (Prix de vente + Frais supplémentaire — public_display_price(),
   only rendered at all when visibility resolves to true, so no ": —" /
   placeholder ever shows in its place when it's hidden) and the ref/
   weight/taille metadata now share this ONE line instead of two, pushed
   to just above the CTA button via margin-top:auto on the shared
   container (previously on whichever of the two happened to render
   first) so the button still lands at a consistent height across a grid
   row regardless of whether the card's name wrapped to one or two lines.
   flex-wrap: if the combined text doesn't fit one line on a narrow card,
   it drops the metadata to a natural second line rather than overflowing
   — still one visual "block", never truncated or clipped. */
.product-card-line {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 var(--space-xs);
  margin-top: auto;
  padding-top: var(--space-xs);
}
.product-card-amount { font-weight: 700; font-size: 1rem; }
/* Renamed from .product-card-price — see includes/product-card.php's own
   comment on why (that name predates this line even having a price on
   it, and nothing outside that one file ever referenced it). Deliberately
   lighter/smaller than .product-card-amount so the price still visually
   leads despite sharing the line. */
.product-card-meta { font-weight: 600; font-size: 0.78rem; color: var(--color-gray-500); }
.product-card-line-sep { color: var(--color-gray-300); font-size: 0.78rem; }
/* Keeps the 44px touch-target height from .btn — only the horizontal
   padding/font-size shrink so "COMMANDER" fits a narrow mobile column
   without wrapping or overflowing. */
.product-card-cta { width: 100%; margin-top: var(--space-xs); padding-inline: var(--space-xs); font-size: 0.72rem; }

@media (min-width: 768px) {
  .product-card-body { padding: var(--space-sm); }
  .product-card-name { font-size: 1.05rem; }
  .product-card-meta { font-size: 0.85rem; }
  .product-card-line-sep { font-size: 0.85rem; }
  .product-card-amount { font-size: 1.1rem; }
  .product-card-cta { padding-inline: 28px; font-size: 0.85rem; }
}

/* ============================================================
   Shop toolbar (count + sort + mobile "Filtrer" toggle)
   ============================================================ */
.shop-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}
.shop-count { font-size: 0.85rem; color: var(--color-gray-700); }
.shop-toolbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-inline-start: auto;
}
.filter-toggle-btn {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-target);
  padding-inline: var(--space-sm);
  border: 1px solid var(--color-black);
  background: none;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
@media (min-width: 1024px) {
  .filter-toggle-btn { display: none; }
}
.sort-select {
  min-height: var(--touch-target);
  padding-inline: var(--space-sm);
  border: 1px solid var(--color-gray-300);
  background: var(--color-white);
  font-size: 0.85rem;
  font-family: inherit;
  color: var(--color-black);
}

/* ============================================================
   Shop layout: filters (off-canvas drawer on mobile/tablet, a static
   sidebar from 1024px up — same underlying element either way) + results.
   ============================================================ */
.shop-layout {
  display: flex;
  flex-direction: column;
}
.shop-filters {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  z-index: 200;
  width: 82%;
  max-width: 320px;
  background: var(--color-white);
  border-inline-end: var(--border-hairline);
  transform: translateX(-100%);
  transition: transform .25s ease;
  overflow-y: auto;
  padding: var(--space-md);
}
.shop-filters.is-open { transform: translateX(0); }

/* Same physical-transform caveat as .mobile-drawer above, but this panel
   DOES have a real desktop (static, transform:none) mode, so the RTL
   override must stay scoped to the same max-width the fixed/off-canvas
   behavior applies in — otherwise its extra attribute-selector
   specificity would win over the desktop rule below and leave a stray
   transform on what should be a plain static sidebar at >=1024px. */
@media (max-width: 1023px) {
  html[dir="rtl"] .shop-filters { transform: translateX(100%); }
  html[dir="rtl"] .shop-filters.is-open { transform: translateX(0); }
}

.shop-filters-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-md);
}

.filter-group { margin-bottom: var(--space-lg); }
.filter-group-label {
  display: block;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-gray-500);
  margin-bottom: var(--space-sm);
}
.filter-chip-list { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.filter-chip {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-target);
  padding-inline: var(--space-sm);
  border: 1px solid var(--color-gray-300);
  font-size: 0.85rem;
}
.filter-chip.active { background: var(--color-black); color: var(--color-white); border-color: var(--color-black); }
.filter-chip:hover:not(.active) { border-color: var(--silver-flat); }

/* ---- Poids/Taille range filters (min/max inputs, not a slider — kept
   as plain bordered number fields to match .sort-select/.filter-chip's
   existing flat, no-accent-color look rather than adding a custom-styled
   track/thumb component). ---- */
/* One shared <form> holds both the Poids and Taille .filter-group blocks
   plus their single submit button — block layout (each .filter-group's
   own margin-bottom already spaces them apart), not a row: only
   .filter-range-inputs itself (the Min/Max pair within one group) is a
   horizontal flex row. */
.filter-range-inputs { display: flex; align-items: center; gap: var(--space-xs); }
/* Chrome-specific flexbox + <input type="number"> quirk: a flex item's
   default min-width is `auto`, which resolves to its CONTENT's intrinsic
   width (not 0) — for a number input that includes the native spinner
   arrows, and Chrome factors those into that intrinsic width
   differently depending on the field's actual min/max/value/placeholder
   digits. Poids (wider values like "200") happened to compute a content
   width Chrome could still fit two-up; Taille's narrower values (e.g.
   both bounds "8") computed differently and wrapped — even though the
   HTML/CSS driving both fields is otherwise identical. Firefox doesn't
   have this default-min-width-from-content behavior for number inputs,
   which is why it rendered both consistently already. Two fixes,
   applied together so content-based sizing can never re-enter the
   picture at all:
   1. min-width: 0 overrides the `auto` default outright — a flex item's
      own explicit width (below) now wins regardless of content.
   2. flex: 0 0 5.5rem (replacing a plain `width`) pins both the
      flex-basis AND flex-shrink to fixed values — Chrome can't
      negotiate this input a different size based on its neighbor or
      its own content the way `width` alone still permitted it to try.
   Spin buttons hidden entirely (both engines' vendor prefixes) rather
   than just narrowed — removes the variable-width factor as a source of
   this class of bug for good, not just for this one symptom; the site
   already provides its own up/down via the OS-native number keyboard
   input mode (inputmode="decimal") and manual typing, so the spinner
   arrows weren't adding anything besides this risk. */
.filter-range-inputs input[type="number"] {
  min-height: var(--touch-target);
  min-width: 0;
  flex: 0 0 5.5rem;
  padding-inline: var(--space-xs);
  border: 1px solid var(--color-gray-300);
  background: var(--color-white);
  color: var(--color-black);
  font-family: inherit;
  font-size: 0.85rem;
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}
.filter-range-inputs input[type="number"]::-webkit-inner-spin-button,
.filter-range-inputs input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.filter-range-inputs input[type="number"]:focus { border-color: var(--color-black); outline: none; }
.filter-range-inputs input[type="number"]::placeholder { color: var(--color-gray-500); }
.filter-range-sep { color: var(--color-gray-500); }
.filter-range-form .btn { margin-top: var(--space-xs); }

.shop-results { min-width: 0; }

.empty-state {
  text-align: center;
  padding: var(--space-2xl) var(--space-md);
  color: var(--color-gray-700);
}
.empty-state .btn { margin-top: var(--space-sm); }

/* ---- Infinite scroll loading indicator (shop.php, js/main.js's
   initShopInfiniteScroll()) — shown below the grid while the next batch
   is being fetched; hidden entirely once shop_items.php reports no
   further batch (hasMore: false) or there was only ever one batch to
   begin with (shop.php's own $has_more, see that file's comment). ---- */
.shop-load-more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: var(--space-lg) 0;
  color: var(--color-gray-700);
  font-size: 0.85rem;
}
.spinner {
  inline-size: 18px;
  block-size: 18px;
  border: 2px solid var(--color-gray-300);
  border-block-start-color: var(--color-black);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Unlike .is-hidden (display:none) below, this keeps the element's layout
   box in place — js/main.js's IntersectionObserver is watching #shopLoadMore
   itself, and needs its real position even while nothing is actively being
   fetched (waiting for the next scroll trigger), or it could never detect
   the sentinel is already back in view. */
.shop-load-more.is-idle { visibility: hidden; }

.is-hidden { display: none !important; }

@media (min-width: 1024px) {
  .shop-layout { flex-direction: row; align-items: flex-start; gap: var(--space-xl); }
  .shop-filters {
    position: static;
    inset: auto;
    z-index: auto;
    width: auto;
    max-width: none;
    border-inline-end: none;
    transform: none;
    transition: none;
    overflow-y: visible;
    padding: 0;
    flex: 0 0 260px;
  }
  .shop-filters-head { display: none; }
  .shop-results { flex: 1; }
}

/* ============================================================
   Product detail (product.php)
   ============================================================ */
.product-detail {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}
/* .product-gallery is just the flex-column wrapper now — the actual
   photo box (light panel fill, matching the shop cards — see
   --color-panel's comment at the top of the file) moved to
   .product-gallery-main so .product-gallery-thumbs/-dots can sit below
   it without inheriting the aspect-ratio/padding meant for the photo
   itself. */
.product-gallery { display: flex; flex-direction: column; gap: var(--space-xs); }
.product-gallery-main {
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-panel);
  padding: var(--space-md);
  overflow: hidden;
  /* Only a horizontal swipe is treated as a gallery gesture (see
     initProductGallery() in js/main.js) — pan-y keeps normal vertical
     page scrolling working over the photo on mobile instead of the
     browser waiting to see if this listener will call
     preventDefault(). */
  touch-action: pan-y;
}
.product-gallery-empty {
  color: var(--color-gray-500);
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: center;
}
/* contain, not cover: this is the one large hero photo a visitor is
   deciding to buy from, so the full piece should always be visible,
   never cropped — unlike the grid cards, which crop to a tidy uniform
   square on purpose. */
.product-gallery-main img { width: 100%; height: 100%; object-fit: contain; }

/* ---- Thumbnail strip (desktop/tablet — >=900px, see the media query
   below) and dot indicator (phone — swap on swipe, see js/main.js).
   Both only ever render in the markup when there's more than one photo
   (product.php's own count($photos) > 1 check) — a single-photo item
   has neither in the DOM at all, not just hidden via CSS. ---- */
.product-gallery-thumbs { display: none; gap: var(--space-xs); flex-wrap: wrap; }
.product-gallery-thumb {
  inline-size: 64px;
  block-size: 64px;
  flex-shrink: 0;
  padding: 2px;
  background: var(--color-white);
  border: 1px solid var(--color-gray-300);
  cursor: pointer;
}
.product-gallery-thumb.active { border-color: var(--color-black); border-width: 2px; }
.product-gallery-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.product-gallery-dots { display: flex; justify-content: center; gap: var(--space-xs); }
.product-gallery-dot {
  inline-size: 6px;
  block-size: 6px;
  border-radius: 50%;
  background: var(--color-gray-300);
}
.product-gallery-dot.active { background: var(--color-black); }

@media (min-width: 900px) {
  .product-gallery-thumbs { display: flex; }
  .product-gallery-dots { display: none; }
}

.product-info { display: flex; flex-direction: column; }
.product-info h1 { margin-bottom: var(--space-xs); }
.product-price { font-size: 1.4rem; font-weight: 700; margin-bottom: var(--space-sm); }
.product-order-cta { width: 100%; font-size: 0.95rem; padding-block: 16px; margin-bottom: var(--space-md); }

.product-meta { margin-bottom: var(--space-md); }
.product-meta-row {
  display: flex;
  justify-content: space-between;
  padding-block: var(--space-xs);
  border-bottom: 1px solid var(--color-gray-100);
  font-size: 0.9rem;
}
.product-meta-row span:first-child { color: var(--color-gray-500); }

.back-to-shop {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--touch-target);
  margin-top: var(--space-sm);
  font-size: 0.85rem;
  font-weight: 600;
  align-self: flex-start;
}
.back-to-shop:hover { color: var(--silver-flat); }

@media (min-width: 900px) {
  .product-detail { flex-direction: row; align-items: flex-start; gap: var(--space-2xl); }
  .product-gallery { flex: 1 1 50%; }
  /* top matches the header's fixed 76px height plus a small margin, so
     this sticky panel clears it instead of tucking underneath. */
  .product-info { flex: 1 1 50%; position: sticky; top: 84px; }
}

/* ============================================================
   Contact page — store address / map (contact.php)
   ============================================================ */
.map-embed {
  aspect-ratio: 16 / 9;
  max-width: 720px;
  margin-inline: auto;
  background: var(--color-white);
  border: var(--border-hairline);
}
@media (min-width: 768px) {
  .map-embed { aspect-ratio: 21 / 9; }
}

/* ============================================================
   Digital business card (contact-card.php)
   ============================================================ */
.card-logo {
  display: block;
  height: 40px;
  width: auto;
  margin: 0 auto var(--space-lg);
}
.qr-card {
  max-width: 320px;
  margin-inline: auto;
  padding: var(--space-lg) var(--space-md);
  background: var(--color-white);
  border: var(--border-hairline);
  text-align: center;
}
.qr-card canvas { width: 100%; height: auto; display: block; }
.qr-card-instruction {
  margin-top: var(--space-sm);
  margin-bottom: 0;
  font-size: 0.9rem;
  color: var(--color-gray-700);
}
.qr-card-download { margin-top: var(--space-md); }

/* ---- Services list (repairs.php) — a plain list, not sequential steps.
   Stacked with horizontal dividers on narrow screens; becomes a 3-column
   row with vertical dividers at desktop width, echoing the spacing/scale
   of the old numbered-steps layout without implying a sequence. ---- */
.service-list {
  display: flex;
  flex-direction: column;
  max-width: 820px;
  margin-inline: auto;
}
.service-list li {
  padding: var(--space-md) var(--space-sm);
  text-align: center;
  color: var(--color-gray-700);
  font-size: 1.05rem;
  line-height: 1.5;
  border-bottom: 1px solid var(--color-gray-300);
}
.service-list li:last-child { border-bottom: none; }
@media (min-width: 768px) {
  .service-list { flex-direction: row; align-items: stretch; }
  .service-list li {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border-bottom: none;
    border-left: 1px solid var(--color-gray-300);
  }
  .service-list li:first-child { border-left: none; }
}
