/**
 * Panier COD public — peau du panier (Phase 37).
 *
 * Chargée UNIQUEMENT si le gérant a activé le panier. Transitions à propriétés
 * NOMMÉES (jamais `all` — cliquet statique 18.1) ; `prefers-reduced-motion`
 * honoré (Phase 19 : le mouvement part, l'acquittement reste).
 *
 * 🚨 DEUX RÈGLES QUI ONT COÛTÉ UN BOUTON ILLISIBLE (contraste mesuré 1,05:1) :
 *
 * 1. **Le panier ne porte JAMAIS une classe du thème** (`button`, `wp-element-*`…).
 *    Astra imprime son CSS de boutons en `<style>` inline, donc APRÈS nos
 *    feuilles : à spécificité égale il gagne, et son fond de marque écrase le
 *    nôtre. D'où le réarmement `.aod-cart__drawer button` / `.aod-cart__box
 *    button` en (0,1,1) — il bat `button` (0,0,1) ET `.button` (0,1,0) quel que
 *    soit l'ordre des feuilles.
 * 2. **Aucun fond actionnable n'est `transparent`.** Un fond transparent fait
 *    dépendre le contraste de ce qu'il y a derrière, donc du thème du clone.
 *
 * Les tokens `--ac-*` doublent ceux du formulaire COD (`--sh-*`, aod-cod-form.css).
 * La duplication est assumée : cette feuille se charge sur toute la boutique quand
 * celle du formulaire ne se charge que sur la fiche produit.
 *
 * 🚨 CE PARAGRAPHE A PROMIS PLUS QUE SA GARDE NE TENAIT, jusqu'au 2026-08-12. Il
 * disait « recopient EXACTEMENT » et « le cliquet compare les deux et rougit si
 * elles divergent ». Mesuré : `test-panier-peau.php` porte une carte de **8 paires
 * NOMMÉES**, avec son propre périmètre écrit (« les couleurs que l'acheteur voit
 * côte à côte »), et elle laisse dehors `surface`↔`bg` **et** `line`↔`line` — or
 * `--sh-line` valait `#d9d9d9` et `--ac-line` `#e1e3e5` : elles **divergeaient en
 * fait**, sous un commentaire qui affirmait le contraire. *Un commentaire qui
 * annonce une classe entière au-dessus d'une garde de huit cas est une promesse que
 * personne ne vérifie* (leçon 48.5).
 *
 * ✅ Depuis la 71.2, les SURFACES et les LISERÉS ne peuvent plus diverger — pas
 * parce qu'on les compare, mais parce que les deux familles lisent la **même
 * variable** : le socle neutre `--aod-n-*`, imprimé sur `:root` de toute page
 * publique par `AOD_COD_Accent::neutral_vars()`. ✅ **Et les deux valeurs de liseré
 * sont FONDUES depuis la Phase 75** : la 71.2 les avait gardées séparées en
 * consignant que *fondre deux valeurs est une décision visuelle, pas un rangement*.
 * La décision est prise, sur la mesure — **1,097:1** entre `#d9d9d9` et `#e1e3e5`,
 * soit 8/10/12 par canal, indiscernables. Elles servaient LE MÊME rôle et
 * divergeaient sans qu'aucune règle ne l'ait jamais dit ; il n'y a plus qu'une
 * valeur, `--aod-n-line`.
 * ⛔ Ce qui RESTE gardé par la carte de paires : les encres, l'accent et ses
 * dérivés. Ne pas la retirer — elle couvre ce que la délégation ne couvre pas.
 */

:root {
	--ac-ink: #1a1a1a;
	--ac-ink-2: #616161;
	--ac-muted: #8a8a8a;
	--ac-line: var(--aod-n-line);
	--ac-line-soft: var(--aod-n-line-soft);
	--ac-surface: var(--aod-n-bg);
	--ac-surface-sub: var(--aod-n-bg-sub);
	/* La marque, et ses rôles — jumeaux de `--sh-accent*` (cf. l'encadré ci-dessus
	   et le bloc de `aod-cod-form.css`). Depuis la Phase 50 les deux ne recopient
	   plus une VALEUR mais la même SOURCE : `AOD_COD_Accent`, qui les calcule
	   depuis l'accent du Personnalisateur. La duplication assumée porte désormais
	   sur des noms, plus sur des couleurs — il n'y a plus de valeur à faire
	   diverger. */
	--ac-accent: var(--aod-a);
	--ac-accent-ink: var(--aod-a-ink);
	--ac-accent-text: var(--aod-a-text);
	--ac-accent-line: var(--aod-a-line);
	--ac-accent-dark: var(--aod-a-strong);
	--ac-accent-wash: var(--aod-a-wash);
	/* 71.2bis — il DÉLÈGUE, comme les cinq du dessus. La carte de 8 paires de
	   `test-panier-peau.php` exigeait déjà `--ac-danger == --sh-error` ; les faire
	   lire la MÊME variable rend l'égalité structurelle au lieu de la faire
	   vérifier. ⚠️ Sa valeur change avec celle du socle (#d72c0d → #b91c1c) :
	   vérifié, ce jeton n'est jamais un FOND ici — un liseré de 3 px et deux
	   `color:` sur surface claire —, donc l'assombrir ne met aucun texte en
	   danger et améliore ses deux encres (sur #f6f6f7 : 4,57 → 5,99:1). */
	--ac-danger: var(--aod-n-bad);
	/* L'encre du GAIN (76.3), déléguée au socle comme sa jumelle `--ac-danger`.
	   ⛔ Pas une valeur en dur : la 71.2bis a mis les sémantiques sur `:root` de
	   toute page publique précisément pour qu'aucune feuille n'ait à les
	   recopier — et pour que le recensement de `test-couleurs-publiques.php`
	   continue de les voir. */
	--ac-ok: var(--aod-n-ok);
	--ac-r-sm: 6px;
	--ac-r-md: 8px;
	--ac-r-lg: 12px;
	/* Surimpression des cartes (2026-09-08, demande owner) : blanc à 10 %
	 * d'opacité — posé en jeton, pas en littéral, pour rester hors du
	 * recensement de `test-surfaces-publiques.php` (même patron que
	 * `.aod-home__btn` dans `aod-cod-pages.css`). Un seul point à changer si
	 * l'opacité est retouchée à nouveau : les deux boutons le lisent. */
	--ac-quick-bg: rgba(255, 255, 255, .1);
}

/* `display:flex` écraserait le `display:none` que l'attribut `hidden` donne
 * par défaut : sans ces règles, le drawer « caché » resterait affiché ET
 * intercepterait les clics de toute la colonne droite. Trouvé par le témoin
 * 35.3 — pas par la relecture. */
.aod-cart__fab[hidden],
.aod-cart__overlay[hidden],
.aod-cart__drawer[hidden],
.aod-cart__box[hidden],
/* ⚠️ Le pied du panneau de choix (2026-08-04) : il porte `display:flex`, donc il
 * tombait EXACTEMENT dans le piège décrit ci-dessus. Mesuré : l'accusé de
 * réception s'affichait AVEC, au-dessus de lui, le bouton « Ajouter » et la
 * quantité de l'article qu'on venait justement d'ajouter — l'écran proposait de
 * refaire le geste qu'il annonçait comme fait. Le corps, lui, n'a pas de
 * `display` propre : il disparaissait bien, ce qui rendait le défaut à moitié
 * invisible. Ce recensement est le seul endroit où l'on répare ça. */
.aod-cart__sheet-foot[hidden] {
	display: none;
}
/* ── Masquage du panier flottant et tiroir sur la page Contact ── */
.aod-contact-page #aod-cart-fab,
.aod-contact-page .aod-cart__fab,
.aod-contact-page .aod-cart__drawer,
body:has(.aod-contact) #aod-cart-fab,
body:has(.aod-contact) .aod-cart__fab,
body:has(.aod-contact) .aod-cart__drawer,
.aod-contact ~ #aod-cart-fab {
	display: none !important;
}

