/* static/css/form-controls.css
 * ═══════════════════════════════════════════════════════════════════════════
 * THE ONE FORM-CONTROL STYLESHEET FOR JINJA PAGES (fix/form-controls-20261025)
 *
 * Owner, 2026-10-25: "clicking save my email I'm clicking a rectangle not a
 * square". Checkbox, radio and switch were styled in 12+ places (register.css,
 * theme.css, signing-flow.css, onboarding.css, admin.css, inline <style> in
 * templates) and a generic `.input-group input` rule (padding 12px 16px,
 * border, width) out-ranked every one of them on /register, so a 24px box
 * rendered 36 x 28. This file is the single place; the React kit has the
 * matching Checkbox / Radio / Switch (frontend/src/shared/kit), and both read
 * the SAME tokens (siteTokens.js SHAPE block -> cfin-theme.tokens.css):
 *     --cfin-control-size   22px  visual box (Apple HIG ~22px)
 *     --cfin-control-hit    44px  hit target, supplied by the row/label
 *     --radius-inner        4px   checkbox corner (the existing radius ladder)
 *     --cfin-field-height   44px  text field / select height
 *     --cfin-toggle-w/h     51 x 31  iOS switch
 *
 * OPT-IN BY CLASS: .cfin-check / .cfin-radio / .cfin-switch on the NATIVE
 * <input>. The input stays native (form value, validation, label click,
 * keyboard, screen reader); only its paint changes (appearance:none + a
 * pseudo-element). The check glyph is the registry's Phosphor "check" /
 * "minus" (core/icon_registry.py icon_svg) as a mask, so it follows the theme
 * colour. A guard (tests/test_form_controls_are_one_system.py) fails the build
 * if the path drifts from the registry or a new checkbox style appears outside
 * this file.
 *
 * WHY THE GEOMETRY CARRIES !important: it is the only place in the codebase
 * where it does, on purpose. A control that is not square is a defect, and the
 * rules that made it so (`.input-group input` at (0,1,1) and at (0,5,2),
 * the (pointer: coarse) min-inline-size floor in tapTargets.css) are
 * unbounded in number. Shape must not depend on who wins a specificity race.
 * ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* Phosphor bold "check" and "minus", viewBox 256, from the icon registry. */
  --cfin-glyph-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z'/%3E%3C/svg%3E");
  --cfin-glyph-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128Z'/%3E%3C/svg%3E");
}

/* ── shared geometry: an exact square / circle, never squeezed by a flex row ── */
input.cfin-check,
input.cfin-radio {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box !important;
  inline-size: var(--cfin-control-size, 22px) !important;
  block-size: var(--cfin-control-size, 22px) !important;
  min-inline-size: var(--cfin-control-size, 22px) !important;
  min-block-size: var(--cfin-control-size, 22px) !important;
  max-inline-size: var(--cfin-control-size, 22px) !important;
  max-block-size: var(--cfin-control-size, 22px) !important;
  aspect-ratio: 1 / 1 !important;
  flex: 0 0 auto !important;
  padding: 0 !important;
  margin: 0;
  position: relative;
  display: inline-block;
  vertical-align: middle;
  border: 2px solid var(--color-border-emphasized);
  background-color: transparent;
  color: var(--color-on-accent);
  cursor: pointer;
  transition: background-color var(--cfin-motion-fast, 120ms) ease, border-color var(--cfin-motion-fast, 120ms) ease;
}
input.cfin-check { border-radius: var(--radius-inner); }
input.cfin-radio { border-radius: 50%; }

/* glyph layer: check / minus for the checkbox, a dot for the radio */
input.cfin-check::after,
input.cfin-radio::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  background-color: currentColor;
  opacity: 0;
  transition: opacity var(--cfin-motion-fast, 120ms) ease;
}
input.cfin-check::after {
  inline-size: 14px;
  block-size: 14px;
  -webkit-mask: var(--cfin-glyph-check) center / contain no-repeat;
  mask: var(--cfin-glyph-check) center / contain no-repeat;
}
input.cfin-radio::after {
  inline-size: 8px;
  block-size: 8px;
  border-radius: 50%;
}

input.cfin-check:hover:not(:disabled),
input.cfin-radio:hover:not(:disabled) {
  border-color: var(--color-accent);
}

/* checked */
input.cfin-check:checked,
input.cfin-radio:checked {
  background-color: var(--color-accent);
  border-color: var(--color-accent);
}
input.cfin-check:checked::after,
input.cfin-radio:checked::after { opacity: 1; }

/* indeterminate (checkbox only; set by script: input.indeterminate = true) */
input.cfin-check:indeterminate {
  background-color: var(--color-accent);
  border-color: var(--color-accent);
}
input.cfin-check:indeterminate::after {
  opacity: 1;
  -webkit-mask-image: var(--cfin-glyph-minus);
  mask-image: var(--cfin-glyph-minus);
}

/* focus-visible: the one ring. A 2px SOLID accent outline, offset 2px: the
   accent is >=3:1 against both the page and the field in both themes, which
   the 30% halo (--cfin-focus-halo) is not. The halo stays as a soft outer glow. */
input.cfin-check:focus-visible,
input.cfin-radio:focus-visible,
input.cfin-switch:focus-visible,
.cfin-field:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  box-shadow: var(--cfin-focus-halo);
}

