/* AOD COD Form — style « checkout Shopify » (RTL-aware)
   Neutres + labels flottants + bouton sombre, dans l'esprit du checkout Shopify. */
/* ---- Les jetons, et LES DEUX SURFACES QUI SERVENT CE MARKUP ----
 *
 * 🚨 LE PANNEAU DE CHOIX N'ÉTAIT PAS DANS CETTE LISTE, ET ÇA A COÛTÉ L'ÉTAT
 * « choisi » (mesuré le 2026-08-04). Depuis le 2026-08-02, le panneau du panier
 * (`.aod-cart__sheet`, ouvert depuis une carte de grille) sert EXACTEMENT ces
 * pastilles — c'est le markup du serveur, précisément pour ne pas en tenir une
 * seconde version. Mais les jetons, eux, étaient déclarés sur `.aod-cod` seul,
 * et le panneau vit HORS de ce conteneur : chaque `var(--sh-…)` y résolvait à
 * VIDE. Mesuré côte à côte, même produit, même pastille cochée :
 *
 *                      panneau            fiche
 *   --sh-accent        (vide)             #008060
 *   coin               0 px               6 px
 *   fond si COCHÉE     transparent        #f0faf6
 *   bordure si COCHÉE  rgb(30,41,59)      #008060
 *
 * Autrement dit : dans le panneau, choisir une couleur ne changeait RIEN à
 * l'écran — la pastille cochée avait la bordure des non-cochées. L'acheteur
 * cliquait, ne voyait rien, et recliquait ailleurs.
 * Aucune porte ne pouvait le voir : la recette vérifiait que la classe
 * `is-selected` était bien posée — elle l'était — jamais qu'elle se VOYAIT.
 *
 * ⚠️ La correction est une SEULE déclaration pour deux hôtes, jamais une copie
 * des valeurs dans `aod-cart.css` : ces couleurs sont déjà dupliquées une fois,
 * à dessein et sous cliquet (`--ac-*`, cf. R2 de `test-panier-peau.php`) ; une
 * troisième copie serait celle de trop. La géométrie propre au formulaire
 * (largeur, marges, cadre) reste, elle, sur `.aod-cod`.
 */
.aod-cod,
.aod-cart__sheet {
	--sh-text: #1a1a1a;
	--sh-text-2: #616161;
	--sh-muted: #8a8a8a;
	--sh-line: var(--aod-n-line);
	--sh-line-strong: #1a1a1a;
	--sh-bg: var(--aod-n-bg);
	--sh-bg-sub: var(--aod-n-bg-sub);
	/* ---- LA COULEUR DE MARQUE (Phase 50, dette `D45`) ----
	 *
	 * 🚨 CES QUATRE JETONS ÉTAIENT UN SEUL VERT ÉCRIT EN DUR, ET C'ÉTAIT DEUX
	 * DÉFAUTS EN UN.
	 *
	 * (1) `#008060` est le vert de Shopify. Il ne venait de personne : ni du thème
	 * du clone, ni du Personnalisateur. Un client dont la marque est rouge servait
	 * un bouton d'achat vert, en dur, dans tous ses clones — la règle n°1 du
	 * template, enfreinte sur la seule surface que l'acheteur regarde.
	 *
	 * (2) Une seule valeur portait QUATRE rôles que rien ne distinguait : fond
	 * d'action, texte sur blanc, liseré, et survol. Ça ne tenait que parce que ce
	 * vert-là est sombre. La couleur devenant celle du client, chaque rôle a
	 * désormais son jeton, calculé par `AOD_COD_Accent` pour tenir sa garantie
	 * (4,5:1 pour du texte, 3:1 pour un liseré) SUR TOUT accent du cube RGB.
	 * Les confondre à nouveau — parce qu'ils se ressemblent sur l'accent par
	 * défaut — rendrait le bouton d'achat illisible sans qu'aucune règle ne bouge.
	 */
	--sh-accent: var(--aod-a);            /* fond d'une action d'achat */
	--sh-accent-ink: var(--aod-a-ink);    /* ce qu'on écrit DESSUS — jamais `#fff` en dur */
	--sh-accent-text: var(--aod-a-text);  /* la marque écrite sur nos fonds clairs */
	--sh-accent-line: var(--aod-a-line);  /* la marque en liseré / anneau / pastille */
	--sh-accent-dark: var(--aod-a-strong);/* survol du bouton */
	--sh-accent-wash: var(--aod-a-wash);  /* fond très pâle des états choisis (jumeau de --ac-accent-wash) */

	/* ⚠️ LE VERT DE VALIDATION N'EST PAS LA MARQUE, et il ne doit pas la suivre.
	   « Commande enregistrée » doit rester vert chez un client au rouge : c'est un
	   code de sens, pas un choix esthétique. C'est pourquoi `--sh-green` ne suit pas
	   `--sh-accent` — les deux étaient égales, ce qui masquait qu'elles n'ont jamais
	   eu le même métier.

	   🚨 ELLES DÉLÈGUENT AU SOCLE DEPUIS LA 71.2bis, et la raison n'est pas le
	   rangement : `aod-cod-pages.css` déclarait `.aod-cod__msg.is-ok` / `.is-error`
	   à SPÉCIFICITÉ ÉGALE (0,2,0) avec d'AUTRES valeurs. Qui peint dépendait donc de
	   l'ordre d'enfilage — mesuré : cette feuille gagne sur les TROIS surfaces, et
	   les couleurs de l'autre ne peignaient rien nulle part. Deux familles qui lisent
	   la MÊME variable ne peuvent plus diverger, et l'ordre cesse de décider.

	   ⛔ Ne leur redonne pas une valeur littérale : le contraste de ces paires est
	   garanti par un calcul qui lit le SOCLE (`test-couleurs-publiques.php`, R6).
	   Vert #008060 sur son wash = 4,63:1 · rouge #b91c1c sur le sien = 5,88:1. */
	--sh-green: var(--aod-n-ok);      /* validations, succès, réassurance */
	--sh-error: var(--aod-n-bad);
	--sh-ok-soft: var(--aod-n-ok-soft);
	--sh-ok-line: var(--aod-n-ok-line);
	--sh-bad-soft: var(--aod-n-bad-soft);
	--sh-bad-line: var(--aod-n-bad-line);
	--sh-radius: 8px;
	--sh-radius-sm: 6px;
}

.aod-cod {
	/* `auto` en latéral, et ce n'est pas cosmétique : entre 601 et 921 px, Astra
	   empile la galerie et le résumé, la colonne passe à ~728 px et le
	   formulaire, plafonné à 560, restait collé au bord en laissant 168 px de
	   vide à sa droite. Sans effet en 2 colonnes (la colonne y fait 552 px). */
	margin: 24px auto;
	padding: 24px;
	border: 1px solid var(--aod-n-line);
	border-radius: 12px;
	background: var(--sh-bg);
	max-width: 560px;
	color: var(--sh-text);
	box-sizing: border-box;
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
	-webkit-font-smoothing: antialiased;
}

/* Desktop 2 colonnes (Astra passe galerie + résumé côte à côte à partir de 922 px).
   Le `margin: auto` ci-dessus CENTRE le formulaire dans sa colonne : sur un écran
   large, où l'owner a élargi le conteneur (CHECKLIST-THEME-CHILD C9), la colonne
   résumé s'étire et le formulaire, plafonné à 560, se met à FLOTTER au milieu — il
   ne s'aligne plus sous le titre/prix du produit (alignés au début), ce qui donne
   une carte qui « pend » dans le vide. On l'ancre donc au DÉBUT de la colonne et on
   le laisse remplir jusqu'à 640 px (au-delà, les champs deviennent trop longs à
   lire). Logique (`margin-inline`) → correct en RTL sans règle miroir.
   ⚠️ On ne touche PAS l'intervalle 601-921 px (Astra empile) : la centrage `auto`
   y reste voulu (cf. le commentaire de `.aod-cod` ci-dessus). */
@media (min-width: 922px) {
	.aod-cod {
		margin-block: 24px;
		margin-inline: 0 auto;
		max-width: 640px;
	}

	/* ---- ⛔ LA GALERIE COLLANTE A ÉTÉ ESSAYÉE, PUIS RETIRÉE (52.5) ----
	 *
	 * Ce paragraphe existe pour qu'on ne la réinvente pas. Le défaut qu'elle
	 * visait est RÉEL et toujours là : mesuré à 1440 px sur un clone Astra nu,
	 * colonne galerie **450 px** de haut contre **1 476 px** pour la colonne
	 * résumé, soit **1 026 px de vide** sous la photo pendant que l'acheteur
	 * remplit un formulaire de 1 270 px. Il est consigné en dette (`D51`).
	 *
	 * 🚨 CE QUI A ÉTÉ ESSAYÉ, ET POURQUOI ÇA NE TIENT PAS.
	 * `position: sticky; top: 24px` marche — jusqu'à ce que la galerie soit plus
	 * HAUTE que la fenêtre : elle est alors épinglée par son sommet et son bas
	 * devient inatteignable à TOUT défilement (mesuré : photo portrait de
	 * 1 100 px, fenêtre de 768 px → 356 px de produit et les miniatures perdus).
	 * Sans `sticky`, il suffisait de faire défiler. *La règle ne révélait pas ce
	 * défaut, elle le créait.*
	 *
	 * Le borner (`max-height` + `overflow-y`) fait de la galerie un conteneur de
	 * défilement, et une revue de code en a tiré SIX défauts, dont trois qu'aucun
	 * CSS ne répare :
	 *   · nos propres flèches de galerie (`.aod-gallery-nav`, absolues) défilent
	 *     avec le contenu et sont rognées dès qu'on descend dans la photo ;
	 *   · la région défilante n'a AUCUN descendant focalisable sur un produit à
	 *     image unique : au clavier, le bas de la photo est perdu (WCAG 2.1.1) ;
	 *   · `overflow-y: auto` force `overflow-x` à `auto` (CSS Overflow 3) : la
	 *     galerie rogne sur les DEUX axes, y compris quand elle tient — la borne
	 *     n'était donc pas « inerte », contrairement à ce qui était écrit ici.
	 * S'y ajoutaient `overscroll-behavior: contain` qui gelait la moitié de la
	 * fenêtre, `100vh` qui vise la grande fenêtre (donc faux sur un téléphone en
	 * paysage, qui franchit pourtant les 922 px), et une dépendance à
	 * `body.woocommerce-js` — une classe posée par un script de `wp_footer`, donc
	 * après le premier rendu et jamais sans JavaScript.
	 *
	 * 🚨 LA LEÇON, ET ELLE VAUT AU-DELÀ D'ICI : *un correctif qui déplace un
	 * défaut vers un état plus rare n'est pas un correctif.* Ici il l'a déplacé
	 * vers les photos portrait, le clavier et les téléphones en paysage — trois
	 * publics moins visibles, pas moins réels. Ce qu'il faut à ce vide, c'est une
	 * mise en page, pas une propriété.
	 */
}

