/*
 * Radio
 *
 * Measured from Figma `Radio` (18 variants: 3 colours x 3 sizes x 2 states).
 *
 * Same construction as Checkbox — hidden native input, styled sibling, outline
 * for the focus ring — with three differences Figma is explicit about:
 *
 *   - the indicator is a circle (radius/full), not a rounded square
 *   - the selected mark is a filled dot, not a glyph, so it scales with the box
 *   - there is no disabled variant in the design; disabled is handled here
 *     anyway, because a form can always disable a field and the component must
 *     not fall back to looking enabled
 *
 * THE DOT IS DERIVED
 *
 * The dot is 6 / 8 / 10 against indicators of 16 / 20 / 24. 10 is on no ladder —
 * spacing jumps 8 to 12 — but it follows from the box:
 *
 *     dot = box / 2 - 2       16->6   20->8   24->10
 *
 * So nothing here reaches for the raw `scale/*` ramp, and the dot stays in
 * proportion if a size is ever retuned.
 */
.ion-radio {
  --ion-radio-size: var(--spacing-20);
  --ion-radio-gap: var(--spacing-8);
  --ion-radio-font-size: var(--type-body);
  --ion-radio-line-height: var(--type-body-line-height);
  --ion-radio-fill: var(--surface-primary);
  --ion-radio-border: var(--border-primary-strong);

  /* Bevelled when selected — flush, size-indexed. Same rungs as Checkbox. */
  --ion-radio-raised: var(--ion-shadow-raised-flush-sm);

  /* Derived — see the header. */
  --ion-radio-dot: calc(var(--ion-radio-size) / 2 - var(--spacing-2));

  display: inline-flex;
  align-items: flex-start;
  gap: var(--ion-radio-gap);
  font-family: var(--font-family-sans);
  font-size: var(--ion-radio-font-size);
  line-height: var(--ion-radio-line-height);
  cursor: pointer;
}

.ion-radio--sm {
  --ion-radio-size: var(--spacing-16);
  --ion-radio-font-size: var(--type-body-sm);
  --ion-radio-line-height: var(--type-body-sm-line-height);
  --ion-radio-raised: var(--ion-shadow-raised-flush-xs);
}

.ion-radio--lg {
  --ion-radio-size: var(--spacing-24);
  --ion-radio-gap: var(--spacing-12);
  --ion-radio-raised: var(--ion-shadow-raised-flush-lg);
}

.ion-radio__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  border: none;
  opacity: 0;
  pointer-events: none;
}

.ion-radio__indicator {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--ion-radio-size);
  height: var(--ion-radio-size);
  background-color: var(--surface-default);
  border-style: solid;

  /*
   * The EMPTY circle carries the heavier border: 2px `border/stronger`, where
   * the selected one drops to 1px of its intent colour. Same reasoning as
   * Checkbox — an unselected control is nothing but its outline, so it has to
   * hold the row on its own; once filled, the fill does that work.
   *
   * Figma strokes this INSIDE, and the global `box-sizing: border-box` in
   * index.css is what reproduces it: the circle stays 16/20/24 and the border
   * eats inward instead of growing the control.
   */
  border-width: var(--border-width-thick);
  border-color: var(--border-stronger);
  border-radius: var(--radius-full);

  /* Centred on the label's first line — see checkbox.css. */
  margin-top: calc((var(--ion-radio-line-height) - var(--ion-radio-size)) / 2);
  transition:
    background-color var(--ion-duration-base) var(--ion-ease-out),
    border-color var(--ion-duration-base) var(--ion-ease-out);
}

.ion-radio__dot {
  width: var(--ion-radio-dot);
  height: var(--ion-radio-dot);
  background-color: var(--icon-on-color);
  border-radius: var(--radius-full);

  /* Hidden rather than unmounted so selecting never reflows the row. */
  opacity: 0;
}

.ion-radio__label {
  color: var(--text-secondary);
  user-select: none;
}

.ion-radio__input:checked + .ion-radio__indicator {
  /* Back to 1px — see the indicator's border comment. */
  border-width: var(--border-width-default);
  background-color: var(--ion-radio-fill);
  border-color: var(--ion-radio-border);

  /* Only the selected dot is bevelled; an empty circle stays flat. */
  box-shadow: var(--ion-radio-raised);
}

.ion-radio__input:checked + .ion-radio__indicator .ion-radio__dot {
  opacity: 1;
}

.ion-radio--neutral {
  --ion-radio-fill: var(--surface-inverse);
  --ion-radio-border: var(--border-inverse);
}

.ion-radio--danger {
  --ion-radio-fill: var(--surface-error);
  --ion-radio-border: var(--border-error-strong);
}

.ion-radio__input:focus-visible + .ion-radio__indicator,
.ion-radio[data-focused='true'] .ion-radio__indicator {
  outline: var(--border-width-thick) solid var(--border-focus);
  outline-offset: 0;
}

/* Driven off `:disabled` so it out-specifies `:checked` — see checkbox.css. */
.ion-radio__input:disabled + .ion-radio__indicator,
.ion-radio__input:checked:disabled + .ion-radio__indicator {
  background-color: var(--surface-disabled);
  border-color: var(--border-disabled);

  /*
   * NOT MEASURED — mirrored from Checkbox. Radio's variant set has only
   * Unselected and Selected, so Figma says nothing about a disabled border
   * width. Checkbox does have the variants and keeps 2px in all three disabled
   * states, so Radio follows it rather than inheriting 1px from `:checked` by
   * accident of specificity. Restated here for the same reason Checkbox has to:
   * `:checked` sets 1px, and only the compound `:checked:disabled` beats it.
   */
  border-width: var(--border-width-thick);

  /* Flat whether or not it is selected. */
  box-shadow: none;
}

.ion-radio__input:disabled + .ion-radio__indicator .ion-radio__dot {
  background-color: var(--icon-disabled);
}

.ion-radio--disabled,
.ion-radio[data-disabled='true'] {
  cursor: not-allowed;
}

.ion-radio--disabled .ion-radio__label,
.ion-radio[data-disabled='true'] .ion-radio__label {
  color: var(--text-disabled);
}