/* error */
input.cfin-check[aria-invalid="true"],
input.cfin-radio[aria-invalid="true"],
.cfin-check-row.is-invalid input.cfin-check,
.cfin-check-row.is-invalid input.cfin-radio {
  border-color: var(--cfin-sev-error);
}

/* disabled */
input.cfin-check:disabled,
input.cfin-radio:disabled,
input.cfin-switch:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── the row: label + control, >=44px hit target, label always clickable ── */
.cfin-check-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-block-size: var(--cfin-control-hit, 44px);
}
.cfin-check-row > input.cfin-check,
.cfin-check-row > input.cfin-radio,
.cfin-check-row > input.cfin-switch { margin-block-start: calc((var(--cfin-control-hit, 44px) - var(--cfin-control-size, 22px)) / 2); }
.cfin-check-row > input.cfin-switch { margin-block-start: calc((var(--cfin-control-hit, 44px) - var(--cfin-toggle-h, 31px)) / 2); }
.cfin-check-row > label {
  flex: 1 1 auto;
  min-inline-size: 0;
  padding-block: 11px;
  cursor: pointer;
}
/* a legacy row (.checkbox-group, .rv-check, ...) that holds a cfin-check keeps
   its own layout but never drops below the hit target */
.checkbox-group:has(> input.cfin-check),
.checkbox-group:has(> input.cfin-radio) { min-block-size: var(--cfin-control-hit, 44px); }
.checkbox-group > input.cfin-check,
.checkbox-group > input.cfin-radio { margin-block-start: calc((var(--cfin-control-hit, 44px) - var(--cfin-control-size, 22px)) / 2); }
.checkbox-group:has(> input.cfin-check) > label,
.checkbox-group:has(> input.cfin-radio) > label { padding-block: 11px; }

/* ── switch: iOS, 51 x 31, on = accent ── */
input.cfin-switch {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box !important;
  inline-size: var(--cfin-toggle-w, 51px) !important;
  block-size: var(--cfin-toggle-h, 31px) !important;
  min-inline-size: var(--cfin-toggle-w, 51px) !important;
  min-block-size: var(--cfin-toggle-h, 31px) !important;
  flex: 0 0 auto !important;
  padding: 0 !important;
  margin: 0;
  position: relative;
  display: inline-block;
  vertical-align: middle;
  border: 0;
  border-radius: var(--cfin-radius-pill, 9999px);
  background-color: var(--color-border-emphasized);
  cursor: pointer;
  transition: background-color var(--cfin-motion-toggle, 200ms) ease;
}
input.cfin-switch::after {
  content: "";
  position: absolute;
  inset-block-start: 2px;
  inset-inline-start: 2px;
  inline-size: calc(var(--cfin-toggle-h, 31px) - 4px);
  block-size: calc(var(--cfin-toggle-h, 31px) - 4px);
  border-radius: 50%;
  background-color: var(--cfin-on-accent-fill);
  box-shadow: var(--cfin-elev-1);
  transition: transform var(--cfin-motion-toggle, 200ms) ease;
}
input.cfin-switch:checked { background-color: var(--color-accent); }
input.cfin-switch:checked::after { transform: translateX(calc(var(--cfin-toggle-w, 51px) - var(--cfin-toggle-h, 31px))); }
[dir="rtl"] input.cfin-switch:checked::after { transform: translateX(calc(var(--cfin-toggle-h, 31px) - var(--cfin-toggle-w, 51px))); }

@media (prefers-reduced-motion: reduce) {
  input.cfin-check, input.cfin-radio, input.cfin-switch,
  input.cfin-check::after, input.cfin-radio::after, input.cfin-switch::after { transition: none; }
}
@media (forced-colors: active) {
  input.cfin-check:checked, input.cfin-radio:checked, input.cfin-switch:checked { background-color: Highlight; border-color: Highlight; }
  input.cfin-check::after, input.cfin-radio::after { background-color: HighlightText; }
}

/* ── fields: ONE height, ONE radius, ONE focus ring, ONE error style ──
   Opt-in `.cfin-field` on a text-like input / select / textarea. */
.cfin-field {
  box-sizing: border-box;
  inline-size: 100%;
  min-block-size: var(--cfin-field-height, 44px);
  padding: 10px 14px;
  border: 1px solid var(--color-border-emphasized);
  border-radius: var(--cfin-radius-field, 12px);
  background-color: var(--cfin-fill-tertiary);
  color: var(--color-text-primary);
  font: inherit;
  transition: border-color var(--cfin-motion-fast, 120ms) ease, box-shadow var(--cfin-motion-fast, 120ms) ease;
}
.cfin-field[aria-invalid="true"] { border-color: var(--cfin-sev-error); }
.cfin-field:disabled { opacity: 0.55; cursor: not-allowed; }
/* one height for input AND select; the textarea only has a min */
input.cfin-field,
select.cfin-field { block-size: var(--cfin-field-height, 44px); }
select.cfin-field { padding-block: 0; }
textarea.cfin-field { min-block-size: 96px; resize: vertical; }

.cfin-field-error {
  margin: 6px 0 0;
  font-size: var(--cfin-text-footnote-size, 13px);
  line-height: var(--cfin-text-footnote-leading, 18px);
  color: var(--cfin-sev-error);
}
