/* =============================================================
   Socle · Champ de saisie (.sc-field, .sc-input, .sc-textarea)
   Consomme uniquement des tokens sémantiques.
   Anatomie : .sc-field > __label + contrôle + __help | __error
   Tailles : --s, --l (défaut : m)
   ============================================================= */

/* ---- Enveloppe de champ ---- */

.sc-field {
  display: flex;
  flex-direction: column;
  /* Huit, pas quatre. Une étiquette collée à son champ se lit comme si
     elle lui appartenait plutôt que comme si elle le nommait, et sur
     une pile de six champs les couples se confondent. */
  gap: var(--gap-s);
}

.sc-field__label {
  color: var(--color-text);
  font-size: var(--text-label-size);
  font-weight: var(--text-label-weight);
  line-height: var(--leading-snug);
}

/* L'astérisque d'un champ obligatoire. Une marque et non une couleur
   seule : la couleur ne dit rien à qui ne la voit pas, et le glyphe
   reste lu par une synthèse vocale. Elle se pose à l'intérieur de
   l'étiquette, après le mot, pour que le clic sur l'étiquette continue
   de porter sur le champ. */
.sc-field__required {
  margin-inline-start: var(--space-2);
  color: var(--color-danger-text);
}

/* L'aide et l'erreur portent la taille de l'étiquette, pas celle d'une
   légende. Tout ce qui entoure un champ appartient au même registre :
   c'est du mode d'emploi, pas de la note de bas de page, et à 12 il
   devient une note que personne ne lit avant de se tromper. */
.sc-field__help {
  color: var(--color-text-muted);
  font-size: var(--text-label-size);
  line-height: var(--leading-snug);
}

.sc-field__error {
  display: flex;
  /* Aligné en haut, pas centré : un message d'erreur qui passe sur deux
     lignes recentrait son icône entre les deux, où elle ne désigne plus
     rien. Elle doit rester en face du début du message. */
  align-items: flex-start;
  gap: var(--gap-2xs);
  color: var(--color-danger-text);
  font-size: var(--text-label-size);
  line-height: var(--leading-snug);
}

/* ---- Contrôle de saisie ---- */

.sc-input,
.sc-textarea {
  /* Tokens de composant : tailles et états ne font que réassigner ceci. */
  --_input-height: var(--control-height-m);
  --_input-padding-x: var(--field-padding);
  --_input-font-size: var(--text-field-size);
  /* Le filet ordinaire, pas le soutenu. Un champ n'a pas besoin de
     crier : sa boîte est visible en permanence, contrairement à un
     bouton fantôme. */
  --_input-border: var(--color-border);
  /* Un champ ne réagit pas au survol, et c'est une règle de Vibecoded
     que je reprends telle quelle. Un survol dit « c'est une cible » ;
     une boîte bordée avec une invite le dit déjà, en permanence. Sur un
     formulaire de six champs, six filets qui s'allument au passage de
     la souris sont du bruit, pas de l'information. */
  --_input-border-hover: var(--_input-border);

  width: 100%;
  height: var(--_input-height);
  padding-inline: var(--_input-padding-x);
  background: var(--color-bg-surface);
  border: var(--border-hairline) solid var(--_input-border);
  border-radius: var(--radius-control);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--_input-font-size);
  line-height: var(--leading-normal);
  transition:
    border-color var(--motion-fast),
    background-color var(--motion-fast),
    box-shadow var(--motion-fast);
}

.sc-input::placeholder,
.sc-textarea::placeholder {
  color: var(--color-text-muted);
}

/* ---- États ---- */

.sc-input:hover:not(:disabled),
.sc-input[data-state="hover"],
.sc-textarea:hover:not(:disabled),
.sc-textarea[data-state="hover"] {
  border-color: var(--_input-border-hover);
}

/* Le focus d'un champ n'est pas un anneau détaché. C'est son propre
   filet qui prend l'accent, et l'anneau vient se coller dessus plutôt
   que flotter à deux pixels : une boîte bordée en permanence n'a pas
   besoin qu'on lui dessine un second cadre autour, il suffit que le
   sien change de couleur. L'anneau reste, à épaisseur de filet et à
   décalage nul, parce qu'un outline est la seule chose que le mode
   contrastes forcés sait encore afficher quand il efface les couleurs.
   Une ombre légère complète le signal, comme sur la référence.
   Cette règle vaut aussi pour la simulation de la doc, plus bas. */
.sc-input:focus-visible,
.sc-textarea:focus-visible,
.sc-input[data-state="focus"],
.sc-textarea[data-state="focus"] {
  --_input-border: var(--color-accent);

  /* L'anneau se pose exactement SUR le filet plutôt qu'à côté : un
     décalage négatif d'une épaisseur de trait les fait coïncider, et
     l'arête reste à un pixel au lieu de doubler. C'est la seule façon
     d'avoir un focus qui ne grossit pas la boîte tout en gardant un
     outline, la seule chose que le mode contrastes forcés sait encore
     afficher quand il efface les couleurs. */
  outline: var(--border-hairline) solid var(--color-focus-ring);
  outline-offset: calc(var(--border-hairline) * -1);
  box-shadow: var(--elevation-raised);
}

.sc-input[aria-invalid="true"],
.sc-textarea[aria-invalid="true"] {
  --_input-border: var(--color-danger);
  --_input-border-hover: var(--color-danger-hover);
}

.sc-input:disabled,
.sc-input[data-disabled],
.sc-textarea:disabled,
.sc-textarea[data-disabled] {
  background: var(--color-bg-subtle);
  opacity: var(--opacity-disabled);
  cursor: not-allowed;
}

/* ---- Tailles ---- */

.sc-input--s {
  --_input-height: var(--control-height-s);
  --_input-padding-x: var(--field-padding-s);
  --_input-font-size: var(--text-field-s-size);
}

.sc-input--l {
  --_input-height: var(--control-height-l);
  --_input-padding-x: var(--field-padding-l);
  --_input-font-size: var(--text-field-l-size);
}

/* ---- Zone de texte ---- */

.sc-textarea {
  /* Hauteur minimale structurelle, sans rôle sémantique. */
  --_textarea-min-height: 6rem;

  height: auto;
  min-height: var(--_textarea-min-height);
  padding-block: var(--padding-s);
  resize: vertical;
}
