/* ==========================================================================
   forms.css — form system
   --------------------------------------------------------------------------
   Milestone 6 · Form System

   Spec: Phase 1 §8 (Forms) · Phase 5 §5 (Form Components) · Phase 3 §6.7 /
   §6.8 / §9.2 / §9.3 (field set, validation copy, microcopy).

   Shared rule (Phase 5 §5): every field uses Inter 16px input text (iOS
   zoom prevention), 44px min height, `radius-sm`, 1px `border` → 2px
   `primary` ring on focus (outer shadow, no layout shift), `space-24`
   vertical gap between fields.

   States follow the Global State Model (Phase 5 §1.6) and Phase 1 §8.3:
   default · hover · focus-visible · filled · disabled · read-only · error
   · success. Visual states only — no JavaScript validation.

   Layout reuses the existing utilities in layout.css (`.grid`, `.col-span-*`,
   `.flow`, `.sr-only`); forms.css adds no page-specific layout.

   Forced-state classes (`.is-hover` / `.is-focus`) mirror the real states so
   pages/components.html can preview them statically — showcase-only, sharing
   selector lists with the real `:hover` / `:focus-visible` rules.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Field system — wrapper around label + control + helper + validation
   -------------------------------------------------------------------------- */

/* Single field group ("Form Group"). Field-to-field gap: space-24 (Phase 1
   §8.1 / Phase 5 §5). Fields compose with `.flow` or `.grid` + `.col-span-*`. */
.form-field {
  margin-block-end: var(--space-24);
}

.form-field:last-child {
  margin-block-end: 0;
}

/* Group of related controls (radio options) — fieldset reset + legend in
   label style. Group gap: space-40 (Phase 1 §8.1) outside, space-16 inside. */
.form-fieldset {
  margin: 0;
  padding: 0;
  border: 0;
  margin-block-end: var(--space-40);
}

.form-fieldset:last-child {
  margin-block-end: 0;
}

.form-fieldset__legend {
  display: block;
  margin-block-end: var(--space-8);
  padding: 0;
  color: var(--color-heading);
  font-size: var(--text-label-current);
  font-weight: var(--weight-semibold);
  line-height: var(--lh-label);
  letter-spacing: var(--tracking-label);
}

.form-fieldset__options {
  display: grid;
  gap: var(--space-16);
}

/* Layout integration — when fields are direct children of `.grid`, the grid
   gap (space-24, matching the field gap) governs row spacing; drop the field's
   own block-end margin so spacing is not doubled (Phase 5 §2 / §5). */
.grid > .form-field,
.grid > .form-fieldset,
.grid > .form-actions {
  margin-block-end: 0;
}

/* --------------------------------------------------------------------------
   Field label — Label style (13px Inter 600), space-8 above field
   -------------------------------------------------------------------------- */
.form-label {
  display: block;
  margin-block-end: var(--space-8);
  color: var(--color-heading);
  font-size: var(--text-label-current);
  font-weight: var(--weight-semibold);
  line-height: var(--lh-label);
  letter-spacing: var(--tracking-label);
}

/* Required/optional indicator — written out, never an asterisk alone
   (Phase 5 §5.12 / Phase 3 §9.2). 14px, space-4 after the label text. */
.form-label__required,
.form-label__optional {
  margin-inline-start: var(--space-4);
  color: var(--color-body);
  font-size: var(--text-small-current);
  font-weight: var(--weight-regular);
}

/* --------------------------------------------------------------------------
   Control base — text / email / telephone inputs (single line, h48)
   -------------------------------------------------------------------------- */
.form-control {
  width: 100%;
  height: 48px;
  min-height: var(--touch-min);
  padding: 0 var(--space-16);
  background: var(--color-background);
  color: var(--color-heading);
  font-family: var(--font-body);
  /* 16px is the documented iOS-zoom-prevention exception (Phase 1 §8.1 /
     Phase 5 §5) and is intentionally not the responsive body token. */
  font-size: 16px;
  line-height: var(--lh-button);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-inset);
  transition:
    border-color var(--transition-fast),
    box-shadow var(--transition-fast),
    background-color var(--transition-fast);
}

.form-control::placeholder {
  color: color-mix(in srgb, var(--color-body) 60%, transparent);
  opacity: 1;
}

.form-control::-webkit-input-placeholder {
  color: color-mix(in srgb, var(--color-body) 60%, transparent);
}

/* Hover — light surface: +8% brand tint (Global State Model, Phase 5 §1.6).
   No elevation: inputs already carry the subtle inset (Phase 1 §16). */
.form-control:hover,
.form-control.is-hover {
  background: color-mix(in srgb, var(--color-primary) 8%, var(--color-background));
}

/* Focus-visible — 2px `primary` ring via outer shadow, no layout shift
   (Phase 1 §8.1 / §6). Replaces the global outline with the documented
   shadow ring so border width does not change. */
.form-control:focus-visible,
.form-control.is-focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary);
  background: var(--color-background);
}

