/**
 * form.css — Generic form component.
 *
 * Philosophy: element-first, minimal classes. The stylesheet targets
 * the <form> element directly and keys its rules off semantic
 * children (label, input, button). Reach for a class only when one
 * element on the same form needs two different looks — typically a
 * domain sub-layout like .address or .catalogs.
 *
 * Scope note: because selectors target bare <form>, plugin-injected
 * forms (comment form, search form, Contact Form 7, WooCommerce) will
 * inherit the base input/label styling. If that becomes a problem,
 * scope by parent (e.g. `main form`) or add an opt-out class.
 *
 * ──────────────────────────────────────────────────────────────────
 * Intended markup
 * ──────────────────────────────────────────────────────────────────
 *
 *   <form novalidate>
 *
 *     Intro text (optional) — spans full width of the grid:
 *
 *       <header>
 *         <h3>Beställ hem katalogerna</h3>
 *         <p>Fyll i dina uppgifter nedan.</p>
 *       </header>
 *
 *     Single field — label stacks above input:
 *
 *       <label>
 *         <span>Namn</span>
 *         <input type="text" name="name" autocomplete="name" required>
 *       </label>
 *
 *     Single checkbox — a plain label. The CSS detects the nested
 *     checkbox and renders the row inline automatically:
 *
 *       <label>
 *         <input type="checkbox" name="privacy" required>
 *         <span>Jag accepterar integritetspolicyn.</span>
 *       </label>
 *
 *     Group wrappers — every related set of fields sits in a <div>
 *     with a descriptive class. Each wrapper spans the form grid
 *     and uses a smaller inner gap so the fields inside read as
 *     one cluster, separated from the next cluster by the larger
 *     --form-gap. Never use `.row` (reserved by container.css).
 *
 *       <div class="name">                 name + email, side-by-side ≥sm
 *       <div class="catalogs">             horizontal checkbox row
 *       <div class="address">              3-col desktop, 1+2 mobile
 *       <div class="privacy">              stacked consent checkboxes
 *
 *     Submit — the element carries the semantics:
 *
 *       <button type="submit" class="btn …">Beställ</button>
 *
 *   </form>
 *
 * Modifiers (on any direct child):
 *   .full          span all grid columns
 *
 * Variants (class on <form>):
 *   form.pill      no frame; single-line input pill + icon submit,
 *                  optional stacked consent + status/alert rows.
 *
 * ──────────────────────────────────────────────────────────────────
 * Accessibility contract (WCAG 2.2 AA)
 * ──────────────────────────────────────────────────────────────────
 *   • Every input sits inside its <label> (implicit association).
 *     Use aria-label only when a visible label is impossible.
 *   • Required fields: set the `required` attribute. [WCAG 3.3.2]
 *   • Personal-data inputs carry autocomplete tokens (name, email,
 *     street-address, address-level2, postal-code). [WCAG 1.3.5]
 *   • Error text uses role="alert" + aria-invalid="true" on the
 *     input. Status (success) uses role="status". [WCAG 3.3.1]
 *   • Focus: :focus-visible only; ring contrast ≥3:1 against its
 *     background. [WCAG 1.4.11, 2.4.7]
 *   • Never rely on placeholder as label. If the design hides the
 *     label, keep <span class="sr-only"> so AT still reads it.
 *     [WCAG 3.3.2]
 *   • Checkbox row (input + inline label) forms one clickable
 *     target ≥24×24 CSS px. [WCAG 2.5.8]
 */

/* ──────────────────────────────────────────────────────
 * Tokens — tweak these to reshape every form on the site.
 * ────────────────────────────────────────────────────── */

:root {
  /* Layout — outer gap sits between group wrappers; inner gap sits
     between fields inside a wrapper. Inner < outer visually clusters
     related fields (gestalt proximity). */
  --form-gap: var(--s-32);
  --form-inner-gap: var(--s-16);
  --form-label-gap: var(--s-4);

  /* Standalone form frame (.section > form variant) */
  --form-frame-padding: var(--s-32);
  --form-frame-radius: var(--s-12);
  --form-frame-border-color: color-mix(
    in srgb,
    var(--wp--preset--color--warm-brown) 18%,
    transparent
  );

  /* Controls */
  --form-control-padding: 0.7rem 0.9rem;
  --form-control-radius: var(--border-radius);
  --form-control-border-color: color-mix(
    in srgb,
    var(--wp--preset--color--warm-brown) 35%,
    transparent
  );
  --form-control-border-color-focus: var(--wp--preset--color--warm-brown);

  /* Typography */
  --form-label-font-size: var(--font-size-sm);
  --form-helper-font-size: var(--font-size-sm);
  --form-helper-color: color-mix(in srgb, currentColor 70%, transparent);

  /* State (component-scoped alias of tokens.css --color-danger) */
  --form-error-color: var(--color-danger);

  /* Check row (checkbox + text) */
  --form-check-gap: var(--s-16);
}

