/**
 * Keeo A11y — feuille de style.
 *
 * Deux parties bien séparées :
 *   1. les PRÉFÉRENCES, déclenchées par les attributs `data-a11y-*` que le
 *      script pose sur `<html>` ;
 *   2. l'HABILLAGE du volet et de son déclencheur.
 *
 * Sur les préférences, `!important` est assumé. C'est l'un des rares cas où il
 * est légitime : l'utilisateur a explicitement demandé cette présentation, elle
 * doit passer devant celle du thème. Le plugin ne connaît pas les thèmes qu'il
 * rencontrera et ne peut pas parier sur leur spécificité.
 *
 * Sur l'habillage, aucun `!important` : tout est préfixé `keeo-a11y-` et
 * paramétré par variables, un site peut donc reprendre l'apparence sans lutter.
 */

/* ═══════════════════════════════════════════════════════════════════════════
   1. PRÉFÉRENCES
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── Taille du texte ──────────────────────────────────────────────────────
 * Agit sur la taille de police racine : tout ce que le thème dimensionne en
 * `rem` suit. Trinity le fait partout, l'échelle se propage donc à la page
 * entière — contrairement à un zoom qui grossit aussi les images.
 */
:root[data-a11y-text-size="112"] { font-size: 112.5% !important; }
:root[data-a11y-text-size="125"] { font-size: 125% !important; }
:root[data-a11y-text-size="150"] { font-size: 150% !important; }

/* ─── Espacement du texte ──────────────────────────────────────────────────
 * Valeurs exactes du critère WCAG 1.4.12 : interligne 1,5 fois la taille de
 * police, espacement des paragraphes 2 fois, des lettres 0,12 em, des mots
 * 0,16 em. Le critère demande que le contenu reste lisible quand l'utilisateur
 * applique ces valeurs — autant lui donner le moyen de le faire.
 *
 * `svg` et tout ce qui est marqué décoratif sont écartés : espacer les lettres
 * d'un pictogramme n'a pas de sens et décale les sprites.
 */
:root[data-a11y-spacing="1"] :where(p, li, dd, dt, blockquote, figcaption,
h1, h2, h3, h4, h5, h6, td, th, label, a, button, span, div):not(svg *):not([aria-hidden="true"]) {
  line-height: 1.5 !important;
  letter-spacing: 0.12em !important;
  word-spacing: 0.16em !important;
}

:root[data-a11y-spacing="1"] :where(p) {
  margin-block-end: 2em !important;
}

/* ─── Surlignage des liens ─────────────────────────────────────────────────
 * Un lien signalé par la seule couleur est invisible pour qui ne la distingue
 * pas (critère 1.4.1). On souligne, et on épaissit assez pour que ce soit net.
 * Les liens qui ne portent qu'une image ou une icône sont laissés tranquilles :
 * un soulignement sous un pictogramme n'apprend rien.
 */
:root[data-a11y-links="1"] a[href]:not(:has(> img)):not(:has(> svg)):not(:has(> picture)) {
  text-decoration: underline !important;
  text-decoration-thickness: 0.09em !important;
  text-underline-offset: 0.18em !important;
}

/* ─── Contraste renforcé ───────────────────────────────────────────────────
 * LIRE CECI AVANT D'AJOUTER UNE RÈGLE DE COULEUR ICI.
 *
 * Le plugin ne connaît pas la palette du thème qu'il rencontre. Forcer le texte
 * en noir et les fonds en blanc casserait toute section écrite en clair sur fond
 * sombre : le contraste y deviendrait NUL. C'est le défaut classique des
 * surcouches d'accessibilité, et c'est précisément ce que ce plugin refuse de
 * faire. Aucune règle ci-dessous ne fixe donc une couleur en dur.
 *
 * D'où deux couches, dont AUCUNE ne vit dans ce fichier pour la partie couleur :
 *
 *   1. CE FICHIER — uniquement des opérations qui ne peuvent pas inverser une
 *      paire texte/fond. Elles reposent toutes sur `currentColor`, c'est-à-dire
 *      la couleur de texte que le thème a lui-même choisie à cet endroit : elle
 *      est lisible sur son propre fond par construction. Ces règles s'appliquent
 *      SANS JavaScript.
 *
 *   2. `assets/js/keeo-a11y-contraste.js` — les couleurs. Le CSS ne peut pas
 *      connaître le fond derrière un texte ; le moteur le mesure et corrige dans
 *      la direction imposée par ce fond. Chargé à la demande, seulement quand la
 *      préférence est active. Voir CLAUDE.md § 6 ter.
 */

