/* ═══════════════════════════════════════════════════════════════════════════
   BLUE HORIZON DESIGN SYSTEM — components/field.css
   Version: 0.1.6 · Build: 2026-09-21 · Benötigt: tokens.css, base.css

   0.1.6 — REPARATUR VON 0.1.5. Der neue Block stand zwischen `.bh-input,`
   und `.bh-textarea {` — also INNERHALB des Gruppenselektors. Damit las
   die Basisregel `.bh-input, .bh-input[type="file"] { cursor; padding }`,
   und `.bh-input` verlor elf Deklarationen an `.bh-textarea`: display,
   width, padding, border, border-radius, background, color, font-family,
   font-size, line-height, transition.

   62 Eingabefelder in zwei Projekten standen dadurch im Browser-Standard
   — hell, ohne Rahmen. Gemeldet wurde EINES, weil auf der betroffenen
   Seite die Nachbarn <textarea> und <select> sind und weiter richtig
   aussahen.

   Zwei Dinge haben das nicht gefangen, und beide sind es wert, gemerkt
   zu werden:
     · Die Klammernbilanz stimmte. Ein Block, der in einen Gruppenselektor
       rutscht, verändert sie nicht.
     · Der Einfügeanker war `.bh-textarea {` — die erste Fundstelle im
       Text, nicht der Anfang einer Regel. Ein Anker muss als
       Regelgrenze NACHGEWIESEN sein, nicht nur eindeutig.

   Siehe die Prüfpflicht unten.

   0.1.5 — Das Dateifeld trug den grauen Systemknopf des Browsers mitten
   in einem Feld, das sonst dem DS folgt. ::file-selector-button gestaltet
   ihn — ohne JavaScript, ohne verstecktes <input>, ohne Markupänderung.

   0.1.4 — Die Regel von 0.1.3 richtete zwei Dinge im Control oben aus,
   stellte sie aber nicht untereinander. Solange ein Projekt einen eigenen
   Wrapper mitbrachte, fiel das nicht auf; ohne ihn standen zwei Textzeilen
   nebeneinander. Jetzt setzt das DS die Spalte — mit zwei Ausnahmen, die
   im Kommentar stehen.

   0.1.3 — Ein Control aus mehreren Teilen (Auswahl plus Knopf darunter)
   ist hoeher als eine Zeile. align-items: center schob das Label dann in
   die Mitte, wo es zwischen den beiden Teilen schwebte statt zu dem zu
   gehoeren, was es benennt. Jetzt richtet sich die Zeile in dem Fall oben
   aus, und das Label bekommt die Hoehe EINES Bedienelements, damit es auf
   dessen Mitte sitzt. Vorbild: .bh-check:has(.bh-check__hint).

   0.1.2 — Die Label-Spalte der Zeilenform war auf 96px festgenagelt. Das
   trägt „Zeit" und „Ort", aber nicht „Worker zuletzt gelaufen" — in einer
   halbbreiten Karte brach dort jedes zweite Label über zwei Zeilen, und die
   Zeilenform war für solche Seiten schlicht unbenutzbar. Jetzt
   --bh-field-label-w mit clamp(), nach dem Vorbild von
   patterns/detail.css (--bh-detail-aside-w).

   0.1.1 — .bh-check bekommt die Beschreibungsform (__text/__title/__hint):
   fetter Titel plus erklärende Zeile. 26 Vorkommen auf 8 Seiten im ersten
   Projekt. Auslöser des Umbruchs ist der Hinweis, nicht ein Modifier.
   VENDORED — READ-ONLY.

   Ersetzt .form-group / .form-input / .form-item aus zehn Projekten.
   Input und Button sitzen auf derselben --bh-control-height. Das ist der
   Kernpunkt der Konsolidierung — siehe TOKEN-MAPPING §5.
   ═══════════════════════════════════════════════════════════════════════════ */

.bh-field { display: flex; flex-direction: column; gap: var(--bh-space-2); min-width: 0; }