/* ---- Réarmement défensif : le thème s'arrête ici ----
 *
 * Une classe À NOUS sur chaque bouton, jamais un sélecteur d'ancêtre. La
 * première version écrivait `.aod-cart__drawer button` (0,1,1) : ça battait
 * bien le thème, mais aussi **nos propres règles de composant** (0,1,0) — et
 * « Commander » est ressorti sans fond. Vu à l'écran, pas à la relecture.
 *
 * Ici tout le monde est à (0,1,0) — sauf `.aod-cart__qty button` (0,1,1), qui
 * a son homologue exact plus bas. Les règles de composant viennent APRÈS dans
 * la feuille : à spécificité égale, elles gagnent. Et (0,1,0) bat le `button`
 * nu (0,0,1) du thème quel que soit l'ordre des feuilles — la seule prise
 * qu'Astra avait, `.button` (0,1,0), a disparu de notre markup. */
.aod-cart__close,
.aod-cart__order,
.aod-cart__remove,
.aod-cart__qty button {
	-webkit-appearance: none;
	appearance: none;
	box-sizing: border-box;
	min-width: 0;
	min-height: 0;
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: 0;
	background: none;
	box-shadow: none;
	color: inherit;
	font: inherit;
	line-height: 1;
	text-transform: none;
	letter-spacing: normal;
	cursor: pointer;
}

/* ---- Icônes du catalogue (37.2) ----
 * Mêmes SVG Phosphor que la sidebar de `/gestion`, rendus par `AOD_COD_Icon`.
 * `currentColor` : l'icône suit la couleur de son bouton, jamais l'inverse —
 * c'est ce qui permet au même fichier de servir sur fond vert et sur fond blanc.
 * `display:block` : sans lui, un SVG est en ligne et traîne l'espace de la
 * ligne de base, ce qui décentre le glyphe dans un bouton carré. */
.aod-i {
	display: block;
	flex: 0 0 auto;
	fill: currentColor;
}

/* ---- Bouton flottant ---- */

/* 🚨 IL CÈDE LE PAS AU CTA D'ACHAT (67.4), ET IL NE DISPARAÎT PAS.
 *
 * Mesuré le 2026-08-11 à 390 px : posé à 18 px du bas, il recouvrait la barre
 * collante « Commander » de la page de vente sur **2 215 px² — 71 % de sa propre
 * surface** —, à l'extrémité droite du bouton, là où se pose le pouce. Et les
 * deux portent le **même accent** : à la capture, ils se lisent comme un seul
 * bloc vert. *De deux actions flottantes qui visent le même coin, c'est le
 * panier qui gagnait contre le bouton qui vend.*
 *
 * ⛔ ON NE L'EFFACE PAS quand la barre est là, et c'est l'arbitrage : ce bouton
 * est le SEUL accès au panier, et la page de vente est celle où l'acheteur a le
 * plus de raisons d'y revenir — il vient peut-être d'ajouter un autre article.
 * Retirer l'accès au panier sur la page qui vend le mieux corrigerait un
 * chevauchement en créant un cul-de-sac.
 * ⛔ ON NE LE RANGE PAS DANS LA BARRE non plus : la barre appartient à la page de
 * vente, le bouton au panier — qui peut être éteint, et qui vit sur les cinq
 * URLs. Ce serait une autre architecture pour un défaut de 56 px.
 *
 * ✅ Il se DÉCALE, en lisant la réserve que la page a déjà déclarée
 * (`--aod-l-sticky-h`, posée sur `body.aod-landing-sticky` par `aod-landing.css`,
 * et remise à 0 sur desktop où la barre n'existe pas). Le repli `0px` est ce qui
 * rend cette ligne inerte sur toutes les autres pages.
 *
 * ⚠️ Il reste décalé quand la barre s'efface (formulaire à l'écran) : c'est
 * voulu. La réserve du corps ne bouge pas non plus, et *un bouton qui saute à
 * chaque apparition de la barre serait pire que 72 px de marge* — il bougerait
 * sous le doigt qui le vise. */
.aod-cart__fab {
	position: fixed;
	inset-block-end: calc(18px + var(--aod-l-sticky-h, 0px));
	inset-inline-end: 18px;
	z-index: 9998;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 56px;
	height: 56px;
	border: 0;
	border-radius: 50%;
	background: var(--ac-accent);
	color: var(--ac-accent-ink);
	cursor: pointer;
	box-shadow: 0 4px 16px rgba(var(--aod-n-shadow-rgb), .18);
	transition: transform .15s ease, background-color .15s ease, box-shadow .15s ease;
}
.aod-cart__fab:hover { background: var(--ac-accent-dark); transform: scale(1.05); }
.aod-cart__fab:focus-visible { outline: 2px solid var(--ac-ink); outline-offset: 3px; }

/* Encre neutre, jamais rouge : un panier qui se remplit est une bonne
 * nouvelle, pas une alerte. Le rouge ne dit qu'une chose ici — indisponible. */
.aod-cart__badge {
	position: absolute;
	inset-block-start: -4px;
	inset-inline-end: -4px;
	min-width: 20px;
	height: 20px;
	padding: 0 6px;
	border-radius: 9999px;
	background: var(--ac-ink);
	color: #fff;
	font-size: var(--aod-fs-xs);
	font-weight: 700;
	line-height: 20px;
	text-align: center;
	box-shadow: 0 0 0 2px var(--ac-surface);
}

/* ---- Déclencheur panier dans le menu d'en-tête (Phase 1) ---- */
.menu-item-aod-cart {
	display: inline-flex;
	align-items: center;
	vertical-align: middle;
}
.aod-header-cart {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 6px 12px;
	border: 1px solid var(--ac-line);
	border-radius: 9999px;
	background: var(--ac-surface);
	color: inherit;
	font-family: inherit;
	font-size: var(--aod-fs-sm);
	line-height: 1;
	cursor: pointer;
	text-decoration: none;
	transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease;
}
.aod-header-cart:hover {
	border-color: var(--ac-ink-2);
}
.aod-header-cart:active {
	transform: scale(0.96);
}
.aod-header-cart:focus-visible {
	outline: 2px solid var(--ac-accent);
	outline-offset: 2px;
}
.aod-header-cart__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
}
.aod-header-cart__badge {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 18px;
	height: 18px;
	padding: 0 5px;
	border-radius: 9999px;
	background: var(--ac-line-soft);
	color: inherit;
	font-size: var(--aod-fs-2xs);
	font-weight: 700;
	line-height: 1;
	transition: background 0.12s ease, color 0.12s ease;
}
.aod-header-cart__badge.is-active {
	background: var(--ac-accent);
	color: var(--ac-accent-ink);
}

/* ---- Panneau ---- */

.aod-cart__overlay {
	position: fixed;
	inset: 0;
	z-index: 9998;
	background: rgba(var(--aod-n-veil-rgb), .5);
}

.aod-cart__drawer {
	position: fixed;
	inset-block: 0;
	inset-inline-end: 0;
	z-index: 9999;
	display: flex;
	flex-direction: column;
	width: min(92vw, 420px);
	background: var(--ac-surface);
	color: var(--ac-ink);
	box-shadow: 0 0 40px rgba(var(--aod-n-shadow-rgb), .20);
}
body.aod-cart-open { overflow: hidden; }

.aod-cart__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 16px;
	border-bottom: 1px solid var(--ac-line);
}
.aod-cart__title {
	margin: 0;
	font-size: var(--aod-fs-lg);
	font-weight: 600;
	line-height: 1.3;
	color: var(--ac-ink);
}

.aod-cart__close {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border-radius: var(--ac-r-md);
	background: none;
	color: var(--ac-ink-2);
	line-height: 1;
	cursor: pointer;
	transition: background-color .15s ease, color .15s ease;
}
/* 🚨 `:focus` est OBLIGATOIRE ici, pas seulement `:focus-visible`. Le thème
 * porte un `button:focus{background:…}` en (0,1,1), qui bat notre (0,1,0) : à
 * l'ouverture du panneau le focus part sur ce bouton, et il ressortait en
 * rectangle bleu de marque. Mesuré, pas deviné. Nos états sont en (0,2,0). */