/* Bordures des champs de formulaire. C'est le défaut le plus fréquent et le plus
 * facilement mesurable du parc : un contour à 1,6:1 quand le critère 1.4.11 en
 * demande 3. `currentColor` reprend la couleur du texte du champ. */
:root[data-a11y-contrast="1"] :where(input, select, textarea) {
  border-color: currentColor !important;
  border-style: solid !important;
  border-width: max(2px, 0.125rem) !important;
}

/* Les textes indicatifs sont presque toujours affaiblis par une opacité que le
 * navigateur applique en plus de la couleur choisie. On rétablit les deux. */
:root[data-a11y-contrast="1"] ::placeholder {
  color: currentColor !important;
  opacity: 1 !important;
}

/* Un lien signalé par la seule couleur disparaît pour qui ne la distingue pas.
 * Demander plus de contraste implique de ne plus dépendre de la teinte. */
:root[data-a11y-contrast="1"] a[href]:not(:has(> img)):not(:has(> svg)):not(:has(> picture)) {
  text-decoration: underline !important;
  text-decoration-thickness: max(0.09em, 2px) !important;
  text-underline-offset: 0.18em !important;
}

/* Contour de focus épaissi. La couleur reste celle du socle du thème : on ne
 * touche qu'à l'épaisseur, qui ne dépend d'aucune palette. */
:root[data-a11y-contrast="1"] *:focus-visible {
  outline-style: solid !important;
  outline-width: max(4px, 0.25rem) !important;
  outline-offset: 2px !important;
}

/* En contraste forcé du système, c'est le navigateur qui impose sa palette :
 * nos épaisseurs restent utiles, nos couleurs n'ont plus cours. */
@media (forced-colors: active) {
  :root[data-a11y-contrast="1"] :where(input, select, textarea) {
    border-color: ButtonText !important;
  }
}

/* ─── Mode sombre ──────────────────────────────────────────────────────────
 * Le plugin n'en fait presque rien, et c'est voulu : inverser une palette qu'on
 * ne connaît pas donne un résultat inégal, et surtout impossible à vérifier. La
 * préférence n'est même pas proposée tant que le thème n'a pas déclaré sa table
 * (filtre `keeo_a11y/mode_sombre`) — sans quoi la case serait un contrôle qui
 * n'agit sur rien.
 *
 * Reste ici la seule chose vraie partout : annoncer au navigateur que la page
 * est sombre. Il en tire les contrôles de formulaire natifs, les barres de
 * défilement et la couleur de fond du canevas — tout ce que le CSS du thème ne
 * peut pas atteindre.
 */
:root[data-a11y-dark="1"] {
  color-scheme: dark;
}

/* ─── Arrêt des animations ─────────────────────────────────────────────────
 * Même recette que le socle du thème : les durées sont ramenées à 0,01 ms et
 * non supprimées, pour que `transitionend` et `animationend` continuent de se
 * déclencher. Plusieurs composants de la flotte enchaînent dessus et resteraient
 * sinon en attente d'un événement qui n'arrive jamais.
 */
:root[data-a11y-motion="1"] *,
:root[data-a11y-motion="1"] *::before,
:root[data-a11y-motion="1"] *::after {
  animation-duration: 0.01ms !important;
  animation-delay: 0s !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  transition-delay: 0s !important;
}

:root[data-a11y-motion="1"] {
  scroll-behavior: auto !important;
}

/* ─── Police lisible ──────────────────────────────────────────────────────
 * La police du système, celle que l'utilisateur a déjà choisie et que son
 * appareil rend le mieux. Aucune prétention thérapeutique : on ne parle pas de
 * « police pour dyslexiques », dont le bénéfice n'est pas démontré.
 */
:root[data-a11y-font="1"] :where(body, body *):not(svg):not(svg *):not([class*="icon"]) {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif !important;
}

/* ─── Alignement du texte ──────────────────────────────────────────────────
 * Le texte justifié creuse des « rivières » de blanc entre les mots et rend le
 * retour à la ligne plus difficile à suivre — c'est l'une des rares
 * recommandations typographiques que la recherche sur la dyslexie établit
 * clairement. Les coupures de mots posent le même problème.
 *
 * On ne touche QU'AUX blocs de texte. Le centrage d'un titre ou d'un chapô est
 * un choix graphique, pas un obstacle : le redresser abîmerait la page sans rien
 * apporter.
 */
