/* ==========================================================================
   BAOBAY — système de design
   --------------------------------------------------------------------------
   Feuille unique, mobile d'abord, élargie sur grand écran. Les jetons de
   marque (--brand*, --on-brand) sont injectés par page depuis
   app/services/theme.py : les valeurs ci-dessous ne sont que le repli quand
   aucune boutique n'est en contexte. Ne pas y coder de couleur de marque en
   dur — c'est ce qui laissait des boutons verts sur les thèmes personnalisés.
   ========================================================================== */

:root {
  /* --- Marque (remplacée à l'exécution, voir theme.py) --- */
  --brand: #16824c;
  --brand-600: #11683d;
  --brand-700: #0c4b2c;
  --brand-ink: #0c4b2c;
  --brand-100: #d3ebde;
  --brand-050: #ecf6f1;
  --brand-light: #ecf6f1;
  --on-brand: #ffffff;
  --accent: var(--brand);
  --grad: linear-gradient(135deg, #1a9457 0%, #0f5e37 100%);

  /* --- Neutres --- */
  --bg: #f4f7f5;
  --surface: #ffffff;
  --surface-2: #f8faf9;
  --text: #101a15;
  --text-2: #47564f;
  --muted: #71847b;
  --border: #e4ebe7;
  --border-strong: #d2ddd7;

  /* --- Couleurs sémantiques --- */
  --pos: #16a34a;
  /* Chaque paire atteint 4.5:1, seuil de lisibilité du texte courant : l'ambre
     et le gris étaient sous le seuil, or l'ambre porte l'avis d'échéance
     d'abonnement, le message qu'il faut justement pouvoir lire. */
  --amber-bg: #fdf0d8; --amber-fg: #996206;
  --blue-bg: #e4edfd;  --blue-fg: #1f56bd;
  --green-bg: #d8f3e3; --green-fg: #0f7a46;
  --red-bg: #fce4e4;   --red-fg: #c22f2f;
  --gray-bg: #eef2f0;  --gray-fg: #607068;
  --wa: #25d366;

  /* --- Échelle d'espacement (base 4px) --- */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 20px; --s6: 24px; --s8: 32px; --s10: 40px;

  /* --- Rayons --- */
  --r-sm: 10px; --r-md: 14px; --r-lg: 18px; --r-xl: 24px; --r-full: 999px;
  --radius: var(--r-lg);        /* alias hérité */
  --radius-sm: var(--r-md);     /* alias hérité */

  /* --- Élévation : plusieurs couches faibles plutôt qu'un flou unique,
         ce qui donne un contact net au sol et une ombre plus crédible. --- */
  --shadow-xs: 0 1px 2px rgba(12, 40, 28, .06);
  --shadow: 0 1px 2px rgba(12, 40, 28, .05), 0 4px 12px -2px rgba(12, 40, 28, .06);
  --shadow-lg: 0 2px 4px rgba(12, 40, 28, .05), 0 12px 28px -6px rgba(12, 40, 28, .14);
  --shadow-xl: 0 4px 8px rgba(12, 40, 28, .06), 0 24px 48px -12px rgba(12, 40, 28, .20);
  --ring: 0 0 0 3px color-mix(in srgb, var(--brand) 28%, transparent);

  /* --- Mouvement : sortie franche, retour amorti. --- */
  --ease: cubic-bezier(.32, .72, 0, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --fast: .14s; --base: .22s;

  --maxw: 480px;
  --nav-h: 62px;
  color-scheme: light dark;
  font-size: 16px;
}

@media (min-width: 768px)  { :root { --maxw: 700px; } }
@media (min-width: 1120px) { :root { --maxw: 880px; } }
/* Au-delà, la largeur utile dépend de la page : une vitrine s'étale, un
   formulaire non. Les valeurs sont posées par gabarit dans « ORDINATEUR ». */

/* --- Mode sombre : seuls les neutres changent ici, les jetons de marque
       sont recalculés côté serveur pour chaque boutique. --- */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1412;
    --surface: #171d1a;
    --surface-2: #1d2522;
    --text: #e8efeb;
    --text-2: #b3c2ba;
    --muted: #8b9d94;
    --border: #263029;
    --border-strong: #33413a;

    --amber-bg: #3a2c10; --amber-fg: #f0be6a;
    --blue-bg: #16264a;  --blue-fg: #9dbcf5;
    --green-bg: #12331f; --green-fg: #6fd79b;
    --red-bg: #3b1b1b;   --red-fg: #f09a9a;
    --gray-bg: #232c28;  --gray-fg: #9bada4;

    --shadow-xs: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 4px 12px -2px rgba(0, 0, 0, .5);
    --shadow-lg: 0 2px 4px rgba(0, 0, 0, .4), 0 12px 28px -6px rgba(0, 0, 0, .6);
    --shadow-xl: 0 4px 8px rgba(0, 0, 0, .5), 0 24px 48px -12px rgba(0, 0, 0, .7);
  }
}

/* ==========================================================================
   Base
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
               "Helvetica Neue", "Noto Sans", Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-synthesis-weight: none;
  overflow-x: hidden;
}

a { color: var(--brand-600); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }
img, svg, video { max-width: 100%; display: block; }

h1, h2, h3, h4 {
  margin: 0 0 .4em;
  line-height: 1.2;
  letter-spacing: -.018em;
  text-wrap: balance;
}
h1 { font-weight: 800; }
h2, h3 { font-weight: 700; }
p { margin: 0 0 .8em; text-wrap: pretty; }

/* Les montants et les compteurs s'alignent en colonne : chiffres de largeur
   fixe, sinon les totaux d'un tableau dansent d'une ligne à l'autre. */
.v, .price, .summary .li, td, .tile .v, .revenue .v, .count, .qty input {
  font-variant-numeric: tabular-nums;
}

::selection { background: color-mix(in srgb, var(--brand) 26%, transparent); }

/* Anneau de focus visible seulement au clavier : indispensable pour la
   navigation sans souris, invisible pour les autres. */
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ==========================================================================
   Utilitaires de mise en page
   ========================================================================== */
.container { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--s4); }
/* Les gabarits posent « container page » sur le même élément. En raccourci,
   « padding: … 0 … » remettait la marge latérale de .container à zéro et tout
   le contenu venait toucher les bords de l'écran. Seul le vertical est donc
   défini ici. */
.page {
  padding-top: var(--s5);
  padding-bottom: calc(var(--nav-h) + var(--s10) + env(safe-area-inset-bottom));
}
.muted { color: var(--muted); }
.center { text-align: center; }
.right { text-align: right; }
.left { text-align: left; }
.content { min-width: 0; }
.top-spacer { flex: 1; }
.mt { margin-top: var(--s4); } .mb { margin-bottom: var(--s4); } .mt8 { margin-top: var(--s2); }
.grow { flex: 1; min-width: 0; }
.row { display: flex; gap: var(--s3); }
.wrap { flex-wrap: wrap; }
.between { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.small { font-size: .8125rem; }
.strong { font-weight: 700; }
.hide { display: none; }

h1.page-title {
  font-size: clamp(1.35rem, 1.15rem + 1vw, 1.75rem);
  margin: var(--s1) 2px var(--s4);
}

/* ==========================================================================
   Barre d'en-tête
   ========================================================================== */
.topbar {
  background: var(--brand);
  color: var(--on-brand);
  padding: var(--s4) 0 var(--s5);
  padding-top: calc(var(--s4) + env(safe-area-inset-top));
  position: sticky; top: 0; z-index: 20;
  box-shadow: var(--shadow);
}
.topbar.plain {
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  color: var(--text);
  box-shadow: inset 0 -1px 0 var(--border);
  /* Fond translucide : le contenu qui défile dessous reste perceptible sans
     nuire à la lecture du titre. */
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  backdrop-filter: saturate(180%) blur(16px);
}
.topbar.plain a, .topbar.plain .icon-btn { color: var(--text); }
.topbar .container { display: flex; align-items: center; gap: var(--s3); }
.topbar h1 { font-size: 1.15rem; margin: 0; font-weight: 700; line-height: 1.25; }
.topbar h1 a { color: inherit; }
.topbar h1 a:hover { text-decoration: none; }
.topbar .sub { font-size: .8125rem; opacity: .88; }
.topbar .spacer { flex: 1; }

.logo-badge {
  width: 42px; height: 42px; border-radius: var(--r-md); flex: none;
  background: color-mix(in srgb, var(--on-brand) 16%, transparent);
  display: flex; align-items: center; justify-content: center;
  font-size: 1.3rem; overflow: hidden; object-fit: cover;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-brand) 12%, transparent);
}
/* Un logo n'est pas une photo. « cover » remplit le cadre en rognant ce qui
   dépasse : un logo en bandeau — 957 × 171 pour la boutique qui a signalé le
   problème — n'y laissait qu'une tranche centrale méconnaissable. Il garde donc
   ses proportions, borné en hauteur, et le cadre s'élargit pour lui. Le repli
   sans logo, lui, reste le carré à pictogramme d'avant. */
img.logo-badge {
  width: auto; max-width: 128px; object-fit: contain;
  padding: 3px 6px; background: none; box-shadow: none;
}
.icon-btn {
  position: relative; width: 40px; height: 40px; border-radius: var(--r-md); flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--on-brand); border: none; cursor: pointer;
  background: color-mix(in srgb, var(--on-brand) 14%, transparent);
  transition: background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.icon-btn:hover { background: color-mix(in srgb, var(--on-brand) 24%, transparent); }