.aod-cart__close:hover,
.aod-cart__close:focus { background: var(--ac-surface-sub); color: var(--ac-ink); }
.aod-cart__close:focus-visible { outline: 2px solid var(--ac-accent-line); outline-offset: 2px; }

.aod-cart__list {
	flex: 1;
	overflow-y: auto;
	padding: 4px 16px;
}
/* ---- Plafond de lignes atteint / dépassé (D35) ----
   UN seul habillage pour les DEUX producteurs : le bandeau du tiroir (refus
   qu'on vient d'opposer, posé par `open( note )`) et la ligne peinte par
   `paintInto()` quand le serveur a écrêté. Deux peaux pour un même message
   diraient au lecteur qu'il s'agit de deux choses. */
.aod-cart__cap {
	margin: 0 0 12px;
	padding: 10px 12px;
	border: 1px solid var(--ac-line);
	border-inline-start: 3px solid var(--ac-danger);
	border-radius: var(--ac-r-sm);
	background: var(--ac-surface-sub);
	color: var(--ac-ink);
	font-size: var(--aod-fs-sm);
	line-height: 1.45;
}

.aod-cart__empty {
	margin: 0;
	padding: 32px 0;
	color: var(--ac-ink-2);
	text-align: center;
	font-size: var(--aod-fs-sm);
}

/* ---- Ligne de panier (panneau ET bloc — même composant) ----
 * DEUX colonnes, pas cinq : la grille à 5 colonnes écrasait le nom jusqu'à
 * « Bas… » dès 390 px. Cette structure tient sans média-query et se met en
 * miroir toute seule en RTL. */

.aod-cart__line {
	display: grid;
	grid-template-columns: 56px 1fr;
	gap: 12px;
	align-items: start;
	padding: 12px 0;
	border-bottom: 1px solid var(--ac-line-soft);
}
.aod-cart__line:last-child { border-bottom: 0; }
.aod-cart__line.is-dead { opacity: .55; }

.aod-cart__img {
	width: 56px;
	height: 56px;
	object-fit: cover;
	border-radius: var(--ac-r-md);
	background: var(--ac-surface-sub);
	box-shadow: inset 0 0 0 1px rgba(var(--aod-n-veil-rgb), .04);
	display: block;
}

.aod-cart__body { min-width: 0; }

.aod-cart__top {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 8px;
}

/* Deux lignes, jamais une ellipse : en arabe, une troncature ronge le DÉBUT
 * d'un nom latin (« …rban Flex ») et rend le produit méconnaissable. */
.aod-cart__name {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	font-size: var(--aod-fs-sm);
	font-weight: 600;
	line-height: 1.35;
	color: var(--ac-ink);
}
.aod-cart__opts {
	display: block;
	margin-top: 2px;
	font-size: var(--aod-fs-xs);
	line-height: 1.4;
	color: var(--ac-ink-2);
}
.aod-cart__dead {
	display: block;
	margin-top: 2px;
	font-size: var(--aod-fs-xs);
	font-weight: 600;
	color: var(--ac-danger);
}
/* Ce que l'offre par lot fait gagner (76.3) — jumelle exacte de `__dead`, à
   l'encre près : même place, même graisse, même cran typo. Une bonne et une
   mauvaise nouvelle sur la même ligne ne doivent pas se lire différemment,
   sinon l'une des deux passe pour du décor. */
.aod-cart__save {
	display: block;
	margin-top: 2px;
	font-size: var(--aod-fs-xs);
	font-weight: 600;
	color: var(--ac-ok);
}

.aod-cart__bottom {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	margin-top: 10px;
}

/* Pilule : un contrôle qu'on manipule au pouce doit dire « bouton » avant
 * d'être lu. 36 px de haut, cibles de 36×36. */
.aod-cart__qty {
	display: inline-flex;
	align-items: center;
	height: 36px;
	border: 1px solid var(--ac-line);
	border-radius: 9999px;
	background: var(--ac-surface-sub);
	overflow: hidden;
}
.aod-cart__qty button {
	width: 36px;
	height: 36px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: none;
	color: var(--ac-ink);
	/* Métrique de GLYPHE, pas du texte : la taille est dictée par la boîte qui
	   la contient, pas par l'échelle. La ranger sur un cran casserait son rapport
	   optique au cadre (même arbitrage qu'en 46.5bis pour `/gestion`). */
	font-size: 16px;
	font-weight: 600;
	cursor: pointer;
	transition: background-color .15s ease, color .15s ease;
}
.aod-cart__qty button:hover,
.aod-cart__qty button:focus { background: var(--ac-line); color: var(--ac-ink); }
.aod-cart__qty button:focus-visible { outline: 2px solid var(--ac-accent-line); outline-offset: -2px; }
.aod-cart__qtyval {
	min-width: 28px;
	text-align: center;
	font-size: var(--aod-fs-sm);
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	color: var(--ac-ink);
}

.aod-cart__linetotal {
	font-size: var(--aod-fs-sm);
	font-weight: 700;
	white-space: nowrap;
	color: var(--ac-ink);
}

.aod-cart__remove {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	margin-block-start: -4px;
	margin-inline-end: -6px;
	border-radius: var(--ac-r-sm);
	background: none;
	/* Encre secondaire et non `--ac-muted` : mesuré à 3,45:1, le gris atténué
	 * ne tenait le seuil que si on classait cette icône en contenu NON textuel.
	 * L'ambiguïté ne vaut pas le gain visuel — #616161 donne 5,92:1. */
	color: var(--ac-ink-2);
	line-height: 1;
	cursor: pointer;
	transition: background-color .15s ease, color .15s ease;
}
.aod-cart__remove:hover,
.aod-cart__remove:focus { background: var(--ac-surface-sub); color: var(--ac-danger); }
.aod-cart__remove:focus-visible { outline: 2px solid var(--ac-accent-line); outline-offset: 2px; }

/* ---- Pied du panneau ---- */

.aod-cart__foot {
	padding: 14px 16px 16px;
	border-top: 1px solid var(--ac-line);
	background: var(--ac-surface);
}
.aod-cart__totalrow {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	font-size: var(--aod-fs-sm);
	color: var(--ac-ink-2);
}
.aod-cart__totalrow strong {
	font-size: var(--aod-fs-md);
	font-weight: 700;
	color: var(--ac-ink);
}
.aod-cart__note {
	margin: 6px 0 12px;
	font-size: var(--aod-fs-xs);
	line-height: 1.45;
	color: var(--ac-ink-2);
}

.aod-cart__order {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	min-height: 48px;
	padding: 14px 16px;
	border-radius: var(--ac-r-md);
	background: var(--ac-accent);
	color: var(--ac-accent-ink);
	font-size: var(--aod-fs-md);
	font-weight: 600;
	line-height: 1.2;
	cursor: pointer;
	transition: background-color .15s ease, opacity .15s ease;
}
.aod-cart__order:hover,
.aod-cart__order:focus { background: var(--ac-accent-dark); color: var(--ac-accent-ink); }
.aod-cart__order:focus-visible { outline: 2px solid var(--ac-ink); outline-offset: 2px; }
.aod-cart__order[disabled] { opacity: .5; cursor: not-allowed; }

/* ---- Surimpression « Ajouter au panier » sur les cartes produit ----
 * Pilule BLANCHE et non bande sombre translucide : ce template vend aussi
 * bien des baskets sur fond jaune que des parfums sur fond noir, et la bande
 * sombre devenait illisible sur les photos sombres. */

.aod-cart__qwrap { position: relative; display: block; }