:root[data-a11y-align="1"] :where(p, li, dd, blockquote, figcaption, td) {
  text-align: start !important;
  hyphens: none !important;
  text-wrap: pretty;
}

/* ─── Encadrer les titres ──────────────────────────────────────────────────
 * Rend la structure de la page visible pour qui ne la perçoit pas par la seule
 * taille des caractères. `currentColor` encore : la couleur du titre est lisible
 * sur son propre fond par construction.
 *
 * ⚠️ Trait TIRETÉ, et c'est délibéré : le contour de focus est plein. Deux
 * indicateurs pleins sur la même page deviennent impossibles à distinguer, et
 * l'utilisateur au clavier perdrait le seul repère qui compte vraiment.
 */
/* ⚠️ Le volet est exclu. Ses propres titres — « Profils », « Texte », l'entête —
 * ne sont pas la structure de la PAGE : les encadrer n'apprend rien et transforme
 * le panneau en sapin de Noël. Règle générale : toute préférence qui décore le
 * contenu doit épargner l'habillage du plugin. */
:root[data-a11y-headings="1"] :where(h1, h2, h3, h4, h5, h6):not(.keeo-a11y-volet *) {
  outline: 2px dashed currentColor !important;
  outline-offset: 4px !important;
}

/* ─── Surligner au survol ──────────────────────────────────────────────────
 * Pour qui suit la souris difficilement, ou dont le pointeur dérive. Même
 * précaution : plein pour le focus, ici on épaissit sans changer de style, et le
 * survol ne peut pas être confondu avec le focus puisqu'il suit le pointeur.
 */
:root[data-a11y-hover="1"] :where(a[href], button, [role="button"], summary,
select, textarea, input:not([type="hidden"])):hover {
  outline: 3px solid currentColor !important;
  outline-offset: 2px !important;
}

/* ─── Grand curseur ────────────────────────────────────────────────────────
 * Deux tracés SVG en URI de données, donc aucun fichier à charger et aucune
 * dépendance. Ils sont NOIRS CERNÉS DE BLANC : un curseur d'une seule couleur
 * disparaît sur la moitié des fonds, et ce réglage sert précisément à qui perd
 * son pointeur de vue.
 *
 * ⚠️ Le dernier chiffre du couple est le point actif (« hotspot »), en
 * coordonnées de l'image : la pointe de la flèche est en 5,2 et celle de
 * l'index en 13,3. Se tromper décale les clics de tout le monde.
 *
 * ⚠️ Le pointeur des éléments cliquables est repris séparément. Sans cela, la
 * main du système reprendrait sa taille normale au-dessus des liens — soit
 * exactement là où l'on a le plus besoin de voir le curseur.
 */
:root[data-a11y-cursor="1"],
:root[data-a11y-cursor="1"] * {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M5 2 L20 17 L13.5 17 L17.5 25.5 L13.5 27.5 L9.5 19 L5 23.5 Z' fill='%23000' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'/%3E%3C/svg%3E") 5 2, auto !important;
}

:root[data-a11y-cursor="1"] :where(a[href], button, [role="button"], summary,
label[for], select, [type="submit"], [type="button"], [type="radio"], [type="checkbox"]) {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M11 3.6a1.9 1.9 0 0 1 3.8 0V14h.9v-2.6a1.8 1.8 0 0 1 3.6 0V14h.9v-1.8a1.7 1.7 0 0 1 3.4 0V14h.9v-.9a1.6 1.6 0 0 1 3.2 0v6.7c0 4.6-3 8.2-7.6 8.2h-2.4c-2.8 0-4.4-1.2-5.9-3.4l-4.2-6.2a1.9 1.9 0 0 1 2.9-2.4L11 16Z' fill='%23000' stroke='%23fff' stroke-width='1.6' stroke-linejoin='round'/%3E%3C/svg%3E") 13 3, pointer !important;
}