/* Error state: override the shared --focus-ring-color so the keyboard
   focus ring shifts to the danger colour without needing a separate
   error-focus rule. Border-color still flips to red via the rule below. */
form [aria-invalid="true"] {
  --focus-ring-color: var(--form-error-color);
}

/* ──────────────────────────────────────────────────────
 * Container
 * ────────────────────────────────────────────────────── */

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

@media (min-width: 40rem) {
  form {
    grid-template-columns: 1fr 1fr;
  }
}

/* Standalone form sat directly inside a .section — gets a framed look so
   it reads as its own card. Scoped to non-pill forms so the newsletter
   and auth-dialog pill variants stay borderless. */
.section > form:not(.pill) {
  width: 100%;
  max-width: var(--mw-text-lg);
  margin-inline: auto;
  /* Mobile (< md / 40rem): borderless, light inline+block padding. */
  padding: 1.25em;
}

/* md+ (>= 40rem): framed form. */
@media (min-width: 40rem) {
  .section > form:not(.pill) {
    padding: var(--form-frame-padding);
    border: 1px solid var(--form-frame-border-color);
    border-radius: var(--form-frame-radius);
  }
}

/* Intro text slot */
form > header {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--s-8);
}

form > header > * {
  margin: 0;
}

/* Full-row modifier (works on any direct child) */
form > .full {
  grid-column: 1 / -1;
}

/* ──────────────────────────────────────────────────────
 * Fields (stacked label + input)
 * ────────────────────────────────────────────────────── */

form label {
  display: grid;
  gap: var(--form-label-gap);
}

form label:has(> input:is([type="text"], [type="email"], [type="tel"], [type="password"])) > span {
  margin-left: 4px;
}

/* ──────────────────────────────────────────────────────
 * Controls
 * ────────────────────────────────────────────────────── */

form input:is([type="text"], [type="email"], [type="tel"], [type="password"]) {
  inline-size: 100%;
  padding: var(--form-control-padding);
  background: var(--wp--preset--color--white);
  border: 1px solid var(--form-control-border-color);
  border-radius: var(--form-control-radius);
  /* Inherit the site's typography — browsers default form controls to
     a system font (Arial / Helvetica) which doesn't match body text. */
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}

form input:hover:not(:disabled) {
  border-color: var(--form-control-border-color-focus);
}

/* Chrome paints a hardcoded light-blue fill on autofilled inputs that
   can't be overridden with `background`. Cover it with a 1000px inset
   shadow in the normal fill color, repair the text color, and stall
   the re-paint transition so it never animates back in. */
form input:is([type="text"], [type="email"], [type="tel"], [type="password"]):is(
    :-webkit-autofill,
    :-webkit-autofill:hover,
    :-webkit-autofill:focus,
    :-webkit-autofill:active
  ) {
  -webkit-box-shadow: 0 0 0 1000px var(--wp--preset--color--white) inset;
  -webkit-text-fill-color: inherit;
  caret-color: inherit;
  transition: background-color 99999s ease-in-out 0s;
}

/* Pill inputs use the pill's filled color so Chrome's autofill rect
   blends with the surrounding label (which also turns white via the
   :has(:-webkit-autofill) rule below in the pill section). */
form.pill input:is([type="email"], [type="password"]):is(
    :-webkit-autofill,
    :-webkit-autofill:hover,
    :-webkit-autofill:focus,
    :-webkit-autofill:active
  ) {
  -webkit-box-shadow: 0 0 0 1000px var(--form-pill-bg-filled) inset;
  -webkit-text-fill-color: inherit;
  caret-color: inherit;
  transition: background-color 99999s ease-in-out 0s;
}

form input:focus-visible {
  border-color: var(--form-control-border-color-focus);
  outline: 2px solid var(--focus-ring-color, var(--wp--preset--color--warm-brown));
  outline-offset: 2px;
}

form input:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Selector specificity bumped to form input[aria-invalid] so it wins
   over the base `form input:is([type=...])` shorthand border. Without
   this the red border-color only applied on focus. */
form input[aria-invalid="true"] {
  border-color: var(--form-error-color);
}

form input[aria-invalid="true"]:focus-visible {
  border-color: var(--form-error-color);
}

/* ──────────────────────────────────────────────────────
 * Checkbox control — custom-styled square that mirrors the
 * text input look (warm-brown border 35%, white fill,
 * border-radius). Native rendering is disabled via
 * appearance: none. Check mark is an inline-SVG data URI of
 * check.svg with warm-brown stroke baked in.
 * ────────────────────────────────────────────────────── */

