/* ------------------------------------------------------------------ *
 *  Police Inter, servie depuis nos propres serveurs
 *
 *  Elle venait de fonts.googleapis.com, ce qui transmettait à Google
 *  l'adresse IP et le navigateur de chaque visiteur — y compris ceux des
 *  destinataires, qui n'ont rien choisi et n'ont accepté aucune condition.
 *  Notre politique de confidentialité déclare OVH, SMSFactor, Stripe et
 *  ipinfo ; elle ne déclarait pas Google, et n'aurait pas dû avoir à le
 *  faire. L'héberger ici supprime le transfert, économise deux résolutions
 *  DNS et deux négociations TLS par page, et fonctionne sur les réseaux
 *  d'entreprise qui bloquent les domaines Google.
 *
 *  Police variable : un seul fichier couvre les graisses 400 à 800.
 *  Inter est publiée sous licence SIL Open Font, qui autorise
 *  expressément cet hébergement.
 * ------------------------------------------------------------------ */

@font-face {
	font-family: 'Inter';
	font-style: normal;
	font-weight: 400 800;
	font-display: swap;
	src: url('../fonts/inter-latin.woff2') format('woff2');
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: 'Inter';
	font-style: normal;
	font-weight: 400 800;
	font-display: swap;
	src: url('../fonts/inter-latin-ext.woff2') format('woff2');
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ==========================================================================
   OURPASS — habillage aux couleurs OURTECH
   --------------------------------------------------------------------------
   Chargée EN DERNIER, après bootstrap / style / custom / vendors.
   Aucune feuille du gabarit d'origine n'est modifiée : retirer le <link>
   de ce fichier restaure le rendu précédent.

   L'application était bâtie sur un gabarit de réservation d'hôtel (« Melkin »),
   d'où les fonds bg_hotel / bg_spa et le vert turquoise des boutons.
   On reprend ici l'identité du site : violet #761FE3, typographie Inter,
   champs et cartes calqués sur la maquette OURPASS de la page d'accueil.
   ========================================================================== */

:root {
	--op-violet: #761FE3;
	--op-violet-fonce: #4C1D95;
	--op-encre: #1A1523;
	--op-gris: #4B4560;
	--op-bordure: #E9E5F0;
	--op-surface: #FAF8FF;
	--op-degrade: linear-gradient(112deg, #761FE3 0%, #4C1D95 100%);
	--op-ombre: rgba(26,21,35,0.10);
}

/* ── Le menu du gabarit, rendu disponible partout ────────────────────────

   Six pages n'avaient pas de menu ; la barre que j'avais improvisee n'etait
   pas celle des autres pages — « nul, meme pas le meme menu ». Elles ont donc
   desormais le MEME menu, au caractere pres.

   Les regles viennent de css/menu.css, a deux exceptions pres, retirees :

     *, *::after, *::before { box-sizing: border-box }
     *::after, *::before    { content: '' }

   Les charger telles quelles aurait pose border-box sur TOUT l'element de
   ces pages, dont la mise en page a ete dessinee sans — cette feuille n'a
   aucun box-sizing global, c'est meme la cause du debordement mobile du
   10 aout. Le menu recoit donc ci-dessous exactement ce que ces deux regles
   lui donnaient, et rien de plus.

   ⚠️ Ces regles sont une COPIE de css/menu.css (gabarit fige, 2024). Toucher
   a l'un sans l'autre les ferait diverger. */

.cd-nav-trigger,
.cd-nav-trigger *,
.cd-overlay-nav,
.cd-overlay-content,
.cd-primary-nav,
.cd-primary-nav * {
	box-sizing: border-box;
}

/* Sans « content », ces pseudo-elements n'existent pas : les trois barres du
   bouton disparaissent, et il ne reste qu'un carre vide. */
.cd-nav-trigger::before,
.cd-nav-trigger::after,
.cd-nav-trigger .cd-icon::before,
.cd-nav-trigger .cd-icon::after,
.cd-overlay-nav span::before,
.cd-overlay-content span::before {
	content: '';
}

/* -------------------------------- 
Primary style
-------------------------------- */
/* -------------------------------- 
Main components 
-------------------------------- */
.cd-nav-trigger {
  position: absolute;
  display: inline-block;
}

.cd-nav-trigger {
  top: 15px;
  right: 20px;
  height: 44px;
  width: 44px;
  z-index: 999999;
  /* image replacement */
  overflow: hidden;
  text-indent: 100%;
  white-space: nowrap;
}
@media (max-width: 991px) {
  .cd-nav-trigger {
    top: 10px;
    right: 15px;
  }
}

.cd-nav-trigger .cd-icon {
  /* icon created in CSS */
  position: absolute;
  left: 50%;
  top: 50%;
  bottom: auto;
  right: auto;
  -webkit-transform: translateX(-50%) translateY(-50%);
  -moz-transform: translateX(-50%) translateY(-50%);
  -ms-transform: translateX(-50%) translateY(-50%);
  -o-transform: translateX(-50%) translateY(-50%);
  transform: translateX(-50%) translateY(-50%);
  display: inline-block;
  width: 18px;
  height: 2px;
  background-color: #222;
  z-index: 9999999;
}

.cd-nav-trigger .cd-icon::before, .cd-nav-trigger .cd-icon:after {
  /* upper and lower lines of the menu icon */
  position: absolute;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  background-color: #222;
  /* Force Hardware Acceleration in WebKit */
  -webkit-transform: translateZ(0);
  -moz-transform: translateZ(0);
  -ms-transform: translateZ(0);
  -o-transform: translateZ(0);
  transform: translateZ(0);
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  /* apply transition to transform property */
  -webkit-transition: -webkit-transform .3s;
  -moz-transition: -moz-transform .3s;
  transition: transform .3s;
}

.cd-nav-trigger .cd-icon::before {
  -webkit-transform: translateY(-6px) rotate(0deg);
  -moz-transform: translateY(-6px) rotate(0deg);
  -ms-transform: translateY(-6px) rotate(0deg);
  -o-transform: translateY(-6px) rotate(0deg);
  transform: translateY(-6px) rotate(0deg);
}

.cd-nav-trigger .cd-icon::after {
  -webkit-transform: translateY(6px) rotate(0deg);
  -moz-transform: translateY(6px) rotate(0deg);
  -ms-transform: translateY(6px) rotate(0deg);
  -o-transform: translateY(6px) rotate(0deg);
  transform: translateY(6px) rotate(0deg);
}

.cd-nav-trigger::before, .cd-nav-trigger::after {
  /* 2 rounded colored backgrounds for the menu icon */
  position: absolute;
  top: 0;
  left: 0;
  border-radius: 50%;
  height: 100%;
  width: 100%;
  /* Force Hardware Acceleration in WebKit */
  -webkit-transform: translateZ(0);
  -moz-transform: translateZ(0);
  -ms-transform: translateZ(0);
  -o-transform: translateZ(0);
  transform: translateZ(0);
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  -webkit-transition-property: -webkit-transform;
  -moz-transition-property: -moz-transform;
  transition-property: transform;
}

.cd-nav-trigger::before {
  background-color: white;
  background-color: white;
  -webkit-transform: scale(1);
  -moz-transform: scale(1);
  -ms-transform: scale(1);
  -o-transform: scale(1);
  transform: scale(1);
  -webkit-transition-duration: 0.3s;
  -moz-transition-duration: 0.3s;
  transition-duration: 0.3s;
  -webkit-transition-delay: 0.4s;
  -moz-transition-delay: 0.4s;
  transition-delay: 0.4s;
}

.cd-nav-trigger::after {
  background-color: white;
  background-color: white;
  -webkit-transform: scale(0);
  -moz-transform: scale(0);
  -ms-transform: scale(0);
  -o-transform: scale(0);
  transform: scale(0);
  -webkit-transition-duration: 0s;
  -moz-transition-duration: 0s;
  transition-duration: 0s;
  -webkit-transition-delay: 0s;
  -moz-transition-delay: 0s;
  transition-delay: 0s;
}

.cd-nav-trigger.close-nav::before {
  /* user clicks on the .cd-nav-trigger element - 1st rounded background disappears */
  -webkit-transform: scale(0);
  -moz-transform: scale(0);
  -ms-transform: scale(0);
  -o-transform: scale(0);
  transform: scale(0);
}

.cd-nav-trigger.close-nav::after {
  /* user clicks on the .cd-nav-trigger element - 2nd rounded background appears */
  -webkit-transform: scale(1);
  -moz-transform: scale(1);
  -ms-transform: scale(1);
  -o-transform: scale(1);
  transform: scale(1);
  -webkit-transition-duration: 0.3s;
  -moz-transition-duration: 0.3s;
  transition-duration: 0.3s;
  -webkit-transition-delay: 0.4s;
  -moz-transition-delay: 0.4s;
  transition-delay: 0.4s;
}

.cd-nav-trigger.close-nav .cd-icon {
  /* user clicks on the .cd-nav-trigger element - transform the icon */
  background-color: transparent;
}

.cd-nav-trigger.close-nav .cd-icon::before, .cd-nav-trigger.close-nav .cd-icon::after {
  background-color: #222;
}

.cd-nav-trigger.close-nav .cd-icon::before {
  -webkit-transform: translateY(0) rotate(45deg);
  -moz-transform: translateY(0) rotate(45deg);
  -ms-transform: translateY(0) rotate(45deg);
  -o-transform: translateY(0) rotate(45deg);
  transform: translateY(0) rotate(45deg);
}

.cd-nav-trigger.close-nav .cd-icon::after {
  -webkit-transform: translateY(0) rotate(-45deg);
  -moz-transform: translateY(0) rotate(-45deg);
  -ms-transform: translateY(0) rotate(-45deg);
  -o-transform: translateY(0) rotate(-45deg);
  transform: translateY(0) rotate(-45deg);
}

.cd-primary-nav {
  position: fixed;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  padding: 80px 5%;
  z-index: 9999;
  background-color: #fff;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  visibility: hidden;
  opacity: 0;
  -webkit-transition: visibility 0s, opacity 0.3s;
  -moz-transition: visibility 0s, opacity 0.3s;
  transition: visibility 0s, opacity 0.3s;
}

ul.cd-primary-nav {
  list-style: none;
  padding: 80px 0 0 0;
  margin: 0;
}

.cd-primary-nav li {
  margin: 1.2em 0;
  text-align: center;
  text-transform: capitalize;
}

.cd-primary-nav a {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-transition: color 0.2s;
  -moz-transition: color 0.2s;
  transition: color 0.2s;
  color: #777;
  font-size: 18px;
}

.no-touch .cd-primary-nav a:hover {
  color: #111;
}

.cd-primary-nav.fade-in {
  visibility: visible;
  opacity: 1;
}

@media only screen and (min-width: 768px) {
  .cd-primary-nav li {
    margin: 2em 0;
  }

  .cd-primary-nav a {
    font-size: 21px;
  }
}
@media only screen and (min-width: 1170px) {
  .cd-primary-nav li {
    margin: 2.6em 0;
  }

  .cd-primary-nav a {
    font-size: 24px;
  }
}
.cd-overlay-nav, .cd-overlay-content {
  /* containers of the 2 main rounded backgrounds - these containers are used to position the rounded bgs behind the menu icon */
  position: fixed;
  top: 18px;
  right: 5%;
  height: 4px;
  width: 4px;
  -webkit-transform: translateX(-20px) translateY(20px);
  -moz-transform: translateX(-20px) translateY(20px);
  -ms-transform: translateX(-20px) translateY(20px);
  -o-transform: translateX(-20px) translateY(20px);
  transform: translateX(-20px) translateY(20px);
}

.cd-overlay-nav span, .cd-overlay-content span {
  display: inline-block;
  position: absolute;
  border-radius: 50%;
  /* Force Hardware Acceleration in WebKit */
  -webkit-transform: translateZ(0);
  -moz-transform: translateZ(0);
  -ms-transform: translateZ(0);
  -o-transform: translateZ(0);
  transform: translateZ(0);
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  will-change: transform;
  -webkit-transform-origin: 50% 50%;
  -moz-transform-origin: 50% 50%;
  -ms-transform-origin: 50% 50%;
  -o-transform-origin: 50% 50%;
  transform-origin: 50% 50%;
  -webkit-transform: scale(0);
  -moz-transform: scale(0);
  -ms-transform: scale(0);
  -o-transform: scale(0);
  transform: scale(0);
}

.cd-overlay-nav.is-hidden, .cd-overlay-content.is-hidden {
  /* background fades out at the end of the animation */
  opacity: 0;
  visibility: hidden;
  -webkit-transition: opacity .3s 0s, visibility 0s .3s;
  -moz-transition: opacity .3s 0s, visibility 0s .3s;
  transition: opacity .3s 0s, visibility 0s .3s;
}

.cd-overlay-nav {
  /* main rounded colored bg 1 */
  z-index: 999;
}

.cd-overlay-nav span {
  background-color: #ddd;
}

.cd-overlay-content {
  /* main rounded colored bg 2 */
  z-index: 99999;
}

.cd-overlay-content span {
  background-color: #ddd;
}



/* ── Ce que style.css donnait aux entrees du menu ─────────────────────────

   Les pages du gabarit chargent css/style.css, qui pose sur `.animated_link`
   ce que le navigateur ne fait pas :

       a.animated_link { text-decoration: none }

   Sans elle, CHAQUE entree du menu porte le soulignement par defaut du
   navigateur — « le surlignage omnipresent sur le menu des nouvelles pages ».
   Le soulignement n'y apparait normalement qu'au survol, dessine par un
   pseudo-element qui se deplie de gauche a droite.

   Releve exhaustif : ce sont les seules regles de style.css, custom.css et
   vendors.css qui touchent le menu. */

a.animated_link {
	position: relative;
	text-decoration: none;
}

a.animated_link::before {
	content: "";
	position: absolute;
	width: 100%;
	height: 1px;
	bottom: -5px;
	left: 0;
	opacity: 1;
	background-color: #555;
	visibility: hidden;
	transform: scaleX(0);
	transition: all 0.3s ease;
}

a.animated_link:hover::before {
	visibility: visible;
	transform: scaleX(1);
}

a.animated_link.active {
	position: relative;
	text-decoration: none;
	color: #2abfaa;
}

a.animated_link.active::before {
	content: "";
	position: absolute;
	width: 100%;
	height: 1px;
	bottom: -5px;
	left: 0;
	opacity: 1;
	background-color: #555;
	visibility: visible;
	transform: scaleX(1);
}


body {
	font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
	color: var(--op-gris);
	background-color: #FFFFFF;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6 {
	font-family: 'Inter', system-ui, sans-serif;
	color: var(--op-encre);
	font-weight: 700;
	letter-spacing: -0.02em;
	line-height: 1.2;
}


/* --------------------------------------------------------------------------
   1. Volet de gauche — la vitrine de marque
   Le gabarit y posait une photo d'hôtel ou de spa. Remplacée par le dégradé
   de marque, avec le halo et le grain de la page d'accueil.
   -------------------------------------------------------------------------- */

.content-left .wrapper,
.wrapper.bg_hotel,
.wrapper.bg_spa,
.content-left-wrapper {
	background-image: none !important;
	background-color: #1A1523 !important;
	position: relative;
	overflow: hidden;
}

.content-left .wrapper::before,
.content-left-wrapper::before {
	position: absolute;
	content: '';
	left: 0; top: 0; right: 0; bottom: 0;
	z-index: 0;
	pointer-events: none;
	background:
		linear-gradient(158deg, #1A1523 0%, #2E1065 52%, #4C1D95 100%),
		radial-gradient(circle at 22% 18%, rgba(147,51,234,0.45) 0%, transparent 45%);
	background-blend-mode: normal, screen;
}

/* Trame de points, comme sur le site */
.content-left .wrapper::after,
.content-left-wrapper::after {
	position: absolute;
	content: '';
	left: 0; top: 0; right: 0; bottom: 0;
	z-index: 1;
	pointer-events: none;
	opacity: 0.5;
	background-image: radial-gradient(rgba(255,255,255,0.09) 1px, transparent 1px);
	background-size: 26px 26px;
}

.content-left .wrapper > *,
.content-left-wrapper > * { position: relative; z-index: 2; }

.left_title,
.left_title * { color: #FFFFFF !important; }

.left_title h2,
.left_title h3 {
	color: #FFFFFF !important;
	font-size: 34px;
	letter-spacing: -0.03em;
	margin-bottom: 12px;
}

.left_title p,
.left_title .subtitle { color: rgba(255,255,255,0.78) !important; font-size: 17px; }


/* --------------------------------------------------------------------------
   2. Volet de droite — le formulaire
   -------------------------------------------------------------------------- */

.content-right {
	background-color: #FFFFFF;
	padding: 48px 56px;
}

.content-right h1,
.content-right h2,
.content-right h3 { color: var(--op-encre); }


/* --------------------------------------------------------------------------
   3. Champs de saisie
   Mêmes règles que sur le site : fond blanc, bordure nette, anneau violet
   au focus.
   -------------------------------------------------------------------------- */

.form-control,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="tel"],
input[type="number"],
input[type="search"],
select,
textarea,
.form-select {
	background-color: #FFFFFF;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	color: var(--op-encre);
	font-size: 15px;
	padding: 11px 14px;
	box-shadow: none;
	transition: border-color 160ms ease, box-shadow 160ms ease;
}

.form-control:focus,
input:focus,
select:focus,
textarea:focus,
.form-select:focus {
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
	outline: none;
}

.form-control::placeholder,
textarea::placeholder { color: #8B8397; opacity: 1; }

label,
.form-label {
	color: var(--op-encre);
	font-weight: 500;
	font-size: 14px;
	margin-bottom: 6px;
}


/* --------------------------------------------------------------------------
   4. Boutons
   Le gabarit les posait en turquoise #2abfaa. Passage au violet de marque.
   -------------------------------------------------------------------------- */

.btn_1,
.btn.btn-primary,
.btn-primary,
button[type="submit"]:not(.btn-secondary):not(.btn-close) {
	background: var(--op-degrade) !important;
	border: none !important;
	color: #FFFFFF !important;
	border-radius: 9px;
	padding: 12px 24px;
	font-size: 15px;
	font-weight: 600;
	letter-spacing: -0.01em;
	box-shadow: 0 4px 14px rgba(118,31,227,0.26);
	transition: transform 160ms ease, box-shadow 180ms ease;
}

.btn_1:hover,
.btn.btn-primary:hover,
.btn-primary:hover {
	transform: translateY(-1px);
	box-shadow: 0 8px 22px rgba(118,31,227,0.36);
	color: #FFFFFF !important;
}

.btn.btn-secondary,
.btn-secondary {
	background: transparent !important;
	border: 1px solid var(--op-bordure) !important;
	color: var(--op-encre) !important;
	border-radius: 9px;
	padding: 12px 24px;
	font-weight: 600;
	box-shadow: none;
}

.btn.btn-secondary:hover,
.btn-secondary:hover {
	border-color: var(--op-violet) !important;
	background-color: rgba(118,31,227,0.06) !important;
	color: var(--op-violet) !important;
}

/* Succès et avertissement conservent leur sens, mais s'accordent au reste. */
.btn.btn-success, .btn-success { border-radius: 9px; font-weight: 600; border: none; }
.btn.btn-warning, .btn-warning { border-radius: 9px; font-weight: 600; border: none; }
.btn-sm { padding: 7px 14px; font-size: 13px; }


/* --------------------------------------------------------------------------
   5. Cartes, fenêtres modales, messages
   -------------------------------------------------------------------------- */

.modal-content {
	border: 1px solid var(--op-bordure);
	border-radius: 16px;
	box-shadow: 0 24px 60px var(--op-ombre);
}

.modal-header {
	border-bottom: 1px solid var(--op-bordure);
	border-radius: 16px 16px 0 0;
}

.modal-header h4,
.modal-header h5 { color: var(--op-encre); font-weight: 700; }

.alert {
	border-radius: 10px;
	border: 1px solid var(--op-bordure);
	font-size: 14px;
}

.alert-warning {
	background-color: #FFF8E6;
	border-color: #F5D98B;
	color: #7A5A00;
}

.alert-success {
	background-color: #E9F9F0;
	border-color: #9AE0BC;
	color: #14663D;
}

.alert-danger {
	background-color: #FDECEC;
	border-color: #F3B6B6;
	color: #8C1F1F;
}


/* --------------------------------------------------------------------------
   6. Choix multiples et options
   -------------------------------------------------------------------------- */

.custom-option-container,
.radio-container,
.form-check {
	color: var(--op-gris);
}

.form-check-input:checked {
	background-color: var(--op-violet);
	border-color: var(--op-violet);
}

.form-check-input:focus {
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
}


/* --------------------------------------------------------------------------
   7. Liens et pied de page
   -------------------------------------------------------------------------- */

a { color: var(--op-violet); }
a:hover { color: var(--op-violet-fonce); }

.animated_link { color: var(--op-violet); }

.footer,
.footer * { color: rgba(255,255,255,0.62) !important; font-size: 13px; }
.footer a { color: rgba(255,255,255,0.85) !important; }


/* --------------------------------------------------------------------------
   8. Navigation en superposition du gabarit
   Elle contenait encore « Restaurant Version » et « Spa Version », entrées de
   démonstration du thème d'origine. Masquées.
   -------------------------------------------------------------------------- */

.cd-primary-nav li a[href*="restaurant"],
.cd-primary-nav li a[href*="spa"],
.cd-primary-nav li a[href*="hotel"] {
	display: none !important;
}

.cd-overlay-nav,
.cd-overlay-content { background-color: var(--op-violet); }


/* --------------------------------------------------------------------------
   9. Adaptations mobiles
   Le gabarit imposait 100vh et un défilement interne, ce qui écrasait le
   formulaire sur petit écran.
   -------------------------------------------------------------------------- */

@media only screen and (max-width: 991px) {
	.row-height { height: auto; }
	.content-right { padding: 36px 24px; height: auto; min-height: 0; overflow-y: visible; }
	.content-left { min-height: 220px; }
	.left_title h2, .left_title h3 { font-size: 26px; }
}

@media only screen and (max-width: 575px) {
	.content-right { padding: 28px 18px; }
	.left_title h2, .left_title h3 { font-size: 22px; }
	.btn_1, .btn-primary, .btn-secondary { width: 100%; margin-bottom: 8px; }
}


/* --------------------------------------------------------------------------
   10. Respect de « animations réduites »
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	.btn_1:hover,
	.btn.btn-primary:hover,
	.btn-primary:hover { transform: none; }
}


/* ==========================================================================
   11. Scène animée du volet gauche
   Le gabarit met .wrapper en display:flex sans direction : tout enfant
   supplémentaire se place donc À CÔTÉ du titre. On repasse la colonne en
   vertical pour que la scène s'installe SOUS le texte.
   La scène est une frise : la carte du lien en haut, puis quatre jalons
   reliés par un trait qui se remplit au fil du cycle.
   Uniquement opacity et transform — rien qui force un repaint.
   ========================================================================== */

.content-left .wrapper,
.content-left-wrapper .wrapper {
	flex-direction: column;
	justify-content: center;
	padding: 40px 24px;
}

.left_title { flex: 0 0 auto; }

.op-scene {
	position: relative;
	width: 100%;
	max-width: 320px;
	margin: 30px auto 0;
	text-align: left;
	flex: 0 0 auto;
}

/* --- Carte du lien -------------------------------------------------------- */

.op-scene_card {
	position: relative;
	border-radius: 14px;
	overflow: hidden;
	border: 1px solid rgba(255,255,255,0.18);
	background-color: rgba(255,255,255,0.07);
	box-shadow: 0 18px 40px rgba(0,0,0,0.36);
	animation: op-flotte 7s ease-in-out infinite alternate;
}

@keyframes op-flotte {
	from { transform: translateY(0); }
	to   { transform: translateY(-6px); }
}

/* Halo violet qui s'intensifie quand le lien est actif */
.op-scene_card::after {
	position: absolute;
	content: '';
	left: -1px; top: -1px; right: -1px; bottom: -1px;
	border-radius: 14px;
	pointer-events: none;
	box-shadow: 0 0 0 0 rgba(196,165,255,0);
	animation: op-halo 14s ease-in-out infinite;
}

@keyframes op-halo {
	0%, 24%   { box-shadow: 0 0 0 0 rgba(196,165,255,0); }
	34%, 62%  { box-shadow: 0 0 22px 2px rgba(196,165,255,0.30); }
	78%, 100% { box-shadow: 0 0 0 0 rgba(196,165,255,0); }
}

.op-scene_head {
	display: flex;
	align-items: center;
	gap: 5px;
	padding: 9px 12px;
	border-bottom: 1px solid rgba(255,255,255,0.12);
	background-color: rgba(255,255,255,0.05);
}

.op-scene_head i { width: 7px; height: 7px; border-radius: 50%; background-color: rgba(255,255,255,0.26); }
.op-scene_head em { margin-left: 8px; font-style: normal; font-size: 10px; color: rgba(255,255,255,0.55); }

.op-scene_body { padding: 14px; }

.op-scene_label {
	display: block;
	font-size: 9px;
	font-weight: 600;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	color: rgba(255,255,255,0.50);
	margin-bottom: 6px;
}

.op-scene_field {
	position: relative;
	height: 33px;
	border-radius: 8px;
	border: 1px solid rgba(255,255,255,0.16);
	background-color: rgba(255,255,255,0.05);
	display: flex;
	align-items: center;
	padding: 0 11px;
	overflow: hidden;
}

.op-scene_dots {
	display: block;
	white-space: nowrap;
	font-size: 14px;
	letter-spacing: 3px;
	color: rgba(255,255,255,0.85);
	max-width: 0;
	animation: op-saisie 14s steps(12, end) infinite;
}

@keyframes op-saisie {
	0%        { max-width: 0; }
	20%, 100% { max-width: 140px; }
}

/* Curseur de saisie, visible seulement pendant la frappe */
.op-scene_field::after {
	position: absolute;
	content: '';
	left: 12px;
	width: 1.5px;
	height: 15px;
	background-color: #C4A5FF;
	transform: translateX(0);
	animation: op-curseur 14s linear infinite;
}

@keyframes op-curseur {
	0%       { opacity: 1; transform: translateX(0); }
	20%      { opacity: 1; transform: translateX(96px); }
	22%, 100%{ opacity: 0; transform: translateX(96px); }
}

.op-scene_link {
	display: flex;
	align-items: center;
	gap: 8px;
	margin-top: 10px;
	padding: 9px 11px;
	border-radius: 8px;
	background: linear-gradient(112deg, rgba(196,165,255,0.24) 0%, rgba(118,31,227,0.20) 100%);
	border: 1px solid rgba(196,165,255,0.34);
	opacity: 0;
	animation: op-lien 14s cubic-bezier(0.22,0.61,0.36,1) infinite;
}

/* Il apparaît, vit, puis se déchire à la destruction */
@keyframes op-lien {
	0%, 24%   { opacity: 0; transform: translateY(8px) scale(0.97); }
	32%, 74%  { opacity: 1; transform: none; }
	84%       { opacity: 0; transform: translateX(14px) scale(0.96); filter: blur(3px); }
	100%      { opacity: 0; transform: translateY(8px) scale(0.97); }
}

.op-scene_link svg { flex: 0 0 13px; width: 13px; height: 13px; color: #E9D8FF; }

.op-scene_url {
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 10.5px;
	color: #FFFFFF;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* --- Frise verticale ------------------------------------------------------ */

.op-scene_steps {
	position: relative;
	list-style: none;
	margin: 22px 0 0;
	padding: 0 0 0 26px;
}

/* Le rail */
.op-scene_steps::before {
	position: absolute;
	content: '';
	left: 5px;
	top: 6px;
	bottom: 12px;
	width: 2px;
	border-radius: 2px;
	background-color: rgba(255,255,255,0.14);
}

/* Le remplissage, qui progresse avec le cycle */
.op-scene_steps::after {
	position: absolute;
	content: '';
	left: 5px;
	top: 6px;
	width: 2px;
	border-radius: 2px;
	background: linear-gradient(180deg, #C4A5FF 0%, #761FE3 100%);
	height: 0;
	animation: op-rail 14s cubic-bezier(0.65,0,0.35,1) infinite;
}

@keyframes op-rail {
	0%, 18%   { height: 0; }
	34%       { height: 26%; }
	50%       { height: 52%; }
	66%       { height: 78%; }
	80%, 88%  { height: calc(100% - 18px); }
	96%, 100% { height: 0; }
}

.op-scene_steps li {
	position: relative;
	margin-bottom: 13px;
	font-size: 13px;
	line-height: 1.3;
	color: rgba(255,255,255,0.40);
	animation: op-etape 14s ease-in-out infinite;
}

.op-scene_steps li:last-child { margin-bottom: 0; }

.op-scene_steps li::before {
	position: absolute;
	content: '';
	left: -26px;
	top: 3px;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	border: 2px solid rgba(255,255,255,0.22);
	background-color: #241A33;
	animation: op-jalon 14s cubic-bezier(0.34,1.56,0.64,1) infinite;
}

.op-scene_steps li:nth-child(1),
.op-scene_steps li:nth-child(1)::before { animation-delay: 0s; }
.op-scene_steps li:nth-child(2),
.op-scene_steps li:nth-child(2)::before { animation-delay: 2.2s; }
.op-scene_steps li:nth-child(3),
.op-scene_steps li:nth-child(3)::before { animation-delay: 4.4s; }
.op-scene_steps li:nth-child(4),
.op-scene_steps li:nth-child(4)::before { animation-delay: 6.6s; }

@keyframes op-etape {
	0%, 14%   { color: rgba(255,255,255,0.40); }
	20%, 58%  { color: #FFFFFF; }
	68%, 100% { color: rgba(255,255,255,0.40); }
}

@keyframes op-jalon {
	0%, 14%   { border-color: rgba(255,255,255,0.22); background-color: #241A33; transform: scale(1); }
	20%       { border-color: #C4A5FF; background-color: #C4A5FF; transform: scale(1.45); }
	26%, 58%  { border-color: #C4A5FF; background-color: #C4A5FF; transform: scale(1); }
	68%, 100% { border-color: rgba(255,255,255,0.22); background-color: #241A33; transform: scale(1); }
}

/* La destruction n'est pas une étape comme les autres : rouge sourd. */
.op-scene_steps li:nth-child(4)::before { animation-name: op-jalon-fin; }

@keyframes op-jalon-fin {
	0%, 14%   { border-color: rgba(255,255,255,0.22); background-color: #241A33; transform: scale(1); }
	20%       { border-color: #FCA5A5; background-color: #FCA5A5; transform: scale(1.45); }
	26%, 58%  { border-color: #FCA5A5; background-color: #FCA5A5; transform: scale(1); }
	68%, 100% { border-color: rgba(255,255,255,0.22); background-color: #241A33; transform: scale(1); }
}

/* --- Adaptations ---------------------------------------------------------- */

@media only screen and (max-width: 991px) {
	.content-left .wrapper { padding: 28px 20px; }
	.op-scene { display: none; }
}

@media (prefers-reduced-motion: reduce) {
	.op-scene_card,
	.op-scene_card::after,
	.op-scene_dots,
	.op-scene_field::after,
	.op-scene_link,
	.op-scene_steps::after,
	.op-scene_steps li,
	.op-scene_steps li::before,
	.content-left .wrapper::before,
	.content-left-wrapper::before { animation: none !important; }

	/* États finaux forcés : une animation coupée ne doit rien effacer. */
	.op-scene_link { opacity: 1; transform: none; filter: none; }
	.op-scene_dots { max-width: 140px; }
	.op-scene_field::after { opacity: 0; }
	.op-scene_steps::after { height: calc(100% - 18px); }
	.op-scene_steps li { color: #FFFFFF; }
	.op-scene_steps li::before { border-color: #C4A5FF; background-color: #C4A5FF; }
	.op-scene_steps li:nth-child(4)::before { border-color: #FCA5A5; background-color: #FCA5A5; }
}


/* ==========================================================================
   12. Formulaire — finitions
   Le gabarit laissait des radios et cases natives, des libellés serrés et
   aucune hiérarchie entre les blocs.
   ========================================================================== */

.content-right .form-group { margin-bottom: 20px; }

/* Radios et cases à cocher, aux couleurs de la marque */
.form-check { padding-left: 30px; margin-bottom: 9px; }

.form-check-input {
	width: 18px;
	height: 18px;
	margin-left: -30px;
	margin-top: 2px;
	border: 1.5px solid var(--op-bordure);
	background-color: #FFFFFF;
	cursor: pointer;
	transition: border-color 150ms ease, background-color 150ms ease, box-shadow 150ms ease;
}

.form-check-input:hover { border-color: var(--op-violet); }

.form-check-input:checked {
	background-color: var(--op-violet);
	border-color: var(--op-violet);
	box-shadow: 0 2px 8px rgba(118,31,227,0.28);
}

.form-check-label { color: var(--op-encre); font-size: 14.5px; cursor: pointer; }

/* Les options en cartes cliquables plutôt qu'en liste nue */
.custom-option-container,
.radio-container {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.custom-option-container .form-check,
.radio-container .form-check {
	flex: 1 1 auto;
	margin-bottom: 0;
	padding: 10px 14px 10px 36px;
	border-radius: 10px;
	border: 1px solid var(--op-bordure);
	background-color: #FFFFFF;
	transition: border-color 150ms ease, background-color 150ms ease;
}

.custom-option-container .form-check:hover,
.radio-container .form-check:hover {
	border-color: rgba(118,31,227,0.45);
	background-color: rgba(118,31,227,0.04);
}

.custom-option-container .form-check-input,
.radio-container .form-check-input { margin-left: -22px; }

/* Champs numériques du générateur : plus lisibles, alignés */
input[type="number"] {
	max-width: 96px;
	text-align: center;
	font-variant-numeric: tabular-nums;
	font-weight: 600;
}

/* Indicateur d'étape « 1/2 » */
.main_question strong {
	display: inline-block;
	padding: 4px 12px;
	border-radius: 100px;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.04em;
	color: var(--op-violet);
	background-color: rgba(118,31,227,0.09);
	border: 1px solid rgba(118,31,227,0.20);
	margin-right: 10px;
}

.main_question {
	font-size: 17px;
	font-weight: 700;
	color: var(--op-encre);
	letter-spacing: -0.02em;
	margin-bottom: 14px;
	display: flex;
	align-items: center;
}


/* ==========================================================================
   13. Fenêtres modales
   ========================================================================== */

.modal-backdrop.show { opacity: 0.55; }

.modal-dialog { max-width: 480px; }

.modal-content {
	border: 1px solid var(--op-bordure);
	border-radius: 18px;
	box-shadow: 0 30px 70px rgba(26,21,35,0.22);
	overflow: hidden;
}

.modal-header {
	padding: 20px 24px 16px;
	border-bottom: 1px solid var(--op-bordure);
	background-color: var(--op-surface);
}

.modal-header h4,
.modal-header h5,
.modal-title {
	color: var(--op-encre);
	font-size: 18px;
	font-weight: 700;
	letter-spacing: -0.02em;
	margin: 0;
}

.modal-body { padding: 22px 24px; color: var(--op-gris); font-size: 15px; line-height: 1.6; }

.modal-footer {
	padding: 16px 24px 20px;
	border-top: 1px solid var(--op-bordure);
	gap: 8px;
}

.btn-close, .close {
	opacity: 0.5;
	transition: opacity 150ms ease;
	background-color: transparent;
	border: none;
	font-size: 22px;
	line-height: 1;
	color: var(--op-gris);
}

.btn-close:hover, .close:hover { opacity: 1; }

/* --- Le lien généré : le moment clé du produit --------------------------- */

#linkModal .modal-content {
	border: none;
	box-shadow: 0 34px 80px rgba(76,29,149,0.34);
}

#linkModal .modal-header {
	background: var(--op-degrade);
	border-bottom: none;
	padding: 26px 24px 22px;
	text-align: center;
	display: block;
	position: relative;
	overflow: hidden;
}

/* Reflet qui traverse l'en-tête au moment de l'ouverture */
#linkModal .modal-header::after {
	position: absolute;
	content: '';
	left: 0; top: 0; right: 0; bottom: 0;
	pointer-events: none;
	transform: translateX(-100%);
	background: linear-gradient(104deg, transparent 40%, rgba(255,255,255,0.22) 50%, transparent 60%);
	animation: op-reflet 2.6s ease-out 0.2s;
}

@keyframes op-reflet {
	to { transform: translateX(100%); }
}

#linkModal .modal-header h4,
#linkModal .modal-header h5,
#linkModal .modal-title {
	color: #FFFFFF;
	font-size: 20px;
	position: relative;
}

#linkModal .btn-close, #linkModal .close {
	color: #FFFFFF;
	opacity: 0.75;
	position: absolute;
	right: 18px;
	top: 18px;
}

#linkModal .modal-body { padding: 24px; text-align: center; }

/* Le lien s'affiche dans un <p id="generatedLink"> — vérifié dans le source,
   ce n'est pas un champ de saisie. */
#linkModal #generatedLink {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 13.5px;
	text-align: center;
	background-color: var(--op-surface) !important;
	border: 1px dashed rgba(118,31,227,0.34) !important;
	color: var(--op-encre) !important;
	padding: 14px 12px;
	word-break: break-all;
	/* Un <p> n'hérite pas du rayon appliqué aux champs de saisie. */
	border-radius: 10px;
	margin: 0;
	line-height: 1.5;
	min-height: 48px;
}

#linkModal .modal-footer { justify-content: center; }
#linkModal #copyLinkBtn { padding: 12px 26px; }

/* Rappel d'usage unique sous le lien */
#linkModal .modal-body small,
#linkModal .modal-body .text-muted {
	display: block;
	margin-top: 12px;
	font-size: 12.5px;
	color: var(--op-gris);
}

/* --- Générateur de mot de passe ------------------------------------------ */

#passwordConfigModal .form-group {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 14px;
	padding: 11px 0;
	border-bottom: 1px solid var(--op-bordure);
	margin-bottom: 0;
}

#passwordConfigModal .form-group:last-of-type { border-bottom: none; }

#passwordConfigModal label {
	margin-bottom: 0;
	font-size: 14.5px;
	color: var(--op-encre);
	flex: 1;
}

#passwordConfigModal .form-check { padding-top: 14px; }

/* ── Le choix du type de mot de passe ────────────────────────────────────
 *
 * Le titre AU-DESSUS, pas à côté. Les autres lignes du modal sont des rangées
 * — « Longueur : [12] » — mais deux boutons pleine largeur ne laissent pas de
 * place à un libellé de quatre mots : il se cassait à un mot par ligne.
 *
 * ⚠️ Bascule dessinée ici, et non reprise de Bootstrap. `btn-outline-secondary`
 * donnait un gris ardoise qui n'appartient pas à la palette, et son état actif
 * était un aplat sombre — sur Windows, où les polices sont plus étroites et le
 * rendu plus contrasté, l'ensemble ressemblait à un bouton désactivé. */
.op-gen-mode {
	padding: 12px 0 14px;
	border-bottom: 1px solid var(--op-bordure);
}

.op-gen-mode > label {
	display: block;
	margin: 0 0 8px;
	font-size: 14.5px;
	color: var(--op-encre);
}

.op-gen-bascule {
	display: flex;
	gap: 4px;
	padding: 4px;
	border-radius: 10px;
	background: var(--op-fond-doux, #F2EEFA);
}

.op-gen-choix {
	flex: 1 1 0;
	padding: 8px 10px;
	border: 0;
	border-radius: 7px;
	background: transparent;
	font: inherit;
	font-size: 14px;
	font-weight: 600;
	color: var(--op-gris);
	cursor: pointer;
	/* `min-width: 0` : sans lui, un libellé long — « Mémorisable » en espagnol
	   fait « Memorizable » — force la cellule à déborder au lieu de se serrer. */
	min-width: 0;
}

.op-gen-choix:hover { color: var(--op-encre); }

.op-gen-choix.est-actif {
	background: #FFFFFF;
	color: var(--op-violet, #6D1BD6);
	box-shadow: 0 1px 3px rgba(26, 21, 35, 0.12);
}

.op-gen-choix:focus-visible {
	outline: 2px solid var(--op-violet, #6D1BD6);
	outline-offset: 2px;
}

/* Le compteur de substitution : libellé, champ, puis l'explication SOUS les
   deux. En rangée, la phrase se retrouvait à côté du champ et le comprimait. */
#passwordConfigModal #opBlocSubstNb { display: block; }
#passwordConfigModal #opBlocSubstNb label { display: block; margin-bottom: 6px; }
#passwordConfigModal #opBlocSubstNb small { display: block; margin-top: 6px; }

/* ── Le drapeau du champ téléphone ───────────────────────────────────────
 *
 * ⚠️ EN SVG, PAS EN EMOJI. Il était écrit « 🇫🇷 » dans le placeholder : deux
 * indicateurs régionaux Unicode, que le système est censé composer. Windows
 * n'a AUCUN glyphe de drapeau national — Microsoft les a retirés de sa police
 * emoji — et le champ y affichait « FR Numéro… ».
 *
 * Trois rectangles encodés ici même : rien à charger, rien à maintenir, et le
 * même rendu sur tous les systèmes. Un seul drapeau suffit : le champ n'accepte
 * que des numéros français. */
/* ⚠️ `input.` ET PAS SEULEMENT `.` : la règle de base vise
   `input[type="text"]`, dont la spécificité (0,0,1,1) l'emporte sur une classe
   seule (0,0,1,0). Mon `padding-left` était donc ignoré, et le drapeau se
   posait SOUS le texte du placeholder. Le fond, lui, s'appliquait — d'où un
   défaut qui ressemblait à un décalage et n'en était pas un. */
input.op-champ-tel {
	padding-left: 40px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 21 15'%3E%3Crect width='7' height='15' fill='%23002395'/%3E%3Crect x='7' width='7' height='15' fill='%23fff'/%3E%3Crect x='14' width='7' height='15' fill='%23ED2939'/%3E%3Crect width='21' height='15' fill='none' stroke='rgba(0,0,0,.14)' stroke-width='1'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: 12px center;
	background-size: 20px 14px;
}

/* --- Durée de validité ---------------------------------------------------- */

#durationModal .modal-body p {
	background-color: var(--op-surface);
	border-left: 3px solid var(--op-violet);
	border-radius: 0 8px 8px 0;
	padding: 12px 14px;
	font-size: 14px;
	margin-bottom: 18px;
}

#durationModal select { font-weight: 500; }


/* ==========================================================================
   14. Scène : reflet et impulsion d'envoi
   ========================================================================== */

/* Un reflet traverse la carte, comme sur un écran réel */
.op-scene_card::before {
	position: absolute;
	content: '';
	left: 0; top: 0; right: 0; bottom: 0;
	z-index: 3;
	pointer-events: none;
	transform: translateX(-100%);
	background: linear-gradient(104deg, transparent 42%, rgba(255,255,255,0.13) 50%, transparent 58%);
	animation: op-balayage 14s ease-in-out infinite;
}

@keyframes op-balayage {
	0%, 34%   { transform: translateX(-100%); }
	46%, 100% { transform: translateX(100%); }
}

/* Onde qui part du jalon « envoyé » */
.op-scene_steps li:nth-child(2)::after {
	position: absolute;
	content: '';
	left: -21px;
	top: 8px;
	width: 2px;
	height: 2px;
	border-radius: 50%;
	pointer-events: none;
	box-shadow: 0 0 0 0 rgba(196,165,255,0.55);
	animation: op-onde 14s ease-out infinite 2.2s;
}

@keyframes op-onde {
	0%, 18%  { box-shadow: 0 0 0 0 rgba(196,165,255,0.55); }
	26%      { box-shadow: 0 0 0 14px rgba(196,165,255,0); }
	100%     { box-shadow: 0 0 0 0 rgba(196,165,255,0); }
}

@media (prefers-reduced-motion: reduce) {
	.op-scene_card::before,
	.op-scene_steps li:nth-child(2)::after,
	#linkModal .modal-header::after { animation: none !important; }
	.op-scene_card::before { opacity: 0; }
	#linkModal .modal-header::after { opacity: 0; }
}


/* ==========================================================================
   15. Icônes d'action du champ mot de passe
   Le gabarit pose deux <i> Font Awesome en position absolue sur le champ,
   sans couleur ni zone cliquable : #togglePassword (afficher/masquer) et
   #copyPassword (presse-papiers).
   Elles étaient invisibles pour deux raisons cumulées — la police n'était
   plus chargée, et rien ne les distinguait du fond.
   ========================================================================== */

#togglePassword,
#copyPassword {
	position: absolute !important;
	top: 50% !important;
	transform: translateY(-50%);
	width: 30px;
	height: 30px;
	display: flex !important;
	align-items: center;
	justify-content: center;
	border-radius: 7px;
	font-size: 14px;
	color: var(--op-gris);
	background-color: transparent;
	cursor: pointer;
	z-index: 3;
	transition: color 150ms ease, background-color 150ms ease;
}

#togglePassword { right: 42px !important; }
#copyPassword   { right: 8px !important; }

#togglePassword:hover,
#copyPassword:hover {
	color: var(--op-violet);
	background-color: rgba(118,31,227,0.10);
}

/* Repère visuel : un filet sépare les deux actions */
#togglePassword::after {
	position: absolute;
	content: '';
	right: -6px;
	top: 7px;
	bottom: 7px;
	width: 1px;
	background-color: var(--op-bordure);
}

/* Le champ doit réserver la place des deux icônes */
.form-group #password { padding-right: 84px; }

/* Confirmation de copie : la pastille vire au vert un instant */
#copyPassword.op-copie {
	color: #15803D;
	background-color: rgba(22,163,74,0.14);
}

/* Boutons « Générer » et « Configurer » sous le champ */
#generatePassword, #openConfigBtn { padding: 9px 18px; font-size: 14px; }


/* ==========================================================================
   16. Volet gauche — profondeur et matière
   La scène restait plate sur un dégradé fixe. On ajoute trois couches qui
   la font vivre sans jamais concurrencer le formulaire : un halo qui dérive,
   un grain, et un voile de sécurité qui pulse au rythme du cycle.
   ========================================================================== */

/* Halo violet qui se déplace lentement derrière la scène */
.content-left .wrapper,
.content-left-wrapper { isolation: isolate; }

.left_title { position: relative; z-index: 2; }

.op-scene::before {
	position: absolute;
	content: '';
	left: 50%;
	top: -70px;
	width: 460px;
	height: 460px;
	margin-left: -230px;
	z-index: -1;
	pointer-events: none;
	border-radius: 50%;
	background: radial-gradient(circle closest-side, rgba(147,51,234,0.34) 0%, transparent 100%);
	animation: op-derive 18s ease-in-out infinite alternate;
}

@keyframes op-derive {
	from { transform: translate(-26px, -14px) scale(1); }
	to   { transform: translate(26px, 18px) scale(1.12); }
}

/* Grain : la même trame que sur le site, elle casse l'aplat */
.op-scene::after {
	position: absolute;
	content: '';
	left: -40px; right: -40px; top: -60px; bottom: -40px;
	z-index: -1;
	pointer-events: none;
	opacity: 0.28;
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/></filter><rect width='160' height='160' filter='url(%23n)'/></svg>");
}

/* Le cadenas du titre respire au rythme du cycle */
.left_title h3::after {
	content: '';
	display: block;
	width: 42px;
	height: 3px;
	border-radius: 3px;
	margin: 14px auto 0;
	background: linear-gradient(90deg, #C4A5FF 0%, #761FE3 100%);
	transform-origin: center;
	animation: op-trait 14s ease-in-out infinite;
}

@keyframes op-trait {
	0%, 100% { transform: scaleX(0.55); opacity: 0.55; }
	40%      { transform: scaleX(1);    opacity: 1; }
	70%      { transform: scaleX(0.8);  opacity: 0.8; }
}

/* La carte prend du relief : léger basculement, comme posée dans l'espace */
.op-scene_card {
	transform-style: preserve-3d;
	animation: op-flotte 9s cubic-bezier(0.45,0,0.55,1) infinite alternate;
}

@keyframes op-flotte {
	from { transform: translateY(0) rotate(-0.4deg); }
	to   { transform: translateY(-8px) rotate(0.4deg); }
}

/* Le rail de la frise diffuse une lueur quand il progresse */
.op-scene_steps::after {
	box-shadow: 0 0 10px rgba(196,165,255,0.55);
}

@media (prefers-reduced-motion: reduce) {
	.op-scene::before,
	.left_title h3::after,
	.op-scene_card { animation: none !important; }
	.left_title h3::after { transform: scaleX(1); opacity: 1; }
}


/* ==========================================================================
   17. Corrections et enrichissement de la scène
   - Le halo débordait sur le titre : il est resserré et l'espace augmenté.
   - Plus de couleur : deux foyers au lieu d'un, et des accents plus francs.
   - Deux terminaux illustrent la réception : SMS sur mobile, e-mail sur poste.
   ========================================================================== */

/* --- Chevauchement : la scène respire sous le texte ----------------------- */

.op-scene { margin-top: 46px; }

.op-scene::before {
	top: 10px;              /* ne remonte plus derrière le titre */
	width: 400px;
	height: 400px;
	margin-left: -200px;
	background: radial-gradient(circle closest-side, rgba(168,85,247,0.40) 0%, transparent 100%);
}

/* Second foyer, plus chaud, décalé : la lumière cesse d'être uniforme */
.op-scene_card::after { z-index: 4; }

.op-devices::before {
	position: absolute;
	content: '';
	left: 50%;
	top: -30px;
	width: 320px;
	height: 260px;
	margin-left: -160px;
	z-index: -1;
	pointer-events: none;
	border-radius: 50%;
	background: radial-gradient(circle closest-side, rgba(236,72,153,0.20) 0%, transparent 100%);
	animation: op-derive2 21s ease-in-out infinite alternate;
}

@keyframes op-derive2 {
	from { transform: translate(20px, 10px) scale(1); }
	to   { transform: translate(-22px, -14px) scale(1.14); }
}

.op-scene::after { top: 0; }   /* le grain ne remonte plus non plus */


/* --- Les deux terminaux --------------------------------------------------- */

.op-devices {
	position: relative;
	display: flex;
	align-items: flex-end;
	justify-content: center;
	gap: 26px;
	margin: 22px 0 4px;
}

.op-dev {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 7px;
	opacity: 0.42;
	animation: op-dev-actif 14s ease-in-out infinite 2.2s;
}

/* Les deux s'allument au moment de l'envoi */
@keyframes op-dev-actif {
	0%, 14%   { opacity: 0.42; transform: translateY(0); }
	22%       { opacity: 1;    transform: translateY(-4px); }
	30%, 62%  { opacity: 1;    transform: translateY(0); }
	72%, 100% { opacity: 0.42; transform: translateY(0); }
}

.op-dev_nom {
	font-size: 10px;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: rgba(255,255,255,0.62);
}

/* Mobile */
.op-dev--tel .op-dev_ecran {
	position: relative;
	display: block;
	width: 46px;
	height: 76px;
	border-radius: 9px;
	border: 2px solid rgba(255,255,255,0.42);
	background: linear-gradient(160deg, rgba(196,165,255,0.20) 0%, rgba(118,31,227,0.14) 100%);
	overflow: hidden;
}

/* L'encoche */
.op-dev--tel .op-dev_ecran::before {
	position: absolute;
	content: '';
	left: 50%;
	top: 3px;
	width: 16px;
	height: 3px;
	margin-left: -8px;
	border-radius: 3px;
	background-color: rgba(255,255,255,0.42);
}

/* La bulle de message qui arrive */
.op-dev_bulle {
	position: absolute;
	left: 6px;
	right: 12px;
	top: 18px;
	height: 13px;
	border-radius: 4px 8px 8px 4px;
	background: linear-gradient(90deg, #C4A5FF 0%, #A855F7 100%);
	opacity: 0;
	animation: op-bulle 14s cubic-bezier(0.34,1.56,0.64,1) infinite 2.2s;
}

@keyframes op-bulle {
	0%, 16%   { opacity: 0; transform: translateX(-16px) scale(0.9); }
	24%, 62%  { opacity: 1; transform: none; }
	72%, 100% { opacity: 0; transform: translateX(-16px) scale(0.9); }
}

/* Ordinateur portable */
.op-dev--pc .op-dev_ecran {
	position: relative;
	display: block;
	width: 82px;
	height: 54px;
	border-radius: 6px 6px 2px 2px;
	border: 2px solid rgba(255,255,255,0.42);
	border-bottom: none;
	background: linear-gradient(160deg, rgba(196,165,255,0.18) 0%, rgba(118,31,227,0.12) 100%);
	overflow: hidden;
	padding: 9px 8px;
}

.op-dev_socle {
	display: block;
	width: 98px;
	height: 5px;
	margin-top: -1px;
	border-radius: 0 0 5px 5px;
	background-color: rgba(255,255,255,0.42);
}

/* Les lignes de l'e-mail qui se remplissent */
.op-dev_ligne {
	display: block;
	height: 5px;
	border-radius: 3px;
	margin-bottom: 5px;
	background: linear-gradient(90deg, #C4A5FF 0%, #A855F7 100%);
	transform-origin: left center;
	transform: scaleX(0);
	animation: op-ligne 14s cubic-bezier(0.65,0,0.35,1) infinite 2.2s;
}

.op-dev_ligne:nth-child(1) { width: 100%; }
.op-dev_ligne:nth-child(2) { width: 62%; animation-delay: 2.5s; }

@keyframes op-ligne {
	0%, 16%   { transform: scaleX(0); }
	26%, 62%  { transform: scaleX(1); }
	72%, 100% { transform: scaleX(0); }
}


/* --- Plus de couleur dans la frise ---------------------------------------- */

.op-scene_steps::after {
	background: linear-gradient(180deg, #E9D8FF 0%, #C4A5FF 40%, #A855F7 100%);
	box-shadow: 0 0 12px rgba(196,165,255,0.65);
}

.op-scene_link {
	background: linear-gradient(112deg, rgba(196,165,255,0.32) 0%, rgba(168,85,247,0.26) 100%);
	border-color: rgba(196,165,255,0.46);
}


/* ==========================================================================
   18. Boutons du parcours en étapes
   Le gabarit les nomme .forward / .backward / .submit — hors classes
   Bootstrap, ils échappaient donc à l'habillage.
   ========================================================================== */

button.forward,
button.submit,
#next-button {
	background: var(--op-degrade) !important;
	border: none !important;
	color: #FFFFFF !important;
	border-radius: 9px;
	padding: 12px 28px;
	font-size: 15px;
	font-weight: 600;
	letter-spacing: -0.01em;
	cursor: pointer;
	box-shadow: 0 4px 14px rgba(118,31,227,0.28);
	transition: transform 160ms ease, box-shadow 180ms ease;
}

button.forward:hover,
button.submit:hover,
#next-button:hover {
	transform: translateY(-1px);
	box-shadow: 0 8px 22px rgba(118,31,227,0.38);
	color: #FFFFFF !important;
}

/* Le gabarit pose l'icône de validation en absolu (top:8px; right:10px) sans
   réserver la place correspondante : elle recouvrait le mot « Envoyer ».
   On la remet dans le flux, le bouton devenant une boîte flex. */
button.submit {
	position: relative;
	display: inline-flex !important;
	align-items: center;
	justify-content: center;
	gap: 9px;
}

button.submit::before {
	position: static !important;
	top: auto !important;
	right: auto !important;
	font-size: 16px !important;
	line-height: 1 !important;
	flex: 0 0 auto;
}

button.backward {
	background: transparent !important;
	border: 1px solid var(--op-bordure) !important;
	color: var(--op-gris) !important;
	border-radius: 9px;
	padding: 12px 22px;
	font-size: 15px;
	font-weight: 600;
	cursor: pointer;
	transition: border-color 160ms ease, color 160ms ease;
}

button.backward:hover {
	border-color: var(--op-violet) !important;
	color: var(--op-violet) !important;
}


@media (prefers-reduced-motion: reduce) {
	.op-dev,
	.op-dev_bulle,
	.op-dev_ligne,
	.op-devices::before { animation: none !important; }
	.op-dev { opacity: 1; }
	.op-dev_bulle { opacity: 1; transform: none; }
	.op-dev_ligne { transform: scaleX(1); }
	button.forward:hover, button.submit:hover, #next-button:hover { transform: none; }
}


/* ==========================================================================
   19. Corrections d'après capture d'écran
   Cinq défauts constatés en conditions réelles.
   ========================================================================== */

/* --- 1. Le cadre noir derrière le titre -----------------------------------
   Le gabarit posait .content-left-wrapper .wrapper .left_title h3 avec
   background-color:#000 et un display:inline-flex. Ma règle avait une
   spécificité inférieure et ne passait donc pas. */

.content-left-wrapper .wrapper .left_title h3,
.content-left .wrapper .left_title h3 {
	background-color: transparent !important;
	padding: 0 !important;
	display: block !important;
	font-size: 34px;
	line-height: 1.15;
	color: #FFFFFF !important;
}

/* --- 2. Les pastilles violettes vides -------------------------------------
   Les <strong> de .main_question sont vides sur la plupart des étapes :
   ma pastille les affichait comme des rectangles violets sans contenu. */

.main_question strong:empty { display: none !important; }

.main_question strong {
	display: inline-block;
	color: var(--op-violet);
}

/* --- 3. Les icônes du champ mot de passe ----------------------------------
   Le .form-group contient le champ ET la rangée de boutons : un top:50%
   les centrait sur l'ensemble, donc sous le champ. On les cale sur la
   hauteur du champ seul. */

#togglePassword,
#copyPassword {
	top: 23px !important;
	transform: translateY(-50%);
}

/* --- 4. Le grain formait un rectangle visible -----------------------------
   Ses bords nets dessinaient un panneau plus clair. On l'estompe. */

.op-scene::after {
	-webkit-mask-image: radial-gradient(ellipse at center, #000 45%, transparent 78%);
	mask-image: radial-gradient(ellipse at center, #000 45%, transparent 78%);
	opacity: 0.20;
}

/* Le halo, lui, doit rester doux sur les bords */
.op-scene::before,
.op-devices::before { filter: blur(4px); }

/* --- 5. Le bouton jaune, hors charte -------------------------------------- */

.btn-warning,
.btn.btn-warning {
	background: rgba(118,31,227,0.10) !important;
	color: var(--op-violet) !important;
	border: 1px solid rgba(118,31,227,0.26) !important;
	box-shadow: none;
}

.btn-warning:hover,
.btn.btn-warning:hover {
	background: rgba(118,31,227,0.18) !important;
	color: var(--op-violet-fonce) !important;
}

/* Le bouton engrenage : même famille, en secondaire plutôt qu'en plein */
#openConfigBtn {
	background: rgba(118,31,227,0.10) !important;
	color: var(--op-violet) !important;
	border: 1px solid rgba(118,31,227,0.26) !important;
	box-shadow: none;
}

#openConfigBtn:hover {
	background: rgba(118,31,227,0.18) !important;
	transform: none;
}

/* --- Finitions du volet gauche -------------------------------------------- */

.left_title p {
	max-width: 420px;
	margin-left: auto;
	margin-right: auto;
}

/* Le logo du gabarit était celui d'un autre produit : on remet OURTECH,
   et on lui donne de l'air. */
#logo img { height: 30px; width: auto; }


/* ==========================================================================
   20. Rendu plus vivant
   La capture montrait une scène délavée et un formulaire un peu froid pour
   un outil qu'on doit avoir envie d'ouvrir. On monte le contraste, on rend
   les couleurs plus franches, et on ajoute des micro-réactions au survol.
   Registre toujours pro : c'est un outil de sécurité, pas un jeu.
   ========================================================================== */

/* --- Le fond gagne en profondeur et en couleur ---------------------------- */

.content-left .wrapper::before,
.content-left-wrapper::before {
	background:
		linear-gradient(158deg, #16101F 0%, #3B0F70 46%, #6D28D9 100%),
		radial-gradient(circle at 24% 16%, rgba(217,70,239,0.34) 0%, transparent 48%),
		radial-gradient(circle at 78% 82%, rgba(56,189,248,0.16) 0%, transparent 46%);
	background-blend-mode: normal, screen, screen;
	background-size: 100% 100%, 190% 190%, 170% 170%;
	animation: op-fond 26s ease-in-out infinite alternate;
}

@keyframes op-fond {
	from { background-position: 0% 0%, 14% 12%, 86% 88%; }
	to   { background-position: 0% 0%, 66% 58%, 30% 34%; }
}

/* --- La carte sort de la brume -------------------------------------------- */

.op-scene_card {
	background-color: rgba(255,255,255,0.10);
	border-color: rgba(255,255,255,0.26);
	-webkit-backdrop-filter: blur(6px);
	backdrop-filter: blur(6px);
	box-shadow: 0 22px 50px rgba(0,0,0,0.44), inset 0 1px 0 rgba(255,255,255,0.14);
}

.op-scene_head { background-color: rgba(255,255,255,0.08); }
.op-scene_head em { color: rgba(255,255,255,0.72); }
.op-scene_label { color: rgba(255,255,255,0.68); }
.op-scene_field { background-color: rgba(0,0,0,0.22); border-color: rgba(255,255,255,0.22); }
.op-scene_dots { color: #FFFFFF; }

.op-scene_link {
	background: linear-gradient(112deg, rgba(232,121,249,0.34) 0%, rgba(139,92,246,0.34) 100%);
	border-color: rgba(240,171,252,0.52);
	box-shadow: 0 4px 16px rgba(217,70,239,0.22);
}

/* --- Terminaux plus lisibles ---------------------------------------------- */

.op-dev { opacity: 0.58; }

.op-dev--tel .op-dev_ecran,
.op-dev--pc .op-dev_ecran {
	border-color: rgba(255,255,255,0.60);
	background: linear-gradient(160deg, rgba(255,255,255,0.16) 0%, rgba(139,92,246,0.18) 100%);
}

.op-dev_socle { background-color: rgba(255,255,255,0.60); }
.op-dev_nom { color: rgba(255,255,255,0.78); }

.op-dev_bulle { background: linear-gradient(90deg, #F0ABFC 0%, #A855F7 100%); }
.op-dev_ligne { background: linear-gradient(90deg, #F0ABFC 0%, #A855F7 100%); }

/* --- Frise plus contrastée ------------------------------------------------ */

.op-scene_steps li { color: rgba(255,255,255,0.58); }
.op-scene_steps::before { background-color: rgba(255,255,255,0.20); }

.op-scene_steps::after {
	background: linear-gradient(180deg, #F0ABFC 0%, #C084FC 45%, #8B5CF6 100%);
	box-shadow: 0 0 14px rgba(240,171,252,0.75);
}

@keyframes op-jalon {
	0%, 14%   { border-color: rgba(255,255,255,0.28); background-color: #241A33; transform: scale(1); }
	20%       { border-color: #F0ABFC; background-color: #F0ABFC; transform: scale(1.5); }
	26%, 58%  { border-color: #F0ABFC; background-color: #F0ABFC; transform: scale(1); }
	68%, 100% { border-color: rgba(255,255,255,0.28); background-color: #241A33; transform: scale(1); }
}

/* --- Micro-réactions du formulaire ---------------------------------------- */

.form-control:hover:not(:focus),
input:hover:not(:focus),
select:hover:not(:focus),
textarea:hover:not(:focus) {
	border-color: rgba(118,31,227,0.40);
}

.form-control:focus,
input:focus,
select:focus,
textarea:focus {
	box-shadow: 0 0 0 4px rgba(118,31,227,0.14);
}

/* Les boutons rebondissent légèrement plutôt que de glisser */
.btn_1, .btn.btn-primary, .btn-primary,
button.forward, button.submit, #next-button {
	transition: transform 220ms cubic-bezier(0.34,1.56,0.64,1), box-shadow 200ms ease;
}

.btn_1:active, .btn.btn-primary:active, .btn-primary:active,
button.forward:active, #next-button:active { transform: translateY(1px) scale(0.985); }

/* Interrupteurs Oui/Non, aux couleurs de la marque */
.switch-light.switch-ios .switch-handle,
.switch-light span span { transition: all 200ms ease; }

.switch-light.switch-ios > span span:first-child,
.switch-light input:checked ~ span a { background: var(--op-degrade) !important; }

/* Les icônes d'action pulsent doucement pour se signaler */
#togglePassword, #copyPassword { animation: op-signal 5s ease-in-out infinite; }

@keyframes op-signal {
	0%, 88%, 100% { opacity: 0.72; }
	94%           { opacity: 1; }
}

#togglePassword:hover, #copyPassword:hover { opacity: 1; animation: none; }

/* --- Le lien généré : un moment qu'on a envie de voir --------------------- */

#linkModal .modal-header {
	background: linear-gradient(112deg, #8B5CF6 0%, #6D28D9 55%, #A21CAF 100%);
}

#linkModal #generatedLink {
	border-style: solid !important;
	border-color: rgba(118,31,227,0.28) !important;
	background: linear-gradient(160deg, #FAF8FF 0%, #F3EDFF 100%) !important;
	box-shadow: inset 0 1px 3px rgba(118,31,227,0.08);
}

#linkModal #copyLinkBtn { min-width: 170px; }

@media (prefers-reduced-motion: reduce) {
	.content-left .wrapper::before,
	.content-left-wrapper::before,
	#togglePassword, #copyPassword { animation: none !important; }
	#togglePassword, #copyPassword { opacity: 1; }
}


/* ==========================================================================
   21. Choix du canal d'envoi
   Le gabarit proposait un libellé « Téléphone », un chevron et un bouton
   enveloppe : rien n'indiquait qu'il fallait cliquer, ni vers quoi on
   basculait. Remplacé par deux options explicites, dont une active.
   ========================================================================== */

/* ══ Le canal : le JUMEAU du choix de sens ══════════════════════════════

   ⚠️ CETTE REGLE ETAIT ECRITE DEUX FOIS, a 7 000 lignes d'ecart : la premiere
   posait `white-space: nowrap`, la seconde ajoutait `flex: 1` et
   `width: 100%`. La derniere gagne PROPRIETE PAR PROPRIETE, jamais en bloc —
   les boutons heritaient donc des deux, et `width: 100%` etirait le cadre sur
   toute la largeur du parent, bien au-dela des champs du formulaire.

   La correction n'est pas d'ajuster cette largeur : c'est de reprendre celle
   du segment JUSTE AU-DESSUS. « J'envoie / Je demande » et « Par SMS / Par
   e-mail / Lien a copier » sont deux fois la meme figure, a trois centimetres
   d'ecart. Les valeurs ci-dessous sont copiees de `.op-sens` — gouttiere,
   retrait, rayon, bordure, fond, graisse — pour qu'on les lise comme une
   paire.

   `inline-flex`, donc la boite EPOUSE son contenu.

   ⚠️ ET AUCUNE ICONE. C'est ce qui a fait deborder le cadre : trois
   pictogrammes de 16 px plus leurs gouttieres coutent ~69 px, et le segment
   passait de 330 a 399 px la ou le sens en fait 361. Mesure faite dans les
   trois langues, en prenant `.op-sens` pour reference — puisque LUI tient.
   Sans icones, le canal est plus etroit que le sens partout : il ne peut
   donc pas deborder, et ce n'est pas une valeur bien choisie, c'est une
   comparaison a un bloc dont on sait deja qu'il rentre. */
.op-canal {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 3px;
	width: 100%;
	margin: 0 0 18px;
	padding: 3px;
	border: 1px solid var(--op-bordure);
	border-radius: 11px;
	background: #FBFAFD;
}

.op-canal_btn {
	appearance: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	min-width: 0;                 /* sans lui, rien ne retrecit */
	padding: 9px 8px;
	border: 0;
	border-radius: 8px;
	background: transparent;
	font: inherit;
	font-size: 14px;
	font-weight: 600;
	color: var(--op-gris);
	cursor: pointer;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	transition: background-color .15s, color .15s;
}

.op-canal_btn:hover { color: var(--op-violet); }

.op-canal_btn.is-actif {
	background: var(--op-violet);
	color: #FFFFFF;
	box-shadow: 0 1px 4px rgba(118,31,227,0.30);
}

.op-canal_btn.is-actif:hover { color: #FFFFFF; }

.op-canal_btn:focus-visible {
	outline: 2px solid var(--op-violet);
	outline-offset: 2px;
}

/* Les icones peuvent revenir : la boite ne PEUT plus deborder — colonnes en
   `minmax(0, 1fr)` et `min-width: 0` sur les boutons. Le seul risque restant
   serait un libelle coupe, et c'est mesure plus bas. */
.op-canal_btn svg { width: 15px; height: 15px; flex: 0 0 15px; }

/* Sous 560 px, la colonne ne paie plus l'icone ET le mot. C'est le mot qui
   informe : l'icone s'efface. */
@media (max-width: 560px) {
	.op-canal_btn svg { display: none; }
}

/* Sur un telephone, la boite suit toujours les champs — c'est la POLICE qui
   cede, pas la largeur. « Je demande un contenu » sur deux colonnes de 166 px
   demande 13 px pour tenir sans se couper. */
@media (max-width: 480px) {
	.op-canal_btn,
	.op-sens_opt { padding: 9px 6px; font-size: 13px; }
}

/* Sous 360 px — iPhone SE et consorts — « Je demande un contenu » demandait
   encore 2 px de trop. Mesure faite libelle par libelle, dans les trois
   langues, plutot que constatee un jour sur un vieux telephone. */
@media (max-width: 360px) {
	.op-canal_btn,
	.op-sens_opt { padding: 9px 2px; font-size: 12px; }
}

.op-canal_legacy {
	position: absolute !important;
	width: 1px; height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
}

@media only screen and (max-width: 575px) {
		}


/* ==========================================================================
   22. Marque OURPASS
   L'application est un produit distinct de la vitrine OURTECH : elle porte
   son propre nom. Le logo du gabarit appartenait à un autre produit, et
   poser celui d'OURTECH aurait entretenu la confusion.
   Construit en texte plutôt qu'en image : Inter est déjà chargée, et les
   couleurs suivent le contexte clair ou sombre.
   ========================================================================== */

#logo {
	display: inline-block;
	text-decoration: none;
	line-height: 1;
}

.op-logo {
	font-family: 'Inter', system-ui, sans-serif;
	font-size: 21px;
	font-weight: 700;
	letter-spacing: 0.14em;
	color: #FFFFFF;
	text-transform: uppercase;
	white-space: nowrap;
	transition: opacity 180ms ease;
}

/* « OUR » reprend le violet de la marque, comme sur le logo OURTECH */
.op-logo b {
	font-weight: 700;
	color: #C4A5FF;
	letter-spacing: 0.14em;
}

#logo:hover .op-logo { opacity: 0.82; }

/* Sur fond clair, si la marque venait à y figurer */
.content-right .op-logo { color: var(--op-encre); }
.content-right .op-logo b { color: var(--op-violet); }

@media only screen and (max-width: 575px) {
	.op-logo { font-size: 18px; }
}


/* ==========================================================================
   23. Choix du format de login
   Ce sont des boutons radio de même nom : un seul choix possible parmi les
   formats. Le gabarit les affichait en pavés violets figés, sans indiquer
   ni l'état ni le caractère cliquable.
   On en fait de vrais interrupteurs, avec un rail et une pastille qui
   glisse, et on rend toute la ligne réactive.
   ========================================================================== */

.form-group.options {
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 11px 14px;
	margin-bottom: 8px;
	border-radius: 12px;
	border: 1px solid var(--op-bordure);
	background-color: #FFFFFF;
	cursor: pointer;
	transition: border-color 170ms ease, background-color 170ms ease, box-shadow 170ms ease;
}

.form-group.options:hover {
	border-color: rgba(118,31,227,0.42);
	background-color: rgba(118,31,227,0.035);
}

/* La ligne choisie se détache */
.form-group.options:has(input:checked) {
	border-color: var(--op-violet);
	background-color: rgba(118,31,227,0.06);
	box-shadow: 0 3px 12px rgba(118,31,227,0.14);
}

.form-group.options .icon_profile {
	flex: 0 0 auto;
	font-size: 18px;
	color: var(--op-gris);
	opacity: 0.65;
}

.form-group.options:has(input:checked) .icon_profile { color: var(--op-violet); opacity: 1; }

.form-group.options em {
	flex: 1;
	font-style: normal;
	font-size: 15px;
	font-weight: 500;
	color: var(--op-encre);
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* --- L'option libre sur une seule ligne ----------------------------------- */

.form-group.options .radio-container {
	flex: 0 0 auto;
	display: block;
	gap: 0;
}

.form-group.options .custom-option-container {
	flex: 1 1 220px;
	display: block;
	min-width: 0;
	order: 2;          /* entre le libellé et l'interrupteur */
}

.form-group.options .custom-option-container input {
	margin-top: 0 !important;
	width: 100%;
	padding: 8px 12px;
	font-size: 14px;
}

/* Le libellé « Autre » ne prend plus toute la place quand un champ suit */
.form-group.options:has(.custom-option-container) em { flex: 0 0 auto; }


@media only screen and (max-width: 575px) {
	.form-group.options { flex-wrap: wrap; gap: 10px; }
	.form-group.options em { flex: 1 1 100%; }
	.form-group.options .custom-option-container { flex: 1 1 100%; order: 0; }
}


/* ==========================================================================
   24. Menu de l'application
   Il contenait encore trois entrées du gabarit d'origine : « Contact Us »
   vers contact-hotel.html, « Restaurant Version » et « Spa Version ».
   ========================================================================== */

.cd-primary-nav li a[href*="contact-hotel"],
.cd-primary-nav li a[href*="index-2.html"],
.cd-primary-nav li a[href*="index-3.html"],
.cd-primary-nav li a[href*="restaurant"],
.cd-primary-nav li a[href*="spa"] {
	display: none !important;
}

.cd-primary-nav li a {
	font-family: 'Inter', system-ui, sans-serif;
	font-weight: 600;
	letter-spacing: -0.01em;
	transition: color 160ms ease, padding-left 160ms ease;
}

.cd-primary-nav li a:hover { padding-left: 6px; }

/* Le voile du menu reprend le dégradé de marque */
.cd-overlay-nav > span,
.cd-overlay-content > span {
	background: var(--op-degrade) !important;
}

/* Bouton d'ouverture du menu, lisible sur les deux fonds */
.cd-nav-trigger { color: var(--op-encre); }


/* ==========================================================================
   25. Pages Suivi et envois en masse
   Elles présentent des tableaux et des zones de dépôt de fichier.
   ========================================================================== */

.table, table {
	font-size: 14.5px;
	border-color: var(--op-bordure);
}

.table thead th, table thead th {
	background-color: var(--op-surface);
	color: var(--op-encre);
	font-weight: 600;
	font-size: 13px;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	border-color: var(--op-bordure) !important;
	padding: 12px 14px;
}

.table tbody td, table tbody td {
	color: var(--op-gris);
	border-color: var(--op-bordure) !important;
	padding: 12px 14px;
	vertical-align: middle;
}

.table tbody tr:hover, table tbody tr:hover { background-color: rgba(118,31,227,0.04); }

/* Étiquettes d'état dans les tableaux de suivi */
.badge, .label {
	border-radius: 100px;
	padding: 5px 11px;
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: 0.02em;
}

.badge-success, .badge.bg-success { background-color: rgba(22,163,74,0.12) !important; color: #15803D !important; }
.badge-warning, .badge.bg-warning { background-color: rgba(245,158,11,0.14) !important; color: #92400E !important; }
.badge-danger,  .badge.bg-danger  { background-color: rgba(220,38,38,0.12) !important; color: #B91C1C !important; }
.badge-primary, .badge.bg-primary { background-color: rgba(118,31,227,0.12) !important; color: var(--op-violet) !important; }

/* Zone de dépôt de fichier des envois en masse */
input[type="file"] {
	padding: 13px 16px;
	border: 1.5px dashed rgba(118,31,227,0.34) !important;
	border-radius: 12px;
	background-color: var(--op-surface) !important;
	color: var(--op-encre) !important;
	width: 100%;
	cursor: pointer;
	transition: border-color 170ms ease, background-color 170ms ease;
}

input[type="file"]:hover {
	border-color: var(--op-violet) !important;
	background-color: rgba(118,31,227,0.06) !important;
}


/* ==========================================================================
   26. Interrupteurs — reconstruction complète
   Le gabarit emploie un montage indirect : > span est en position:absolute
   left:-100px, les libellés compensent par margin-left:100px, et le rail est
   dessiné par un ::before à left:100px. Le surcharger partiellement décalait
   tout de 100 px — d'où la première correction.
   Deuxième problème : avec les libellés DANS le rail, la pastille recouvrait
   « Non » et seul « Oui » restait lisible, quel que soit l'état.
   On sort donc le libellé du rail : interrupteur compact à gauche, état
   écrit à droite, un seul mot affiché à la fois.
   ========================================================================== */

.switch-light.switch-ios {
	position: relative !important;
	display: block !important;
	float: none !important;
	width: 94px !important;
	height: 30px !important;
	margin: 0 !important;
	padding: 0 !important;
	overflow: visible !important;
	flex: 0 0 94px;
	cursor: pointer;
}

.switch-light.switch-ios input {
	position: absolute !important;
	opacity: 0 !important;
	width: 100% !important;
	height: 100% !important;
	margin: 0 !important;
	z-index: 5;
	cursor: pointer;
}

/* Le rail : compact, il ne contient plus que la pastille */
.switch-light.switch-ios > span {
	position: absolute !important;
	left: 0 !important;
	top: 0 !important;
	right: auto !important;
	width: 50px !important;
	height: 30px !important;
	margin: 0 !important;
	padding: 0 !important;
	display: block !important;
	border-radius: 100px !important;
	background-color: #E4DFF0 !important;
	border: 1px solid var(--op-bordure) !important;
	overflow: visible !important;
	text-align: left !important;
	transition: background-color 200ms ease, border-color 200ms ease;
}

/* Le rail que le gabarit dessinait par un pseudo-élément décalé */
.switch-light.switch-ios > span::before,
.switch-light.switch-ios > span::after {
	display: none !important;
	content: none !important;
}

/* Les libellés sortent du rail et se placent à sa droite.
   Un seul est visible : celui de l'état courant. */
.switch-light.switch-ios > span span,
.switch-light.switch-ios > span span:first-of-type,
.switch-light.switch-ios > span span:last-of-type {
	position: absolute !important;
	left: 60px !important;
	top: 50% !important;
	transform: translateY(-50%);
	width: auto !important;
	margin: 0 !important;
	padding: 0 !important;
	display: block !important;
	white-space: nowrap;
	font-size: 13px;
	font-weight: 600;
	line-height: 1;
	background: none !important;
	border: none !important;
	transition: opacity 180ms ease, color 180ms ease;
}

.switch-light.switch-ios > span span:first-of-type { opacity: 1 !important; color: var(--op-gris) !important; }
.switch-light.switch-ios > span span:last-of-type  { opacity: 0 !important; color: var(--op-violet) !important; }

.switch-light.switch-ios input:checked ~ span span:first-of-type { opacity: 0 !important; }
.switch-light.switch-ios input:checked ~ span span:last-of-type  { opacity: 1 !important; }

/* Rail activé */
.switch-light.switch-ios input:checked ~ span {
	background: var(--op-degrade) !important;
	border-color: transparent !important;
}

/* La pastille : ronde, elle ne masque plus rien */
.switch-light.switch-ios > a,
.switch-ios.switch-light a {
	position: absolute !important;
	z-index: 3;
	left: 3px !important;
	top: 3px !important;
	right: auto !important;
	width: 24px !important;
	height: 24px !important;
	margin: 0 !important;
	border-radius: 50% !important;
	background-color: #FFFFFF !important;
	border: none !important;
	box-shadow: 0 2px 5px rgba(26,21,35,0.24) !important;
	transform: translateX(0);
	transition: transform 240ms cubic-bezier(0.34,1.56,0.64,1) !important;
}

.switch-light.switch-ios input:checked ~ a,
.switch-ios.switch-light input:checked ~ a {
	left: 3px !important;
	margin-left: 0 !important;
	transform: translateX(20px);
}

.form-group.options:hover .switch-light.switch-ios > span { border-color: rgba(118,31,227,0.38) !important; }


/* --- La ligne « Autre » ---------------------------------------------------
   Son interrupteur est enveloppé dans .radio-container, contrairement aux
   autres : sans traitement, la ligne débordait à gauche. */

.form-group.options .radio-container {
	flex: 0 0 96px !important;
	display: block !important;
	width: 96px;
	margin: 0;
	padding: 0;
	order: 3;
}

.form-group.options .custom-option-container {
	flex: 1 1 auto !important;
	display: block !important;
	min-width: 0;
	margin: 0;
	padding: 0;
	order: 2;
}

.form-group.options:has(.custom-option-container) em {
	flex: 0 0 auto;
	margin-right: 4px;
}

@media only screen and (max-width: 575px) {
	.form-group.options .radio-container { order: 2; }
	.form-group.options .custom-option-container { order: 3; flex: 1 1 100% !important; }
}


/* ==========================================================================
   27. Alignement des lignes d'options
   Le gabarit positionne l'icône en absolu (top:15px) et pousse le texte de
   7 px vers le bas (em { position:relative; top:7px }), le tout compensé par
   un padding-left de 60px sur la ligne.
   Passer la ligne en flex sans annuler ces règles laissait l'icône et le
   texte désalignés verticalement.
   ========================================================================== */

.form-group.options i,
.form-group.options .icon_profile {
	position: static !important;
	left: auto !important;
	top: auto !important;
	display: flex !important;
	align-items: center;
	justify-content: center;
	flex: 0 0 22px;
	width: 22px;
	height: 22px;
	font-size: 21px !important;
	line-height: 1 !important;
}

.form-group.options em {
	position: static !important;
	top: auto !important;
	display: flex;
	align-items: center;
	min-height: 22px;
}

/* Le remplissage à gauche réservait la place de l'icône absolue :
   elle est maintenant dans le flux. */
.form-group.options {
	padding-left: 14px !important;
}


/* ==========================================================================
   28. Ajustements
   - La ligne « Autre » : le libellé est passé dans le champ, l'interrupteur
     revient en fin de ligne, aligné sur les quatre autres options.
   - Le sous-titre du volet gauche n'était pas centré sur le même axe que le
     titre : ses marges automatiques ne s'appliquaient pas.
   ========================================================================== */

/* --- Ligne « Autre » : même gabarit que les autres options ---------------- */

.form-group.options .custom-option-container {
	order: 1;
	flex: 1 1 auto !important;
	margin: 0 !important;
	padding: 0 !important;
	min-width: 0;
}

.form-group.options .custom-option-container input {
	margin: 0 !important;
	width: 100%;
	padding: 8px 12px;
	font-size: 14px;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.form-group.options .radio-container {
	order: 2;
	flex: 0 0 94px !important;
	width: 94px;
	margin: 0 !important;
	padding: 0 !important;
}

/* --- Sous-titre du volet gauche ------------------------------------------- */

.content-left .left_title,
.content-left-wrapper .left_title {
	width: 100% !important;
	max-width: none !important;
	margin: 0 auto !important;
	text-align: center !important;
}

.content-left .left_title p,
.content-left-wrapper .left_title p {
	max-width: 440px !important;
	margin: 0 auto !important;
	padding: 0 !important;
	text-align: center !important;
	float: none !important;
}

@media only screen and (max-width: 575px) {
	.form-group.options .custom-option-container { order: 1; flex: 1 1 100% !important; }
	.form-group.options .radio-container { order: 2; }
}


/* ==========================================================================
   29. Page de consultation du lien (execute_code.php / validate.php)
   C'est la page la plus publique d'OURPASS : celle que reçoit le
   destinataire. Le gabarit y posait une photo d'hôtel en fond
   (img/sub_header_hotel.jpg) et affichait le mot de passe en vert dessus.
   On y remet exactement le fond du volet gauche de l'application : même
   dégradé, même trame de points, même grain.
   Les styles en ligne du PHP imposent le !important.
   ========================================================================== */

.parallax_window_in {
	position: relative;
	background-image: none !important;
	background: linear-gradient(158deg, #16101F 0%, #3B0F70 46%, #6D28D9 100%) !important;
	background-size: 100% 100% !important;
	min-height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 40px 20px;
	overflow: hidden;
}

/* Les couches de parallaxe injectées par le script du gabarit */
.parallax-mirror,
.parallax_window_in .parallax-slider {
	display: none !important;
}

/* Foyers colorés — identiques au volet gauche */
.parallax_window_in::before {
	position: absolute;
	content: '';
	left: 0; top: 0; right: 0; bottom: 0;
	z-index: 0;
	pointer-events: none;
	background:
		radial-gradient(circle at 24% 16%, rgba(217,70,239,0.32) 0%, transparent 46%),
		radial-gradient(circle at 78% 82%, rgba(56,189,248,0.15) 0%, transparent 44%);
	animation: op-fond 26s ease-in-out infinite alternate;
}

/* Trame de points — identique au volet gauche */
.parallax_window_in::after {
	position: absolute;
	content: '';
	left: 0; top: 0; right: 0; bottom: 0;
	z-index: 0;
	pointer-events: none;
	opacity: 0.5;
	background-image: radial-gradient(rgba(255,255,255,0.09) 1px, transparent 1px);
	background-size: 26px 26px;
}

/* --- La carte centrale ---------------------------------------------------- */

#sub_content_in {
	position: relative;
	z-index: 2;
	max-width: 560px;
	margin: 0 auto;
	padding: 44px 40px;
	border-radius: 20px;
	text-align: center;
	border: 1px solid rgba(255,255,255,0.18);
	background-color: rgba(255,255,255,0.08);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
	box-shadow: 0 26px 64px rgba(0,0,0,0.44), inset 0 1px 0 rgba(255,255,255,0.14);
}

#sub_content_in h1 {
	color: #FFFFFF !important;
	font-family: 'Inter', system-ui, sans-serif;
	font-size: 25px;
	font-weight: 700;
	letter-spacing: -0.02em;
	line-height: 1.3;
	margin: 0 0 6px;
}

#sub_content_in h1 p {
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.10em;
	text-transform: uppercase;
	color: rgba(255,255,255,0.62) !important;
	margin: 0 0 16px;
}

#sub_content_in > p {
	color: rgba(255,255,255,0.74) !important;
	font-size: 15px;
	line-height: 1.6;
	margin: 14px 0 0;
}

/* --- Le mot de passe : il s'affichait en vert sur la photo ---------------- */

#sub_content_in h1 > div {
	display: flex !important;
	align-items: center;
	justify-content: center;
	gap: 12px;
	margin: 4px auto 0;
	padding: 16px 20px;
	border-radius: 12px;
	border: 1px dashed rgba(196,165,255,0.48);
	background-color: rgba(0,0,0,0.28);
}

/* Cette règle DOIT rester limitée à #sub_content_in : sur index.php, le champ
   de saisie porte le même id="password". Sans ce préfixe, le texte y devenait
   blanc sur fond blanc. */
#sub_content_in #password {
	color: #FFFFFF !important;
	margin-right: 0 !important;
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important;
	font-size: 21px;
	font-weight: 600;
	letter-spacing: 0.04em;
	word-break: break-all;
	-webkit-user-select: all;
	user-select: all;
}

#copyBtn {
	flex: 0 0 auto;
	width: 38px !important;
	height: 38px !important;
	display: inline-flex !important;
	align-items: center;
	justify-content: center;
	border-radius: 9px !important;
	border: 1px solid rgba(255,255,255,0.24) !important;
	background-color: rgba(255,255,255,0.10) !important;
	cursor: pointer;
	transition: background-color 160ms ease, border-color 160ms ease, transform 160ms ease;
}

#copyBtn:hover {
	background-color: rgba(255,255,255,0.20) !important;
	border-color: rgba(255,255,255,0.46) !important;
	transform: translateY(-1px);
}

#copyBtn img { width: 17px !important; height: 17px; opacity: 0.9; }

/* --- Menu : entrées restantes du gabarit d'hôtel -------------------------- */

.cd-primary-nav li a[href*="index-2"],
.cd-primary-nav li a[href*="index-3"],
.cd-primary-nav li a[href*="rooms"],
.cd-primary-nav li a[href*="hotel"] {
	display: none !important;
}

@media only screen and (max-width: 575px) {
	#sub_content_in { padding: 32px 22px; }
	#sub_content_in h1 { font-size: 21px; }
	#password { font-size: 18px; }
}

@media (prefers-reduced-motion: reduce) {
	.parallax_window_in::before { animation: none !important; }
	#copyBtn:hover { transform: none; }
}


/* ==========================================================================
   30. Page d'acceptation des conditions (validate.php)
   Porte d'entrée du destinataire, avant l'affichage du mot de passe.
   Le gabarit y laisse un corps vide et un voile noir à 50 %, avec une carte
   blanche au centre. On y remet le fond de l'application.
   Cette page a ses propres <style> internes : d'où les !important.
   ========================================================================== */

body:has(#myModal) {
	min-height: 100vh;
	margin: 0;
	font-family: 'Inter', system-ui, sans-serif !important;
	background: linear-gradient(158deg, #16101F 0%, #3B0F70 46%, #6D28D9 100%) !important;
	background-attachment: fixed;
}

/* Foyers colorés et trame, comme partout ailleurs */
body:has(#myModal)::before {
	position: fixed;
	content: '';
	left: 0; top: 0; right: 0; bottom: 0;
	z-index: 0;
	pointer-events: none;
	background:
		radial-gradient(circle at 24% 16%, rgba(217,70,239,0.30) 0%, transparent 46%),
		radial-gradient(circle at 78% 82%, rgba(56,189,248,0.14) 0%, transparent 44%),
		radial-gradient(rgba(255,255,255,0.07) 1px, transparent 1px);
	background-size: 100% 100%, 100% 100%, 26px 26px;
}

/* Le voile : plus léger, le fond doit rester lisible */
#myModal.modal,
#modalConditions.modal-conditions {
	background-color: rgba(12,8,20,0.55) !important;
	-webkit-backdrop-filter: blur(3px);
	backdrop-filter: blur(3px);
}

/* --- La carte ------------------------------------------------------------- */

#myModal .modal-content,
#modalConditions .modal-conditions-content {
	background-color: #FFFFFF !important;
	border-radius: 18px !important;
	padding: 36px 32px !important;
	max-width: 480px !important;
	border: none;
	box-shadow: 0 30px 70px rgba(0,0,0,0.42) !important;
	text-align: center;
}

#myModal .modal-content h2,
#modalConditions .modal-conditions-content h2 {
	font-family: 'Inter', system-ui, sans-serif;
	font-size: 21px !important;
	font-weight: 700;
	letter-spacing: -0.02em;
	color: var(--op-encre) !important;
	margin-bottom: 14px !important;
}

#myModal .modal-content p,
#modalConditions .modal-conditions-content p {
	font-size: 15px !important;
	line-height: 1.6;
	color: var(--op-gris) !important;
	margin-bottom: 22px !important;
}

#myModal .modal-content label {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 9px;
	font-size: 14.5px !important;
	color: var(--op-encre) !important;
	cursor: pointer;
}

#myModal .modal-content input[type="checkbox"] {
	width: 18px;
	height: 18px;
	margin-right: 0 !important;
	accent-color: var(--op-violet);
	cursor: pointer;
}

#myModal .modal-content a,
#modalConditions a { color: var(--op-violet) !important; font-weight: 600; }

/* --- Les boutons ---------------------------------------------------------- */

#myModal .modal-content button {
	padding: 12px 26px !important;
	border-radius: 9px !important;
	font-family: 'Inter', system-ui, sans-serif;
	font-size: 15px !important;
	font-weight: 600;
	letter-spacing: -0.01em;
	margin-top: 20px !important;
	transition: transform 160ms ease, box-shadow 180ms ease, background-color 180ms ease;
}

#accept {
	background: var(--op-degrade) !important;
	color: #FFFFFF !important;
	box-shadow: 0 4px 14px rgba(118,31,227,0.28);
	margin-right: 8px;
}

#accept:not(:disabled):hover { transform: translateY(-1px); box-shadow: 0 8px 22px rgba(118,31,227,0.38); }

#accept:disabled {
	background: #E4DFF0 !important;
	color: #9B93AC !important;
	cursor: not-allowed;
	box-shadow: none;
}

#reject {
	background: transparent !important;
	color: var(--op-gris) !important;
	border: 1px solid var(--op-bordure) !important;
}

#reject:hover { border-color: var(--op-violet) !important; color: var(--op-violet) !important; }

.close-conditions {
	color: var(--op-gris) !important;
	font-size: 24px;
	cursor: pointer;
	opacity: 0.55;
	transition: opacity 150ms ease;
}

.close-conditions:hover { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
	#accept:not(:disabled):hover { transform: none; }
}


/* ==========================================================================
   31. Les deux états de la page de consultation
   Le gabarit posait un cadre noir derrière le titre
   (#sub_content_in h1 { background-color:#000 }) avec une spécificité
   supérieure à mes règles.
   Les deux états ne diffèrent que par leur contenu : on les distingue par
   la présence de #password, sans toucher au PHP.
   ========================================================================== */

/* --- Le cadre noir ------------------------------------------------------- */

#sub_content_in,
#sub_content_in h1 {
	background-color: transparent !important;
	background: none !important;
	padding-left: 0 !important;
	padding-right: 0 !important;
	display: block !important;
}

#sub_content_in {
	display: block !important;
	background-color: rgba(255,255,255,0.08) !important;
	padding: 44px 40px !important;
}

/* --- Entrée de la carte, commune aux deux états -------------------------- */

#sub_content_in {
	animation: op-carte-entree 620ms cubic-bezier(0.22,0.61,0.36,1) both;
}

@keyframes op-carte-entree {
	from { opacity: 0; transform: translateY(16px) scale(0.985); }
	to   { opacity: 1; transform: none; }
}


/* ==========================================================================
   ÉTAT 1 — le lien est valide, le mot de passe s'affiche
   ========================================================================== */

/* Le cadre du mot de passe se dessine, puis respire */
#sub_content_in:has(#password) h1 > div {
	animation: op-cadre 700ms cubic-bezier(0.22,0.61,0.36,1) 280ms both,
	           op-respire 3.4s ease-in-out 1.1s infinite;
}

@keyframes op-cadre {
	from { opacity: 0; transform: scaleY(0.7); border-color: rgba(196,165,255,0); }
	to   { opacity: 1; transform: none; border-color: rgba(196,165,255,0.48); }
}

@keyframes op-respire {
	0%, 100% { box-shadow: 0 0 0 0 rgba(196,165,255,0); }
	50%      { box-shadow: 0 0 22px 1px rgba(196,165,255,0.22); }
}

/* Le mot de passe se révèle : d'abord flou, puis net.
   Portée limitée à la page de consultation, cf. remarque ci-dessus. */
#sub_content_in #password {
	animation: op-revele 720ms cubic-bezier(0.22,0.61,0.36,1) 520ms both;
}

@keyframes op-revele {
	from { opacity: 0; filter: blur(9px); letter-spacing: 0.24em; }
	to   { opacity: 1; filter: none; letter-spacing: 0.04em; }
}

/* Le bouton de copie arrive en dernier */
#copyBtn {
	animation: op-bouton 460ms cubic-bezier(0.34,1.56,0.64,1) 900ms both;
}

@keyframes op-bouton {
	from { opacity: 0; transform: scale(0.6); }
	to   { opacity: 1; transform: none; }
}

/* Le rappel d'usage unique apparaît une fois le reste en place */
#sub_content_in:has(#password) > p {
	animation: op-mention 560ms ease 1.15s both;
}

@keyframes op-mention {
	from { opacity: 0; transform: translateY(7px); }
	to   { opacity: 1; transform: none; }
}


/* ==========================================================================
   ÉTAT 2 — le lien a déjà été consulté, ou a expiré
   ========================================================================== */

#sub_content_in:not(:has(#password)) {
	background-color: rgba(255,255,255,0.05) !important;
	border-color: rgba(255,255,255,0.12) !important;
}

/* Un cadenas se ferme au-dessus du message */
#sub_content_in:not(:has(#password))::before {
	display: block;
	content: '';
	width: 62px;
	height: 62px;
	margin: 0 auto 22px;
	border-radius: 50%;
	background-color: rgba(255,255,255,0.07);
	border: 1px solid rgba(255,255,255,0.14);
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='26' height='26' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.62)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect x='3' y='11' width='18' height='11' rx='2'/><path d='M7 11V7a5 5 0 0 1 10 0v4'/></svg>");
	background-repeat: no-repeat;
	background-position: center;
	animation: op-verrou 900ms cubic-bezier(0.34,1.56,0.64,1) 200ms both;
}

/* L'anse du cadenas se rabat, puis le cercle marque le coup */
@keyframes op-verrou {
	0%   { opacity: 0; transform: translateY(-14px) scale(0.82); }
	55%  { opacity: 1; transform: translateY(2px) scale(1.06); }
	72%  { transform: translateY(0) scale(0.97); }
	100% { opacity: 1; transform: none; }
}

/* Onde unique, comme un verrou qui claque */
#sub_content_in:not(:has(#password))::after {
	position: absolute;
	content: '';
	left: 50%;
	top: 44px;
	width: 62px;
	height: 62px;
	margin-left: -31px;
	border-radius: 50%;
	pointer-events: none;
	border: 1px solid rgba(255,255,255,0.34);
	opacity: 0;
	animation: op-onde-verrou 1.1s ease-out 700ms 1 both;
}

@keyframes op-onde-verrou {
	0%   { opacity: 0.7; transform: scale(1); }
	100% { opacity: 0; transform: scale(1.9); }
}

#sub_content_in:not(:has(#password)) h1 {
	color: rgba(255,255,255,0.88) !important;
	font-size: 22px;
	animation: op-message 560ms ease 620ms both;
}

#sub_content_in:not(:has(#password)) > p {
	animation: op-message 560ms ease 780ms both;
}

@keyframes op-message {
	from { opacity: 0; transform: translateY(8px); }
	to   { opacity: 1; transform: none; }
}


/* --- Confirmation de copie ------------------------------------------------ */

#copyBtn.op-copie {
	background-color: rgba(34,197,94,0.26) !important;
	border-color: rgba(134,239,172,0.62) !important;
}


@media (prefers-reduced-motion: reduce) {
	#sub_content_in,
	#sub_content_in:has(#password) h1 > div,
	#sub_content_in #password, #copyBtn,
	#sub_content_in > p,
	#sub_content_in:not(:has(#password))::before,
	#sub_content_in:not(:has(#password))::after,
	#sub_content_in:not(:has(#password)) h1 { animation: none !important; }

	/* états finaux forcés : une animation coupée ne doit rien effacer */
	#sub_content_in, #sub_content_in #password, #copyBtn, #sub_content_in > p,
	#sub_content_in h1 > div, #sub_content_in:not(:has(#password))::before {
		opacity: 1 !important; transform: none !important; filter: none !important;
	}
	#sub_content_in:not(:has(#password))::after { opacity: 0 !important; }
}


/* ==========================================================================
   32. Pages à contenu : suivi et envois en masse
   La règle min-height:100vh posée pour la page de consultation s'appliquait
   aussi à ces pages : leur en-tête occupait tout l'écran et masquait le
   tableau, sans indice qu'il fallait défiler.
   On les distingue par ce qu'elles contiennent : une pagination ou un champ
   de fichier — ce que la page de consultation n'a pas.
   ========================================================================== */

body:has(.pagination) .parallax_window_in,
body:has(input[type="file"]) .parallax_window_in {
	min-height: auto !important;
	display: block !important;
	padding: 108px 20px 44px !important;
}

body:has(.pagination) #sub_content_in,
body:has(input[type="file"]) #sub_content_in {
	max-width: 900px;
	padding: 26px 32px !important;
	animation: none;
}

body:has(.pagination) #sub_content_in h1,
body:has(input[type="file"]) #sub_content_in h1 {
	font-size: 27px;
	margin-bottom: 4px;
}

/* Indice de défilement : le contenu continue plus bas */
body:has(.pagination) .parallax_window_in::after,
body:has(input[type="file"]) .parallax_window_in::after {
	content: '';
	position: absolute;
	left: 50%;
	bottom: 12px;
	width: 22px;
	height: 22px;
	margin-left: -11px;
	opacity: 0.5;
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.9)' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
	background-repeat: no-repeat;
	background-position: center;
	background-size: contain;
	animation: op-invite 2.2s ease-in-out infinite;
}

@keyframes op-invite {
	0%, 100% { transform: translateY(0); opacity: 0.35; }
	50%      { transform: translateY(5px); opacity: 0.75; }
}

/* Le corps de page redevient clair sous l'en-tête */
body:has(.pagination) #general_page,
body:has(input[type="file"]) #general_page {
	background-color: var(--op-surface);
	padding-bottom: 60px;
}

body:has(.pagination) #general_page > .container,
body:has(input[type="file"]) #general_page > .container {
	max-width: 900px;
	margin: -26px auto 0;
	position: relative;
	z-index: 3;
}


/* ==========================================================================
   33. Liste de suivi des liens
   Le gabarit rend une <ul> de <li>. Plutôt qu'un tableau, on en fait des
   cartes lisibles, avec un liseré de couleur par statut.
   Le statut se déduit de l'icône employée par le PHP : « checked » pour un
   lien consulté, « hourglass » pour un lien encore actif.
   ========================================================================== */

#general_page .container > ul,
#general_page ul:has(li .content) {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: 10px;
}

#general_page ul li:has(.content) {
	position: relative;
	display: flex;
	align-items: flex-start;
	gap: 14px;
	padding: 16px 18px 16px 22px;
	border-radius: 12px;
	border: 1px solid var(--op-bordure);
	background-color: #FFFFFF;
	box-shadow: 0 2px 8px rgba(26,21,35,0.04);
	transition: border-color 170ms ease, box-shadow 170ms ease, transform 170ms ease;
}

#general_page ul li:has(.content):hover {
	border-color: rgba(118,31,227,0.34);
	box-shadow: 0 8px 22px rgba(26,21,35,0.09);
	transform: translateY(-1px);
}

/* Liseré de statut, à gauche */
#general_page ul li:has(.content)::before {
	position: absolute;
	content: '';
	left: 0;
	top: 12px;
	bottom: 12px;
	width: 4px;
	border-radius: 0 4px 4px 0;
	background-color: var(--op-bordure);
}

/* Lien déjà consulté */
#general_page ul li:has(img[src*="checked"])::before { background-color: #16A34A; }

/* Lien encore actif */
#general_page ul li:has(img[src*="hourglass"])::before { background-color: #F59E0B; }

#general_page ul li .icon {
	flex: 0 0 22px;
	width: 22px !important;
	height: 22px !important;
	margin-top: 2px;
}

#general_page ul li .content { flex: 1; min-width: 0; }

#general_page ul li .content h3 {
	font-size: 15.5px;
	font-weight: 600;
	color: var(--op-encre);
	margin: 0 0 6px;
	word-break: break-all;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	letter-spacing: -0.01em;
}

#general_page ul li .content p {
	font-size: 13px;
	color: var(--op-gris);
	margin: 0 0 2px;
	line-height: 1.5;
}

#general_page ul li .content p.date { color: #8B8397; font-size: 12.5px; }

/* La mention « Validé: Oui/Non » devient une étiquette */
#general_page ul li .content p:first-of-type {
	display: inline-block;
	padding: 3px 10px;
	border-radius: 100px;
	font-size: 11.5px;
	font-weight: 600;
	margin-bottom: 8px;
	background-color: rgba(118,31,227,0.09);
	color: var(--op-violet);
}

#general_page ul li:has(img[src*="checked"]) .content p:first-of-type {
	background-color: rgba(22,163,74,0.12);
	color: #15803D;
}

#general_page ul li:has(img[src*="hourglass"]) .content p:first-of-type {
	background-color: rgba(245,158,11,0.14);
	color: #92400E;
}

/* Aucun résultat */
#general_page ul li:not(:has(.content)) {
	list-style: none;
	padding: 34px 18px;
	text-align: center;
	color: var(--op-gris);
	border: 1px dashed var(--op-bordure);
	border-radius: 12px;
	background-color: #FFFFFF;
}


/* ==========================================================================
   34. Pagination
   ========================================================================== */

.pagination {
	display: flex !important;
	justify-content: center;
	align-items: center;
	gap: 6px;
	flex-wrap: wrap;
	margin: 26px auto 0 !important;
	padding: 0 !important;
	max-width: 900px;
	list-style: none;
}

.pagination .page-item { margin: 0 !important; list-style: none; }

.pagination .page-link,
.pagination a {
	display: inline-flex !important;
	align-items: center;
	justify-content: center;
	min-width: 38px;
	height: 38px;
	padding: 0 12px !important;
	border-radius: 9px !important;
	border: 1px solid var(--op-bordure) !important;
	background-color: #FFFFFF !important;
	color: var(--op-encre) !important;
	font-size: 14px;
	font-weight: 600;
	text-decoration: none;
	transition: border-color 160ms ease, background-color 160ms ease, color 160ms ease;
}

.pagination .page-link:hover,
.pagination a:hover {
	border-color: var(--op-violet) !important;
	background-color: rgba(118,31,227,0.07) !important;
	color: var(--op-violet) !important;
}

.pagination .active .page-link,
.pagination .active a {
	background: var(--op-degrade) !important;
	border-color: transparent !important;
	color: #FFFFFF !important;
	box-shadow: 0 3px 10px rgba(118,31,227,0.28);
}

/* Compteur « Liens 1–5 sur 23 », au-dessus des numéros. */
.op-pagination-etat {
	margin: 26px 0 0 !important;
	text-align: center;
	font-size: 13.5px;
	color: var(--op-gris);
}

.op-pagination-etat strong {
	color: var(--op-encre);
	font-weight: 700;
}

/* La barre suit immédiatement le compteur quand celui-ci est présent. */
.op-pagination-etat + .pagination { margin-top: 10px !important; }

/* Flèches désactivées et ellipse : présentes pour garder la barre stable,
   mais sans affordance de clic. */
.pagination .op-page-inactive,
.pagination .op-page-ellipse {
	color: var(--op-gris) !important;
	background-color: transparent !important;
	cursor: default;
	opacity: 0.55;
}

.pagination .op-page-ellipse {
	border-color: transparent !important;
	min-width: 22px;
	padding: 0 4px !important;
}

/* Le conteneur reste dans le DOM même sans page à afficher — ourpass.css
   reconnaît la page de suivi via body:has(.pagination) — mais il ne doit
   alors occuper aucune place. */
.pagination[data-vide="1"] {
	margin: 0 !important;
	min-height: 0;
}


/* ==========================================================================
   35. Alignement de la ligne « Autre »
   Son interrupteur est enveloppé dans .radio-container, contrairement aux
   quatre autres options : sa largeur effective différait de ~30 px.
   On impose la même emprise au dernier élément de chaque ligne.
   ========================================================================== */

.form-group.options > *:last-child {
	flex: 0 0 96px !important;
	width: 96px !important;
	max-width: 96px !important;
	margin: 0 !important;
	padding: 0 !important;
}

.form-group.options .radio-container > .switch-light.switch-ios {
	margin: 0 !important;
	width: 96px !important;
	flex: none !important;
}

.switch-light.switch-ios { width: 96px !important; flex: 0 0 96px; }


/* ==========================================================================
   36. Zone de dépôt des envois en masse
   Le champ natif reste dans le DOM et reçoit le fichier — le formulaire
   fonctionne donc sans JavaScript, la zone n'étant qu'une surcouche.
   ========================================================================== */

.op-drop_input {
	position: absolute !important;
	width: 1px; height: 1px;
	opacity: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
}

.op-drop {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 5px;
	padding: 34px 24px;
	margin-bottom: 16px;
	border-radius: 14px;
	border: 1.5px dashed rgba(118,31,227,0.34);
	background-color: var(--op-surface);
	color: var(--op-gris);
	cursor: pointer;
	text-align: center;
	transition: border-color 180ms ease, background-color 180ms ease, transform 180ms ease;
}

.op-drop:hover {
	border-color: var(--op-violet);
	background-color: rgba(118,31,227,0.05);
}

/* Pendant le survol d'un fichier déposé */
.op-drop.is-survol {
	border-color: var(--op-violet);
	border-style: solid;
	background-color: rgba(118,31,227,0.09);
	transform: scale(1.008);
}

.op-drop svg {
	width: 30px;
	height: 30px;
	color: var(--op-violet);
	opacity: 0.75;
	margin-bottom: 4px;
	transition: transform 200ms ease;
}

.op-drop.is-survol svg { transform: translateY(-3px); }

.op-drop_titre {
	font-size: 15.5px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-drop_sous { font-size: 13.5px; color: var(--op-gris); }

.op-drop_nom {
	display: none;
	margin-top: 8px;
	padding: 6px 14px;
	border-radius: 100px;
	font-size: 13px;
	font-weight: 600;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	color: #15803D;
	background-color: rgba(22,163,74,0.12);
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Une fois le fichier choisi */
.op-drop.is-rempli {
	border-style: solid;
	border-color: rgba(22,163,74,0.45);
	background-color: rgba(22,163,74,0.05);
}

.op-drop.is-rempli svg { color: #16A34A; }
.op-drop.is-rempli .op-drop_titre { color: #15803D; }
.op-drop.is-rempli .op-drop_sous { display: none; }
.op-drop.is-rempli .op-drop_nom { display: inline-block; }

@media (prefers-reduced-motion: reduce) {
	.op-drop.is-survol { transform: none; }
	.op-drop.is-survol svg { transform: none; }
}


/* ==========================================================================
   37. Alignement des lignes d'options — par grille
   Le calage en flex dérivait sur la ligne « Autre », dont l'interrupteur est
   enveloppé dans un .radio-container supplémentaire. Une grille à trois
   colonnes fixes rend l'alignement déterministe, quel que soit le contenu
   de chaque cellule.
   ========================================================================== */

.form-group.options {
	display: grid !important;
	grid-template-columns: 22px 1fr 96px;
	align-items: center;
	gap: 14px !important;
	padding: 11px 14px !important;
}

.form-group.options > i,
.form-group.options > .icon_profile { grid-column: 1; justify-self: center; }
.form-group.options > em,
.form-group.options > .custom-option-container { grid-column: 2; }
.form-group.options > .switch-light,
.form-group.options > .radio-container { grid-column: 3; justify-self: start; }

.form-group.options > * { min-width: 0; }


/* ==========================================================================
   38. Titres de section des pages internes
   Ces pages portent un <style> interne qui impose .main_title h2 en bleu
   #007bff — hors charte. On le reprend, avec une spécificité supérieure.
   ========================================================================== */

#general_page .main_title,
.main_title {
	max-width: 900px;
	margin: 0 auto 26px !important;
	padding: 0 20px;
	text-align: center;
}

#general_page .main_title h2,
.main_title h2 {
	color: var(--op-encre) !important;
	font-family: 'Inter', system-ui, sans-serif;
	font-size: 26px !important;
	font-weight: 700 !important;
	letter-spacing: -0.025em !important;
	line-height: 1.25;
	margin: 0 0 8px !important;
}

#general_page .main_title p,
.main_title p {
	color: var(--op-gris) !important;
	font-size: 15.5px !important;
	font-weight: 400 !important;
	line-height: 1.6;
	margin: 0 auto !important;
	max-width: 620px;
}


/* ==========================================================================
   39. Le cadenas ne concerne que la page de consultation
   Ma règle d'état verrouillé s'appliquait aussi aux pages de suivi et
   d'envoi, dont l'en-tête n'a évidemment pas de mot de passe.
   ========================================================================== */

body:has(.pagination) #sub_content_in::before,
body:has(input[type="file"]) #sub_content_in::before,
body:has(.pagination) #sub_content_in::after,
body:has(input[type="file"]) #sub_content_in::after {
	display: none !important;
	content: none !important;
}


/* ==========================================================================
   40. Formulaire d'envoi en masse
   Le gabarit alignait libellé et champ sur une même ligne en flex, ce qui
   laissait le libellé à gauche, la zone de dépôt au centre et le bouton
   décroché en dessous. On remet le tout dans une carte, en colonne.
   ========================================================================== */

body:has(input[type="file"]) #general_page form {
	max-width: 620px;
	margin: 0 auto;
	padding: 30px 32px 32px;
	border-radius: 16px;
	border: 1px solid var(--op-bordure);
	background-color: #FFFFFF;
	box-shadow: 0 6px 24px rgba(26,21,35,0.06);
}

/* Les groupes repassent en colonne : le libellé au-dessus de son champ */
body:has(input[type="file"]) #general_page form .form-group {
	display: block !important;
	margin-bottom: 18px !important;
}

body:has(input[type="file"]) #general_page form .form-group label {
	display: block;
	margin: 0 0 8px !important;
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--op-gris);
}

body:has(input[type="file"]) #general_page form input[type="text"],
body:has(input[type="file"]) #general_page form select {
	width: 100%;
	margin-right: 0 !important;
	box-shadow: none !important;
}

/* Le bouton d'envoi prend toute la largeur de la carte */
body:has(input[type="file"]) #general_page form button {
	width: 100%;
	margin-top: 4px;
	padding: 13px 24px !important;
	background: var(--op-degrade) !important;
	border: none !important;
	border-radius: 10px !important;
	color: #FFFFFF !important;
	font-size: 15px;
	font-weight: 600;
	cursor: pointer;
	transition: transform 160ms ease, box-shadow 180ms ease;
	box-shadow: 0 4px 14px rgba(118,31,227,0.28);
}

body:has(input[type="file"]) #general_page form button:hover {
	transform: translateY(-1px);
	box-shadow: 0 8px 22px rgba(118,31,227,0.38);
}

/* Le lien vers le modèle CSV devient une pastille, au-dessus de la carte */
body:has(input[type="file"]) #general_page a[href*=".csv"],
body:has(input[type="file"]) #general_page a[download] {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	margin: 0 auto 16px;
	padding: 9px 16px;
	border-radius: 100px;
	border: 1px solid var(--op-bordure);
	background-color: #FFFFFF;
	color: var(--op-violet) !important;
	font-size: 13.5px;
	font-weight: 600;
	text-decoration: none !important;
	transition: border-color 160ms ease, background-color 160ms ease;
}

body:has(input[type="file"]) #general_page a[href*=".csv"]:hover,
body:has(input[type="file"]) #general_page a[download]:hover {
	border-color: var(--op-violet);
	background-color: rgba(118,31,227,0.06);
}

/* Le conteneur du lien se centre */
body:has(input[type="file"]) #general_page > .container { text-align: center; }
body:has(input[type="file"]) #general_page form { text-align: left; }

@media (prefers-reduced-motion: reduce) {
	body:has(input[type="file"]) #general_page form button:hover { transform: none; }
}


/* ==========================================================================
   41. Rangée unique pour les lignes d'options
   La ligne « Autre » repassait à la ligne, ce qui creusait un grand vide
   sous l'icône. On fixe explicitement la rangée de chaque cellule.
   ========================================================================== */

.form-group.options > i,
.form-group.options > .icon_profile,
.form-group.options > em,
.form-group.options > .custom-option-container,
.form-group.options > .switch-light,
.form-group.options > .radio-container {
	grid-row: 1 !important;
}

/* Le champ ne doit jamais forcer un retour à la ligne */
.form-group.options .custom-option-container input {
	width: 100% !important;
	min-width: 0 !important;
	box-sizing: border-box;
}


/* ==========================================================================
   42. Champs en erreur
   Le contrôle se fait désormais à la volée : pas d'alerte, pas de
   rechargement, le champ fautif est signalé sur place.
   ========================================================================== */

.op-invalide,
input.op-invalide,
.form-control.op-invalide {
	border-color: #DC2626 !important;
	background-color: #FEF2F2 !important;
	box-shadow: 0 0 0 3px rgba(220,38,38,0.10) !important;
	animation: op-secousse 380ms cubic-bezier(0.36,0.07,0.19,0.97);
}

.op-invalide:focus {
	border-color: #DC2626 !important;
	box-shadow: 0 0 0 4px rgba(220,38,38,0.16) !important;
}

/* Une secousse brève attire l'œil sans bloquer la saisie */
@keyframes op-secousse {
	0%, 100% { transform: translateX(0); }
	20%      { transform: translateX(-5px); }
	40%      { transform: translateX(5px); }
	60%      { transform: translateX(-3px); }
	80%      { transform: translateX(3px); }
}

.op-erreur {
	display: flex;
	align-items: center;
	gap: 6px;
	margin-top: 7px;
	font-size: 13px;
	font-weight: 500;
	line-height: 1.4;
	color: #B91C1C;
}

.op-erreur::before {
	content: '';
	flex: 0 0 15px;
	width: 15px;
	height: 15px;
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B91C1C' stroke-width='2.2' stroke-linecap='round'><circle cx='12' cy='12' r='10'/><path d='M12 8v5M12 16h.01'/></svg>");
	background-repeat: no-repeat;
	background-position: center;
	background-size: contain;
}

/* Les messages de jquery.validate, quand il en produit */
label.error {
	display: block;
	margin-top: 7px;
	font-size: 13px;
	font-weight: 500;
	color: #B91C1C;
}

input.error, select.error, textarea.error {
	border-color: #DC2626 !important;
	background-color: #FEF2F2 !important;
}

@media (prefers-reduced-motion: reduce) {
	.op-invalide { animation: none; }
}


/* ==========================================================================
   43. Page de confirmation d'envoi
   validation_secure.php et email.php confirment l'expédition puis
   redirigent au bout de 8 secondes. Elles restaient sur fond blanc, sans
   habillage.
   On y met le fond de l'application et une scène : le mot de passe est
   scellé dans un pli, puis expédié.
   ========================================================================== */

.op-page-envoi {
	min-height: 100vh;
	margin: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: 40px 20px;
	font-family: 'Inter', system-ui, sans-serif;
	background: linear-gradient(158deg, #16101F 0%, #3B0F70 46%, #6D28D9 100%) !important;
	background-attachment: fixed;
	position: relative;
	overflow-x: hidden;
}

.op-page-envoi::before {
	position: fixed;
	content: '';
	left: 0; top: 0; right: 0; bottom: 0;
	z-index: 0;
	pointer-events: none;
	background:
		radial-gradient(circle at 24% 16%, rgba(217,70,239,0.28) 0%, transparent 46%),
		radial-gradient(circle at 78% 82%, rgba(56,189,248,0.13) 0%, transparent 44%),
		radial-gradient(rgba(255,255,255,0.07) 1px, transparent 1px);
	background-size: 100% 100%, 100% 100%, 26px 26px;
}

/* Le bloc de confirmation du gabarit */
.op-page-envoi #success {
	position: relative;
	z-index: 2;
	max-width: 520px;
	margin: 0 auto;
	padding: 38px 36px 32px;
	border-radius: 20px;
	text-align: center;
	border: 1px solid rgba(255,255,255,0.16);
	background-color: rgba(255,255,255,0.08);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
	box-shadow: 0 26px 64px rgba(0,0,0,0.42);
	animation: op-carte-entree 620ms cubic-bezier(0.22,0.61,0.36,1) 2.6s both;
}

.op-page-envoi #success h4 {
	color: #FFFFFF;
	font-size: 20px;
	font-weight: 700;
	letter-spacing: -0.02em;
	line-height: 1.35;
	margin: 12px 0 6px;
}

.op-page-envoi #success h4 span { display: block; margin-bottom: 4px; }

.op-page-envoi #success small {
	display: block;
	margin-top: 14px;
	font-size: 13px;
	color: rgba(255,255,255,0.62);
}

.op-page-envoi #success .icon svg circle { stroke: #6EE7B7 !important; }
.op-page-envoi #success .icon svg path { stroke: #6EE7B7 !important; }



/* ==========================================================================
   44. Socle de l'ordinateur — volet gauche
   Il n'était qu'une barre droite de 5 px, ce qui ne lit pas comme un
   ordinateur. On lui donne la perspective d'un vrai socle : trapèze évasé
   vers l'avant, arête haute soulignée, encoche d'ouverture et ombre portée.
   ========================================================================== */

.op-dev--pc { position: relative; }

.op-dev_socle {
	position: relative;
	display: block;
	width: 104px !important;
	height: 9px !important;
	margin-top: -1px;
	border-radius: 0 0 3px 3px !important;
	background: linear-gradient(180deg,
		rgba(255,255,255,0.62) 0%,
		rgba(255,255,255,0.34) 55%,
		rgba(255,255,255,0.18) 100%) !important;
	/* évasement : plus large en bas qu'en haut */
	clip-path: polygon(5% 0, 95% 0, 100% 100%, 0 100%);
}

/* encoche d'ouverture, creusée dans l'arête avant */
.op-dev_socle::after {
	position: absolute;
	content: '';
	left: 50%;
	bottom: 0;
	width: 22px;
	height: 4px;
	margin-left: -11px;
	border-radius: 11px 11px 0 0;
	background-color: rgba(26,21,35,0.55);
}

/* ombre portée sous l'appareil */
.op-dev--pc::after {
	position: absolute;
	content: '';
	left: 50%;
	bottom: 14px;
	width: 112px;
	height: 6px;
	margin-left: -56px;
	border-radius: 50%;
	background: radial-gradient(ellipse at center, rgba(0,0,0,0.34) 0%, transparent 72%);
	pointer-events: none;
}

/* l'écran repose sur le socle, sans bord inférieur */
.op-dev--pc .op-dev_ecran {
	border-bottom: none !important;
	border-radius: 6px 6px 0 0 !important;
}

/* --- Scène de réception ---------------------------------------------------
   L'appareil du destinataire reçoit le message. Le canal est connu selon la
   page : mobile pour le SMS, ordinateur pour l'e-mail.
   -------------------------------------------------------------------------- */

.op-recept {
	position: relative;
	z-index: 2;
	margin: 0 auto 4px;
	text-align: center;
}

.op-appareil { width: 190px; height: 254px; overflow: visible; display: block; margin: 0 auto; }

/* --- Mobile --------------------------------------------------------------- */
#op-tel .ecran-veille { animation: t-veille 6s ease-in-out infinite; }
#op-tel .notif        { animation: t-notif 6s cubic-bezier(0.34,1.4,0.64,1) infinite; }
#op-tel .lueur        { animation: t-lueur 6s ease-out infinite; }
#op-tel .appareil     { animation: t-vibre 6s ease-in-out infinite; }
#op-tel .pastille     { animation: t-pastille 6s cubic-bezier(0.34,1.8,0.64,1) infinite; transform-origin: 146px 70px; }
#op-tel .onde1        { animation: t-onde 6s ease-out infinite; transform-origin: 105px 34px; }
#op-tel .onde2        { animation: t-onde 6s ease-out infinite 0.22s; transform-origin: 105px 34px; }

@keyframes t-veille { 0%,22% { opacity:0.16 } 32%,86% { opacity:1 } 96%,100% { opacity:0.16 } }
@keyframes t-notif  { 0%,24% { transform: translateY(-38px); opacity:0 } 36%,80% { transform: translateY(0); opacity:1 } 92%,100% { transform: translateY(-38px); opacity:0 } }
@keyframes t-lueur  { 0%,20% { opacity:0 } 30% { opacity:0.55 } 46%,100% { opacity:0 } }
@keyframes t-vibre {
	0%,26% { transform: translateX(0) rotate(0deg) }
	28% { transform: translateX(-2.5px) rotate(-0.8deg) }
	30% { transform: translateX(2.5px) rotate(0.8deg) }
	32% { transform: translateX(-2px) rotate(-0.6deg) }
	34% { transform: translateX(1.5px) rotate(0.5deg) }
	36%,100% { transform: translateX(0) rotate(0deg) }
}
@keyframes t-pastille { 0%,32% { opacity:0; transform: scale(0) } 42%,84% { opacity:1; transform: scale(1) } 92%,100% { opacity:0; transform: scale(0) } }
@keyframes t-onde     { 0%,16% { opacity:0; transform: scale(0.3) } 24% { opacity:0.7; transform: scale(1) } 40%,100% { opacity:0; transform: scale(1.9) } }

/* --- Ordinateur ----------------------------------------------------------- */
#op-pc .ecran-veille { animation: p-veille 6s ease-in-out infinite; }
#op-pc .pli-vol      { animation: p-vol 6s cubic-bezier(0.4,0,0.25,1) infinite; }
#op-pc .ligne-neuve  { animation: p-ligne 6s cubic-bezier(0.34,1.4,0.64,1) infinite; }
#op-pc .ligne-vieille{ animation: p-glisse 6s cubic-bezier(0.34,1.4,0.64,1) infinite; }
#op-pc .compteur     { animation: p-compteur 6s cubic-bezier(0.34,1.8,0.64,1) infinite; transform-origin: 57px 95px; }
#op-pc .lueur        { animation: p-lueur 6s ease-out infinite; }

@keyframes p-veille   { 0%,24% { opacity:0.20 } 34%,86% { opacity:1 } 96%,100% { opacity:0.20 } }
@keyframes p-vol      { 0%,8% { transform: translate(100px,-60px) scale(0.5); opacity:0 } 16% { opacity:1 } 34% { transform: translate(0,0) scale(1); opacity:1 } 42%,100% { transform: translate(0,0) scale(0.86); opacity:0 } }
@keyframes p-ligne    { 0%,38% { opacity:0; transform: translateY(-10px) } 50%,84% { opacity:1; transform: translateY(0) } 94%,100% { opacity:0; transform: translateY(-10px) } }
@keyframes p-glisse   { 0%,38% { transform: translateY(-15px) } 50%,84% { transform: translateY(0) } 94%,100% { transform: translateY(-15px) } }
@keyframes p-compteur { 0%,42% { opacity:0; transform: scale(0) } 52%,84% { opacity:1; transform: scale(1) } 92%,100% { opacity:0; transform: scale(0) } }
@keyframes p-lueur    { 0%,26% { opacity:0 } 36% { opacity:0.5 } 54%,100% { opacity:0 } }

/* --- Le mot de confirmation ----------------------------------------------- */

.op-recept_mot {
	margin-top: 4px;
	font-size: 14px;
	font-weight: 600;
	letter-spacing: -0.01em;
	color: rgba(255,255,255,0.92);
	opacity: 0;
	animation: op-mot 6s cubic-bezier(0.34,1.5,0.64,1) infinite;
}

.op-recept_mot span {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	padding: 6px 14px;
	border-radius: 100px;
	border: 1px solid rgba(110,231,183,0.34);
	background-color: rgba(110,231,183,0.11);
}

.op-recept_mot svg { width: 13px; height: 13px; flex: 0 0 13px; }
.op-recept_mot .trait { stroke-dasharray: 26; stroke-dashoffset: 26; animation: op-mot-coche 6s ease-out infinite; }

@keyframes op-mot       { 0%,44% { opacity:0; transform: translateY(9px) scale(0.94) } 56%,86% { opacity:1; transform: none } 94%,100% { opacity:0; transform: translateY(9px) scale(0.94) } }
@keyframes op-mot-coche { 0%,50% { stroke-dashoffset:26 } 62%,86% { stroke-dashoffset:0 } 94%,100% { stroke-dashoffset:26 } }

#op-compte { color: #C4A5FF; font-weight: 700; font-variant-numeric: tabular-nums; }

@media only screen and (max-width: 575px) {
	.op-appareil { width: 158px; height: 212px; }
}

@media (prefers-reduced-motion: reduce) {
	.op-appareil *, .op-recept_mot, .op-recept_mot .trait { animation: none !important; }
	.op-recept_mot { opacity: 1; transform: none; }
	.op-recept_mot .trait { stroke-dashoffset: 0; }
}

/* Le cadenas arrive aussi sur le mobile, comme sur l'ordinateur. */
#op-tel .cadenas-vol { animation: t-cadenas 6s cubic-bezier(0.4,0,0.25,1) infinite; }

@keyframes t-cadenas {
	0%, 6%    { transform: translate(0,-58px) scale(0.5); opacity: 0; }
	14%       { opacity: 1; }
	28%       { transform: translate(0,0) scale(1); opacity: 1; }
	34%, 100% { transform: translate(0,0) scale(0.9); opacity: 0; }
}


/* ==========================================================================
   45. Aperçu de l'étape 2 — l'appareil du destinataire
   Le récapitulatif montrait un bloc de texte sans contexte. Il montre
   désormais le message tel qu'il arrivera : téléphone pour un SMS,
   ordinateur portable pour un e-mail.
   ========================================================================== */

.op-apercu {
	display: flex;
	justify-content: center;
	padding: 8px 0 4px;
}

/* --------------------------------------------------------------------------
   Téléphone — proportions portrait, sans quoi la silhouette ne se lit pas.
   -------------------------------------------------------------------------- */

.op-tel {
	position: relative;
	width: 196px;
	padding: 8px;
	border-radius: 34px;
	background: linear-gradient(155deg, #3A3348 0%, #1B1626 46%, #0F0C16 100%);
	box-shadow:
		0 18px 40px rgba(26,21,35,0.34),
		inset 0 0 0 1px rgba(255,255,255,0.10),
		inset 0 1px 1px rgba(255,255,255,0.18);
}

/* Tranches : boutons latéraux, c'est ce qui fait lire « téléphone ». */
.op-tel_bouton-on,
.op-tel_bouton-vol {
	position: absolute;
	right: -2px;
	width: 3px;
	border-radius: 0 2px 2px 0;
	background: linear-gradient(180deg, #3A3348, #201A2C);
}

/* En POURCENTAGE : l'ecran n'a plus de hauteur fixe depuis qu'il suit son
   contenu, et 116 px les posaient tres bas sur un telephone court. */
.op-tel_bouton-on  { top: 32%; height: 15%; }

.op-tel_bouton-vol {
	right: auto;
	left: -2px;
	top: 21%;
	height: 9%;
	border-radius: 2px 0 0 2px;
	box-shadow: 0 20px 0 0 #2A2438;
}

.op-tel_ecran {
	position: relative;
	display: flex;
	flex-direction: column;
	border-radius: 27px;
	background: #F5F2FA;
	overflow: hidden;
}

/* Îlot de la caméra frontale. */




/* ⚠️ Ici vivaient les DEUX premieres versions de ces icones : un reseau
   decoupe au `clip-path` et une pile pleine. Elles sont redefinies 90 lignes
   plus bas — barres en elements enfants, pile avec sa borne et son niveau —
   mais `clip-path` n'y etait pas remis a zero : il continuait donc de
   decouper le conteneur des nouvelles barres, depuis l'ancienne regle.
   C'est le piege habituel de cette feuille : la derniere regle gagne
   PROPRIETE PAR PROPRIETE, jamais en bloc. Une regle morte ne l'est
   vraiment qu'une fois effacee. */

/* ── La barre d'etat, en trois colonnes ────────────────────────────────
   heure | ilot | reseau + batterie

   L'ilot etait en position absolue, centre, z-index 3 : il recouvrait le
   dernier chiffre de l'heure, et le recouvrait DIFFEREMMENT selon la largeur
   du telephone. Place dans la grille, il ne peut plus la rencontrer.

   Les trois icones partageaient une regle sans largeur ni hauteur — un <i>
   vide en display:block mesure zero. Elles n'ont jamais rien affiche. */
/* ── La barre d'etat ───────────────────────────────────────────────────
   Le releve du 22 aout, depuis le navigateur de Jonas :

     statut{grid | col: 42px 31.9px 54px | wrap:nowrap | enfants:3}

   42 + 32 + 54, plus deux ecarts, dans une barre qui mesure 132 : la grille
   DEBORDAIT, et l'ecran — en `overflow: hidden` — coupait le depassement.

   Plus de grille. Deux elements seulement se partagent la barre, aux deux
   bouts ; l'ilot est hors flux, centre, et assez etroit (40 px sur 158) pour
   ne pouvoir toucher ni l'un ni l'autre :

     heure 12..40   |   ilot 59..99   |   icones 116..146

   Une repartition qui ne depend d'aucune negociation de largeur ne peut pas
   deborder — c'est ce que trois reglages de grille n'avaient pas obtenu. */
.op-tel_statut {
	position: relative;
	z-index: 3;
	/* 30 px, et non 13 : ses deux enfants sont en position absolue, la barre
	   n'a donc aucune hauteur propre et il faut la lui donner. A 13 px —
	   valeur heritee du chassis de 144 px, jamais revue quand il est passe a
	   196 — l'ilot descendait jusqu'a 23 px pendant que l'entete demarrait a
	   13 : « OURPASS » se posait sur l'heure. */
	height: 30px;
	padding: 9px 11px 4px;
	font-size: 11px;
	font-weight: 700;
	color: var(--op-encre);
	font-variant-numeric: tabular-nums;
}

/* Les trois zones sont POSEES, pas reparties. Ni `space-between` ni la
   grille n'ont tenu : l'un et l'autre calculent des largeurs, et un
   calcul qui se trompe de quelques pixels fait toucher l'ilot. Ici chaque
   element a sa place, avec de la marge :

     heure 9..33   |   ilot 47..77   |   icones 94..115     (sur 124)

   14 px de degagement d'un cote, 17 de l'autre. Les valeurs sont calculees
   a chaque changement de largeur, jamais esperees : c'est ce qui distingue
   cette mise en page de celles qui negociaient. */
/* ⚠️ `.op-tel_statut` en tete, imperativement : cette classe sert AUSSI a
   l'heure de l'horodatage, « Aujourd'hui a 21:41 », plus bas dans le fil.
   Sans le prefixe, la regle arrachait cette heure-la de sa phrase pour la
   poser sur celle de la barre d'etat — d'ou les deux heures superposees et
   « Aujourd'hui a » reste sans heure. */
.op-tel_statut .op-tel_horloge {
	position: absolute;
	top: 9px;
	left: 11px;
	letter-spacing: 0.01em;
}


.op-tel_ilot {
	position: absolute;
	top: 8px;
	left: 50%;
	z-index: 4;
	width: 49px;
	height: 15px;
	border-radius: 8px;
	background: #100D18;
	transform: translateX(-50%);
}




.op-tel_icones {
	position: absolute;
	top: 9px;
	right: 11px;
	display: inline-flex;
	align-items: center;
	gap: 3px;
}

/* Le reseau mobile, pas le wifi : c'est par lui que passe un SMS. */
.op-tel_res {
	display: inline-flex;
	align-items: flex-end;
	gap: 1.4px;
	height: 9.5px;
}
.op-tel_res i {
	width: 2px;
	border-radius: 1px;
	background: var(--op-encre);
}
.op-tel_res i:nth-child(1) { height: 3.5px; }
.op-tel_res i:nth-child(2) { height: 5.5px; }
.op-tel_res i:nth-child(3) { height: 7px; }
.op-tel_res i:nth-child(4) { height: 9.5px; }

.op-tel_batt {
	position: relative;
	width: 18px;
	height: 10px;
	border: 1px solid rgba(26,21,35,0.42);
	border-radius: 2.5px;
}
.op-tel_batt i {
	position: absolute;
	top: 1px;
	bottom: 1px;
	left: 1px;
	width: 64%;
	border-radius: 1.5px;
	background: #34C759;
}
/* La borne du haut de la pile. */
.op-tel_batt::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 100%;
	width: 1.5px;
	height: 4px;
	margin-left: 1.2px;
	border-radius: 0 1px 1px 0;
	background: rgba(26,21,35,0.42);
	transform: translateY(-50%);
}

.op-tel_entete {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 6px 12px 8px;
	border-bottom: 1px solid rgba(26,21,35,0.09);
	background: rgba(255,255,255,0.72);
}

.op-tel_avatar {
	flex: 0 0 auto;
	width: 27px; height: 27px;
	border-radius: 50%;
	background: var(--op-degrade);
	color: #FFFFFF;
	font-size: 11px;
	font-weight: 700;
	display: flex;
	align-items: center;
	justify-content: center;
}

.op-tel_exp {
	font-size: 13px;
	font-weight: 700;
	color: var(--op-encre);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.op-tel_fil {
	flex: 1 1 auto;
	/* De l'air sous le dernier message : colle a la barre de saisie, le
	   fil se lisait comme un seul bloc. */
	padding: 12px 11px 17px;
	overflow: hidden;
}

.op-tel_bulle {
	position: relative;
	background: #FFFFFF;
	border-radius: 18px 18px 18px 5px;
	padding: 9px 12px;
	font-size: 13px;
	line-height: 1.5;
	color: var(--op-encre);
	word-break: break-word;
	box-shadow: 0 2px 9px rgba(26,21,35,0.10);
}

.op-apercu_lien {
	/* Entier ou a la ligne, jamais coupe en deux : « ourpass.f / r/... » se
	   lisait comme une adresse tronquee, pas comme un lien. */
	display: inline-block;
	white-space: nowrap;
	color: var(--op-violet);
	text-decoration: underline;
	word-break: break-all;
}

.op-tel_heure {
	display: block;
	margin-top: 6px;
	font-size: 10.5px;
	color: var(--op-gris);
}

.op-tel_saisie {
	flex: 0 0 auto;
	margin: 0 11px 11px;
	padding: 8px 13px;
	border-radius: 15px;
	background: rgba(26,21,35,0.06);
	font-size: 12px;
	color: var(--op-gris);
}

/* Barre de navigation du système. */
.op-tel_saisie::after {
	content: "";
	display: block;
	width: 82px; height: 3px;
	margin: 9px auto -4px;
	border-radius: 2px;
	background: rgba(26,21,35,0.28);
}

/* --------------------------------------------------------------------------
   Ordinateur portable — les pastilles appartiennent à la fenêtre affichée
   à l'écran, pas au châssis : les poser sur le cadre ne correspond à aucun
   appareil réel.
   -------------------------------------------------------------------------- */

.op-pc { width: 348px; max-width: 100%; }

.op-pc_capot {
	padding: 10px 10px 12px;
	border-radius: 12px 12px 4px 4px;
	background: linear-gradient(155deg, #3A3348 0%, #1B1626 55%, #120F1A 100%);
	box-shadow:
		0 16px 34px rgba(26,21,35,0.30),
		inset 0 0 0 1px rgba(255,255,255,0.09);
}

/* Un ecran d'ordinateur est plus LARGE que haut ; un telephone l'inverse.

   J'avais donne 348 px de plancher a celui-ci pour qu'il fasse la meme
   hauteur totale que le telephone. Resultat : un ecran de 348 sur 348, carre,
   qui ne ressemblait plus a un portable. Les deux appareils ne peuvent pas
   avoir la meme hauteur sans que l'un des deux mente sur ce qu'il est.

   265 px pour 348 de large : l'ecran redevient plus large que haut, sans
   tomber d'un coup a la hauteur du seul contenu. Il grandit si le message est
   long, et les deux appareils restent alignes par le bas — ce qui suffit a ce
   que la scene ne penche pas. */
.op-pc_ecran {
	min-height: 265px;
	border-radius: 4px;
	background: #FFFFFF;
	overflow: hidden;
}

/* Barre de titre de la fenêtre, pastilles comprises. */
.op-pc_titre {
	display: flex;
	align-items: center;
	gap: 5px;
	padding: 7px 10px;
	background: #EDE9F3;
	border-bottom: 1px solid rgba(26,21,35,0.08);
}

.op-pc_pastille {
	width: 8px; height: 8px;
	border-radius: 50%;
	flex: 0 0 auto;
}

.op-pc_pastille--r { background: #FF5F57; }
.op-pc_pastille--j { background: #FEBC2E; }
.op-pc_pastille--v { background: #28C840; }

.op-pc_titre span {
	margin-left: 6px;
	font-size: 10.5px;
	font-weight: 600;
	color: var(--op-gris);
}

.op-pc_mail-entete {
	background: var(--op-degrade);
	color: #FFFFFF;
	padding: 11px 13px;
	font-size: 13px;
	letter-spacing: 0.05em;
	display: flex;
	align-items: center;
	gap: 7px;
}

.op-pc_mail-entete strong { font-weight: 800; }

/* Cadenas dessiné en CSS : pas de requête réseau pour une vignette. */
.op-apercu_cadenas {
	position: relative;
	width: 11px; height: 9px;
	border-radius: 2px;
	background: #FFFFFF;
	flex: 0 0 auto;
}

.op-apercu_cadenas::before {
	content: "";
	position: absolute;
	left: 50%; bottom: 100%;
	transform: translateX(-50%);
	width: 7px; height: 6px;
	border: 1.6px solid #FFFFFF;
	border-bottom: 0;
	border-radius: 4px 4px 0 0;
}

.op-pc_mail-corps { padding: 12px 15px 15px; }

.op-pc_mail-corps p {
	margin: 0 0 7px;
	font-size: 11.5px;
	line-height: 1.55;
	color: var(--op-encre);
}

.op-pc_mail-obj {
	font-weight: 700 !important;
	font-size: 12.5px !important;
	margin-bottom: 10px !important;
}

.op-pc_mail-id {
	background: rgba(118,31,227,0.06);
	border-left: 2px solid var(--op-violet);
	border-radius: 0 5px 5px 0;
	padding: 7px 10px !important;
	font-size: 10.5px !important;
	color: var(--op-gris) !important;
}

.op-pc_mail-id b { color: var(--op-encre); font-size: 12px; }

.op-pc_bouton {
	display: inline-block;
	margin: 9px 0 8px;
	padding: 8px 16px;
	border-radius: 7px;
	background: var(--op-degrade);
	color: #FFFFFF;
	font-size: 11px;
	font-weight: 600;
}

.op-pc_mail-note {
	font-size: 10px !important;
	color: var(--op-gris) !important;
	margin: 0 !important;
}

/* Socle plus large que l'écran : c'est ce débord qui fait lire « portable ». */
.op-pc_socle {
	position: relative;
	height: 10px;
	margin: 0 -20px;
	border-radius: 0 0 11px 11px;
	background: linear-gradient(180deg, #2C2539 0%, #16121F 100%);
	box-shadow: 0 6px 14px rgba(26,21,35,0.24);
}

.op-pc_encoche {
	position: absolute;
	top: 0; left: 50%;
	transform: translateX(-50%);
	width: 60px; height: 4px;
	border-radius: 0 0 4px 4px;
	background: rgba(255,255,255,0.13);
}

.op-apercu_dest {
	margin: 13px 0 0 !important;
	text-align: center;
	font-size: 11.5px;
	color: var(--op-gris);
	word-break: break-all;
}

/* --------------------------------------------------------------------------
   Champs de rédaction
   -------------------------------------------------------------------------- */

.op-sms-edit { margin-top: 18px; }
.op-sms-edit[hidden] { display: none !important; }

.op-sms-edit label {
	display: block;
	margin-bottom: 6px;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-sms-edit textarea {
	width: 100%;
	padding: 11px 13px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	font-family: inherit;
	font-size: 13.5px;
	line-height: 1.55;
	color: var(--op-encre);
	background: #FFFFFF;
	resize: vertical;
	transition: border-color 160ms ease, box-shadow 160ms ease;
}

.op-sms-edit textarea:focus {
	outline: none;
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
}

.op-sms-aide {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 12px;
	justify-content: space-between;
	margin: 7px 0 0 !important;
	font-size: 12px;
}

.op-sms-compteur { color: var(--op-gris); font-variant-numeric: tabular-nums; }

/* Au-delà d'un segment l'opérateur facture deux SMS : l'alerte doit être nette. */
.op-sms-compteur.op-sms-depasse { color: #C0392B; font-weight: 600; }

.op-sms-note { color: var(--op-gris); }

/* Signature de l'e-mail : champ court, sous le message. */
.op-label-signature { margin-top: 14px !important; }

.op-sms-edit input[type="text"] {
	width: 100%;
	padding: 10px 13px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	font-family: inherit;
	font-size: 13.5px;
	color: var(--op-encre);
	background: #FFFFFF;
	transition: border-color 160ms ease, box-shadow 160ms ease;
}

.op-sms-edit input[type="text"]:focus {
	outline: none;
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
}

.op-pc_signature {
	margin: 10px 0 8px !important;
	font-size: 11.5px !important;
	color: var(--op-encre) !important;
	font-style: italic;
}

.op-sms-note code {
	background: rgba(118,31,227,0.08);
	color: var(--op-violet);
	padding: 1px 5px;
	border-radius: 4px;
	font-size: 11.5px;
}

@media (max-width: 575px) {
	.op-tel { width: 210px; }
	.op-tel_ecran { height: 372px; }
	.op-pc_socle { margin: 0 -10px; }
}


/* ==========================================================================
   46. Quota atteint, et compteur d'usage
   ========================================================================== */

.op-quota {
	min-height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 24px;
	background: var(--op-degrade);
}

.op-quota_carte {
	max-width: 460px;
	width: 100%;
	background: #FFFFFF;
	border-radius: 16px;
	padding: 34px 30px;
	text-align: center;
	box-shadow: 0 20px 50px rgba(26,21,35,0.28);
}

.op-quota_carte h1 {
	margin: 0 0 12px;
	font-size: 22px;
	font-weight: 800;
	color: var(--op-encre);
}

.op-quota_carte p {
	margin: 0 0 14px;
	font-size: 14.5px;
	line-height: 1.6;
	color: var(--op-gris);
}

.op-quota_detail {
	font-size: 13px !important;
	font-weight: 600;
	color: var(--op-violet) !important;
}

.op-quota_lien {
	display: inline-block;
	margin: 6px 4px 0;
	padding: 10px 20px;
	border-radius: 9px;
	border: 1px solid var(--op-bordure);
	color: var(--op-encre);
	font-size: 14px;
	font-weight: 600;
	text-decoration: none;
}

.op-quota_lien--fort {
	background: var(--op-degrade);
	border-color: transparent;
	color: #FFFFFF;
}

/* Compteur d'usage affiché dans l'application. */
/* Le bandeau de consommation est defini une seule fois, plus bas. */


/* ==========================================================================
   47. Page de supervision (rôle super_admin)
   ========================================================================== */

.op-page-supervision { background: #F6F4FA; }

.op-sup {
	max-width: 1180px;
	margin: 0 auto;
	padding: 26px 20px 60px;
}

.op-sup_entete {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 22px;
}

.op-sup_entete h1 {
	margin: 0;
	font-size: 21px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-sup_entete nav a {
	margin-left: 14px;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--op-violet);
	text-decoration: none;
}

.op-sup_entete nav a:hover { text-decoration: underline; }

.op-sup_avis {
	margin: 0 0 16px;
	padding: 11px 15px;
	border-radius: 9px;
	font-size: 13.5px;
}

.op-sup_avis--ok { background: #EDF9F0; border: 1px solid #A9DDB7; color: #1E7A3C; }
.op-sup_avis--ko { background: #FFF5F3; border: 1px solid #E0A79E; color: #C0392B; }

.op-sup_chiffres {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: 14px;
	margin-bottom: 8px;
}

.op-sup_chiffre {
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	border-radius: 12px;
	padding: 18px 20px;
}

.op-sup_valeur {
	display: block;
	font-size: 26px;
	font-weight: 800;
	color: var(--op-violet);
	line-height: 1.15;
}

.op-sup_label {
	display: block;
	margin-top: 4px;
	font-size: 12.5px;
	color: var(--op-gris);
}

.op-sup_note {
	margin: 8px 0 22px;
	font-size: 11.5px;
	color: var(--op-gris);
}

/* Le tableau doit défiler dans son cadre : la page ne défile jamais latéralement. */
.op-sup_table {
	width: 100%;
	border-collapse: collapse;
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	border-radius: 12px;
	overflow: hidden;
	font-size: 13px;
}

.op-sup_table th,
.op-sup_table td {
	padding: 11px 13px;
	text-align: left;
	border-bottom: 1px solid var(--op-bordure);
	vertical-align: middle;
}

.op-sup_table th {
	background: #FAF8FF;
	font-size: 11.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--op-gris);
}

.op-sup_table tr:last-child td { border-bottom: 0; }

.op-sup_ligne--tendu { background: #FFFBF3; }
.op-sup_ligne--plein { background: #FFF5F3; }

.op-sup_role {
	display: block;
	font-size: 11px;
	color: var(--op-gris);
}

.op-sup_etat {
	display: inline-block;
	padding: 3px 9px;
	border-radius: 20px;
	font-size: 11.5px;
	font-weight: 600;
	white-space: nowrap;
}

.op-sup_etat--ok    { background: #EDF9F0; color: #1E7A3C; }
.op-sup_etat--tendu { background: #FFF4E3; color: #9A6414; }
.op-sup_etat--ko    { background: #FFECE8; color: #C0392B; }

.op-sup_form { display: flex; gap: 6px; align-items: center; }

.op-sup_form select {
	padding: 6px 9px;
	border: 1px solid var(--op-bordure);
	border-radius: 7px;
	font-size: 12.5px;
	background: #FFFFFF;
	color: var(--op-encre);
}

.op-sup_form button {
	padding: 6px 13px;
	border: none;
	border-radius: 7px;
	background: var(--op-degrade);
	color: #FFFFFF;
	font-size: 12.5px;
	font-weight: 600;
	cursor: pointer;
}

@media (max-width: 900px) {
	.op-sup_table { display: block; overflow-x: auto; white-space: nowrap; }
}


/* ==========================================================================
   48. Parcours de compte — inscription, confirmation, mot de passe oublié
   ========================================================================== */

.op-page-compte { background: var(--op-degrade); }

/* ⚠️ `box-sizing: border-box` — CETTE FEUILLE N'EN A AUCUN DE GLOBAL.
 *
 * Les pages de compte (`depot.php`, `validate.php`, `confirmer.php`…) ne
 * chargent QUE cette feuille : pas de Bootstrap, donc pas la remise a zero
 * qui pose `border-box` partout ailleurs. Toute boite qui combine
 * `width: 100%` et un remplissage deborde donc de son parent.
 *
 * Mesure sur un ecran de 375 px :
 *     .op-compte_carte  max-width 460 + 2 x 34 de remplissage
 *     -> 407 px rendus pour 339 disponibles : 68 px hors ecran.
 *     .op-compte_carte input[type=password]  width 100% + 2 x 14
 *     -> 28 px de plus que sa carte.
 *
 * C'est le troisieme defaut du meme genre dans ce projet. La regle est posee
 * ici une fois pour toutes, sur la branche entiere. */
.op-compte,
.op-compte *,
.op-compte *::before,
.op-compte *::after { box-sizing: border-box; }

.op-compte {
	min-height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 32px 18px;
}

.op-compte_carte {
	width: 100%;
	max-width: 460px;
	background: #FFFFFF;
	border-radius: 18px;
	padding: 36px 34px 30px;
	box-shadow: 0 22px 54px rgba(26,21,35,0.30);
}

.op-compte_logo {
	display: inline-block;
	margin-bottom: 20px;
	font-size: 19px;
	letter-spacing: 0.06em;
	color: var(--op-violet);
	text-decoration: none;
}

.op-compte_carte h1 {
	margin: 0 0 10px;
	font-size: 23px;
	font-weight: 800;
	color: var(--op-encre);
}

.op-compte_intro {
	margin: 0 0 22px;
	font-size: 14.5px;
	line-height: 1.6;
	color: var(--op-gris);
}

.op-compte_note {
	margin: -10px 0 22px;
	font-size: 12.5px;
	line-height: 1.55;
	color: var(--op-gris);
}

.op-compte_carte label {
	display: block;
	margin: 16px 0 6px;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-compte_carte input[type="email"],
.op-compte_carte input[type="text"],
.op-compte_carte input[type="password"] {
	width: 100%;
	padding: 12px 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	font-family: inherit;
	font-size: 14.5px;
	color: var(--op-encre);
	background: #FFFFFF;
	transition: border-color 160ms ease, box-shadow 160ms ease;
}

.op-compte_carte input:focus {
	outline: none;
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
}

.op-compte_aide {
	display: block;
	margin-top: 5px;
	font-size: 11.5px;
	line-height: 1.5;
	color: var(--op-gris);
}

.op-compte_bouton {
	display: inline-block;
	width: 100%;
	margin-top: 22px;
	padding: 13px 24px;
	border: none;
	border-radius: 10px;
	background: var(--op-degrade);
	color: #FFFFFF;
	font-family: inherit;
	font-size: 15px;
	font-weight: 600;
	text-align: center;
	text-decoration: none;
	cursor: pointer;
	box-shadow: 0 4px 14px rgba(118,31,227,0.26);
}

.op-compte_erreurs {
	margin: 0 0 18px;
	padding: 12px 16px 12px 32px;
	border: 1px solid #E0A79E;
	border-radius: 10px;
	background: #FFF5F3;
	color: #C0392B;
	font-size: 13px;
	line-height: 1.55;
}

.op-compte_pied {
	margin: 18px 0 0;
	text-align: center;
	font-size: 13px;
	color: var(--op-gris);
}

.op-compte_pied a,
.op-login_liens a {
	color: var(--op-violet);
	font-weight: 600;
	text-decoration: none;
}

.op-compte_pied a:hover,
.op-login_liens a:hover { text-decoration: underline; }

.op-login_liens {
	margin: 14px 0 0;
	font-size: 13px;
	color: var(--op-gris);
}

.op-login_liens span { margin: 0 6px; opacity: 0.5; }

/* Sceau de résultat : coche ou croix, dessiné sans image. */
.op-compte_sceau {
	position: relative;
	width: 58px;
	height: 58px;
	margin: 0 0 18px;
	border-radius: 50%;
}

.op-compte_sceau--ok { background: #EDF9F0; border: 2px solid #4BAF6A; }
.op-compte_sceau--ko { background: #FFF5F3; border: 2px solid #D9736A; }

.op-compte_sceau::before,
.op-compte_sceau::after {
	content: "";
	position: absolute;
	border-radius: 2px;
}

.op-compte_sceau--ok::before {
	width: 8px; height: 18px;
	border-right: 3px solid #4BAF6A;
	border-bottom: 3px solid #4BAF6A;
	top: 16px; left: 23px;
	transform: rotate(45deg);
	border-radius: 0;
}

.op-compte_sceau--ko::before,
.op-compte_sceau--ko::after {
	width: 26px; height: 3px;
	background: #D9736A;
	top: 25px; left: 14px;
}

.op-compte_sceau--ko::before { transform: rotate(45deg); }
.op-compte_sceau--ko::after  { transform: rotate(-45deg); }

/* ── Le dépôt : le feuillet entre dans la fente ──────────────────────────
 *
 * ⚠️ RESERVE A `depot.php`. Quinze pages posent un `op-compte_sceau` ; celle-ci
 * est la seule a raconter un DEPOT. Animer le sceau commun ferait bouger la
 * confirmation d'inscription, celle du mot de passe oublie et celle du
 * paiement — trois pages que personne n'a demande de toucher.
 *
 * Le geste : un feuillet glisse d'en haut, disparait dans la fente, la boite
 * encaisse, la coche se trace. Une coche qui apparait dit « c'est bon » ; elle
 * ne dit pas ce qui vient de se passer.
 *
 * ⚠️ LE FEUILLET DOIT PASSER ENTIEREMENT SOUS LA LIGNE DE DECOUPE. Il mesure
 * 26 px et part de `y=2`, la decoupe tombe a `y=34` : il lui faut donc 34 px de
 * descente pour que son bord SUPERIEUR passe dessous. Avec 16, la moitie du
 * feuillet restait plantee dans la fente — et un depot a moitie avale se lit
 * comme un depot rate. */
.op-depot_sceau { width: 58px; height: 58px; margin: 0 auto 18px; }
.op-depot_sceau svg { display: block; width: 58px; height: 58px; }

.op-depot_sceau .op-depot_trace {
	fill: none; stroke: #4BAF6A; stroke-width: 2.6;
	stroke-linecap: round; stroke-linejoin: round;
}
.op-depot_sceau .op-depot_plein   { fill: #EDF9F0; }
.op-depot_sceau .op-depot_feuille { fill: #FFFFFF; stroke: #4BAF6A; stroke-width: 2.2; }
.op-depot_sceau .op-depot_ligne   { stroke: #9BD3AC; stroke-width: 2; stroke-linecap: round; }

/* La coche part masquee : `stroke-dashoffset` egal a la longueur du trace. */
.op-depot_sceau .op-depot_coche {
	fill: none; stroke: #4BAF6A; stroke-width: 3;
	stroke-linecap: round; stroke-linejoin: round;
	stroke-dasharray: 26; stroke-dashoffset: 26;
}

/* `transform-box: fill-box` : sans lui, l'origine d'un `transform` en SVG est
   celle du repere entier, et la boite se mettrait a l'echelle depuis le coin
   haut-gauche du dessin au lieu de son propre centre. */
.op-depot_sceau .op-depot_boite { transform-box: fill-box; transform-origin: center; }

.op-depot_sceau .op-depot_paquet { animation: op-depot-glisser 580ms cubic-bezier(0.5, 0, 0.55, 1) 100ms both; }
.op-depot_sceau .op-depot_fente  { animation: op-depot-clin 200ms ease-out 560ms; }
.op-depot_sceau .op-depot_boite  { animation: op-depot-encaisser 280ms ease-out 580ms; }
.op-depot_sceau .op-depot_coche  { animation: op-depot-tracer 300ms ease-out 720ms both; }

@keyframes op-depot-glisser {
	from { transform: translateY(-28px); opacity: 0; }
	18%  { opacity: 1; }
	to   { transform: translateY(34px); opacity: 1; }
}

@keyframes op-depot-clin { 50% { stroke-width: 5; } }

/* La boite encaisse : sans ce creux, le feuillet tombe dans un decor qui
   l'ignore, et rien ne dit qu'il est arrive quelque part. */
@keyframes op-depot-encaisser {
	0%, 100% { transform: scale(1, 1); }
	45%      { transform: scale(1.05, 0.93); }
}

@keyframes op-depot-tracer { to { stroke-dashoffset: 0; } }

/* Une animation qu'on ne peut pas refuser est une animation de trop.
   ⚠️ L'ETAT FINAL DOIT RESTER VISIBLE : retirer les animations sans le poser
   laisserait une coche invisible et un feuillet en travers de la fente. */
@media (prefers-reduced-motion: reduce) {
	.op-depot_sceau .op-depot_paquet,
	.op-depot_sceau .op-depot_fente,
	.op-depot_sceau .op-depot_boite,
	.op-depot_sceau .op-depot_coche { animation: none; }

	.op-depot_sceau .op-depot_paquet { transform: translateY(34px); }
	.op-depot_sceau .op-depot_coche  { stroke-dashoffset: 0; }
}

@media (max-width: 480px) {
	.op-compte_carte { padding: 28px 22px 24px; }
}


/* ==========================================================================
   49. Souscription d'abonnement
   ========================================================================== */

.op-page-abo { background: #F6F4FA; }

.op-abo {
	max-width: 1080px;
	margin: 0 auto;
	padding: 26px 20px 60px;
}

.op-abo_entete {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 26px;
}

.op-abo_entete nav a {
	margin-left: 14px;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--op-violet);
	text-decoration: none;
}

.op-abo h1 {
	margin: 0 0 10px;
	font-size: 25px;
	font-weight: 800;
	color: var(--op-encre);
}

.op-abo_intro {
	margin: 0 0 24px;
	max-width: 720px;
	font-size: 14.5px;
	line-height: 1.6;
	color: var(--op-gris);
}

.op-abo_avis {
	margin: 0 0 18px;
	padding: 11px 15px;
	border: 1px solid #E5B96A;
	border-radius: 9px;
	background: #FFFBF3;
	font-size: 13.5px;
	color: #9A6414;
}

.op-abo_offres {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	gap: 18px;
	align-items: start;
}

.op-abo_offre {
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	border-radius: 14px;
	padding: 24px 22px 26px;
}

/* L'offre en cours doit se distinguer sans effort de lecture. */
.op-abo_offre--courante {
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.10);
}

.op-abo_offre h2 {
	margin: 0 0 8px;
	font-size: 17px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-abo_prix {
	margin: 0 0 6px;
	font-size: 28px;
	font-weight: 800;
	color: var(--op-violet);
	line-height: 1.15;
}

.op-abo_prix span {
	display: block;
	font-size: 12px;
	font-weight: 500;
	color: var(--op-gris);
}

.op-abo_resume {
	margin: 0 0 14px;
	font-size: 13px;
	line-height: 1.55;
	color: var(--op-gris);
}

.op-abo_liste {
	margin: 0 0 18px;
	padding: 0;
	list-style: none;
}

.op-abo_liste li {
	position: relative;
	padding: 4px 0 4px 20px;
	font-size: 13px;
	line-height: 1.5;
	color: var(--op-encre);
}

.op-abo_liste li::before {
	content: "";
	position: absolute;
	left: 2px; top: 11px;
	width: 5px; height: 9px;
	border-right: 2px solid var(--op-violet);
	border-bottom: 2px solid var(--op-violet);
	transform: rotate(45deg);
}

.op-abo_offre label {
	display: block;
	margin: 4px 0 6px;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-abo_offre input[type="number"] {
	width: 100%;
	padding: 11px 13px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	font-family: inherit;
	font-size: 14.5px;
	color: var(--op-encre);
}

.op-abo_offre input[type="number"]:focus {
	outline: none;
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
}

.op-abo_courante {
	margin: 0;
	padding: 11px 14px;
	border-radius: 9px;
	background: rgba(118,31,227,0.07);
	color: var(--op-violet);
	font-size: 13px;
	font-weight: 600;
	text-align: center;
}

.op-abo_note {
	margin: 26px 0 0;
	font-size: 12px;
	color: var(--op-gris);
}


/* ==========================================================================
   50. Choix de l'offre à l'inscription
   ========================================================================== */

.op-choix-offre {
	margin: 0 0 6px;
	padding: 0;
	border: 0;
}

.op-choix-offre legend {
	padding: 0;
	margin-bottom: 8px;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-choix-offre_item {
	display: flex !important;
	align-items: flex-start;
	gap: 10px;
	margin: 0 0 8px !important;
	padding: 12px 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 11px;
	cursor: pointer;
	font-weight: 400 !important;
	transition: border-color 150ms ease, background-color 150ms ease;
}

.op-choix-offre_item:hover { border-color: var(--op-violet); }

/* L'offre retenue doit se voir d'un coup d'œil, pas seulement au bouton radio. */
.op-choix-offre_item:has(input:checked) {
	border-color: var(--op-violet);
	background: rgba(118,31,227,0.05);
	box-shadow: 0 0 0 2px rgba(118,31,227,0.10);
}

.op-choix-offre_item input { margin-top: 3px; flex: 0 0 auto; accent-color: var(--op-violet); }

.op-choix-offre_corps { display: block; }

.op-choix-offre_nom {
	display: block;
	font-size: 14px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-choix-offre_prix {
	display: block;
	margin-top: 1px;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-violet);
}

.op-choix-offre_note {
	display: block;
	margin-top: 3px;
	font-size: 12px;
	line-height: 1.45;
	color: var(--op-gris);
}

#bloc-sieges[hidden] { display: none !important; }

#bloc-sieges input[type="number"] {
	width: 100%;
	padding: 12px 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	font-family: inherit;
	font-size: 14.5px;
	color: var(--op-encre);
}

#bloc-sieges input[type="number"]:focus {
	outline: none;
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
}

.op-abo_avis--attente {
	border-color: var(--op-violet);
	background: rgba(118,31,227,0.06);
	color: var(--op-violet);
}


/* ==========================================================================
   51. Envoi en masse — saisie directe des destinataires
   ========================================================================== */

.op-lot {
	margin-top: 34px;
	padding-top: 28px;
	border-top: 1px solid var(--op-bordure);
}

.op-lot_titre {
	margin: 0 0 6px !important;
	font-size: 17px !important;
	font-weight: 700;
	color: var(--op-encre) !important;
}

.op-lot_intro {
	margin: 0 0 18px !important;
	font-size: 13.5px;
	line-height: 1.55;
	color: var(--op-gris) !important;
}

.op-lot_avis {
	margin: 0 0 14px;
	padding: 11px 15px;
	border-radius: 9px;
	font-size: 13.5px;
	line-height: 1.5;
}

.op-lot_avis--ok { background: #EDF9F0; border: 1px solid #A9DDB7; color: #1E7A3C; }
.op-lot_avis--ko { background: #FFF5F3; border: 1px solid #E0A79E; color: #C0392B; }

.op-lot_rejets {
	margin: 0 0 16px;
	padding: 11px 15px 11px 32px;
	border: 1px solid #E5B96A;
	border-radius: 9px;
	background: #FFFBF3;
	font-size: 12.5px;
	line-height: 1.55;
	color: #9A6414;
}

.op-lot_reglages {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
	gap: 14px;
	margin-bottom: 18px;
}

.op-lot label {
	display: block;
	margin-bottom: 5px;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-lot input[type="text"],
.op-lot input[type="email"],
.op-lot input[type="tel"],
.op-lot select {
	width: 100%;
	padding: 10px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	font-family: inherit;
	font-size: 13.5px;
	color: var(--op-encre);
	background: #FFFFFF;
	box-shadow: none;
	margin: 0;
}

.op-lot input:focus,
.op-lot select:focus {
	outline: none;
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
}

/* Destinataire et mot de passe sur une seule ligne : c'est la même personne. */
.op-lot_ligne {
	display: grid;
	grid-template-columns: 1fr 1fr 34px;
	gap: 8px;
	align-items: center;
	margin-bottom: 8px;
}

.op-lot_retirer {
	width: 34px;
	height: 38px;
	padding: 0;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	background: #FFFFFF;
	color: var(--op-gris);
	font-size: 19px;
	line-height: 1;
	cursor: pointer;
	transition: border-color 150ms ease, color 150ms ease;
}

.op-lot_retirer:hover { border-color: #D9736A; color: #C0392B; }
.op-lot_retirer[hidden] { visibility: hidden; display: block !important; }

.op-lot_actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px 16px;
	align-items: center;
	margin: 4px 0 20px;
}

.op-lot_ajouter {
	padding: 9px 16px;
	border: 1px dashed var(--op-violet);
	border-radius: 9px;
	background: rgba(118,31,227,0.05);
	color: var(--op-violet);
	font-family: inherit;
	font-size: 13.5px;
	font-weight: 600;
	cursor: pointer;
	transition: background-color 150ms ease;
}

.op-lot_ajouter:hover { background: rgba(118,31,227,0.11); }

.op-lot_ajouter:disabled {
	border-style: solid;
	border-color: var(--op-bordure);
	background: transparent;
	color: var(--op-gris);
	cursor: not-allowed;
}

.op-lot_compteur { font-size: 12.5px; color: var(--op-gris); }

@media (max-width: 575px) {
	.op-lot_ligne { grid-template-columns: 1fr 34px; }
	.op-lot_ligne input[name="lot_password[]"] { grid-column: 1 / 2; }
}


/* ==========================================================================
   52. Envoi en masse — les deux zones côte à côte
   Empilées, la saisie directe passait sous la ligne de flottaison et
   personne ne la voyait.
   ========================================================================== */

.op-masse {
	display: grid;
	grid-template-columns: 1fr;
	gap: 20px;
	align-items: start;
}

@media (min-width: 992px) {
	.op-masse { grid-template-columns: 1fr 1fr; gap: 24px; }
}

.op-masse_col {
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	border-radius: 14px;
	padding: 24px 22px 26px;
}

/* La bordure supérieure de la section n'a plus de sens dans une carte. */
.op-lot.op-masse_col {
	margin-top: 0;
	padding-top: 24px;
	border-top: 1px solid var(--op-bordure);
}

.op-masse_modele {
	display: inline-block;
	margin-bottom: 16px;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--op-violet);
	text-decoration: none;
}

.op-masse_modele::before {
	content: "↓";
	margin-right: 6px;
	font-weight: 700;
}

.op-masse_modele:hover { text-decoration: underline; }

/* La zone de dépôt occupe la hauteur disponible de sa colonne. */
.op-masse_col .op-drop { min-height: 190px; }

/* Les lignes de destinataires sont numérotées : on sait combien on en a saisi
   sans les compter, et un rejet se retrouve immédiatement. */
.op-lot_lignes { counter-reset: op-dest; }

.op-lot_ligne {
	position: relative;
	counter-increment: op-dest;
	padding-left: 26px;
}

.op-lot_ligne::before {
	content: counter(op-dest);
	position: absolute;
	left: 0;
	top: 50%;
	transform: translateY(-50%);
	width: 19px;
	text-align: right;
	font-size: 11.5px;
	font-variant-numeric: tabular-nums;
	color: var(--op-gris);
}

/* En colonne étroite, destinataire et mot de passe se superposent. */
@media (min-width: 992px) and (max-width: 1300px) {
	.op-lot_ligne {
		grid-template-columns: 1fr 34px;
		row-gap: 6px;
	}
	.op-lot_ligne input[name="lot_password[]"] { grid-column: 1 / 2; }
}

.op-masse_col .btn.btn-primary { width: 100%; margin-top: 4px; }


/* ==========================================================================
   53. Volet droit : rendre le débordement accessible
   Le gabarit donne au volet une hauteur fixe (.row-height = 100vh) et centre
   son contenu par align-items:center. Dès que le contenu dépasse cette
   hauteur, ce centrage place la partie haute AU-DESSUS de la zone
   défilable : elle devient inatteignable. L'ajout du bandeau de quota a
   suffi à faire basculer la page dans ce cas.
   margin:auto centre exactement pareil quand il y a de la place, mais laisse
   le débordement accessible quand il n'y en a plus.
   ========================================================================== */

@media (min-width: 992px) {
	.content-right {
		align-items: flex-start !important;
		padding-top: 40px;
		padding-bottom: 40px;
	}

	.content-right > * {
		margin-top: auto !important;
		margin-bottom: auto !important;
		width: 100%;
	}
}

/* Le bandeau de quota doit coûter le moins de hauteur possible : c'est une
   information de contexte, pas le sujet de la page. */


/* ==========================================================================
   54. Envoi en masse — corrections de mise en page
   ========================================================================== */

/* Le conteneur de ces pages est plafonné à 900 px (section 32). Deux colonnes
   n'y tiennent pas confortablement : on l'élargit là, et uniquement là. */
body:has(.op-masse) #general_page > .container { max-width: 1180px; }

/* La bascule à deux colonnes dépendait de la largeur de la FENÊTRE alors que
   la largeur utile est celle du conteneur, plafonnée. auto-fit ne forme une
   seconde colonne que si la place existe réellement. */
.op-masse {
	grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)) !important;
}

/* Les formulaires se lisent alignés à gauche ; le centrage venait d'un parent. */
.op-masse_col,
.op-masse_col h3,
.op-masse_col p,
.op-masse_col label { text-align: left !important; }

/* Le gabarit impose son propre rendu aux <label>, ce qui remettait la zone de
   dépôt en ligne et en capitales. */
.op-drop {
	display: flex !important;
	text-transform: none !important;
	letter-spacing: normal !important;
}

.op-drop_titre,
.op-drop_sous {
	display: block !important;
	width: 100%;
	text-align: center !important;
	text-transform: none !important;
	letter-spacing: normal !important;
	font-family: inherit !important;
}

/* Les réglages du lot n'ont pas à former une carte dans la carte. */
.op-masse_col .form-group {
	background: transparent !important;
	border: 0 !important;
	box-shadow: none !important;
	padding: 0 !important;
}


/* ==========================================================================
   55. Bandeau de consommation — une seule ligne, alignée
   Trois phrases empilées occupaient trois lignes pour une information de
   contexte. Étiquettes courtes, chiffres alignés, séparateurs discrets.
   ========================================================================== */

/* ── Bandeau de consommation ────────────────────────────────────────────

   Il existait en TROIS exemplaires dans cette feuille — lignes 4061, 4962 et
   5025 — chacun corrigeant le precedent a coups de !important. Marges,
   remplissages et bordures s'y additionnaient, et le contenu ne tenait plus
   dans son cadre.

   Une seule regle desormais. Les ecarts viennent de `gap`, jamais de
   remplissages qui s'ajoutent a la largeur, et le bandeau se replie
   proprement quand la place manque au lieu de deborder. */

.op-usage {
	display: flex;
	/* UNE ligne : le palier a gauche, les deux compteurs a droite. Le repli
	   automatique etait la pour loger la jauge et le restant ; ils sont
	   passes au survol, il n'a plus de raison d'etre et faisait un pave de
	   trois lignes pour annoncer deux chiffres. */
	flex-wrap: nowrap;
	align-items: center;
	justify-content: space-between;
	gap: 6px 14px;
	box-sizing: border-box;
	max-width: 100%;
	margin: 0 0 14px;
	padding: 9px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: #FFFFFF;
	font-size: 12px;
	line-height: 1.35;
	color: var(--op-gris);
}

.op-usage_palier {
	/* Le badge tient sur la MEME ligne que les deux compteurs. Il occupait
	   la sienne parce que la jauge et le restant, alors affiches en
	   permanence, ne laissaient pas la place ; ces deux-la sont passes au
	   survol, et les trois elements tiennent desormais ensemble. */
	flex: 0 0 auto;
	padding: 3px 10px;
	border-radius: 20px;
	background: rgba(118,31,227,0.09);
	color: var(--op-violet);
	font-size: 11.5px;
	font-weight: 700;
	white-space: nowrap;
}

/* Les deux compteurs partagent une ligne, separes par un trait.
   `nowrap` les y tient : c'est ce qui rend le separateur sur — une bordure
   gauche ne sait pas qu'elle vient de passer a la ligne, et se retrouvait
   sinon orpheline en tete de la suivante. */
.op-usage_chiffres {
	display: inline-flex;
	flex-wrap: nowrap;
	align-items: baseline;
	min-width: 0;
}

/* Sous 420 px, une seule ligne deborderait : la, et la seulement, le
   bandeau se replie. */
@media (max-width: 420px) {
	.op-usage { flex-wrap: wrap; justify-content: center; }
	.op-usage_palier { flex: 0 0 100%; text-align: center; }
}

.op-usage_stat {
	position: relative;
	display: inline-flex;
	align-items: baseline;
	gap: 5px;
	white-space: nowrap;
	cursor: default;
}
.op-usage_stat:focus-visible { outline: 2px solid var(--op-violet); outline-offset: 3px; border-radius: 4px; }

/* L'infobulle du restant. Elle sort du bandeau : `overflow` doit rester
   visible sur les parents, sinon elle serait rognee au lieu de flotter. */
.op-usage_bulle {
	position: absolute;
	top: calc(100% + 8px);
	left: 50%;
	z-index: 30;
	display: flex;
	flex-direction: column;
	gap: 5px;
	min-width: 150px;
	padding: 8px 11px;
	border-radius: 8px;
	background: var(--op-encre);
	box-shadow: 0 8px 22px rgba(26,21,35,0.26);
	color: #FFFFFF;
	white-space: nowrap;
	opacity: 0;
	visibility: hidden;
	transform: translate(-50%, -4px);
	transition: opacity .14s, transform .14s, visibility .14s;
}
.op-usage_stat:hover .op-usage_bulle,
.op-usage_stat:focus-within .op-usage_bulle,
.op-usage_stat:focus .op-usage_bulle {
	opacity: 1;
	visibility: visible;
	transform: translate(-50%, 0);
}
.op-usage_bulle::before {
	content: '';
	position: absolute;
	bottom: 100%;
	left: 50%;
	margin-left: -5px;
	border: 5px solid transparent;
	border-bottom-color: var(--op-encre);
}

@media (prefers-reduced-motion: reduce) {
	.op-usage_bulle { transition: none; }
}

/* Ces deux-la doivent battre `.op-usage_stat em`, de meme poids et ecrit
   plus bas : on nomme les deux ancetres. Sans cela le texte de l'infobulle
   restait gris sur fond sombre — illisible, comme sur la capture. */
.op-usage_stat .op-usage_bulle em {
	font-size: 11.5px;
	color: #FFFFFF;
	opacity: 1;
}
.op-usage_stat .op-usage_bulle .op-usage_jauge { background: rgba(255,255,255,0.26); }

/* Le trait qui separe les deux compteurs. */
.op-usage_stat + .op-usage_stat {
	margin-left: 16px;
	padding-left: 16px;
	border-left: 1px solid var(--op-bordure);
}

.op-usage_stat strong {
	color: var(--op-encre);
	font-weight: 800;
	font-size: 15px;
	letter-spacing: -0.01em;
	font-variant-numeric: tabular-nums;
}

.op-usage_stat em {
	font-style: normal;
	font-size: 11px;
	opacity: 0.72;
}

.op-usage_lien {
	flex: 0 0 auto;
	padding: 4px 11px;
	border-radius: 7px;
	background: var(--op-degrade);
	color: #FFFFFF !important;
	font-size: 11.5px;
	font-weight: 600;
	text-decoration: none;
	white-space: nowrap;
}

/* Proche du plafond : l'utilisateur doit le voir avant d'etre bloque. */
.op-usage--tendu { border-color: #E5B96A; background: #FFFBF3; }
.op-usage--plein { border-color: #E0A79E; background: #FFF5F3; }
.op-usage--plein .op-usage_stat strong { color: #C0392B; }

/* Etroit : l'ecart se resserre, et le bouton passe a la ligne plutot que de
   comprimer les chiffres.

   Un quatrieme bloc, herite des anciennes duplications, remettait ici un
   remplissage sur .op-usage_stat et neutralisait un separateur qui n'existe
   plus. Il est fondu dans cette regle. */
@media (max-width: 620px) {
	.op-usage { gap: 5px 14px; }
}

/* Trop etroit pour les tenir cote a cote : ils s'empilent, et le trait
   s'efface avec eux plutot que de flotter en tete de ligne. */
@media (max-width: 430px) {
	.op-usage_chiffres { flex-wrap: wrap; gap: 3px 0; }
	.op-usage_stat + .op-usage_stat {
		margin-left: 0;
		padding-left: 0;
		border-left: none;
	}
}


/* ==========================================================================
   56. Import en masse — tout doit tenir sans défiler
   Le <form> était lui-même stylé en carte de 620 px centrée (section 36) :
   dans une colonne, cela faisait une carte dans la carte, décalée.
   ========================================================================== */

/* Le bandeau haut prend moins de place sur ces deux pages. */
/* ⚠️ La hauteur, et pas seulement les marges.
 *
 * Le bandeau se reduisait par un effet de bord : une regle voisine vise
 * `body:has(input[type="file"])`, et la page d'envoi en masse portait un champ
 * de depot CSV. Le jour ou le mode « lien a copier » a masque ce depot, le
 * `min-height: 100vh` general a repris la main — le bandeau a occupe tout
 * l'ecran et le formulaire est passe sous la ligne de flottaison, sans qu'une
 * seule regle de mise en page ait bouge.
 *
 * Ces pages se designent desormais elles-memes, quel que soit leur contenu. */
body:has(.op-masse) .parallax_window_in { min-height: auto !important; }
body:has(.op-masse) .parallax_window_in { padding: 92px 20px 26px !important; }
body:has(.op-masse) #sub_content_in { padding: 18px 26px !important; }
body:has(.op-masse) #sub_content_in h1 { margin-bottom: 4px !important; }
body:has(.op-masse) #sub_content_in p { margin-bottom: 0 !important; font-size: 13.5px !important; }
body:has(.op-masse) #general_page { padding-bottom: 24px !important; }

/* La colonne EST la carte : le formulaire qu'elle contient n'en est plus une. */
.op-masse_col form {
	max-width: none !important;
	margin: 0 !important;
	padding: 0 !important;
	border: 0 !important;
	border-radius: 0 !important;
	background: transparent !important;
	box-shadow: none !important;
}

.op-masse_col { padding: 18px 18px 20px !important; }
.op-masse_col .form-group { margin-bottom: 12px !important; }
.op-masse_col .op-drop { min-height: 0; padding: 22px 18px !important; margin-bottom: 12px !important; }
.op-masse_col .op-lot_titre { margin-bottom: 4px !important; font-size: 16px !important; }
.op-masse_col .op-lot_intro { margin-bottom: 12px !important; font-size: 12.5px; }
.op-lot_reglages { gap: 10px !important; margin-bottom: 12px !important; }
.op-lot_ligne { margin-bottom: 6px !important; }
.op-lot_actions { margin: 2px 0 12px !important; }

/* Au-delà de quelques destinataires, la liste défile dans son cadre plutôt
   que d'allonger la page indéfiniment. */
.op-lot_lignes {
	max-height: 210px;
	overflow-y: auto;
	padding-right: 4px;
}

.op-lot_lignes::-webkit-scrollbar { width: 6px; }
.op-lot_lignes::-webkit-scrollbar-thumb {
	background: rgba(118,31,227,0.28);
	border-radius: 3px;
}


/* ==========================================================================
   57. Envoi en masse — disposition en flex plutôt qu'en grille
   Avec la grille, les deux cartes se retrouvaient en (ligne 1, colonne 2) et
   (ligne 2, colonne 1) : une case fantôme occupait la première position.
   Le flex place les éléments dans l'ordre du document, sans placement
   automatique en cases : le décalage devient impossible.
   ========================================================================== */

.op-masse {
	display: flex !important;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: 20px;
	grid-template-columns: none !important;
}

.op-masse > .op-masse_col {
	flex: 1 1 340px;
	min-width: 0;   /* sans quoi un champ large empêche la colonne de rétrécir */
	margin: 0 !important;
}

/* L'ordre est imposé explicitement : le fichier d'abord, la saisie ensuite. */
.op-masse > .op-masse_col:first-child { order: 1; }
.op-masse > .op-lot.op-masse_col      { order: 2; }


/* ==========================================================================
   58. Envoi en masse — parité avec « Nouveau lien »
   Identifiant en saisie libre, génération de mot de passe, message et
   signature : la saisie directe offre les mêmes possibilités que l'envoi
   unitaire.
   ========================================================================== */

.op-lot_texte { margin-bottom: 12px; }

.op-lot_texte textarea {
	width: 100%;
	padding: 10px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	font-family: inherit;
	font-size: 13.5px;
	line-height: 1.5;
	color: var(--op-encre);
	background: #FFFFFF;
	resize: vertical;
}

.op-lot_texte textarea:focus,
.op-lot_texte input:focus {
	outline: none;
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
}

.op-lot_texte label { margin-top: 10px; }
.op-lot_texte label:first-child { margin-top: 0; }

/* Trois colonnes : destinataire, mot de passe, puis les deux boutons. */
.op-lot_ligne { grid-template-columns: 1fr 1fr 32px 32px !important; gap: 6px !important; }

.op-lot_generer {
	width: 32px;
	height: 38px;
	padding: 0;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	background: rgba(118,31,227,0.05);
	color: var(--op-violet);
	font-size: 15px;
	line-height: 1;
	cursor: pointer;
	transition: background-color 150ms ease;
}

.op-lot_generer:hover { background: rgba(118,31,227,0.14); }

.op-lot_ajouter--secondaire {
	border-style: solid;
	border-color: var(--op-bordure);
	background: #FFFFFF;
	color: var(--op-encre);
}

.op-lot_ajouter--secondaire:hover { background: var(--op-surface); }

@media (max-width: 1300px) {
	.op-lot_ligne { grid-template-columns: 1fr 32px 32px !important; }
	.op-lot_ligne input[name="lot_password[]"] { grid-column: 1 / 2; }
}


/* ==========================================================================
   59. Envoi en masse — reprendre la main sur les boutons
   La section 36 force TOUS les <button> de ces formulaires en width:100%,
   fond violet, avec !important. Mes boutons de ligne s'en trouvaient
   élargis jusqu'à se chevaucher et déborder de la carte.
   Les sélecteurs ci-dessous reprennent la même chaîne d'ancêtres, plus la
   classe : ils l'emportent donc en spécificité.
   ========================================================================== */

body:has(input[type="file"]) #general_page form button.op-lot_generer,
body:has(input[type="file"]) #general_page form button.op-lot_retirer {
	width: 34px !important;
	height: 38px !important;
	margin: 0 !important;
	padding: 0 !important;
	border-radius: 9px !important;
	font-size: 15px !important;
	font-weight: 500 !important;
	box-shadow: none !important;
	flex: 0 0 auto;
}

body:has(input[type="file"]) #general_page form button.op-lot_generer {
	background: rgba(118,31,227,0.07) !important;
	border: 1px solid var(--op-bordure) !important;
	color: var(--op-violet) !important;
}

body:has(input[type="file"]) #general_page form button.op-lot_generer:hover {
	background: rgba(118,31,227,0.16) !important;
	transform: none;
}

body:has(input[type="file"]) #general_page form button.op-lot_retirer {
	background: #FFFFFF !important;
	border: 1px solid var(--op-bordure) !important;
	color: var(--op-gris) !important;
	font-size: 18px !important;
}

body:has(input[type="file"]) #general_page form button.op-lot_retirer:hover {
	border-color: #D9736A !important;
	color: #C0392B !important;
	transform: none;
}

/* Les deux actions du lot ne sont pas des boutons d'envoi. */
body:has(input[type="file"]) #general_page form button.op-lot_ajouter {
	width: auto !important;
	padding: 9px 16px !important;
	font-size: 13.5px !important;
	border-radius: 9px !important;
	box-shadow: none !important;
	background: rgba(118,31,227,0.05) !important;
	border: 1px dashed var(--op-violet) !important;
	color: var(--op-violet) !important;
}

body:has(input[type="file"]) #general_page form button.op-lot_ajouter--secondaire {
	background: #FFFFFF !important;
	border: 1px solid var(--op-bordure) !important;
	color: var(--op-encre) !important;
}

body:has(input[type="file"]) #general_page form button.op-lot_ajouter:hover { transform: none; }

body:has(input[type="file"]) #general_page form button.op-lot_ajouter:disabled {
	border-style: solid !important;
	border-color: var(--op-bordure) !important;
	background: transparent !important;
	color: var(--op-gris) !important;
	cursor: not-allowed;
}

/* Les champs doivent pouvoir rétrécir, sinon la ligne déborde de la carte. */
.op-lot_ligne > input { min-width: 0 !important; width: 100% !important; }

.op-lot_ligne {
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 34px 34px !important;
	gap: 6px !important;
	padding-left: 22px;
}

@media (max-width: 1400px) {
	.op-lot_ligne {
		grid-template-columns: minmax(0, 1fr) 34px 34px !important;
		row-gap: 5px !important;
	}
	.op-lot_ligne > input[name="lot_password[]"] { grid-column: 1 / 2; }
}

/* Choix de la longueur, à côté de la génération groupée. */
.op-lot_generation {
	display: inline-flex;
	align-items: center;
	gap: 8px;
}

.op-lot_generation label {
	margin: 0 !important;
	font-size: 12.5px;
	color: var(--op-gris);
	font-weight: 500 !important;
}

body:has(input[type="file"]) #general_page form .op-lot_generation select {
	width: auto !important;
	min-width: 68px;
	padding: 8px 10px !important;
	margin: 0 !important;
	border: 1px solid var(--op-bordure) !important;
	border-radius: 9px !important;
	font-size: 13px !important;
	background: #FFFFFF !important;
	color: var(--op-encre) !important;
}

.op-lot_actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px 12px;
	align-items: center;
}


/* ==========================================================================
   60. Modal des réglages du lot
   Un seul modal pour la composition du mot de passe et la durée de validité :
   sur « Nouveau lien » ce sont deux boîtes distinctes, ce qui oblige à
   les ouvrir l'une après l'autre pour un même réglage.
   ========================================================================== */

.op-modal[hidden] { display: none !important; }

.op-modal {
	position: fixed;
	inset: 0;
	z-index: 2000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 20px;
}

.op-modal_voile {
	position: absolute;
	inset: 0;
	background: rgba(26,21,35,0.55);
}

.op-modal_boite {
	position: relative;
	width: 100%;
	max-width: 460px;
	max-height: calc(100vh - 40px);
	overflow-y: auto;
	background: #FFFFFF;
	border-radius: 16px;
	box-shadow: 0 24px 60px rgba(26,21,35,0.34);
}

.op-modal_tete {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 18px 20px 12px;
	border-bottom: 1px solid var(--op-bordure);
}

.op-modal_tete h3 {
	margin: 0 !important;
	font-size: 16.5px !important;
	font-weight: 700;
	color: var(--op-encre) !important;
}

.op-modal_fermer {
	width: 30px;
	height: 30px;
	padding: 0;
	border: 0;
	border-radius: 8px;
	background: transparent;
	color: var(--op-gris);
	font-size: 21px;
	line-height: 1;
	cursor: pointer;
}

.op-modal_fermer:hover { background: var(--op-surface); color: var(--op-encre); }

.op-modal_corps { padding: 16px 20px 18px; }

.op-modal_intro {
	margin: 0 0 16px !important;
	font-size: 12.5px;
	line-height: 1.55;
	color: var(--op-gris) !important;
}

.op-modal_grille {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 12px;
	margin-bottom: 14px;
}

.op-modal_corps label {
	display: block;
	margin: 0 0 5px !important;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-modal_corps input[type="number"],
.op-modal_corps select {
	width: 100%;
	padding: 10px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	font-family: inherit;
	font-size: 13.5px;
	color: var(--op-encre);
	background: #FFFFFF;
}

.op-modal_corps input:focus,
.op-modal_corps select:focus {
	outline: none;
	border-color: var(--op-violet);
	box-shadow: 0 0 0 3px rgba(118,31,227,0.12);
}

.op-modal_case {
	display: flex !important;
	align-items: center;
	gap: 9px;
	margin: 0 0 16px !important;
	font-weight: 500 !important;
	font-size: 13px !important;
	cursor: pointer;
}

.op-modal_case input { width: auto; accent-color: var(--op-violet); }

.op-modal_erreur {
	margin: 12px 0 0 !important;
	padding: 9px 12px;
	border: 1px solid #E0A79E;
	border-radius: 9px;
	background: #FFF5F3;
	font-size: 12.5px;
	color: #C0392B !important;
}

.op-modal_erreur[hidden] { display: none !important; }

.op-modal_pied {
	display: flex;
	justify-content: flex-end;
	gap: 8px;
	padding: 12px 20px 18px;
	border-top: 1px solid var(--op-bordure);
}

@media (max-width: 420px) {
	.op-modal_grille { grid-template-columns: 1fr; }
}


/* ==========================================================================
   61. Modal des réglages — mise en page
   Le rendu partait en escalier : les champs commençaient en deuxième case,
   tout était centré et les champs ne remplissaient pas leur colonne.
   La grille est remplacée par un flex explicite, insensible au placement
   automatique, et l'alignement est imposé.
   ========================================================================== */

.op-modal_boite,
.op-modal_boite * { text-align: left !important; }

.op-modal_tete { align-items: center; }

.op-modal_grille {
	display: flex !important;
	flex-wrap: wrap;
	gap: 12px !important;
	grid-template-columns: none !important;
}

.op-modal_grille > div {
	flex: 1 1 calc(50% - 6px);
	min-width: 0;
	margin: 0 !important;
}

/* Les pseudo-éléments hérités du gabarit deviendraient des éléments de
   disposition à part entière et décaleraient tout. */
.op-modal_grille::before,
.op-modal_grille::after {
	content: none !important;
	display: none !important;
}

.op-modal_boite label {
	width: 100%;
	font-weight: 600 !important;
}

.op-modal_boite input[type="number"],
.op-modal_boite select {
	width: 100% !important;
	max-width: none !important;
	margin: 0 !important;
	box-sizing: border-box;
}

.op-modal_boite input[type="number"]:disabled {
	background: var(--op-surface) !important;
	color: var(--op-gris) !important;
	cursor: not-allowed;
}

.op-modal_case {
	align-items: flex-start !important;
	line-height: 1.45;
}

.op-modal_case input {
	width: auto !important;
	margin-top: 2px !important;
	flex: 0 0 auto;
}

.op-modal_case span { font-weight: 400; color: var(--op-encre); }

/* « Enregistrer » est l'action principale : trait plein, pas pointillé. */
.op-modal_pied .op-lot_ajouter { width: auto !important; }

#op-modal-enregistrer {
	background: var(--op-degrade) !important;
	border: 1px solid transparent !important;
	color: #FFFFFF !important;
	box-shadow: 0 3px 10px rgba(118,31,227,0.26) !important;
}

.op-modal_pied .op-lot_ajouter--secondaire {
	background: #FFFFFF !important;
	border: 1px solid var(--op-bordure) !important;
	color: var(--op-encre) !important;
}


/* ==========================================================================
   62. Logo sur fond clair
   .op-logo est blanc, ce qui convient à l'en-tête sombre. Sur les cartes
   blanches des parcours de compte, de la supervision et de l'abonnement,
   seul le « OUR » en gras — qui porte le violet — restait visible : le
   « PASS » était blanc sur blanc.
   ========================================================================== */

.op-page-compte .op-logo,
.op-page-supervision .op-logo,
.op-page-abo .op-logo,
.op-compte_logo .op-logo,
.op-modal .op-logo {
	color: var(--op-encre) !important;
}

.op-page-compte .op-logo b,
.op-page-supervision .op-logo b,
.op-page-abo .op-logo b,
.op-compte_logo .op-logo b,
.op-modal .op-logo b {
	color: var(--op-violet) !important;
}


/* ==========================================================================
   63. Envoi en masse — capacité restante
   L'interface autorisait 25 destinataires quel que soit le quota. Elle
   annonce désormais ce qui reste réellement, et neutralise l'envoi quand
   il n'y a plus rien à consommer.
   ========================================================================== */

.op-lot_avis--info {
	background: rgba(118,31,227,0.06);
	border: 1px solid var(--op-violet);
	color: var(--op-violet);
}

.op-lot_avis--info strong { font-weight: 700; }

.op-lot_avis a {
	margin-left: 6px;
	color: inherit;
	font-weight: 600;
	text-decoration: underline;
}

/* Un bouton d'envoi désactivé ne doit pas garder l'aspect d'une action
   disponible : le gabarit le laisse en violet plein. */
body:has(input[type="file"]) #general_page form button.btn-primary:disabled {
	background: var(--op-surface) !important;
	border: 1px solid var(--op-bordure) !important;
	color: var(--op-gris) !important;
	box-shadow: none !important;
	cursor: not-allowed;
}


/* ==========================================================================
   64. Pages légales (confidentialité, CGU, CGV)
   ========================================================================== */

.op-page-legal { background: var(--op-surface); }

.lg_bandeau {
	background: var(--op-degrade);
	padding: 46px 20px 40px;
}

.lg_contenu {
	max-width: 820px;
	margin: 0 auto;
}

.lg_retour {
	display: inline-block;
	margin-bottom: 16px;
	font-size: 13.5px;
	font-weight: 600;
	color: rgba(255,255,255,0.82);
	text-decoration: none;
}

.lg_retour:hover { color: #FFFFFF; }

.lg_bandeau h1 {
	margin: 0 0 8px;
	font-size: 30px;
	font-weight: 800;
	color: #FFFFFF;
	line-height: 1.2;
}

.lg_chapeau {
	margin: 0 0 4px;
	font-size: 15px;
	color: rgba(255,255,255,0.88);
}

.lg_maj {
	margin: 0;
	font-size: 12.5px;
	color: rgba(255,255,255,0.66);
}

.lg_corps {
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	border-radius: 16px;
	padding: 34px 36px 30px;
	margin-top: -22px;
	margin-bottom: 50px;
	box-shadow: 0 10px 34px rgba(26,21,35,0.08);
}

.lg_corps section { margin-bottom: 30px; }
.lg_corps section:last-of-type { margin-bottom: 18px; }

.lg_corps h2 {
	margin: 0 0 12px;
	font-size: 18px;
	font-weight: 700;
	color: var(--op-encre);
	padding-bottom: 8px;
	border-bottom: 1px solid var(--op-bordure);
}

.lg_corps p {
	margin: 0 0 12px;
	font-size: 14.5px;
	line-height: 1.65;
	color: var(--op-gris);
}

.lg_corps strong { color: var(--op-encre); }

.lg_corps a { color: var(--op-violet); font-weight: 600; text-decoration: none; }
.lg_corps a:hover { text-decoration: underline; }

.lg_liste { margin: 0 0 12px; padding: 0; list-style: none; }

.lg_liste li {
	position: relative;
	padding: 5px 0 5px 20px;
	font-size: 14.5px;
	line-height: 1.6;
	color: var(--op-gris);
}

.lg_liste li::before {
	content: "";
	position: absolute;
	left: 2px; top: 13px;
	width: 6px; height: 6px;
	border-radius: 50%;
	background: var(--op-violet);
}

/* Le tableau des traitements doit défiler dans son cadre : la page ne défile
   jamais latéralement. */
.lg_table {
	width: 100%;
	border-collapse: collapse;
	margin: 0 0 14px;
	font-size: 13px;
	display: block;
	overflow-x: auto;
}

.lg_table th,
.lg_table td {
	padding: 10px 12px;
	text-align: left;
	border: 1px solid var(--op-bordure);
	vertical-align: top;
	color: var(--op-gris);
	min-width: 130px;
}

.lg_table th {
	background: var(--op-surface);
	font-weight: 700;
	font-size: 12px;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--op-encre);
}

.lg_note {
	padding: 11px 14px;
	border-left: 3px solid var(--op-violet);
	background: rgba(118,31,227,0.05);
	border-radius: 0 8px 8px 0;
	font-size: 13px !important;
}

.lg_pied {
	margin: 26px 0 0 !important;
	padding-top: 18px;
	border-top: 1px solid var(--op-bordure);
	text-align: center;
	font-size: 13px;
}

@media (max-width: 575px) {
	.lg_bandeau { padding: 34px 16px 34px; }
	.lg_bandeau h1 { font-size: 24px; }
	.lg_corps { padding: 24px 20px 22px; border-radius: 14px; margin-left: 12px; margin-right: 12px; }
}


/* ==========================================================================
   65. Pied de page légal de l'application
   Ces liens doivent être atteignables depuis n'importe quelle page : c'est
   une obligation, pas un ornement. Discret mais toujours présent.
   ========================================================================== */

.op-pied-legal {
	padding: 18px 20px 22px;
	text-align: center;
	font-size: 12px;
	line-height: 1.9;
	color: var(--op-gris);
	background: transparent;
}

.op-pied-legal a {
	color: var(--op-gris);
	text-decoration: none;
	white-space: nowrap;
}

.op-pied-legal a:hover { color: var(--op-violet); text-decoration: underline; }

.op-pied-legal span { margin: 0 7px; opacity: 0.45; }

/* Sur les pages à fond violet, le gris serait illisible. */
.op-page-compte .op-pied-legal,
.op-page-compte .op-pied-legal a { color: rgba(255,255,255,0.72); }
.op-page-compte .op-pied-legal a:hover { color: #FFFFFF; }

/* Le parcours en étapes occupe toute la hauteur : le pied se place sous lui. */
body:has(.row-height) .op-pied-legal {
	position: relative;
	z-index: 4;
	background: #FFFFFF;
	border-top: 1px solid var(--op-bordure);
}

.lg_corps h3 {
	margin: 20px 0 8px;
	font-size: 15px;
	font-weight: 700;
	color: var(--op-encre);
}


/* ==========================================================================
   66. Carte des consultations
   Les couleurs de statut sont choisies pour ce qu'elles signifient : le
   violet pour l'état normal, le vert pour l'objectif atteint, l'ambre pour
   le seul cas qui appelle une action. Le rouge est écarté — aucun de ces
   trois états n'est une erreur, et l'employer ici le banaliserait.
   ========================================================================== */

.op-page-carte { background: var(--op-surface); }

.op-carte {
	max-width: 1180px;
	margin: 0 auto;
	padding: 26px 20px 30px;
}

.op-carte_entete {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	align-items: center;
	justify-content: space-between;
	margin-bottom: 22px;
}

.op-carte_entete nav a {
	margin-left: 14px;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--op-violet);
	text-decoration: none;
}

.op-carte h1 {
	margin: 0 0 8px;
	font-size: 25px;
	font-weight: 800;
	color: var(--op-encre);
}

.op-carte_intro {
	margin: 0 0 24px;
	max-width: 760px;
	font-size: 13.5px;
	line-height: 1.6;
	color: var(--op-gris);
}

.op-carte_intro a { color: var(--op-violet); font-weight: 600; }

/* --- Statuts ------------------------------------------------------------- */

.op-statuts {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
	gap: 14px;
	margin-bottom: 24px;
}

.op-statut {
	position: relative;
	padding: 18px 20px 18px 62px;
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	border-radius: 14px;
}

.op-statut_icone {
	position: absolute;
	left: 18px; top: 20px;
	width: 32px; height: 32px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 9px;
	font-size: 14px;
}

.op-statut_val {
	display: block;
	font-size: 26px;
	font-weight: 800;
	line-height: 1.1;
	color: var(--op-encre);
}

.op-statut_lib {
	display: block;
	margin-top: 2px;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-statut_note {
	display: block;
	margin-top: 3px;
	font-size: 11.5px;
	color: var(--op-gris);
}

/* En attente : état normal, ni succès ni alerte. */
.op-statut--attente { border-left: 3px solid var(--op-violet); }
.op-statut--attente .op-statut_icone { background: rgba(118,31,227,0.10); color: var(--op-violet); }

/* Consulté : le secret est arrivé. */
.op-statut--consulte { border-left: 3px solid #2E9E5B; }
.op-statut--consulte .op-statut_icone { background: rgba(46,158,91,0.12); color: #2E9E5B; }
.op-statut--consulte .op-statut_val { color: #2E9E5B; }

/* Expiré sans lecture : le seul cas qui demande une action. */
.op-statut--expire { border-left: 3px solid #D08700; }
.op-statut--expire .op-statut_icone { background: rgba(208,135,0,0.13); color: #D08700; }
.op-statut--expire .op-statut_val { color: #D08700; }

/* --- Blocs de carte ------------------------------------------------------ */

.op-cartes {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
	gap: 18px;
	margin-bottom: 18px;
	align-items: start;
}

.op-carte_bloc {
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	border-radius: 14px;
	padding: 20px 22px 22px;
	margin-bottom: 18px;
}

.op-carte_bloc h2 {
	margin: 0 0 10px;
	font-size: 16px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-carte_note {
	margin: 0 0 12px;
	font-size: 12px;
	line-height: 1.5;
	color: var(--op-gris);
}

.op-carte_vide {
	margin: 0;
	padding: 26px 0;
	text-align: center;
	font-size: 13.5px;
	color: var(--op-gris);
}

/* --- Monde --------------------------------------------------------------- */

.op-monde { width: 100%; height: auto; display: block; }

.op-monde_terre { fill: rgba(118,31,227,0.16); }

.op-monde_halo {
	fill: rgba(118,31,227,0.16);
	stroke: rgba(118,31,227,0.30);
	stroke-width: 1;
}

.op-monde_point { fill: var(--op-violet); cursor: help; }

.op-legende {
	display: flex;
	flex-wrap: wrap;
	gap: 6px 18px;
	margin: 14px 0 0;
	padding: 0;
	list-style: none;
}

.op-legende li {
	display: flex;
	align-items: center;
	gap: 7px;
	font-size: 12.5px;
	color: var(--op-gris);
}

.op-legende_pastille {
	width: 9px; height: 9px;
	border-radius: 50%;
	background: var(--op-violet);
	flex: 0 0 auto;
}

.op-legende_nom { color: var(--op-encre); font-weight: 600; }

.op-legende_val {
	padding: 1px 8px;
	border-radius: 20px;
	background: rgba(118,31,227,0.09);
	color: var(--op-violet);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

/* --- France en hexagones -------------------------------------------------- */

.op-hexa { width: 100%; height: auto; display: block; max-width: 340px; margin: 0 auto; }

.op-hexa_case {
	fill: rgba(26,21,35,0.05);
	stroke: #FFFFFF;
	stroke-width: 2.5;
	transition: fill 160ms ease;
}

/* L'intensité suit le volume, via une variable posée par le PHP. */
.op-hexa_case--active {
	fill: rgba(118,31,227, calc(0.22 + 0.68 * var(--force, 0)));
	cursor: help;
}

.op-hexa_txt {
	font-size: 10px;
	font-weight: 700;
	text-anchor: middle;
	fill: var(--op-gris);
	pointer-events: none;
	font-family: 'Inter', system-ui, sans-serif;
}

.op-hexa_txt--active { fill: #FFFFFF; }

.op-carte_ip { font-variant-numeric: tabular-nums; color: var(--op-gris); }

@media (max-width: 767px) {
	.op-carte h1 { font-size: 21px; }
	.op-carte_bloc { padding: 16px 16px 18px; }
}


/* ==========================================================================
   67. Supervision — volumes, tâches planifiées, regroupement
   ========================================================================== */

.op-sup_titre {
	margin: 28px 0 12px;
	font-size: 16px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-sup_ecart {
	display: block;
	margin-top: 2px;
	font-style: normal;
	font-size: 11px;
	font-weight: 600;
}

.op-sup_ecart--haut { color: #2E9E5B; }
.op-sup_ecart--bas  { color: #D08700; }

/* --- Tâches planifiées --------------------------------------------------- */

.op-sup_crons {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
	gap: 12px;
}

.op-cron {
	display: flex;
	align-items: flex-start;
	gap: 11px;
	padding: 14px 16px;
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	border-radius: 12px;
}

.op-cron_pastille {
	width: 10px; height: 10px;
	margin-top: 4px;
	border-radius: 50%;
	flex: 0 0 auto;
}

.op-cron_nom {
	display: block;
	font-size: 14px;
	font-weight: 700;
	color: var(--op-encre);
	text-transform: capitalize;
}

.op-cron_detail {
	display: block;
	margin-top: 2px;
	font-size: 11.5px;
	line-height: 1.5;
	color: var(--op-gris);
}

.op-cron--ok       { border-left: 3px solid #2E9E5B; }
.op-cron--ok  .op-cron_pastille { background: #2E9E5B; box-shadow: 0 0 0 3px rgba(46,158,91,0.16); }

/* Une tâche muette depuis trop longtemps est le seul cas qui appelle une
   intervention : c'est donc le seul en rouge. */
.op-cron--ko       { border-left: 3px solid #C0392B; background: #FFF5F3; }
.op-cron--ko  .op-cron_pastille { background: #C0392B; box-shadow: 0 0 0 3px rgba(192,57,43,0.16); }

.op-cron--inconnu  { border-left: 3px solid var(--op-gris); }
.op-cron--inconnu .op-cron_pastille { background: var(--op-gris); }

/* --- Regroupement par organisation --------------------------------------- */

.op-sup_org {
	margin-bottom: 22px;
	border: 1px solid var(--op-bordure);
	border-radius: 12px;
	overflow: hidden;
	background: #FFFFFF;
}

.op-sup_org-tete {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 16px;
	align-items: baseline;
	justify-content: space-between;
	padding: 13px 16px;
	background: #FAF8FF;
	border-bottom: 1px solid var(--op-bordure);
}

.op-sup_org-tete h3 {
	margin: 0;
	font-size: 15px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-sup_org-meta { font-size: 12.5px; color: var(--op-gris); }

.op-sup_org .op-sup_table { border: 0; border-radius: 0; }

/* --- Case d'acceptation à l'inscription ---------------------------------- */

.op-compte_case {
	display: flex !important;
	align-items: flex-start;
	gap: 10px;
	margin: 20px 0 0 !important;
	font-weight: 400 !important;
	font-size: 13px !important;
	line-height: 1.55;
	color: var(--op-gris) !important;
	cursor: pointer;
}

.op-compte_case input {
	width: auto !important;
	margin-top: 3px !important;
	flex: 0 0 auto;
	accent-color: var(--op-violet);
}

.op-compte_case a { color: var(--op-violet); font-weight: 600; }

/* --- Avertissement avant divulgation ------------------------------------- */

.op-avert_intro {
	margin: 0 0 14px;
	font-size: 14.5px;
	line-height: 1.6;
	color: var(--op-gris);
}

.op-avert_liste {
	margin: 0 0 16px;
	padding: 0;
	list-style: none;
	text-align: left;
}

.op-avert_liste li {
	position: relative;
	padding: 6px 0 6px 22px;
	font-size: 13.5px;
	line-height: 1.55;
	color: var(--op-gris);
}

.op-avert_liste li::before {
	content: "";
	position: absolute;
	left: 3px; top: 14px;
	width: 6px; height: 6px;
	border-radius: 50%;
	background: var(--op-violet);
}

.op-avert_loi {
	margin: 0 0 16px;
	padding: 11px 14px;
	border-left: 3px solid #D08700;
	background: #FFFBF3;
	border-radius: 0 8px 8px 0;
	font-size: 12.5px;
	line-height: 1.55;
	color: #7A5200;
	text-align: left;
}

.op-avert_case {
	display: flex !important;
	align-items: flex-start;
	gap: 10px;
	margin: 0 0 18px !important;
	font-size: 13px;
	line-height: 1.55;
	text-align: left;
	cursor: pointer;
}

.op-avert_case input { width: auto !important; margin-top: 3px; flex: 0 0 auto; accent-color: var(--op-violet); }

.op-avert_actions { display: flex; gap: 10px; flex-wrap: wrap; }
.op-avert_actions button { flex: 1 1 auto; }

/* L'Outre-mer est détaché de la silhouette : un liseré le signale comme tel. */
.op-hexa_case--detache {
	stroke: var(--op-bordure);
	stroke-width: 2;
	stroke-dasharray: 3 3;
}

.op-hexa { max-width: 320px; }


/* ==========================================================================
   68. Cartes réelles
   Les contours schématiques ne se lisaient pas. Ceux-ci sont les vrais
   tracés administratifs, simplifiés pour rester légers.
   ========================================================================== */

.op-monde_terre {
	fill: rgba(26,21,35,0.07);
	stroke: rgba(26,21,35,0.16);
	stroke-width: 0.4;
	stroke-linejoin: round;
}

.op-france { width: 100%; height: auto; display: block; max-width: 420px; margin: 0 auto; }

.op-france_reg {
	fill: rgba(26,21,35,0.06);
	stroke: #FFFFFF;
	stroke-width: 1.6;
	stroke-linejoin: round;
	transition: fill 160ms ease;
}

/* L'intensité suit le volume, via une variable posée par le PHP. */
.op-france_reg--active {
	fill: rgba(118,31,227, calc(0.24 + 0.66 * var(--force, 0)));
	cursor: help;
}

.op-france_reg:hover { fill: rgba(118,31,227,0.34); }

.op-france_om {
	margin: 12px 0 0 !important;
	padding: 9px 13px;
	border-left: 3px solid var(--op-violet);
	background: rgba(118,31,227,0.05);
	border-radius: 0 8px 8px 0;
	font-size: 12.5px;
	color: var(--op-gris) !important;
}

.op-france_om strong { color: var(--op-violet); }


/* ==========================================================================
   69. Suivi — statut, localisation, révocation
   Mêmes couleurs que la carte : violet pour l'état normal, vert pour le
   secret remis, ambre pour ce qui appelle une action, gris pour un lien
   neutralisé. Le rouge reste réservé aux refus.
   ========================================================================== */

.op-lien { position: relative; }

/* Liseré de statut, lisible d'un coup d'œil sur toute la liste. */
.op-lien--attente  { border-left: 3px solid var(--op-violet) !important; }
.op-lien--consulte { border-left: 3px solid #2E9E5B !important; }
.op-lien--expire   { border-left: 3px solid #D08700 !important; }
.op-lien--revoque  { border-left: 3px solid var(--op-gris) !important; opacity: 0.75; }

.op-lien_etat {
	display: inline-block;
	margin-left: 8px;
	padding: 2px 10px;
	border-radius: 20px;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.02em;
	vertical-align: middle;
	white-space: nowrap;
}

.op-lien_etat--attente  { background: rgba(118,31,227,0.10); color: var(--op-violet); }
.op-lien_etat--consulte { background: rgba(46,158,91,0.13);  color: #2E9E5B; }
.op-lien_etat--expire   { background: rgba(208,135,0,0.14);  color: #9A6414; }
.op-lien_etat--revoque  { background: rgba(26,21,35,0.08);   color: var(--op-gris); }

.op-lien_info {
	margin: 4px 0 2px !important;
	font-size: 13px;
	line-height: 1.5;
	color: var(--op-gris) !important;
}

.op-lien_info strong { color: var(--op-encre); font-weight: 600; }

.op-lien_lieu::before {
	content: "📍";
	margin: 0 4px 0 6px;
	font-size: 11px;
}

.op-lien_lieu--absent { font-style: italic; opacity: 0.7; }
.op-lien_lieu--absent::before { content: "—"; }

/* La révocation est irréversible : le bouton reste discret jusqu'au survol,
   pour ne pas être cliqué par inadvertance en parcourant la liste. */
.op-lien_revoc { margin-top: 8px; }

.op-lien_revoc button {
	padding: 6px 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 8px;
	background: #FFFFFF;
	color: var(--op-gris);
	font-family: inherit;
	font-size: 12.5px;
	font-weight: 600;
	cursor: pointer;
	transition: border-color 150ms ease, color 150ms ease, background-color 150ms ease;
}

.op-lien_revoc button:hover {
	border-color: #C0392B;
	background: #FFF5F3;
	color: #C0392B;
}


/* ==========================================================================
   70. Avertissement de consultation — tenue sur petit écran
   Le contenu dépassait la hauteur d'un téléphone et les boutons se
   retrouvaient hors écran : le lien devenait impossible à valider.
   ========================================================================== */

@media (max-width: 575px) {
	.op-avert_intro { font-size: 13.5px; }
	.op-avert_liste li { padding: 5px 0 5px 18px; font-size: 13px; }
	.op-avert_loi { font-size: 12px; padding: 9px 12px; }
	.op-avert_case { font-size: 12.5px; }

	/* Empilés, les deux boutons gardent une cible confortable au doigt. */
	.op-avert_actions { flex-direction: column-reverse; gap: 8px; }
	.op-avert_actions button { width: 100%; min-height: 46px; }
}

/* Le signalement est une action secondaire : discret, mais toujours atteignable. */
.op-avert_signalement { margin-top: 10px; text-align: center; }

.op-avert_signalement button {
	width: 100%;
	padding: 10px 16px;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	background: #FFFFFF;
	color: var(--op-gris);
	font-family: inherit;
	font-size: 13px;
	font-weight: 600;
	cursor: pointer;
}

.op-avert_signalement button:hover {
	border-color: #C0392B;
	background: #FFF5F3;
	color: #C0392B;
}


/* ==========================================================================
   71. Ouverture du menu — fluidité
   Le gabarit ouvre le menu par deux cercles dilatés depuis le bouton. Leur
   diamètre vaut DEUX FOIS la diagonale de l'écran — environ 4400 px de côté
   sur un écran courant, soit près de vingt millions de pixels — et ils
   portent will-change: transform, ce qui force le navigateur à garder cette
   texture en mémoire graphique en permanence, menu fermé compris.
   L'animation étant pilotée image par image en JavaScript (Velocity), le
   résultat saccade.

   On neutralise les deux calques géants et l'on ouvre le menu par une
   simple variation d'opacité, composée par le GPU. Le JavaScript du gabarit
   continue de tourner : ses transformations sont simplement sans effet.
   ========================================================================== */

/* Les conteneurs cessent d'être des calques permanents. */
.cd-overlay-nav,
.cd-overlay-content {
	position: fixed !important;
	top: 0 !important;
	right: 0 !important;
	bottom: 0 !important;
	left: 0 !important;
	width: 100% !important;
	height: 100% !important;
	transform: none !important;
	pointer-events: none;
	/* Une règle plus haut (section 4) donne un fond violet à ces conteneurs.
	   Inoffensif tant qu'ils mesuraient 4 x 4 px derrière le bouton ; étendus
	   à tout l'écran, ils masquaient la page entière. */
	background-color: transparent !important;
	/* Masqués tant que le menu est fermé : le conteneur ne doit rien
	   intercepter ni rien peindre. */
	visibility: hidden;
}

body:has(.cd-nav-trigger.close-nav) .cd-overlay-nav { visibility: visible; }

/* Le voile occupe l'écran sans être redimensionné ni mis à l'échelle. */
.cd-overlay-nav > span,
.cd-overlay-content > span {
	position: fixed !important;
	top: 0 !important;
	left: 0 !important;
	width: 100% !important;
	height: 100% !important;
	border-radius: 0 !important;
	/* Neutralise le scale appliqué en ligne par Velocity. */
	transform: none !important;
	/* La texture n'est plus conservée hors animation. */
	will-change: auto !important;
	backface-visibility: visible !important;
	opacity: 0;
	transition: opacity 220ms ease;
}

.cd-overlay-nav > span { background-color: var(--op-violet) !important; }

/* Le second voile ne sert qu'à la fermeture : il resterait blanc par-dessus. */
.cd-overlay-content > span { background-color: transparent !important; }

/* L'état ouvert se lit sur le bouton, que le gabarit marque déjà. */
body:has(.cd-nav-trigger.close-nav) .cd-overlay-nav > span { opacity: 1; }

/* Le panneau n'attend plus la fin des 500 ms d'animation JavaScript. */
body:has(.cd-nav-trigger.close-nav) .cd-primary-nav {
	visibility: visible !important;
	opacity: 1 !important;
	transition: opacity 200ms ease 60ms !important;
}

.cd-primary-nav {
	background-color: transparent !important;
	transition: opacity 160ms ease !important;
}

/* Sur le fond violet, les libellés doivent ressortir. */
body:has(.cd-nav-trigger.close-nav) .cd-primary-nav li a { color: #FFFFFF !important; }
body:has(.cd-nav-trigger.close-nav) .cd-primary-nav li a:hover { color: rgba(255,255,255,0.78) !important; }

@media (prefers-reduced-motion: reduce) {
	.cd-overlay-nav > span,
	.cd-primary-nav { transition: none !important; }
}

/* ==================================================================
   Restriction géographique
   ==================================================================
   Le réglage est une liste blanche : ce qui n'est pas coché est refusé.
   Le vocabulaire visuel le dit — vert pour ce qui est autorisé, rouge
   pour la tentative refusée dans le suivi.
   ------------------------------------------------------------------ */

.op-geo {
	margin-top: 18px;
	padding-top: 18px;
	border-top: 1px solid var(--op-bordure);
}

.op-geo_bascule { margin-bottom: 4px !important; }

.op-geo_aide {
	margin: 0 0 12px !important;
	font-size: 12.5px;
	line-height: 1.5;
	color: var(--op-gris);
}

.op-geo_panneau {
	padding: 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 12px;
	background: var(--op-surface);
}

/* --- Pastilles des zones retenues -------------------------------- */

.op-geo_choix {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	min-height: 32px;
	align-items: center;
	margin-bottom: 14px;
	padding-bottom: 12px;
	border-bottom: 1px dashed var(--op-bordure);
}

.op-geo_choix_vide {
	font-size: 12.5px;
	font-style: italic;
	color: var(--op-gris);
}

.op-geo_jeton {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 5px 10px 5px 11px;
	border: 1px solid rgba(46,158,91,0.35);
	border-radius: 999px;
	background: rgba(46,158,91,0.10);
	color: #1F7A46;
	font-size: 12.5px;
	font-weight: 600;
	line-height: 1.2;
	cursor: pointer;
	transition: background-color .15s ease, border-color .15s ease;
}

/* La croix est ajoutée par le style : le texte du bouton reste le nom
   du pays, ce que les lecteurs d'écran annoncent correctement. */
.op-geo_jeton::after {
	content: '×';
	font-size: 15px;
	line-height: 1;
	opacity: .55;
}

.op-geo_jeton:hover {
	background: rgba(46,158,91,0.18);
	border-color: rgba(46,158,91,0.55);
}
.op-geo_jeton:hover::after { opacity: 1; }

/* --- Titres de section ------------------------------------------- */

.op-geo_titre {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	margin: 0 0 8px !important;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--op-gris);
}

.op-geo_recherche {
	flex: 0 1 160px;
	min-width: 0;
	padding: 5px 10px !important;
	border: 1px solid var(--op-bordure) !important;
	border-radius: 8px !important;
	background: #fff !important;
	font-size: 12.5px !important;
	font-weight: 400;
	letter-spacing: 0;
	text-transform: none;
	color: var(--op-encre) !important;
}
.op-geo_recherche:focus {
	border-color: var(--op-violet) !important;
	outline: none;
}

/* --- Continents --------------------------------------------------- */

.op-geo_continents {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-bottom: 18px;
}

.op-geo_pilule {
	margin: 0 !important;
	cursor: pointer;
}

/* La case elle-même est masquée : la pilule entière fait office de
   contrôle, et reste atteignable au clavier via :focus-visible. */
.op-geo_pilule input {
	position: absolute;
	opacity: 0;
	width: 0;
	height: 0;
}

.op-geo_pilule span {
	display: inline-block;
	padding: 6px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 999px;
	background: #fff;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--op-gris);
	transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

.op-geo_pilule input:checked + span {
	border-color: rgba(46,158,91,0.55);
	background: rgba(46,158,91,0.12);
	color: #1F7A46;
}

.op-geo_pilule input:focus-visible + span {
	outline: 2px solid var(--op-violet);
	outline-offset: 2px;
}

/* --- Liste des pays ----------------------------------------------- */

.op-geo_liste {
	margin: 0 !important;
	padding: 0 !important;
	max-height: 240px;
	overflow-y: auto;
	list-style: none !important;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: #fff;
}

.op-geo_liste li { margin: 0 !important; padding: 0 !important; }
.op-geo_liste li + li { border-top: 1px solid var(--op-bordure); }

.op-geo_liste label {
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 0 !important;
	padding: 8px 12px;
	font-size: 13.5px;
	font-weight: 500;
	color: var(--op-encre);
	cursor: pointer;
}

.op-geo_liste label:hover { background: var(--op-surface); }

.op-geo_liste input {
	flex: 0 0 auto;
	margin: 0 !important;
	accent-color: #2E9E5B;
}

/* Largeur fixe : les drapeaux n'ont pas tous la même chasse selon la
   police système, et une colonne qui tremble rend la liste illisible. */
/* Le drapeau est tantôt une IMAGE, tantôt un emoji de repli : la règle doit
   valoir pour les deux. `flex: 0 0 22px` fixe la gouttière, l'image s'y pose à
   sa taille propre et l'emoji s'y centre. */
.op-geo_drapeau {
	flex: 0 0 22px;
	font-size: 16px;
	line-height: 1;
	text-align: center;
}

img.op-geo_drapeau {
	width: 20px;
	height: 15px;
	/* Un liseré : sans lui, un drapeau à bande blanche — Pologne, Autriche —
	   se fond dans la ligne et paraît amputé de sa moitié. */
	border: 1px solid rgba(26, 21, 35, 0.14);
	border-radius: 2px;
	object-fit: cover;
	vertical-align: middle;
}

/* Dans une pastille, il précède le nom et doit s'aligner sur le texte. */
.op-geo_jeton img.op-geo_drapeau { margin-right: 5px; }

.op-geo_nom {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.op-geo_code {
	flex: 0 0 auto;
	padding: 2px 6px;
	border-radius: 5px;
	background: var(--op-surface);
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: .04em;
	color: var(--op-gris);
}

.op-geo_liste input:checked ~ .op-geo_code {
	background: rgba(46,158,91,0.14);
	color: #1F7A46;
}

.op-geo_rien {
	padding: 14px 12px !important;
	font-size: 13px;
	font-style: italic;
	color: var(--op-gris);
	text-align: center;
}

.op-geo_note {
	margin: 12px 0 0 !important;
	font-size: 12px;
	line-height: 1.5;
	color: var(--op-gris);
}

/* Limite demandée mais aucune zone cochée : l'état est signalé, sans
   quoi le destinataire lui-même se verrait refuser l'accès. */
.op-geo--vide .op-geo_choix {
	border-bottom-color: rgba(200,60,60,0.45);
}
.op-geo--vide .op-geo_choix_vide {
	color: #B3261E;
	font-style: normal;
	font-weight: 600;
}
.op-geo--vide .op-geo_choix_vide::before { content: '⚠ '; }

/* --- Suivi : restriction posée et tentatives refusées -------------- */

.op-lien_geo,
.op-lien_bloc {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 4px;
}

.op-lien_geo_pastille,
.op-lien_bloc_pastille {
	flex: 0 0 auto;
	align-self: center;
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: #2E9E5B;
}

.op-lien_bloc {
	margin-top: 6px !important;
	padding: 8px 10px;
	border-radius: 8px;
	border-left: 3px solid #C8322B;
	background: rgba(200,50,43,0.07);
	color: #8E241E !important;
}

.op-lien_bloc strong { color: #6E1B16; }
.op-lien_bloc_pastille { background: #C8322B; }

.op-lien_bloc_regle {
	flex: 1 0 100%;
	margin-top: 3px;
	font-size: 12px;
	opacity: .85;
}

/* Rappel de la restriction sous le formulaire d'envoi en masse : le modal
   une fois refermé, plus rien ne signalait qu'une limite était posée. */
.op-lot_geo_resume {
	margin: 10px 0 0;
	padding: 8px 12px;
	border-left: 3px solid #2E9E5B;
	border-radius: 8px;
	background: rgba(46,158,91,0.09);
	font-size: 13px;
	font-weight: 600;
	color: #1F7A46;
}

/* Le modal de validité accueille désormais la liste des pays : sans borne,
   il dépasserait la fenêtre sur un portable. */
#durationModal .modal-body {
	max-height: 70vh;
	overflow-y: auto;
}

/* Rappel de la restriction sur la page d'envoi unitaire : le réglage vit
   dans un modal, il doit rester visible une fois celui-ci refermé.

   La rangée de boutons est un `d-flex` sans retour à la ligne : le rappel
   s'y intercalait entre deux boutons. On autorise le passage à la ligne pour
   cette rangée précise, et `order` renvoie le rappel après tous les boutons,
   quelle que soit sa position dans le balisage. */
.d-flex:has(> .op-geo_rappel) { flex-wrap: wrap; }

.op-geo_rappel {
	flex: 0 0 100%;
	order: 99;
	margin: 10px 0 0;
	padding: 7px 12px;
	border-left: 3px solid #2E9E5B;
	border-radius: 8px;
	background: rgba(46,158,91,0.09);
	font-size: 13px;
	font-weight: 600;
	color: #1F7A46;
}

/* Le modal des pays est indépendant de Bootstrap : les scripts de la page
   n'en chargent plus de version fonctionnelle depuis la fermeture de
   BootstrapCDN. Il n'a donc besoin que de ces règles. */
#op-modal-geo .op-modal_boite { max-width: 560px; }
#op-modal-geo .op-modal_corps { max-height: 68vh; overflow-y: auto; }

/* Bouton « Envoyer » pendant la requête : verrouillé et animé.
   Le double clic créait deux liens, remettait deux messages et décomptait
   deux fois le quota. */
.op-envoi-attente {
	position: relative;
	padding-left: 34px !important;
	opacity: .75;
	cursor: progress !important;
}

.op-envoi-attente::before {
	content: '';
	position: absolute;
	left: 14px;
	top: 50%;
	width: 13px;
	height: 13px;
	margin-top: -7px;
	border: 2px solid rgba(255,255,255,0.35);
	border-top-color: #fff;
	border-radius: 50%;
	animation: op-tourne .7s linear infinite;
}

@keyframes op-tourne { to { transform: rotate(360deg); } }

/* Respect du réglage système : pas d'animation pour qui la refuse — le
   verrouillage et le libellé suffisent à signaler l'attente. */
@media (prefers-reduced-motion: reduce) {
	.op-envoi-attente::before { animation: none; }
}

/* Masquage des boutons du parcours — par classe, jamais en ligne.
   ------------------------------------------------------------------
   `button.submit` porte `display: inline-flex !important` (voir plus haut :
   l'icône de validation recouvrait le mot « Envoyer »). Or un !important de
   feuille l'emporte sur un style en ligne : `envoi.style.display = 'none'`
   était donc sans effet, et « Envoyer » restait affiché dès l'étape 1 quoi que
   décide le JavaScript.

   Ces sélecteurs sont plus spécifiques que les règles qu'ils doivent battre :
   `button.submit.op-cache` (0,2,1) l'emporte sur `button.submit` (0,1,1), et
   `#next-button.op-cache` (1,1,0) sur `#next-button` (1,0,0). */
button.submit.op-cache,
button.forward.op-cache,
#next-button.op-cache {
	display: none !important;
}

/* Menu de langue dans le modal du destinataire : même allure que les autres
   champs de la surcouche, que le gabarit ne style pas ici. */
#op-modal-geo select {
	width: 100%;
	padding: 9px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	background: #fff;
	font-size: 14px;
	color: var(--op-encre);
}

#op-modal-geo select:focus {
	border-color: var(--op-violet);
	outline: none;
}

#op-modal-geo > .op-modal_boite > .op-modal_corps > label {
	display: block;
	margin-bottom: 6px;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-encre);
}

/* ==================================================================
   Sélecteur de langue
   ==================================================================
   Deux emplacements : sous l'animation de la page d'envoi, et dans le
   pied de page des quinze autres. Ce sont des liens — ils fonctionnent
   sans JavaScript et l'adresse obtenue reste partageable.

   Les fonds diffèrent selon les pages : neutre par défaut, clair sur les
   fonds sombres. On ne suppose donc jamais la couleur d'arrière-plan.
   ------------------------------------------------------------------ */

.op-langues {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 2px;
	padding: 3px;
	border: 1px solid var(--op-bordure);
	border-radius: 999px;
	background: rgba(255,255,255,0.55);
	vertical-align: middle;
}

.op-langues_globe {
	margin: 0 3px 0 7px;
	font-size: 12px;
	line-height: 1;
	opacity: .7;
}

.op-langues_choix {
	padding: 4px 9px;
	border-radius: 999px;
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: .04em;
	color: var(--op-gris) !important;
	text-decoration: none !important;
	white-space: nowrap;
	transition: background-color .15s ease, color .15s ease;
}

.op-langues_choix:hover {
	background: rgba(118,31,227,0.09);
	color: var(--op-violet) !important;
	text-decoration: none !important;
}

.op-langues_choix.est-active {
	background: var(--op-violet);
	color: #FFFFFF !important;
}

/* --- Emplacements ------------------------------------------------- */

/* Colonne de gauche de la page d'envoi : sous l'animation.
   Le conteneur reprend exactement le cadrage de .op-scene — 320 px centrés,
   retrait de 26 px comme les jalons — pour que le sélecteur s'aligne sur eux.
   Collé à l'animation il paraissait en faire partie ; l'écart le rétablit
   comme un réglage distinct. */
.content-left-wrapper .op-langues-zone {
	/* Centre sous l'apercu, et non cale a gauche. Le `max-width: 320px` avec
	   `padding-left: 26px` datait de la scene decorative, large de 320 px :
	   le selecteur se logeait dessous, aligne sur elle. L'apercu a une autre
	   largeur — le calage a gauche s'est mis a ne plus rien viser. */
	display: flex;
	justify-content: center;
	box-sizing: border-box;
	width: 100%;
	margin: 34px auto 0;
}

.content-left-wrapper .op-langues { display: inline-flex; }

@media (max-width: 991px) {
	.content-left-wrapper .op-langues-zone { margin-top: 32px; }
}

/* Pied de page : à gauche, les liens légaux restant centrés. La position
   absolue évite de toucher à la mise en page existante des quinze pages. */
.op-pied-legal { position: relative; }

@media (min-width: 900px) {
	.op-pied-legal .op-langues {
		position: absolute;
		left: 20px;
		top: 14px;
	}
}

@media (max-width: 899px) {
	.op-pied-legal .op-langues { display: flex; margin: 0 auto 12px; }
}

/* --- Fonds sombres ------------------------------------------------- */

.content-left-wrapper .op-langues,
.op-page-compte .op-langues {
	border-color: rgba(255,255,255,0.28);
	background: rgba(255,255,255,0.12);
}

.content-left-wrapper .op-langues_choix,
.op-page-compte .op-langues_choix { color: rgba(255,255,255,0.80) !important; }

.content-left-wrapper .op-langues_choix:hover,
.op-page-compte .op-langues_choix:hover {
	background: rgba(255,255,255,0.18);
	color: #FFFFFF !important;
}

.content-left-wrapper .op-langues_choix.est-active,
.op-page-compte .op-langues_choix.est-active {
	background: #FFFFFF;
	color: var(--op-violet) !important;
}

/* --- Infobulle ------------------------------------------------------
   Le choix ne concerne pas que l'affichage : c'est le genre de malentendu
   qui fait partir un e-mail dans une langue illisible. */

.op-langues::after {
	content: attr(data-op-info);
	position: absolute;
	bottom: calc(100% + 9px);
	left: 0;
	z-index: 60;
	width: max-content;
	max-width: 270px;
	padding: 9px 12px;
	border-radius: 9px;
	background: var(--op-encre);
	box-shadow: 0 10px 30px rgba(26,21,35,0.30);
	font-size: 12px;
	font-weight: 500;
	line-height: 1.45;
	letter-spacing: 0;
	color: #FFFFFF;
	text-align: left;
	opacity: 0;
	visibility: hidden;
	transform: translateY(4px);
	transition: opacity .16s ease, transform .16s ease, visibility .16s;
	pointer-events: none;
}

.op-langues:hover::after,
.op-langues:focus-within::after {
	opacity: 1;
	visibility: visible;
	transform: translateY(0);
}

/* Sans survol, l'infobulle n'existe pas — et c'est précisément là qu'on
   ignorerait le message. Il s'affiche alors en clair. */
.op-langues_note { display: none; }

@media (hover: none) {
	.op-langues::after { display: none; }
	.op-langues { flex-wrap: wrap; justify-content: center; }
	.op-langues_note {
		display: block;
		flex-basis: 100%;
		padding: 5px 9px 3px;
		font-size: 10.5px;
		line-height: 1.35;
		color: var(--op-gris);
		text-align: center;
	}
	.content-left-wrapper .op-langues_note,
	.op-page-compte .op-langues_note { color: rgba(255,255,255,0.72); }
}

/* Choix de la périodicité sur la page d'abonnement. */
.op-abo_periode {
	margin: 12px 0 4px;
	padding: 10px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: var(--op-surface);
}

.op-abo_periode legend {
	padding: 0 6px;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--op-gris);
}

.op-abo_periode label {
	display: flex;
	align-items: center;
	gap: 8px;
	margin: 4px 0 !important;
	font-size: 13.5px;
	font-weight: 500;
	color: var(--op-encre);
	cursor: pointer;
}

.op-abo_periode input { margin: 0 !important; accent-color: var(--op-violet); }

/* Le total est ce que le client cherche des yeux : il doit ressortir. */
.op-abo_total {
	display: block;
	margin-top: 8px;
	font-size: 14px !important;
	font-weight: 700;
	color: var(--op-violet) !important;
}

/* Formulaire de dérogation de quota, dans la supervision. Trois champs
   étroits et un bouton : il partage la cellule avec le choix de rôle. */
.op-sup_form--quota {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	margin-top: 6px;
	padding-top: 6px;
	border-top: 1px dashed var(--op-bordure);
}

.op-sup_form--quota input[type="number"] {
	width: 74px;
	flex: 0 0 auto;
}

.op-sup_form--quota input[type="text"] {
	flex: 1 1 110px;
	min-width: 0;
}

.op-sup_form--quota input {
	padding: 4px 7px;
	border: 1px solid var(--op-bordure);
	border-radius: 6px;
	font-size: 12px;
	color: var(--op-encre);
}

.op-sup_form--quota input:focus { border-color: var(--op-violet); outline: none; }

/* Note de fonctionnement de la supervision : repliée par défaut, elle ne
   doit pas repousser les chiffres qu'on vient consulter. */
.op-sup_note-modele {
	margin: 0 0 22px;
	padding: 0;
	border: 1px solid var(--op-bordure);
	border-radius: 12px;
	background: var(--op-surface);
}

.op-sup_note-modele summary {
	padding: 13px 18px;
	font-size: 13.5px;
	font-weight: 700;
	color: var(--op-violet);
	cursor: pointer;
	list-style: none;
}

.op-sup_note-modele summary::-webkit-details-marker { display: none; }

.op-sup_note-modele summary::before {
	content: '▸';
	display: inline-block;
	margin-right: 8px;
	transition: transform .15s ease;
}

.op-sup_note-modele[open] summary::before { transform: rotate(90deg); }

.op-sup_note-modele h3 {
	margin: 18px 18px 6px;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--op-gris);
}

.op-sup_note-modele p {
	margin: 0 18px 10px;
	font-size: 13.5px;
	line-height: 1.6;
	color: var(--op-encre);
}

.op-sup_note-modele p:last-child { margin-bottom: 18px; }

/* ==================================================================
   Page « Mon équipe »
   ================================================================== */

.op-page-equipe { background: var(--op-surface); }

.op-equipe { padding: 40px 20px 60px; }

.op-equipe_contenu {
	max-width: 620px;
	margin: 0 auto;
	padding: 30px 34px 34px;
	border: 1px solid var(--op-bordure);
	border-radius: 16px;
	background: #fff;
	box-shadow: 0 18px 50px var(--op-ombre);
}

.op-equipe_contenu h1 { margin: 18px 0 10px; font-size: 24px; color: var(--op-encre); }

.op-equipe_contenu h2 {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	margin: 28px 0 10px;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--op-gris);
}

.op-equipe_intro, .op-equipe_note, .op-equipe_vide {
	margin: 0 0 14px;
	font-size: 13.5px;
	line-height: 1.6;
	color: var(--op-gris);
}

.op-equipe_compte { font-size: 11.5px; font-weight: 600; color: var(--op-violet); }

.op-equipe_liste { margin: 0; padding: 0; list-style: none; }

.op-equipe_liste li {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 11px 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: #fff;
}

.op-equipe_liste li + li { margin-top: 6px; }

.op-equipe_liste--attente li { background: var(--op-surface); border-style: dashed; }

.op-equipe_qui {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: 14px;
	color: var(--op-encre);
}

.op-equipe_role {
	margin-left: 8px;
	padding: 2px 8px;
	border-radius: 999px;
	background: rgba(118,31,227,0.10);
	font-size: 11px;
	font-style: normal;
	font-weight: 700;
	color: var(--op-violet);
	white-space: nowrap;
}

.op-equipe_retirer button {
	padding: 5px 11px;
	border: 1px solid rgba(200,50,43,0.35);
	border-radius: 8px;
	background: rgba(200,50,43,0.06);
	font-size: 12px;
	font-weight: 600;
	color: #8E241E;
	cursor: pointer;
}

.op-equipe_retirer button:hover { background: rgba(200,50,43,0.13); }

.op-equipe_form { margin-top: 4px; }
.op-equipe_form label { display: block; margin-bottom: 6px; font-size: 13px; font-weight: 600; }

.op-equipe_form input[type="email"] {
	width: 100%;
	padding: 10px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	font-size: 14px;
}

.op-equipe_form input[type="email"]:focus { border-color: var(--op-violet); outline: none; }

.op-equipe_form button {
	margin-top: 10px;
	padding: 10px 20px;
	border: none;
	border-radius: 9px;
	background: var(--op-degrade);
	font-size: 14px;
	font-weight: 700;
	color: #fff;
	cursor: pointer;
}

.op-equipe_avis {
	margin: 0 0 16px;
	padding: 11px 14px;
	border-radius: 10px;
	font-size: 13.5px;
	line-height: 1.5;
}

.op-equipe_avis--ok { background: rgba(46,158,91,0.10); border-left: 3px solid #2E9E5B; color: #1F7A46; }
.op-equipe_avis--ko { background: rgba(200,50,43,0.08); border-left: 3px solid #C8322B; color: #8E241E; }

.op-equipe_retour { margin: 26px 0 0; font-size: 13px; }
.op-equipe_retour a, .op-equipe_lien { color: var(--op-violet); text-decoration: none; font-weight: 600; }
.op-equipe_retour a:hover, .op-equipe_lien:hover { text-decoration: underline; }

/* Accès au portail de facturation Stripe, sur l'offre en cours. */
.op-abo_portail { margin-top: 10px; }

.op-abo_portail button {
	width: 100%;
	padding: 10px 18px;
	border: 1px solid var(--op-violet);
	border-radius: 9px;
	background: #fff;
	font-size: 14px;
	font-weight: 700;
	color: var(--op-violet);
	cursor: pointer;
	transition: background-color .15s ease;
}

.op-abo_portail button:hover { background: rgba(118,31,227,0.07); }
.op-abo_portail .op-compte_aide { display: block; margin-top: 7px; }

/* ------------------------------------------------------------------ *
 *  Abonnement — nombre d'utilisateurs
 *
 *  Le portail Stripe ne peut pas porter ce réglage : nos tarifs sont
 *  créés à la volée par Checkout, et le portail ne modifie une quantité
 *  que pour les tarifs de son catalogue. Le champ est donc ici.
 * ------------------------------------------------------------------ */

.op-abo_sieges { margin-top: 14px; }

.op-abo_sieges > label {
	display: block;
	margin-bottom: 6px;
	font-size: 13px;
	font-weight: 700;
	color: var(--op-encre, #1b1533);
}

.op-abo_sieges-ligne { display: flex; gap: 8px; }

.op-abo_sieges-ligne input {
	width: 88px;
	padding: 10px 12px;
	border: 1px solid #d9d3e6;
	border-radius: 9px;
	font-size: 14px;
	font-weight: 700;
	text-align: center;
}

.op-abo_sieges-ligne button {
	flex: 1;
	padding: 10px 18px;
	border: 0;
	border-radius: 9px;
	background: var(--op-violet);
	font-size: 14px;
	font-weight: 700;
	color: #fff;
	cursor: pointer;
	transition: filter .15s ease;
}

.op-abo_sieges-ligne button:hover { filter: brightness(1.08); }
.op-abo_sieges .op-compte_aide { display: block; margin-top: 7px; }

/* ------------------------------------------------------------------ *
 *  Envoi depuis le domaine du client
 * ------------------------------------------------------------------ */

.op-smtp_etat {
	margin: 10px 0 18px;
	padding: 10px 14px;
	border-radius: 10px;
	background: #f4f1fa;
	font-size: 13px;
	color: #4a4363;
}

.op-smtp_etat.est-actif {
	background: rgba(22,163,74,0.08);
	color: #166534;
}

.op-smtp_ligne {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	margin-bottom: 12px;
}

.op-smtp_champ { flex: 1 1 120px; min-width: 0; }
.op-smtp_champ--large { flex: 2 1 220px; }

.op-smtp_form .op-smtp_champ label {
	display: block;
	margin-bottom: 5px;
	font-size: 13px;
	font-weight: 600;
}

.op-smtp_form .op-smtp_champ input,
.op-smtp_form .op-smtp_champ select {
	width: 100%;
	padding: 10px 12px;
	border: 1px solid #d9d3e6;
	border-radius: 9px;
	font-size: 14px;
	background: #fff;
}

.op-smtp_actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin-top: 4px;
}

.op-smtp_actions button {
	flex: 1 1 180px;
	padding: 11px 18px;
	border: 0;
	border-radius: 9px;
	background: var(--op-violet);
	font-size: 14px;
	font-weight: 700;
	color: #fff;
	cursor: pointer;
}

.op-smtp_actions .op-smtp_secondaire {
	border: 1px solid var(--op-violet);
	background: #fff;
	color: var(--op-violet);
}

.op-smtp_arret { margin-top: 10px; }

.op-smtp_arret button {
	padding: 8px 14px;
	border: 1px solid #d9d3e6;
	border-radius: 9px;
	background: #fff;
	font-size: 13px;
	color: #6b6483;
	cursor: pointer;
}

.op-smtp_arret button:hover { border-color: #b91c1c; color: #b91c1c; }

/* ------------------------------------------------------------------ *
 *  Invitation reçue, visiteur non connecté
 *
 *  Cette page est la première chose que voit quelqu'un qui ne connaît
 *  pas OURPASS : elle doit dire qui invite, pour quoi, et quoi faire.
 * ------------------------------------------------------------------ */

.op-invitation { text-align: center; }

.op-invitation h1 {
	margin: 18px 0 14px;
	font-size: 22px;
	font-weight: 800;
	line-height: 1.3;
}

.op-invitation_qui {
	margin-bottom: 10px;
	font-size: 15px;
	line-height: 1.55;
	color: #4a4363;
}

.op-invitation .op-equipe_note { text-align: center; }

.op-invitation_actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin: 22px 0 18px;
}

.op-invitation_principal,
.op-invitation_secondaire {
	flex: 1 1 180px;
	padding: 12px 18px;
	border-radius: 10px;
	font-size: 14px;
	font-weight: 700;
	text-decoration: none;
	text-align: center;
}

.op-invitation_principal {
	background: var(--op-violet);
	color: #fff;
}

.op-invitation_principal:hover { filter: brightness(1.08); }

.op-invitation_secondaire {
	border: 1px solid var(--op-violet);
	background: #fff;
	color: var(--op-violet);
}

.op-invitation_secondaire:hover { background: rgba(118,31,227,0.07); }

.op-invitation .op-langues { justify-content: center; margin-top: 12px; }

/* Formulaire d'invitation : ce qui est imposé par le lien, et ce qui reste
   à saisir. Les deux doivent se distinguer au premier regard. */

.op-invitation_form { margin-top: 18px; text-align: left; }

.op-invitation_fige {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 12px;
	padding: 10px 14px;
	margin-bottom: 8px;
	border-radius: 9px;
	background: #f4f1fa;
	font-size: 14px;
}

.op-invitation_etiquette {
	font-size: 12px;
	font-weight: 600;
	color: #6b6483;
	text-transform: uppercase;
	letter-spacing: .03em;
}

.op-invitation_fige strong { word-break: break-all; }

.op-invitation_form label { display: block; margin: 14px 0 5px; font-size: 13px; font-weight: 600; }

.op-invitation_form input[type="password"] {
	width: 100%;
	padding: 11px 12px;
	border: 1px solid #d9d3e6;
	border-radius: 9px;
	font-size: 14px;
}

.op-invitation_case {
	display: flex !important;
	gap: 9px;
	align-items: flex-start;
	margin: 16px 0 0 !important;
	font-size: 13px;
	font-weight: 400 !important;
	line-height: 1.45;
}

.op-invitation_case input { margin-top: 3px; flex: 0 0 auto; }

.op-invitation_case--alerte {
	padding: 12px 14px;
	border: 1px solid #fca5a5;
	border-radius: 9px;
	background: rgba(220,38,38,0.05);
}

.op-invitation_oubli { margin-top: 8px; font-size: 13px; }

.op-invitation_form button,
.op-invitation_confirme button {
	width: 100%;
	margin-top: 18px;
	padding: 12px 18px;
	border: 0;
	border-radius: 10px;
	background: var(--op-violet);
	font-size: 15px;
	font-weight: 700;
	color: #fff;
	cursor: pointer;
}

.op-invitation_form button:hover,
.op-invitation_confirme button:hover { filter: brightness(1.08); }

/* Réglage de l'expiration de session, page Mon équipe. */
.op-delai_form { margin-bottom: 26px; }
.op-delai_ligne { display: flex; flex-wrap: wrap; gap: 8px; }
.op-delai_ligne select {
	flex: 1 1 200px;
	padding: 10px 12px;
	border: 1px solid #d9d3e6;
	border-radius: 9px;
	background: #fff;
	font-size: 14px;
}
.op-delai_ligne button {
	flex: 0 0 auto;
	padding: 10px 18px;
	border: 0;
	border-radius: 9px;
	background: var(--op-violet);
	font-size: 14px;
	font-weight: 700;
	color: #fff;
	cursor: pointer;
}
.op-delai_form .op-compte_aide { display: block; margin-top: 7px; }

/* Vérification d'appareil : réglage d'équipe et saisie du code. */
.op-appareil_form { margin-bottom: 26px; }
.op-appareil_form button {
	margin-top: 14px;
	padding: 10px 18px;
	border: 0;
	border-radius: 9px;
	background: var(--op-violet);
	font-size: 14px;
	font-weight: 700;
	color: #fff;
	cursor: pointer;
}
.op-appareil_avert { margin-top: 10px !important; font-size: 12.5px; }

.op-code_champ {
	font-size: 26px !important;
	font-weight: 700;
	letter-spacing: 10px;
	text-align: center;
}

.op-login_intro { margin-bottom: 16px; font-size: 14px; line-height: 1.55; }

.op-login_case {
	display: flex;
	gap: 9px;
	align-items: flex-start;
	margin: 4px 0 16px;
	font-size: 13px;
	line-height: 1.45;
}
.op-login_case input { margin-top: 3px; }

/* Le renvoi de code est un bouton, pas un lien : il poste le formulaire.
   Il doit néanmoins se lire comme les liens qui l'entourent. */
.op-login_lien-bouton {
	padding: 0;
	border: 0;
	background: none;
	font: inherit;
	color: var(--op-violet);
	text-decoration: underline;
	cursor: pointer;
}

/* Remise du SMS, distincte de la consultation du lien. */
.op-sms_etat { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }
.op-sms_etat::before {
	content: "";
	flex: 0 0 auto;
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: currentColor;
	opacity: .75;
}
.op-sms_etat--remis      { color: #15803D; }
.op-sms_etat--en_attente { color: #8A5A00; }
.op-sms_etat--echec      { color: #B3261E; }

/* ------------------------------------------------------------------ *
 *  Registre de preuve
 * ------------------------------------------------------------------ */

.op-reg_verdict {
	display: flex;
	flex-direction: column;
	gap: 3px;
	margin: 18px 0;
	padding: 14px 16px;
	border-radius: 10px;
	border-left: 4px solid currentColor;
	font-size: 14px;
}
.op-reg_verdict strong { font-size: 15px; }
.op-reg_verdict span { color: var(--op-gris); }
.op-reg_verdict.est-intact { color: #15803D; background: rgba(21,128,61,0.07); }
.op-reg_verdict.est-rompu  { color: #B3261E; background: rgba(179,38,30,0.07); }

.op-reg_choix { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 16px 0; }
.op-reg_choix label { font-size: 13px; font-weight: 600; }
.op-reg_choix select {
	padding: 8px 11px;
	border: 1px solid var(--op-bordure);
	border-radius: 8px;
	background: #fff;
	font-size: 13px;
}

.op-reg_actions { display: flex; flex-wrap: wrap; gap: 9px; margin: 16px 0 22px; }

.op-reg_export {
	padding: 9px 15px;
	border: 1px solid var(--op-violet);
	border-radius: 9px;
	font-size: 13px;
	font-weight: 700;
	color: var(--op-violet);
	text-decoration: none;
}
.op-reg_export:hover { background: rgba(118,31,227,0.07); }
.op-reg_export--csv,
.op-reg_export--equipe { border-color: var(--op-bordure); color: var(--op-gris); }

/* Le tableau déborde sur mobile : il défile dans son propre cadre plutôt
   que de faire glisser toute la page. */
.op-reg_table {
	display: block;
	overflow-x: auto;
	width: 100%;
	border-collapse: collapse;
	font-size: 13px;
	white-space: nowrap;
}
.op-reg_table th {
	padding: 9px 12px;
	border-bottom: 2px solid var(--op-bordure);
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: .04em;
	text-transform: uppercase;
	color: var(--op-gris);
	text-align: left;
}
.op-reg_table td { padding: 9px 12px; border-bottom: 1px solid var(--op-bordure); }
.op-reg_table tr:hover td { background: rgba(118,31,227,0.03); }
.op-reg_table em { display: block; font-size: 12px; color: var(--op-gris); font-style: normal; }

.op-reg_seq { font-variant-numeric: tabular-nums; color: var(--op-gris); }
.op-reg_jeton,
.op-reg_emp {
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 12px;
	color: var(--op-gris);
}

/* Jeton retenu : le lien est encore ouvrable. */
.op-reg_masque {
	font-family: 'Inter', system-ui, sans-serif;
	font-size: 12px;
	font-style: italic;
	color: #8A5A00;
}

/* Explication du registre, repliée par défaut. */
.op-reg_aide {
	margin: 18px 0 22px;
	padding: 0;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: var(--op-surface, #fff);
}
.op-reg_aide summary {
	padding: 13px 16px;
	font-size: 14px;
	font-weight: 700;
	color: var(--op-violet);
	cursor: pointer;
	list-style: none;
}
.op-reg_aide summary::-webkit-details-marker { display: none; }
.op-reg_aide summary::before { content: "▸ "; }
.op-reg_aide[open] summary::before { content: "▾ "; }
.op-reg_aide > p { margin: 0 16px 14px; font-size: 13.5px; line-height: 1.6; color: var(--op-gris); }
.op-reg_aide-fort { padding: 12px 16px; margin: 0 16px 14px !important; background: rgba(118,31,227,0.05); border-radius: 8px; }

.op-reg_glossaire { margin: 0 16px 14px; font-size: 13.5px; }
.op-reg_glossaire dt {
	margin-top: 12px;
	font-weight: 700;
	font-size: 11.5px;
	letter-spacing: .05em;
	text-transform: uppercase;
	color: var(--op-encre, #1b1533);
}
.op-reg_glossaire dd { margin: 3px 0 0; line-height: 1.6; color: var(--op-gris); }

/* ------------------------------------------------------------------ *
 *  Rapport d'activité
 *
 *  Pensé pour être imprimé : pas de carte interactive, pas de couleur
 *  qui disparaît en noir et blanc, et des blocs qui ne se coupent pas
 *  en travers d'une page.
 * ------------------------------------------------------------------ */

.op-rapport { max-width: 780px; }

.op-rap_entete { padding-bottom: 18px; border-bottom: 2px solid var(--op-encre, #1b1533); }
.op-rap_entete h1 { margin: 14px 0 6px; font-size: 26px; font-weight: 800; letter-spacing: -0.02em; }
.op-rap_sous { margin: 0; font-size: 13.5px; color: var(--op-gris); }

.op-rap_periode { display: flex; flex-wrap: wrap; gap: 7px; margin: 20px 0 26px; }
.op-rap_periode button {
	padding: 8px 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 999px;
	background: #fff;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-gris);
	cursor: pointer;
}
.op-rap_periode button.est-active { border-color: var(--op-violet); background: var(--op-violet); color: #fff; }
.op-rap_imprimer {
	margin-left: auto;
	align-self: center;
	font-size: 13px;
	font-weight: 700;
	color: var(--op-violet);
	text-decoration: none;
}

.op-rap_chiffres {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
	gap: 1px;
	margin-bottom: 30px;
	background: var(--op-bordure);
	border: 1px solid var(--op-bordure);
	border-radius: 12px;
	overflow: hidden;
}
.op-rap_chiffres div { background: #fff; padding: 16px 18px; }
.op-rap_chiffres .n {
	display: block;
	font-size: 27px;
	font-weight: 800;
	line-height: 1;
	letter-spacing: -0.03em;
	font-variant-numeric: tabular-nums;
}
.op-rap_chiffres .k {
	display: block;
	margin-top: 6px;
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: .04em;
	text-transform: uppercase;
	color: var(--op-gris);
}

.op-rap_bloc { margin-bottom: 30px; }
.op-rap_bloc h2 {
	margin: 0 0 10px;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--op-violet);
	padding-bottom: 7px;
	border-bottom: 1px solid var(--op-bordure);
}
.op-rap_note { margin: 0 0 14px; font-size: 13px; line-height: 1.55; color: var(--op-gris); }
.op-rap_avert { padding: 10px 13px; background: rgba(138,90,0,0.07); border-radius: 8px; color: #6E4700; }

.op-rap_courbe { display: block; width: 100%; height: 150px; }
.op-rap_legende { display: flex; justify-content: space-between; margin: 4px 0 0; font-size: 11.5px; color: var(--op-gris); }

.op-rap_barre { display: flex; align-items: center; gap: 12px; margin-bottom: 9px; font-size: 13px; }
.op-rap_barre-lib { flex: 0 0 42%; }
.op-rap_barre-piste { flex: 1; height: 9px; border-radius: 999px; background: var(--op-bordure); overflow: hidden; }
.op-rap_barre-piste span { display: block; height: 100%; border-radius: 999px; }
.op-rap_barre-val { flex: 0 0 5.5rem; text-align: right; font-variant-numeric: tabular-nums; color: var(--op-gris); }

.op-rap_table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.op-rap_table th { text-align: left; font-weight: 600; padding: 8px 0; }
.op-rap_table td { padding: 8px 0; text-align: right; font-variant-numeric: tabular-nums; }
.op-rap_table tr + tr th, .op-rap_table tr + tr td { border-top: 1px solid var(--op-bordure); }
.op-rap_table--large thead th { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--op-gris); }
.op-rap_table--large td:first-child, .op-rap_table--large th:first-child { text-align: left; }

@media print {
	.op-rap_periode, .op-equipe_retour, .op-compte_logo { display: none !important; }
	.op-rapport { max-width: none; }
	.op-rap_bloc, .op-rap_chiffres { break-inside: avoid; }
	body { background: #fff; }
	@page { margin: 15mm; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Ma sécurité — double authentification et appareils reconnus
   ══════════════════════════════════════════════════════════════════════════ */

/* Les trois étapes d'activation. Numérotées parce que l'ordre compte
   réellement : on ne peut pas confirmer un code avant d'avoir posé la clé. */
.op-totp_etapes {
	margin: 0 0 18px;
	padding-left: 20px;
	font-size: 13.5px;
	line-height: 1.7;
	color: var(--op-gris);
}

.op-totp_etapes li + li { margin-top: 10px; }

/* La clé, à recopier à la main. Chasse fixe et bien espacée : c'est le seul
   endroit du service où l'on demande à quelqu'un de lire vingt-six
   caractères sans se tromper. */
.op-totp_secret {
	display: block;
	margin: 10px 0;
	padding: 14px 16px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: var(--op-surface);
	font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace;
	font-size: 17px;
	font-weight: 600;
	letter-spacing: 0.14em;
	color: var(--op-encre);
	word-break: break-all;
	user-select: all;
}

.op-totp_lien {
	padding: 0;
	border: none;
	background: none;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-violet);
	text-decoration: underline;
	cursor: pointer;
}

.op-totp_lien:hover { color: var(--op-violet-fonce); }

/* Le champ de saisie du code : même traitement que la clé, en plus large. */
.op-totp_champ {
	font-family: 'SFMono-Regular', Consolas, monospace;
	font-size: 20px;
	letter-spacing: 0.32em;
	text-align: center;
}

.op-totp_boutons { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

.op-totp_annuler {
	background: none !important;
	color: var(--op-gris) !important;
	border: 1px solid var(--op-bordure) !important;
}

.op-totp_danger {
	background: #DC2626 !important;
	background-image: none !important;
}

.op-totp_etat {
	display: inline-block;
	margin: 0 0 10px;
	padding: 7px 13px;
	border-radius: 999px;
	background: rgba(22,163,74,0.10);
	font-size: 13px;
	font-weight: 600;
	color: #15803D;
}

.op-totp_impose {
	padding: 11px 14px;
	border-left: 3px solid var(--op-violet);
	background: var(--op-surface);
}

.op-totp_retirer { margin-top: 18px; }

.op-totp_retirer summary {
	font-size: 13px;
	color: var(--op-gris);
	cursor: pointer;
}

/* Les codes de secours. Encadrés en rouge non pour alarmer, mais parce que
   c'est le seul écran du service qui ne se réaffichera jamais. */
.op-totp_secours {
	margin: 0 0 26px;
	padding: 18px 20px;
	border: 2px solid #DC2626;
	border-radius: 12px;
	background: rgba(220,38,38,0.03);
}

.op-totp_secours h2 { margin-top: 0; }

.op-totp_secours-avert {
	margin: 0 0 14px;
	font-size: 13.5px;
	line-height: 1.6;
	font-weight: 600;
	color: #991B1B;
}

.op-totp_codes {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
	gap: 8px;
	margin: 0 0 14px;
	padding: 0;
	list-style: none;
}

.op-totp_codes li {
	padding: 10px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 8px;
	background: #fff;
	font-family: 'SFMono-Regular', Consolas, monospace;
	font-size: 15px;
	font-weight: 600;
	letter-spacing: 0.05em;
	text-align: center;
	color: var(--op-encre);
	user-select: all;
}

/* Marques dans la liste des membres. */
.op-totp_marque { color: #15803D !important; }
.op-totp_manque { color: #B45309 !important; font-weight: 600; }

/* Liste des appareils reconnus. */
.op-appareil_liste li { flex-wrap: wrap; }

.op-appareil_liste li.est-courant {
	border-color: var(--op-violet);
	background: var(--op-surface);
}

.op-appareil_ici {
	margin-left: 8px;
	font-size: 11.5px;
	font-weight: 700;
	font-style: normal;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--op-violet);
}

.op-appareil_oubli { margin: 0; }

.op-appareil_oubli button { font-size: 12.5px; }

/* À l'impression, seuls les codes de secours ont un sens sur papier. */
@media print {
	.op-equipe_intro, .op-equipe_note, .op-equipe_retour,
	.op-equipe_form, .op-appareil_liste, .op-totp_etapes,
	.op-compte_logo, .op-totp_retirer { display: none !important; }

	.op-totp_secours { border-color: #000; }
}

/* Le QR d'activation. Fond blanc conservé quel que soit le thème : un
   lecteur attend du noir sur blanc, et l'inverser fait échouer la moitié
   des appareils photo. */
.op-totp_qr {
	display: inline-block;
	margin: 12px 0 6px;
	padding: 10px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: #fff;
	line-height: 0;
}

.op-totp_qr svg { display: block; max-width: 100%; height: auto; }

.op-totp_sinon { margin: 4px 0 8px; font-size: 13px; }

.op-totp_manuel summary {
	font-size: 13px;
	color: var(--op-gris);
	cursor: pointer;
}

@media print {
	.op-totp_qr, .op-totp_manuel, .op-totp_sinon { display: none !important; }
}

/* Renvoi du propriétaire vers le réglage d'équipe. */
.op-totp_equipe {
	margin-top: 18px;
	padding: 12px 14px;
	border-left: 3px solid var(--op-bordure);
	background: var(--op-surface);
}

/* ══════════════════════════════════════════════════════════════════════════
   Page d'accueil publique

   Tout est préfixé op-acc_ : la feuille dépasse huit mille lignes, et une
   classe générique de plus y trouverait toujours quelqu'un à contredire.
   ══════════════════════════════════════════════════════════════════════════ */

/* Le remplissage compris dans la largeur, et non ajoute a elle.

   Sans cela, `.op-acc_bande` — largeur 100 %, remplissage 24 px de chaque
   cote — mesure la largeur de l'ecran PLUS quarante-huit pixels, et pousse
   toute la page. Constate sur un iPhone de 375 px : page large de 403.

   La feuille n'a aucun `box-sizing` global ; on le pose donc pour cette
   page, sans toucher au reste du site. */
.op-page-accueil *,
.op-page-accueil *::before,
.op-page-accueil *::after {
	box-sizing: border-box;
}

.op-page-accueil {
	background: #FFFFFF;
	color: var(--op-encre);
}

.op-acc_bande {
	width: 100%;
	max-width: 1120px;
	margin: 0 auto;
	padding: 0 24px;
}

.op-acc_bande--etroite { max-width: 760px; }

/* ── En-tête ───────────────────────────────────────────────────────────── */

.op-acc_entete {
	position: sticky;
	top: 0;
	z-index: 20;
	background: var(--op-violet-fonce);
	border-bottom: 1px solid rgba(255,255,255,0.08);
}

.op-acc_entete .op-acc_bande {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 20px;
	min-height: 66px;
}

.op-acc_marque { text-decoration: none; }

.op-acc_nav {
	display: flex;
	align-items: center;
	gap: 26px;
	font-size: 14px;
}

.op-acc_nav a {
	color: rgba(255,255,255,0.78);
	text-decoration: none;
	transition: color 160ms ease;
}

.op-acc_nav a:hover { color: #FFFFFF; }

.op-acc_connexion {
	padding: 8px 16px;
	border: 1px solid rgba(255,255,255,0.30);
	border-radius: 999px;
	color: #FFFFFF !important;
	font-weight: 600;
}

.op-acc_connexion:hover { background: rgba(255,255,255,0.10); }

/* ── Ouverture ─────────────────────────────────────────────────────────── */

.op-acc_hero {
	padding: 76px 0 84px;
	background: var(--op-degrade);
	color: #FFFFFF;
}

.op-acc_hero-grille {
	display: grid;
	grid-template-columns: 1.35fr 1fr;
	gap: 56px;
	align-items: center;
}

.op-acc_surtitre {
	margin: 0 0 16px;
	font-size: 12.5px;
	font-weight: 700;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: #C4A5FF;
}

.op-acc_hero h1 {
	margin: 0 0 20px;
	font-size: clamp(32px, 4.4vw, 52px);
	line-height: 1.12;
	letter-spacing: -0.022em;
	font-weight: 700;
	color: #FFFFFF;
	text-wrap: balance;
}

.op-acc_chapo {
	margin: 0 0 30px;
	max-width: 56ch;
	font-size: 16.5px;
	line-height: 1.65;
	color: rgba(255,255,255,0.86);
}

.op-acc_actions {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	margin: 0 0 26px;
}

.op-acc_bouton {
	display: inline-block;
	padding: 14px 26px;
	border: none;
	border-radius: 10px;
	background: #FFFFFF;
	font-size: 15px;
	font-weight: 700;
	color: var(--op-violet-fonce);
	text-decoration: none;
	transition: transform 140ms ease, box-shadow 140ms ease;
}

.op-acc_bouton:hover {
	transform: translateY(-1px);
	box-shadow: 0 10px 24px rgba(0,0,0,0.18);
	color: var(--op-violet-fonce);
}

.op-acc_bouton--pale {
	background: rgba(255,255,255,0.12);
	color: #FFFFFF;
	box-shadow: inset 0 0 0 1px rgba(255,255,255,0.32);
}

.op-acc_bouton--pale:hover { background: rgba(255,255,255,0.2); color: #FFFFFF; }

.op-acc_bouton--clair { background: var(--op-degrade); color: #FFFFFF; }
.op-acc_bouton--clair:hover { color: #FFFFFF; }

.op-acc_bouton--creux {
	background: transparent;
	color: var(--op-violet);
	box-shadow: inset 0 0 0 1.5px var(--op-violet);
}

.op-acc_bouton--creux:hover { background: var(--op-surface); color: var(--op-violet-fonce); }

.op-acc_gages {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 22px;
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: 13.5px;
	color: rgba(255,255,255,0.72);
}

.op-acc_gages li::before {
	content: "✓";
	margin-right: 7px;
	font-weight: 700;
	color: #8CE0B0;
}

/* Le cycle de vie d'un lien. La quatrième étape s'efface : c'est la promesse
   du produit, autant la donner à voir plutôt qu'à lire. */
.op-acc_cycle {
	margin: 0;
	padding: 26px 24px;
	border-radius: 16px;
	background: rgba(0,0,0,0.16);
	box-shadow: inset 0 0 0 1px rgba(255,255,255,0.10);
	list-style: none;
	counter-reset: none;
}

.op-acc_cycle li {
	position: relative;
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 14px 0;
	font-size: 15px;
	font-weight: 600;
	color: #FFFFFF;
}

/* Le fil qui relie les étapes. */
.op-acc_cycle li:not(:last-child)::after {
	content: "";
	position: absolute;
	left: 15px;
	top: 44px;
	width: 2px;
	height: 22px;
	background: rgba(255,255,255,0.22);
}

.op-acc_cycle-num {
	flex: 0 0 auto;
	display: grid;
	place-items: center;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: rgba(255,255,255,0.14);
	box-shadow: inset 0 0 0 1px rgba(255,255,255,0.28);
	font-size: 13px;
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

.op-acc_cycle li.est-fin { color: rgba(255,255,255,0.5); }

.op-acc_cycle li.est-fin .op-acc_cycle-num {
	background: transparent;
	box-shadow: inset 0 0 0 1px rgba(255,255,255,0.22);
	color: rgba(255,255,255,0.5);
}

/* ── Sections courantes ────────────────────────────────────────────────── */

.op-acc_section { padding: 76px 0; }
.op-acc_section--pale { background: var(--op-surface); }

.op-acc_section h2,
.op-acc_probleme h2,
.op-acc_final h2 {
	margin: 0 0 14px;
	font-size: clamp(25px, 2.9vw, 34px);
	line-height: 1.2;
	letter-spacing: -0.018em;
	font-weight: 700;
	color: var(--op-encre);
	text-wrap: balance;
}

.op-acc_intro {
	margin: 0 0 40px;
	max-width: 62ch;
	font-size: 16px;
	line-height: 1.65;
	color: var(--op-gris);
}

/* ── Le problème ───────────────────────────────────────────────────────── */

.op-acc_probleme {
	padding: 76px 0;
	background: var(--op-encre);
	color: #FFFFFF;
}

.op-acc_probleme h2 { color: #FFFFFF; }
.op-acc_probleme .op-acc_intro { color: rgba(255,255,255,0.74); margin-bottom: 26px; }

.op-acc_ou {
	margin: 0 0 30px;
	padding: 0;
	list-style: none;
	max-width: 62ch;
}

.op-acc_ou li {
	padding: 12px 0 12px 26px;
	border-bottom: 1px solid rgba(255,255,255,0.10);
	font-size: 15.5px;
	line-height: 1.5;
	color: rgba(255,255,255,0.84);
	position: relative;
}

.op-acc_ou li::before {
	content: "";
	position: absolute;
	left: 2px;
	top: 21px;
	width: 8px;
	height: 8px;
	border-radius: 2px;
	background: #F97066;
}

.op-acc_pointe {
	margin: 0;
	max-width: 60ch;
	font-size: 17px;
	line-height: 1.6;
	font-weight: 600;
	color: #FFFFFF;
}

/* ── Étapes ────────────────────────────────────────────────────────────── */

.op-acc_etapes {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
	gap: 26px;
	margin: 0;
	padding: 0;
	list-style: none;
	counter-reset: etape;
}

/* Les quatre etapes dans un cadre, comme les trois cartes d'usage.

   Elles flottaient sur le fond de section, quand la section suivante presente
   ses trois cas dans des cartes bordees : deux fois la meme figure — un titre,
   un texte court, cote a cote — traitees de deux facons a trois centimetres
   d'ecart. Le cadre est repris a l'identique de `.op-acc_usage` (bordure,
   rayon, fond, retraits) plutot que redessine : deux cadres presque pareils
   se remarquent plus qu'un seul. */
.op-acc_etapes li {
	counter-increment: etape;
	padding: 22px 24px;
	border: 1px solid var(--op-bordure);
	border-radius: 14px;
	background: #FFFFFF;
}

.op-acc_etapes li::before {
	content: counter(etape);
	display: grid;
	place-items: center;
	width: 34px;
	height: 34px;
	margin-bottom: 14px;
	border-radius: 50%;
	background: var(--op-degrade);
	font-size: 14px;
	font-weight: 700;
	color: #FFFFFF;
	font-variant-numeric: tabular-nums;
}

.op-acc_etapes h3 {
	margin: 0 0 8px;
	font-size: 16.5px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-acc_etapes p {
	margin: 0;
	font-size: 14.5px;
	line-height: 1.65;
	color: var(--op-gris);
}

/* ── Cartes de différenciation ─────────────────────────────────────────── */

.op-acc_cartes {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
	gap: 20px;
}

.op-acc_carte {
	padding: 26px;
	border: 1px solid var(--op-bordure);
	border-radius: 14px;
	background: #FFFFFF;
}

.op-acc_carte h3 {
	margin: 0 0 10px;
	font-size: 17px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-acc_carte p {
	margin: 0;
	font-size: 14.5px;
	line-height: 1.65;
	color: var(--op-gris);
}

/* La carte du registre porte tout l'argumentaire : elle occupe la largeur
   entière et prend la couleur de la marque. C'est le seul endroit de la
   page où l'on hausse le ton. */
.op-acc_carte.est-phare {
	grid-column: 1 / -1;
	border: none;
	background: var(--op-degrade);
	color: #FFFFFF;
}

.op-acc_carte.est-phare h3 { color: #FFFFFF; font-size: 19px; }
.op-acc_carte.est-phare p { color: rgba(255,255,255,0.86); max-width: 78ch; }

.op-acc_carte-pointe {
	margin-top: 14px !important;
	padding-top: 14px;
	border-top: 1px solid rgba(255,255,255,0.20);
	font-weight: 600;
	color: #FFFFFF !important;
}

/* ── Fiche technique ───────────────────────────────────────────────────── */

.op-acc_fiche {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
	gap: 0 40px;
	margin: 0 0 30px;
}

.op-acc_fiche > div {
	padding: 16px 0;
	border-bottom: 1px solid var(--op-bordure);
}

.op-acc_fiche dt {
	margin-bottom: 5px;
	font-size: 12.5px;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--op-violet);
}

.op-acc_fiche dd {
	margin: 0;
	font-size: 14.5px;
	line-height: 1.6;
	color: var(--op-gris);
}

/* ── Tarifs ────────────────────────────────────────────────────────────── */

.op-acc_offres {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
	gap: 22px;
	align-items: start;
}

.op-acc_offre {
	position: relative;
	display: flex;
	flex-direction: column;
	padding: 30px 26px;
	border: 1px solid var(--op-bordure);
	border-radius: 16px;
	background: #FFFFFF;
}

.op-acc_offre.est-phare {
	border-color: var(--op-violet);
	box-shadow: 0 14px 40px rgba(118,31,227,0.14);
}

.op-acc_offre-ruban {
	position: absolute;
	top: -12px;
	left: 26px;
	margin: 0;
	padding: 5px 13px;
	border-radius: 999px;
	background: var(--op-degrade);
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: #FFFFFF;
}

.op-acc_offre h3 {
	margin: 0 0 6px;
	font-size: 19px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-acc_offre-resume {
	margin: 0 0 20px;
	font-size: 14px;
	line-height: 1.55;
	color: var(--op-gris);
	min-height: 44px;
}

.op-acc_offre-prix {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 7px;
	margin: 0 0 6px;
}

.op-acc_offre-prix strong {
	font-size: 34px;
	font-weight: 700;
	letter-spacing: -0.02em;
	color: var(--op-encre);
	font-variant-numeric: tabular-nums;
}

.op-acc_offre-prix span { font-size: 13.5px; color: var(--op-gris); }

.op-acc_offre-annuel {
	margin: 0 0 4px;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--op-violet);
}

.op-acc_offre-sieges {
	margin: 0 0 20px;
	font-size: 13px;
	color: var(--op-gris);
}

.op-acc_offre-points {
	flex: 1 1 auto;
	margin: 0 0 24px;
	padding: 20px 0 0;
	border-top: 1px solid var(--op-bordure);
	list-style: none;
}

.op-acc_offre-points li {
	position: relative;
	padding: 7px 0 7px 24px;
	font-size: 14px;
	line-height: 1.5;
	color: var(--op-gris);
}

.op-acc_offre-points li::before {
	content: "✓";
	position: absolute;
	left: 0;
	font-weight: 700;
	color: var(--op-violet);
}

.op-acc_offre .op-acc_bouton { text-align: center; }

.op-acc_note {
	margin: 30px 0 0;
	max-width: 70ch;
	font-size: 13px;
	line-height: 1.6;
	color: var(--op-gris);
}

/* ── Objections ────────────────────────────────────────────────────────── */

.op-acc_question {
	border-bottom: 1px solid var(--op-bordure);
}

.op-acc_question summary {
	padding: 20px 0;
	font-size: 16px;
	font-weight: 600;
	color: var(--op-encre);
	cursor: pointer;
	list-style: none;
	position: relative;
	padding-right: 34px;
}

.op-acc_question summary::-webkit-details-marker { display: none; }

.op-acc_question summary::after {
	content: "+";
	position: absolute;
	right: 4px;
	top: 17px;
	font-size: 22px;
	font-weight: 400;
	color: var(--op-violet);
	transition: transform 180ms ease;
}

.op-acc_question[open] summary::after { content: "−"; }

.op-acc_question p {
	margin: 0 0 22px;
	max-width: 68ch;
	font-size: 15px;
	line-height: 1.7;
	color: var(--op-gris);
}

/* ── Dernier appel ─────────────────────────────────────────────────────── */

.op-acc_final {
	padding: 76px 0;
	background: var(--op-surface);
	text-align: center;
}

.op-acc_final p {
	max-width: 58ch;
	margin: 0 auto 28px;
	font-size: 16px;
	line-height: 1.65;
	color: var(--op-gris);
}

.op-acc_final .op-acc_actions { justify-content: center; margin-bottom: 0; }

/* ── Pied de page ──────────────────────────────────────────────────────── */

.op-acc_pied {
	padding: 40px 0;
	background: var(--op-violet-fonce);
	color: rgba(255,255,255,0.72);
}

.op-acc_pied-grille {
	display: flex;
	flex-wrap: wrap;
	gap: 24px;
	align-items: flex-start;
	justify-content: space-between;
}

.op-acc_pied-note {
	margin: 12px 0 0;
	max-width: 48ch;
	font-size: 13px;
	line-height: 1.6;
}

.op-acc_pied-liens {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 22px;
	font-size: 13.5px;
}

.op-acc_pied-liens a {
	color: rgba(255,255,255,0.78);
	text-decoration: none;
}

.op-acc_pied-liens a:hover { color: #FFFFFF; text-decoration: underline; }

/* ── Petits écrans ─────────────────────────────────────────────────────── */

@media (max-width: 860px) {
	.op-acc_hero-grille { grid-template-columns: 1fr; gap: 36px; }
	.op-acc_hero { padding: 52px 0 60px; }
	.op-acc_section, .op-acc_probleme, .op-acc_final { padding: 54px 0; }
	.op-acc_nav { gap: 16px; font-size: 13.5px; }
	.op-acc_nav a:not(.op-acc_connexion) { display: none; }
	.op-acc_carte.est-phare { grid-column: auto; }
}

@media (prefers-reduced-motion: reduce) {
	.op-acc_bouton, .op-acc_question summary::after { transition: none; }
	.op-acc_bouton:hover { transform: none; }
}

/* Sélecteur de langue au pied de l'accueil : fond sombre, et la note
   destinée aux expéditeurs n'a pas lieu d'être devant un visiteur qui n'a
   pas encore de compte. */
.op-acc_pied-droite {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 16px 22px;
}

.op-acc_pied .op-langues {
	border-color: rgba(255,255,255,0.22);
	background: rgba(255,255,255,0.06);
}

.op-acc_pied .op-langues_choix { color: rgba(255,255,255,0.78) !important; }

.op-acc_pied .op-langues_choix:hover {
	background: rgba(255,255,255,0.14);
	color: #FFFFFF !important;
}

.op-acc_pied .op-langues_choix.est-active {
	background: #FFFFFF;
	color: var(--op-violet-fonce) !important;
}

.op-acc_pied .op-langues_note { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════════
   Illustrations de l'accueil
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Section du suivi ──────────────────────────────────────────────────
   Le tableau tenait d'abord en vignette sous l'ouverture. Une vignette
   décore ; ce tableau doit se lire. Il a donc sa section et sa largeur. */

.op-acc_suivi-grille {
	display: grid;
	grid-template-columns: 1.25fr 1fr;
	gap: 36px;
	align-items: start;
}

.op-vis_fenetre {
	border-radius: 14px;
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	box-shadow: 0 18px 44px rgba(26,21,35,0.10);
	overflow: hidden;
}

/* La barre de titre : trois pastilles, comme toute fenêtre. Assez pour dire
   « voici l'application » sans prétendre être une capture d'écran. */
.op-vis_barre {
	display: flex;
	align-items: center;
	gap: 7px;
	padding: 11px 15px;
	border-bottom: 1px solid var(--op-bordure);
	background: var(--op-surface);
}

.op-vis_barre span {
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: var(--op-bordure);
}

.op-vis_barre b {
	margin-left: 8px;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--op-gris);
}

.op-vis_table { width: 100%; border-collapse: collapse; }

.op-vis_table th {
	padding: 11px 16px;
	border-bottom: 1px solid var(--op-bordure);
	background: rgba(250,248,255,0.6);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	text-align: left;
	color: var(--op-gris);
}

.op-vis_table th:last-child { text-align: right; }

.op-vis_table td {
	padding: 13px 16px;
	border-bottom: 1px solid var(--op-bordure);
	font-size: 13.5px;
	vertical-align: middle;
}

.op-vis_table tr:last-child td { border-bottom: none; }

.op-vis_qui {
	color: var(--op-encre);
	font-weight: 500;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.op-vis_quand {
	font-size: 12.5px;
	color: var(--op-gris);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.op-vis_lieu { text-align: right; font-size: 15px; }

.op-vis_pastille {
	display: inline-block;
	padding: 4px 11px;
	border-radius: 999px;
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: 0.02em;
	white-space: nowrap;
}

.op-vis_pastille.est-consulte { background: rgba(22,163,74,0.12);  color: #15803D; }
.op-vis_pastille.est-attente  { background: rgba(180,83,9,0.12);   color: #B45309; }
.op-vis_pastille.est-refuse   { background: rgba(220,38,38,0.10);  color: #B91C1C; }
.op-vis_pastille.est-expire   { background: rgba(75,69,96,0.10);   color: var(--op-gris); }
.op-vis_pastille.est-revoque  { background: rgba(118,31,227,0.10); color: var(--op-violet); }

/* Ce que le suivi vous apprend, en regard du tableau. */
.op-acc_suivi-points { margin: 0; padding: 0; list-style: none; }

.op-acc_suivi-points li {
	position: relative;
	padding: 0 0 20px 22px;
}

.op-acc_suivi-points li:last-child { padding-bottom: 0; }

.op-acc_suivi-points li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 7px;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--op-violet);
}

.op-acc_suivi-points b {
	display: block;
	margin-bottom: 3px;
	font-size: 15px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-acc_suivi-points span {
	display: block;
	font-size: 14px;
	line-height: 1.6;
	color: var(--op-gris);
}

@media (max-width: 860px) {
	.op-acc_suivi-grille { grid-template-columns: 1fr; gap: 28px; }
	.op-vis_table th:nth-child(2),
	.op-vis_table td.op-vis_quand { display: none; }
}

/* ── Pictogrammes des étapes ───────────────────────────────────────────── */

.op-vis_picto {
	width: 26px;
	height: 26px;
	margin-bottom: 12px;
	fill: none;
	stroke: var(--op-violet);
	stroke-width: 1.8;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* Le pictogramme remplace la pastille numérotée : deux marqueurs pour la
   même étape se disputeraient l'attention. */
.op-acc_etapes li::before { display: none; }

/* ── Chaîne d'empreintes du registre ───────────────────────────────────
   Refaite en HTML : le SVG rendait ces libellés minuscules dès que la carte
   se rétrécissait, et c'est justement le visuel qu'il faut lire. */

.op-vis_registre {
	max-width: 520px;
	margin: 24px 0 4px;
}

.op-vis_pile-titre,
.op-vis_retouche-titre {
	margin: 0 0 14px;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: rgba(255,255,255,0.66) !important;
}

.op-vis_maillon {
	display: flex;
	gap: 13px;
	padding: 13px 15px;
	border-radius: 11px;
	background: rgba(255,255,255,0.10);
	box-shadow: inset 0 0 0 1px rgba(255,255,255,0.16);
}

.op-vis_maillon-num {
	flex: 0 0 auto;
	display: grid;
	place-items: center;
	width: 24px;
	height: 24px;
	border-radius: 50%;
	background: rgba(255,255,255,0.18);
	font-size: 12px;
	font-weight: 700;
	color: #FFFFFF;
	font-variant-numeric: tabular-nums;
}

.op-vis_maillon-corps { min-width: 0; flex: 1 1 auto; }

.op-vis_maillon-corps b {
	display: block;
	margin-bottom: 7px;
	font-size: 14.5px;
	font-weight: 700;
	color: #FFFFFF;
}

.op-vis_champ {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 8px;
	margin-top: 3px;
}

.op-vis_champ i {
	flex: 0 0 62px;
	font-style: normal;
	font-size: 11.5px;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: rgba(255,255,255,0.52);
}

/* La valeur : c'est elle qu'il faut suivre d'une ligne à l'autre. */
.op-vis_val {
	padding: 2px 8px;
	border-radius: 6px;
	background: rgba(0,0,0,0.22);
	font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace;
	font-size: 13px;
	letter-spacing: 0.03em;
	color: #FFFFFF;
}

/* La valeur reprise de la ligne précédente porte la couleur de la marque :
   l'œil retrouve la même teinte deux lignes de suite, et le chaînage se
   comprend sans qu'on l'explique. */
.op-vis_val.est-reprise {
	background: rgba(196,165,255,0.20);
	box-shadow: inset 0 0 0 1px rgba(196,165,255,0.55);
	color: #C4A5FF;
}

.op-vis_val.est-ko {
	background: rgba(249,112,102,0.18);
	box-shadow: inset 0 0 0 1px rgba(249,112,102,0.60);
	color: #FFB4AE;
}

.op-vis_vide { font-style: normal; font-size: 12.5px; color: rgba(255,255,255,0.42); }

/* Le trait qui descend d'un maillon au suivant, et dit ce qu'il porte. */
.op-vis_descend {
	position: relative;
	margin: 0 !important;
	padding: 8px 0 8px 37px;
	font-size: 11.5px;
	line-height: 1.4;
	color: rgba(255,255,255,0.50) !important;
}

.op-vis_descend::before {
	content: "";
	position: absolute;
	left: 26px;
	top: 0;
	bottom: 0;
	width: 2px;
	background: linear-gradient(rgba(196,165,255,0.65), rgba(196,165,255,0.20));
}

@media (max-width: 860px) {
	.op-vis_champ i { flex-basis: 100%; }
}

/* ── Carte de la restriction géographique ──────────────────────────────── */

.op-vis_carte {
	display: block;
	width: 100%;
	height: auto;
	margin: 18px 0 0;
	border-radius: 10px;
	background: var(--op-surface);
}

.op-vis_terre { fill: rgba(118,31,227,0.14); }

.op-vis_ok-halo { fill: rgba(22,163,74,0.18); }
.op-vis_ok      { fill: #15803D; }

.op-vis_ko-halo { fill: rgba(220,38,38,0.14); }

.op-vis_ko {
	stroke: #DC2626;
	stroke-width: 2.4;
	stroke-linecap: round;
	fill: none;
}

@media (max-width: 860px) {
	.op-acc_apercu { margin-top: 38px; }
	.op-vis_bloc-titre { font-size: 17px; }
	.op-vis_bloc-emp   { font-size: 15px; }
	.op-vis_bloc-note  { font-size: 12px; }
	.op-vis_annot      { font-size: 14px; }
}

/* La note qui remplace l'encart de retouche, retiré : il ajoutait de
   l'ambiguïté là où la chaîne intacte se comprend seule. */
.op-vis_pile-note {
	margin: 14px 0 0 !important;
	font-size: 12.5px !important;
	line-height: 1.55;
	color: rgba(255,255,255,0.60) !important;
}

/* ── Section SMS ────────────────────────────────────────────────────────
   Vitrine : c'est la première chose qu'on regarde ici. Le téléphone est
   entièrement en CSS — donc net sur un écran à haute densité comme sur un
   vidéoprojecteur, et rien à télécharger. */

.op-acc_surtitre--clair { color: var(--op-violet) !important; }

.op-acc_sms-grille {
	display: grid;
	grid-template-columns: minmax(0, 420px) 1fr;
	gap: 48px;
	align-items: center;
}

/* Le telephone occupe SA largeur — 196 px, celle a laquelle toute sa
   geometrie interne a ete ecrite — et pas un pixel de plus.

   La colonne en reservait 250 : le chassis n'y grandissait pas, mais il
   flottait dans 54 px de vide, et le panneau « Ce que vous voyez, vous »
   se retrouvait a 144 px, comprime contre lui. D'ou l'impression d'un
   telephone trop gros — c'etait son VOISIN qui etait trop petit.

   ⚠️ On ne touche pas a la largeur du telephone. Les valeurs internes sont
   derivees d'un meme coefficient a 196 px ; le comment plus bas (~11046)
   raconte les deux methodes essayees avant, dont l'une rendait le texte
   trouble. Rebalancer la colonne ne coute rien et ne casse rien. */
.op-vis_sms {
	display: grid;
	grid-template-columns: 196px minmax(0, 1fr);
	gap: 30px;
	align-items: center;
}

/* La hauteur vient du RAPPORT d'un vrai telephone, pas d'un tatonnement.

   Corps d'un iPhone 15 : 147,6 x 71,6 mm, soit 2,16 fois plus haut que large.
   A 196 px de large et 8 px de cadre de chaque cote :

       chassis  196 x 424     ->  ecran 424 - 16 = 408 px

   J'avais d'abord ecrit 364 px, la valeur de `.op-apercu--double`. C'est la
   hauteur du telephone quand il est COTE A COTE avec l'ordinateur, ou on le
   comprime justement pour aligner les deux appareils. Seul, il tombait a un
   rapport de 1,94 : trop trapu pour se lire comme un telephone. Ici il est
   seul — c'est la hauteur du cas seul qu'il lui faut. */
.op-vis_sms .op-tel_ecran { min-height: 408px; }

/* ── Ce que voit l'expéditeur ──────────────────────────────────────────── */

.op-vis_recu-titre {
	margin: 0 0 12px;
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--op-gris);
}

.op-vis_etats { margin: 0; padding: 0; list-style: none; }

.op-vis_etats li {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	padding: 9px 0;
	font-size: 13px;
	line-height: 1.45;
	color: var(--op-gris);
}

.op-vis_etat-marque {
	flex: 0 0 auto;
	width: 10px;
	height: 10px;
	margin-top: 4px;
	border-radius: 50%;
}

.op-vis_etats li.est-remis   { color: var(--op-encre); font-weight: 600; }
.op-vis_etats li.est-remis   .op-vis_etat-marque { background: #15803D; box-shadow: 0 0 0 4px rgba(22,163,74,0.16); }
.op-vis_etats li.est-attente .op-vis_etat-marque { background: #B45309; }
.op-vis_etats li.est-echec   .op-vis_etat-marque { background: #DC2626; }

.op-vis_etats b { font-variant-numeric: tabular-nums; }

/* ── Réglages d'équipe et carte de France ──────────────────────────────
   Les quatre cartes de différenciation portent chacune leur image : deux
   illustrées et deux nues se regardaient mal. */

.op-vis_reglages {
	margin: 18px 0 0;
	padding: 0;
	list-style: none;
	border-radius: 10px;
	background: rgba(0,0,0,0.04);
	box-shadow: inset 0 0 0 1px var(--op-bordure);
	overflow: hidden;
}

.op-vis_reglages li {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 11px 14px;
	border-bottom: 1px solid var(--op-bordure);
	font-size: 13px;
}

.op-vis_reglages li:last-child { border-bottom: none; }

.op-vis_reglage-nom { color: var(--op-encre); font-weight: 500; }

.op-vis_reglage-val {
	padding: 3px 10px;
	border-radius: 999px;
	background: rgba(118,31,227,0.10);
	font-size: 11.5px;
	font-weight: 700;
	color: var(--op-violet);
	font-variant-numeric: tabular-nums;
}

/* L'interrupteur : sa position dit l'état sans qu'on ait à le nommer. */
.op-vis_bascule {
	position: relative;
	flex: 0 0 auto;
	width: 34px;
	height: 19px;
	border-radius: 999px;
	background: var(--op-violet);
}

.op-vis_bascule::after {
	content: "";
	position: absolute;
	top: 2.5px;
	right: 2.5px;
	width: 14px;
	height: 14px;
	border-radius: 50%;
	background: #FFFFFF;
}

.op-vis_france {
	display: block;
	width: 100%;
	max-width: 220px;
	height: auto;
	margin: 16px auto 0;
}

.op-vis_france-reg {
	fill: rgba(118,31,227,0.20);
	stroke: rgba(118,31,227,0.55);
	stroke-width: 1.2;
	stroke-linejoin: round;
}

@media (max-width: 980px) {
	.op-acc_sms-grille { grid-template-columns: 1fr; gap: 34px; }
}

@media (max-width: 620px) {
	.op-vis_sms { grid-template-columns: 1fr; gap: 22px; justify-items: center; }
	.op-vis_recu { width: 100%; }
}

/* ── Sélecteur de langue sur la page d'acceptation ──────────────────────
   Le sélecteur y vit dans .modal-content, une carte blanche et sobre. La
   pastille active en violet plein — celle de l'application — y détonne, et
   le contraste du texte blanc dessus a été signalé comme illisible.

   Version claire : fond très pâle, contour et texte en violet. Le choix
   actif reste évident, sans le pavé de couleur. Rien d'autre sur le site
   n'est touché : la portée s'arrête à cette carte. */
.modal-content .op-langues_choix.est-active {
	background: rgba(118,31,227,0.10);
	box-shadow: inset 0 0 0 1.5px var(--op-violet);
	color: var(--op-violet) !important;
}

.modal-content .op-langues {
	background: #FFFFFF;
	border-color: var(--op-bordure);
}

.modal-content .op-langues_choix { color: var(--op-gris) !important; }

.modal-content .op-langues_choix:hover {
	background: rgba(118,31,227,0.07);
	color: var(--op-violet) !important;
}

/* ── Le O du logotype, remplacé par la marque ───────────────────────────
   La lettre reste dans le code, rendue transparente : le mot se copie, se
   cherche et se lit à voix haute comme « OURPASS ». L'anneau se superpose. */
.op-acc_marque { display: inline-flex; align-items: center; text-decoration: none; }

.op-logo_o {
	position: relative;
	display: inline-block;
	color: transparent;
	/* La marque fait 0,88 em de large ; la lettre O n'en occupe qu'environ
	   0,78. Sans largeur imposée, l'anneau déborde de sa propre case et mord
	   sur l'espacement qui le sépare du U.

	   La case suit désormais la marque au plus juste : 0,84 em pour un
	   anneau de 0,81. Une case trop large — 0,98 em, essayée le 11 août —
	   ouvrait un trou entre le logo et les lettres, aussi visible que
	   l'était le manque d'espace. */
	min-width: 0.84em;
	text-align: center;
}

.op-logo_marque {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	/* Diamètre calé sur la hauteur des capitales, plus un léger débord :
	   une forme ronde posée exactement à la hauteur d'un E paraît plus
	   petite que lui. 0,81 em de case donne 0,78 em d'anneau pour des
	   capitales à 0,73 — le débord de 7 % que pratiquent les fondeurs.

	   La version précédente montait à 0,88 em, soit 0,85 d'anneau : il
	   dépassait les lettres de dix-sept pour cent et cassait l'alignement. */
	width: 0.81em;
	height: 0.81em;
	display: block;
	pointer-events: none;
	animation: op-tourner-cle 800ms cubic-bezier(0.45, 0, 0.25, 1) 300ms both;
}

/* ── Un mot de passe qu'on déverrouille ────────────────────────────────

   Le logotype s'affiche d'abord masqué — O suivi de six points, comme un
   champ de mot de passe. Puis la serrure s'ouvre, la clé tourne, et le mot
   se dévoile.

     1. l'entaille s'ouvre, À GAUCHE     900 → 1600 ms
     2. elle descend jusqu'en bas        1600 → 2700 ms
     3. les points s'effacent            2700 → 3200 ms
     4. le mot se dévoile                2700 → 3800 ms

   ── Pourquoi cet ordre, et pas l'inverse ────────────────────────────────

   La version précédente faisait tourner l'anneau AVANT d'ouvrir l'entaille.
   Le mouvement était donc rigoureusement invisible : entaille fermée,
   l'anneau est un cercle parfait, et faire tourner un cercle parfait ne
   produit rien à l'écran.

   L'entaille s'ouvre donc d'abord, sur la gauche, puis descend d'un quart
   de tour jusqu'à sa place. C'est ce trajet — et lui seul — qui donne à
   voir la clé qui tourne. */

.op-logo_marque {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	width: 0.88em;
	height: 0.88em;
	display: block;
	pointer-events: none;
	animation: op-tourner-cle 1100ms cubic-bezier(0.4, 0, 0.2, 1) 1600ms both;
}

@keyframes op-tourner-cle {
	0%   { transform: translate(-50%, -50%) rotate(-90deg); }
	72%  { transform: translate(-50%, -50%) rotate(7deg); }   /* le déclic */
	100% { transform: translate(-50%, -50%) rotate(0deg); }
}

.op-logo_entaille {
	transform-box: view-box;
	transform-origin: 50px 50px;
	animation: op-ouvrir-serrure 700ms cubic-bezier(0.22, 1.2, 0.36, 1) 900ms both;
}

@keyframes op-ouvrir-serrure {
	0%   { transform: scaleY(0); }      /* fermé : la lettre O */
	100% { transform: scaleY(1); }      /* ouvert : la marque */
}

/* Le mot donne sa largeur à la boîte ; les points se superposent. */
.op-logo_suite { position: relative; display: inline-block; }

.op-logo_points {
	position: absolute;
	left: 0.05em;
	top: 0;
	white-space: nowrap;
	/* Six puces sont plus étroites que six capitales : on les écarte pour
	   couvrir la même largeur, sans quoi elles paraissent tassées contre
	   le O. */
	letter-spacing: 0.54em;
	animation: op-effacer-points 500ms ease-out 2700ms both;
}

@keyframes op-effacer-points {
	0%   { opacity: 1; }
	100% { opacity: 0; }
}

.op-logo_mot {
	display: inline-block;
	animation: op-devoiler-mot 1100ms cubic-bezier(0.3, 0.8, 0.3, 1) 2700ms both;
}

@keyframes op-devoiler-mot {
	0%   { clip-path: inset(0 100% 0 0); opacity: 0; }
	100% { clip-path: inset(0 0 0 0);    opacity: 1; }
}

/* Au survol, la serrure se referme et se rouvre en tournant : remasquer le
   mot le ferait clignoter à chaque passage de souris. */
.op-acc_marque:hover .op-logo_marque,
.op-acc_pied-marque:hover .op-logo_marque {
	animation: op-retourner-cle 1200ms cubic-bezier(0.45, 0, 0.25, 1) both;
}

@keyframes op-retourner-cle {
	0%   { transform: translate(-50%, -50%) rotate(0deg); }
	45%  { transform: translate(-50%, -50%) rotate(-90deg); }
	100% { transform: translate(-50%, -50%) rotate(0deg); }
}

/* Rien ne s'impose à qui a refusé les animations : tout est déjà en place,
   marque ouverte et mot visible. */
@media (prefers-reduced-motion: reduce) {
	.op-logo_marque,
	.op-logo_entaille,
	.op-logo_points,
	.op-logo_mot,
	.op-acc_marque:hover .op-logo_entaille,
	.op-acc_pied-marque:hover .op-logo_entaille {
		animation: none;
	}
	.op-logo_marque  { transform: translate(-50%, -50%); }
	.op-logo_entaille { transform: scaleY(1); }
	.op-logo_points   { display: none; }
	.op-logo_mot      { clip-path: none; opacity: 1; }
}

/* ── Pictogrammes des boutons d'action ─────────────────────────────────
   Les quatre boutons sous le champ de mot de passe portaient trois natures
   d'icône : deux images PNG, un emoji, une image encore. Aucune cohérence
   d'épaisseur ni de style, et l'emoji rendu en couleur par la police du
   système.

   Un tracé unique désormais : il suit la taille du texte, en prend la
   couleur — donc reste lisible sur un bouton clair comme sur un bouton
   violet — et ne se pixellise sur aucun écran. */
.op-btn_picto {
	display: block;
	width: 20px;
	height: 20px;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.7;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* ── La page de vente ne doit jamais glisser sur le cote ────────────────

   Constate sur iPhone : toutes les lignes de texte coupees au meme endroit,
   le bouton « Se connecter » hors de l'ecran. Ce n'est pas un bloc isole qui
   deborde — c'est la page entiere qui est plus large que l'ecran, et l'on
   n'en voit que la partie gauche.

   Cause la plus probable : un enfant de grille ne se reduit jamais sous la
   largeur minimale de son contenu, sauf a le lui dire. Le tableau du suivi,
   dont les cellules portent `white-space: nowrap`, reclame ainsi plus de
   quatre cents pixels et pousse toute la page.

   Trois reponses, de la plus precise a la plus large. */

/* 1. Les enfants de grille ont le droit de retrecir. */
.op-acc_suivi-grille > *,
.op-acc_sms-grille > *,
.op-vis_sms > *,
.op-acc_cartes > *,
.op-acc_offres > * {
	min-width: 0;
}

/* 2. Le tableau defile dans SA fenetre, jamais dans la page. */
.op-vis_fenetre { overflow-x: auto; }

/* 3. Et quoi qu'il arrive, la page ne glisse pas.

      `clip` plutot que `hidden` la ou il est reconnu : `hidden` cree un
      conteneur de defilement, ce qui CASSE le `position: sticky` de
      l'en-tete. `clip` coupe sans creer ce conteneur. `hidden` reste en
      repli pour les navigateurs anciens, qui perdront l'en-tete collant
      mais garderont une page utilisable. */
.op-page-accueil {
	overflow-x: hidden;
	max-width: 100%;
}

@supports (overflow-x: clip) {
	.op-page-accueil { overflow-x: clip; }
}

/* Sur un telephone, la colonne « Envoye » du tableau disparait deja ; on
   resserre aussi les remplissages, qui pesent lourd sur quatre colonnes. */
@media (max-width: 620px) {
	.op-vis_table th,
	.op-vis_table td { padding-left: 11px; padding-right: 11px; }
	.op-vis_qui { font-size: 12.5px; }
}

/* ── Reglages de confort sur petit ecran ───────────────────────────────

   Les bornes a 100vw et les coupures de mots posees ici traitaient l'effet
   d'un debordement dont la cause — le remplissage ajoute a la largeur — est
   desormais corrigee. Ne restent que les ajustements de lisibilite.

   La coupure des mots longs reste : une adresse ou un terme technique plus
   large que la colonne pousserait encore la page. */
.op-page-accueil h1,
.op-page-accueil h2,
.op-page-accueil h3,
.op-page-accueil p,
.op-page-accueil li,
.op-page-accueil dd,
.op-page-accueil summary {
	overflow-wrap: break-word;
}

@media (max-width: 480px) {
	.op-acc_hero h1 { font-size: 28px; line-height: 1.16; }
	.op-acc_chapo { font-size: 15.5px; }
	.op-acc_bande { padding: 0 18px; }
}

/* ── Le registre : montrer ce qui va avec quoi ──────────────────────────

   Trois tentatives ont echoue parce qu'elles corrigeaient des MOTS. Le vrai
   obstacle etait structurel : rien ne montrait que deux lignes parlent du
   meme lien. Le jeton ne se lit pas, et il est masque tant que le lien reste
   ouvrable — sur ces lignes-la, l'appariement etait impossible.

   Chaque lien porte desormais un numero et une couleur, attribues a sa
   premiere apparition. Meme pastille = meme lien. */

/* ── Le guide, devant le tableau ────────────────────────────────────── */

.op-reg_guide {
	margin: 0 0 26px;
	padding: 22px 24px;
	border: 1px solid var(--op-bordure);
	border-radius: 14px;
	background: var(--op-surface);
}

.op-reg_guide_titre {
	margin: 0 0 16px;
	font-size: 16px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-reg_guide_liste {
	margin: 0;
	padding-left: 22px;
	display: grid;
	gap: 12px;
	font-size: 14px;
	line-height: 1.6;
}

.op-reg_guide_liste li::marker { color: var(--op-violet); font-weight: 700; }
.op-reg_guide_liste strong     { color: var(--op-encre); }

.op-reg_guide_legende {
	margin: 20px 0 8px;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-encre);
}

/* L'exemple est construit avec les vraies lignes du compte : celui qui lit
   les retrouve a l'identique trois centimetres plus bas. */
.op-reg_exemple {
	width: auto;
	margin: 0;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: #FFFFFF;
	overflow: hidden;
}

.op-reg_exemple td {
	padding: 9px 14px;
	border-bottom: 1px solid var(--op-bordure);
	font-size: 13px;
	vertical-align: middle;
}
.op-reg_exemple tr:last-child td { border-bottom: 0; }

.op-reg_guide_fleche {
	margin: 10px 0 0;
	font-size: 13.5px;
	line-height: 1.6;
	color: var(--op-gris);
}
.op-reg_guide_fleche strong { color: var(--op-encre); }

/* ── La pastille : meme lien, meme couleur ──────────────────────────── */

.op-reg_pastille {
	display: inline-block;
	padding: 3px 10px;
	border-radius: 999px;
	border: 1px solid currentColor;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 0.01em;
	white-space: nowrap;
}

/* Dix teintes qui se distinguent aussi en niveaux de gris : deux liens
   voisins ne doivent pas se confondre sur une impression noir et blanc,
   ni pour un daltonien. Le fond reste tres clair, le texte porte la
   couleur — c'est le contraste du TEXTE qui doit rester lisible. */
.op-reg_pastille[data-teinte="0"] { color: #B45309; background: #FEF3C7; }
.op-reg_pastille[data-teinte="1"] { color: #1D4ED8; background: #DBEAFE; }
.op-reg_pastille[data-teinte="2"] { color: #15803D; background: #DCFCE7; }
.op-reg_pastille[data-teinte="3"] { color: #A21CAF; background: #FAE8FF; }
.op-reg_pastille[data-teinte="4"] { color: #B91C1C; background: #FEE2E2; }
.op-reg_pastille[data-teinte="5"] { color: #0F766E; background: #CCFBF1; }
.op-reg_pastille[data-teinte="6"] { color: #6D28D9; background: #EDE9FE; }
.op-reg_pastille[data-teinte="7"] { color: #A16207; background: #FEF9C3; }
.op-reg_pastille[data-teinte="8"] { color: #0E7490; background: #CFFAFE; }
.op-reg_pastille[data-teinte="9"] { color: #BE185D; background: #FCE7F3; }

/* Le jeton reste disponible, mais il ne commande plus la lecture. */
.op-reg_jetonval {
	display: inline-block;
	margin-left: 8px;
	font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace;
	font-size: 11.5px;
	color: #8B8397;
}

.op-reg_jeton .op-reg_masque {
	margin-left: 8px;
	font-size: 11.5px;
	color: #8B8397;
}

/* ── L'evenement : une etiquette, pas une phrase ─────────────────────── */

.op-reg_ev {
	display: inline-block;
	padding: 3px 9px;
	border-radius: 6px;
	font-size: 12.5px;
	font-weight: 600;
	white-space: nowrap;
}

.op-reg_ev--creation     { background: #EDE9FE; color: #5B21B6; }
.op-reg_ev--consultation { background: #DCFCE7; color: #15803D; }
.op-reg_ev--refus_geo    { background: #FEE2E2; color: #B91C1C; }
.op-reg_ev--revocation   { background: #F1F0F5; color: #4B4560; }
.op-reg_ev--signalement  { background: #FEF3C7; color: #B45309; }
.op-reg_ev--destruction  { background: #F1F0F5; color: #4B4560; }

/* Le detail sur sa propre ligne : colle a l'evenement, « Lien cree » suivi
   de « Paris, FR » formait une phrase qui n'existe pas. */
.op-reg_detail {
	display: block;
	margin-top: 4px;
	font-size: 12px;
	font-style: normal;
	color: var(--op-gris);
}

.op-reg_quand { white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ── Le sceau : present, mais discret ────────────────────────────────── */

.op-reg_maillon {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-size: 12.5px;
	font-weight: 600;
	line-height: 1.35;
	white-space: nowrap;
	cursor: help;
}

.op-reg_marque { flex: none; width: 13px; height: 13px; }

/* Intact : gris. Vingt-six lignes vertes crieraient une information qui
   n'interesse que le jour ou elle manque. */
.op-reg_maillon--suit    { color: #8B8397; font-weight: 500; }
.op-reg_maillon--origine { color: #8B8397; font-weight: 500; font-style: italic; }
.op-reg_maillon--rompu   { color: #B3261E; font-weight: 700; }

.op-reg_table--chaine tr.est-rompue { background: rgba(179,38,30,0.06); }

@media (max-width: 900px) {
	.op-reg_table--chaine { display: block; overflow-x: auto; }
	.op-reg_guide { padding: 18px 16px; }
	.op-reg_exemple { display: block; overflow-x: auto; }
}

/* Le guide est un depliant : deplie, il repoussait le tableau sous la ligne
   de flottaison a chaque visite. On ne relit pas un mode d'emploi tous les
   jours — mais il doit rester la, et se voir. */
.op-reg_guide_titre {
	cursor: pointer;
	margin: 0;
	font-size: 15px;
	font-weight: 700;
	color: var(--op-encre);
	list-style: none;
	display: flex;
	align-items: center;
	gap: 9px;
}
.op-reg_guide_titre::-webkit-details-marker { display: none; }

/* Le chevron tourne a l'ouverture : sans lui, rien ne dit que le titre
   s'ouvre — c'est le defaut qui rend un depliant invisible. */
.op-reg_guide_titre::before {
	content: '';
	flex: none;
	width: 7px;
	height: 7px;
	border-right: 2px solid var(--op-violet);
	border-bottom: 2px solid var(--op-violet);
	transform: rotate(-45deg);
	transition: transform 0.18s ease;
}
.op-reg_guide[open] > .op-reg_guide_titre::before { transform: rotate(45deg); }

.op-reg_guide[open] > .op-reg_guide_titre {
	margin-bottom: 16px;
	padding-bottom: 14px;
	border-bottom: 1px solid var(--op-bordure);
}

.op-reg_guide_chapo {
	margin: 0 0 16px;
	font-size: 14.5px;
	line-height: 1.65;
	color: var(--op-gris);
}
.op-reg_guide_chapo strong { color: var(--op-encre); }

/* Le detail par colonne : une sous-partie, plus un depliant dans le
   depliant — deux clics pour une definition, personne n'y allait. */
.op-reg_guide .op-reg_aide {
	margin-top: 20px;
	padding-top: 18px;
	border-top: 1px solid var(--op-bordure);
}

.op-reg_aide_titre {
	margin: 0 0 12px;
	font-size: 13.5px;
	font-weight: 700;
	letter-spacing: 0.03em;
	text-transform: uppercase;
	color: var(--op-violet);
}

/* ── Abonnement : la vue d'un membre ─────────────────────────────────────

   Un membre invite n'a aucune main sur la facturation : le contrat, les
   sieges et l'offre appartiennent au proprietaire. Lui presenter la grille
   tarifaire et trois boutons qui echouent etait l'incoherence signalee — le
   bouton « souscrire », lui, ne bloquait meme pas : il serait parti creer un
   SECOND abonnement a son nom.

   Il voit desormais ce dont il beneficie, et a qui s'adresser. */

.op-abo_membre {
	max-width: 640px;
	margin: 8px 0 0;
	padding: 24px 26px;
	border: 1px solid var(--op-bordure);
	border-radius: 14px;
	background: var(--op-surface);
}

.op-abo_membre h2 {
	margin: 0 0 12px;
	font-size: 17px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-abo_membre p {
	margin: 0 0 12px;
	font-size: 14.5px;
	line-height: 1.65;
	color: var(--op-gris);
}
.op-abo_membre p strong { color: var(--op-encre); }

.op-abo_membre-qui {
	padding-top: 12px;
	border-top: 1px solid var(--op-bordure);
}
.op-abo_membre-qui a { font-weight: 600; }

.op-abo_membre .op-abo_liste {
	margin: 16px 0 0;
	padding-top: 16px;
	border-top: 1px solid var(--op-bordure);
}

/* ── Abonnement : changer de palier ──────────────────────────────────────

   Il n'existait aucun chemin. Le garde-fou anti-doublon refusait toute
   nouvelle souscription — a juste titre — et renvoyait vers le formulaire
   des sieges, qui ne fait varier que la QUANTITE du palier en cours. */

.op-abo_changer {
	margin-top: 14px;
	padding-top: 14px;
	border-top: 1px solid var(--op-bordure);
}

.op-abo_changer label {
	display: block;
	margin-bottom: 6px;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-abo_changer .op-compte_aide {
	display: block;
	margin-top: 8px;
	font-size: 12px;
	line-height: 1.55;
}

/* ── Abonnement : l'ecran de confirmation ────────────────────────────────

   Le changement d'offre prelevait sans rien annoncer : on cliquait, on etait
   debite, on l'apprenait apres. Le montant vient de Stripe, pas d'un calcul
   refait ici — un ecart d'un centime avec le releve suffirait a faire douter
   du reste. */

.op-abo_apercu {
	max-width: 620px;
	margin: 0 0 28px;
	padding: 24px 26px;
	border: 2px solid var(--op-violet);
	border-radius: 14px;
	background: #FFFFFF;
	box-shadow: 0 10px 30px rgba(118,31,227,0.10);
}

.op-abo_apercu h2 {
	margin: 0 0 12px;
	font-size: 17px;
	font-weight: 700;
	color: var(--op-encre);
}

.op-abo_apercu-quoi {
	margin: 0 0 16px;
	font-size: 14.5px;
	line-height: 1.6;
	color: var(--op-gris);
}
.op-abo_apercu-quoi strong { color: var(--op-encre); }

/* Le detail vient de Stripe : temps non utilise en negatif, temps restant en
   positif. Le montrer evite la question « d'ou sort ce chiffre ». */
.op-abo_apercu-lignes {
	margin: 0 0 16px;
	padding: 14px 16px;
	border-radius: 10px;
	background: var(--op-surface);
	list-style: none;
	font-size: 13px;
}

.op-abo_apercu-lignes li {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	padding: 4px 0;
	color: var(--op-gris);
}

.op-abo_apercu-lignes b {
	flex: none;
	font-variant-numeric: tabular-nums;
	color: var(--op-encre);
}

.op-abo_apercu-total {
	margin: 0 0 8px;
	padding: 14px 16px;
	border-radius: 10px;
	background: rgba(118,31,227,0.08);
	font-size: 15px;
	line-height: 1.55;
	color: var(--op-encre);
}

.op-abo_apercu-total strong {
	font-size: 20px;
	font-variant-numeric: tabular-nums;
	color: var(--op-violet);
}

/* Une baisse ne prelevait rien : le dire en vert, pas en violet d'alerte. */
.op-abo_apercu-total.est-avoir { background: rgba(21,128,61,0.08); }
.op-abo_apercu-total.est-avoir strong { color: #15803D; }

.op-abo_apercu-suite {
	margin: 0 0 20px;
	font-size: 13px;
	color: var(--op-gris);
}

.op-abo_apercu-actions {
	display: flex;
	align-items: center;
	gap: 16px;
	flex-wrap: wrap;
}

.op-abo_apercu-annuler {
	font-size: 13.5px;
	color: var(--op-gris);
	text-decoration: underline;
}
.op-abo_apercu-annuler:hover { color: var(--op-encre); }

/* ── Abonnement : les factures, a la demande ─────────────────────────────

   Elles etaient d'abord posees en pied de page, affichees qu'on les demande
   ou non. Elles vivent maintenant derriere un bouton, dans la fenetre du
   projet — celle des reglages d'envoi en masse.

   Le portail Stripe, lui, ne les annonce plus : il ne reste que ce qui exige
   vraiment Stripe, la saisie d'une carte et la resiliation. */

.op-abo_actions {
	display: flex;
	flex-wrap: wrap;
	gap: 26px;
	align-items: flex-start;
}

.op-abo_factures-bouton {
	padding: 10px 18px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: #FFFFFF;
	font-size: 14px;
	font-weight: 600;
	color: var(--op-encre);
	cursor: pointer;
}
.op-abo_factures-bouton:hover { border-color: var(--op-violet); color: var(--op-violet); }

/* La fenetre porte un tableau : plus large que celle des reglages. */
#op-modal-factures .op-modal_boite { max-width: 820px; }

.op-abo_factures-note {
	margin: 0 0 16px;
	font-size: 13px;
	color: var(--op-gris);
}

.op-abo_factures td { vertical-align: top; }

.op-abo_factures-heure {
	font-size: 11.5px;
	color: #8B8397;
	font-variant-numeric: tabular-nums;
}

.op-abo_motif { background: #EDE9FE; color: #5B21B6; }

/* Le detail du calcul : six factures du meme jour, numerotees
   « 394CBDB0-0064 », ne se distinguaient autrement que par leur montant. */
.op-abo_factures-detail {
	margin: 8px 0 6px;
	padding: 0;
	list-style: none;
	font-size: 12px;
	color: var(--op-gris);
}

.op-abo_factures-detail li {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 14px;
	padding: 2px 0;
}

.op-abo_factures-detail b {
	flex: none;
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	color: var(--op-encre);
}

.op-abo_factures-numero {
	display: block;
	font-family: 'SFMono-Regular', Consolas, monospace;
	font-size: 11px;
	color: #8B8397;
}

.op-abo_factures-montant {
	white-space: nowrap;
	font-variant-numeric: tabular-nums;
}

.op-abo_factures-pdf {
	font-size: 13px;
	font-weight: 600;
	white-space: nowrap;
	color: var(--op-violet);
}

.op-abo_etat--paid  { background: #DCFCE7; color: #15803D; }
.op-abo_etat--open  { background: #FEF3C7; color: #B45309; }
.op-abo_etat--draft { background: #F1F0F5; color: #4B4560; }
.op-abo_etat--void,
.op-abo_etat--uncollectible { background: #FEE2E2; color: #B91C1C; }

@media (max-width: 760px) {
	.op-abo_factures table { display: block; overflow-x: auto; }
	.op-abo_actions { gap: 18px; }
}

/* ── Double canal : le lien par e-mail, le code par SMS ──────────────────

   Ce que ca protege : l'e-mail parti a la mauvaise adresse. C'est la premiere
   cause reelle de fuite — une autocompletion malheureuse, un homonyme, un
   « repondre a tous ». Le mauvais destinataire recoit alors un lien qu'il ne
   peut pas ouvrir.

   ⚠️ Aucune case a cocher : le validateur du gabarit trebuche sur un element
   cochable invisible et le bouton « Suivant » cesse de fonctionner. D'ou une
   bascule dessinee, comme pour le choix du canal. */

.op-code2 { margin-top: 4px; }

.op-code2_bascule {
	display: flex;
	align-items: center;
	gap: 11px;
	width: 100%;
	padding: 12px 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: #FFFFFF;
	font-size: 14px;
	font-weight: 600;
	line-height: 1.4;
	text-align: left;
	color: var(--op-gris);
	cursor: pointer;
	transition: border-color 140ms ease, color 140ms ease;
}
.op-code2_bascule:hover { border-color: var(--op-violet); }

.op-code2_bascule.is-actif {
	border-color: var(--op-violet);
	background: var(--op-surface);
	color: var(--op-encre);
}

/* La marque tient lieu de case : meme lecture, sans element cochable. */
.op-code2_marque {
	position: relative;
	flex: none;
	width: 20px;
	height: 20px;
	border: 2px solid var(--op-bordure);
	border-radius: 6px;
	transition: background 140ms ease, border-color 140ms ease;
}

.op-code2_bascule.is-actif .op-code2_marque {
	border-color: var(--op-violet);
	background: var(--op-violet);
}

.op-code2_bascule.is-actif .op-code2_marque::after {
	content: '';
	position: absolute;
	left: 5px;
	top: 1px;
	width: 5px;
	height: 10px;
	border: solid #FFFFFF;
	border-width: 0 2px 2px 0;
	transform: rotate(45deg);
}

.op-code2_detail {
	margin-top: 10px;
	padding: 14px 16px;
	border-left: 3px solid var(--op-violet);
	border-radius: 0 10px 10px 0;
	background: var(--op-surface);
}

.op-code2_aide {
	margin: 0 0 12px;
	font-size: 13px;
	line-height: 1.6;
	color: var(--op-gris);
}

.op-code2_detail label {
	display: block;
	margin-bottom: 5px;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-code2_erreur {
	margin: 8px 0 0;
	font-size: 12.5px;
	font-weight: 600;
	color: #B3261E;
}

/* ── La page de fin dit que deux messages sont partis ────────────────── */

.op-fin_code {
	max-width: 520px;
	margin: 14px auto 0;
	padding: 12px 16px;
	border-radius: 10px;
	background: rgba(118,31,227,0.08);
	font-size: 14px;
	line-height: 1.55;
	color: var(--op-encre);
}

.op-fin_code--ko { background: rgba(179,38,30,0.10); color: #B3261E; }

/* ── L'ecran de saisie, chez le destinataire ─────────────────────────── */

.op-code_form { margin-top: 20px; }

.op-code_form label {
	display: block;
	margin-bottom: 8px;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-encre);
}

/* Chiffres espaces et centres : on recopie un code lu sur un autre ecran,
   souvent d'une main. Chaque caractere doit se distinguer sans effort. */
.op-code_saisie {
	width: 100%;
	max-width: 240px;
	margin: 0 auto 16px;
	padding: 14px 10px;
	display: block;
	border: 2px solid var(--op-bordure);
	border-radius: 12px;
	font-family: 'SFMono-Regular', Consolas, monospace;
	font-size: 30px;
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.34em;
	/* L'espacement decale le texte vers la droite : on compense pour que les
	   chiffres restent centres dans le cadre. */
	text-indent: 0.34em;
	text-align: center;
	color: var(--op-encre);
}

.op-code_saisie:focus {
	outline: none;
	border-color: var(--op-violet);
	box-shadow: 0 0 0 4px rgba(118,31,227,0.12);
}

/* Les deux appareils, cote a cote — un seul bloc et non trois enfants.

   `.op-apercu` est un conteneur flex EN LIGNE : les libelles ajoutes a cote
   du telephone devenaient des colonnes voisines, et « SMS vers numero non
   renseigne » s'affichait a un mot par ligne, ecrase entre les deux ecrans. */
/* Les deux appareils s'alignent par le BAS. Le telephone est plus haut que
   l'ordinateur : cales par le haut, leurs destinataires finissaient a deux
   hauteurs differentes, et l'on ne savait plus lequel allait avec lequel. */
/* Les deux messages sont deux COLONNES de meme hauteur (`stretch`), et
   l'appareil de chacune est pousse en bas par `margin-top: auto`. Les
   appareils s'alignent donc par le bas, et les destinataires qui les suivent
   tombent sur une meme ligne — sans qu'aucune hauteur soit devinee.
   `align-items: flex-end` ne suffisait pas : il alignait les BLOCS, pas les
   appareils, et la legende « et, dans le meme temps » decalait l'un des deux. */
.op-apercu { flex-wrap: wrap; gap: 24px; align-items: stretch; }

.op-apercu_msg {
	display: flex;
	flex-direction: column;
	align-items: center;
}
.op-apercu_msg > .op-tel,
.op-apercu_msg > .op-pc { margin-top: auto; }

/* Les deux maquettes gardent leur taille propre.
   Sans cela, l'arrivee d'un second element faisait RETRECIR l'ordinateur —
   de 348 px a la portion congrue — et son contenu s'enroulait, allongeant
   l'ecran d'autant. On prefere un retour a la ligne a un ecrasement. */
.op-apercu > .op-apercu_msg { flex: 0 0 auto; }

/* Le destinataire du SMS etait ecrit DANS le chassis noir du telephone :
   gris sur noir, illisible, et pose a une autre hauteur que celui de
   l'ordinateur, qui est sous le socle. Il sort du chassis. */
.op-appareil {
	display: flex;
	flex-direction: column;
	align-items: center;
}



.op-apercu_et {
	margin: 0 0 2px;
	font-size: 12.5px;
	font-style: italic;
	text-align: center;
	color: var(--op-gris);
}


/* L'aide de l'ecran de saisie, sous le champ et AVANT le bouton.

   Elle portait `op-compte_note`, dont le `margin-top: -10px` est prevu pour
   une note qui suit un champ. Placee apres un bouton, ce retrait la faisait
   remonter DANS le bouton : le texte et le libelle se superposaient. */
.op-code_aide {
	margin: 0 0 4px;
	font-size: 12.5px;
	line-height: 1.55;
	text-align: center;
	color: var(--op-gris);
}

/* Le bouton de cet ecran n'a rien au-dessus qui le pousse : sa marge propre
   suffit, et le champ garde son air. */
.op-code_form .op-compte_bouton { margin-top: 16px; }

/* ── Le selecteur de langue dans une carte blanche ───────────────────────

   Le bloc « Fonds sombres » plus haut teinte les codes de langue en BLANC
   des que le corps porte `op-page-compte`, parce que ces pages ont un fond
   violet. Mais huit d'entre elles posent le selecteur DANS une carte
   `.op-compte_carte`, dont le fond est `#FFFFFF` : les langues inactives
   deviennent blanc sur blanc, invisibles. Seule l'active se voit, sa
   pastille la sauvant.

   Pages concernees : inscription, confirmation, oubli et reinitialisation de
   mot de passe, retour d'abonnement, refus geographique, et les deux ecrans
   du double canal.

   ⚠️ Le fond ne se deduit pas de la classe du CORPS. C'est le CONTENEUR qui
   le porte — d'ou une regle attachee a la carte, et non a la page. Elle doit
   rester APRES le bloc « Fonds sombres » : meme specificite, c'est l'ordre
   qui tranche. */

.op-compte_carte .op-langues {
	border-color: var(--op-bordure);
	background: rgba(255,255,255,0.55);
}

.op-compte_carte .op-langues_choix { color: var(--op-gris) !important; }

.op-compte_carte .op-langues_choix:hover {
	background: var(--op-surface);
	color: var(--op-encre) !important;
}

.op-compte_carte .op-langues_choix.est-active {
	background: var(--op-violet);
	color: #FFFFFF !important;
}

/* Deux maquettes cote a cote : on reduit l'echelle, on ne rogne pas.

   L'ordinateur seul tient dans l'ecran. Avec le telephone du code a cote,
   l'ensemble le depasse — et rogner le contenu irait contre l'objet meme de
   l'apercu, qui est de RELIRE le message avant de l'envoyer.

   `zoom` et non `transform: scale()` : la seconde ne reduit que le dessin, pas
   la place occupee, et laisserait un grand vide sous les maquettes. */
.op-apercu--double { zoom: 0.82; }

/* Le zoom s'applique aussi aux libelles : on les remonte d'autant pour qu'ils
   gardent leur taille de lecture. */
.op-apercu--double .op-apercu_et { font-size: 14.5px; }

/* Dans le volet gauche, le telephone n'est pas une vitrine mais un apercu :
   sa hauteur d'ecran figee a 404 px, plus le titre au-dessus, depassait la
   colonne et venait chevaucher le selecteur de langues. Il suit son contenu. */
@media (min-width: 992px) {
	/* Un telephone est deux fois plus haut que large : l'ecran fait 180 px,
   il en mesure donc 364. La hauteur suivait le contenu depuis qu'elle
   n'est plus figee a 404 px, et un message court donnait un appareil
   trapu qui ne ressemblait plus a rien. Elle a de nouveau un plancher,
   proportionne cette fois, et grandit encore si le message est long. */
	.op-apercu--gauche .op-tel_ecran { min-height: 364px; }
	/* Et l'ensemble ne deborde jamais en largeur. */
	.op-apercu--gauche { max-width: 100%; }
}

/* A DEUX, le telephone se met aussi a la LARGEUR qui convient.
   232 px face aux 348 px de l'ordinateur donnent un rapport de 1 a 1,5 ;
   dans la vraie vie un telephone fait le quart d'un portable. Seul, cette
   largeur sert la lecture du message ; a cote de l'ordinateur, elle donne un
   telephone geant. 200 px rapprochent la proportion sans rendre le texte
   plus petit — on reduit le chassis, pas la typographie. */
/* ⚠️ `box-sizing`, en tete de la maquette et avant toute autre regle.

   La feuille n'a AUCUN `box-sizing` global — c'est note, et je l'ai quand
   meme oublie. Sans lui, `width` designe le contenu et le remplissage s'y
   AJOUTE : `.op-tel_statut`, large de 164 avec 9 px de chaque cote, mesurait
   182 px dans un ecran qui en fait 150. Elle depassait de 32 px a droite,
   `overflow: hidden` la rognait, et `right: 9px` visait un bord situe hors
   de l'ecran — d'ou des icones tantot rognees, tantot invisibles.

   Voila ce que mes reglages successifs poursuivaient sans le savoir. Toutes
   les geometries calculees ce soir supposaient border-box ; elles etaient
   justes, la boite ne l'etait pas. */
.op-tel, .op-tel *,
.op-pc,  .op-pc *  { box-sizing: border-box; }

/* ── La maquette du telephone, ecrite a 196 px ─────────────────────────

   J'ai essaye deux methodes avant celle-ci, et il faut dire pourquoi.

   1. Regler les tailles une par une : le chassis maigrissait, l'interieur
      restait, et chaque correction en appelait une autre. Deux heures.
   2. Garder les proportions d'origine et tout mettre a l'echelle par `zoom` :
      les proportions tenaient, mais le texte devenait TROUBLE — 232 x 0,62
      tombe sur des pixels fractionnaires et le rendu du texte s'y perd.

   Ici les valeurs sont ecrites a 196 px,
   toutes derivees d'un meme coefficient
   et arrondies au demi-pixel : calculees ensemble, une fois, au lieu d'etre
   rattrapees l'une apres l'autre. */



/* A DEUX, le telephone se met a la hauteur de l'ordinateur.
   Son ecran a une hauteur figee — 404 px, pour qu'il ressemble a un
   telephone quand il est seul — pendant que l'ordinateur s'arrete a son
   contenu, vers 300 px. Cote a cote, cela faisait un tiers de vide sous la
   bulle et un desequilibre que Jonas a releve. Ici l'ecran suit son
   contenu, avec un plancher pour qu'il reste reconnaissable. */
.op-apercu--double .op-tel_ecran { min-height: 364px; }

/* ── Le compteur dit le RESTANT ──────────────────────────────────────────

   « SMS 15 / 150 » oblige a soustraire de tete pour savoir de quoi l'on
   dispose encore — et c'est cela qu'on veut savoir avant d'envoyer. La jauge
   donne l'ordre de grandeur d'un coup d'oeil, le chiffre donne l'exact. */



.op-usage_tete {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 10px;
}

.op-usage_titre {
	font-size: 10.5px;
	font-weight: 700;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--op-gris);
}

.op-usage_tete strong {
	font-size: 14px;
	font-variant-numeric: tabular-nums;
	color: var(--op-encre);
}

.op-usage_jauge {
	display: block;
	height: 5px;
	border-radius: 999px;
	background: rgba(26,21,35,0.10);
	overflow: hidden;
}

.op-usage_jauge > span {
	display: block;
	height: 100%;
	border-radius: 999px;
	background: var(--op-violet);
	transition: width 240ms ease;
}

/* La couleur suit l'etat du bloc : inutile de recalculer le seuil ici. */
.op-usage--tendu .op-usage_jauge > span { background: #B45309; }
.op-usage--plein .op-usage_jauge > span { background: #B3261E; }


.op-usage--plein .op-usage_stat--sms em { font-weight: 600; color: #B3261E; }


/* ── Répartition : qui reçoit quoi, et par quel canal ────────────────────

   L'identifiant ne voyage JAMAIS avec le lien. Deux moities dans la meme
   enveloppe, et un message intercepte donne l'acces complet : vendre la
   separation des canaux tout en l'offrant ici n'aurait pas tenu.

   Le bilan informe, il ne bloque pas — l'expediteur voit ce qu'une
   interception livrerait, et decide. */

.op-rep { margin-top: 6px; }

.op-rep_ligne {
	display: grid;
	grid-template-columns: 132px 1fr;
	gap: 10px;
	align-items: center;
	padding: 7px 0;
}

.op-rep_titre { font-size: 12.5px; font-weight: 600; color: var(--op-gris); }

.op-rep_opts { display: flex; flex-wrap: wrap; gap: 5px; }


.op-rep_tel {
	margin-top: 10px;
	padding: 12px 14px;
	border-left: 3px solid var(--op-violet);
	border-radius: 0 10px 10px 0;
	background: var(--op-surface);
}
.op-rep_tel label {
	display: block; margin-bottom: 5px;
	font-size: 12.5px; font-weight: 600; color: var(--op-encre);
}

.op-rep_bilan { display: grid; gap: 4px; margin-top: 11px; }

.op-rep_bil {
	padding: 7px 11px;
	border-radius: 8px;
	font-size: 12px;
	line-height: 1.5;
}
/* Le verdict est unique : trois niveaux, jamais deux avis contradictoires
   sur le meme reglage. Le niveau de base n'est PAS un reproche — il decrit
   une protection reelle, le lien a usage unique, et ne doit donc pas porter
   la couleur d'alerte que portait l'ancien « intercepte — ... ». */
.op-rep_bil.est-base { background: rgba(66,58,82,0.07);  color: #423A52; }
.op-rep_bil.est-bon  { background: rgba(21,128,61,0.10); color: #15803D; }
.op-rep_bil.est-max  { background: rgba(21,128,61,0.15); color: #14652F; }
.op-rep_niv { font-weight: 700; }
.op-rep_niv::after { content: ' —'; font-weight: 400; opacity: 0.55; }

@media (max-width: 560px) {
	.op-rep_ligne { grid-template-columns: 1fr; gap: 5px; }
}

/* ── Le format de l'identifiant, quand il part par SMS ───────────────────

   Il ne se demande plus en PREMIERE question : l'identifiant n'est stocke
   nulle part et ne voyage plus avec le lien, donc il ne sert que dans ce
   cas-la. Le demander d'entree encombrait le formulaire pour rien.

   ⚠️ Des boutons, plus des radios : quatre elements cochables de moins dans
   #wrapped. C'est ce genre d'element, rendu invisible, qui avait casse le
   bouton « Suivant » le 6 aout. */

.op-fmt_bloc {
	margin-top: 10px;
	padding: 12px 14px;
	border-left: 3px solid var(--op-violet);
	border-radius: 0 10px 10px 0;
	background: var(--op-surface);
}

.op-fmt_bloc > label {
	display: block;
	margin-bottom: 7px;
	font-size: 12.5px;
	font-weight: 600;
	color: var(--op-encre);
}

.op-fmt_opts { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; }

.op-fmt {
	padding: 6px 11px;
	border: 1px solid var(--op-bordure);
	border-radius: 8px;
	background: #FFFFFF;
	font: inherit;
	font-size: 12.5px;
	font-family: 'SFMono-Regular', Consolas, monospace;
	color: var(--op-gris);
	cursor: pointer;
}
.op-fmt:hover { border-color: var(--op-violet); }
.op-fmt[aria-pressed="true"] {
	border-color: var(--op-violet);
	background: color-mix(in srgb, var(--op-violet) 8%, #FFFFFF);
	color: var(--op-violet);
	font-weight: 700;
}

.op-fmt_libre { font-size: 13px; }

/* ── L'apercu prend la place de la scene, dans le volet gauche ───────────

   Pour qui envoie tous les jours, une animation qui raconte le produit est
   de la place perdue. L'apercu, lui, sert a chaque envoi — et le voir se
   composer evite d'avancer d'une etape pour le decouvrir.

   ⚠️ Sous 992 px les deux colonnes s'empilent : le volet gauche passe
   AU-DESSUS du formulaire. Un apercu la repousserait le formulaire hors de
   l'ecran. On rend donc la scene a cette largeur, et l'apercu reprend sa
   place a l'etape 2. */

.op-apercu--gauche { display: none; }

@media (min-width: 992px) {
	.op-apercu--gauche {
		display: flex;
		margin-top: 26px;
		/* Les maquettes sont dessinees pour un fond clair : sur le violet du
		   volet, elles ont besoin de leur propre respiration. */
		padding: 16px;
		border-radius: 14px;
		background: rgba(255,255,255,0.10);
		border: 1px solid rgba(255,255,255,0.16);
	}

	/* La scene decorative cede la place. */
	.op-scene--repli { display: none; }

	/* Et l'apercu de l'etape 2 devient un doublon : il disparait, l'etape ne
	   garde que ce qui se modifie. */
	.preview-section > #op-apercu { display: none; }
}

/* Le volet gauche defile avec son contenu plutot que de le tronquer. */
@media (min-width: 992px) {
	.content-left-wrapper .wrapper { overflow-y: auto; }
}

/* ── Répartition : une case à cocher, pas un bouton ────────────────────
   Le reste du formulaire coche (« envoyer plus tard », les options du
   générateur) ; ces deux réglages font de même. La cible englobe le
   libellé : sur un téléphone, viser un carré de 16 px est un mauvais
   service. */
.op-rep_case {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0 0 10px;
    padding: 9px 12px;
    border: 1px solid var(--op-bordure);
    border-radius: 8px;
    font-size: 14px;
    line-height: 1.45;
    color: var(--op-encre);
    cursor: pointer;
    transition: border-color .15s, background-color .15s;
}
.op-rep_case:hover { border-color: var(--op-violet); }
.op-rep_case:focus-within {
    border-color: var(--op-violet);
    box-shadow: 0 0 0 3px rgba(108,75,212,0.14);
}
.op-rep_case input {
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    margin: 2px 0 0;
    accent-color: var(--op-violet);
    cursor: pointer;
}
/* Le fond marque l'etat choisi ; :has manque a Safari 15.3 et anterieurs, ou
   la bordure et la coche natives suffisent a le dire. */
.op-rep_case:has(input:checked) { border-color: var(--op-violet); background: #F6F3FE; }

/* ── L'encart d'identifiant, dans l'aperçu de l'e-mail ─────────────────
   Il remplace le bouton quand cet e-mail ne porte QUE l'identifiant : il
   n'ouvre rien, il ne doit donc rien proposer à cliquer. */
.op-pc_encart {
    /* La COULEUR, explicitement. Les paragraphes voisins la tiennent de
       `.op-pc_mail-corps p` ; cet encart est un <div>, il ne recevait donc
       rien et heritait du blanc du volet gauche. L'identifiant s'affichait
       blanc sur blanc : il changeait bien a chaque clic, on ne le voyait
       simplement pas. Meme piege que le selecteur de langue des cartes
       blanches — la couleur ne se deduit pas du conteneur parent. */
    color: var(--op-encre);
    margin: 8px 0 10px;
    padding: 8px 10px;
    border: 1px solid var(--op-bordure);
    border-left: 3px solid var(--op-violet);
    border-radius: 5px;
    background: #FAF9FD;
    font-size: 12px;
    line-height: 1.4;
    word-break: break-all;
}
.op-pc_encart-titre {
    display: block;
    font-size: 9.5px;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--op-gris);
    margin-bottom: 2px;
}

/* ── Le volet gauche est violet, les maquettes sont dessinées pour du blanc ──
   Les légendes et le destinataire héritaient de --op-gris, choisi pour un
   fond clair : sur le violet du volet, ils devenaient illisibles. Le
   contraste s'accroche au CONTENEUR qui porte le fond, jamais à la page —
   c'est la même règle que pour le sélecteur de langue des cartes blanches. */
@media (min-width: 992px) {
	.op-apercu--gauche .op-apercu_dest,
	.op-apercu--gauche .op-apercu_et { color: rgba(255,255,255,0.80); }
}

/* Le renvoi a la norme, dans le verdict. Souligne pour se donner a cliquer,
   sans changer de couleur : c'est une reference, pas une action. */
.op-rep_iso {
	color: inherit;
	text-decoration: underline;
	text-underline-offset: 2px;
}
.op-rep_iso:hover { text-decoration-thickness: 2px; }

/* ── Que transmet-on : un mot de passe, ou un texte ─────────────────────
   Discret, volontairement. Le choix du CANAL est la question principale de
   l'etape — il porte les grands boutons a picto. Celui-ci vient apres, et
   sept fois sur dix la reponse par defaut convient : un segment sobre,
   moitie moins haut, sans icone. */
.op-quoi {
	display: inline-flex;
	gap: 2px;
	margin: 0 0 14px;
	padding: 3px;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	background: #FBFAFD;
}
.op-quoi_opt {
	appearance: none;
	padding: 6px 14px;
	border: 0;
	border-radius: 7px;
	background: transparent;
	font: inherit;
	font-size: 13px;
	color: var(--op-gris);
	cursor: pointer;
	transition: background-color .15s, color .15s;
	white-space: nowrap;
}
.op-quoi_opt:hover { color: var(--op-encre); }
.op-quoi_opt[aria-pressed="true"] {
	background: #FFFFFF;
	color: var(--op-violet);
	font-weight: 700;
	box-shadow: 0 1px 3px rgba(26,21,35,0.10);
}
.op-quoi_opt:focus-visible { outline: 2px solid var(--op-violet); outline-offset: 1px; }

.op-texte {
	min-height: 116px;
	resize: vertical;
	line-height: 1.55;
	font-family: inherit;
}
.op-texte_compteur {
	margin: 6px 0 0;
	text-align: right;
	font-size: 12px;
	color: var(--op-gris);
	font-variant-numeric: tabular-nums;
}
.op-texte_compteur.est-plein { color: #B3261E; font-weight: 600; }

/* Le message, sur la page de consultation : plusieurs lignes, donc un bloc
   qui les preserve. `pre-wrap` garde les retours a la ligne sans qu'on ait a
   fabriquer des <br> — donc sans reintroduire de balisage dans une donnee
   qui vient du dehors. */
.op-secret--texte {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	max-width: 560px;
	margin: 0 auto;
	text-align: left;
}
.op-secret_texte {
	flex: 1 1 auto;
	margin: 0;
	padding: 14px 16px;
	border-radius: 10px;
	background: rgba(255,255,255,0.10);
	font-family: inherit;
	font-size: 15px;
	line-height: 1.6;
	color: #FFFFFF;
	white-space: pre-wrap;
	word-break: break-word;
	max-height: 46vh;
	overflow-y: auto;
}
.op-secret_copie {
	flex: 0 0 auto;
	margin-top: 6px;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

/* ── `hidden` doit vraiment cacher ─────────────────────────────────────
   L'attribut `hidden` ne tient sa valeur que de la feuille du navigateur —
   la priorite la plus faible qui soit. La moindre regle d'auteur qui pose un
   `display` la bat : Font Awesome en pose un sur `.fa`, si bien que l'œil et
   l'icone de copie restaient VISIBLES en mode texte, ou ils n'ont rien a
   reveler ni a copier.

   La regle ci-dessous retablit l'intention. `.op-lot_retirer[hidden]`, qui
   veut au contraire garder sa place, l'emporte par sa specificite — classe
   plus attribut contre attribut seul.

   Ecrite ici plutot qu'element par element : le defaut se reproduira a chaque
   icone qu'on masquera, et une rustine par icone n'aurait pas de fin. */
[hidden] { display: none !important; }

/* Une precision sous un champ, pas dedans : un placeholder est tronque des
   qu'il depasse la largeur, et il disparait a la premiere frappe — deux
   raisons de ne jamais y loger ce qu'il faut avoir lu. */
.op-astuce {
	margin: 6px 0 0;
	font-size: 12.5px;
	line-height: 1.45;
	color: var(--op-gris);
}

/* La nature d'un lien dans le suivi : mot de passe, ou message. Discret —
   c'est une information de tri, pas un statut : l'etat du lien (consulte,
   expire, revoque) garde ses couleurs, celle-ci n'en prend pas. */
.op-lien_nature {
	display: inline-block;
	margin: 0 0 6px;
	padding: 2px 8px;
	border: 1px solid var(--op-bordure);
	border-radius: 20px;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.03em;
	color: var(--op-gris);
	white-space: nowrap;
}
.op-lien_nature--texte { border-color: rgba(118,31,227,0.30); color: var(--op-violet); }

/* ── J'envoie, ou je demande ────────────────────────────────────────────
   Plus affirmé que le choix « mot de passe / texte » : celui-ci change le
   sens de l'opération, pas seulement la nature du contenu. Il se place en
   tête, avant la question du canal. */
/* ⚠️ LES PSEUDO-ELEMENTS SONT DES ELEMENTS DE GRILLE.
 *
 * `menu.css` ET cette feuille posent chacune `*::after, *::before { content: '' }`.
 * En flux normal ces deux boites sont vides, en ligne, invisibles — sans
 * consequence. Dans une GRILLE, elles deviennent des elements a part entiere
 * et occupent une CASE.
 *
 * Le `::before` prenait donc la case 1 : le premier bouton se retrouvait en
 * case 2, le dernier passait a la ligne. Mesure a l'appui, colonnes a
 * x = 24 / 156 / 287 et premier bouton a x = 156.
 *
 * C'est aussi pourquoi le defaut n'existait pas en `flex` : un element de
 * flux vide y prend une largeur NULLE, alors qu'une case de grille garde sa
 * largeur quoi qu'elle contienne.
 *
 * On ne touche pas a la regle universelle — d'autres composants s'appuient
 * dessus pour leurs decorations. On la neutralise la ou elle nuit. */
.op-sens::before,  .op-sens::after,
.op-canal::before, .op-canal::after { display: none; }

/* ══ Les deux segments font la largeur des CHAMPS ═══════════════════════

   Releve fait sur l'arbre reel, pas au juge :

       #middle-wizard > .step        > .op-sens / .op-canal
       #middle-wizard > .form-group  > les champs

   `.step` n'a AUCUNE regle dans les trois feuilles : sa largeur est donc
   celle de `#middle-wizard`, exactement comme celle des `.form-group`. Les
   segments n'etaient pas dans un conteneur plus large — ils DEBORDAIENT du
   leur, parce qu'en `inline-flex` la boite epouse un contenu trop long.

   D'ou : `width: 100%` donne pile la largeur des champs, et des colonnes en
   `minmax(0, 1fr)` retirent le plancher de largeur minimale que `1fr` seul
   laisse. Le debordement devient impossible, et les deux segments sont
   identiques aux champs par CONSTRUCTION — plus aucune valeur a calibrer. */
.op-sens {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 3px;
	width: 100%;
	margin: 0 0 18px;
	padding: 3px;
	border: 1px solid var(--op-bordure);
	border-radius: 11px;
	background: #FBFAFD;
}
.op-sens_opt {
	appearance: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 0;                 /* sans lui, rien ne retrecit */
	padding: 9px 10px;
	border: 0;
	border-radius: 8px;
	background: transparent;
	font: inherit;
	font-size: 14px;
	font-weight: 600;
	color: var(--op-gris);
	cursor: pointer;
	transition: background-color .15s, color .15s;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.op-sens_opt:hover { color: var(--op-encre); }
.op-sens_opt[aria-pressed="true"] {
	background: var(--op-violet);
	color: #FFFFFF;
	box-shadow: 0 1px 4px rgba(118,31,227,0.30);
}
.op-sens_opt:focus-visible { outline: 2px solid var(--op-violet); outline-offset: 2px; }

/* L'avertissement de la page de dépôt : il doit se lire, pas se deviner. */
.op-depot_garde {
	margin-top: 18px !important;
	padding: 11px 13px;
	border-left: 3px solid #B45309;
	border-radius: 0 8px 8px 0;
	background: rgba(180,83,9,0.07);
	text-align: left;
}

/* Une demande se repère avant tout le reste : c'est le sens de l'opération,
   pas la nature du contenu. */
.op-lien_nature--demande {
	border-color: rgba(180,83,9,0.34);
	color: #B45309;
	background: rgba(180,83,9,0.06);
}

/* ── Le dépôt, affiché une seule fois ───────────────────────────────────
   En tête de page : c'est la seule chose qui compte à cet instant, et le
   seul moment où elle sera visible. */
.op-depot_vu {
	margin: 0 0 22px;
	padding: 18px 20px;
	border: 1px solid var(--op-violet);
	border-radius: 12px;
	background: #FAF7FF;
}
.op-depot_vu h2 { margin: 0 0 12px; font-size: 16px; color: var(--op-encre); }
.op-depot_contenu {
	margin: 0 0 12px;
	padding: 14px 16px;
	border-radius: 9px;
	background: #FFFFFF;
	border: 1px solid var(--op-bordure);
	font-family: Menlo, Consolas, monospace;
	font-size: 14px;
	line-height: 1.6;
	color: var(--op-encre);
	white-space: pre-wrap;
	word-break: break-word;
	max-height: 40vh;
	overflow-y: auto;
}
.op-depot_copier {
	appearance: none;
	padding: 8px 16px;
	border: 1px solid var(--op-bordure);
	border-radius: 8px;
	background: #FFFFFF;
	font: inherit;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-violet);
	cursor: pointer;
}
.op-depot_avert {
	margin: 12px 0 0;
	font-size: 12.5px;
	line-height: 1.5;
	color: #B45309;
}

/* Le bouton d'affichage, sur la ligne d'une demande déposée. */
.op-lien_depot { margin: 8px 0 0; }
.op-lien_depot-btn {
	appearance: none;
	padding: 7px 15px;
	border: 0;
	border-radius: 8px;
	background: var(--op-violet);
	font: inherit;
	font-size: 13px;
	font-weight: 700;
	color: #FFFFFF;
	cursor: pointer;
}
.op-lien_depot-btn:hover { background: var(--op-violet-fonce); }

/* ── Modale du dépôt ────────────────────────────────────────────────────
   Écrite ici plutôt que reprise de Bootstrap : `suivi.php` charge la feuille
   de Bootstrap, pas son script. Porter un composant sans relever ses
   dépendances est exactement ce qui avait cassé le menu le 20 août. */
.op-modale {
	position: fixed;
	inset: 0;
	z-index: 1200;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 20px;
	background: rgba(26,21,35,0.62);
	overflow-y: auto;
}
.op-modale .op-depot_vu {
	width: 100%;
	max-width: 520px;
	margin: auto;
	position: relative;
	background: #FFFFFF;
	box-shadow: 0 18px 48px rgba(26,21,35,0.34);
}
.op-modale_fermer {
	position: absolute;
	top: 10px;
	right: 12px;
	width: 30px;
	height: 30px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: transparent;
	font-size: 22px;
	line-height: 1;
	color: var(--op-gris);
	cursor: pointer;
}
.op-modale_fermer:hover { background: rgba(26,21,35,0.07); color: var(--op-encre); }

/* Les avertissements de la page de dépôt : même présentation que ceux de la
   page de consultation, puisque c'est la même promesse dans l'autre sens. */
.op-depot_liste {
	margin: 16px 0 14px;
	padding-left: 18px;
	text-align: left;
	font-size: 13px;
	line-height: 1.55;
	color: var(--op-gris);
}
.op-depot_liste li { margin-bottom: 7px; }
.op-depot_liste strong { color: var(--op-encre); }

/* Un point de dépôt : ambre plutôt que violet. Le contenu est venu vers vous,
   il n'est pas parti — c'est la même géographie, l'événement inverse. Le rouge
   reste écarté ici comme ailleurs : rien de tout cela n'est une erreur. */
.op-monde_point--depot { fill: #B45309 !important; }

/* La porte de sortie de la page de dépôt : discrète, mais toujours là.
   Elle ne doit pas concurrencer le bouton de dépôt, ni disparaître pour
   autant — c'est elle qui distingue un service d'un piège. */
.op-depot_signal { margin: 10px 0 0; text-align: center; }
/* Le signalement porte la MEME allure que sur la page de consultation
   (`#reject`) : un bouton borde, gris, discret — pas un lien souligne. Les
   deux pages proposent le meme geste, dans le meme sens, et rien ne
   justifiait qu'il change de forme d'une page a l'autre.

   Il reste volontairement en retrait par rapport a l'action principale :
   signaler est serieux, mais ce n'est pas ce que la page demande. */
.op-depot_signal-btn {
	appearance: none;
	padding: 10px 18px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: transparent;
	font: inherit;
	font-size: 13.5px;
	font-weight: 600;
	color: var(--op-gris);
	cursor: pointer;
	transition: border-color .15s, color .15s;
}
.op-depot_signal-btn:hover { border-color: var(--op-violet); color: var(--op-violet); }
.op-depot_signal-btn:hover { color: #B45309; }

/* ── Les trois usages, sur la page d'accueil ────────────────────────────
   Trois colonnes qui se replient en une seule : ce sont trois gestes de même
   rang, aucun n'est le principal. Le « quand » vient avant l'explication —
   c'est ce qu'un visiteur cherche d'abord : est-ce que ça me concerne ? */
.op-acc_usages {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
	gap: 22px;
	margin-top: 30px;
}
.op-acc_usage {
	padding: 22px 24px;
	border: 1px solid var(--op-bordure);
	border-radius: 14px;
	background: #FFFFFF;
}
.op-acc_usage h3 { margin: 0 0 6px; font-size: 17px; color: var(--op-encre); }
.op-acc_usage p  { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--op-gris); }
/* Le lien d'installation, dans la carte qui décrit l'extension. Pas de
   pseudo-élément décoratif : la feuille pose `content: ''` sur *::after, ce
   qui donne une boîte à tout le monde — inoffensif ici, fatal dans une
   grille. La flèche n'en vaut pas le risque. */
.op-acc_usage_lien {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	margin-top: 14px;
	padding: 8px 16px;
	border-radius: 9px;
	background: transparent;
	box-shadow: inset 0 0 0 1.5px var(--op-violet);
	font-size: 13.5px;
	font-weight: 700;
	color: var(--op-violet);
	text-decoration: none;
	transition: background 140ms ease, color 140ms ease;
}

.op-acc_usage_lien:hover {
	background: var(--op-surface);
	color: var(--op-violet-fonce);
}

.op-acc_usage_quand {
	margin: 0 0 12px !important;
	font-size: 12.5px !important;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--op-violet) !important;
}

/* ── Les postes connectés à l'extension ─────────────────────────────────
   Une ligne par machine, l'action à droite. Le bouton « Couper » est sobre :
   ce n'est pas une destruction, c'est une porte qu'on referme — et on peut
   toujours se reconnecter. */
.op-ext_liste { list-style: none; margin: 16px 0 0; padding: 0; }
.op-ext_poste {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 14px;
	padding: 13px 15px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	margin-bottom: 9px;
}
.op-ext_poste strong { display: block; font-size: 14.5px; color: var(--op-encre); }
.op-ext_dates {
	display: block;
	margin-top: 3px;
	font-size: 12.5px;
	color: var(--op-gris);
}
.op-ext_form { margin: 0; flex: 0 0 auto; }
.op-ext_couper {
	appearance: none;
	padding: 7px 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 8px;
	background: #FFFFFF;
	font: inherit;
	font-size: 13px;
	font-weight: 600;
	color: var(--op-gris);
	cursor: pointer;
}
.op-ext_couper:hover { border-color: #D9736A; color: #C0392B; }

.op-ext_tout { margin: 12px 0 0; }
.op-ext_tout-btn {
	appearance: none;
	padding: 0;
	border: 0;
	background: none;
	font: inherit;
	font-size: 12.5px;
	color: var(--op-gris);
	text-decoration: underline;
	text-underline-offset: 2px;
	cursor: pointer;
}
.op-ext_tout-btn:hover { color: #C0392B; }

.op-equipe_vide { margin: 16px 0 0; font-size: 14px; color: var(--op-gris); }



/* ══ Creer le lien ══════════════════════════════════════════════════════

   Il PREND LA PLACE de « Suivant » quand le canal vaut « lien a copier » : il
   n'y a pas d'etape 2 — rien a rediger, personne a qui envoyer.

   ⚠️ Les valeurs viennent de `#next-button` (plus haut dans CETTE feuille),
   PAS de `button.forward` de `style.css`. La seconde donne un bouton turquoise
   a coins droits ; c'est la premiere qui gagne a l'ecran, avec des
   `!important` et une specificite d'identifiant. J'avais copie la mauvaise, et
   le bouton ne ressemblait a rien de la page.

   Il ne porte pas la classe `.forward` : l'assistant du gabarit branche son
   avancement sur ce selecteur, et la lui donner le ferait sauter vers une
   etape qui n'existe pas ici. On copie l'apparence, jamais le nom. */
.op-creer_lien {
	display: inline-flex;
	align-items: center;
	gap: 9px;
	background: var(--op-degrade);
	border: none;
	color: #FFFFFF;
	border-radius: 9px;
	padding: 12px 28px;
	font-size: 15px;
	font-weight: 600;
	letter-spacing: -0.01em;
	cursor: pointer;
	box-shadow: 0 4px 14px rgba(118,31,227,0.28);
	transition: transform 160ms ease, box-shadow 180ms ease;
}
.op-creer_lien:active { transform: translateY(1px) scale(0.985); }
.op-creer_lien .op-btn_picto { width: 17px; height: 17px; flex: 0 0 17px; }


/* ══ Un bouton d'envoi ETEINT se voit ═══════════════════════════════════

   ⚠️ `!important` — et ce n'est pas du confort.

   Cette feuille contient, bien plus haut :

       button[type="submit"]:not(.btn-secondary):not(.btn-close) {
           background: var(--op-degrade) !important;
       }

   Un `!important` bat toute regle qui n'en a pas, quelle que soit sa place ou
   sa specificite. Ma premiere version posait `.op-compte_bouton:disabled`
   sans lui : l'attribut `disabled` etait bien la, le bouton refusait bien le
   clic, mais il gardait son degrade violet. Rien ne le distinguait d'un
   bouton actif.

   C'est d'ailleurs pour cette raison que `#accept:disabled`, sur la page de
   consultation, en porte un depuis toujours. Je l'avais lu sans le voir.

   Le selecteur reprend celui qui gene, plus `:disabled` : meme forme, une
   condition de plus, donc il l'emporte proprement. */
button[type="submit"]:not(.btn-secondary):not(.btn-close):disabled,
.op-compte_bouton:disabled {
	background: #E4DFF0 !important;
	color: #9B93AC !important;
	box-shadow: none !important;
	cursor: not-allowed !important;
	transform: none !important;
}



/* ══ La page de depot copie la page de consultation ═════════════════════

   Les deux pages proposent le meme geste au meme destinataire, a une minute
   d'intervalle. Elles n'ont pourtant pas la meme enveloppe : `validate.php`
   vit dans `.modal-content`, `depot.php` dans `.op-compte_carte`. Ces deux
   conteneurs portent des regles OPPOSEES pour le selecteur de langue —
   « FR » encadre de violet d'un cote, rempli de violet de l'autre.

   On aligne le depot sur la consultation, qui sert de reference. */

.op-depot_langues { margin: 14px 0 0; }

.op-depot_langues { text-align: center; }

.op-depot_langues .op-langues {
	display: inline-flex;
	width: auto;
	background: #FFFFFF;
	border-color: var(--op-bordure);
}

.op-depot_langues .op-langues_choix { color: var(--op-gris) !important; }

.op-depot_langues .op-langues_choix:hover {
	background: rgba(118,31,227,0.07);
	color: var(--op-violet) !important;
}

/* Encadre, pas rempli — comme sur la page de consultation. */
.op-depot_langues .op-langues_choix.est-active {
	background: rgba(118,31,227,0.10);
	box-shadow: inset 0 0 0 1.5px var(--op-violet);
	color: var(--op-violet) !important;
}

/* ⚠️ `!important` : la meme regle que pour le bouton eteint.
 *
 *     button[type="submit"]:not(.btn-secondary):not(.btn-close) {
 *         background: var(--op-degrade) !important;
 *     }
 *
 * Elle rendait le signalement VIOLET PLEIN, au meme rang visuel que l'action
 * principale — alors qu'il doit rester en retrait. Sur la page de
 * consultation, `#reject` echappe a cette regle parce que son bouton est un
 * `type="submit"`… lui aussi, mais avec des `!important` a lui. J'ai refait
 * l'erreur deux fois dans la meme soiree : dans cette feuille, un bouton
 * d'envoi qu'on veut discret DOIT le reclamer. */
button[type="submit"].op-depot_signal-btn {
	background: transparent !important;
	border: 1px solid var(--op-bordure) !important;
	color: var(--op-gris) !important;
	box-shadow: none !important;
	width: 100% !important;
	padding: 13px 24px !important;
	border-radius: 10px !important;
	font-size: 14px !important;
	font-weight: 600 !important;
}
button[type="submit"].op-depot_signal-btn:hover {
	border-color: var(--op-violet) !important;
	color: var(--op-violet) !important;
}


/* ══ Le depot se lit comme la consultation ══════════════════════════════

   `#myModal .modal-content` porte `text-align: center` : sur `validate.php`,
   le titre et l'introduction sont centres, et `.op-avert_liste` remet la
   liste a gauche. La carte du depot n'avait rien — d'ou un titre cale a
   gauche la ou l'autre page le centre, pour le meme destinataire a une
   minute d'intervalle.

   On centre ce qui l'est la-bas, et RIEN DE PLUS : une liste, un encadre
   legal ou une case a cocher centres se lisent mal. */
/* On centre LE CONTENEUR, comme `#myModal .modal-content` le fait sur la page
   de consultation — et non tel ou tel element choisi a la main. Cibler le
   titre par `> h1` supposait de connaitre l'arborescence exacte, qui change
   selon la branche affichee (demande valide, deja deposee, signalee…).
   Centrer le conteneur ne suppose rien.

   Puis on remet a gauche ce qui se lit mal centre — c'est exactement ce que
   fait `.op-avert_liste` la-bas. */
.op-depot_carte { text-align: center; }

.op-depot_carte label,
.op-depot_carte .op-avert_liste,
.op-depot_carte .op-depot_liste,
.op-depot_carte .op-compte_note,
.op-depot_carte .op-depot_garde,
.op-depot_carte .op-compte_err,
.op-depot_carte .form-control { text-align: left; }

/* Le logo reste cale a gauche : c'est une signature, pas un titre. */
.op-depot_carte > .op-compte_logo { display: block; text-align: left; }

/* ⚠️ `text-align: center` NE CENTRE PAS UN BLOC. Le sceau est un `div` de
   58 px de large : il restait colle a gauche sous un titre centre. Les autres
   pages qui l'emploient ont une carte alignee a gauche, ou il tombe juste —
   cette page est la seule a tout centrer. */
.op-depot_carte > .op-compte_sceau { margin-left: auto; margin-right: auto; }


/* ══ Le selecteur de langue se CENTRE, il ne s'etire pas ════════════════

   Il occupait toute la largeur de la carte, au meme rang visuel que les
   boutons — alors que c'est un reglage, pas une action. `inline-flex` le fait
   epouser ses quatre elements (le globe et les trois codes), et le
   `text-align: center` du conteneur le pose au milieu.

   ⚠️ `width: auto` explicitement : une regle plus haut dans cette feuille
   etire les enfants du modal, et sans elle la boite reprend toute la ligne. */
#myModal .modal-content .op-langues {
	display: inline-flex;
	width: auto;
	margin-left: auto;
	margin-right: auto;
}


/* Le texte d'une demande, en lecture seule dans le recapitulatif. Il n'est
   pas dans un champ : rien ne s'y saisit, et un champ grise inviterait a
   essayer. */
.op-recap_dem-texte {
	margin: 6px 0 10px;
	padding: 11px 13px;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	background: var(--op-surface);
	font-size: 14px;
	line-height: 1.55;
	color: var(--op-encre);
	white-space: pre-wrap;
}


/* ══ Les onglets du compte ══════════════════════════════════════════════

   « Mon equipe », « Mon abonnement », « Mon compte » et « Ma securite »
   occupaient quatre lignes du menu principal — un tiers de sa hauteur — pour
   des pages qu'on ouvre une fois par trimestre. Elles se naviguent
   maintenant entre elles.

   Une rangee de liens, pas des boutons : ce sont des pages, pas des actions.
   L'onglet courant est souligne plutot que rempli — la page porte deja son
   titre juste au-dessus, le repere n'a pas besoin de crier. */
.op-onglets {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
	margin: 0 0 22px;
	padding-bottom: 2px;
	border-bottom: 1px solid var(--op-bordure);
}

.op-onglets_lien {
	padding: 9px 14px;
	border-bottom: 2px solid transparent;
	margin-bottom: -3px;          /* le trait recouvre celui du conteneur */
	color: var(--op-gris);
	font-size: 14px;
	font-weight: 600;
	text-decoration: none;
	white-space: nowrap;
	transition: color .15s, border-color .15s;
}

.op-onglets_lien:hover { color: var(--op-encre); }

.op-onglets_lien.est-actif {
	color: var(--op-violet);
	border-bottom-color: var(--op-violet);
}

.op-onglets_lien:focus-visible {
	outline: 2px solid var(--op-violet);
	outline-offset: 2px;
	border-radius: 6px;
}

/* Sur un telephone, quatre onglets ne tiennent pas sur une ligne : ils se
   replient plutot que de deborder ou de retrecir la police. */
@media (max-width: 560px) {
	.op-onglets_lien { padding: 8px 10px; font-size: 13px; }
}


/* ══ Supervision : reglages et presence ═════════════════════════════════ */

.op-sup_aide {
	margin: -6px 0 14px;
	font-size: 13.5px;
	line-height: 1.55;
	color: var(--op-gris);
	max-width: 74ch;
}

/* Le reglage se modifie sur une ligne : un champ, un bouton. C'est une
   valeur d'exploitation, pas un formulaire. */
.op-sup_reglage {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px;
	margin: 0 0 26px;
	padding: 14px 16px;
	border: 1px solid var(--op-bordure);
	border-radius: 12px;
	background: var(--op-surface);
}

.op-sup_reglage label { font-size: 13px; font-weight: 600; color: var(--op-encre); }

.op-sup_reglage select,
.op-sup_reglage input[type="url"],
.op-sup_reglage input[type="email"] {
	flex: 1 1 260px;
	min-width: 0;
	padding: 9px 12px;
	border: 1px solid var(--op-bordure);
	border-radius: 9px;
	font: inherit;
	font-size: 14px;
	background: #FFFFFF;
}

.op-sup_reglage button {
	padding: 9px 18px;
	border: none;
	border-radius: 9px;
	background: var(--op-degrade);
	color: #FFFFFF;
	font: inherit;
	font-size: 14px;
	font-weight: 600;
	cursor: pointer;
}

/* Qui est en ligne. Une liste, pas un tableau : trois informations par
   ligne ne valent pas des colonnes. */
.op-sup_enligne {
	margin: 0 0 26px;
	padding: 0;
	list-style: none;
	display: grid;
	gap: 6px;
}

.op-sup_enligne li {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 4px 12px;
	padding: 10px 14px;
	border: 1px solid var(--op-bordure);
	border-radius: 10px;
	background: #FFFFFF;
	font-size: 14px;
}

.op-sup_enligne b { color: var(--op-encre); }

.op-sup_enligne span {
	font-size: 12.5px;
	color: var(--op-gris);
	font-variant-numeric: tabular-nums;
}


/* Le logo Chrome dans un bouton d'installation. Aligné en flex et non en
   grille : la feuille pose `content: ''` sur *::after, et un pseudo-élément
   consommerait alors une cellule entière — invisible ici, fatal là-bas. */
.op-chrome {
	width: 17px;
	height: 17px;
	flex: 0 0 auto;
	display: block;
}

/* Une classe à part, et non `--creux` : celle-ci habille aussi les boutons
   des offres, que `.op-acc_offre .op-acc_bouton` centre en bloc. Les passer
   en flex les casserait. */
.op-acc_bouton--ext {
	display: inline-flex;
	align-items: center;
	gap: 9px;
}

/* ══ Connexion Microsoft ══════════════════════════════════════════════════
 *
 * Le bouton suit les recommandations de marque de Microsoft : logo à gauche,
 * libellé à droite, logo jamais déformé ni recoloré. Le reste — rayon, hauteur,
 * police — s'aligne sur le bouton principal d'OURPASS pour que les deux se
 * lisent comme une paire et non comme deux éléments venus de deux sites.
 *
 * ⚠️ HAUTEUR ET POLICE CALÉES SUR `.btn-primary`. Avant, ce bouton faisait
 * 50 px pour 47, et 16 px de police pour 15 : l'écart était assez petit pour
 * passer inaperçu à la relecture, assez grand pour se voir à l'écran.
 */

.op-login_ou {
	display: flex;
	align-items: center;
	gap: 12px;
	margin: 18px 0 14px;
	color: var(--op-texte-doux, #6b6478);
	font-size: 13px;
}

/* Les deux filets se dessinent en pseudo-éléments : pas de balise vide dans le
   document pour un trait qui n'a aucun sens à la lecture. */
.op-login_ou::before,
.op-login_ou::after {
	content: "";
	flex: 1;
	height: 1px;
	background: var(--op-bordure, #e9e5f0);
}

.op-sso_bouton {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	width: 100%;
	min-height: 47px;
	padding: 12px 24px;
	border: 1px solid var(--op-bordure, #e9e5f0);
	border-radius: 9px;
	background: #fff;
	color: #1a1523;
	font-size: 15px;
	font-weight: 600;
	line-height: 1.2;
	cursor: pointer;
	transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}

.op-sso_bouton:hover {
	border-color: #b9b1c7;
	background: #faf9fc;
}

/* ⚠️ Le focus reste visible : ce bouton se rejoint au clavier depuis le champ
   mot de passe, et un anneau supprimé rendrait le parcours invisible. */
.op-sso_bouton:focus-visible {
	outline: none;
	/* ⚠️ Pas de `border-color` ici : une regle plus specifique de la feuille le
	   fixe au focus, et le poser sans effet ferait croire au prochain lecteur
	   qu'il agit. L'anneau porte a lui seul l'indication — verifie au clavier. */
	box-shadow: 0 0 0 3px rgba(109, 60, 224, .25);
}

/* Le logo garde ses proportions quoi qu'il arrive au conteneur : les quatre
   carrés de Microsoft ne se déforment pas. */
.op-sso_logo {
	flex: 0 0 auto;
	width: 18px;
	height: 18px;
	display: block;
}

@media (max-width: 480px) {
	.op-sso_bouton { font-size: 14px; }
}

/* ⚠️ Le bouton principal était en largeur automatique — 142 px quand les champs
 * et le bouton Microsoft en font 528. Empilés, trois éléments de largeurs
 * différentes donnent une colonne qui « bave » sur la gauche, et le second
 * chemin paraît plus important que le premier. On aligne le bouton principal
 * sur la largeur du formulaire.
 *
 * Portée volontairement étroite (`#login-form`) : ailleurs sur le site, un
 * bouton à sa taille de contenu reste le bon choix.
 */
#login-form button[name="login"] {
	display: block;
	width: 100%;
	min-height: 47px;
}

/* ══ Fermeture du menu : le panneau ne doit pas survivre au voile ═════════
 *
 * ⚠️ LE DÉFAUT. À la fermeture, `close-nav` quitte le bouton immédiatement,
 * mais `fade-in` — posée par `functions.js` — reste 500 ms de plus. Or c'est
 * `close-nav` qui pilote le voile violet ET la couleur blanche des libellés.
 *
 * Pendant ces 500 ms, le panneau restait donc affiché par `fade-in` seul :
 * plus de voile derrière lui, et des libellés revenus au gris. Le menu
 * réapparaissait en gris par-dessus la page, une demi-seconde, à chaque
 * fermeture.
 *
 * La section plus haut avait déjà affranchi l'OUVERTURE de ces 500 ms ; la
 * fermeture était restée en arrière. On la traite ici de la même façon :
 * l'état visible se lit sur le bouton, jamais sur une classe d'animation.
 */
body:not(:has(.cd-nav-trigger.close-nav)) .cd-primary-nav,
body:not(:has(.cd-nav-trigger.close-nav)) .cd-primary-nav.fade-in {
	opacity: 0 !important;
	/* La visibilité bascule APRÈS le fondu, sinon le panneau disparaîtrait
	   d'un coup au lieu de s'effacer. */
	visibility: hidden !important;
	transition: opacity 160ms ease, visibility 0s linear 160ms !important;
}

/* ⚠️ Les libellés restent blancs, y compris pendant le fondu de fermeture.
 * Conditionner leur couleur à `close-nav` les faisait virer au gris à
 * l'instant même où le bouton perdait sa classe — c'est-à-dire pile au moment
 * où le panneau était encore visible. Ce panneau ne s'affiche jamais que
 * par-dessus le voile violet : le blanc est sa seule couleur utile.
 */
.cd-primary-nav li a { color: #FFFFFF !important; }

/* ══ Aperçu du mode « Lien à copier » ═════════════════════════════════════
 *
 * Ce mode n'envoie rien : l'aperçu restait vide, par prudence — montrer un
 * message qui ne partira pas serait pire. Le raisonnement tenait, mais il
 * reste quelque chose de vrai à montrer : le lien, et le fait qu'il ne
 * bougera que par la main de l'expéditeur. Même châssis que l'ordinateur
 * portable, pour que les trois canaux se lisent comme une même famille.
 */
.op-pc--lien .op-pc_lien {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 18px 14px 22px;
}

/* La barre d'adresse : reconnaissable au premier coup d'œil, sans imiter un
   navigateur précis — l'aperçu ne doit pas vieillir avec Chrome. */
.op-pc--lien .op-pc_url {
	flex: 1;
	display: flex;
	align-items: center;
	gap: 7px;
	min-width: 0;
	padding: 8px 11px;
	border: 1px solid var(--op-bordure, #e9e5f0);
	border-radius: 999px;
	background: #fff;
	font-size: 11px;
	color: #4a4358;
}

.op-pc--lien .op-pc_url-texte {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* ⚠️ Le jeton est masqué, jamais rendu. Un aperçu se regarde par-dessus
   l'épaule ; il n'a aucune raison de porter la valeur qui ouvre le coffre. */
.op-pc--lien .op-pc_url-jeton {
	letter-spacing: 2px;
	color: #9a92ab;
}

.op-pc--lien .op-pc_copier {
	flex: 0 0 auto;
	padding: 8px 14px;
	border-radius: 999px;
	background: var(--op-degrade, #761fe3);
	color: #fff;
	font-size: 11px;
	font-weight: 600;
	white-space: nowrap;
}

/* Le châssis est dimensionné pour un e-mail. Une barre d'adresse et un bouton
   n'en remplissent pas le quart : l'écran se règle sur son contenu, sinon la
   maquette a l'air d'attendre quelque chose qui ne viendra pas. */
.op-pc--lien .op-pc_ecran {
	height: auto;
	min-height: 0;
}

/* La ligne qui accompagne « Mail et SMS envoyés ! ». Elle dit qu'un second
   message est parti, sans jamais montrer le code lui-même : cet écran se
   regarde par-dessus l'épaule autant qu'un autre. */
.op-recept_note {
	margin: 10px 0 0;
	text-align: center;
	font-size: 13px;
	color: rgba(255, 255, 255, 0.72);
}

/* ══ Guide d'utilisation ══════════════════════════════════════════════════
 *
 * Reprend le gabarit des pages legales — meme corps, meme largeur — avec
 * trois elements de plus : un sommaire, des etapes numerotees, et deux tons
 * d'encadre. Un guide se parcourt en diagonale : ce qui compte doit se voir
 * sans lire.
 */
.op-aide_sommaire {
	margin: 0 0 42px;
	padding: 20px 24px;
	border: 1px solid var(--op-bordure, #e9e5f0);
	border-radius: 12px;
	background: rgba(118, 31, 227, 0.03);
}

.op-aide_sommaire-titre {
	margin: 0 0 10px;
	font-size: 13px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: #6b6478;
}

.op-aide_sommaire ol { margin: 0; padding-left: 20px; }
.op-aide_sommaire li { margin: 5px 0; }

/* Les etapes portent leur numero en pastille : on retrouve son rang d'un coup
   d'oeil quand on revient au guide au milieu d'une manipulation. */
.op-aide_etapes { list-style: none; margin: 0; padding: 0; counter-reset: op-etape; }

.op-aide_etapes > li {
	position: relative;
	margin: 0 0 30px;
	padding-left: 52px;
	counter-increment: op-etape;
}

.op-aide_etapes > li::before {
	content: counter(op-etape);
	position: absolute;
	left: 0;
	top: 1px;
	width: 34px;
	height: 34px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	background: var(--op-degrade, #761fe3);
	color: #fff;
	font-weight: 700;
	font-size: 15px;
}

.op-aide_etapes > li > h3 { margin: 4px 0 8px; }

/* Deux tons, et un seul sens chacun : l'avertissement signale ce qui peut
   blesser, la note explique ce qui rassure. Trois tons et on ne les distingue
   plus. */
.op-aide_avert,
.op-aide_note {
	margin: 14px 0 0;
	padding: 12px 16px;
	border-left: 3px solid;
	border-radius: 0 8px 8px 0;
	font-size: 15px;
}

.op-aide_avert {
	border-left-color: #d97706;
	background: rgba(217, 119, 6, 0.07);
}

.op-aide_note {
	border-left-color: var(--op-violet, #761fe3);
	background: rgba(118, 31, 227, 0.05);
}

@media (max-width: 560px) {
	.op-aide_etapes > li { padding-left: 44px; }
	.op-aide_etapes > li::before { width: 30px; height: 30px; font-size: 14px; }
}

/* ══ Guide : recherche, tuiles, figures, questions ════════════════════════ */

.op-aide_chercher {
	position: relative;
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 26px 0 10px;
	padding: 0 16px;
	border-radius: 999px;
	background: #fff;
	box-shadow: 0 10px 30px rgba(30, 12, 60, 0.16);
}

.op-aide_chercher svg { width: 19px; height: 19px; flex: 0 0 auto; color: #8d84a0; }

.op-aide_chercher input {
	flex: 1;
	min-width: 0;
	border: 0;
	background: none;
	padding: 16px 0;
	font: inherit;
	font-size: 16px;
	color: #1a1523;
}
.op-aide_chercher input::placeholder { color: #9a92ab; }
.op-aide_chercher input:focus { outline: none; }

/* Le halo est porté par le conteneur : un anneau autour du champ seul
   laisserait la loupe et la croix dehors. */
.op-aide_chercher:focus-within { box-shadow: 0 0 0 3px rgba(139, 92, 246, .45), 0 10px 30px rgba(30, 12, 60, .16); }

.op-aide_chercher button {
	flex: 0 0 auto;
	width: 26px; height: 26px;
	border: 0; border-radius: 50%;
	background: #efeaf7;
	color: #6b6478;
	font-size: 17px; line-height: 1;
	cursor: pointer;
}

.op-aide_resultat { margin: 0; min-height: 20px; font-size: 14px; color: #d9c9f5; }

/* Pendant un filtrage, le sommaire n'a plus de sens : il renvoie a des
   sections masquees. */
.op-aide--filtre .op-aide_sommaire,
.op-aide--filtre .op-aide_tuiles { display: none; }

.op-aide_tuiles {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
	gap: 14px;
	margin: 0 0 34px;
}

.op-aide_tuile {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 20px;
	border: 1px solid var(--op-bordure, #e9e5f0);
	border-radius: 14px;
	background: #fff;
	text-decoration: none;
	color: inherit;
	transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.op-aide_tuile:hover {
	transform: translateY(-2px);
	border-color: #cbbdea;
	box-shadow: 0 12px 26px rgba(30, 12, 60, .10);
}
.op-aide_tuile strong { font-size: 16px; }
.op-aide_tuile span:last-child { font-size: 14px; color: #6b6478; line-height: 1.5; }

.op-aide_tuile-ic {
	width: 38px; height: 38px;
	display: flex; align-items: center; justify-content: center;
	border-radius: 10px;
	background: rgba(118, 31, 227, .08);
	color: var(--op-violet, #761fe3);
	margin-bottom: 4px;
}
.op-aide_tuile-ic svg { width: 20px; height: 20px; }

.op-aide_fig { margin: 22px 0 26px; }
.op-aide_fig svg { width: 100%; height: auto; display: block; }
.op-aide_fig figcaption {
	margin-top: 10px;
	font-size: 14px;
	line-height: 1.55;
	color: #6b6478;
}

/* Les libellés des figures : trois tailles, pas une de plus. */
.op-aide_fig .afT { font: 700 11px/1 Inter, system-ui, sans-serif; fill: #1a1523; }
.op-aide_fig .afL { font: 700 14px/1 Inter, system-ui, sans-serif; fill: #1a1523; }
.op-aide_fig .afM { font: 600 11px/1 Inter, system-ui, sans-serif; fill: #4a4358; }
.op-aide_fig .afS { font: 400 11px/1 Inter, system-ui, sans-serif; fill: #8d84a0; }
.op-aide_fig .afC { font: 700 17px/1 ui-monospace, SFMono-Regular, Menlo, monospace; }

.op-aide_q {
	border: 1px solid var(--op-bordure, #e9e5f0);
	border-radius: 12px;
	margin: 10px 0;
	background: #fff;
}
.op-aide_q > summary {
	list-style: none;
	cursor: pointer;
	padding: 15px 46px 15px 18px;
	position: relative;
	font-weight: 600;
	border-radius: 12px;
}
.op-aide_q > summary::-webkit-details-marker { display: none; }
.op-aide_q > summary::after {
	content: "";
	position: absolute;
	right: 20px; top: 50%;
	width: 9px; height: 9px;
	border-right: 2px solid #8d84a0;
	border-bottom: 2px solid #8d84a0;
	transform: translateY(-70%) rotate(45deg);
	transition: transform .18s ease;
}
.op-aide_q[open] > summary::after { transform: translateY(-30%) rotate(-135deg); }
.op-aide_q > summary:focus-visible { outline: 3px solid rgba(118, 31, 227, .35); outline-offset: 2px; }
.op-aide_q > p { margin: 0 18px 16px; }

@media (max-width: 560px) {
	.op-aide_chercher input { font-size: 16px; padding: 14px 0; }
	.op-aide_tuiles { grid-template-columns: 1fr; }
}

/* ── Pastille d'offre dans le menu ───────────────────────────────────────
 *
 * L'envoi en masse demande l'offre Entreprise. L'entree reste visible — la
 * page qu'elle ouvre explique et propose l'abonnement, c'est une porte de
 * vente — mais on doit le savoir AVANT de cliquer. Ce qui use la confiance,
 * ce n'est pas de voir une fonction qu'on n'a pas : c'est de le decouvrir
 * une fois arrive.
 *
 * Discrete a dessein : elle qualifie l'entree, elle ne la concurrence pas.
 * `text-transform: none` annule le `capitalize` pose sur les <li> du menu. */
.op-nav_pastille {
	display: inline-block;
	margin-left: .5em;
	padding: .18em .55em;
	border: 1px solid rgba(118, 31, 227, .35);
	border-radius: 999px;
	background: rgba(118, 31, 227, .08);
	color: #761FE3;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: none;
	vertical-align: middle;
	white-space: nowrap;
}

/* ── Force du mot de passe, sous le champ ────────────────────────────────
 *
 * Elle n'existait que dans le panneau de generation, et seulement apres un
 * tirage : celui qui tape SON mot de passe n'avait aucun retour. Or c'est
 * precisement lui qui en a besoin — un mot de passe genere est bon par
 * construction.
 *
 * Les memes trois niveaux et le meme seuil de 60 bits que le generateur :
 * deux echelles differentes dans un meme ecran ne s'expliqueraient pas. */
.op-force {
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 8px 0 0;
	font-size: 13px;
}
.op-force_barre {
	flex: 1;
	height: 5px;
	border-radius: 999px;
	background: var(--op-bordure, #E9E5F0);
	overflow: hidden;
}
.op-force_jauge {
	display: block;
	height: 100%;
	width: 0;
	border-radius: 999px;
	transition: width .18s ease, background-color .18s ease;
	background: #D64545;
}
.op-force_mot {
	font-weight: 700;
	white-space: nowrap;
	color: #D64545;
}
.op-force[data-niveau="moyen"] .op-force_jauge { background: #D98A00; }
.op-force[data-niveau="moyen"] .op-force_mot   { color: #D98A00; }
.op-force[data-niveau="eleve"] .op-force_jauge { background: #1E8E5A; }
.op-force[data-niveau="eleve"] .op-force_mot   { color: #1E8E5A; }

/* Compte des unites SMS sous le message du lot.
 * Un SMS est facture AU SEGMENT : depasser 160 unites ne coute pas un peu plus
 * cher, il double la facture — et sur un lot, l'ecart se multiplie par le
 * nombre de destinataires. D'ou l'alerte franche, et non un simple compteur. */
.op-sms_compte {
	margin: 8px 0 0;
	font-size: 13px;
	color: var(--op-gris, #4B4560);
}
.op-sms_compte.est-trop {
	color: #D64545;
	font-weight: 700;
}

/* Les deux modes du generateur, dans le panneau de l'envoi en masse.
 * Meme role que sur l'ecran unitaire : les reglages de l'un n'ont aucun sens
 * pour l'autre — un nombre de majuscules ne dit rien d'une suite de mots — on
 * montre donc les uns OU les autres. */
.op-modal_modes {
	display: flex;
	gap: 8px;
	margin-bottom: 18px;
}
.op-modal_mode {
	flex: 1;
	padding: 9px 12px;
	border: 1px solid var(--op-bordure, #E9E5F0);
	border-radius: 8px;
	background: #FFFFFF;
	color: var(--op-gris, #4B4560);
	font: 600 14px/1 Inter, system-ui, sans-serif;
	cursor: pointer;
}
.op-modal_mode.est-actif {
	border-color: var(--op-violet, #761FE3);
	background: var(--op-violet, #761FE3);
	color: #FFFFFF;
}

/* ── Les liens fabriques par un lot « lien a copier » ────────────────────
 *
 * Rendus a l'ecran, un bouton par ligne — et non exportes en fichier : un CSV
 * de quinze liens vivants est exactement le fichier qui traine que ce produit
 * combat. Le repere accompagne chaque lien, sinon quinze URL identiques a
 * l'oeil ne disent plus laquelle est a qui. */
.op-liens {
	margin: 18px 0 0;
	border: 1px solid var(--op-bordure, #E9E5F0);
	border-radius: 12px;
	overflow: hidden;
	background: #FFFFFF;
}
.op-liens_note {
	margin: 0;
	padding: 14px 18px;
	background: var(--op-surface, #FAF8FF);
	border-bottom: 1px solid var(--op-bordure, #E9E5F0);
	font-size: 13px;
	color: var(--op-gris, #4B4560);
}
.op-liens_ligne {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 12px 18px;
	border-bottom: 1px solid var(--op-bordure, #E9E5F0);
}
.op-liens_ligne:last-child { border-bottom: 0; }
.op-liens_repere {
	flex: 0 0 190px;
	font-weight: 600;
	font-size: 14px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.op-liens_url {
	flex: 1;
	min-width: 0;
	border: 1px solid var(--op-bordure, #E9E5F0);
	border-radius: 8px;
	padding: 8px 10px;
	font: 13px/1.3 ui-monospace, Menlo, monospace;
	background: var(--op-surface, #FAF8FF);
	color: var(--op-encre, #1A1523);
}
.op-liens_copier {
	flex: 0 0 auto;
	border: 0;
	border-radius: 8px;
	padding: 9px 16px;
	background: var(--op-violet, #761FE3);
	color: #FFFFFF;
	font: 600 14px/1 Inter, system-ui, sans-serif;
	cursor: pointer;
}
@media (max-width: 700px) {
	.op-liens_ligne { flex-wrap: wrap; }
	.op-liens_repere { flex: 1 0 100%; }
}

/* ── Le selecteur de canal, au-dessus des deux colonnes ──────────────────
 *
 * Il vivait dans le formulaire d'import, masque en mode « lien » : une fois
 * dans ce mode, plus aucun chemin de retour. Une navigation qui disparait
 * selon l'endroit ou l'on se trouve n'est pas une navigation. */
.op-masse_canal {
	margin: 0 0 18px;
}

/* En mode « lien » il n'y a pas de depot de fichier : la colonne de gauche
 * n'a plus d'objet, et la laisser vide donnait une page qui semblait cassee.
 * Le formulaire prend toute la largeur, borne pour rester lisible. */
@media (min-width: 992px) {
	.op-masse.est-lien { grid-template-columns: minmax(0, 720px); justify-content: center; }
}

/* ⚠️ Un seul champ, une seule colonne.
 * La grille resolvait deux pistes pour l'unique champ « Titre » restant apres
 * le retrait du format d'identifiant — et l'y placait a DROITE, avec un vide a
 * gauche. Un reglage isole n'a pas besoin d'une grille. */
.op-lot_reglages { grid-template-columns: minmax(0, 1fr); }

/* Lignes d'un lot « lien a copier » : pas de destinataire, seulement le rang.
 * Le compteur `op-dest` numerote deja chaque ligne ; c'est lui qui distingue
 * les liens, et il ne promet rien — contrairement a un nom qu'on aurait saisi
 * et que rien n'enregistre. */
.op-lot_lignes.sans-destinataire .op-lot_ligne {
	grid-template-columns: 1fr 34px;
}

/* Le selecteur de canal des pages d'envoi en masse est fait d'ANCRES — la
 * bascule doit fonctionner sans JavaScript. Mais une ancre se souligne, et
 * trois onglets soulignes ne ressemblent plus au selecteur de l'ecran
 * unitaire, qui est fait de boutons. */
.op-canal a.op-canal_btn,
.op-canal a.op-canal_btn:hover,
.op-canal a.op-canal_btn:focus {
	text-decoration: none;
}

/* Le bouton d'ensemble ferme la liste des liens : c'est le geste qui suit la
 * lecture, donc il vient apres elle et se voit — quinze liens copies un par un
 * sont quinze allers-retours vers l'outil de destination. */
.op-liens_tout {
	padding: 14px 18px;
	border-top: 1px solid var(--op-bordure, #E9E5F0);
	background: var(--op-surface, #FAF8FF);
}
.op-liens_tout-btn {
	width: 100%;
	border: 0;
	border-radius: 10px;
	padding: 13px 18px;
	background: var(--op-degrade, linear-gradient(112deg, #761FE3, #4C1D95));
	color: #FFFFFF;
	font: 700 15px/1 Inter, system-ui, sans-serif;
	cursor: pointer;
}

/* ── Blocs de code sur les pages legales ──────────────────────────────
   Aucune page de ce gabarit n'en portait jusqu'ici. La page du dossier
   RSSI en affiche : sans regle, ils heritaient du <pre> par defaut du
   navigateur — sans fond, sans marge, et surtout SANS defilement, si
   bien qu'une ligne de commande un peu longue poussait toute la page
   de 25 px vers la droite.

   `overflow-x: auto` enferme le defilement DANS le bloc : la page ne
   bouge plus, et la ligne reste lisible en la faisant glisser. */
.op-page-legal pre {
    background: #F4F1FA;
    border: 1px solid var(--op-bordure, #E9E5F0);
    border-radius: 8px;
    padding: 14px 16px;
    margin: 14px 0 18px;
    overflow-x: auto;
    line-height: 1.55;
}
.op-page-legal pre code {
    font-size: 13.5px;
    color: var(--op-encre, #1A1523);
    white-space: pre;
}
.op-page-legal p code {
    background: #F4F1FA;
    border-radius: 4px;
    padding: 1px 5px;
    font-size: 13.5px;
}