/* Titre */
.aod-cod__title {
	margin: 0 0 18px;
	font-size: var(--aod-fs-xl);
	font-weight: 600;
	line-height: 1.3;
	letter-spacing: -.01em;
	color: var(--sh-text);
}

/* Anti-spam : champ honeypot. Sorti de l'écran (et non display:none, que certains
   bots détectent) — invisible pour l'humain, présent dans le DOM pour les bots. */
.aod-cod__hp {
	position: absolute !important;
	left: -9999px !important;
	top: auto;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* ---- Champs ---- */
.aod-cod__field { margin-bottom: 14px; }

/* Libellés de section (variantes, mode de livraison) : titre au-dessus */
.aod-cod__field:not(.aod-cod__float) > label {
	display: block;
	margin-bottom: 8px;
	font-weight: 500;
	font-size: var(--aod-fs-sm);
	color: var(--sh-text);
}
.aod-cod__field > label span { color: var(--sh-error); }

/* 🚨 LA LISTE COUVRE TOUS LES CHAMPS DE SAISIE, ET C'EST UNE RÈGLE D'iOS, PAS UN
 * GOÛT. Sous 16 px, Safari mobile ZOOME la page à la mise au point d'un champ :
 * l'acheteur touche « Téléphone » et le site lui saute au visage, sur le seul
 * formulaire qui encaisse. `--aod-fs-md` vaut donc 16 px (cf. `AOD_COD_Accent`).
 * ⚠️ La liste s'arrêtait à text/tel/number/select ; `email` et `textarea` — les
 * DEUX champs propres au formulaire de contact — n'y étaient pas. Ils tenaient
 * les 16 px par ACCIDENT, parce qu'Astra les habille lui-même : un thème de
 * clone plus sobre les aurait laissés à 14,59 px. Une garantie qui dépend du
 * thème n'est pas une garantie. */
.aod-cod__form input[type="text"],
.aod-cod__form input[type="tel"],
.aod-cod__form input[type="email"],
.aod-cod__form input[type="number"],
.aod-cod__form textarea,
.aod-cod__form select {
	width: 100%;
	padding: 12px 13px;
	border: 1px solid var(--sh-muted);
	border-radius: var(--sh-radius-sm);
	font-size: var(--aod-fs-md);
	line-height: 1.4;
	box-sizing: border-box;
	background: var(--aod-n-bg);
	color: var(--sh-text);
	transition: border-color .12s, box-shadow .12s;
	-webkit-appearance: none;
	appearance: none;
}
.aod-cod__form input:hover,
.aod-cod__form select:hover { border-color: var(--sh-text-2); }

/* Focus « Shopify » : bordure verte doublée d'un anneau fin */
.aod-cod__form input:focus,
.aod-cod__form select:focus {
	outline: none;
	border-color: var(--sh-accent-line);
	box-shadow: 0 0 0 1px var(--sh-accent-line);
}
.aod-cod__form input::placeholder { color: transparent; } /* labels flottants */

/* Flèche personnalisée pour les selects */
.aod-cod__form select {
	min-height: 52px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%238a8a8a' d='M6 8 0 2 1.4.6 6 5.2 10.6.6 12 2z'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 13px center;
	padding-right: 38px;
}
.rtl .aod-cod__form select {
	background-position: left 13px center;
	padding-right: 13px;
	padding-left: 38px;
}
.aod-cod__form select:disabled { background-color: var(--sh-bg-sub); color: #9ca3af; cursor: not-allowed; }

/* ---- Labels flottants (signature Shopify) ---- */
.aod-cod__float { position: relative; }
.aod-cod__float > input,
.aod-cod__float > select {
	height: 56px;
	padding-top: 24px;
	padding-bottom: 6px;
	line-height: 1.2;
}
.aod-cod__float > label {
	position: absolute;
	inset-inline-start: 14px;
	/* Repos : centré verticalement (robuste quelle que soit la hauteur de
	   glyphes, ex. arabe) — pas de chevauchement avec la valeur saisie. */
	top: 50%;
	transform: translateY(-50%);
	margin: 0;
	font-size: var(--aod-fs-md);
	line-height: 1;
	font-weight: 400;
	color: var(--sh-text-2);
	background: transparent;
	pointer-events: none;
	transition: top .12s ease, transform .12s ease, font-size .12s ease, color .12s ease;
	max-width: calc(100% - 40px);
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}
/* État « flottant » : focus, champ rempli, ou select (toujours).
   Épinglé en haut (line-height:1) avec une marge nette sous le label, le
   texte/la sélection occupant le bas du champ (padding-top:24px). */
.aod-cod__float > input:focus + label,
.aod-cod__float > input:not(:placeholder-shown) + label,
.aod-cod__float--sel > select + label {
	top: 9px;
	transform: translateY(0);
	font-size: var(--aod-fs-2xs);
	color: var(--sh-text-2);
}

/* ---- Quantité : stepper − / + ---- */
.aod-cod__stepper {
	display: inline-flex;
	align-items: stretch;
	border: 1px solid var(--sh-muted);
	border-radius: var(--sh-radius-sm);
	overflow: hidden;
	background: var(--aod-n-bg);
	transition: border-color .12s, box-shadow .12s;
}
.aod-cod__stepper:focus-within {
	border-color: var(--sh-accent-line);
	box-shadow: 0 0 0 1px var(--sh-accent-line);
}
.aod-cod__stepper-btn {
	flex: 0 0 auto;
	width: 46px;
	border: 0;
	background: var(--sh-bg-sub);
	color: var(--sh-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: 22px;
	line-height: 1;
	font-weight: 500;
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
	transition: background .12s, color .12s;
	user-select: none;
}
.aod-cod__stepper-btn:hover { background: var(--sh-accent); color: var(--sh-accent-ink); }
.aod-cod__stepper-btn:active { background: var(--sh-accent-dark); color: var(--sh-accent-ink); }
.aod-cod__stepper-btn:focus-visible { outline: 2px solid var(--sh-accent-line); outline-offset: -2px; }
/* L'input du stepper : pas de bordure propre (le conteneur la porte), centré,
   sans les flèches natives. On surcharge le style générique des number. */
.aod-cod__stepper input[type="number"] {
	width: 64px;
	border: 0;
	border-inline: 1px solid var(--sh-line);
	border-radius: 0;
	text-align: center;
	padding: 12px 4px;
	font-weight: 600;
	-moz-appearance: textfield;
}
.aod-cod__stepper input[type="number"]:focus { box-shadow: none; border-color: var(--sh-line); }
.aod-cod__stepper input[type="number"]::-webkit-outer-spin-button,
.aod-cod__stepper input[type="number"]::-webkit-inner-spin-button {
	-webkit-appearance: none;
	margin: 0;
}

/* Wilaya / Commune : pleine largeur, l'un sous l'autre (noms longs AR) */
.aod-cod__row { display: flex; flex-direction: column; gap: 14px; margin-bottom: 14px; }
.aod-cod__row .aod-cod__field { width: 100%; margin-bottom: 0; }

/* ---- Mode de livraison : lignes de sélection style Shopify ---- */
.aod-cod__radios { display: flex; flex-direction: row; gap: 0; border: 1px solid var(--sh-line); border-radius: var(--sh-radius-sm); overflow: hidden; }
.aod-cod__radio { cursor: pointer; margin: 0; flex: 1 1 0; min-width: 0; }
.aod-cod__radio + .aod-cod__radio .aod-cod__radio-card { border-inline-start: 1px solid var(--sh-line); }
.aod-cod__radio input { position: absolute; opacity: 0; width: 0; height: 0; }
.aod-cod__radio-card {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 14px 12px;
	height: 100%;
	background: var(--aod-n-bg);
	transition: background .12s;
}
/* Très petits écrans : on repasse les deux modes l'un sous l'autre. */
@media (max-width: 360px) {
	.aod-cod__radios { flex-direction: column; }
	.aod-cod__radio + .aod-cod__radio .aod-cod__radio-card { border-inline-start: 0; border-top: 1px solid var(--sh-line); }
}
/* Pastille radio dessinée (à gauche) */
.aod-cod__radio-card::before {
	content: "";
	flex: 0 0 auto;
	width: 18px;
	height: 18px;
	border-radius: 50%;
	border: 1px solid var(--sh-muted);
	background: var(--aod-n-bg);
	transition: border-color .12s, box-shadow .12s;
}
/* Icône du mode de livraison. Elle a remplacé 🏠 / 🏢 en 38.3 : un emoji est
   dessiné par l'OS, donc l'acheteur Android, l'acheteur iOS et l'acheteur
   Windows ne voyaient pas le même formulaire — et aucun ne voyait celui qu'on
   avait dessiné. Elle suit l'état choisi, comme la pastille radio. */
.aod-cod__radio-icon { display: flex; align-items: center; flex: 0 0 auto; color: var(--sh-muted); transition: color .12s; }
.aod-cod__radio input:checked + .aod-cod__radio-card .aod-cod__radio-icon { color: var(--sh-accent-text); }
.aod-cod__radio-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.aod-cod__radio-title { font-weight: 500; font-size: var(--aod-fs-sm); color: var(--sh-text); line-height: 1.25; }
/* ⚠️ Plus de `margin-inline-start:auto` : dans un conteneur en COLONNE, cette
   marge automatique poussait le prix sur l'axe transversal — il se retrouvait
   collé au bord droit, sous un titre qui, lui, commençait à gauche. Le prix est
   une seconde ligne du même bloc, il s'aligne comme le titre. */
.aod-cod__radio-price { font-size: var(--aod-fs-sm); font-weight: 600; color: var(--sh-text); }
.aod-cod__radio-price:empty::before { content: "—"; color: var(--sh-muted); font-weight: 400; }

.aod-cod__radio input:checked + .aod-cod__radio-card { background: var(--sh-bg-sub); }
.aod-cod__radio input:checked + .aod-cod__radio-card::before {
	border-color: var(--sh-accent-line);
	box-shadow: inset 0 0 0 4px var(--sh-accent-line);
}
.aod-cod__radio input:focus-visible + .aod-cod__radio-card::before {
	box-shadow: 0 0 0 2px rgba(var(--aod-a-rgb), .25);
}

/* ---- Récapitulatif ---- */
.aod-cod__summary {
	margin: 18px 0;
	padding: 16px;
	background: var(--sh-bg-sub);
	border: 1px solid var(--sh-line);
	border-radius: var(--sh-radius-sm);
}
.aod-cod__summary > div {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 12px;
	padding: 5px 0;
	font-size: var(--aod-fs-sm);
	color: var(--sh-text-2);
}
.aod-cod__summary > div strong { color: var(--sh-text); font-weight: 500; }
/* 🚨 SANS CETTE RÈGLE, `hidden` NE CACHE RIEN, ET RIEN NE LE DIT. La règle
   ci-dessus pose `display: flex` sur CHAQUE enfant direct : elle bat la feuille
   du navigateur (`[hidden] { display: none }`), qui est la moins prioritaire de
   toutes. Le JS pose bien l'attribut, le DOM est juste, l'inspecteur affiche
   `hidden` — et la ligne reste à l'écran. C'est le seul moyen d'effacer la ligne
   d'article quand le panier mène (Phase 65). */
.aod-cod__summary > div[hidden] { display: none; }

/* ---- La ligne d'article : CE QU'ON COMMANDE (Phase 65, dette `D51`) ---- */
/* 🚨 Le récapitulatif totalisait de l'ARGENT sans jamais nommer la marchandise.
   Mesuré : à 390 px, le bouton d'envoi est à 1 685 px du haut — la photo est
   sortie de l'écran depuis ~1 300 px quand l'acheteur valide.
   ⛔ Pas de vignette : les deux pages qui vendent débordent déjà en 3G lente
   (Phase 61). Le nom porte l'identité, pas une seconde copie de la photo.
   ⚠️ `align-items: flex-start`, JAMAIS `baseline` comme ses voisines : un nom de
   produit peut passer sur deux lignes, et une ligne de base alignerait le détail
   sur la PREMIÈRE — il flotterait au-dessus du texte qu'il qualifie. */
.aod-cod__item {
	align-items: flex-start !important;
	padding-bottom: 12px !important;
	margin-bottom: 4px;
	border-bottom: 1px solid var(--sh-line);
}
.aod-cod__item-name {
	font-weight: 500;
	color: var(--sh-text);
}
/* Le détail (variante · quantité) est secondaire : il ne doit pas concurrencer
   le nom. `text-align: end` et non `right` — le bloc miroite en arabe. */
.aod-cod__item-detail {
	flex: 0 0 auto;
	text-align: end;
	color: var(--sh-text-2);
	font-size: var(--aod-fs-xs);
}
.aod-cod__item-detail:empty { display: none; }

.aod-cod__grand {
	margin-top: 8px;
	padding-top: 12px !important;
	border-top: 1px solid var(--sh-line);
	font-size: var(--aod-fs-md) !important;
	color: var(--sh-text) !important;
}
.aod-cod__grand > span { font-weight: 500; }
.aod-cod__grand strong { font-size: var(--aod-fs-xl); font-weight: 600 !important; color: var(--sh-text) !important; }

/* ---- Bouton « Confirmer » (Pay-now Shopify : sombre, pleine largeur) ---- */
/* Scopé par `.aod-cod` (0,2,0 — et 0,3,0 sur les états) : le formulaire COD
   s'habille LUI-MÊME, il ne délègue rien au thème. Un thème qui porte
   `.woocommerce button:hover` (0,2,1) battrait un `.aod-cod__submit:hover`
   (0,2,0) ; le préfixe met nos règles hors d'atteinte sans un seul
   `!important`. C'est ce scope qui a permis de retirer celui qui portait la
   couleur du texte. */
.aod-cod .aod-cod__submit {
	-webkit-appearance: none;
	appearance: none;
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	width: 100%;
	min-height: 48px;
	padding: 16px;
	font-size: var(--aod-fs-md);
	font-weight: 600;
	line-height: 1.2;
	border: none;
	border-radius: var(--sh-radius);
	background: var(--sh-accent);
	color: var(--sh-accent-ink);
	cursor: pointer;
	transition: background-color .12s ease, transform .05s ease, opacity .12s ease;
}
/* `:focus` AUTANT que `:hover` : un thème porte `button:focus{background}` en
   (0,1,1), qui bat une règle de composant non scopée — le bouton reprenait
   alors la couleur du thème au seul moment où il a le focus, donc juste avant
   d'être activé au clavier. Leçon de la 37.1, appliquée ici. */
.aod-cod .aod-cod__submit:hover,
.aod-cod .aod-cod__submit:focus { background: var(--sh-accent-dark); color: var(--sh-accent-ink); }
.aod-cod .aod-cod__submit:focus-visible { outline: 2px solid var(--sh-accent-line); outline-offset: 2px; }
.aod-cod .aod-cod__submit:active { transform: translateY(1px); }
.aod-cod .aod-cod__submit[disabled] { opacity: .55; cursor: not-allowed; }
/* ⚠️ PAS de surcharge `prefers-reduced-motion` ici : elle vit en FIN de fichier,
   avec toutes les autres (raison écrite là-bas). Elle a été réalignée sur le
   scope `.aod-cod` en même temps que ces règles — sans quoi cette base en
   (0,3,0) aurait battu une surcharge restée en (0,2,0), et le creux d'appui
   aurait survécu au mouvement réduit SANS que rien ne le signale. */

/* ---- Bouton « Ajouter au panier » (panier COD) : secondaire, pleine largeur ---- */
/* Le sélecteur `.button` a disparu du markup ET d'ici : le porter revenait à
   inviter les règles de boutons du thème, dont le CSS dynamique est imprimé
   APRÈS nos feuilles. Fond BLANC explicite et non `transparent` — mesuré, un
   fond transparent fait dépendre le contraste de ce qu'il y a derrière.
   Vert #008060 sur blanc = 4,93:1 (AA tenu). */
.aod-cod__addcart,
.aod-cod__buynow {
	-webkit-appearance: none;
	appearance: none;
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	width: 100%;
	min-height: 48px;
	margin-top: 10px;
	padding: 14px 16px;
	font-size: var(--aod-fs-md);
	font-weight: 600;
	line-height: 1.2;
	border: 1.5px solid var(--sh-accent-line);
	border-radius: var(--sh-radius);
	background: var(--sh-bg);
	color: var(--sh-accent-text);
	cursor: pointer;
	transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
.aod-cod__addcart:hover,
.aod-cod__addcart:focus-visible,
.aod-cod__buynow:hover,
.aod-cod__buynow:focus-visible {
	background: var(--sh-accent);
	color: var(--sh-accent-ink);
}
.aod-cod__addcart:focus-visible,
.aod-cod__buynow:focus-visible {
	outline: 2px solid var(--sh-accent-line);
	outline-offset: 2px;
}
/* ── MODE CHOIX SEUL (80.1) : « Ajouter au panier » devient le bouton PRINCIPAL ──
 *
 * 🚨 CE N'EST PAS UNE PRÉFÉRENCE ESTHÉTIQUE : c'est le SEUL geste de l'écran.
 * Mesuré avant la 80.1, panier activé, à 390 px : ce bouton était **secondaire**
 * (blanc, contour) et posé **61 px SOUS** un « Confirmer la commande » vert et plein
 * qui, panier non vide, ne commandait même pas ce produit — il commandait le panier.
 * *Le bouton principal proposait l'action qui ignore la page où il se trouve.*
 *
 * 🚨 ET CES RÈGLES VIVENT ICI, PAS DANS `aod-cart.css`, ET C'EST R2 DE
 * `test-panneau-choix.php` QUI ME L'A APPRIS. Mon premier jet les avait posées
 * là-bas, avec le reste du mode panier — or ce sont des règles de PEAU visant
 * `.aod-cod__*`, c'est-à-dire exactement la « seconde peau » que ce recensement
 * fermé existe pour interdire. *La peau d'un élément vit dans la feuille qui la
 * déclare déjà ; ailleurs, c'est une copie qui attend de diverger.* Le skin de base
 * de ce bouton est vingt lignes plus haut : l'état s'écrit à côté de lui.
 *
 * ⚠️ La classe `.aod-cod--pick-only` n'est posée par PHP que dans ce mode ; le
 * bouton garde donc sa peau secondaire partout où « Confirmer la commande » existe
 * encore — c'est-à-dire sur tout clone au panier éteint, où il n'existe pas du tout. */
.aod-cod--pick-only .aod-cod__addcart {
	color: var(--sh-accent-ink);
	background: var(--sh-accent);
	border-color: var(--sh-accent);
}

.aod-cod--pick-only .aod-cod__addcart:hover,
.aod-cod--pick-only .aod-cod__addcart:focus-visible {
	color: var(--sh-accent-ink);
	background: var(--sh-accent-dark);
	border-color: var(--sh-accent-dark);
}

/* ── « Commander maintenant » (81.2) : le second geste, SECONDAIRE et assumé ──
 *
 * Il ne reçoit AUCUNE règle de couleur : sa peau EST la peau de base déclarée
 * vingt lignes plus haut — fond blanc, contour et texte à l'accent. C'est ce qui
 * le distingue de son voisin sans écrire une seule couleur de plus, puisque
 * `.aod-cod--pick-only` ne repeint QUE `.aod-cod__addcart`.
 *
 * ⚠️ LE SEUL RÉGLAGE ICI EST L'ÉCART : 6 px au lieu de 10 px, pour que les deux
 * boutons se lisent comme UNE paire et non comme deux propositions étrangères.
 * ⛔ Et il reste à 48 px de haut sur 100 % de large, comme son voisin : un
 * « deuxième choix » qu'on rapetisse devient un choix qu'on décourage — or
 * l'owner l'a demandé pour l'acheteur pressé, qui est celui qui convertit. */
.aod-cod--pick-only .aod-cod__buynow {
	margin-top: 6px;
}

/* Le prix de la sélection : une seule ligne, pas un récapitulatif de commande.
 * ⛔ Ni livraison ni total — la wilaya n'est plus demandée ici. */
.aod-cod--pick-only .aod-cod__summary--pick {
	margin-block: 4px 12px;
	padding-block-start: 12px;
	border-top: 1px solid var(--sh-line);
}

.aod-cod.is-cart-full .aod-cod__step,
.aod-cod.is-cart-full .aod-cod__submit,
.aod-cod.is-cart-full .aod-cod__buynow {
	display: none !important;
}

/* Sur la page de commande (/commander), les étapes et la soumission restent toujours visibles */
.aod-order-page .aod-cod__step {
	display: block !important;
}
.aod-order-page .aod-cod .aod-cod__submit {
	display: flex !important;
}

.aod-cod.is-cart-empty .aod-cod__summary--pick,
.aod-cod.is-cart-empty .aod-cod__buynow,
.aod-cod.is-cart-empty .aod-cod__viewcart {
	display: none !important;
}

.aod-cod.is-cart-empty .aod-cod__addcart {
	background: var(--sh-bg);
	color: var(--sh-text);
	border: 1.5px solid var(--sh-line);
	border-radius: var(--sh-radius);
	margin-top: 10px;
	min-height: 48px;
	width: 100%;
	font-weight: 600;
	transition: background .12s ease, border-color .12s ease;
}
.aod-cod.is-cart-empty .aod-cod__addcart:hover {
	background: var(--sh-bg-sub);
	border-color: var(--sh-line-strong);
}

@media (prefers-reduced-motion: reduce) {
	.aod-cod__addcart,
	.aod-cod__buynow { transition: none; }
}

/* ---- Icônes vectorielles du formulaire (catalogue fermé AOD_COD_Icon) ----
   Même classe `.aod-i` que le panier : `currentColor` + dimensions posées à
   l'impression, jamais lues du fichier source. */
.aod-cod .aod-i { display: block; flex: 0 0 auto; fill: currentColor; }

/* ---- Réassurance ---- */
.aod-cod__reassure {
	display: flex;
	/* `flex-start` et non `center` : la phrase tient sur deux lignes en mobile,
	   et un cadenas centré verticalement se retrouvait au milieu du paragraphe,
	   à côté de rien. Il s'aligne sur la PREMIÈRE ligne, comme une puce. */
	align-items: flex-start;
	justify-content: center;
	gap: 6px;
	margin: 14px 0 0;
	text-align: center;
	font-size: var(--aod-fs-xs);
	color: var(--sh-text-2);
	line-height: 1.5;
}
.aod-cod__reassure-ic { color: var(--sh-green); margin-top: 2px; }

/* ---- Le prix ne change pas de sens ---- *
 *
 * 🚨 Le dinar algérien a un symbole ARABE (`د.ج`), bidirectionnel FORT. Posé
 * dans un conteneur dont la direction n'est pas fixée, il fait basculer tout le
 * bloc : le symbole passe DEVANT le nombre. `AOD_COD_Price` règle le cas des
 * prix rendus par PHP (`<bdi dir="ltr">`), mais ces trois cellules-ci sont
 * remplies par le JS avec du TEXTE — `.text()` ne peut pas poser de balise.
 *
 * `direction: ltr` + `unicode-bidi: isolate` sur la cellule fait exactement ce
 * que ferait ce `<bdi dir="ltr">`, sans toucher au markup. La page arabe reste
 * RTL ; seul le contenu de ces cellules garde son ordre.
 *
 * ⚠️ Elles ne contiennent QUE le prix — c'est ce qui autorise la règle. L'y
 * poser sur un conteneur qui porte aussi du texte arabe le retournerait.
 */
.aod-cod__subtotal,
.aod-cod__shipping,
.aod-cod__total {
	direction: ltr;
	unicode-bidi: isolate;
}

/* ---- Messages ---- */
.aod-cod__msg { margin: 12px 0 0; text-align: center; font-weight: 500; font-size: var(--aod-fs-sm); }
.aod-cod__msg:empty { margin: 0; }
.aod-cod__msg.is-error {
	color: var(--sh-error);
	background: var(--sh-bad-soft);
	border: 1px solid var(--sh-bad-line);
	border-radius: var(--sh-radius-sm);
	padding: 11px 13px;
}
.aod-cod__msg.is-success {
	color: var(--sh-green);
	background: var(--sh-ok-soft);
	border: 1px solid var(--sh-ok-line);
	border-radius: var(--sh-radius-sm);
	padding: 11px 13px;
}

/* ---- Panneau de remerciement (après commande validée) ---- */
.aod-cod__done {
	text-align: center;
	padding: 28px 20px;
	background: var(--sh-ok-soft);
	border: 1px solid var(--sh-ok-line);
	border-radius: var(--sh-radius);
	animation: aod-done-in .35s ease;
}
.aod-cod__done-ic {
	width: 56px;
	height: 56px;
	margin: 0 auto 14px;
	display: flex;
	align-items: center;
	justify-content: center;
	/* 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: 30px;
	line-height: 1;
	color: #fff;
	background: var(--sh-green);
	border-radius: 50%;
}
.aod-cod__done-title { margin: 0 0 6px; font-size: var(--aod-fs-lg); font-weight: 700; color: #14784f; }
.aod-cod__done-order { margin: 0 0 4px; font-weight: 600; color: var(--sh-green); }
.aod-cod__done-sub { margin: 0; color: #4b5563; font-size: var(--aod-fs-sm); }
@keyframes aod-done-in {
	from { opacity: 0; transform: translateY(8px); }
	to   { opacity: 1; transform: none; }
}

/* ---- Incitation livraison gratuite ---- */
.aod-cod__free-hint {
	margin: 10px 0 0;
	padding: 9px 12px;
	text-align: center;
	font-size: var(--aod-fs-sm);
	font-weight: 500;
	color: var(--sh-green);
	background: var(--sh-ok-soft);
	border: 1px solid var(--sh-ok-line);
	border-radius: var(--sh-radius-sm);
}
.aod-cod__free-hint[hidden] { display: none; }

/* Repère d'arrivée depuis la grille (2026-08-02) — MÊME peau que l'incitation
   au seuil de gratuité juste au-dessus : les deux sont des phrases de conduite,
   pas des alertes. Aucune surface nouvelle, aucun jeton nouveau. */
.aod-cod__pickhint {
	margin: 0 0 12px;
	padding: 9px 12px;
	text-align: center;
	font-size: var(--aod-fs-sm);
	font-weight: 500;
	color: var(--sh-green);
	background: var(--sh-ok-soft);
	border: 1px solid var(--sh-ok-line);
	border-radius: var(--sh-radius-sm);
}
.aod-cod__pickhint[hidden] { display: none; }
/* Respiration sous le bord haut quand le NAVIGATEUR saute à l'ancre — il le
   fait avant que le JS ne s'exécute, et le JS, lui, sait en plus déduire un
   en-tête collant (cf. aod-cod-form.js). Sans cette marge, la phrase naissait
   rognée par le haut de l'écran : mesuré. */
#aod-options { scroll-margin-block-start: 24px; }

/* ⛔ ICI VIVAIT LA PEAU DE L'INTITULÉ DE REPLI (`.aod-cod__pick-sum`, 76.1) —
   RETIRÉE LE 2026-08-14 (Phase 80.2) avec l'enveloppe `<details>` qu'elle
   habillait, devenue inatteignable depuis la 80.1. Le pourquoi, en entier, est
   dans `class-aod-cod-form.php`, à l'endroit exact où l'enveloppe était rendue.
   ⚠️ `.aod-cod--cart` A SUIVI, LE MÊME JOUR, SUR ARBITRAGE DE L'OWNER. Elle
   n'habillait que cet intitulé ; le retrait l'a d'abord laissée en place comme
   « point d'accroche », et l'arbitrage a tranché l'inverse — *un point
   d'accroche que personne n'accroche est du code mort qui se déguise en
   intention.* ⛔ Si une peau la redemande un jour, écris la RÈGLE d'abord : une
   classe posée sans règle qui la lise ne se voit pas (dette `D41`). */
.aod-cod__radio-price s { opacity: .55; font-weight: 400; margin-inline-end: 4px; }

/* RTL : WordPress ajoute la classe .rtl au <body> pour l'arabe */
.rtl .aod-cod__summary > div { flex-direction: row-reverse; }

@media (max-width: 480px) {
	/* 16 px : le désempilement de la 38.1 a rendu au contenu 47 px par côté, ce
	   qui permet de resserrer aussi la marge propre du formulaire sans tasser
	   quoi que ce soit. */
	.aod-cod { padding: 16px; }
}

/* ---- Arguments de vente ---- */
.aod-cod__points{list-style:none;margin:0 0 16px;padding:0;display:flex;flex-wrap:wrap;gap:8px}
.aod-cod__points li{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--sh-bg-sub);border:1px solid var(--sh-line);border-radius:var(--sh-radius-sm);font-size:var(--aod-fs-xs);color:var(--sh-text);line-height:1.3}
.aod-cod__points li::before{content:none}
.aod-cod__point-check{color:var(--sh-green);font-weight:700;font-size:var(--aod-fs-sm);line-height:1}
/* ---- Contenu d'un pack assortiment ---- */
.aod-cod__pack{background:var(--sh-bg-sub);border:1px solid var(--aod-n-line);border-radius:var(--sh-radius-sm);padding:12px 14px;margin:0 0 16px}
.aod-cod__pack-label{display:block;font-size:var(--aod-fs-xs);font-weight:600;color:var(--sh-text-2);margin-bottom:5px}
.aod-cod__pack-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:3px}
.aod-cod__pack-list li{font-size:var(--aod-fs-sm);color:var(--sh-text)}
.aod-cod__pack-qty{font-weight:600;color:var(--sh-text)}

/* ---- Cartes d'offres (1 produit, 2 produits…) ---- */
.aod-cod__offers{display:flex;flex-direction:column;gap:7px;margin:0 0 14px}
/* La carte est un conteneur : en-tête cliquable + panneau de variantes imbriqué. */
.aod-cod__offer-card{
	position:relative;
	border:1.5px solid var(--sh-line);border-radius:var(--sh-radius-sm);background:var(--aod-n-bg);
	transition:border-color .12s,box-shadow .12s;overflow:hidden;
}
.aod-cod__offer-card:hover{border-color:var(--sh-line-strong)}
.aod-cod__offer-card.is-selected{border-color:var(--sh-accent-line);box-shadow:0 0 0 1px var(--sh-accent-line)}
/* Différenciation des offres : liseré couleur à gauche (survit à l'état sélectionné). */
.aod-cod__offer-card::before{
	content:"";position:absolute;left:0;top:0;bottom:0;width:4px;
	background:var(--aod-c,transparent);z-index:1;pointer-events:none;
}
.rtl .aod-cod__offer-card::before{left:auto;right:0}
/* En-tête cliquable de la carte (rangée titre / prix / coche). */
.aod-cod__offer-head{
	display:flex;align-items:center;gap:9px;margin:0;padding:8px 12px;cursor:pointer;
}
.aod-cod__offer-card.is-selected .aod-cod__offer-head{background:var(--sh-bg-sub)}
.aod-cod__offer-card input{position:absolute;opacity:0;width:0;height:0;pointer-events:none}
.aod-cod__offer-body{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
/* 🚨 ENCRE = `--aod-ci`, JAMAIS `--aod-c` (49.2). Les deux sortent de la même
   entrée de palette (`accent_palette()`), mais `--aod-c` est la teinte VIVE du
   décor : en texte elle descendait à 2,80:1. Le repli garde `--sh-text` — un
   `--aod-ci` absent doit donner du texte lisible, pas la teinte vive. */
.aod-cod__offer-title{font-size:var(--aod-fs-sm);font-weight:700;color:var(--aod-ci,var(--sh-text))}
.aod-cod__offer-badge{
	align-self:flex-start;font-size:var(--aod-fs-2xs);font-weight:700;color:#fff;background:var(--sh-green,#16a34a);
	border-radius:999px;padding:1px 7px;
}
.aod-cod__offer-price{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap}
.aod-cod__offer-now{font-size:var(--aod-fs-sm);font-weight:700;color:var(--sh-accent-text)}
.aod-cod__offer-old{font-size:var(--aod-fs-xs);color:var(--sh-text-2)}
/* Pastille « cochée » à droite de l'en-tête. */
.aod-cod__offer-check{
	flex-shrink:0;width:18px;height:18px;border-radius:50%;border:2px solid var(--sh-line-strong);
	background:var(--aod-n-bg);position:relative;
	/* Propriétés NOMMÉES : `transition:.12s` nu vaut `all`, donc animerait aussi
	   le layout. Seuls la bordure et le fond changent à l'état sélectionné. */
	transition:border-color .12s,background-color .12s;
}
.aod-cod__offer-card.is-selected .aod-cod__offer-check{border-color:var(--sh-accent-line);background:var(--sh-accent)}
.aod-cod__offer-card.is-selected .aod-cod__offer-check::after{
	content:"";position:absolute;inset:0;margin:auto;width:5px;height:9px;
	border:solid var(--sh-accent-ink);border-width:0 2px 2px 0;transform:translateY(-1px) rotate(45deg);
}

/* ---- SOLO : aucune offre configurée, donc aucun choix à présenter (38.1) ----
 * Le gérant n'a défini aucun lot : il ne reste que « 1 produit ». On rendait
 * pourtant tout l'appareillage du choix — carte encadrée, liseré de couleur
 * censé distinguer cette offre de ses voisines inexistantes, coche verte
 * indécochable — et, à l'intérieur, un bloc « Article » qui n'affiche même pas
 * son titre quand il est seul.
 *
 * 🚨 Ce n'est pas qu'une affaire de goût : MESURÉ au navigateur sur 390 px, cet
 * empilement mangeait 67 px de CHAQUE côté (34 % de l'écran) pour encadrer une
 * décision qui n'existe pas. Les pastilles de variantes tombaient à 258 px.
 *
 * ⚠️ On retire la PEAU, jamais les NŒUDS : `aod-cod-form.js` lit `data-qty` /
 * `data-price` sur la carte et itère `.aod-cod__unit` pour composer une
 * sélection par article. Le contrat DOM est inchangé.
 * ------------------------------------------------------------------------- */
.aod-cod__offers--solo { gap: 0; margin-bottom: 16px; }
.aod-cod__offers--solo .aod-cod__offer-card {
	border: 0;
	border-radius: 0;
	background: none;
	overflow: visible;
}
/* Le liseré de différenciation : plus rien à différencier. */
.aod-cod__offers--solo .aod-cod__offer-card::before { content: none; }
.aod-cod__offers--solo .aod-cod__offer-card:hover { border-color: transparent; }
.aod-cod__offers--solo .aod-cod__offer-card.is-selected { box-shadow: none; }
/* L'en-tête ne porte plus que le prix — il informe, il ne propose plus. */
.aod-cod__offers--solo .aod-cod__offer-head,
.aod-cod__offers--solo .aod-cod__offer-card.is-selected .aod-cod__offer-head {
	padding: 0 0 14px;
	background: none;
	cursor: default;
}
.aod-cod__offers--solo .aod-cod__offer-now { font-size: var(--aod-fs-lg); letter-spacing: -.01em; }
.aod-cod__offers--solo .aod-cod__offer-panel { padding: 0; border-top: 0; }

/* (La règle jumelle `.aod-cod__unit--solo` vit plus bas, juste après
   `.aod-cod__unit` — raison écrite là-bas.) */

/* ---- Panneau de variantes, déplié À L'INTÉRIEUR de la carte sélectionnée ---- */
.aod-cod__offer-panel{padding:10px 12px;border-top:1px solid var(--sh-line)}
.aod-cod__offer-panel[hidden]{display:none}
.aod-cod__unit{
	border:1px solid var(--sh-line);border-radius:var(--sh-radius-sm);
	/* Différenciation des articles : bordure de départ épaisse et colorée. */
	border-inline-start:3px solid var(--aod-c,var(--sh-line));
	background:var(--aod-n-bg);padding:10px 12px;margin:0 0 8px;
}
.aod-cod__unit:last-child{margin-bottom:0}
/* Article unique : le bloc n'a même pas de titre (le PHP ne le rend qu'à partir
   de 2 unités) — c'est un cadre autour de rien, à liseré coloré censé le
   distinguer de frères qui n'existent pas.
   🚨 CETTE RÈGLE DOIT RESTER APRÈS `.aod-cod__unit`. Les deux sélecteurs sont
   en (0,1,0) : à spécificité égale, c'est l'ordre du fichier qui tranche.
   Placée plus haut — avec le reste du bloc SOLO, où elle avait sa place
   logique — elle perdait, et le bloc gardait ses 12 px de marge et ses 3 px de
   liseré. Mesuré, pas relu : le désempilement paraissait fait, il ne l'était
   qu'à moitié. On ne compense PAS en montant la spécificité, ça déplacerait
   simplement le même piège d'un cran. */
.aod-cod__unit--solo{
	border:0;
	border-inline-start:0;
	border-radius:0;
	background:none;
	padding:0;
	margin:0;
}
/* Libellé « Article k » : ENCRE (`--aod-ci`). Cf. `.aod-cod__offer-title`. */
.aod-cod__unit-head{display:flex;align-items:center;gap:6px;font-size:var(--aod-fs-xs);font-weight:700;color:var(--aod-ci,var(--sh-text));margin:0 0 9px}
/* Pastille de couleur de l'article devant son libellé : DÉCOR (`--aod-c`), la
   teinte vive — c'est elle qui différencie les articles à l'œil. Elle tient son
   propre seuil (≥ 3:1, élément graphique), pas celui du texte. */
.aod-cod__unit-head::before{content:"";flex-shrink:0;width:8px;height:8px;border-radius:50%;background:var(--aod-c,var(--sh-text))}
.aod-cod__unit .aod-cod__optsec:last-child{margin-bottom:0}

/* ---- Sections d'options (Taille, Couleur, Pointure…) — style Shopify ---- */
.aod-cod__optsec{margin-bottom:16px}
/* 🚨 `label.aod-cod__optlabel` et non `.aod-cod__optlabel` seul. Plus haut dans
   CE MÊME fichier, `.aod-cod__field:not(.aod-cod__float) > label` pèse (0,2,1) —
   `:not()` ne compte pas pour rien, il ajoute la spécificité de ce qu'il
   contient. Un simple `.aod-cod__optlabel` (0,1,0) perdait : le libellé restait
   `display:block`, donc ni `gap` ni `margin-inline-start:auto` n'avaient d'effet
   et l'écho de sélection se collait à l'astérisque (« Pointure *41 »).
   On égalise à (0,2,1) et on gagne par l'ordre du fichier. La leçon de la
   Phase 37 vaut aussi contre ses PROPRES règles, pas seulement contre le thème. */
.aod-cod__optsec > label.aod-cod__optlabel{
	display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;
	font-size:var(--aod-fs-sm);font-weight:600;color:var(--sh-text);margin:0 0 8px;
}
.aod-cod__optlabel span{color:var(--sh-error)}
/* Écho de la valeur choisie, à côté du libellé (« Pointure * 41 »). Rempli par
   le JS depuis le texte AFFICHÉ de la pastille, donc juste dans les deux
   langues sans une seule chaîne traduisible de plus. */
.aod-cod__optpick{
	font-style:normal;font-weight:500;font-size:var(--aod-fs-xs);color:var(--sh-text-2);
	margin-inline-start:auto;text-align:end;
}
.aod-cod__optpick:empty{display:none}

/* 🚨 GRILLE, et non plus `flex-wrap`. En flex, six pointures se tassaient à
   gauche et la septième partait seule à la ligne : un ruban irrégulier qui se
   lisait comme un défaut d'affichage. Une grille à colonnes égales range les
   valeurs en matrice — et donne le même rendu quel que soit leur nombre. */
.aod-cod__opts{
	display:grid;
	grid-template-columns:repeat(auto-fill,minmax(62px,1fr));
	gap:8px;
	/* Toutes les RANGÉES à la même hauteur, pas seulement les colonnes.
	   Chaque rangée se dimensionne sinon sur son propre contenu : un nom qui
	   passe à deux lignes n'allongeait que SA rangée. Invisible sur un écran
	   large où tout tient sur une ligne, flagrant à 390 px — mesuré, une valeur
	   seule sur la 2ᵉ rangée sortait à 109 px quand les 3 autres faisaient 92. */
	grid-auto-rows:1fr;
}
/* Valeurs porteuses d'une pastille de couleur ou d'un nom long : maille plus
   large, sinon « Bleu marine » se coupe. */
.aod-cod__optsec.has-media .aod-cod__opts{grid-template-columns:repeat(auto-fill,minmax(116px,1fr))}
/* Vignettes photo : la maille suit la vignette (52 px) plus son cadre.
   ⚠️ APRÈS `.has-media` à dessein — les deux sélecteurs sont en (0,3,0), donc
   c'est l'ordre du fichier qui tranche, et une section à photos porte les DEUX
   classes.
   ⚠️ `.has-photo`, PAS `.is-visual` : cette maille étroite suppose une carte
   VERTICALE (vignette au-dessus du nom). `is-visual` est le drapeau du gérant, il
   ne dit rien de ce que la carte rend — une section cochée « visuelle » dont les
   valeurs n'ont qu'un `hex` rend des cartes COMPACTES en ligne, et 86 px n'y
   laissaient que ~39 px de texte : « Milanais argent » s'écrivait une lettre par
   ligne, sur 242 px de haut. `has-photo` est calculé sur les valeurs. */
.aod-cod__optsec.has-photo .aod-cod__opts{grid-template-columns:repeat(auto-fill,minmax(86px,1fr))}

/* La case radio reste accessible mais invisible (la carte fait le rendu). */
.aod-cod__opt{position:relative;display:flex;margin:0;cursor:pointer}
.aod-cod__opt input{position:absolute;opacity:0;width:0;height:0;pointer-events:none}
.aod-cod__opt-card{
	display:flex;align-items:center;justify-content:center;gap:5px;
	/* La pastille remplit sa cellule de grille : c'est ce qui aligne les
	   colonnes au pixel au lieu de les faire dépendre de la longueur du texte. */
	width:100%;box-sizing:border-box;
	/* 44 px : cible tactile. Le template est mobile-first et l'ancienne valeur
	   (32 px) obligeait à viser. */
	min-height:44px;padding:6px 10px;
	border:1px solid var(--sh-line);border-radius:var(--sh-radius-sm);background:var(--aod-n-bg);
	font-size:var(--aod-fs-sm);font-weight:500;color:var(--sh-text);text-align:center;
	/* Survol, coché et focus ne changent que bordure / ombre / fond. */
	transition:border-color .12s,box-shadow .12s,background-color .12s;
}
/* `:focus-within` autant que `:hover` : au clavier, c'est la radio cachée qui
   prend le focus, et le survol seul laisserait la navigation clavier muette. */
.aod-cod__opt:hover .aod-cod__opt-card,
.aod-cod__opt:focus-within .aod-cod__opt-card{border-color:var(--sh-line-strong)}
/* État choisi : anneau INTÉRIEUR. Un `box-shadow` extérieur débordait sur la
   cellule voisine maintenant que les pastilles sont serrées en grille. */
.aod-cod__opt input:checked + .aod-cod__opt-card{
	border-color:var(--sh-accent-line);
	box-shadow:inset 0 0 0 1px var(--sh-accent-line);
	background:var(--sh-accent-wash);
	font-weight:600;
}
.aod-cod__opt input:focus-visible + .aod-cod__opt-card{
	outline:2px solid var(--sh-accent-line);outline-offset:2px;
}
/* ⚠️ `break-word`, JAMAIS `anywhere`. Les deux coupent un mot trop long ; seul
   `anywhere` autorise la coupure à n'importe quel caractère POUR LE CALCUL de la
   largeur minimale, si bien que la boîte peut se réduire à une lettre au lieu de
   réclamer la place du mot. C'est ce qui rendait « Milanais argent » à la
   verticale, une lettre par ligne. `break-word` préfère les espaces et ne casse
   un mot que s'il ne tient pas seul. */
.aod-cod__opt-name{line-height:1.25;min-width:0;overflow-wrap:break-word}
/* Le supplément passe SOUS le nom : inline, il faisait exploser la largeur
   d'une seule pastille et cassait l'alignement de toute la grille. */
.aod-cod__opt-plus{font-size:var(--aod-fs-2xs);color:var(--sh-accent-text);font-weight:700;line-height:1}
.aod-cod__opt--visual .aod-cod__opt-card,
.aod-cod__optsec:not(.has-media) .aod-cod__opt-card{flex-direction:column;gap:3px}
/* Valeurs « avec photo » : pastille verticale (vignette + nom). */
.aod-cod__opt--visual .aod-cod__opt-card{gap:6px;padding:8px 6px}
.aod-cod__opt-thumb{
	width:52px;height:52px;border-radius:6px;background-size:cover;background-position:center;
	border:1px solid var(--sh-line);flex:0 0 auto;
}
/* Pastille de couleur prédéfinie (pas de photo) : on affiche la vraie couleur.
   Le double anneau (blanc à l'intérieur, gris à l'extérieur) garde le point
   visible aussi bien sur un blanc pur que sur un noir pur. */
.aod-cod__opt-thumb--color{box-shadow:inset 0 0 0 2px #fff;border-color:rgba(0,0,0,.28)}
/* Emplacement RÉSERVÉ d'une valeur sans visuel, dans une section qui en a un.
   Elle occupe exactement la place d'une vignette : c'est tout son rôle — sans
   elle, le nom d'une carte sans photo se centrait à une autre hauteur que celui
   de ses voisines et la rangée partait en escalier (et à 390 px, une valeur seule
   sur sa ligne mesurait 51 px contre 92 px). Discrète à dessein : elle ne montre
   rien, elle ne doit donc pas se donner l'air de montrer quelque chose. */
.aod-cod__opt-thumb--vide{background:var(--sh-bg-sub);border-style:dashed}
/* Couleur sans photo : carte compacte en ligne (point rond + nom),
   au lieu de la grande vignette verticale des valeurs avec photo. */
/* ⚠️ GRILLE et non `flex-direction:row`, et c'est le cœur du correctif.
   En ligne, le supplément (« +7,300.00 د.ج ») se plaçait À CÔTÉ du nom au lieu
   de dessous — défaisant la règle écrite quelques lignes plus haut, dont le
   commentaire dit précisément qu'inline il « faisait exploser la largeur ». Il
   mangeait ~75 px, laissant ~35 px au nom, et `overflow-wrap:anywhere` faisait
   alors son travail : « Milanais argent » s'écrivait UNE LETTRE PAR LIGNE, sur
   242 px de haut. La grille garde le point à gauche sur toute la hauteur et
   empile nom puis supplément dans la seconde colonne — le rendu que la règle
   d'origine décrivait déjà. Une valeur sans supplément n'a qu'une rangée. */
.aod-cod__opt--color .aod-cod__opt-card{
	display:grid;grid-template-columns:auto minmax(0,1fr);
	align-items:center;justify-items:start;justify-content:flex-start;
	padding:6px 10px;gap:2px 8px;text-align:start;
}
/* Placement EXPLICITE des trois éléments. Laissé à l'auto-placement, le
   supplément se rangeait sous le POINT et non sous le nom. */
.aod-cod__opt--color .aod-cod__opt-thumb{width:20px;height:20px;border-radius:50%;grid-column:1;grid-row:1/span 2}

/* ═══════════════════════════════════════════════════════════════════════════
   81.3 — LA SECTION PREND LA FORME QUE SON CONTENU RÉCLAME.

   🚨 CE QUI ÉTAIT MESURÉ (2026-08-14, `montre-pulse-s1` § « Bracelet », 390 px) :
   la pastille de couleur faisait **20 × 20 px, RONDE**, posée dans une carte à
   bordure — soit exactement la taille, la forme et la place d'un bouton radio.
   *L'acheteur ne pouvait pas savoir s'il regardait une couleur ou une case
   cochée.* Baymard mesure 40 px de minimum sur mobile, Apple 44 px de cible
   tactile ; et la recherche impute **17 % des abandons de panier** à une
   sélection d'options confuse.

   Deux gabarits, et un seul par section — `is-row` est calculé par PHP :

   ① SANS `is-row` (couleur, noms courts, aucun supplément, aucune rupture) :
      **pastille nue de 44 px**. Plus de carte autour du point : la pastille EST
      la cible. Le nom passe au survol (`title`) et à l'écho de sélection, et
      reste lisible par un lecteur d'écran. Impossible à confondre avec une case.

   ② AVEC `is-row` : **une valeur par ligne, pleine largeur**. C'est le seul
      gabarit où un nom long, un supplément et « Rupture de stock » tiennent tous
      les trois sans coupure de mot.

   ⛔ AUCUNE RÈGLE N'EST POSÉE AILLEURS QUE DANS CETTE FEUILLE. C'est la leçon de
   R2 de `test-panneau-choix.php` : une peau `.aod-cod__*` écrite dans un autre
   fichier est la « seconde peau » que ce recensement fermé existe pour interdire.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── ① PASTILLE NUE ─────────────────────────────────────────────────────── */

/* La maille suit la pastille, pas un nom qu'on n'affiche plus. */
.aod-cod__optsec.has-media:not(.has-photo):not(.is-row) .aod-cod__opts{
	grid-template-columns:repeat(auto-fill,minmax(56px,1fr));
}
/* ⚠️ La carte cesse d'être une carte : ni bordure, ni fond, ni ombre. Ce qui
   distinguait « choisi » de « non choisi » se déplace entièrement sur l'anneau
   de la pastille, ci-dessous — deux marqueurs concurrents se contrediraient. */
.aod-cod__optsec.has-media:not(.has-photo):not(.is-row) .aod-cod__opt-card{
	display:flex;align-items:center;justify-content:center;
	min-height:44px;padding:0;gap:0;
	border:0;background:none;box-shadow:none;
}
/* 44 px de cible tactile (Apple HIG), 36 px de couleur visible : l'écart porte
   l'anneau de sélection sans faire bouger la grille au clic. */
.aod-cod__optsec.has-media:not(.has-photo):not(.is-row) .aod-cod__opt-thumb{
	width:36px;height:36px;border-radius:50%;
	/* 🚨 AUCUNE BORDURE N'EST REDÉCLARÉE ICI, ET C'EST R1 DE
	   `test-surfaces-publiques.php` QUI ME L'A APPRIS. Mon premier jet écrivait
	   `border:1px solid rgba(0,0,0,.28)` pour garder un blanc pur visible sur fond
	   clair — une surface LITTÉRALE de plus sur les feuilles publiques, dont le
	   plafond (4) « ne remonte jamais ». Or le liseré existait DÉJÀ, deux fois : la
	   règle de base pose `border:1px solid var(--sh-line)` et `--color` en remplace
	   la teinte. *Redéclarer ce que la cascade fournit, ce n'est pas être explicite,
	   c'est ajouter une valeur en dur que l'accent du client ne pourra plus
	   reteindre.*
	   ⚠️ `box-shadow:none`, en revanche, est OBLIGATOIRE : `--color` pose un anneau
	   INTÉRIEUR blanc, et l'état choisi ci-dessous emploie la même propriété. Deux
	   anneaux sur un seul `box-shadow`, c'est le second qui efface le premier. */
	box-shadow:none;
	transition:box-shadow .12s;
}
/* 🚨 L'ANNEAU EST À L'EXTÉRIEUR, et c'est ce qui le rend impossible à confondre
   avec le point intérieur d'un bouton radio : un radio coché se remplit AU
   CENTRE, une pastille choisie se cercle AU DEHORS. Le liseré blanc intermédiaire
   détache l'anneau d'une couleur foncée. */
.aod-cod__optsec.has-media:not(.has-photo):not(.is-row) input:checked + .aod-cod__opt-card .aod-cod__opt-thumb{
	box-shadow:0 0 0 2px var(--sh-bg),0 0 0 4px var(--sh-accent);
}
.aod-cod__optsec.has-media:not(.has-photo):not(.is-row) .aod-cod__opt:hover .aod-cod__opt-thumb{
	box-shadow:0 0 0 2px var(--sh-bg),0 0 0 4px var(--sh-line-strong);
}
.aod-cod__optsec.has-media:not(.has-photo):not(.is-row) input:focus-visible + .aod-cod__opt-card{
	outline:2px solid var(--sh-accent-line);outline-offset:2px;border-radius:50%;
}
/* ⚠️ MASQUÉ, PAS RETIRÉ. Le nom reste dans le document : `display:none` le
   soustrairait aussi aux lecteurs d'écran, et le choix deviendrait anonyme. */
.aod-cod__optsec.has-media:not(.has-photo):not(.is-row) .aod-cod__opt-name{
	position:absolute;width:1px;height:1px;padding:0;margin:-1px;
	overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;
}

/* ── ② LIGNES PLEINE LARGEUR ────────────────────────────────────────────── */

.aod-cod__optsec.is-row .aod-cod__opts{grid-template-columns:1fr;gap:6px;grid-auto-rows:auto}
/* Trois colonnes explicites — visuel | nom | supplément. Laissées à
   l'auto-placement, elles se rangeaient sous le visuel (le piège déjà rencontré
   sur `--color` quelques lignes plus haut). */
.aod-cod__optsec.is-row .aod-cod__opt-card{
	display:grid;grid-template-columns:auto minmax(0,1fr) auto;
	align-items:center;justify-items:start;gap:0 10px;
	min-height:52px;padding:8px 12px;text-align:start;
}
/* 40 px : le plancher mobile de Baymard. La ligne a la place, elle la prend. */
.aod-cod__optsec.is-row .aod-cod__opt-thumb{
	width:40px;height:40px;grid-column:1;grid-row:1;
}
.aod-cod__optsec.is-row .aod-cod__opt--color .aod-cod__opt-thumb{border-radius:50%}
.aod-cod__optsec.is-row .aod-cod__opt-name{grid-column:2;grid-row:1;font-size:var(--aod-fs-sm)}
/* ⛔ Le supplément retourne EN LIGNE ici, et c'est permis : la règle qui
   l'envoyait sous le nom protégeait une maille de 116 px. Sur une ligne pleine
   largeur il ne peut plus rien faire exploser — il se cale à la fin. */
.aod-cod__optsec.is-row .aod-cod__opt-plus,
.aod-cod__optsec.is-row .aod-cod__opt-rupture{
	grid-column:3;grid-row:1;justify-self:end;text-align:end;
}
/* Une section en lignes qui porte des photos garde sa vignette rectangulaire :
   on ne transforme pas une photo de produit en rondelle. */
.aod-cod__optsec.is-row.has-photo .aod-cod__opt-thumb{border-radius:6px}
/* ⛔ Et on annule le `flex-direction:column` des cartes visuelles : sur une
   ligne, empiler vignette et nom rendrait la rangée deux fois plus haute pour
   la moitié de l'information. */
.aod-cod__optsec.is-row .aod-cod__opt--visual .aod-cod__opt-card{gap:0 10px;padding:8px 12px}

@media (prefers-reduced-motion: reduce) {
	.aod-cod__opt-thumb{transition:none}
}
.aod-cod__opt--color .aod-cod__opt-name{grid-column:2;grid-row:1}
.aod-cod__opt--color .aod-cod__opt-plus,
.aod-cod__opt--color .aod-cod__opt-rupture{grid-column:2;grid-row:2}

/* ---- 40.4 — Variante en rupture de stock : grisée, non sélectionnable ---- */
/* La radio est `disabled` (garde serveur en plus) : elle ne peut pas être cochée,
   donc l'état « sélectionné » ne s'applique jamais. On dégrise + on barre le nom,
   et le badge « Rupture de stock » reste à pleine couleur (contraste AA). */
.aod-cod__opt--rupture{cursor:not-allowed}
.aod-cod__opt--rupture .aod-cod__opt-card{
	background:var(--sh-bg-sub);border-style:dashed;border-color:var(--sh-line);
}
.aod-cod__opt--rupture:hover .aod-cod__opt-card,
.aod-cod__opt--rupture:focus-within .aod-cod__opt-card{border-color:var(--sh-line)}
.aod-cod__opt--rupture .aod-cod__opt-thumb{opacity:.45}
.aod-cod__opt--rupture .aod-cod__opt-name{color:var(--sh-muted);text-decoration:line-through}
.aod-cod__opt-rupture{
	font-size:var(--aod-fs-2xs);font-weight:700;line-height:1.1;color:var(--sh-error);
	text-transform:uppercase;letter-spacing:.02em;
}

/* ---- Message d'erreur de configuration (variante manquante) ---- */
.aod-cod__config-msg{margin:8px 0 12px;font-size:var(--aod-fs-xs);color:var(--sh-error);min-height:0}
.aod-cod__config-msg:empty{display:none}

/* ---- Flèches de navigation ‹ › superposées sur la galerie produit ---- */
.woocommerce-product-gallery .aod-gallery-nav{
	position:absolute;top:50%;transform:translateY(-50%);z-index:99;
	display:flex;align-items:center;justify-content:center;
	width:40px;height:40px;padding:0;margin:0;
	border:1px solid rgba(var(--aod-n-veil-rgb),.08);border-radius:50%;
	background:rgba(var(--aod-n-bg-rgb),.9);color:#1a1a1a;
	/* Métrique de GLYPHE (la flèche dans son rond de 40 px), pas du texte. */
	font-size:24px;line-height:1;cursor:pointer;
	box-shadow:0 1px 4px rgba(var(--aod-n-shadow-rgb),.18);
	transition:background .12s, transform .05s, box-shadow .12s;
	-webkit-appearance:none;appearance:none;
}
.woocommerce-product-gallery .aod-gallery-nav:hover{background:var(--aod-n-bg);box-shadow:0 2px 8px rgba(var(--aod-n-shadow-rgb),.25)}
.woocommerce-product-gallery .aod-gallery-nav:active{transform:translateY(-50%) scale(.94)}
.woocommerce-product-gallery .aod-gallery-nav:focus-visible{outline:2px solid var(--sh-accent-line);outline-offset:2px}
.woocommerce-product-gallery .aod-gallery-nav--prev{left:12px}
.woocommerce-product-gallery .aod-gallery-nav--next{right:12px}
/* RTL : on inverse les positions pour garder ‹ = précédent côté logique. */
.rtl .woocommerce-product-gallery .aod-gallery-nav--prev{left:auto;right:12px}
.rtl .woocommerce-product-gallery .aod-gallery-nav--next{right:auto;left:12px}

/* ---- Mouvement réduit (ROADMAP Phase 19, étape 19.1) --------------------- *
 * `prefers-reduced-motion` est un réglage SYSTÈME, souvent posé pour un trouble
 * vestibulaire ou une sensibilité migraineuse. `/gestion` l'honorait depuis la
 * Phase 16 ; le côté ACHETEUR, lui, ne l'honorait pas du tout — l'inverse de la
 * priorité du template.
 *
 * 🚨 EN FIN DE FICHIER, ET CE N'EST PAS COSMÉTIQUE. Une surcharge placée avant
 * les règles qu'elle annule perd la cascade SANS RIEN SIGNALER : l'écran reste
 * identique pour qui n'a pas activé le réglage, donc personne ne le voit. C'est
 * le défaut que la Phase 16 a dû corriger dans `dashboard.css`.
 *
 * 🚨 ON NE NEUTRALISE PAS `transform` EN BLOC. Sur les 10 `transform` de ce
 * plugin, 6 sont du POSITIONNEMENT (centrage d'un label flottant, d'une flèche
 * de galerie, glyphe de la coche). Un `transform:none` global ne calmerait pas
 * l'interface : il DÉPLACERAIT ces éléments. On ne retire que le déplacement,
 * et l'acquittement non-moteur (fond, couleur, opacité) reste — c'est lui qui
 * dit à l'acheteur que son geste a été reçu.
 * ------------------------------------------------------------------------- */
/* ---- Statut du stock visuel ---- */
.aod-cod__stock-status{display:flex;align-items:center;margin:0 0 14px}
.aod-cod__stock-pill{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:var(--sh-radius-sm);font-size:var(--aod-fs-xs);font-weight:600;line-height:1}
.aod-cod__stock-pill--in{background:var(--sh-ok-soft);color:var(--sh-green);border:1px solid var(--sh-ok-line)}
.aod-cod__stock-pill--low{background:var(--sh-bad-soft);color:var(--sh-error);border:1px solid var(--sh-bad-line)}
.aod-cod__stock-pill--out{background:var(--sh-bad-soft);color:var(--sh-error);border:1px solid var(--sh-bad-line)}
.aod-cod__stock-dot{width:7px;height:7px;border-radius:var(--sh-radius-sm);background:currentColor}

/* ---- Sélecteur de quantité numérique ---- */
.aod-cod__qty-wrap{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:14px 0;padding:10px 14px;background:var(--sh-bg-sub);border:1px solid var(--sh-line);border-radius:var(--sh-radius-sm)}
.aod-cod__qty-label{font-size:var(--aod-fs-sm);font-weight:600;color:var(--sh-text)}
.aod-cod__qty-stepper{display:inline-flex;align-items:center;border:1px solid var(--sh-line);border-radius:var(--sh-radius-sm);background:var(--sh-bg);overflow:hidden}
.aod-cod__qty-btn{display:flex;align-items:center;justify-content:center;width:38px;height:38px;padding:0;margin:0;border:0;background:var(--sh-bg);color:var(--sh-text);font-size:var(--aod-fs-md);font-weight:600;line-height:1;cursor:pointer;transition:background .12s ease}
.aod-cod__qty-btn:hover{background:var(--sh-bg-sub)}
.aod-cod__qty-val{display:flex;align-items:center;justify-content:center;min-width:36px;padding:0 6px;font-size:var(--aod-fs-sm);font-weight:700;color:var(--sh-text);text-align:center}

/* ---- Étapes du formulaire COD ---- */
.aod-cod__step{margin:22px 0 0;padding-top:18px;border-top:1px solid var(--sh-line)}
.aod-cod__step:first-of-type{margin-top:12px;padding-top:0;border-top:0}
.aod-cod__step-header{display:flex;align-items:center;gap:10px;margin-bottom:14px}
.aod-cod__step-num{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background:var(--sh-bg-sub);color:var(--sh-text);border:1.5px solid var(--sh-line-strong);font-size:var(--aod-fs-xs);font-weight:700;line-height:1;flex-shrink:0}
.aod-cod__step-title{font-size:var(--aod-fs-base);font-weight:600;color:var(--sh-text)}
.aod-cod__phone-hint{display:block;margin-top:6px;font-size:var(--aod-fs-xs);color:var(--sh-error)}
.aod-cod__phone-hint[hidden]{display:none !important}
.aod-cod__field input.is-invalid{border-color:var(--sh-error)}

/* ---- Grille de réassurance à 4 piliers ---- */
.aod-cod__reassure-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:20px 0 12px;padding:14px 12px;background:var(--sh-bg-sub);border:1px solid var(--sh-line);border-radius:var(--sh-radius)}
.aod-cod__reassure-item{display:flex;align-items:flex-start;gap:8px}
.aod-cod__reassure-ico{display:flex;align-items:center;justify-content:center;color:var(--sh-green);flex-shrink:0;margin-top:2px}
.aod-cod__reassure-text{display:flex;flex-direction:column;gap:2px;min-width:0}
.aod-cod__reassure-text strong{font-size:var(--aod-fs-xs);font-weight:700;color:var(--sh-text);line-height:1.2}
.aod-cod__reassure-text span{font-size:var(--aod-fs-2xs);color:var(--sh-text-2);line-height:1.2}

/* ---- Bouton voir panier quand panier non vide ---- */
.aod-cod__viewcart{display:inline-flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:48px;padding:12px 18px;margin-top:10px;background:var(--sh-bg-sub);color:var(--sh-text);border:1.5px solid var(--sh-line);border-radius:var(--sh-radius-sm);font-size:var(--aod-fs-base);font-weight:600;cursor:pointer;text-decoration:none;transition:background .12s ease,border-color .12s ease}
.aod-cod__viewcart:hover{background:var(--sh-bg);border-color:var(--sh-line-strong)}

/* ---- Toast notification flottante (vert de confirmation, rouge d'erreur) ---- */
.aod-toast{position:fixed;inset-block-end:24px;inset-inline:16px;margin-inline:auto;max-width:440px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;background:var(--aod-n-ok);color:var(--aod-n-bg);border:1px solid var(--aod-n-ok-line);border-radius:var(--sh-radius,8px);z-index:99999;box-shadow:0 8px 24px rgba(var(--aod-n-shadow-rgb),.25);opacity:0;transform:translateY(12px);transition:opacity .2s ease,transform .2s ease}
.aod-toast.is-visible{opacity:1;transform:translateY(0)}
.aod-toast__msg{font-size:var(--aod-fs-sm);font-weight:600;line-height:1.3;color:var(--aod-n-bg)}
.aod-toast__actions{display:flex;align-items:center;gap:8px;flex-shrink:0}
.aod-toast__btn{padding:6px 12px;border-radius:var(--sh-radius-sm,6px);font-size:var(--aod-fs-xs);font-weight:700;cursor:pointer;border:0;line-height:1;transition:opacity .12s ease}
.aod-toast__btn--view{background:var(--aod-n-bg);color:var(--aod-n-ok)}
.aod-toast__btn--close{background:transparent;color:inherit;opacity:.85}
.aod-toast__btn--close:hover{opacity:1}
.aod-toast--error{background:var(--aod-n-bad);border-color:var(--aod-n-bad-line)}
.aod-toast--error .aod-toast__btn--view{color:var(--aod-n-bad)}
/* ---- Barre mobile collante (CTA sticky) ---- */
.aod-sticky-bar{display:none}
@media (max-width:780px){
	.aod-sticky-bar{display:block;position:fixed;inset-block-end:0;inset-inline:0;z-index:9990;background:var(--sh-bg);border-top:1px solid var(--sh-line);padding:10px 16px}
	.aod-sticky-bar[hidden]{display:none}
	.aod-sticky-bar__inner{display:flex;align-items:center;justify-content:space-between;gap:12px;max-width:560px;margin:0 auto}
	.aod-sticky-bar__info{display:flex;flex-direction:column;min-width:0}
	.aod-sticky-bar__title{font-size:var(--aod-fs-xs);color:var(--sh-text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
	.aod-sticky-bar__price{font-size:var(--aod-fs-base);font-weight:700;color:var(--sh-text)}
	.aod-sticky-bar__btn{display:inline-flex;align-items:center;justify-content:center;padding:10px 20px;background:var(--sh-accent);color:var(--sh-accent-ink);border:0;border-radius:var(--sh-radius-sm);font-size:var(--aod-fs-sm);font-weight:700;cursor:pointer;white-space:nowrap;transition:opacity .12s ease}
}

/* ---- Polissage Galerie et Onglets WooCommerce ---- */
.woocommerce-product-gallery{border-radius:var(--sh-radius);overflow:hidden}
.woocommerce-product-gallery .woocommerce-product-gallery__image{border-radius:var(--sh-radius);overflow:hidden}
.woocommerce-product-gallery .flex-control-thumbs{display:flex;gap:8px;padding:10px 0 0;margin:0;list-style:none}
.woocommerce-product-gallery .flex-control-thumbs li{flex:0 0 70px;cursor:pointer;border-radius:var(--sh-radius-sm);overflow:hidden;border:2px solid transparent;transition:border-color .12s ease}
.woocommerce-product-gallery .flex-control-thumbs li img.flex-active,
.woocommerce-product-gallery .flex-control-thumbs li:hover{border-color:var(--sh-accent-line)}

.woocommerce-tabs{margin-top:36px;padding-top:24px;border-top:1px solid var(--sh-line)}
.woocommerce-tabs ul.tabs{display:flex;gap:8px;padding:0;margin:0 0 20px;list-style:none;border-bottom:2px solid var(--sh-line)}
.woocommerce-tabs ul.tabs li{margin:0;padding:10px 18px;font-size:var(--aod-fs-sm);font-weight:600;color:var(--sh-text-2);border-bottom:2px solid transparent;margin-bottom:-2px;cursor:pointer;transition:color .12s ease,border-color .12s ease}
.woocommerce-tabs ul.tabs li.active{color:var(--sh-accent-text);border-bottom-color:var(--sh-accent)}
.woocommerce-tabs .panel{font-size:var(--aod-fs-sm);line-height:1.7;color:var(--sh-text)}

@media (prefers-reduced-motion: reduce) {
	/* Le label flottant CHANGE DE PLACE (top + transform) : on garde le
	   changement, on retire le trajet. La taille et la couleur continuent de
	   transitionner — ce n'est pas du mouvement. */
	.aod-cod__float > label {
		transition: font-size .12s ease, color .12s ease;
	}

	/* Creux d'appui du bouton de commande : pur décalage, `none` est correct ici.
	   🚨 Scopé `.aod-cod` (0,3,0) depuis la 37.3, comme la règle qu'il annule :
	   une media query n'ajoute AUCUNE spécificité, donc une surcharge restée en
	   (0,2,0) aurait perdu contre la base scopée — le mouvement serait resté,
	   visible seulement de qui a activé le réglage, c'est-à-dire de personne
	   pendant la revue. */
	.aod-cod .aod-cod__submit { transition: none; }
	.aod-cod .aod-cod__submit:active { transform: none; }

	/* Entrée du panneau de remerciement. `animation:none` rend l'état FINAL des
	   keyframes (opacity 1), donc le panneau s'affiche — simplement, d'un coup. */
	.aod-cod__done { animation: none; }

	/* 🚨 CAS MIXTE — `translateY(-50%)` CENTRE la flèche, `scale(.94)` est l'appui.
	   `transform:none` ici décentrerait le bouton de la moitié de sa hauteur : on
	   retombe sur le centrage, pas sur rien. */
	.woocommerce-product-gallery .aod-gallery-nav:active {
		transform: translateY(-50%);
	}
	.aod-toast,
	.aod-sticky-bar__btn,
	.aod-cod__qty-btn,
	.aod-cod__viewcart,
	.woocommerce-tabs ul.tabs li {
		transition: none;
	}
	.aod-toast.is-visible { transform: none; }
}

/* ---- 49.4 — L'en-tête d'une carte d'offre : 44 px au pouce, MOBILE SEULEMENT -
 * Mesuré à 316×34 px. Ce `<label>` EST la cible : c'est lui qui commande le
 * bouton radio de l'offre (« 1 produit », « 2 produits »…), donc le choix qui
 * décide du montant de la commande. On borne la hauteur ; le contenu se centre
 * tout seul, la rangée est déjà en `flex` avec `align-items:center`. */
@media (max-width: 780px) {
	.aod-cod__offer-head { min-height: 44px; }
}