.bh-field__label {
  display: flex;
  align-items: center;
  gap: var(--bh-space-1);
  color: var(--bh-text-primary);
  font-size: var(--bh-text-md);
  font-weight: var(--bh-weight-semibold);
  line-height: 1.2;
}
.bh-field__label[data-required]::after,
.bh-field__label--required::after {
  content: "*";
  color: var(--bh-color-danger);
}

.bh-field__hint  { color: var(--bh-text-muted);  font-size: var(--bh-text-xs); }
.bh-field__error { color: var(--bh-color-danger); font-size: var(--bh-text-xs); font-weight: var(--bh-weight-medium); }

/* ── Bedienelemente ──────────────────────────────────────────────────────── */
/* ══ PRÜFPFLICHT FÜR NEUE REGELN IN DIESER DATEI ════════════════════════
   Wer hier eine Regel ergänzt, beantwortet vorher zwei Fragen — beide
   mit einem Befehl, nicht aus dem Kopf:

   1. WEN TRIFFT SIE SONST NOCH? Ein Pseudo-Element oder ein breiter
      Selektor trifft mehr als das Beispiel, an dem er gebaut wurde.
      Die Antwort ist ein Deklarationsdiff der GANZEN Datei gegen den
      Stand davor: Welcher Selektor hat danach andere Deklarationen?
      Erwartet werden genau die neuen. Steht ein alter in der Liste,
      ist die Regel an der falschen Stelle gelandet.

   2. IST DER EINFÜGEORT EINE REGELGRENZE? Ein Anker wie `.bh-textarea {`
      ist im Text eindeutig und trotzdem falsch, wenn er die zweite
      Zeile eines Gruppenselektors ist. Die Klammernbilanz merkt davon
      nichts — sie bleibt ausgeglichen.

   Beides ist in 0.1.5 unterblieben; der Eintrag zu 0.1.6 beschreibt,
   was es gekostet hat. */

.bh-input,
.bh-textarea {
  display: block;
  width: 100%;
  min-height: var(--bh-control-height);
  padding: 0 var(--bh-field-padding-x);

  border: 1px solid var(--bh-border-subtle);
  border-radius: var(--bh-radius-lg);
  background: var(--bh-surface-1);
  color: var(--bh-text-primary);

  font-family: inherit;
  font-size: var(--bh-text-sm);
  line-height: var(--bh-leading-normal);

  transition: border-color var(--bh-transition), box-shadow var(--bh-transition),
              background var(--bh-transition);
}

/* ── Dateifeld ───────────────────────────────────────────────────────────
   Der Knopf gehört zum Element, nicht zum Markup: ::file-selector-button
   gestaltet ihn direkt. Ohne diese Regel steht mitten in einem Feld, das
   sonst dem Designsystem folgt, der graue Systemknopf des Browsers.

   Das ist der Grund, warum hier KEIN verstecktes <input> mit einem <label>
   als Knopfersatz steht. Das erste Projekt hat es so gelöst — Feld auf
   display: none, ein Knopf daneben, ein Skript dazwischen —, und damit
   ist die Dateiauswahl ohne JavaScript nicht mehr erreichbar. Eine Regel
   ist billiger als ein Skript und bleibt ohne eines bedienbar.

   Die Höhe ist die eines Bedienelements minus zwei Rahmen und zwei
   Polsterzeilen — der Knopf sitzt IM Feld, nicht neben ihm. */
.bh-input[type="file"] { cursor: pointer; padding-inline-start: var(--bh-space-2); }

.bh-input[type="file"]::file-selector-button {
  margin-inline-end: var(--bh-space-3);
  min-height: calc(var(--bh-control-height) - var(--bh-space-2) - 2px);
  padding: 0 var(--bh-space-3);

  border: 1px solid var(--bh-border-subtle);
  border-radius: var(--bh-radius-md);
  background: var(--bh-surface-2);
  color: var(--bh-text-primary);

  font: inherit;
  font-weight: var(--bh-weight-medium);
  cursor: pointer;
  transition: background var(--bh-transition), border-color var(--bh-transition);
}
.bh-input[type="file"]::file-selector-button:hover {
  background: var(--bh-surface-3);
  border-color: var(--bh-border-strong);
}