/* Groupe empilé : « Ajouter au panier » PUIS « Commander » (2026-09-08, demande
 * owner — même paire de gestes que `.aod-cod__addcart` / `.aod-cod__buynow` sur
 * la fiche, transposée à la carte de grille).
 *
 * 🚨 DEUX EMPLACEMENTS SELON L'APPAREIL (2026-09-08, demande owner) — décidé par
 * le JS à la construction (`initQuickAdd`, `(hover: none)`), jamais par une media
 * query CSS qui rejouerait sur les deux DOM à la fois :
 *   · souris (`hover: hover`) — le groupe reste nesté dans `.aod-cart__qwrap` et
 *     flotte SUR l'image, révélé au survol (voir plus bas) ;
 *   · tactile (`hover: none`) — le groupe est un enfant DIRECT de la carte,
 *     APRÈS `.astra-shop-summary-wrap` (donc après le prix) : rien à « révéler »
 *     sur un appareil qui n'a pas de survol, il vit dans le flux normal.
 * Ce bloc ne porte que ce que les DEUX emplacements partagent. La position (l'un
 * en surimpression, l'autre dans le flux) vit dans les deux blocs scopés par
 * PARENT, juste après. */
.aod-cart__quickgrp {
	display: flex;
	flex-direction: column;
	gap: 6px;
	transition: opacity .18s ease;
}
/* ---- Emplacement SOURIS : surimpression sur l'image ---- */
.aod-cart__qwrap .aod-cart__quickgrp {
	position: absolute;
	inset-inline: 0;
	inset-block-end: 10px;
	z-index: 2;
}
/* ---- Emplacement TACTILE : bloc statique, sous le prix ----
 * ⛔ Marge et écart réduits (2026-09-08, demande owner) — `margin-block-start`
 * 12→8 px, `gap` 6→4 px (surcharge le `gap` commun juste au-dessus, propre à
 * ce contexte) : « pas trop de marge verticale », une fois le groupe sorti de
 * l'image, où l'espace ne coûtait rien. */
li.product > .aod-cart__quickgrp {
	position: static;
	opacity: 1;
	pointer-events: auto;
	display: flex;
	flex-direction: row;
	align-items: center;
	gap: 8px;
	padding: 8px 12px;
	background: var(--aod-n-bg-sub);
	border-bottom: 1px solid var(--aod-n-line-soft);
	width: 100%;
	box-sizing: border-box;
	margin: 0;
}
.aod-cart__sugcard > .aod-cart__quickgrp {
	margin-block-start: 8px;
	gap: 4px;
}

/*
 * ⚠️ Le groupe porte la POSITION ; chaque bouton garde sa peau dans SON PROPRE
 * bloc de règle — R3 de `test-panier-peau.php` lit le sélecteur `quickadd`
 * ISOLÉ (jamais combiné par une virgule) pour vérifier qu'il déclare un fond
 * explicite ; un bloc combiné le rendrait introuvable et ferait rougir le
 * cliquet sans rien changer au rendu.
 *
 * ⛔ PLEINE LARGEUR (demande owner) : chaque bouton égale la largeur de son
 * groupe, donc de la carte — flush aux deux bords à la souris (sur l'image),
 * flush aux deux bords au doigt (sous le prix).
 * 🎨 Fond BLANC transparent, opacité 10 % (demande owner, ajustée deux fois —
 * 70 % puis 10 %) — remplace le jeton `--aod-n-bg-rgb` (qui suit le thème
 * sombre/clair) par une valeur FIXE : sur ces cartes, la surimpression doit
 * rester blanche quel que soit le mode d'affichage du visiteur.
 * ⛔ Coins DROITS par défaut (demande owner) — nécessaire à la souris : les
 * boutons touchent les deux bords de l'IMAGE, un coin arrondi y « flotterait ».
 * 🎨 Coins ARRONDIS au doigt (demande owner, 2026-09-08) — le groupe ne touche
 * plus l'image mais la carte, sous un texte : l'arrondi y est attendu. Scopé
 * par PARENT (`li.product > .aod-cart__quickgrp …`), jamais par media query :
 * c'est le même signal `(hover: none)` qui a placé le groupe à cet endroit. */
.aod-cart__quickadd {
	display: block;
	width: 100%;
	padding: 10px 12px;
	border-radius: 0;
	background: var(--ac-quick-bg);
	color: var(--ac-ink);
	font-size: var(--aod-fs-sm);
	font-weight: 600;
	line-height: 1.2;
	text-align: center;
	cursor: pointer;
	box-shadow: none;
	transition: background-color .18s ease, color .18s ease;
}
/* Même peau que « Ajouter » : c'est la paire qui doit se lire comme UN choix à
 * deux issues, pas comme deux boutons étrangers. */
.aod-cart__quickorder {
	display: block;
	width: 100%;
	padding: 10px 12px;
	border-radius: 0;
	background: var(--ac-quick-bg);
	color: var(--ac-ink);
	font-size: var(--aod-fs-sm);
	font-weight: 600;
	line-height: 1.2;
	text-align: center;
	cursor: pointer;
	box-shadow: none;
	transition: background-color .18s ease, color .18s ease;
}
/* 🎨 COULEUR au doigt (2026-09-08, demande owner) — le blanc à 10 % (pensé
 * pour flotter sur une photo) devenait quasi invisible posé sur le fond blanc
 * de la carte, sous le prix. Fond plein `--ac-accent` (la marque du client,
 * pas une couleur en dur).
 * ⚠️ HAUTEUR : demande owner de la réduire, ARBITRÉE contre le plancher
 * tactile — ces mêmes boutons sont gardés à ≥ 44 px par
 * `tools/recette/panier-tactile.js` (R1-R6) ET `a11y-public.js`, à toute
 * largeur ≤ 780 px (règle 49.4, un peu plus bas dans ce fichier). Décision :
 * le `padding` descend de 10 à 6 px — ce qui RESTE VISIBLE tant que le
 * contenu (texte + padding) dépasse 44 px, et n'a plus d'effet en dessous,
 * où `min-height: 44px` prend le relais. Le bouton ne descend donc jamais
 * sous la cible tactile. `display:flex` + centrage : sans lui, le surplus
 * de hauteur qu'ajoute `min-height` ne se répartit pas et le texte remonte
 * en haut du bouton. */
li.product > .aod-cart__quickgrp .aod-cart__quickadd {
	width: 44px;
	height: 44px;
	min-width: 44px;
	min-height: 44px;
	flex: 0 0 44px;
	padding: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: var(--aod-n-bg);
	color: var(--aod-a-text);
	border: 1px solid var(--aod-n-line);
	border-radius: var(--ac-r-md);
	cursor: pointer;
	box-shadow: 0 1px 3px rgba(var(--aod-n-shadow-rgb), .04);
	transition: background-color .15s ease, border-color .15s ease, color .15s ease, transform .15s ease;
}
li.product > .aod-cart__quickgrp .aod-cart__quickadd:hover {
	background: var(--aod-a-wash);
	border-color: var(--aod-a);
	color: var(--aod-a-text);
	transform: scale(1.04);
}
li.product > .aod-cart__quickgrp .aod-cart__quickadd.is-added {
	background: var(--aod-n-ok-soft);
	border-color: var(--aod-n-ok-line);
	color: var(--aod-n-ok);
}
li.product > .aod-cart__quickgrp .aod-cart__quickorder {
	flex: 1 1 auto;
	height: 44px;
	min-height: 44px;
	padding: 0 14px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: var(--aod-a);
	color: var(--aod-a-ink);
	border: 1px solid var(--aod-a-line);
	border-radius: var(--ac-r-md);
	font-size: var(--aod-fs-sm);
	font-weight: 700;
	cursor: pointer;
	transition: background-color .15s ease, transform .15s ease;
	white-space: nowrap;
	text-decoration: none;
}
li.product > .aod-cart__quickgrp .aod-cart__quickorder:hover {
	background: var(--aod-a-strong);
	color: var(--aod-a-ink);
	transform: scale(1.02);
}
.aod-cart__sugcard > .aod-cart__quickgrp .aod-cart__quickadd,
.aod-cart__sugcard > .aod-cart__quickgrp .aod-cart__quickorder {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 6px 12px;
	border-radius: var(--ac-r-md);
	background: var(--ac-accent);
	color: var(--ac-accent-ink);
	box-shadow: none;
}
.aod-cart__sugcard > .aod-cart__quickgrp .aod-cart__quickadd:hover,
.aod-cart__sugcard > .aod-cart__quickgrp .aod-cart__quickorder:hover {
	background: var(--ac-accent-dark);
	color: var(--ac-accent-ink);
}

