.form {
  --form-required-color: var(--color-error);
  --form-field-gap: var(--space-m);

  /* Form-specific reading width — tighter than the page container */
  --form-max-width: 50rem;
  --form-input-padding-inline: var(--space-m);

  /* Short labels ("Submit", "Send") read cramped at content width; give
     the pill a floor so it holds its own beside the fields. */
  --form-submit-min-width: 10rem;

  /* Muted copy (labels, revealed placeholders, disclosures) — one step
     darker than --text-muted, which measures under WCAG AA's 4.5:1 both
     on the white inputs (4.19:1) and on the card (3.88:1). neutral-700
     clears it on both. */
  --form-muted-color: var(--digicert-neutral-700);

  /* Control height: room for the floated caption above the value. */
  --form-field-height: var(--btn-height-lg);

  /* Floated caption size; the control's top padding reserves this room so
     the value text never moves when the label floats. */
  --form-label-float-size: var(--body-font-size-xxs);
  --form-select-icon-size: var(--icon-size-s);

  max-width: var(--form-max-width);
  margin-inline: auto;
  background-color: var(--light-color);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius-xl);
  padding: var(--space-m);
}

@media (width >= 600px) {
  .form {
    padding: var(--space-xl);
  }
}

.form .field-wrapper {
  position: relative;
  display: grid;
  grid-auto-flow: row;
  align-items: center;
}

/* Per-page intro authored as a content row in the block table. Default
   content styling applies as-is; only the outer margins are trimmed so
   the card padding and the gap to the first field stay exact. */
.form .form-intro {
  margin-bottom: var(--space-xl);
}

.form .form-intro > :first-child {
  margin-top: 0;
}

.form .form-intro > :last-child {
  margin-bottom: 0;
}

/* Heading rows: card-scale sizes, and let --form-field-gap govern row
   spacing instead of stacking the global heading margins on top of it. */
.form .heading-wrapper :is(h1, h2, h3, h4, h5, h6) {
  margin-block: 0;
}

.form .heading-wrapper h2 {
  font-size: var(--heading-font-size-m);
}

.form .heading-wrapper h3 {
  font-size: var(--heading-font-size-s);
}

/* Plaintext rows are intros/disclosures: centered, capped, small. */
.form .plaintext-wrapper p {
  max-width: 80ch;
  margin: 0 auto;
  text-align: center;
  color: var(--form-muted-color);
  font-size: var(--body-font-size-xs);
}

.form fieldset {
  display: grid;
  grid-auto-flow: row;
  margin: 0;
  border: none;
  padding: 0;
}

.form form > .field-wrapper + .field-wrapper,
.form form fieldset .field-wrapper + .field-wrapper {
  margin-top: var(--form-field-gap);
}

.form form > .selection-wrapper + .selection-wrapper,
.form form fieldset .selection-wrapper + .selection-wrapper {
  margin-top: var(--space-xs);
}

/* The action zone breathes more than the field grid: --space-2xl above
   the submit row, --space-xl above the trailing disclosure that captions
   it. */
.form form > .field-wrapper + .submit-wrapper {
  margin-top: var(--space-2xl);
}

.form form > .submit-wrapper + .plaintext-wrapper {
  margin-top: var(--space-xl);
}

/* ≥600px the form itself becomes a two-column grid: the grid gap takes
   over row spacing, and full-line rows span both columns. */