/* ─── Masquer les images ───────────────────────────────────────────────────
 * `visibility` et non `display` : la place est conservée, donc la page ne se
 * réorganise pas sous les yeux de l'utilisateur — c'est tout l'intérêt pour qui
 * coche ceci par besoin de calme.
 *
 * ⚠️ Les `svg` ne sont PAS masqués. Beaucoup de boutons de la flotte n'ont
 * qu'une icône pour libellé visible : les faire disparaître laisserait des
 * boutons vides. Les visuels posés en `background-image` ne sont pas couverts
 * non plus — le CSS ne sait pas les distinguer d'un dégradé ou d'une puce.
 * C'est dit dans la description du réglage, on ne laisse pas croire l'inverse.
 */
:root[data-a11y-images="1"] :where(img, picture, video, canvas):not(.keeo-a11y-volet *) {
  visibility: hidden !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   2. HABILLAGE

   Aucun `!important` ici, contrairement aux préférences : tout est préfixé
   `keeo-a11y-` et paramétré par variables, un site peut donc reprendre
   l'apparence sans lutter contre nous.

   Le volet est un panneau latéral pleine hauteur, et non une boîte centrée.
   Deux raisons, dans cet ordre : il laisse la page visible pendant qu'on règle,
   donc on VOIT l'effet de ce qu'on coche ; et il monte en hauteur sans jamais
   déborder, ce qu'une boîte centrée ne sait pas faire passé une dizaine
   d'options.
   ═══════════════════════════════════════════════════════════════════════════ */

.keeo-a11y-volet,
.keeo-a11y-flottant,
.keeo-a11y-rappel {
  --keeo-a11y-fond: #ffffff;
  --keeo-a11y-surface: #f4f6f9;
  --keeo-a11y-texte: #14171c;
  --keeo-a11y-attenue: #4a5261;
  --keeo-a11y-accent: #1d55aa;
  --keeo-a11y-bord: #c5ccd6;
  --keeo-a11y-rayon: 0.75rem;
  /* La police du système, pas celle du site. Ce volet est un panneau de
   * réglages, pas du contenu : il doit se lire de la même façon sur les 285
   * sites de la flotte, y compris ceux dont la charte impose une police
   * décorative. Un site qui veut la sienne redéclare cette variable. */
  --keeo-a11y-police: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
}

/* Le volet suit le thème du système. Sans ça, un panneau blanc pur s'ouvre en
 * pleine nuit sur un site sombre — et c'est précisément l'utilisateur sensible
 * à la lumière qui vient y chercher de l'aide. Ce sont NOS couleurs, pas celles
 * du site : aucune palette n'est devinée ici. */
@media (prefers-color-scheme: dark) {
  .keeo-a11y-volet,
  .keeo-a11y-flottant,
  .keeo-a11y-rappel {
    --keeo-a11y-fond: #14171c;
    --keeo-a11y-surface: #1e232b;
    --keeo-a11y-texte: #f2f4f7;
    --keeo-a11y-attenue: #b3bcc9;
    --keeo-a11y-accent: #7fb2ff;
    --keeo-a11y-bord: #3a424e;
  }
}

/* Classe de masquage propre au plugin : on ne dépend pas du `sr-only` du thème,
 * qui peut ne pas exister. JAMAIS `display: none` — l'élément doit rester
 * annoncé aux lecteurs d'écran. */
.keeo-a11y-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ─── Rappel de tête de page ───────────────────────────────────────────────
 * Même principe qu'un lien d'évitement : hors écran, ramené au premier plan dès
 * qu'il reçoit le focus. C'est le chemin clavier vers le volet, et c'est lui qui
 * autorise l'utilisateur à masquer le bouton flottant sans se condamner.
 *
 * ⚠️ Pas de `display: none` ni de `visibility: hidden` : l'un et l'autre le
 * rendraient infocusable, donc inutile.
 */
.keeo-a11y-rappel {
  position: fixed;
  inset-block-start: 0;
  inset-inline-start: 0;
  z-index: 9999;
  transform: translateY(-150%);
  margin: 0.5rem;
  padding: 0.7rem 1.1rem;
  border: 1px solid var(--keeo-a11y-bord);
  border-radius: 0.4rem;
  background: var(--keeo-a11y-fond);
  color: var(--keeo-a11y-texte);
  font: inherit;
  cursor: pointer;
  box-shadow: 0 4px 16px rgb(0 0 0 / 24%);
}

.keeo-a11y-rappel:focus-visible {
  transform: translateY(0);
}

/* ─── Mention de pied de page ──────────────────────────────────────────────
 * « Accessibilité : … », imposée sur chaque page par le décret n° 2019-768.
 *
 * Volontairement NON habillée : elle est rendue dans le pied du thème, dont on
 * ignore la mise en page. Un site qui veut la placer ailleurs la déplace en CSS
 * ou décoche l'option et l'écrit lui-même. Aucune couleur imposée : elle hérite
 * de celle du pied, donc elle est lisible sur son fond par construction.
 */
.keeo-a11y-mention {
  margin: 0;
  font-size: 0.9em;
}

/* ─── Déclaration d'accessibilité ──────────────────────────────────────────
 * Le corps de la déclaration est du CONTENU de page : il s'habille avec le
 * thème, et on n'impose ni couleur, ni police, ni espacement.
 *
 * Une seule reprise : les marqueurs de liste. Beaucoup de thèmes de la flotte
 * les suppriment par défaut, or ce document énumère des non-conformités et des
 * dérogations — sans puce, la structure de l'énumération disparaît à l'œil sur
 * un document opposable. Rien d'autre n'est repris.
 */
.keeo-a11y-declaration-page ul {
  margin-block: 0.5em 1.25em;
  padding-inline-start: 1.5em;
  list-style: disc outside;
}

.keeo-a11y-declaration-page li {
  margin-block-end: 0.25em;
}

/* ─── Repère de lecture ────────────────────────────────────────────────────
 * Posés par `keeo-a11y-confort.js`, qui n'est téléchargé qu'à la demande.
 *
 * ⚠️ `pointer-events: none` est obligatoire, pas cosmétique : sans lui ces
 * calques voleraient tous les clics de la page, et fausseraient en plus le test
 * au point du moteur de contraste.
 *
 * Noir cerné de blanc, comme les curseurs : un repère d'une seule couleur
 * disparaît sur la moitié des fonds.
 */
.keeo-a11y-guide {
  position: fixed;
  inset-inline: 0;
  inset-block-start: 0;
  z-index: 9997;
  height: 0;
  pointer-events: none;
  border-block-start: 3px solid #000;
  box-shadow: 0 -2px 0 0 #fff, 0 2px 0 0 #fff;
}

.keeo-a11y-masque {
  position: fixed;
  inset-inline: 0;
  z-index: 9997;
  pointer-events: none;
  background: rgb(0 0 0 / 62%);
}

.keeo-a11y-masque--haut {
  inset-block-start: 0;
  height: 0;
}

.keeo-a11y-masque--bas {
  inset-block-end: 0;
  inset-block-start: 100%;
}

/* Nos calques se déplacent à chaque mouvement du pointeur : aucune transition,
 * jamais — elle ajouterait un retard entre le pointeur et son repère. */
.keeo-a11y-guide,
.keeo-a11y-masque {
  transition: none !important;
}

/* ─── Déclencheur flottant ─────────────────────────────────────────────── */

.keeo-a11y-flottant {
  position: fixed;
  inset-block-end: 1rem;
  inset-inline-end: 1rem;
  /* Sous une éventuelle modale du thème, au-dessus du reste. Le volet lui-même
   * n'a pas ce souci : un `<dialog>` modal est placé dans le « top layer », donc
   * au-dessus de tout, quel que soit le z-index du thème. */
  z-index: 9998;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  padding: 0;
  border: 1px solid var(--keeo-a11y-bord);
  border-radius: 50%;
  background: var(--keeo-a11y-fond);
  color: var(--keeo-a11y-accent);
  cursor: pointer;
  box-shadow: 0 2px 10px rgb(0 0 0 / 18%);
}

.keeo-a11y-flottant__icone {
  display: block;
  width: 1.75rem;
  height: 1.75rem;
  /* `fill: currentColor` sur le tracé : l'icône suit la couleur du bouton, donc
   * aussi son survol et le thème sombre, sans seconde déclaration. */
  fill: currentColor;
}

.keeo-a11y-flottant:hover {
  background: var(--keeo-a11y-accent);
  color: var(--keeo-a11y-fond);
}

/* L'utilisateur a demandé à ne plus le voir. `display: none` est ici le bon
 * outil — ce n'est pas du contenu qu'on soustrait aux lecteurs d'écran, c'est un
 * doublon du rappel de tête de page, qui lui reste en place. */
:root[data-a11y-sans-bouton="1"] .keeo-a11y-flottant {
  display: none;
}

/* ─── Volet ────────────────────────────────────────────────────────────── */

.keeo-a11y-volet {
  /* Collé au bord, pleine hauteur. `inset` + `margin` explicites : la feuille de
   * l'agent utilisateur centre les `<dialog>` par des marges automatiques. */
  position: fixed;
  inset: 0 0 0 auto;
  width: min(27rem, 100vw);
  height: 100dvh;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--keeo-a11y-fond);
  color: var(--keeo-a11y-texte);
  box-shadow: -10px 0 40px rgb(0 0 0 / 28%);
  font-family: var(--keeo-a11y-police);
  /* Le pied reste visible, seul le corps défile. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.keeo-a11y-volet:not([open]) {
  display: none;
}

/* Voile volontairement léger : tout l'intérêt du panneau latéral est de VOIR la
 * page changer pendant qu'on règle. Assez pour marquer que la page est inerte
 * — `showModal()` s'en charge réellement —, pas assez pour la masquer. */
.keeo-a11y-volet::backdrop {
  background: rgb(0 0 0 / 25%);
}

.keeo-a11y-volet__entete {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: 0;
  padding: 1rem 1.25rem;
  border-block-end: 1px solid var(--keeo-a11y-bord);
}

.keeo-a11y-volet__titre {
  margin: 0;
  font-size: 1.1rem;
  line-height: 1.3;
}

/* Les thèmes habillent leurs titres par des sélecteurs de type — trinity écrit
 * `h2:not([class*="text-heading"])` pour la police, soit (0,1,1), et
 * `h1, h2, h3, h4, h5, h6` pour la couleur, soit (0,0,1) — ce qui l'emporte sur
 * ce que le volet hérite. On reprend la main avec deux classes, soit (0,2,0),
 * plutôt qu'avec `!important` : la règle de l'habillage est qu'un site doit
 * pouvoir nous reprendre l'apparence sans lutter.
 *
 * ⚠️ La COULEUR est aussi concernée, pas seulement la police. Un titre sans
 * déclaration de couleur reprend celle des titres du thème — ici le bleu marine
 * `#014580`, illisible sur le fond sombre du volet (1,85:1). Toute nouvelle
 * balise de titre ajoutée au volet doit être reprise ici. */
.keeo-a11y-volet .keeo-a11y-volet__titre,
.keeo-a11y-volet .keeo-a11y-groupe__titre,
.keeo-a11y-volet .keeo-a11y-champ legend {
  font-family: var(--keeo-a11y-police);
}

.keeo-a11y-volet .keeo-a11y-volet__titre,
.keeo-a11y-volet .keeo-a11y-champ legend {
  color: var(--keeo-a11y-texte);
}

.keeo-a11y-volet .keeo-a11y-groupe__titre {
  color: var(--keeo-a11y-attenue);
}

.keeo-a11y-fermer {
  flex: 0 0 auto;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 0.4rem;
  background: transparent;
  color: inherit;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.keeo-a11y-fermer:hover {
  border-color: var(--keeo-a11y-bord);
}

.keeo-a11y-volet__corps {
  flex: 1 1 auto;
  padding: 0.5rem 1.25rem 1rem;
  overflow-y: auto;
  /* Le défilement du volet ne doit pas entraîner celui de la page derrière. */
  overscroll-behavior: contain;
}

/* ─── Groupes ──────────────────────────────────────────────────────────── */

.keeo-a11y-groupe {
  margin-block: 1.25rem 0;
}

.keeo-a11y-groupe__titre {
  margin: 0 0 0.6rem;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--keeo-a11y-attenue);
}

/* ─── Profils ──────────────────────────────────────────────────────────────
 * Des boutons à bascule (`aria-pressed`), pas des cases : leur état n'est pas
 * stocké, il est calculé à partir des préférences réelles.
 */

.keeo-a11y-profils {
  margin-block: 1.25rem 0;
}

.keeo-a11y-profils__intro {
  margin: 0 0 0.6rem;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--keeo-a11y-attenue);
}