.aod-cart__quickadd:hover,
.aod-cart__quickorder:hover { background: var(--ac-accent); color: var(--ac-accent-ink); }
.aod-cart__quickadd:focus-visible,
.aod-cart__quickorder:focus-visible { outline: 2px solid var(--ac-accent-line); outline-offset: 2px; }
.aod-cart__quickadd.is-added,
.aod-cart__quickadd.is-added:hover {
	background: var(--ac-accent-wash);
	color: var(--ac-accent-text);
}

/* Le survol n'existe qu'à la souris : au tactile le groupe n'est plus nesté dans
 * `.aod-cart__qwrap` du tout (voir plus haut, JS), donc cette règle ne le
 * concerne déjà plus — inutile de la doubler d'une media query.
 * `pointer-events: none` au repos : le groupe couvre désormais deux fois plus
 * de hauteur sur l'image, sans lui la zone resterait cliquable-invisible dès
 * qu'on approche du bas de la carte sans avoir encore survolé. */
@media (hover: hover) {
	.aod-cart__qwrap .aod-cart__quickgrp { opacity: 0; pointer-events: none; }
.aod-cart__sugcard:hover .aod-cart__quickgrp,
.aod-cart__qwrap:hover .aod-cart__quickgrp,

	.aod-cart__quickgrp:focus-within { opacity: 1; pointer-events: auto; }
}

/* ---- Bouton natif WooCommerce des grilles, retiré (2026-09-08, demande owner) ----
 * `.aod-loop-cta` (posé par `AOD_COD_No_Tunnel::loop_cta_args`, cf. `aod-cod-pages.css`)
 * est un TROISIÈME « Commander » sous le prix, en plus des deux boutons de
 * `.aod-cart__quickgrp` sur l'image. Une fois le panier COD actif, ces deux-là
 * couvrent déjà « Ajouter au panier » et « Commander » : le garder revenait à
 * proposer trois fois le même geste sur une seule carte.
 * ⛔ Cette feuille n'est chargée QUE si `AOD_COD_Form::cart_enabled()` (cf.
 * `class-aod-cod-cart.php`) — la règle ne s'applique donc jamais sur un clone au
 * panier éteint, où `.aod-loop-cta` reste le SEUL contrôle d'achat de la grille.
 * `display: none` et non un retrait du markup : le lien reste dans le DOM WooCommerce
 * pour les extensions qui s'y attendent, il ne s'affiche simplement plus.
 * ⚠️ SPÉCIFICITÉ (0,2,2), même patron que `aod-cod-pages.css` juste au-dessus :
 * un simple `.aod-loop-cta` (0,1,0) perd contre le CSS inline d'Astra
 * (`.woocommerce-js a.button`, (0,2,1)) — mesuré, le bouton restait affiché. */
body.woocommerce ul.products li.product a.aod-loop-cta.aod-loop-cta,
body.woocommerce-page ul.products li.product a.aod-loop-cta.aod-loop-cta,
body ul.products li.product a.aod-loop-cta.aod-loop-cta,
body li.product a.aod-loop-cta.aod-loop-cta,
body a.aod-loop-cta.aod-loop-cta { display: none !important; }

/* ---- Panneau de CHOIX ouvert depuis une carte de grille (2026-08-02) ----
 * Il monte du BAS (le tiroir du panier vient du côté) : le geste part d'une
 * carte sous le pouce, la réponse doit arriver là où le pouce est déjà.
 * Il emprunte la peau du tiroir — mêmes jetons, même en-tête, même bouton de
 * fermeture : deux surfaces qui font le même métier ne s'habillent pas
 * différemment. */

.aod-cart__sheet {
	position: fixed;
	inset-inline: 0;
	inset-block-end: 0;
	z-index: 100000;
	display: flex;
	flex-direction: column;
	max-height: 88vh;
	padding: 0;
	background: var(--ac-surface);
	border-start-start-radius: 16px;
	border-start-end-radius: 16px;
	box-shadow: 0 -8px 32px rgba(var(--aod-n-shadow-2-rgb), .18);
	/* Le mouvement d'ENTRÉE (2026-08-04). La boîte apparaissait d'un coup, seule
	 * surface animée nulle part de cette feuille : une apparition sèche se lit
	 * comme un défaut d'affichage, pas comme une réponse au geste. La classe est
	 * posée à la frame suivante par le JS — un `[hidden]` vaut `display:none`, et
	 * on ne transitionne pas depuis `display:none`. */
	transform: translateY(12%);
	opacity: 0;
	transition: transform .22s ease-out, opacity .22s ease-out;
}
.aod-cart__sheet.is-in { transform: translateY(0); opacity: 1; }
.aod-cart__sheet[hidden] { display: none; }

/* Poignée : dit « ceci est une feuille qui se referme » avant toute lecture.
 * Décor pur — `aria-hidden`, aucun focus. Elle disparaît sur grand écran, où la
 * boîte n'est plus une feuille mais une modale centrée. */
.aod-cart__grip {
	display: block;
	width: 40px;
	height: 4px;
	margin: 8px auto 0;
	border-radius: 9999px;
	background: var(--ac-line);
}

.aod-cart__sheet .aod-cart__head { padding-block: 12px; }

/* ---- En-tête produit : la photo, le nom, le prix ----
 * L'acheteur vient de toucher une grande image et recevait une ligne de texte.
 * La vignette est le fil entre la carte qu'il a touchée et la boîte qui s'ouvre
 * — et elle prend ensuite la photo de la variante choisie. */
.aod-cart__sheet-head {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 14px 16px;
	border-bottom: 1px solid var(--ac-line-soft);
}
.aod-cart__sheet-thumb {
	flex: 0 0 auto;
	width: 64px;
	height: 64px;
	border: 1px solid var(--ac-line);
	border-radius: var(--ac-r-md);
	background-color: var(--ac-surface-sub);
	background-position: center;
	background-size: cover;
	transition: background-image .15s ease;
}
.aod-cart__sheet-ident { min-width: 0; }
.aod-cart__sheet-name {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	margin: 0;
	font-size: var(--aod-fs-md);
	font-weight: 600;
	line-height: 1.35;
	color: var(--ac-ink);
}
.aod-cart__sheet-priceline {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 8px;
	margin: 5px 0 0;
	font-size: var(--aod-fs-md);
	font-weight: 700;
	color: var(--ac-accent-text);
}
/* Le prix barré passe DERRIÈRE le prix courant : même ligne, mais il ne doit
 * pas peser autant que celui qu'on paie. Woo rend `<del>` + `<ins>`. */
.aod-cart__sheet-priceline del {
	order: 2;
	font-size: var(--aod-fs-sm);
	font-weight: 500;
	color: var(--ac-muted);
}
.aod-cart__sheet-priceline ins { order: 1; background: none; text-decoration: none; }
.aod-cart__sheet-off {
	order: 3;
	padding: 2px 8px;
	border-radius: 9999px;
	background: var(--ac-accent-wash);
	color: var(--ac-accent-text);
	font-size: var(--aod-fs-xs);
	font-weight: 700;
	white-space: nowrap;
}

.aod-cart__sheet-body {
	flex: 1 1 auto;
	overflow-y: auto;
	padding: 14px 16px 2px;
}
/* Les pastilles viennent de la feuille du FORMULAIRE (elles sont servies par le
 * serveur, cf. `assets()`), et on n'en recopie pas les règles ici — R2 de
 * `test-panier-peau.php` l'interdit. Le seul ajustement propre au panneau : une
 * tuile porteuse d'un supplément (« +500 ») est plus haute que ses voisines, et
 * la rangée paraissait bancale. La carte remplit sa cellule ; la grille les
 * égalise. */