@media (width >= 600px) {
  .form fieldset {
    grid-template-columns: repeat(2, auto);
    gap: var(--space-xs) var(--space-m);
  }

  /* A step fieldset is the form's main layout, not an inline subgroup —
     its rows and columns breathe like the form's own grid. */
  .form.stepped fieldset {
    gap: var(--form-field-gap) var(--grid-gap);
  }

  .form form {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--form-field-gap) var(--grid-gap);
  }

  /* Must repeat the base rules' `+` selectors — a bare `> .field-wrapper`
     reset is lower-specificity and loses, leaving the stacking margin to
     misalign the first grid row and double the row gap. */
  .form form > .field-wrapper + .field-wrapper,
  .form form fieldset .field-wrapper + .field-wrapper,
  .form form fieldset .selection-wrapper + .selection-wrapper {
    margin-top: 0;
  }

  /* Direct children only — inside a fieldset's own grid these wrappers
     must keep flowing into its 2/3 columns. A sheet row styled
     `full-width` spans the row too (the comp's email and company fields). */

  /* This full-width list is mirrored by fullWidthWrappers in form.js
     (single-field eligibility) — keep the two in sync. */
  .form form > .text-area-wrapper,
  .form form > .submit-wrapper,
  .form form > .heading-wrapper,
  .form form > .plaintext-wrapper,
  .form form > .checkbox-wrapper,
  .form form > .toggle-wrapper,
  .form form > .fieldset-wrapper,
  .form form > .field-wrapper.full-width {
    grid-column: 1 / -1;
  }

  .form form > .submit-wrapper {
    justify-items: center;
  }

  /* Margins add to the grid row gap here, so subtract it to keep the
     action zone's totals exact. */
  .form form > .field-wrapper + .submit-wrapper {
    margin-top: calc(var(--space-2xl) - var(--form-field-gap));
  }

  .form form > .submit-wrapper + .plaintext-wrapper {
    margin-top: calc(var(--space-xl) - var(--form-field-gap));
  }

  /* Single visible field: the button joins the field's row instead of
     sitting centered below an empty column. The field stretches, the
     button takes content width, bottoms aligned (button and input heights
     differ by a few px; bottom alignment reads cleaner than stretching
     the button). Heading/plaintext rows keep spanning full width. */
  .form.single-field form {
    grid-template-columns: 1fr auto;
  }

  .form.single-field form > .submit-wrapper {
    grid-column: auto;
    justify-items: start;
    align-self: end;
    margin-top: 0;
  }
}

@media (width >= 900px) {
  .form fieldset {
    grid-template-columns: repeat(3, auto);
  }

  /* A step fieldset is the form's main layout, not an inline subgroup —
     it keeps the form's own two columns instead of packing 3-across. */
  .form.stepped fieldset {
    grid-template-columns: repeat(2, auto);
  }
}

.form fieldset > legend {
  margin-bottom: var(--space-xs);
  font-size: var(--body-font-size-s);
  font-weight: var(--font-weight-medium);
}

/* ============================================
   SELECTION CONTROLS (radio / checkbox / toggle)
   ============================================ */
.form .field-wrapper.selection-wrapper {
  grid-auto-flow: column;
  justify-content: start;
  gap: 1ch;
}

.form .selection-wrapper label {
  margin: 0;
  font-size: var(--body-font-size-s);
}

.form .selection-wrapper input {
  width: max-content;
  margin: 0;
}

.form .toggle-wrapper .switch {
  position: relative;
  display: inline-block;
  width: 52px;
  height: 28px;
}

.form .toggle-wrapper input {
  opacity: 0;
  width: 52px;
  height: 28px;
}

.form .toggle-wrapper .slider {
  position: absolute;
  cursor: pointer;
  inset: 0;
  border-radius: 28px;
  background-color: var(--dark-color);
  transition: background-color 0.2s;
}

.form .toggle-wrapper .slider::before {
  content: '';
  position: absolute;
  width: 24px;
  height: 24px;
  top: 2px;
  left: 2px;
  border-radius: 50%;
  background-color: var(--background-color);
  transition: transform 0.2s;
}

.form .toggle-wrapper input:checked + .slider {
  background-color: var(--link-color);
}

.form .toggle-wrapper input:focus + .slider {
  outline: 2px solid var(--link-color);
  outline-offset: 2px;
}

.form .toggle-wrapper input:checked + .slider::before {
  transform: translateX(24px);
}