.bh-textarea {
  min-height: calc(var(--bh-control-height) * 3);
  padding: var(--bh-space-2) var(--bh-field-padding-x);
  resize: vertical;
}

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

.bh-input:hover,
.bh-textarea:hover { border-color: var(--bh-border-strong); }

.bh-input:focus,
.bh-textarea:focus {
  outline: none;
  border-color: var(--bh-color-accent);
  box-shadow: var(--bh-focus-ring);
}
.bh-input:focus-visible,
.bh-textarea:focus-visible { outline: none; }

.bh-input:active,
.bh-textarea:active { border-color: var(--bh-color-accent); }

.bh-input:disabled,
.bh-textarea:disabled {
  background: var(--bh-surface-2);
  color: var(--bh-text-muted);
  border-color: var(--bh-border-subtle);
  cursor: not-allowed;
}
.bh-input:read-only,
.bh-textarea:read-only { background: var(--bh-surface-2); color: var(--bh-text-secondary); }

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

.bh-input[aria-invalid="true"]:focus,
.bh-textarea[aria-invalid="true"]:focus,
.bh-input.is-invalid:focus,
.bh-textarea.is-invalid:focus { box-shadow: var(--bh-focus-ring-danger); }

/* ── Kontrollkästchen und Auswahlknopf ───────────────────────────────────── */
.bh-check {
  display: inline-flex;
  align-items: center;
  gap: var(--bh-space-2);
  min-height: var(--bh-control-height);
  color: var(--bh-text-secondary);
  cursor: pointer;
}
.bh-check input[type="checkbox"],
.bh-check input[type="radio"] {
  flex: none;
  width: 16px; height: 16px;
  accent-color: var(--bh-color-accent-strong);
  cursor: pointer;
}
.bh-check:hover { color: var(--bh-text-primary); }
.bh-check input:focus-visible {
  outline: var(--bh-focus-width) solid var(--bh-focus-color);
  outline-offset: var(--bh-focus-offset);
}
.bh-check:has(input:disabled) { color: var(--bh-text-muted); cursor: not-allowed; }
.bh-check input:disabled { cursor: not-allowed; }

/* Beschreibungsform: fetter Titel, darunter die erklärende Zeile.
   Auslöser ist der Hinweis selbst — er allein macht die Zeile zweizeilig.
   Ein Modifier zusätzlich wäre ein zweiter Kanal für dieselbe Bedeutung
   (DESIGN.md §0b). Gilt für Kontrollkästchen und Auswahlknopf gleicher-
   maßen: .bh-check trägt beide Typen, eine eigene Radio-Klasse gibt es
   nicht. Titel ohne Hinweis bleibt einzeilig und mittig. */
.bh-check__text  { display: flex; flex-direction: column; min-width: 0; }
.bh-check__title {
  color: var(--bh-text-primary);
  font-size: var(--bh-text-sm);
  font-weight: var(--bh-weight-medium);
  line-height: var(--bh-leading-normal);
}
.bh-check__hint {
  color: var(--bh-text-muted);
  font-size: var(--bh-text-xs);
  line-height: var(--bh-leading-normal);
}

/* Zweizeilig oben ausrichten, sonst schwimmt das Feld neben der zweiten
   Zeile. Die Mindesthöhe weicht dabei der Polsterung — sonst addierte
   sich 36px Mindesthöhe zum gewachsenen Inhalt. */
.bh-check:has(.bh-check__hint) {
  align-items: flex-start;
  min-height: 0;
  padding-block: var(--bh-space-2);
}
.bh-check:has(.bh-check__hint) input[type="checkbox"],
.bh-check:has(.bh-check__hint) input[type="radio"] {
  /* Titelzeile 13px × 1.5 = 19.5px, Feld 16px → 2px auf die Zeilenmitte. */
  margin-top: 2px;
}