.keeo-a11y-profils__liste {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
}

.keeo-a11y-profil {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.1rem 0.6rem;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--keeo-a11y-bord);
  border-radius: 0.5rem;
  background: var(--keeo-a11y-surface);
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.keeo-a11y-profil__icone {
  grid-column: 1;
  grid-row: 1;
  width: 1.4rem;
  height: 1.4rem;
  fill: currentColor;
  opacity: 0.8;
}

.keeo-a11y-profil__titre {
  grid-column: 2;
  grid-row: 1;
  font-weight: 600;
}

.keeo-a11y-profil__aide {
  grid-column: 2;
  grid-row: 2;
}

.keeo-a11y-profil__aide {
  font-size: 0.8em;
  line-height: 1.35;
  color: var(--keeo-a11y-attenue);
}

.keeo-a11y-profil:hover {
  border-color: var(--keeo-a11y-accent);
}

.keeo-a11y-profil[aria-pressed="true"] {
  border-color: var(--keeo-a11y-accent);
  box-shadow: inset 0 0 0 1px var(--keeo-a11y-accent);
  background: color-mix(in srgb, var(--keeo-a11y-accent) 10%, var(--keeo-a11y-surface));
}

.keeo-a11y-profil[aria-pressed="true"] .keeo-a11y-profil__icone {
  opacity: 1;
  color: var(--keeo-a11y-accent);
}