/* Filled — `:not(:placeholder-shown)` keys the state without JavaScript.
   The design system defines no distinct resting visual for filled fields
   (Phase 1 §8.3 lists only default/focus/error/success/disabled), so the
   selector exists so error/success styles can key off it. */
.form-control:not(:placeholder-shown) {
  background: var(--color-background);
}

/* Error — 2px `highlight` border + ring; message styled by .form-validation
   below (Phase 1 §8.3). Driven by `aria-invalid` so markup stays semantic. */
.form-control[aria-invalid="true"] {
  border-color: var(--color-error);
  box-shadow: 0 0 0 2px var(--color-error);
  background: var(--color-background);
}

/* Success — 2px `secondary` border + ring (Phase 1 §8.3). */
.form-control--success {
  border-color: var(--color-secondary);
  box-shadow: 0 0 0 2px var(--color-secondary);
  background: var(--color-background);
}

/* Disabled — 40% opacity, no interactions (Phase 1 §8.3). */
.form-control:disabled {
  opacity: 0.4;
  pointer-events: none;
  box-shadow: none;
  cursor: not-allowed;
}

/* Read-only — the design system defines no read-only visual; this follows
   the conventional form pattern of a tinted, non-interactive field that
   remains focusable and announced by assistive tech. */
.form-control[readonly] {
  background: var(--color-surface);
  color: var(--color-body);
  box-shadow: none;
  cursor: default;
}

/* --------------------------------------------------------------------------
   Textarea — min 120px, vertical resize only (Phase 1 §8.2 / Phase 5 §5.2)
   -------------------------------------------------------------------------- */
.form-control--textarea {
  height: auto;
  min-height: 120px;
  padding: var(--space-8) var(--space-16);
  line-height: var(--lh-body);
  resize: vertical;
}

/* --------------------------------------------------------------------------
   Select dropdown — 48px trigger + chevron (Phase 1 §8.2 / Phase 5 §5.3).
   Native `<select>` keeps full keyboard/AT behaviour without JavaScript;
   the custom combobox panel (options card, checkmark, arrows/Enter/Escape)
   is deferred to the JS milestone.
   -------------------------------------------------------------------------- */
.form-select {
  position: relative;
}

.form-control--select {
  appearance: none;
  padding-right: var(--space-40);
  cursor: pointer;
}

.form-control--select option {
  color: var(--color-heading);
  background: var(--color-background);
}

.form-select__chevron {
  position: absolute;
  top: 50%;
  right: var(--space-16);
  width: 16px;
  height: 16px;
  transform: translateY(-50%);
  color: var(--color-body);
  pointer-events: none;
}

.form-select:focus-within .form-select__chevron,
.form-select.is-focus .form-select__chevron {
  color: var(--color-primary);
}

/* --------------------------------------------------------------------------
   Checkbox — 20×20 box, 2px border, checked → `primary` fill + white mark,
   44×44 hit area, label gap space-8 (Phase 1 §8.2 / Phase 5 §5.4)
   -------------------------------------------------------------------------- */
.form-check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  min-height: var(--touch-min);
  cursor: pointer;
}

.form-check__input {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  margin: 0;
  appearance: none;
  background: var(--color-background);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-sm);
  transition:
    border-color var(--transition-fast),
    background-color var(--transition-fast),
    box-shadow var(--transition-fast);
}

.form-check__input:hover,
.form-check__input.is-hover {
  border-color: var(--color-primary);
}

.form-check__input:checked {
  background: var(--color-primary);
  border-color: var(--color-primary);
}

.form-check__input:checked::after {
  content: "";
  display: block;
  width: 10px;
  height: 5px;
  margin: 4px auto 0;
  border-bottom: 2px solid var(--color-white);
  border-left: 2px solid var(--color-white);
  transform: rotate(-45deg);
}

.form-check__input:focus-visible,
.form-check__input.is-focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--color-primary);
}

.form-check__input[aria-invalid="true"] {
  border-color: var(--color-error);
}

.form-check__input:disabled {
  opacity: 0.4;
  pointer-events: none;
}

.form-check__text {
  color: var(--color-heading);
  font-size: var(--text-body-current);
  line-height: var(--lh-body);
}

/* --------------------------------------------------------------------------
   Radio button — 20×20 circle, 2px border, selected → `primary` outer +
   `primary` dot, 44×44 hit area (Phase 1 §8.2 / Phase 5 §5.5)
   -------------------------------------------------------------------------- */
.form-radio {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  min-height: var(--touch-min);
  cursor: pointer;
}

.form-radio__input {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  margin: 0;
  appearance: none;
  background: var(--color-background);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-full);
  display: grid;
  place-items: center;
  transition:
    border-color var(--transition-fast),
    box-shadow var(--transition-fast);
}

.form-radio__input::after {
  content: "";
  width: 10px;
  height: 10px;
  background: var(--color-primary);
  border-radius: var(--radius-full);
  transform: scale(0);
  transition: transform var(--transition-fast);
}