.icon-btn:active { transform: scale(.94); }
.icon-btn .count {
  position: absolute; top: -5px; right: -5px;
  background: var(--surface); color: var(--brand-600);
  border-radius: var(--r-full); min-width: 19px; height: 19px;
  font-size: .68rem; font-weight: 800; padding: 0 5px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-xs);
}
.svg-ic {
  width: 22px; height: 22px; stroke: currentColor; fill: none;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}

/* En-tête d'accueil commerçant */
.greet { display: flex; align-items: center; gap: var(--s3); }
.greet .hi { font-size: .82rem; opacity: .88; }
.greet .name { font-size: 1.4rem; font-weight: 800; line-height: 1.15; letter-spacing: -.02em; }
.greet .tag { font-size: .78rem; opacity: .82; }
.avatar {
  width: 46px; height: 46px; border-radius: 50%; flex: none;
  background: color-mix(in srgb, var(--on-brand) 18%, transparent);
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 1.05rem; color: var(--on-brand);
  overflow: hidden; object-fit: cover;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-brand) 14%, transparent);
}

/* ==========================================================================
   Cartes
   ========================================================================== */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--s4);
  box-shadow: var(--shadow);
  margin-bottom: var(--s3);
}
.card.tight { padding: var(--s3) var(--s4); }

.section-title {
  display: flex; align-items: center; justify-content: space-between;
  margin: var(--s5) 2px var(--s3);
}
.section-title:first-child { margin-top: 0; }
.section-title h3 { margin: 0; font-size: 1.02rem; letter-spacing: -.01em; }
.section-title a { font-size: .85rem; font-weight: 600; }

/* Bandeau de chiffre d'affaires */
.revenue {
  background: var(--brand); color: var(--on-brand);
  border-radius: var(--r-xl); padding: var(--s5);
  box-shadow: var(--shadow-lg);
  margin-bottom: var(--s3); position: relative; overflow: hidden;
}
/* Halo diagonal : donne du volume à l'aplat sans seconde couleur. */
.revenue::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 100% at 100% 0%,
              color-mix(in srgb, var(--on-brand) 16%, transparent) 0%, transparent 60%);
}
.revenue .k { font-size: .85rem; opacity: .88; }
.revenue .v { font-size: clamp(2rem, 1.6rem + 2vw, 2.6rem); font-weight: 800; letter-spacing: -.03em; }
.revenue .v small { font-size: 1rem; font-weight: 700; opacity: .88; }
.revenue .delta {
  display: inline-flex; align-items: center; gap: var(--s1);
  font-size: .82rem; font-weight: 700; padding: 3px 10px;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--on-brand) 18%, transparent);
}
.revenue .chart { margin-top: var(--s3); position: relative; }
.chart svg { width: 100%; height: auto; display: block; }

/* ==========================================================================
   Tuiles de statistiques
   ========================================================================== */
.tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s3); margin-bottom: var(--s3); }
.tiles.two { grid-template-columns: repeat(2, 1fr); }
.tile {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: var(--s3) var(--s4);
  box-shadow: var(--shadow-xs);
  transition: box-shadow var(--base) var(--ease), transform var(--base) var(--ease);
}
.tile:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.tile .k { color: var(--muted); font-size: .74rem; line-height: 1.25; font-weight: 500; }
.tile .v { font-size: 1.45rem; font-weight: 800; color: var(--text); margin-top: 2px; letter-spacing: -.025em; }
.tile .v small { font-size: .78rem; font-weight: 700; }
.tile .chip-pos { color: var(--pos); font-size: .72rem; font-weight: 700; }
.tile .chip-neg { color: var(--red-fg); font-size: .72rem; font-weight: 700; }

/* ==========================================================================
   Boutons
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  background: var(--brand); color: var(--on-brand);
  border: 1px solid transparent; padding: var(--s3) var(--s5);
  border-radius: var(--r-md);
  font-size: 1rem; font-weight: 650; font-family: inherit;
  cursor: pointer; min-height: 50px; text-align: center;
  box-shadow: var(--shadow-xs);
  transition: transform var(--fast) var(--ease),
              box-shadow var(--base) var(--ease),
              background var(--base) var(--ease),
              filter var(--base) var(--ease);
}
.btn:hover { filter: brightness(1.07); box-shadow: var(--shadow); text-decoration: none; }
.btn:active { transform: translateY(1px) scale(.995); box-shadow: var(--shadow-xs); }
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: .5; cursor: not-allowed; filter: none;
  transform: none; box-shadow: none;
}
.btn.block { width: 100%; }
.btn.secondary { background: var(--brand-050); color: var(--brand-ink); box-shadow: none; }
.btn.secondary:hover { background: var(--brand-100); filter: none; }
.btn.outline { background: transparent; color: var(--brand-ink); border-color: var(--border-strong); box-shadow: none; }
.btn.outline:hover { background: var(--brand-050); border-color: var(--brand); filter: none; }
.btn.ghost { background: transparent; color: var(--brand-600); min-height: auto; padding: 6px 8px; box-shadow: none; }
.btn.ghost:hover { background: var(--brand-050); filter: none; }
.btn.danger { background: var(--surface); color: var(--red-fg); border-color: color-mix(in srgb, var(--red-fg) 30%, transparent); box-shadow: none; }
.btn.danger:hover { background: var(--red-bg); filter: none; }
.btn.wa { background: var(--wa); color: #06301a; }
.btn.dark { background: var(--brand-700); color: var(--on-brand); }
.btn.sm { min-height: 40px; padding: var(--s2) var(--s4); font-size: .9rem; border-radius: var(--r-sm); }
.btn.full-dark { background: var(--grad); color: var(--on-brand); }

/* Bouton « Commander » des fiches produit */
.order-btn {
  display: flex; align-items: center; justify-content: center; gap: var(--s2); width: 100%;
  background: var(--wa); color: #06301a;
  border: none; border-radius: var(--r-md);
  padding: 11px var(--s4); font-weight: 700; font-size: .9rem; font-family: inherit;
  cursor: pointer; min-height: 44px;
  box-shadow: 0 1px 2px rgba(6, 48, 26, .18), 0 4px 12px -4px rgba(37, 211, 102, .5);
  transition: transform var(--fast) var(--ease), box-shadow var(--base) var(--ease), filter var(--base) var(--ease);
}
.order-btn:hover { filter: brightness(1.04); box-shadow: 0 2px 4px rgba(6,48,26,.2), 0 8px 20px -6px rgba(37,211,102,.6); }
.order-btn:active { transform: translateY(1px) scale(.99); }
.order-btn .wa-ic { width: 18px; height: 18px; }

/* ==========================================================================
   Formulaires
   ========================================================================== */
label { display: block; font-weight: 600; font-size: .875rem; margin: var(--s4) 0 var(--s2); color: var(--text-2); }

input, select, textarea {
  width: 100%; padding: 13px var(--s4);
  border: 1.5px solid var(--border-strong); border-radius: var(--r-md);
  /* 16px minimum : en dessous, iOS zoome à la mise au point du champ. */
  font-size: 1rem; font-family: inherit;
  background: var(--surface); color: var(--text);
  min-height: 50px;
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
textarea { min-height: 96px; resize: vertical; }
input::placeholder, textarea::placeholder { color: var(--muted); opacity: 1; }
input:hover, select:hover, textarea:hover { border-color: color-mix(in srgb, var(--brand) 45%, var(--border-strong)); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--brand); box-shadow: var(--ring);
}
input:disabled, select:disabled, textarea:disabled { background: var(--surface-2); color: var(--muted); cursor: not-allowed; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--brand); }