/* ─── Profil PARTIELLEMENT appliqué ────────────────────────────────────────
 * Deux profils peuvent demander des valeurs DIFFÉRENTES pour un même réglage :
 * « Confort de lecture » veut le texte à 112 %, « Basse vision » à 150 %. Le
 * second écrase donc le premier, qui n'est plus entièrement appliqué — alors que
 * trois de ses quatre réglages le sont toujours.
 *
 * Le montrer simplement « décoché » est exact mais illisible : le client l'a
 * signalé comme un bogue d'affichage, ce qui prouve que l'information passait
 * mal. `aria-pressed="mixed"` est l'état ARIA prévu pour exactement ça, et les
 * lecteurs d'écran l'annoncent « partiellement enfoncé ».
 */
.keeo-a11y-profil[aria-pressed="mixed"] {
  border-color: var(--keeo-a11y-accent);
  border-style: dashed;
}

.keeo-a11y-profil[aria-pressed="mixed"] .keeo-a11y-profil__icone {
  color: var(--keeo-a11y-accent);
}

/* ⚠️ L'état actif ne peut PAS reposer sur la seule couleur (critère 1.4.1) :
 * une bordure accentuée est invisible pour qui ne distingue pas la teinte. D'où
 * ces marques, redondantes avec `aria-pressed` pour les lecteurs d'écran mais
 * indispensables à l'œil. Si `color-mix` n'est pas gérée, la déclaration de fond
 * tombe et ces repères-là restent. */