.form-radio__input:hover,
.form-radio__input.is-hover {
  border-color: var(--color-primary);
}

.form-radio__input:checked {
  border-color: var(--color-primary);
}

.form-radio__input:checked::after {
  transform: scale(1);
}

.form-radio__input:focus-visible,
.form-radio__input.is-focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--color-primary);
}

.form-radio__input[aria-invalid="true"] {
  border-color: var(--color-error);
}

.form-radio__input:disabled {
  opacity: 0.4;
  pointer-events: none;
}

.form-radio__text {
  color: var(--color-heading);
  font-size: var(--text-body-current);
  line-height: var(--lh-body);
}

/* --------------------------------------------------------------------------
   Toggle switch — track 44×24, thumb 20, on → `primary` (Phase 5 §5.6).
   role="switch" on the input keeps the native checkbox semantics while
   exposing aria-checked.
   -------------------------------------------------------------------------- */
.form-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  min-height: var(--touch-min);
  cursor: pointer;
}

.form-switch__input {
  position: relative;
  flex-shrink: 0;
  width: 44px;
  height: 24px;
  margin: 0;
  appearance: none;
  background: var(--color-border);
  border-radius: var(--radius-full);
  transition:
    background-color var(--transition-fast),
    box-shadow var(--transition-fast);
}

.form-switch__input::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  background: var(--color-white);
  border-radius: var(--radius-full);
  transition: transform var(--transition-fast);
}

.form-switch__input:hover,
.form-switch__input.is-hover {
  border-color: var(--color-primary);
}

.form-switch__input:checked {
  background: var(--color-primary);
}

.form-switch__input:checked::before {
  transform: translateX(20px);
}

.form-switch__input:focus-visible,
.form-switch__input.is-focus {
  outline: none;
  box-shadow: 0 0 0 2px var(--color-primary);
}

.form-switch__input:disabled {
  opacity: 0.4;
  pointer-events: none;
}

.form-switch__text {
  color: var(--color-heading);
  font-size: var(--text-body-current);
  line-height: var(--lh-body);
}

/* --------------------------------------------------------------------------
   File upload — dropzone placeholder only (Phase 5 §5.7). Idle, hover /
   focus-within and disabled states; drag-over and selected require the JS
   milestone. Zone h96, padding space-24, radius-md, 1px dashed border.
   -------------------------------------------------------------------------- */
.form-file {
  position: relative;
}

.form-file__zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  min-height: 96px;
  padding: var(--space-24);
  text-align: center;
  background: var(--color-background);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition:
    border-color var(--transition-fast),
    background-color var(--transition-fast),
    box-shadow var(--transition-fast);
}

.form-file__zone:hover,
.form-file__zone.is-hover {
  background: color-mix(in srgb, var(--color-primary) 8%, var(--color-background));
}

.form-file:focus-within .form-file__zone,
.form-file__zone.is-focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px var(--color-primary);
}

.form-file__icon {
  width: 32px;
  height: 32px;
  color: var(--color-primary);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.form-file__text {
  color: var(--color-heading);
  font-size: var(--text-body-current);
  font-weight: var(--weight-semibold);
  line-height: var(--lh-body);
}

.form-file__hint {
  color: var(--color-body);
  font-size: var(--text-small-current);
  line-height: var(--lh-small);
}

.form-file:has(.form-file__input:disabled) {
  opacity: 0.4;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   Helper text — Small 14px `body`, 6px below field, linked via
   aria-describedby (Phase 1 §8.1 / Phase 5 §5.11). 6px is doc-specified and
   intentionally not a token (the scale offers 4/8).
   -------------------------------------------------------------------------- */
.form-helper {
  margin-block-start: 6px;
  color: var(--color-body);
  font-size: var(--text-small-current);
  line-height: var(--lh-small);
}

/* --------------------------------------------------------------------------
   Validation message — inline, variants error / success / info (Phase 5
   §5.9 / Phase 1 §8.3). 14px, 6px below field, icon gap space-8. Referenced
   by the field's aria-describedby; the message carries aria-live="polite".
   -------------------------------------------------------------------------- */
.form-validation {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  margin-block-start: 6px;
  font-size: var(--text-small-current);
  line-height: var(--lh-small);
}

.form-validation__icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.form-validation--error {
  color: var(--color-highlight);
}

.form-validation--success {
  color: var(--color-secondary);
}

.js-form-status {
  margin-block-start: var(--space-24);
}

/* --------------------------------------------------------------------------
   Submit button container (Phase 5 §5.8) — primary only; full width on
   mobile, auto at desktop.
   -------------------------------------------------------------------------- */
.form-actions {
  margin-block-start: var(--space-24);
}

.form-actions .button {
  width: 100%;
}

@media (min-width: 768px) {
  .form-actions .button {
    width: auto;
  }
}
