## input (`poetry_input`)

A form control for entering a single line of text.

Class: Poetry::Ui::Input::Component - BEM block `poetry-ui-input`.
- `disabled:` (boolean) - default false - Disables the native input.
- `invalid:` (boolean) - default false - Marks the input aria-invalid - the error skin keys on the attribute.
- `mask:` (string) - Format-as-you-type mask descriptor ('(999) 999-9999'). Extra knobs (slot char, always-show, auto-clear) ride Stimulus values via data: - one declarative option covers the common case.
- `name:` (string) - The submitted param name.
- `placeholder:` (string) - Native placeholder text - not a substitute for a Label.
- `type:` (string) - default "text" - The native type attribute (text, email, password, file, ...).
- `value:` (string) - The current value.
- PART `input` - The <input> element itself - no inner anatomy; error state is aria-invalid (set by Field/FormBuilder), never a parallel class | states: data-raw (mask: is set - the unmasked value, kept live by the mask controller (the masked text is what submits))
- WIRING `poetry--core--mask`: values alwaysShowMask, autoClear, mask, showMaskOnFocus, slotChar, upcase; events poetry:mask:change, poetry:mask:complete
- RULE: Inside a form, never render Input directly - use the FormBuilder's field (it wires ids, errors, and aria).
- RULE: Error styling comes from aria-invalid, set from model errors - never hand-toggle error classes.
- RULE: mask: formats as the user types ('(999) 999-9999'; 9=digit, a=letter, A=upper, *=alnum, #=sign/digit, \\ escapes, ? makes the rest optional) - the MASKED text submits; read data-raw for the bare value.