/* Chevron dessiné en CSS : la flèche native diffère à chaque navigateur. */
select {
  appearance: none; -webkit-appearance: none;
  padding-right: var(--s10);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 20px) calc(50% + 2px), calc(100% - 14px) calc(50% + 2px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

.hint { color: var(--muted); font-size: .8rem; margin-top: 5px; }

.search {
  display: flex; align-items: center; gap: var(--s3);
  background: var(--surface); border: 1.5px solid var(--border-strong);
  border-radius: var(--r-md); padding: 0 var(--s4); margin-bottom: var(--s3);
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.search:focus-within { border-color: var(--brand); box-shadow: var(--ring); }
.search svg { width: 20px; height: 20px; stroke: var(--muted); fill: none; stroke-width: 2; flex: none; }
.search input { border: none; box-shadow: none; padding-left: 0; background: transparent; }
.search input:focus { box-shadow: none; }
.search input:hover { border-color: transparent; }

.checkline { display: flex; align-items: center; gap: var(--s3); }
.checkline input[type=checkbox], .checkline input[type=radio] {
  width: 20px; height: 20px; min-height: auto; flex: none; margin: 0;
}

/* Interrupteur */
.switch { position: relative; display: inline-block; width: 46px; height: 27px; flex: none; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .track {
  position: absolute; inset: 0; background: var(--border-strong);
  border-radius: var(--r-full); cursor: pointer;
  transition: background var(--base) var(--ease);
}
.switch .track::before {
  content: ""; position: absolute; height: 21px; width: 21px; left: 3px; top: 3px;
  background: #fff; border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .28);
  transition: transform var(--base) var(--ease-out);
}
.switch input:checked + .track { background: var(--brand); }
.switch input:checked + .track::before { transform: translateX(19px); }
.switch input:focus-visible + .track { box-shadow: var(--ring); }

/* ==========================================================================
   Pastilles de filtre
   ========================================================================== */
.pills {
  display: flex; gap: var(--s2); overflow-x: auto;
  padding-bottom: var(--s1); margin-bottom: var(--s3);
  scrollbar-width: none; scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}
.pills::-webkit-scrollbar { display: none; }
.pills a, .pills button {
  padding: var(--s2) var(--s4); border-radius: var(--r-full);
  background: var(--surface); color: var(--brand-ink);
  border: 1.5px solid var(--border);
  font-size: .85rem; font-weight: 600; font-family: inherit;
  white-space: nowrap; cursor: pointer; scroll-snap-align: start;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.pills a:hover, .pills button:hover { background: var(--brand-050); border-color: var(--brand-100); text-decoration: none; }
.pills a.active, .pills button.active {
  background: var(--brand); color: var(--on-brand); border-color: var(--brand);
  box-shadow: var(--shadow-xs);
}

/* ==========================================================================
   Icônes et pastille d'état
   ========================================================================== */
/* Les icônes sont des SVG au trait héritant de currentColor (services/icons.py).
   L'alignement optique se fait sur la ligne de base du texte voisin. */
.ic-svg { display: inline-block; vertical-align: -.16em; flex: none; }

/* « Ouvert / Fermé » s'écrivait avec un emoji 🟢 dessiné par la police du
   système : taille et rendu variables, couleur non maîtrisée. */
.dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: currentColor; flex: none; vertical-align: middle;
  margin-right: 2px;
}
.dot.on {
  background: #34d97b;
  box-shadow: 0 0 0 3px color-mix(in srgb, #34d97b 28%, transparent);
  animation: dot-pulse 2.4s var(--ease) infinite;
}
.dot.off {
  background: #f2686b;
  box-shadow: 0 0 0 3px color-mix(in srgb, #f2686b 24%, transparent);
}
@keyframes dot-pulse {
  0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, #34d97b 28%, transparent); }
  50%      { box-shadow: 0 0 0 6px color-mix(in srgb, #34d97b 6%, transparent); }
}
.status { display: inline-flex; align-items: center; gap: 6px; }

/* ==========================================================================
   Personnalisation de la boutique (réglages)
   ========================================================================== */
.swatches { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s2); }
.swatch {
  width: 40px; height: 40px; border-radius: var(--r-sm); flex: none;
  background: var(--sw); border: none; cursor: pointer; padding: 0;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.swatch:hover { transform: translateY(-2px); }
/* Anneau tracé avec la couleur de la page : lisible sur n'importe quelle teinte. */
.swatch.active {
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12),
              0 0 0 2px var(--surface), 0 0 0 4px var(--text);
}
.color-field {
  width: 56px; height: 56px; min-height: 56px; flex: none;
  padding: 4px; border-radius: var(--r-sm); cursor: pointer;
  border: 1.5px solid var(--border-strong); background: var(--surface);
}

/* Aperçu : ses propres jetons sont réécrits par le script, il ne suit donc
   pas la palette de la page tant que le choix n'est pas enregistré. */
.theme-preview {
  margin-top: var(--s4); border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--border);
}
.theme-preview .tp-bar {
  background: var(--brand); color: var(--on-brand);
  padding: var(--s3) var(--s4); font-weight: 700; font-size: .92rem;
  display: flex; align-items: center; gap: var(--s2);
}
.theme-preview .tp-dot {
  width: 8px; height: 8px; border-radius: 50%; background: #34d97b; flex: none;
}
.theme-preview .tp-body {
  background: var(--surface); padding: var(--s4);
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
}
.theme-preview .tp-btn {
  background: var(--brand); color: var(--on-brand);
  padding: var(--s2) var(--s4); border-radius: var(--r-sm); font-weight: 650; font-size: .88rem;
}
.theme-preview .tp-chip {
  background: var(--brand-050); color: var(--brand-ink);
  padding: var(--s2) var(--s3); border-radius: var(--r-full); font-weight: 700; font-size: .78rem;
}
.theme-preview .tp-link { color: var(--brand-600); font-weight: 650; font-size: .88rem; }

/* ==========================================================================
   Page d'accueil
   ========================================================================== */
.landing .page { padding-bottom: var(--s8); }
.landing-main { padding-bottom: calc(var(--s10) + env(safe-area-inset-bottom)); }

/* Bandeau pleine largeur : le dégradé va d'un bord à l'autre, le contenu
   reste dans la colonne de lecture. */
.hero {
  background: var(--grad); color: var(--on-brand);
  padding: calc(var(--s8) + env(safe-area-inset-top)) 0 var(--s10);
  margin-bottom: var(--s6); position: relative; overflow: hidden;
}
.hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(90% 70% at 80% 0%,
              color-mix(in srgb, var(--on-brand) 14%, transparent) 0%, transparent 65%);
}
.hero > .container { position: relative; }
.hero-brand { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s6); }
.hero-mark {
  width: 42px; height: 42px; border-radius: var(--r-md); flex: none;
  background: color-mix(in srgb, var(--on-brand) 16%, transparent);
  display: flex; align-items: center; justify-content: center;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-brand) 14%, transparent);
}
.hero-name { font-weight: 800; font-size: 1.25rem; letter-spacing: -.02em; }
.hero-title {
  font-size: clamp(1.75rem, 1.3rem + 2.4vw, 2.6rem);
  font-weight: 800; letter-spacing: -.03em; line-height: 1.15; margin: 0 0 var(--s3);
}
.hero-sub { font-size: 1.02rem; opacity: .92; margin-bottom: var(--s6); max-width: 46ch; }
.hero-actions { display: grid; gap: var(--s3); }
/* Bouton secondaire sur fond de marque : le contour hérite de l'encre, donc
   reste lisible quelle que soit la couleur choisie. */
.btn.ghost-light {
  background: color-mix(in srgb, var(--on-brand) 14%, transparent);
  color: var(--on-brand);
  border: 1.5px solid color-mix(in srgb, var(--on-brand) 35%, transparent);
  box-shadow: none;
}
.btn.ghost-light:hover { background: color-mix(in srgb, var(--on-brand) 24%, transparent); filter: none; }
.hero-note {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  margin: var(--s4) 0 0; font-size: .85rem; opacity: .9;
}

.section-heading {
  font-size: 1.15rem; font-weight: 750; letter-spacing: -.02em;
  margin: 0 2px var(--s4);
}
section + section { margin-top: var(--s8); }

/* Étapes numérotées */
.step-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
.step-list li {
  display: flex; align-items: center; gap: var(--s3);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: var(--s3) var(--s4);
  box-shadow: var(--shadow-xs);
}
.step-num {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  background: var(--brand); color: var(--on-brand);
  display: flex; align-items: center; justify-content: center;
  font-size: .8rem; font-weight: 800;
}
.step-ic {
  width: 38px; height: 38px; border-radius: var(--r-sm); flex: none;
  background: var(--brand-050); color: var(--brand-ink);
  display: flex; align-items: center; justify-content: center;
}
.step-list strong { display: block; font-size: .95rem; }
.step-list small { color: var(--muted); font-size: .82rem; }

/* Atouts */
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--s3); }
.feature {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s4);
  box-shadow: var(--shadow-xs);
  transition: box-shadow var(--base) var(--ease), transform var(--base) var(--ease);
}
.feature:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.feature strong { display: block; margin: var(--s3) 0 var(--s1); font-size: .98rem; }
.feature small { color: var(--muted); font-size: .85rem; line-height: 1.5; }
.feature-ic {
  width: 40px; height: 40px; border-radius: var(--r-sm);
  background: var(--brand-050); color: var(--brand-ink);
  display: flex; align-items: center; justify-content: center;
}

.demo-card { text-align: left; }
.demo-card strong { display: block; margin: var(--s3) 0 var(--s1); }
.final-cta strong { display: block; font-size: 1.1rem; margin-bottom: var(--s1); }
.landing-footer { margin-top: var(--s8); text-align: center; }
.landing-footer p { margin: 0 0 var(--s1); }

/* Page d'accueil : marque du bandeau et pictogrammes des étapes. */
.hero-mark {
  width: 68px; height: 68px; border-radius: var(--r-lg);
  margin: var(--s2) auto var(--s5);
  display: flex; align-items: center; justify-content: center;
  background: var(--brand-050); color: var(--brand-600);
}
.step-ic {
  width: 38px; height: 38px; border-radius: var(--r-sm); flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--brand-050); color: var(--brand-600);
}

/* Vignette sans photo : un pictogramme discret plutôt qu'un emoji de panier. */
.ph {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; color: var(--muted); opacity: .45;
}
.ph .ic-svg { width: 34%; height: 34%; max-width: 46px; max-height: 46px; stroke-width: 1.5; }