.keeo-a11y-profil[aria-pressed="true"] .keeo-a11y-profil__titre::before {
  content: "\2713\00a0";
}

.keeo-a11y-profil[aria-pressed="mixed"] .keeo-a11y-profil__titre::before {
  content: "\2013\00a0";
}

/* ─── Champs ───────────────────────────────────────────────────────────── */

.keeo-a11y-champ {
  margin: 0 0 0.5rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--keeo-a11y-bord);
  border-radius: 0.5rem;
  background: var(--keeo-a11y-surface);
}

.keeo-a11y-champ--crans legend {
  /* `float` plutôt que `display: block` : un `<legend>` en disposition normale
   * ne se laisse pas placer dans une grille par tous les navigateurs. */
  float: left;
  width: 100%;
  padding: 0;
  margin-block-end: 0.5rem;
  font-weight: 600;
}

/* Le pictogramme ne peut pas être posé sur le `<legend>` lui-même : plusieurs
 * navigateurs refusent `display: flex` dessus. D'où ce `<span>` intérieur. */
.keeo-a11y-champ__entete {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}

.keeo-a11y-crans {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.15rem 0.75rem;
  clear: both;
}

.keeo-a11y-cran {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  /* Cible de pointage confortable (critère 2.5.8) : la hauteur vient du padding,
   * pas de la taille de la case. */
  padding: 0.35rem 0;
  cursor: pointer;
}

/* Tuile : pictogramme, intitulé, case à droite, description en dessous. */
.keeo-a11y-champ--bascule {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.3rem 0.6rem;
}

.keeo-a11y-champ__icone {
  grid-column: 1;
  grid-row: 1;
  width: 1.4rem;
  height: 1.4rem;
  fill: currentColor;
  opacity: 0.75;
}

.keeo-a11y-champ__titre {
  grid-column: 2;
  grid-row: 1;
  font-weight: 600;
  cursor: pointer;
}

.keeo-a11y-champ--bascule input[type="checkbox"] {
  grid-column: 3;
  grid-row: 1;
}

