/* Form field — `l-form-field` wraps a label, a control, and optional
   `.l-hint` / `.l-error` messages. The element wires ARIA (id/for,
   aria-describedby, aria-invalid, …) and reflects `layout`,
   `required` / `optional`, and `invalid`. This file only
   styles the anatomy; the control keeps its own styling (`.l-checkbox`, …). */

@layer components {
  l-form-field {
    display: grid;
    gap: var(--l-form-field-gap);
    /* Controls size to their content; text controls (`.l-input`, `.l-textarea`)
       opt into full width via their own CSS — the field never forces width. */
    justify-items: start;

    & > label {
      color: var(--l-form-control-label-color);
      font-size: var(--l-text-sm);
      font-weight: var(--l-font-weight-medium);
    }

    /* Required marker on the label. */
    &[required] > label::after {
      content: var(--l-form-control-required-content);
      margin-inline-start: 0.25ch;
      color: var(--l-form-control-required-color);
    }

    /* Inline layout for toggle controls (checkbox / radio / switch):
       control on the left, label on the right, messages below the label. */
    &[layout='inline'] {
      grid-template-columns: auto 1fr;
      column-gap: var(--l-form-field-choice-gap);
      align-items: center;

      & > :is(input, select, textarea) {
        grid-column: 1;
        grid-row: 1;
      }

      & > label {
        grid-column: 2;
        grid-row: 1;
        cursor: pointer;
      }

      & > :is(.l-hint, .l-error) {
        grid-column: 2;
      }
    }
  }

  /* A disabled control takes its label, hint and required marker with it —
     otherwise a greyed-out control sits under a full-strength label and the
     field still reads as available. Greying the label alone would invert the
     hierarchy: `.l-hint` is `text-secondary`, darker than the disabled ink, so
     the helper text would end up more prominent than the label it explains.

     Matches a native control anywhere in the field, not just a direct child —
     `l-input-group`, `l-input-stepper` and `l-input-otp` keep the real `<input>`
     one level down — plus a form-associated element carrying `disabled` on the
     host (`l-segmented-control`). Placed after the layout block so it outranks
     the inline label rule at equal specificity. */
  l-form-field:has(:is(input, select, textarea):disabled),
  l-form-field:has(> [disabled]) {
    & > label,
    & > .l-hint {
      color: var(--l-form-control-disabled-color);
    }

    /* The marker is decorative once nothing can be submitted; a red asterisk on
       an inert field reads as an unmet requirement the user could act on. */
    &[required] > label::after {
      color: var(--l-form-control-disabled-color);
    }

    /* Undo the inline layout's `cursor: pointer` — clicking the label would
       focus a control that can't be operated. */
    & > label {
      cursor: not-allowed;
    }
  }

  /* `<fieldset disabled>` is the platform's way to disable a group — the usual
     shape for a radio set, where the legend is the group's label and each option
     carries its own. The browser disables the controls inside and Luxen greys
     them, but none of the surrounding text: the legend, the option labels and
     the hint all stayed at full strength, so a disabled group read as available.
     `l-form-field` cannot cover this — the legend lives inside the fieldset, not
     beside the control — so the rule keys off the fieldset itself and applies
     wherever the pattern is used, with or without a field wrapper. */
  fieldset:disabled {
    & :is(legend, label),
    & .l-hint {
      color: var(--l-form-control-disabled-color);
    }

    & label {
      cursor: not-allowed;
    }
  }

  .l-hint {
    display: block;
    margin: 0;
    color: var(--l-form-control-hint-color);
    font-size: var(--l-text-sm);
  }

  .l-error {
    display: block;
    margin: 0;
    color: var(--l-form-control-error-color);
    font-size: var(--l-text-sm);
  }

  /* `.l-error` is a standalone message too (e.g. a radio fieldset or a custom
     control the field can't auto-wire): visible by default, hidden via the
     `hidden` attribute the author toggles directly. */
  .l-error[hidden] {
    display: none;
  }

  /* Inside a field, the field owns the error's visibility through its reflected
     `invalid` state — no JS needed for the resting state. Hidden by default, so
     a `.l-error` never flashes on load (even before the element upgrades, or if
     its script never runs), and revealed only once the field is invalid. */
  l-form-field .l-error {
    display: none;
  }

  l-form-field[invalid] .l-error {
    display: block;
  }
}