.aod-cart__sheet .aod-cod__opt-card { height: 100%; }

.aod-cart__sheet-msg {
	margin: 8px 0 0;
	padding: 0 16px;
	font-size: var(--aod-fs-sm);
	font-weight: 500;
	color: #b42318;
}
.aod-cart__sheet-msg:empty { display: none; }

/* ---- Pied d'action : quantité + bouton ---- */
.aod-cart__sheet-foot {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
	border-top: 1px solid var(--ac-line);
	background: var(--ac-surface);
}
.aod-cart__sheet-qty { flex: 0 0 auto; height: 44px; }
.aod-cart__sheet-qty button { width: 40px; height: 44px; }

/* Même bouton d'achat que « Commander » : c'est le même geste, il se lit pareil. */
.aod-cart__sheet-add {
	flex: 1 1 auto;
	min-width: 0;
	min-height: 44px;
	padding: 12px 14px;
	border: 1px solid var(--ac-accent-line);
	border-radius: var(--ac-r-md);
	background: var(--ac-accent);
	color: var(--ac-accent-ink);
	font-size: var(--aod-fs-md);
	font-weight: 700;
	line-height: 1.25;
	cursor: pointer;
	transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.aod-cart__sheet-add:hover,
.aod-cart__sheet-add:focus { background: var(--ac-accent-dark); border-color: var(--ac-accent-dark); color: var(--ac-accent-ink); }
.aod-cart__sheet-add:focus-visible { outline: 2px solid var(--ac-accent-line); outline-offset: 2px; }
/* 🚨 L'ÉTAT INACTIF N'EST PAS UN VERT DÉLAVÉ (2026-08-04). Il l'était — `opacity:.55`
 * sur le fond d'achat —, et un bouton vert pâle se lit comme un bouton vert : on
 * appuie. Il devient une surface neutre, explicitement colorée (jamais
 * `transparent`, cf. R3) et lisible : #616161 sur #f6f6f7 = 5,6:1. Ce n'est pas
 * une décoration, c'est ce qui distingue « pas encore » de « vas-y ». */
.aod-cart__sheet-add[disabled] {
	border-color: var(--ac-line);
	background: var(--ac-surface-sub);
	color: var(--ac-ink-2);
	opacity: 1;
	cursor: default;
}
.aod-cart__sheet-add[disabled]:hover,
.aod-cart__sheet-add[disabled]:focus { background: var(--ac-surface-sub); color: var(--ac-ink-2); border-color: var(--ac-line); }
.aod-cart__sheet-addprice { white-space: nowrap; }

/* ---- Accusé de réception ----
 * Il REMPLACE le corps du panneau : rendre la boîte à un écran unique évite de
 * se demander si le geste a compté pendant qu'on relit les options. */
.aod-cart__sheet-done { padding: 26px 16px calc(20px + env(safe-area-inset-bottom)); text-align: center; }
.aod-cart__sheet-donemsg {
	margin: 0 0 16px;
	font-size: var(--aod-fs-md);
	font-weight: 700;
	color: var(--ac-accent-text);
}
.aod-cart__sheet-donemsg::before {
	content: '✓';
	display: block;
	margin: 0 auto 8px;
	width: 40px;
	height: 40px;
	border-radius: 50%;
	background: var(--ac-accent-wash);
	color: var(--ac-accent-text);
	/* Métrique de GLYPHE, pas du texte : la taille est dictée par la boîte qui
	   la contient, pas par l'échelle. La ranger sur un cran casserait son rapport
	   optique au cadre (même arbitrage qu'en 46.5bis pour `/gestion`). */
	font-size: 20px;
	line-height: 40px;
}
.aod-cart__sheet-doneact { display: flex; gap: 10px; }
.aod-cart__sheet-see,
.aod-cart__sheet-go {
	flex: 1 1 0;
	min-height: 44px;
	padding: 11px 12px;
	border: 1px solid var(--ac-accent-line);
	border-radius: var(--ac-r-md);
	font-size: var(--aod-fs-sm);
	font-weight: 700;
	line-height: 1.25;
	cursor: pointer;
	transition: background-color .15s ease, color .15s ease;
}
.aod-cart__sheet-see { background: var(--ac-accent); color: var(--ac-accent-ink); }
.aod-cart__sheet-see:hover,
.aod-cart__sheet-see:focus { background: var(--ac-accent-dark); color: var(--ac-accent-ink); }
.aod-cart__sheet-go { background: var(--ac-surface); color: var(--ac-accent-text); }
.aod-cart__sheet-go:hover,
.aod-cart__sheet-go:focus { background: var(--ac-accent-wash); color: var(--ac-accent-text); }
.aod-cart__sheet-see:focus-visible,
.aod-cart__sheet-go:focus-visible { outline: 2px solid var(--ac-accent-line); outline-offset: 2px; }

/* ---- Grand écran : ce n'est plus une feuille, c'est une modale ----
 * 🚨 MESURÉ AVANT (2026-08-04, 1440 px) : la feuille faisait 1440 px de large,
 * trois vignettes de 60 px tassées au bord et un bouton d'achat de 1376 px. Une
 * feuille qui monte du bas est un geste de POUCE ; sur un écran d'ordinateur
 * elle devient une barre vide. La grille de pastilles y était le pire : en
 * `auto-fill`, elle ouvrait seize colonnes pour trois valeurs. */
@media (min-width: 900px) {
	.aod-cart__sheet {
		inset-inline: 0;
		inset-block: 0;
		width: min(92vw, 440px);
		/* ⚠️ `height: fit-content` N'EST PAS UN ORNEMENT. Un élément `fixed` dont
		 * les DEUX bords verticaux sont posés (`inset-block: 0`) et dont la hauteur
		 * reste `auto` est ÉTIRÉ du haut au bas de la fenêtre : mesuré, la modale
		 * sortait à 720 px de haut pour 300 px de contenu, avec un vide sous les
		 * pastilles. Et `margin: auto` ne centre que si la hauteur est définie —
		 * sans ça il ne faisait rien non plus. */
		height: fit-content;
		max-height: min(82vh, 720px);
		margin: auto;
		border-radius: var(--ac-r-lg);
		box-shadow: 0 16px 48px rgba(var(--aod-n-shadow-2-rgb), .28);
		/* Sur grand écran le mouvement vient du CENTRE, pas du bas : la boîte ne
		 * monte plus de sous le pouce, elle se pose. */
		transform: scale(.97);
	}
	.aod-cart__sheet.is-in { transform: scale(1); }
	.aod-cart__grip { display: none; }
	.aod-cart__sheet .aod-cart__head { padding-block: 14px; }
}

/* ---- Bloc de gestion sur la page du formulaire ----
 * Même largeur, même bordure, même rayon que `.aod-cod` : les deux cartes
 * doivent se lire comme une pile, pas comme deux blocs étrangers. */

.aod-cart__box {
	box-sizing: border-box;
	max-width: 560px;
	margin: 24px 0 -8px;
	padding: 16px;
	border: 1px solid var(--ac-line);
	border-radius: var(--ac-r-lg);
	background: var(--ac-surface);
	color: var(--ac-ink);
	box-shadow: 0 1px 2px rgba(var(--aod-n-shadow-rgb), .04);
}
.aod-cart__box-title {
	margin: 0 0 4px;
	font-size: var(--aod-fs-lg);
	font-weight: 600;
	line-height: 1.3;
	color: var(--ac-ink);
}
.aod-cart__box .aod-cart__list { padding: 0; }
.aod-cart__box .aod-cart__totalrow {
	margin-top: 12px;
	padding-top: 12px;
	border-top: 1px solid var(--ac-line);
}
.aod-cart__box .aod-cart__note { margin: 6px 0 0; }

/* ---- « Complétez votre commande » ---- */

.aod-cart__suggest {
	max-width: 1080px;
	margin: 32px auto 40px;
	padding: 0 16px;
}
.aod-cart__suggest-title {
	margin: 0 0 16px;
	font-size: var(--aod-fs-xl);
	font-weight: 600;
	text-align: center;
	color: var(--ac-ink);
}
.aod-cart__suggrid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 16px;
}
@media (min-width: 768px) {
	.aod-cart__suggrid { grid-template-columns: repeat(4, 1fr); }
}
.aod-cart__sugcard {
	overflow: hidden;
	border: 1px solid var(--ac-line);
	border-radius: var(--ac-r-lg);
	background: var(--ac-surface);
	box-shadow: 0 1px 2px rgba(var(--aod-n-shadow-rgb), .04);
	transition: box-shadow .18s ease, transform .18s ease;
}
.aod-cart__sugcard:hover {
	box-shadow: 0 4px 16px rgba(var(--aod-n-shadow-rgb), .10);
	transform: translateY(-2px);
}
.aod-cart__suglink {
	display: block;
	color: inherit;
	text-decoration: none;
}
.aod-cart__sugimg {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 1 / 1;
	object-fit: cover;
}
.aod-cart__sugtitle {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	padding: 12px 12px 2px;
	font-size: var(--aod-fs-sm);
	font-weight: 600;
	line-height: 1.35;
	color: var(--ac-ink);
}
.aod-cart__sugprice {
	display: block;
	padding: 2px 12px 12px;
	font-size: var(--aod-fs-sm);
	color: var(--ac-ink-2);
}