.keeo-a11y-champ__aide {
  grid-column: 2 / -1;
  grid-row: 2;
  margin: 0;
  font-size: 0.85em;
  line-height: 1.4;
  color: var(--keeo-a11y-attenue);
}

/* Toute la tuile est cliquable, description comprise : le `<label>` s'étire par
 * un pseudo-élément transparent. Il passe au-dessus de la case, mais un clic sur
 * un label coche la case de toute façon — le résultat est identique, et la zone
 * de pointage y gagne beaucoup (critère 2.5.8). */
.keeo-a11y-champ__titre::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* `:has()` plutôt qu'une classe posée en JS : l'état vient de la case elle-même,
 * il ne peut donc pas se désynchroniser. Si `:has()` manque, la case reste
 * cochée et visible — on perd la teinte, jamais l'information. */
.keeo-a11y-champ--bascule:has(input:checked) {
  border-color: var(--keeo-a11y-accent);
  background: color-mix(in srgb, var(--keeo-a11y-accent) 10%, var(--keeo-a11y-surface));
}

.keeo-a11y-champ--bascule:has(input:checked) .keeo-a11y-champ__icone {
  opacity: 1;
  color: var(--keeo-a11y-accent);
}

/* Le focus se voit sur la TUILE : la case est petite et collée au bord droit. */
.keeo-a11y-champ--bascule:has(input:focus-visible) {
  outline: 3px solid var(--keeo-a11y-accent);
  outline-offset: 2px;
}

.keeo-a11y-volet input[type="radio"],
.keeo-a11y-volet input[type="checkbox"] {
  width: 1.15rem;
  height: 1.15rem;
  margin: 0.15rem 0 0;
  accent-color: var(--keeo-a11y-accent);
  flex: 0 0 auto;
}

/* ─── Pied ─────────────────────────────────────────────────────────────── */

.keeo-a11y-volet__pied {
  flex: 0 0 auto;
  padding: 0.9rem 1.25rem 1.1rem;
  border-block-start: 1px solid var(--keeo-a11y-bord);
  background: var(--keeo-a11y-fond);
}

.keeo-a11y-reinit {
  width: 100%;
  padding: 0.65rem 1rem;
  border: 1px solid var(--keeo-a11y-bord);
  border-radius: 0.4rem;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.keeo-a11y-reinit:hover {
  border-color: var(--keeo-a11y-accent);
  color: var(--keeo-a11y-accent);
}

.keeo-a11y-declaration {
  display: inline-block;
  margin-block-start: 0.75rem;
  color: var(--keeo-a11y-accent);
}

.keeo-a11y-volet__mention {
  margin: 0.75rem 0 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--keeo-a11y-attenue);
}

/* Anneau de focus propre au plugin : il ne peut pas compter sur celui du thème,
 * et un volet dont on ne voit pas le focus est inutilisable au clavier. */
.keeo-a11y-volet :where(input, button, a):focus-visible,
.keeo-a11y-flottant:focus-visible,
.keeo-a11y-rappel:focus-visible {
  outline: 3px solid var(--keeo-a11y-accent);
  outline-offset: 2px;
}

/* En contraste forcé du système, nos couleurs n'ont plus cours : on garde des
 * séparations dessinées par des bordures, que le navigateur repeint lui-même. */
@media (forced-colors: active) {
  .keeo-a11y-volet,
  .keeo-a11y-flottant,
  .keeo-a11y-rappel,
  .keeo-a11y-champ,
  .keeo-a11y-profil {
    border: 1px solid CanvasText;
  }

  /* `box-shadow` et `color-mix` ne sont pas rendus en contraste forcé : sans
   * cette reprise, un profil actif serait indiscernable d'un profil inactif. */
  .keeo-a11y-profil[aria-pressed="true"] {
    border-width: 3px;
  }
}

/* Nos propres transitions respectent la préférence système. */
@media (prefers-reduced-motion: reduce) {
  .keeo-a11y-flottant,
  .keeo-a11y-fermer,
  .keeo-a11y-reinit,
  .keeo-a11y-rappel {
    transition: none;
  }
}

/* Sur petit écran, le volet prend toute la largeur : à 27 rem sur un téléphone
 * il en occupait déjà la quasi-totalité, autant assumer et gagner les marges. */
@media (max-width: 30rem) {
  .keeo-a11y-volet {
    width: 100vw;
  }

  .keeo-a11y-crans,
  .keeo-a11y-profils__liste {
    grid-template-columns: 1fr;
  }
}