/* ============================================
   FLOATING LABELS
   Every text-like control (input, select, textarea) is followed by its
   real <label> inside the wrapper, so sibling selectors can read the
   control's state. Empty and unfocused, the label sits in the placeholder
   position; with a value or focus it floats to a caption inside the top
   border. The control's top padding always reserves the caption's room,
   so the value text and caret never move.

   Emptiness: inputs/textareas via :placeholder-shown (form-fields.js
   guarantees a placeholder attribute — a space when none is authored);
   selects via their empty-value placeholder option being the selection.
   ============================================ */
.form .field-wrapper:not(.selection-wrapper) > :is(input, select, textarea) {
  box-sizing: border-box;
  display: block;
  width: 100%;
  min-height: var(--form-field-height);
  margin: 0;
  padding: calc(var(--space-s) + var(--form-label-float-size)) var(--form-input-padding-inline) var(--space-s);
  border: 1px solid var(--border-color-strong);
  border-radius: var(--border-radius-m);
  background-color: var(--background-color);
  color: var(--text-color);
  font-size: var(--body-font-size-s);
  line-height: var(--line-height-normal);
}

.form .field-wrapper:not(.selection-wrapper) > label {
  position: absolute;
  top: calc(var(--form-field-height) / 2);
  inset-inline: var(--form-input-padding-inline);
  transform: translateY(-50%);
  margin: 0;
  overflow: hidden;
  color: var(--form-muted-color);
  font-size: var(--body-font-size-s);
  line-height: 1;
  white-space: nowrap;
  text-overflow: ellipsis;
  pointer-events: none;
  transition: top var(--duration-fast) ease, transform var(--duration-fast) ease, font-size var(--duration-fast) ease;
}

/* Leave room for the select chevron. */
.form .field-wrapper:is(.select-wrapper, .country-wrapper, .state-wrapper) > label {
  inset-inline-end: calc(var(--form-input-padding-inline) + var(--form-select-icon-size) + var(--space-s));
}

@media (prefers-reduced-motion: reduce) {
  .form .field-wrapper:not(.selection-wrapper) > label {
    transition: none;
  }
}

/* Validation: only after the visitor has interacted (:user-invalid), so a
   required field isn't red on first paint. Label, border and focus ring
   share the required-marker color. Browsers without :user-invalid keep
   the neutral styling — graceful degradation, the native validation
   bubble still carries the error; a bare :invalid fallback would repaint
   required fields red on first load. */
.form .field-wrapper:not(.selection-wrapper) > :is(input, select, textarea):user-invalid + label {
  color: var(--form-required-color);
}

/* Floated: a value, or focus on a typed field (the caret takes the
   label's place). A select floats only once a real option is picked — a
   focused empty select has nothing to show in the value slot, so the
   label stays put. */
.form .field-wrapper:not(.selection-wrapper) > :is(
  :is(input, textarea):focus,
  input:not(:placeholder-shown),
  textarea:not(:placeholder-shown),
  select:not(:has(> option[value='']:checked))
) + label {
  top: var(--space-s);
  transform: none;
  font-size: var(--form-label-float-size);
}

.form label[data-required]::after {
  content: '*';
  margin-inline-start: var(--space-xs);
  color: var(--form-required-color);
}

/* An authored placeholder is a hint, not the label: hidden until the
   label has floated out of its way. */
.form :is(input, textarea)::placeholder {
  color: transparent;
}

.form :is(input, textarea):focus::placeholder {
  color: var(--form-muted-color);
}

.form textarea {
  resize: vertical;
}

/* Filled fields read darker than empty ones. */
.form .field-wrapper:not(.selection-wrapper) > :is(
  :is(input, select, textarea):hover,
  input:not(:placeholder-shown),
  textarea:not(:placeholder-shown),
  select:not(:has(> option[value='']:checked))
) {
  border-color: var(--text-color);
}

/* Focus ring drawn inside the box (border + inset shadow = 2px) so the
   field's footprint doesn't change. Replaces the global offset outline. */