form input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 1.5rem;
  height: 1.5rem;
  margin: 0;
  padding: 0;
  flex-shrink: 0;
  background: var(--wp--preset--color--white);
  border: 1px solid var(--form-control-border-color);
  border-radius: var(--form-control-radius);
  cursor: pointer;
  transition:
    border-color 0.2s ease,
    background-color 0.2s ease;
}

form input[type="checkbox"]:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpolyline points='40 144 96 200 224 72' fill='none' stroke='%235B4E47' stroke-linecap='round' stroke-linejoin='round' stroke-width='24'/%3E%3C/svg%3E");
  background-position: center;
  background-repeat: no-repeat;
  background-size: 1rem 1rem;
}

/* ──────────────────────────────────────────────────────
 * Check row — 2-column grid for any label that contains
 * a checkbox. Col 1 holds the checkbox, col 2 holds the
 * text (and the validation message, in row 2). Keeping
 * the columns fixed means an error message appears under
 * the text without nudging the checkbox sideways.
 * ────────────────────────────────────────────────────── */

form label:has(> input[type="checkbox"]) {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 0 var(--form-check-gap);
}

form label:has(> input[type="checkbox"]) > input {
  margin-block-start: -0.1em;
}

/* ──────────────────────────────────────────────────────
 * Error text
 * ────────────────────────────────────────────────────── */

form [role="alert"] {
  margin: 0;
  font-size: var(--form-helper-font-size);
  color: var(--form-error-color);
}

/* Text-input labels: the error message is positioned absolutely below
   the input so the label's height never changes. The form's natural
   gap above the next field is the slot the message sits in — adding
   or removing it doesn't shift any surrounding layout. */
form label:has(> input:is([type="text"], [type="email"], [type="tel"], [type="password"])) {
  position: relative;
}

form
  label:has(> input:is([type="text"], [type="email"], [type="tel"], [type="password"]))
  > [data-error] {
  position: absolute;
  top: calc(100% + var(--form-label-gap));
  left: 0;
  right: 0;
  margin: 0;
}

/* Checkbox labels: the error message sits in column 2 below the text
   (grid-column: 2 forces it into the text column; auto-placement then
   puts it on the next row since row 1 col 2 is occupied). */
form label:has(> input[type="checkbox"]) > [data-error] {
  grid-column: 2;
}

/* ──────────────────────────────────────────────────────
 * Group wrappers — any <div> child of <form> is treated as
 * a semantic cluster of related fields. Each spans the
 * form grid and uses the smaller inner gap so the cluster
 * reads as one unit.
 * ────────────────────────────────────────────────────── */

form > div {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--form-inner-gap);
}

/* .name — name + email, 1-col mobile, 2-col ≥sm. */
@media (min-width: 40rem) {
  form > .name {
    grid-template-columns: 1fr 1fr;
  }
}

/* .catalogs — horizontal checkbox row, wraps on narrow screens. */
form > .catalogs {
  display: flex;
  flex-wrap: wrap;
}

form > .catalogs > [data-error] {
  flex: 0 0 100%;
}

/* .contact — tel + e-post, 1-col mobile, 2-col ≥sm (mirrors .name). */
@media (min-width: 40rem) {
  form > .contact {
    grid-template-columns: 1fr 1fr;
  }
}

/* .address — 2-col mobile (street spans both, Ort + Postnr share a
   row below), 3-col desktop. */
form > .address {
  grid-template-columns: 1fr 1fr;
}

form > .address > label:first-child {
  grid-column: 1 / -1;
}

@media (min-width: 40rem) {
  form > .address {
    grid-template-columns: 2fr 1.2fr 1fr;
  }

  form > .address > label:first-child {
    grid-column: auto;
  }
}

form > .privacy {
  padding-block: var(--form-inner-gap);
}

/* .privacy — stacked checkboxes; inherits the single-column grid
   and inner gap from form > div, no extra rules needed. */

/* ──────────────────────────────────────────────────────
 * Submit — centered, spans the grid, sits one extra gap
 * below the previous row (visual separation = gap × 2).
 * ────────────────────────────────────────────────────── */

form > button {
  grid-column: 1 / -1;
  justify-self: center;
  margin-block-start: var(--form-inner-gap);
}

form[hidden] {
  display: none;
}

/* ──────────────────────────────────────────────────────
 * form.pill — composite input + icon button on one row.
 * ──────────────────────────────────────────────────────
 *
 *   <form class="pill" novalidate>
 *     <label>
 *       <span class="sr-only">E-post</span>
 *       <input type="email" name="email" autocomplete="email"
 *              placeholder="E-post" required aria-describedby="nl-err">
 *       <button type="submit"
 *               class="btn icon sm"
 *               aria-label="Prenumerera">
 *         <svg>…paper-plane-right…</svg>
 *       </button>
 *     </label>
 *     <label>
 *       <input type="checkbox" name="privacy" required>
 *       <span>Jag har läst <a href="…">Integritetspolicyn</a>…</span>
 *     </label>
 *     <p id="nl-err" class="alert" role="alert" hidden>…</p>
 *     <p class="status" role="status" hidden>Tack! Du är anmäld…</p>
 *   </form>
 *
 * Design:
 *   - No frame (padding/border reset).
 *   - The email/password label *is* the visual pill: input is
 *     borderless/transparent, circular icon button sits flush-right
 *     inside it.
 *   - Focus ring lifts to the outer label via :has(input:focus-visible).
 *   - Consent row is indented by the pill's left-padding so its
 *     checkbox aligns with the pill's visible text start.
 *   - Alert/status messages have a tinted background + rounded corners.
 */