/* ==========================================================================
   Badges
   ========================================================================== */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 11px; border-radius: var(--r-full);
  font-size: .74rem; font-weight: 700; line-height: 1.5; white-space: nowrap;
}
.badge.amber { background: var(--amber-bg); color: var(--amber-fg); }
.badge.blue  { background: var(--blue-bg);  color: var(--blue-fg); }
.badge.green { background: var(--green-bg); color: var(--green-fg); }
.badge.red   { background: var(--red-bg);   color: var(--red-fg); }
.badge.gray  { background: var(--gray-bg);  color: var(--gray-fg); }
.badge.pro   { background: color-mix(in srgb, var(--on-brand) 20%, transparent); color: var(--on-brand); }

/* ==========================================================================
   Grille catalogue
   ========================================================================== */
/* auto-fill plutôt que 2 colonnes fixes : la vitrine tenait dans un ruban de
   460px même sur un écran d'ordinateur.

   « align-items: start » : par défaut une grille étire chaque carte à la
   hauteur de la plus haute de sa rangée, et comme « .product .body » porte
   « flex: 1 », tout le vide se retrouve sous le bouton « Commander ». Une
   carte se règle donc sur son seul contenu, à n'importe quelle largeur. */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(152px, 1fr)); gap: var(--s3); align-items: start; }

.product {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); overflow: hidden;
  display: flex; flex-direction: column;
  /* Posé sur la carte elle-même, et pas seulement sur la grille : « align-self »
     l'emporte sur le « align-items » du conteneur, quel qu'il soit. La carte ne
     peut donc plus dépasser la hauteur de ce qu'elle contient, même si une
     règle ajoutée plus tard réétirait la grille. */
  align-self: start;
  height: fit-content;
  box-shadow: var(--shadow-xs);
  transition: box-shadow var(--base) var(--ease), transform var(--base) var(--ease), border-color var(--base) var(--ease);
}
.product:hover { box-shadow: var(--shadow-lg); transform: translateY(-2px); border-color: var(--border-strong); }
.product .thumb {
  aspect-ratio: 1 / 1; background: var(--surface-2);
  display: flex; align-items: center; justify-content: center;
  font-size: 1.8rem; position: relative; flex-shrink: 0; overflow: hidden;
}
.product .thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s var(--ease-out); }
.product:hover .thumb img { transform: scale(1.045); }
.product .thumb .fav {
  position: absolute; top: var(--s2); right: var(--s2);
  width: 28px; height: 28px; border-radius: 50%;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  display: flex; align-items: center; justify-content: center; font-size: .9rem;
  box-shadow: var(--shadow-xs);
}
/* « flex: 1 » faisait grandir le corps de la carte pour occuper toute la
   hauteur disponible : dès que la carte était étirée — par une voisine plus
   haute, ou par n'importe quelle règle de grille — le vide se creusait sous le
   bouton « Commander ». Le corps prend maintenant la hauteur de son contenu et
   rien de plus, ce qui rend ce vide impossible quelle que soit la largeur. */
.product .body { padding: var(--s2) var(--s3) var(--s3); display: flex; flex-direction: column; gap: 3px; flex: 0 0 auto; min-height: 0; }
.product .name {
  font-weight: 600; font-size: .875rem; line-height: 1.3; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; text-overflow: ellipsis; word-break: break-word;
}
.product .price { margin-top: auto; padding-top: var(--s1); font-size: .95rem; }
.product .price small { font-size: .7rem; font-weight: 600; }
.product .order-btn {
  background: var(--brand); color: var(--on-brand);
  border-radius: var(--r-sm); padding: var(--s2) var(--s3);
  font-size: .8rem; margin-top: var(--s2); min-height: 36px;
  box-shadow: none; gap: 5px;
}
.product .order-btn:hover { box-shadow: var(--shadow-xs); }
.product .order-btn .wa-ic { width: 14px; height: 14px; flex-shrink: 0; }

/* Image principale d'une fiche produit */
.hero-thumb {
  aspect-ratio: 16 / 12; background: var(--surface-2); position: relative;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
/* « contain », et non « cover ». Le rognage convient à une vignette de
   catalogue, où l'uniformité des tuiles prime ; sur la fiche, le client est
   précisément venu regarder l'article, et une image plus large que son cadre y
   perdait ses bords. Le fond neutre habille le vide laissé sur les côtés. */
.hero-thumb img, .pdp-photo { width: 100%; height: 100%; object-fit: contain; }

/* Galerie de vignettes d'une fiche produit */
.gallery {
  display: flex; gap: var(--s2); overflow-x: auto;
  padding: var(--s3); border-top: 1px solid var(--border);
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.gallery::-webkit-scrollbar { display: none; }
.gallery-thumb {
  width: 62px; height: 62px; flex: none; padding: 0; cursor: pointer;
  border: 2px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-2); overflow: hidden;
  transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.gallery-thumb:hover { transform: translateY(-2px); }
.gallery-thumb.active { border-color: var(--brand); }
.gallery-thumb img { width: 100%; height: 100%; object-fit: cover; }

/* ==========================================================================
   Listes
   ========================================================================== */
.list {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-xs); overflow: hidden;
}
.row-item { display: flex; align-items: center; gap: var(--s3); }
.list .row-item { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border); transition: background var(--fast) var(--ease); }
.list .row-item:last-child { border-bottom: none; }
.list a.row-item:hover { background: var(--surface-2); text-decoration: none; }
.row-item .th {
  width: 46px; height: 46px; border-radius: var(--r-sm); flex: none;
  background: var(--surface-2); display: flex; align-items: center; justify-content: center;
  font-size: 1.3rem; overflow: hidden;
}
.row-item .th img { width: 100%; height: 100%; object-fit: cover; }
.row-item .t1 { font-weight: 650; font-size: .93rem; color: var(--text); }
.row-item .t2 { color: var(--muted); font-size: .8rem; }