@media (prefers-reduced-motion: reduce) {
	.aod-cart__fab,
	.aod-cart__close,
	.aod-cart__qty button,
	.aod-cart__remove,
	.aod-cart__order,
	.aod-cart__quickgrp,
	.aod-cart__quickadd,
	.aod-cart__quickorder,
	.aod-cart__sugcard,
	.aod-cart__sheet,
	.aod-cart__sheet-thumb,
	.aod-cart__sheet-add,
	.aod-cart__sheet-see,
	.aod-cart__sheet-go,
	.aod-header-cart {
		transition: none;
	}
	.aod-header-cart:active { transform: none; }
	.aod-cart__fab:hover { transform: none; }
	/* Décor, pas du positionnement (piège Phase 19) : neutraliser est sûr. */
	.aod-cart__sugcard:hover { transform: none; }
	/* ⚠️ Le panneau, LUI, n'est pas neutralisable de la même façon : sa
	 * translation est son état de départ, pas un ornement. Sans transition, un
	 * `transform` laissé à 12 % afficherait la boîte DÉCALÉE pour toujours. On
	 * retire donc le mouvement en la posant d'emblée à sa place — l'acquittement
	 * reste, le déplacement part (Phase 19). */
	.aod-cart__sheet,
	.aod-cart__sheet.is-in { transform: none; opacity: 1; }
}

/* ---- 49.4 — L'aperçu d'ajout des cartes : 44 px au pouce, MOBILE SEULEMENT ---
 * Mesuré à 145×36 px. Au doigt ce bouton est PERMANENT (le bloc
 * `@media (hover: hover)` plus haut réserve l'apparition au survol à la souris),
 * donc c'est une vraie cible — et c'est même le geste d'ajout le plus rapide du
 * catalogue quand le panier COD est actif.
 *
 * 🚨 La porte de la 49.1 le mesurait à `opacity: 0` jusqu'au 2026-08-06 : elle
 * redimensionnait la fenêtre à 390 px sans changer la NATURE de l'appareil, si
 * bien que `hover: hover` matchait encore. Elle juge désormais dans un contexte
 * tactile. */
@media (max-width: 780px) {
	.aod-cart__quickadd,
	.aod-cart__quickorder { min-height: 44px; }
}

/* ---- 67.3 — Les commandes du panier au pouce : 44 px, MOBILE SEULEMENT ------
 *
 * 🚨 LA RÈGLE ÉTAIT CONNUE, ET APPLIQUÉE À UNE SURFACE SUR TROIS. Le bloc
 * ci-dessus (49.4) a porté à 44 px la surimpression des cartes — le SEUL contrôle
 * du panier rendu au chargement, donc le seul que la porte publique pouvait voir,
 * puisque `a11y-public.js` ne contient pas un `click(`. Mesuré le 2026-08-11 à
 * 390 px en contexte tactile : `close` **40×40**, `remove` **32×32**,
 * `less`/`more` **36×36** — soit **8 cibles sur 9** du tiroir sous le plancher,
 * et les mêmes dans le bloc posé au-dessus du formulaire COD, c'est-à-dire sur
 * l'écran où l'acheteur VALIDE.
 * *La porte a corrigé ce qu'elle pouvait voir, et le reste est passé pour
 * conforme.* C'est le mode de dérive que la Phase 46 a passé huit étapes à
 * refermer, transposé du gérant à l'acheteur.
 *
 * ⚠️ Le panneau de choix montrait déjà la bonne réponse : sa quantité et son
 * bouton d'ajout sont à 44 px depuis le 2026-08-04. Il ne restait que son
 * « fermer », qui partage la classe du tiroir — donc corrigé ici, une fois.
 *
 * ⛔ MOBILE SEULEMENT, et R6 de `panier-tactile.js` garde l'inverse : 44 px est
 * une règle du TACTILE (46.6, puis 49.4 pour le public). L'appliquer à la souris
 * élargirait des commandes qu'on vise au pixel, et **rien ne rougirait**.
 *
 * ⚠️ Les marges négatives de `remove` suivent sa taille : elles servent à le
 * faire affleurer le coin de la ligne. Les laisser à −4/−6 px sur une boîte de
 * 44 px décalerait le glyphe au lieu de le loger. */
@media (max-width: 780px) {
	.aod-cart__close { width: 44px; height: 44px; }
	.aod-cart__qty { height: 44px; }
	.aod-cart__qty button { width: 44px; height: 44px; }
	.aod-cart__remove {
		width: 44px;
		height: 44px;
		margin-block-start: -10px;
		margin-inline-end: -10px;
	}
}

/* ══════════════════════════════════════════════════════════════════════════
 * Page de commande du panier (77.1, restructurée en 77.6)
 *
 * ⚠️ ELLE VIT DANS CETTE FEUILLE, ET C'EST UN CHOIX. `aod-cart.css` est la seule
 * qui ne part QUE si le gérant a allumé le panier (`AOD_COD_Cart::assets()`), et
 * cette page n'existe que dans ce cas : la garantie « toggle OFF = pas un octet »
 * est ainsi tenue par construction, sans une garde de plus à maintenir.
 *
 * 🚨 TOUT EST SCOPÉ `.aod-order-page`, ET C'EST OBLIGATOIRE. `.aod-cod` et
 * `.aod-cart__box` vivent AUSSI sur la fiche produit et sur la page de vente, où
 * leurs largeurs (560 px, 640 px au-delà de 922 px) sont gardées par R4 de
 * `test-vides-publics.php`. Une règle non scopée les déplacerait sur trois pages
 * pour en corriger une. La classe de corps est posée par
 * `AOD_COD_Order_Page::body_class()`.
 * ══════════════════════════════════════════════════════════════════════════ */

/* 🚨 `width: 100%` N'EST PAS REDONDANT AVEC `max-width`, et c'est ce qui a fait
 * échouer le premier jet : `.ast-container` d'Astra est un conteneur **FLEX** (il
 * loge le contenu et la barre latérale). Un enfant flex se dimensionne sur son
 * CONTENU, pas sur la place disponible — mesuré à 1440 px : `max-width: 1200px`
 * était bien appliqué et la largeur utilisée valait **952 px**. La largeur déclarée
 * donne la base ; `max-width` la plafonne ; `margin-inline: auto` la centre.
 * *Un `max-width` seul ne fait rien grandir : il n'empêche que de dépasser.*
 * ⚠️ Écrit ainsi, ce bloc tient dans un parent `block`, `flex` OU `grid` — donc
 * chez le thème que l'owner déposera, quel qu'il soit. */