.form .field-wrapper:not(.selection-wrapper) > :is(input, select, textarea):focus {
  outline: none;
  border-color: var(--form-focus-color, var(--link-color));
  box-shadow: inset 0 0 0 1px var(--form-focus-color, var(--link-color));
}

/* Windows High Contrast forces border-color to one system ink for both
   states and drops box-shadow, while the author's `outline: none` is
   still honored — the focused field would look identical to an unfocused
   one. Restore an outline; Highlight is honored in forced-colors (same
   situation as utilities.css's content-anchor caret). */
@media (forced-colors: active) {
  .form .field-wrapper:not(.selection-wrapper) > :is(input, select, textarea):focus {
    outline: var(--focus-outline-width) solid Highlight;
    outline-offset: var(--focus-outline-offset);
  }
}

.form .field-wrapper:not(.selection-wrapper) > :is(input, select, textarea):user-invalid {
  --form-focus-color: var(--form-required-color);

  border-color: var(--form-required-color);
}

/* Native selects pin the arrow to the edge; appearance:none normalizes the
   box to match inputs and our own chevron (the comp's 20px icon) gets the
   input inset. While the placeholder option is the selection the field's
   own text is transparent — the in-place label stands in — so the
   placeholder text only shows as the heading of the open list. Options
   keep full color so the list doesn't inherit either treatment. */
.form select {
  appearance: none;
  padding-inline-end: calc(var(--form-input-padding-inline) + var(--form-select-icon-size) + var(--space-s));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='-5 -7 20 20'%3E%3Cpath d='M1.175 0L5 3.825L8.825 0L10 1.18333L5 6.18333L0 1.18333L1.175 0Z' fill='%2348565e'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--form-input-padding-inline) center;
  background-size: var(--form-select-icon-size);
}

.form select:has(> option[value='']:checked) {
  color: transparent;
}

.form select option {
  color: var(--text-color);
}

/* ============================================
   SUBMIT
   Default-size global button; medium weight is the one deviation.
   ============================================ */
.form .submit-wrapper .button {
  min-width: var(--form-submit-min-width);
  font-weight: var(--font-weight-medium);
}

/* Inflight submit: keep the button's brand color so it stays visible
   against the form background. Global button:disabled flips it to
   --light-color which reads as "vanished" on light pages. aria-busy is
   only set during submission, not for the silent-lead-loss guard. */
.form button[type="submit"][aria-busy="true"] {
  background-color: var(--btn-primary-bg);
  color: var(--btn-primary-fg);
  cursor: wait;
}

.form .hidden-wrapper,
.form .honeypot-wrapper,
.form .field-wrapper[hidden] {
  display: none;
}

/* ============================================
   LOAD FAILURE
   ============================================ */

/* Fallback state: what form.js renders in place of a form whose sheet
   would not load. Reads as intro copy. */
.form .form-fallback > :first-child {
  margin-top: 0;
}

.form .form-fallback > :last-child {
  margin-bottom: 0;
}

.form .form-fallback .form-fallback-hint {
  color: var(--form-muted-color);
  font-size: var(--body-font-size-s);
}

.form .form-fallback .button-container {
  margin-top: var(--space-m);
}

/* Inflight retry: same treatment as the inflight submit above. */
.form .form-fallback button[aria-busy="true"] {
  background-color: var(--btn-primary-bg);
  color: var(--btn-primary-fg);
  cursor: wait;
}

/* No-script safety net. This file loads independently of form.js, so when
   the module never runs (blocked or failed import — aem.js still marks the
   block loaded) the authored rows would render as a raw table. Hide them
   and show the message instead: STRINGS.fallback from form.js, then a hint
   naming a content blocker, the known cause of a module that never ran
   (PROTO-681). form.js renders .form-fallback for every failure it sees, so
   this rule only fires when the module didn't run.
   test/form-sheet-failure.test.mjs fails if the message drifts. */
.form[data-block-status="loaded"]:not(:has(form, .form-fallback)) > div {
  display: none;
}