/* ── Anordnung ───────────────────────────────────────────────────────────── */
.bh-form       { display: flex; flex-direction: column; gap: var(--bh-space-4); }
.bh-form-grid  { display: grid; gap: var(--bh-space-4); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.bh-form-grid--full { grid-column: 1 / -1; }
@media (max-width: 767px) { .bh-form-grid { grid-template-columns: 1fr; } }

.bh-form-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--bh-space-2);
  padding-top: var(--bh-space-4);
  border-top: 1px solid var(--bh-border-subtle);
}

/* Zeilenform: Label links, Control rechts auf voller Restbreite.
   Trägt die Eigenschaften-Karte des Detail-Patterns. */
.bh-field--row {
  flex-direction: row;
  align-items: center;
  gap: var(--bh-space-3);
}
/* Die Breite der Label-Spalte gehört zum Inhalt, nicht zur Komponente: wie
   lang die Bezeichnungen sind, weiß nur die Seite. clamp() hält sie in einem
   Rahmen, in dem die Zeilenform noch eine Zeilenform ist — darunter wird das
   Label zum Umbruch gezwungen, darüber bleibt fürs Control nichts übrig.

   Vorbild und ältere Fassung desselben Gedankens: patterns/detail.css mit
   --bh-detail-aside-w. */
.bh-field--row { --bh-field-label-w: 96px; }
.bh-field--row .bh-field__label {
  flex: none;
  width: clamp(72px, var(--bh-field-label-w), 220px);
  font-size: var(--bh-text-xs);
  font-weight: var(--bh-weight-medium);
  color: var(--bh-text-muted);
}
.bh-field--row .bh-field__control { flex: 1; min-width: 0; }

/* Mehr als ein Element im Control heisst: hoeher als eine Zeile. */
.bh-field--row:has(.bh-field__control > :nth-child(2)) {
  align-items: flex-start;
}
.bh-field--row:has(.bh-field__control > :nth-child(2)) .bh-field__label {
  min-height: var(--bh-control-height);
}

/* … und sie stehen UNTEREINANDER. Ohne das legt der Block sie
   nebeneinander, sobald es Inline-Elemente sind — zwei Textzeilen in
   einem Control wurden zu einer.

   :not(br) ist der Unterschied zur Regel darüber. Eine mehrzeilige
   Anschrift mit <br> hat ebenfalls ein zweites Kind, und dort ist die
   obere Ausrichtung richtig — eine Flex-Spalte wäre es nicht: Jedes <br>
   würde zum eigenen Flex-Element und die Zeilen fielen auseinander.
   Belegt in ticketsystem/impressum.php.

   :not(.bh-stack) beim Abstand: Wer ausdrücklich gestapelt hat, hat den
   Abstand schon entschieden. Sonst überschriebe diese Regel ihn still —
   sie ist durch das :has() spezifischer als jede Stapelklasse, und ein
   Autor, der .bh-stack--tight schreibt, bekäme trotzdem 4px.

   4px und nicht 8: Zwei Angaben in EINEM Feld gehören enger zusammen als
   zwei Felder. Wer mehr Luft braucht — eine Auswahl mit einem Knopf
   darunter etwa —, schreibt .bh-stack--tight und bekommt 8. */
.bh-field--row:has(.bh-field__control > :nth-child(2):not(br)) .bh-field__control {
  display: flex;
  flex-direction: column;
}
.bh-field--row:has(.bh-field__control > :nth-child(2):not(br)) .bh-field__control:not(.bh-stack) {
  gap: var(--bh-space-1);
}

@media (max-width: 767px) {
  .bh-input, .bh-textarea { min-height: var(--bh-control-height-touch); }
  .bh-textarea { min-height: calc(var(--bh-control-height-touch) * 3); }
}
