/* ══ Label input ═══════════════════════════════════════════
   « Label input », de Bencho (bencho.dev, licence MIT) : voir assets/js/champ.js
   et champ_flottant() dans app/lib/helpers.php. Une feuille à part parce que
   le même champ sert sur le site (contact) et dans l'administration (connexion),
   qui n'ont pas la même feuille de style.

   Les jetons de Bencho sont branchés ici, sur le champ seulement, pour que rien
   ne déborde : l'encre est celle du site (--encre) ou de l'admin (--texte), la
   police d'interface est --sans dans les deux, les gris sont ceux du texte
   secondaire. --ink-rgb : trois nombres nus, parce que le CSS en fait des rgba()
   — c'est l'encre du site, #17151A. --fill-on n'a pas d'équivalent : il reste
   indéfini et chaque règle retombe sur --ink, comme chez Bencho. */
.lbi {
  --ink: var(--encre, var(--texte, #17151A));
  --ink-rgb: 23, 21, 26;
  --ink-3: var(--sourdine, var(--muted, #6B6672));
  --ink-4: #8C8794;
  --font-ui: var(--sans, 'DM Sans', system-ui, sans-serif);
  position: relative;
  font-family: var(--font-ui);
  display: grid;
  /* room above the field for the lifted label */
  padding-top: 10px;}

/* no fill: the field is only its outline */
.lbi-box {
  position: relative;
  width: 100%;
  height: 52px;
  border-radius: 14px;
  background: transparent;
  cursor: text;}

.lbi-multi .lbi-box { height: 184px; }

/* sans script, pas de contour dessiné : un filet à la place */
.lbi:not([data-pret]) .lbi-box { box-shadow: inset 0 0 0 1px rgba(var(--ink-rgb), 0.3); }

/* ── the outline ────────────────────────────────────────────
   Always whole. At rest a hairline of grey; focused, the ink
   at a hair heavier; left filled, a grey a step darker than
   rest. The gap across the top is the only part that moves. */
.lbi-ring {
  position: absolute;
  inset: 0;
  overflow: visible;
  pointer-events: none;}

.lbi-ring path {
  fill: none;
  stroke: rgba(var(--ink-rgb), 0.18);
  stroke-width: 1;
  stroke-linecap: butt;
  transition: stroke 240ms ease, stroke-width 240ms ease;}

.lbi[data-focus="true"] .lbi-ring path {
  stroke: var(--ink);
  stroke-width: 1.5;}

.lbi[data-filled="true"]:not([data-focus="true"]) .lbi-ring path {
  stroke: rgba(var(--ink-rgb), 0.3);}

/* the gap: whole at rest, parting from its middle when the
   label lifts. The dash is longer than the path so offset 0
   is the whole line and -1 is none of it. */
.lbi-gap {
  stroke-dasharray: 1 2;
  stroke-dashoffset: 0;
  transition: stroke 240ms ease, stroke-width 240ms ease,
    stroke-dashoffset 320ms cubic-bezier(0.22, 1, 0.36, 1);}

.lbi[data-up="true"] .lbi-gap { stroke-dashoffset: -1; }

/* ── the label ──────────────────────────────────────────────
   Its placeholder self in the middle of the field, and its
   lifted self centred on the top edge — the second is the
   first moved up by half the field and scaled from its left. */
.lbi-label {
  position: absolute;
  left: var(--lbi-x, 18px);
  top: 50%;
  font-size: 15px;
  line-height: 1;
  letter-spacing: -0.005em;
  white-space: pre;
  color: var(--ink-4);
  transform-origin: left center;
  transform: translateY(-50%);
  transition: transform 420ms cubic-bezier(0.22, 1, 0.36, 1), color 200ms ease;
  cursor: text;
  -webkit-user-select: none;
  user-select: none;}

/* la zone de texte : l'étiquette se pose sur la première ligne, à la même
   distance du bord du haut qu'au milieu d'un champ de 52, pour monter pareil */
.lbi-multi .lbi-label { top: 26px; }

.lbi[data-up="true"] .lbi-label,
.lbi:not([data-pret]):has(.lbi-field:focus, .lbi-field:not(:placeholder-shown)) .lbi-label {
  transform: translateY(-50%) translateY(-26px) scale(0.78);
  color: var(--ink-3);}

.lbi[data-focus="true"] .lbi-label { color: var(--fill-on, var(--ink)); }

.lbi-label span span { display: inline-block; }

/* le mot entier, lu par les lecteurs d'écran, sans prendre de place (l'encoche se mesure sur les lettres) */
.lbi-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* the hop: each letter lifts a hair and settles, one after the
   next, so the word ripples as it rises */
.lbi[data-focus="true"] .lbi-label span span {
  animation: lbi-hop 460ms cubic-bezier(0.3, 0.7, 0.3, 1) both;
  animation-delay: calc(var(--i) * 24ms);}

/* `.lbi .lbi-box` devant : l'admin habille déjà tous ses champs
   (.admin input[type="text"], fond gris, bordure) et doit perdre ici */
.lbi .lbi-box .lbi-field {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0 var(--lbi-x, 18px) 0 var(--lbi-x, 18px);
  border: 0;
  border-radius: inherit;
  background: transparent;
  font: inherit;
  font-size: 15px;
  letter-spacing: -0.005em;
  color: var(--fill-on, var(--ink));
  outline: none;
  box-shadow: none;
  min-height: 0;
  cursor: text;}

.lbi .lbi-box .lbi-field:focus { border: 0; background: transparent; box-shadow: none; }

.lbi-field::placeholder { color: transparent; }

.lbi.lbi-multi .lbi-box .lbi-field { padding-top: 18px; padding-bottom: 14px; line-height: 1.5; resize: none; }

/* de la place pour l'œil */
.lbi:has(.lbi-eye) .lbi-box .lbi-field { padding-right: 48px; }

/* the reveal: the text passes through a soft blur rather than
   snapping between dots and letters. Two names so a second
   press restarts it. */
.lbi .lbi-field[data-flip="1"] { animation: lbi-flip-a 260ms ease-out; }

.lbi .lbi-field[data-flip="0"] { animation: lbi-flip-b 260ms ease-out; }

.lbi-eye {
  position: absolute;
  right: 10px;
  top: 50%;
  width: 30px;
  height: 30px;
  margin-top: -15px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--ink-4);
  cursor: pointer;
  transition: color 160ms ease, scale 130ms cubic-bezier(0.3, 0.9, 0.4, 1);}

.lbi-eye:hover { color: var(--fill-on, var(--ink)); }

.lbi-eye:active { scale: 0.9; }

/* the two icons in one place, trading places */
.lbi-eye svg {
  grid-area: 1 / 1;
  width: 16px;
  height: 16px;
  transition: opacity 200ms ease, scale 260ms cubic-bezier(0.22, 1, 0.36, 1), filter 200ms ease;}

.lbi-eye svg:last-child { opacity: 0; scale: 0.7; filter: blur(2px); }

.lbi-eye[data-show="true"] svg:first-child { opacity: 0; scale: 0.7; filter: blur(2px); }

.lbi-eye[data-show="true"] svg:last-child { opacity: 1; scale: 1; filter: none; }

@media (prefers-reduced-motion: reduce) {
  .lbi-ring path,
    .lbi-gap,
    .lbi-label { transition-duration: 1ms; }
}

@media (prefers-reduced-motion: reduce) {
  .lbi[data-focus="true"] .lbi-label span span,
    .lbi-field { animation: none; }
}

@keyframes lbi-hop {
  0% { transform: translateY(0); }
  38% { transform: translateY(-3px); }
  100% { transform: translateY(0); }}

@keyframes lbi-flip-a { from { filter: blur(3px); opacity: 0.5; } }

@keyframes lbi-flip-b { from { filter: blur(3px); opacity: 0.5; } }