.form[data-block-status="loaded"]:not(:has(form, .form-fallback))::after {
  content: "Sorry, this form could not be loaded. A browser extension such as an ad blocker may have blocked the request. Allow this site in your blocker, then try again.";
}

/* Author badge (preview only): form.js sets the attribute and
   html.form-debug through the utils/author-debug.js gate, so this is
   inert in production. Styled like the section badges in utilities.css. */
html.form-debug .form[data-form-load-error]::before {
  display: block;
  width: fit-content;
  margin-bottom: var(--space-m);
  padding: 0 var(--space-xs);
  border-radius: var(--border-radius-xs);
  background: var(--digicert-red);
  color: var(--digicert-white);
  font-size: var(--body-font-size-xxs);
  font-weight: var(--font-weight-bold);
  content: "⚠ form failed to load: " attr(data-form-load-error);
}

/* Deprecated variant classes, intentionally styling nothing so pages still
   authoring them render identically to a bare form:
   - `contact`: its two-column layout is the default now.
   - `placeholder-labels`: its label-in-the-field presentation is the
     default now, as floating labels. */

/* Stepped variant: fieldsets walk as progressive steps. Steps, the trailing
   disclosure, and the relocated submit toggle via .step-hidden; the injected
   nav row holds Back left and Next / Submit right so the action geometry
   never jumps between steps. */
.form .step-hidden {
  display: none;
}

.form.stepped form > .fieldset-wrapper {
  animation: form-step-in var(--duration-fast) ease-out;
}

@keyframes form-step-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .form.stepped form > .fieldset-wrapper {
    animation: none;
  }
}

/* A legend, when authored, reads as the step's title; steps authored
   without one are just fields. */
.form.stepped fieldset > legend {
  margin-bottom: var(--space-m);
  font-size: var(--heading-font-size-s);
  font-weight: var(--font-weight-semibold);
}

/* Dots center on the nav row itself, so uneven Back / Next widths can't
   skew them off the form's midline. */
.form.stepped .step-progress {
  position: absolute;
  left: 50%;
  display: flex;
  gap: var(--space-s);
  transform: translateX(-50%);
}

.form.stepped .step-dot {
  width: var(--space-s);
  height: var(--space-s);
  border-radius: 50%;
  background-color: var(--border-color-strong);
}

.form.stepped .step-dot.active {
  background-color: var(--link-color);
}

.form form > .step-nav-wrapper {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-m);
}

/* Same breathing room as the flat form's action zone above submit; the
   `+` form matches the field-gap rules' specificity, which a bare
   .step-nav-wrapper selector loses to (the nav is a .field-wrapper). */
.form form > .field-wrapper + .step-nav-wrapper {
  margin-top: var(--space-2xl);
}

/* Back renders left but sits after Next/Submit in the DOM, so Tab from
   the step's last field reaches the primary action first. */
.form .step-nav-wrapper .button.secondary {
  order: -1;
  margin-right: auto;
}

.form .step-nav-wrapper .button svg {
  display: block;
  width: var(--icon-size-s);
  height: var(--icon-size-s);
}

/* One arrow asset (icons/arrow-right.svg inlined); Back mirrors it. */
.form .step-nav-wrapper .button.secondary svg {
  transform: scaleX(-1);
}

.form form .step-nav-wrapper .submit-wrapper {
  margin: 0;
}

@media (width >= 600px) {
  .form form > .step-nav-wrapper {
    grid-column: 1 / -1;
  }

  /* The grid row gap adds to the margin here; subtract it to keep the
     action zone's total exact, matching the submit row above. */
  .form form > .field-wrapper + .step-nav-wrapper {
    margin-top: calc(var(--space-2xl) - var(--form-field-gap));
  }

  /* A lone textarea in a step otherwise strands the grid's other column. */
  .form.stepped fieldset > .text-area-wrapper {
    grid-column: 1 / -1;
  }
}