.aod-order__main {
	width: 100%;
	max-width: 1200px;
	margin-inline: auto;
	padding: 32px 16px 64px;
	box-sizing: border-box;
}

/* Sur mobile on rend les 24 px que la respiration de bureau vient de prendre :
 * le marché est mobile (§1 règle 6), et chaque pixel de haut y est du défilement
 * avant le bouton d'envoi. */
@media (max-width: 559px) {
	.aod-order__main { padding: 20px 16px 40px; }
}

/* 🚨 SOUS LE PALIER À DEUX COLONNES, LES DEUX CARTES DOIVENT S'ALIGNER.
 * `.aod-cod` est centré par son `margin: 24px auto` ; `.aod-cart__box` est calé au
 * début par son `margin: 24px 0`. Mesuré à 768 px avant correctif : la boîte à
 * **x=36**, le formulaire à **x=104** — **68 px de décalage** entre deux cartes
 * empilées, l'une sous l'autre. C'est EXACTEMENT le défaut que R4 de
 * `test-vides-publics.php` décrit pour la page de vente, reproduit ici parce que
 * les deux mêmes blocs s'y empilent. Il se répare du même geste : la boîte suit le
 * formulaire. */
.aod-order-page .aod-order__form .aod-cart__box {
	margin-inline: auto;
}

.aod-order__title {
	margin: 0 0 24px;
	font-size: var(--aod-fs-2xl);
	line-height: 1.2;
}

/* L'état vide (77.1). Rendu par le SERVEUR et visible par défaut — cf. le
 * commentaire du gabarit : JS absent, l'acheteur lit une phrase vraie et repart
 * vers la Boutique, au lieu de remplir un formulaire qui ne peut pas aboutir.
 * ⚠️ Borné : à 1200 px de large, un encadré vide de bord à bord pour deux lignes
 * de texte serait pire que la marge qu'on vient de retirer. */
.aod-order__empty {
	max-width: 560px;
	margin-inline: auto;
	padding: 40px 24px;
	text-align: center;
	background: var(--ac-surface-sub);
	border: 1px solid var(--ac-line);
	border-radius: var(--ac-r-lg);
}

.aod-order__empty-text {
	margin: 0 0 16px;
	font-size: var(--aod-fs-md);
	color: var(--ac-ink-2);
}

/* Bouton d'action : la peau de la marque, et un gabarit tactile de 44 px de haut
 * — c'est le seul chemin de sortie de la page, il ne se vise pas au pixel. */
.aod-order__empty-cta {
	display: inline-block;
	min-height: 44px;
	padding: 12px 24px;
	font-size: var(--aod-fs-sm);
	font-weight: 600;
	color: var(--ac-accent-ink);
	background: var(--ac-accent);
	border-radius: var(--ac-r-md);
	text-decoration: none;
}

.aod-order__empty-cta:hover,
.aod-order__empty-cta:focus {
	background: var(--ac-accent-dark);
	color: var(--ac-accent-ink);
}

.aod-order__empty-cta:focus-visible {
	outline: 2px solid var(--ac-accent);
	outline-offset: 2px;
}

/* ── Les champs s'apparient dès que la colonne peut les tenir ────────────────
 *
 * `.aod-cod__form` devient une grille de 2 colonnes : « Nom » et « Téléphone »
 * se placent côte à côte, comme « Wilaya » et « Commune » (dont `.aod-cod__row`
 * est `display: block` partout ailleurs — donc empilé).
 *
 * ⚠️ TOUT S'ÉTEND SUR LES DEUX COLONNES PAR DÉFAUT, et seuls deux champs sont
 * réduits. L'inverse — apparier par défaut et corriger les exceptions — casserait
 * le récapitulatif, le bouton d'envoi, la réassurance et le honeypot à chaque
 * champ ajouté au formulaire. *Une grille se déclare par ce qui NE se coupe pas.*
 *
 * ⚠️ `:not(.aod-cod__address)` : l'adresse porte la même paire de classes que le
 * nom et le téléphone, et elle doit rester pleine largeur (c'est le champ le plus
 * long à lire).
 * ⛔ Et surtout PAS un `nth-of-type` : le nombre de nœuds avant ces champs change
 * selon que le produit a des options — cette page n'en a pas, la fiche si. Un
 * sélecteur positionnel serait juste ici et faux ailleurs le jour où on le
 * réutilise.
 *
 * 🔒 Ces quatre règles sont RECENSÉES par R2 de `test-panneau-choix.php`, qui tient
 * un registre fermé de ce que cette feuille a le droit de dire au markup de la
 * fiche — et qui vérifie qu'elles ne déclarent QUE de la mise en page. */
@media (min-width: 560px) {
	.aod-order-page .aod-cod__form {
		display: grid;
		grid-template-columns: 1fr 1fr;
		column-gap: 14px;
		align-items: start;
	}

	.aod-order-page .aod-cod__form > * {
		grid-column: 1 / -1;
	}

	.aod-order-page .aod-cod__form > .aod-cod__field.aod-cod__float:not(.aod-cod__address) {
		grid-column: auto;
	}

	.aod-order-page .aod-cod__row {
		display: grid;
		grid-template-columns: 1fr 1fr;
		column-gap: 14px;
	}
}

/* ── Deux colonnes : le formulaire, et le panier qu'il commande ──────────────
 *
 * 🚨 `:not([hidden])`, ET CE N'EST PAS UN RAFFINEMENT. `display: grid` écraserait
 * le `display: none` que l'attribut `hidden` donne par défaut : sans cette garde,
 * le formulaire resterait AFFICHÉ sur un panier vide — c'est-à-dire exactement le
 * défaut que l'état vide serveur existe pour empêcher. Le même piège est déjà
 * documenté en tête de cette feuille pour le tiroir, où il avait été trouvé par un
 * témoin et non par la relecture.
 *
 * ⛔ AUCUN `position: sticky` — livré puis RETIRÉ en 52.5, six raisons écrites
 * dans `aod-cod-form.css`. On ne le réinvente pas ici sous prétexte que la colonne
 * de droite est plus courte que celle de gauche.
 *
 * ⚠️ Les colonnes sont posées en numéros de grille, jamais en `float` ni en
 * `margin-left` : CSS Grid suit le sens d'écriture, donc l'ordre s'inverse SEUL en
 * arabe. Le récapitulatif reste du côté de la fin de ligne dans les deux langues.
 *
 * ⚠️ `minmax(0, …)` sur les deux pistes : sans le `0`, une ligne de panier au nom
 * long impose sa largeur minimale de contenu et fait déborder la grille. */
@media (min-width: 1024px) {
	.aod-order-page .aod-order__form:not([hidden]) {
		display: grid;
		grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
		gap: 32px;
		align-items: start;
	}

	.aod-order-page .aod-order__form .aod-cod {
		grid-column: 1;
		grid-row: 1;
		max-width: none;
		margin: 0;
	}

	.aod-order-page .aod-order__form .aod-cart__box {
		grid-column: 2;
		grid-row: 1;
		max-width: none;
		margin: 0;
		/* ⚠️ LE RÉCAPITULATIF SE DISTINGUE DU FORMULAIRE PAR SA SURFACE, et ce
		 * n'est pas de la décoration : côte à côte, deux cartes blanches de même
		 * bordure et de même rayon se lisent comme **deux formulaires**. Rien ne
		 * disait laquelle on remplit et laquelle on relit. Le fond sourd est le
		 * jeton du socle (`--aod-n-bg-sub`), pas une valeur écrite ici : il suit
		 * l'accent du client et reste dans le recensement de
		 * `test-couleurs-publiques.php`.
		 * ⛔ Réservé au palier à DEUX colonnes : empilées, les deux cartes se
		 * suivent dans le temps, et teinter la première la ferait passer pour
		 * désactivée. */
		background: var(--ac-surface-sub);
	}
}