/* Menu du profil */
.menu {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-xs); overflow: hidden;
}
.menu a {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s4); border-bottom: 1px solid var(--border);
  color: var(--text); font-weight: 600;
  transition: background var(--fast) var(--ease);
}
.menu a:hover { background: var(--surface-2); text-decoration: none; }
.menu a:last-child { border-bottom: none; }
.menu a .mi {
  width: 22px; height: 22px; flex: none; stroke: var(--brand-600); fill: none;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
.menu a .chev { margin-left: auto; color: var(--muted); }
.menu a.danger { color: var(--red-fg); }
.menu a.danger .mi { stroke: var(--red-fg); }
.menu a.danger:hover { background: var(--red-bg); }

/* ==========================================================================
   Quantité et options
   ========================================================================== */
.qty { display: inline-flex; align-items: center; gap: var(--s3); }
.qty button {
  width: 40px; height: 40px; min-height: 40px; padding: 0;
  border-radius: var(--r-sm); border: none; cursor: pointer;
  background: var(--brand-050); color: var(--brand-ink);
  font-size: 1.3rem; font-weight: 700; font-family: inherit;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.qty button:hover { background: var(--brand-100); }
.qty button:active { transform: scale(.93); }
.qty input { width: 56px; text-align: center; min-height: 40px; padding: 6px; font-weight: 700; }

.chips { display: flex; gap: var(--s2); flex-wrap: wrap; }
.chip-opt {
  min-width: 52px; text-align: center; padding: 11px var(--s4);
  border-radius: var(--r-md); border: 1.5px solid var(--border-strong);
  background: var(--surface); color: var(--text);
  font-weight: 700; font-family: inherit; cursor: pointer;
  transition: all var(--fast) var(--ease);
}
.chip-opt:hover { border-color: var(--brand); }
.chip-opt.active { background: var(--brand); color: var(--on-brand); border-color: var(--brand); }

/* ==========================================================================
   Moyens de paiement
   ========================================================================== */
.pay-opt {
  display: flex; align-items: center; gap: var(--s3);
  border: 1.5px solid var(--border-strong); border-radius: var(--r-md);
  padding: var(--s4); margin-bottom: var(--s3);
  cursor: pointer; background: var(--surface);
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease), background var(--fast) var(--ease);
}
.pay-opt:hover { border-color: color-mix(in srgb, var(--brand) 50%, var(--border-strong)); }
.pay-opt.active { border-color: var(--brand); box-shadow: var(--ring); background: var(--brand-050); }
/* Pastille d'opérateur. La mise en forme était portée par « .pay-opt .pm-ic »,
   ce qui posait deux problèmes : la pastille du récapitulatif de commande, hors
   de tout « .pay-opt », n'en héritait pas — ni rayon ni centrage — et son
   « color: #fff » l'emportait en spécificité sur les règles de famille, qui
   écrivaient donc en blanc sur fond clair. Un seul niveau de spécificité ici :
   les familles suivent, et gagnent par l'ordre d'écriture. */
.pm-ic {
  width: 44px; height: 44px; border-radius: var(--r-sm); flex: none;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; letter-spacing: -.01em; line-height: 1.1;
  /* « SEPA » ou « MPSA » ne tiennent pas dans la place d'un « CB ». */
  font-size: .66rem; padding: 2px; text-align: center;
  background: var(--gray-bg); color: var(--text-2);
}

/* Couleurs des opérateurs historiques : ce sont leurs marques, elles ne suivent
   donc pas le thème de la boutique. */
.pm-mtn  { background: #ffcc00; color: #23200a; }
.pm-om   { background: #ff6600; color: #fff; }
.pm-am   { background: #e8112d; color: #fff; }
.pm-card { background: var(--gray-bg); color: var(--text-2); }
.pm-cod  { background: var(--brand-050); color: var(--brand-ink); }

/* Repli par famille de moyen de paiement : le catalogue en compte plus de
   soixante-dix, trop pour une règle par opérateur. Ceux qui ont une couleur de
   marque la posent en style en ligne, avec une encre calculée pour rester
   lisible dessus ; les autres retombent sur ces teintes neutres. */
.pm-mobile_money { background: var(--brand-050); color: var(--brand-ink); }
.pm-wallet       { background: var(--blue-bg);   color: var(--blue-fg); }
.pm-transfer     { background: var(--gray-bg);   color: var(--text-2); }
.pm-cash         { background: var(--green-bg);  color: var(--green-fg); }
.pm-bnpl         { background: var(--amber-bg);  color: var(--amber-fg); }

/* Liste informative de moyens de paiement — page d'abonnement hors zone
   d'encaissement. Même présentation que les options cliquables, mais sans
   curseur ni survol : rien ici ne se sélectionne. */
.pay-list .pay-opt { cursor: default; }
.pay-list .pay-opt:hover { border-color: var(--border-strong); }
.pay-opt .pm-t1 { font-weight: 700; color: var(--text); }
.pay-opt .pm-t2 { color: var(--muted); font-size: .8rem; }
.pay-opt .radio {
  margin-left: auto; width: 22px; height: 22px; border-radius: 50%; flex: none;
  border: 2px solid var(--border-strong);
  display: flex; align-items: center; justify-content: center;
  transition: border-color var(--fast) var(--ease);
}
.pay-opt.active .radio { border-color: var(--brand); }
.pay-opt.active .radio::after {
  content: ""; width: 12px; height: 12px; border-radius: 50%; background: var(--brand);
  animation: pop .22s var(--ease-out);
}
@keyframes pop { from { transform: scale(0); } to { transform: scale(1); } }

/* ==========================================================================
   Récapitulatif
   ========================================================================== */
/* Ligne de prix — règles globales, valables aussi bien sur une carte de la
   grille que sur la fiche produit. Réservées à « .product », elles laissaient
   l'ancien prix de la fiche s'afficher aussi gros que le prix courant, sans
   barré, et déborder sur deux lignes. */
.price {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px 8px;
  font-weight: 750; color: var(--text); letter-spacing: -.02em;
}
.price-lg { font-size: clamp(1.35rem, 1.2rem + .8vw, 1.6rem); font-weight: 800; }
/* Taille exprimée en em : l'ancien prix reste proportionné au prix courant,
   quelle que soit la taille de celui-ci. */
.old {
  text-decoration: line-through; text-decoration-thickness: 1.5px;
  color: var(--muted); font-size: .62em; font-weight: 500; white-space: nowrap;
}

.summary .li { display: flex; justify-content: space-between; padding: var(--s2) 0; color: var(--text-2); }
.summary .li.total {
  border-top: 1px solid var(--border); margin-top: var(--s1); padding-top: var(--s3);
  font-weight: 800; font-size: 1.08rem; color: var(--text); letter-spacing: -.02em;
}
.divider { height: 1px; background: var(--border); margin: var(--s3) 0; border: none; }

.rating { display: inline-flex; align-items: center; gap: 5px; color: #f5a623; font-weight: 700; }
.rating .rc { color: var(--muted); font-weight: 500; }

/* Confirmation de commande */
.confirm-icon {
  width: 92px; height: 92px; border-radius: 50%; background: var(--brand);
  display: flex; align-items: center; justify-content: center;
  margin: var(--s2) auto var(--s4);
  box-shadow: 0 10px 30px -8px color-mix(in srgb, var(--brand) 65%, transparent);
  animation: confirm-in .45s var(--ease-out) both;
}
.confirm-icon svg { width: 46px; height: 46px; stroke: var(--on-brand); fill: none; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
@keyframes confirm-in {
  from { transform: scale(.6); opacity: 0; }
  to   { transform: scale(1);  opacity: 1; }
}

/* ==========================================================================
   Barre de panier flottante
   ========================================================================== */
.cartbar {
  position: fixed; left: 0; right: 0; z-index: 25; padding: 0 var(--s4);
  bottom: calc(var(--nav-h) + var(--s3) + env(safe-area-inset-bottom));
  animation: cartbar-in .3s var(--ease-out) both;
}
@keyframes cartbar-in { from { transform: translateY(120%); } to { transform: none; } }
.cartbar .inner {
  max-width: var(--maxw); margin: 0 auto;
  background: var(--brand-700); color: var(--on-brand);
  border-radius: var(--r-md); padding: var(--s3) var(--s4);
  display: flex; align-items: center; gap: var(--s3);
  box-shadow: var(--shadow-xl);
}
.cartbar a { color: var(--on-brand); font-weight: 800; }

/* ==========================================================================
   Navigation basse
   ========================================================================== */
.tabnav {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-top: 1px solid var(--border);
  /* Marge basse sur les téléphones à barre d'accueil : sans elle, le dernier
     onglet se retrouve sous l'indicateur système et devient incliquable. */
  padding-bottom: env(safe-area-inset-bottom);
  display: flex;
}
.tabnav .inner { max-width: var(--maxw); margin: 0 auto; display: flex; width: 100%; }
.tabnav a {
  flex: 1; text-align: center; padding: var(--s2) 2px var(--s2);
  font-size: .68rem; color: var(--muted); font-weight: 600;
  position: relative; transition: color var(--fast) var(--ease);
}
.tabnav a:hover { color: var(--text-2); text-decoration: none; }
.tabnav a .ic {
  width: 24px; height: 24px; margin: 0 auto 2px; display: block;
  stroke: currentColor; fill: none; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform var(--base) var(--ease-out);
}
.tabnav a.active { color: var(--brand); }
.tabnav a.active .ic { transform: translateY(-1px); }
/* Trait de sélection : repère l'onglet courant sans alourdir l'icône. */
.tabnav a.active::before {
  content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 26px; height: 3px; border-radius: 0 0 3px 3px; background: var(--brand);
}

/* ==========================================================================
   Messages
   ========================================================================== */
.flash {
  padding: var(--s3) var(--s4); border-radius: var(--r-md);
  margin-bottom: var(--s3); font-weight: 600; font-size: .9rem;
  border: 1px solid transparent;
  animation: flash-in .28s var(--ease-out) both;
}
@keyframes flash-in { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: none; } }
.flash.amber { background: var(--amber-bg); color: var(--amber-fg); border-color: color-mix(in srgb, var(--amber-fg) 25%, transparent); }
/* Bandeau d'échéance : cliquable, il mène directement au renouvellement. */
.notice-link {
  display: flex; align-items: center; gap: var(--s3);
  text-decoration: none; color: inherit;
  transition: filter var(--fast) var(--ease);
}
.notice-link:hover { filter: brightness(.97); text-decoration: none; }
.notice-link small { display: block; font-weight: 500; opacity: .9; margin-top: 2px; }
.notice-link strong { display: block; }

.flash.err  { background: var(--red-bg);   color: var(--red-fg);   border-color: color-mix(in srgb, var(--red-fg) 22%, transparent); }
.flash.ok   { background: var(--green-bg); color: var(--green-fg); border-color: color-mix(in srgb, var(--green-fg) 22%, transparent); }
.flash.info { background: var(--blue-bg);  color: var(--blue-fg);  border-color: color-mix(in srgb, var(--blue-fg) 22%, transparent); }

/* ==========================================================================
   Tableaux
   ========================================================================== */
.table-wrap {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  border-radius: var(--r-md);
}
table { width: 100%; border-collapse: collapse; font-size: .9rem; }
th, td { text-align: left; padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border); white-space: nowrap; }
th {
  color: var(--muted); font-size: .72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  background: var(--surface-2); position: sticky; top: 0;
}
tbody tr { transition: background var(--fast) var(--ease); }
tbody tr:hover { background: var(--surface-2); }
tbody tr:last-child td { border-bottom: none; }

/* ==========================================================================
   Alerte de nouvelle commande
   --------------------------------------------------------------------------
   Bandeau flottant, posé au-dessus de la barre d'onglets pour ne pas la
   masquer. Vert WhatsApp délibérément, et non la couleur de la boutique : ce
   message parle d'une commande arrivée, pas de l'enseigne du commerçant.
   ========================================================================== */
.order-alert {
  position: fixed; z-index: 40; left: var(--s4); right: var(--s4);
  bottom: calc(var(--nav-h) + var(--s3) + env(safe-area-inset-bottom));
  max-width: 420px; margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  background: var(--wa); color: #06301a;
  border-radius: var(--r-md); padding: var(--s3) var(--s4);
  font-size: .92rem; text-decoration: none;
  box-shadow: var(--shadow-xl);
  animation: order-alert-in .32s var(--ease-out) both;
}
.order-alert:hover { text-decoration: none; filter: brightness(1.04); }
.order-alert span { font-weight: 700; white-space: nowrap; }
.order-alert.out { animation: order-alert-out .8s var(--ease) both; }

@keyframes order-alert-in {
  from { transform: translateY(140%); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes order-alert-out {
  to { transform: translateY(140%); opacity: 0; }
}

@media (min-width: 1024px) {
  /* La barre d'onglets devient colonne latérale : le bandeau n'a plus à s'en
     écarter, il se pose en bas à droite comme une notification classique. */
  .order-alert {
    left: auto; right: var(--s5); bottom: var(--s5); margin: 0;
  }
}

/* ==========================================================================
   Barre de validation de commande
   --------------------------------------------------------------------------
   Le bouton qui conclut la commande vit collé au bas de l'écran plutôt qu'en
   fin de formulaire : le client venait de saisir ses coordonnées et devait
   encore faire défiler pour trouver comment envoyer.
   ========================================================================== */
.checkout-bar {
  position: sticky; bottom: 0; z-index: 20;
  margin: var(--s4) calc(var(--s4) * -1) 0;
  padding: var(--s3) var(--s4) calc(var(--s3) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  backdrop-filter: saturate(180%) blur(12px);
  border-top: 1px solid var(--border);
}
.checkout-bar .hint { margin: var(--s2) 0 0; }
/* Le bouton WhatsApp porte le vert de la marque, pas la couleur de la boutique :
   c'est l'application vers laquelle il envoie qui doit être reconnaissable. */
.checkout-bar .btn.wa { min-height: 54px; font-size: 1.02rem; }

@media (min-width: 1024px) {
  /* Sur grand écran la page tient sans défilement : la barre reprend sa place
     dans le flux et se borne à la largeur du formulaire. */
  .checkout-bar {
    position: static; margin: var(--s5) 0 0; padding: 0;
    background: none; border-top: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
}

/* ==========================================================================
   Reçu d'abonnement
   --------------------------------------------------------------------------
   Document destiné autant à l'écran qu'au papier. Les couleurs de marque n'y
   apparaissent pas : un reçu se lit en noir sur blanc, et une imprimante à
   jet d'encre ne doit pas vider une cartouche pour un aplat décoratif.
   ========================================================================== */
.receipt-page { background: var(--bg); padding: var(--s4); }
.receipt-actions {
  display: flex; gap: var(--s2); flex-wrap: wrap;
  max-width: 760px; margin: 0 auto var(--s4);
}
.receipt {
  max-width: 760px; margin: 0 auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s8);
  box-shadow: var(--shadow);
}
.receipt-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: var(--s4); flex-wrap: wrap;
  padding-bottom: var(--s5); border-bottom: 2px solid var(--border);
}
.receipt-brand { font-size: 1.5rem; font-weight: 800; letter-spacing: -.03em; }
.receipt-title { font-size: 1.1rem; font-weight: 700; }

.receipt-parties {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s5); margin: var(--s6) 0;
}
.receipt-parties h4 {
  font-size: .74rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 0 0 var(--s2);
}

.receipt-lines { margin: var(--s6) 0; }
.receipt-lines th, .receipt-lines td { white-space: normal; }
.receipt-lines tfoot td {
  border-top: 2px solid var(--border); border-bottom: none;
  padding-top: var(--s4);
}
.receipt-total { font-size: 1.3rem; font-weight: 800; letter-spacing: -.02em; }

.receipt-meta {
  display: grid; gap: var(--s2);
  padding: var(--s4); border-radius: var(--r-md); background: var(--surface-2);
}
.receipt-meta > div { display: flex; justify-content: space-between; gap: var(--s3); font-size: .89rem; }
.receipt-note { margin: var(--s5) 0 0; text-align: center; }

@media print {
  /* Le papier porte déjà le fond : réimprimer les ombres, bordures et boutons
     gaspille de l'encre et brouille la lecture. */
  .receipt-page { background: none; padding: 0; }
  .receipt-actions { display: none; }
  .receipt { border: none; box-shadow: none; max-width: none; padding: 0; }
}

/* ==========================================================================
   PIÈCES RÉSERVÉES AU GRAND ÉCRAN — état par défaut
   --------------------------------------------------------------------------
   Tout ce qui suit est masqué tant que la fenêtre est étroite : le rendu sur
   téléphone reste exactement celui d'avant. Les blocs « ORDINATEUR » plus bas
   se contentent de les révéler et de les mettre en page.
   ========================================================================== */
.auth-brand,
.nav-brand, .nav-foot,
.shop-nav, .shop-foot,
.shop-mock,
.onb-side { display: none; }

/* Deux conteneurs sans effet sur téléphone : « contents » les efface de la
   mise en page, leurs enfants restent des frères comme avant l'ajout. */
.auth-shell, .auth-flow, .onb-split, .dash-split { display: contents; }

/* Bandeau de vitrine. Sur téléphone il ne reste que l'image, arrondie et
   espacée comme lorsqu'elle était posée en style en ligne. */
.shop-hero.no-banner { display: none; }
.shop-hero-bg { border-radius: var(--radius); margin-bottom: 14px; }
.shop-hero-in { display: none; }

/* Le remplissage de la fiche produit vient de la feuille, plus d'un style en
   ligne : sans cela la mise en colonnes du grand écran ne peut le reprendre. */
.pdp-info { padding: var(--s4); }

/* Colonne étroite du mot de passe oublié : elle se borne elle-même sur
   téléphone, et laisse « .auth-page .page » la centrer sur ordinateur. */
.auth-narrow { max-width: 420px; margin: 0 auto; padding-top: 32px; }

/* ==========================================================================
   ORDINATEUR — 1024 px et plus
   --------------------------------------------------------------------------
   Jusqu'ici le grand écran n'était qu'un téléphone centré dans du vide : même
   colonne de 880 px, même barre d'onglets collée en bas de la fenêtre. Cette
   section donne à chaque famille de pages la forme attendue sur ordinateur —
   colonne latérale pour l'espace commerçant, écran partagé pour l'inscription,
   vraie vitrine pour la boutique — sans jamais toucher aux règles de base.
   ========================================================================== */
@media (min-width: 1024px) {
  :root {
    --rail-w: 264px;
    --maxw: 940px;
  }
  /* La vitrine respire plus large que l'administration : c'est un catalogue,
     pas un formulaire. */
  body.shopfront  { --maxw: 1160px; }
  body.has-sidebar { --maxw: 1020px; }
  /* La création de boutique porte deux colonnes de front, formulaire et
     maquette : elle a besoin de plus de large que les autres pages. */
  body.onboarding { --maxw: 1220px; }

  /* ---------- Espace commerçant : la barre d'onglets devient une colonne ----- */
  body.has-sidebar { padding-left: var(--rail-w); }

  .tabnav {
    top: 0; right: auto; width: var(--rail-w);
    flex-direction: column;
    background: var(--surface);
    -webkit-backdrop-filter: none; backdrop-filter: none;
    border-top: none; border-right: 1px solid var(--border);
    padding-bottom: 0;
  }
  .tabnav .inner {
    flex: 1; flex-direction: column; align-items: stretch;
    max-width: none; width: 100%;
    padding: var(--s3); gap: 2px;
  }
  .tabnav a {
    flex: none;
    display: flex; align-items: center; gap: var(--s3);
    text-align: left; padding: 11px var(--s3);
    font-size: .94rem; font-weight: 600;
    color: var(--text-2); border-radius: var(--r-md);
    transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
  }
  .tabnav a .ic { width: 21px; height: 21px; margin: 0; }
  .tabnav a:hover { background: var(--surface-2); color: var(--text); }
  .tabnav a.active { background: var(--brand-050); color: var(--brand-ink); }
  .tabnav a.active .ic { transform: none; }
  /* Le trait de sélection passe du haut de l'onglet au bord gauche de la
     colonne : c'est là que l'œil suit une navigation verticale. */
  .tabnav a.active::before {
    top: 50%; left: 0; transform: translateY(-50%);
    width: 3px; height: 22px; border-radius: 0 3px 3px 0;
  }

  .nav-brand {
    display: flex; align-items: center; gap: var(--s3);
    padding: var(--s4) var(--s4) var(--s3);
    border-bottom: 1px solid var(--border);
    color: var(--text);
  }
  .nav-brand:hover { text-decoration: none; }
  .nav-mark {
    width: 40px; height: 40px; flex: none; border-radius: var(--r-md);
    background: var(--brand); color: var(--on-brand);
    display: flex; align-items: center; justify-content: center;
    overflow: hidden; object-fit: cover;
    box-shadow: var(--shadow-xs);
  }
  /* Borné assez court pour qu'il reste de la place au nom à côté : la colonne
     ne fait que 264 px, un logo en bandeau la remplirait à lui seul. */
  img.nav-mark {
    width: auto; max-width: 124px; object-fit: contain;
    background: none; box-shadow: none; padding: 0;
  }
  .nav-brand strong {
    display: block; font-size: .98rem; line-height: 1.25;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .nav-brand small { display: block; color: var(--muted); font-size: .76rem; }

  .nav-foot { display: block; padding: var(--s3); border-top: 1px solid var(--border); }
  .nav-foot .btn { width: 100%; }

  /* ---------- Espace commerçant : en-tête et contenu ---------- */
  body.has-sidebar .topbar { padding-top: var(--s5); padding-bottom: var(--s6); }
  body.has-sidebar .topbar h1 { font-size: 1.35rem; }
  body.has-sidebar .greet .name { font-size: 1.7rem; }
  body.has-sidebar .page {
    padding-top: var(--s6);
    padding-bottom: var(--s10);
  }
  body.has-sidebar .tiles { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--s4); }
  /* Un bouton d'action étiré sur mille pixels n'a plus l'air d'un bouton. */
  body.has-sidebar main.page > .btn.block { width: auto; min-width: 300px; }

  /* Recettes et indicateurs côte à côte : les trois tuiles empilées font
     exactement la hauteur du bandeau, la ligne se lit d'un regard. */
  .dash-split {
    display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
    gap: var(--s4); align-items: start; margin-bottom: var(--s3);
  }
  .dash-split .revenue, .dash-split .tiles { margin-bottom: 0; }
  /* Sélecteur aligné sur celui de « body.has-sidebar .tiles » plus haut : à
     spécificité moindre, la colonne unique se faisait reprendre par la grille
     automatique et les trois tuiles retombaient en 2 + 1. */
  body.has-sidebar .dash-split .tiles { grid-template-columns: 1fr; gap: var(--s3); }

  /* ---------- Inscription et connexion : écran partagé ---------- */
  body.auth-page { background: var(--surface); }
  .auth-shell {
    display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    min-height: 100vh; min-height: 100dvh;
  }
  .auth-brand {
    display: flex; flex-direction: column; justify-content: space-between;
    gap: var(--s8);
    background: var(--grad); color: var(--on-brand);
    padding: var(--s10) clamp(2.5rem, 4vw, 4.5rem);
    overflow: hidden;
    /* Le formulaire d'inscription dépasse la hauteur d'écran : sans cela le
       volet de marque s'étirerait avec lui et son contenu partirait se perdre
       en haut et en bas de la page. Il reste donc en vue pendant la saisie. */
    position: sticky; top: 0; align-self: start;
    height: 100vh; height: 100dvh;
  }
  .auth-brand::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(85% 65% at 85% 5%,
                color-mix(in srgb, var(--on-brand) 16%, transparent) 0%, transparent 62%);
  }
  .auth-brand > * { position: relative; }
  .auth-wordmark { display: flex; align-items: center; gap: var(--s3); }
  .auth-wordmark .hero-mark {
    width: 44px; height: 44px; margin: 0; border-radius: var(--r-md);
    background: color-mix(in srgb, var(--on-brand) 16%, transparent);
    color: var(--on-brand);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-brand) 16%, transparent);
  }
  .auth-wordmark span { font-weight: 800; font-size: 1.2rem; letter-spacing: -.02em; }
  .auth-pitch h2 {
    font-size: clamp(1.8rem, 1.1rem + 1.6vw, 2.5rem);
    font-weight: 800; letter-spacing: -.03em; line-height: 1.15;
    margin: 0 0 var(--s3); max-width: 18ch;
  }
  .auth-pitch p { opacity: .92; max-width: 42ch; margin-bottom: var(--s6); }
  .auth-points { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s4); }
  .auth-points li { display: flex; align-items: flex-start; gap: var(--s3); }
  .auth-points .pt-ic {
    width: 36px; height: 36px; flex: none; border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--on-brand) 15%, transparent);
    display: flex; align-items: center; justify-content: center;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-brand) 14%, transparent);
  }
  .auth-points strong { display: block; font-size: .97rem; }
  /* Ces deux lignes sont du petit texte posé sur un aplat de marque : trop
     transparentes, elles passaient sous le seuil de lisibilité avec les teintes
     moyennes. L'atténuation reste perceptible sans coûter le contraste. */
  .auth-points small { display: block; opacity: .9; font-size: .87rem; line-height: 1.45; }
  .auth-foot { font-size: .85rem; opacity: .9; }

  .auth-flow {
    display: flex; align-items: center; justify-content: center;
    padding: var(--s8) var(--s6);
  }
  /* L'en-tête bleu du téléphone n'a plus lieu d'être : la marque occupe déjà
     toute la moitié gauche. */
  .auth-page .topbar { display: none; }
  .auth-page .page {
    max-width: 460px; width: 100%; margin: 0;
    padding-top: 0; padding-bottom: 0;
  }
  .auth-page .page-title { font-size: 1.6rem; margin: 0 0 var(--s5); }
  .auth-narrow { max-width: none; padding-top: 0; }
  .auth-page .card { padding: var(--s6); box-shadow: var(--shadow-lg); }

  /* ---------- Création de boutique : formulaire et aperçu vivant ---------- */
  .onb-split {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    gap: clamp(2rem, 4vw, 3.5rem); align-items: start;
  }
  .onb-side { display: block; position: sticky; top: var(--s6); }
  .onb-side .onb-cap { margin: 0 0 var(--s3); }
  .onb-side .onb-cap strong { display: block; font-size: 1rem; }
  /* L'aperçu réduit du formulaire ferait doublon avec la maquette de droite. */
  .onb-split .theme-preview { display: none; }

  .shop-mock {
    display: block; border-radius: var(--r-lg); overflow: hidden;
    background: var(--surface); border: 1px solid var(--border-strong);
    box-shadow: var(--shadow-xl);
  }
  .mock-chrome {
    display: flex; align-items: center; gap: 6px;
    padding: 10px var(--s3); background: var(--surface-2);
    border-bottom: 1px solid var(--border);
  }
  .mock-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--border-strong); flex: none; }
  .mock-url {
    flex: 1; margin-left: var(--s2); padding: 4px var(--s3);
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-full);
    font-size: .72rem; color: var(--muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .mock-url b { color: var(--text-2); font-weight: 700; }
  .mock-top {
    display: flex; align-items: center; gap: var(--s3);
    background: var(--brand); color: var(--on-brand); padding: var(--s3) var(--s4);
  }
  .mock-logo {
    width: 34px; height: 34px; flex: none; border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--on-brand) 18%, transparent);
    display: flex; align-items: center; justify-content: center;
    overflow: hidden;
  }
  .mock-logo img { width: 100%; height: 100%; object-fit: contain; }
  .mock-name { font-weight: 800; font-size: .98rem; line-height: 1.2; letter-spacing: -.01em; }
  .mock-sub { font-size: .72rem; opacity: .9; display: flex; align-items: center; gap: 5px; }
  .mock-cart {
    width: 30px; height: 30px; flex: none; border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--on-brand) 16%, transparent);
    display: flex; align-items: center; justify-content: center;
  }
  .mock-hero {
    background: var(--grad); color: var(--on-brand);
    padding: var(--s5) var(--s4);
  }
  .mock-hero-t { font-weight: 800; font-size: 1.15rem; letter-spacing: -.02em; }
  .mock-hero-s { font-size: .8rem; opacity: .9; }
  .mock-body { background: var(--surface); }
  .mock-pills { display: flex; gap: 6px; padding: var(--s3) var(--s4) 0; }
  .mock-pills span {
    font-size: .7rem; font-weight: 650; padding: 4px 10px;
    border-radius: var(--r-full);
    border: 1px solid var(--border); color: var(--text-2);
  }
  .mock-pills span.on { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
  .mock-grid {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s3);
    padding: var(--s3) var(--s4) var(--s4);
  }
  .mock-card { border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; }
  .mock-ph {
    aspect-ratio: 1 / 1; background: var(--brand-050);
    display: flex; align-items: center; justify-content: center; color: var(--brand-600);
  }
  .mock-card .mock-l { height: 7px; border-radius: 4px; background: var(--border); margin: 8px 8px 5px; }
  .mock-card .mock-p { font-size: .72rem; font-weight: 800; margin: 0 8px; color: var(--text); }
  .mock-card .mock-btn {
    margin: 6px 8px 8px; padding: 5px; border-radius: var(--r-sm);
    background: var(--brand); color: var(--on-brand);
    font-size: .66rem; font-weight: 700; text-align: center;
  }

  /* ---------- Vitrine : un vrai site, à l'enseigne du commerçant ---------- */
  .shop-head .container { gap: var(--s4); padding-top: var(--s1); padding-bottom: var(--s1); }
  .shop-head img.logo-badge { height: 48px; max-width: 210px; padding: 4px 8px; }
  /* Quand la barre de rayons est là, elle finit l'en-tête : le trait de l'onglet
     courant doit toucher le bas du bandeau, pas flotter vingt pixels au-dessus. */
  .shop-head:has(.shop-nav) { padding-bottom: 0; }
  /* Le carré n'est imposé qu'au repli à pictogramme ; un vrai logo se dimensionne
     sur sa propre forme, voir « img.logo-badge ». */
  .shop-head div.logo-badge { width: 58px; height: 58px; border-radius: var(--r-lg); }
  .shop-head h1 { font-size: 1.5rem; letter-spacing: -.025em; }
  .shop-head .sub { font-size: .88rem; }
  .shop-head .icon-btn { width: 46px; height: 46px; }

  /* Barre de rayons : les catégories deviennent une navigation de site, à la
     place du ruban de pastilles qui défile au doigt. */
  .shop-nav {
    display: block; margin-top: var(--s4);
    border-top: 1px solid color-mix(in srgb, var(--on-brand) 20%, transparent);
  }
  .shop-nav .container { display: flex; align-items: center; gap: var(--s1); padding-top: 2px; }
  /* Pleine opacité pour tous les rayons : à 85 % le libellé se mélangeait au
     fond de marque et retombait sous le seuil de lisibilité. C'est le trait
     sous l'onglet, et lui seul, qui distingue le rayon ouvert. */
  .shop-nav a {
    color: var(--on-brand); font-size: .9rem; font-weight: 600;
    padding: var(--s3) var(--s3) calc(var(--s3) - 2px);
    border-bottom: 2px solid transparent;
    white-space: nowrap;
    transition: border-color var(--fast) var(--ease);
  }
  .shop-nav a:hover { text-decoration: none; border-bottom-color: color-mix(in srgb, var(--on-brand) 45%, transparent); }
  .shop-nav a.active { border-bottom-color: var(--on-brand); }
  .shop-nav .shop-nav-wa {
    margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
    border-bottom: none;
  }
  .shop-nav .shop-nav-wa:hover { border-bottom-color: transparent; text-decoration: underline; }
  /* La barre de rayons remplace le ruban de pastilles et la loupe de la page. */
  body.shopfront .page > .pills { display: none; }

  /* Bandeau d'enseigne : la bannière passe en fond plein cadre, le nom, le
     logo et la description viennent par-dessus. C'est ce qui fait qu'un
     visiteur reconnaît « son » site et non une fiche dans un annuaire. */
  .shop-hero {
    position: relative; overflow: hidden;
    border-radius: var(--r-xl); margin-bottom: var(--s6);
    background: var(--grad); color: var(--on-brand);
    box-shadow: var(--shadow-lg);
    display: flex; align-items: flex-end;
  }
  .shop-hero.no-banner { display: flex; }
  /* La hauteur minimale n'a de sens que pour donner sa place à une bannière.
     Appliquée à toutes les boutiques, elle laissait un aplat de couleur vide
     au-dessus du nom chez celles qui n'ont ni bannière ni description — le
     bandeau se règle donc sur son contenu tant qu'il n'y a pas d'image. */
  .shop-hero:has(.shop-hero-bg) { min-height: 260px; }
  .shop-hero:not(:has(.shop-hero-bg)) .shop-hero-in { padding: var(--s6) var(--s8); }
  .shop-hero-bg {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; border-radius: 0; margin: 0;
  }
  /* Voile dégradé : sans lui le nom devient illisible dès que la bannière est
     claire ou chargée. Il emprunte la teinte de la boutique, pas un noir neutre. */
  .shop-hero::after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(180deg,
                color-mix(in srgb, var(--brand-700) 15%, transparent) 0%,
                color-mix(in srgb, var(--brand-700) 82%, transparent) 100%);
  }
  .shop-hero-in {
    display: block; position: relative; z-index: 1;
    width: 100%; padding: var(--s8) var(--s8) var(--s6);
  }
  .shop-hero-mark {
    width: 76px; height: 76px; border-radius: var(--r-lg); margin-bottom: var(--s4);
    background: color-mix(in srgb, var(--on-brand) 18%, transparent);
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
    display: flex; align-items: center; justify-content: center;
    overflow: hidden; object-fit: cover;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-brand) 30%, transparent), var(--shadow);
  }
  img.shop-hero-mark {
    width: auto; max-width: 320px; object-fit: contain;
    padding: var(--s2) var(--s3);
  }
  .shop-hero h2 {
    font-size: clamp(2rem, 1.4rem + 1.8vw, 2.9rem);
    font-weight: 800; letter-spacing: -.035em; margin: 0 0 var(--s2);
  }
  .shop-hero p { max-width: 58ch; opacity: .94; margin: 0 0 var(--s4); }
  .shop-meta { display: flex; flex-wrap: wrap; gap: var(--s2); }
  .shop-meta span {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 12px; border-radius: var(--r-full);
    background: color-mix(in srgb, var(--on-brand) 16%, transparent);
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
    font-size: .82rem; font-weight: 650;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-brand) 18%, transparent);
  }

  /* Catalogue : des vignettes de taille de site, pas de téléphone agrandi. */
  /* Le cadre passe en 4:3 : la plupart des photos de produits sont plus larges
     que hautes, le carré les rognait tout en allongeant la carte. Le calage en
     haut est posé sur « .grid » plus haut, il vaut pour toutes les largeurs. */
  body.shopfront .grid {
    grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
    gap: var(--s4);
  }
  body.shopfront .product .thumb { aspect-ratio: 4 / 3; }
  body.shopfront .product .body { padding: var(--s3) var(--s4) var(--s4); }
  body.shopfront .product .name { font-size: .95rem; }
  body.shopfront .product .order-btn { min-height: 42px; font-size: .86rem; }
  body.shopfront .search { max-width: 420px; margin-left: auto; }
  body.shopfront .page { padding-top: var(--s6); padding-bottom: var(--s10); }

  /* Panier flottant : centré et resserré, il ne barre plus toute la page. */
  body.shopfront .cartbar { bottom: var(--s5); }
  body.shopfront .cartbar .inner { max-width: 540px; }

  /* Fiche produit en deux colonnes : images à gauche, achat à droite.

     Le cadre blanc enveloppait les deux colonnes. Or l'image et sa galerie font
     plus de 500 px de haut quand le bloc d'achat n'en fait que 290 : le cadre
     prenait la hauteur de la plus grande et laissait deux cent cinquante pixels
     de blanc sous le bouton « Ajouter au panier ». Le cadre disparaît donc sur
     grand écran — l'image porte le sien — et il ne reste aucune boîte à remplir.
     Les deux colonnes se calent en haut, chacune à sa hauteur. */
  .pdp-card {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    grid-template-rows: auto auto;
    column-gap: var(--s6);
    align-items: start;
    background: none; border: none; box-shadow: none;
    overflow: visible; margin-bottom: 0;
  }
  .pdp-card .hero-thumb {
    border-radius: var(--r-lg);
    border: 1px solid var(--border);
  }
  /* 4:3 plutôt qu'un carré : la plupart des photos de produits sont plus larges
     que hautes, et « contain » les cernait sinon de deux grandes bandes vides. */
  .pdp-card .hero-thumb { grid-column: 1; grid-row: 1; aspect-ratio: 4 / 3; }
  .pdp-card .gallery { grid-column: 1; grid-row: 2; border-top: none; padding-left: 0; }
  /* Une seule rangée, et calé en haut : posé sur « 1 / -1 » il s'étirait sur
     toute la hauteur de la colonne image, ce qui rouvrait le vide sous le
     bouton même une fois le cadre retiré. */
  .pdp-card .pdp-info {
    grid-column: 2; grid-row: 1; align-self: start;
    padding: 0 0 var(--s4);
  }
  .pdp-card .pdp-info h2 { font-size: 1.6rem; }
  .pdp-card .pdp-info .btn.block { min-height: 54px; }

  /* Pied de page de vitrine : coordonnées, horaires et moyens de paiement.
     C'est la partie qui manquait pour qu'une boutique ait l'air d'un site
     tenu par quelqu'un plutôt que d'une simple liste d'articles. */
  .shop-foot {
    display: block; margin-top: var(--s10);
    background: var(--surface); border-top: 1px solid var(--border);
    padding: var(--s8) 0 var(--s10);
  }
  .shop-foot-main {
    display: grid; grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
    gap: var(--s6); align-items: start;
  }
  .shop-foot-mark {
    width: 48px; height: 48px; border-radius: var(--r-md); margin-bottom: var(--s3);
    background: var(--brand); color: var(--on-brand);
    display: flex; align-items: center; justify-content: center;
    overflow: hidden; object-fit: cover;
  }
  img.shop-foot-mark {
    width: auto; max-width: 200px; object-fit: contain;
    background: none; border-radius: 0;
  }
  .shop-foot h4 {
    font-size: .78rem; text-transform: uppercase; letter-spacing: .06em;
    color: var(--muted); margin: 0 0 var(--s3);
  }
  .shop-foot .fname { font-size: 1.15rem; font-weight: 800; letter-spacing: -.02em; margin-bottom: var(--s2); }
  .shop-foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
  .shop-foot li { font-size: .89rem; color: var(--text-2); display: flex; align-items: flex-start; gap: var(--s2); }
  .shop-foot li .ic-svg { color: var(--brand-600); flex: none; margin-top: 2px; }
  .shop-foot-bar {
    display: flex; justify-content: space-between; flex-wrap: wrap; gap: var(--s3);
    margin-top: var(--s8); padding-top: var(--s4);
    border-top: 1px solid var(--border);
    font-size: .82rem; color: var(--muted);
  }

  /* ---------- Page d'accueil publique ---------- */
  .landing .hero { padding: var(--s10) 0 calc(var(--s10) + var(--s6)); }
  .landing .hero-actions { display: flex; flex-wrap: wrap; gap: var(--s3); }
  .landing .hero-actions .btn.block { width: auto; min-width: 240px; }
  .landing .hero-note { justify-content: flex-start; }
  .step-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); }
  .feature-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .landing .final-cta .btn.block, .landing .demo-card .btn.block { width: auto; min-width: 280px; }
}

/* ==========================================================================
   ORDINATEUR — très grand écran
   ========================================================================== */
@media (min-width: 1440px) {
  :root { --maxw: 1000px; }
  body.shopfront  { --maxw: 1280px; }
  body.has-sidebar { --maxw: 1140px; }
  .shop-hero:has(.shop-hero-bg) { min-height: 320px; }
}