:root {
  --form-pill-bg: transparent;
  --form-pill-bg-filled: var(--wp--preset--color--white);
  --form-pill-border: var(--form-control-border-color);
  --form-pill-pad-y: 0.35rem;
  --form-pill-pad-x: 1.25rem;
  --form-pill-input-color: inherit;
  --form-pill-placeholder-color: color-mix(in srgb, currentColor 45%, transparent);
  --form-pill-message-radius: var(--s-8);
  --form-pill-message-padding: var(--s-12) var(--s-16);
}

form.pill {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--form-inner-gap);
  padding: 0;
  border: 0;
  background: transparent;
}

/* Composite control: the input label is the pill frame. */
form.pill > label:has(> input:is([type="email"], [type="password"])) {
  display: flex;
  align-items: center;
  gap: var(--s-8);
  padding: var(--form-pill-pad-y) var(--form-pill-pad-y) var(--form-pill-pad-y)
    var(--form-pill-pad-x);
  background: var(--form-pill-bg);
  border: 1px solid var(--form-pill-border);
  border-radius: var(--btn-border-radius-pill);
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}

form.pill > label:hover:has(> input:not(:disabled)) {
  border-color: var(--form-control-border-color-focus);
}

form.pill > label:has(> input:is([type="email"], [type="password"]):focus-visible) {
  border-color: var(--form-control-border-color-focus);
  outline: 2px solid var(--focus-ring-color, var(--wp--preset--color--warm-brown));
  outline-offset: 2px;
}

/* Has-value: as soon as the user (or autofill) puts content in the
   field, the pill switches from its surface tint to a solid white
   fill — visually committing the field to an "active" data state.
   :not(:placeholder-shown) catches typed and pasted values; the
   :-webkit-autofill branch catches Chrome's browser-filled state. */
form.pill > label:has(> input:is([type="email"], [type="password"]):not(:placeholder-shown)),
form.pill > label:has(> input:is([type="email"], [type="password"]):-webkit-autofill) {
  background: var(--form-pill-bg-filled);
}

/* Disabled mirror for the pill label so the whole component fades
   when the input is disabled, matching the bare input rule. */
form.pill > label:has(> input:disabled) {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Pill: when the inner input is invalid, lift the danger token to the
   label so the outer outline (via :has(focus-visible)) uses it too. */
form.pill > label:has(> input:is([type="email"], [type="password"])[aria-invalid="true"]) {
  --focus-ring-color: var(--form-error-color);
  border-color: var(--form-error-color);
}

/* Input is borderless so the outer label carries the visible frame.
   letter-spacing: inherit so browsers don't apply their default
   input tracking — the input visually matches surrounding body text. */
form.pill input:is([type="email"], [type="password"]) {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  color: var(--form-pill-input-color);
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
}

form.pill input:is([type="email"], [type="password"]):focus-visible {
  outline: none;
  border: 0;
  box-shadow: none;
}

form.pill input:is([type="email"], [type="password"])::placeholder {
  color: var(--form-pill-placeholder-color);
}

/* Consent row aligns with the pill's visible text start (compensates
   for the pill's border-radius curve on the left edge). */
form.pill > label:has(> input[type="checkbox"]) {
  padding-inline: calc(var(--form-pill-pad-x) / 2);
}

/* Live regions — alert (error) and status (success). Both use a
   tinted background matching their semantics. */
form.pill .alert,
form.pill .status {
  grid-column: 1 / -1;
  margin: 0;
  padding: var(--form-pill-message-padding);
  border-radius: var(--form-pill-message-radius);
  font-size: var(--form-helper-font-size);
  transition: opacity 0.4s ease;
}

form.pill .alert {
  background: color-mix(in srgb, var(--form-error-color) 8%, transparent);
  color: var(--form-error-color);
}

form.pill .status:not([hidden]) {
  display: block;
  background: color-mix(in srgb, var(--wp--preset--color--forest-green) 10%, transparent);
  color: var(--wp--preset--color--forest-green);
}

form.pill .is-fading {
  opacity: 0;
}

form.pill[aria-busy="true"] {
  opacity: 0.7;
  pointer-events: none;
}
