/* admin-fields.css — the floating-label field, backoffice only.
 *
 * Overrides pkg/webui's forms.css, which is SHARED with auth-service and must not
 * change: the login pages keep the label-above field and keep working.
 *
 * ## Why the label does not move in the markup
 *
 * The obvious way to build a floating label is `input` then `label`, so that
 * `input:focus + label` can reach it. That would have meant re-nesting 74 fields
 * across 17 fragments and inverting the `.input-group` icon idiom with them.
 *
 * It is unnecessary. The label is ABSOLUTELY POSITIONED over the field, and DOM
 * order is irrelevant to an absolutely positioned box. So the label stays exactly
 * where it is, and the float state is read off the field with :has(). The only
 * template change is `placeholder=" "`.
 *
 * That attribute is not decoration: an input with NO placeholder attribute never
 * matches :placeholder-shown, so without it an empty field would report itself as
 * filled and the label would float over nothing.
 *
 * ## TWO LABEL IDIOMS, and why both survive
 *
 * 60 fields are `<label for=x>` + a sibling control. 12 are a label WRAPPING its
 * control, with no id — not sloppiness: those render inside `{{range}}` loops and
 * repeated `{{define}}` blocks, where static ids would emit DUPLICATE IDS, a
 * worse bug than the one `for=` fixes. Containment associates them instead.
 *
 * A wrapping label cannot be positioned OVER the control it contains, so the two
 * are told apart with :has() and styled to look the same: the wrapping label
 * renders permanently where a sibling label ends up once its field holds
 * anything. Selects and date inputs float permanently too, for their own
 * reasons. */

.field {
  position: relative;
  display: block;
  margin-bottom: 1.125rem;
}

/* ---- WHICH ELEMENT IS THE BOX -------------------------------------------
 *
 * There are two answers in this product and they must not be confused, because
 * confusing them is what put a floated label on top of the value for 50 fields.
 *
 *   `.field > input`        — the CONTROL is the box. It carries the border,
 *                             the radius, the height and the padding.
 *   `.field > .input-group` — the GROUP is the box. forms.css draws the border
 *                             and the ring on the wrapper, lays out `icon +
 *                             control` as a flex row, and deliberately strips
 *                             the inner control at (0,3,1) so nothing can give
 *                             it a box of its own. Its comment asks, in
 *                             capitals, not to weaken that.
 *
 * This sheet used to assume the first answer everywhere. Against a group the
 * padding rule lost at (0,2,1) while min-height tied at (0,3,1) and won on
 * order — so the control was 48px tall with ZERO top padding, its text centred,
 * and the label pinned at 14px landed on it. Any rule below that sets a box
 * property has to name its idiom. */

.field :is(input, select, textarea):not([type="checkbox"]):not([type="radio"]):not(.input-group *) {
  min-height: 3rem;
  border-radius: var(--sn-shape-corner-medium);
  transition: border-color var(--sn-motion-duration-short) var(--sn-motion-ease),
              box-shadow var(--sn-motion-duration-short) var(--sn-motion-ease);
}
/* The `:not()` chain is what makes this reach: app.css styles bare inputs at
 * (0,2,1), so (0,1,1) here lost and a field kept symmetric padding. */
.field > :is(input, select, textarea):not([type="checkbox"]):not([type="radio"]) {
  padding: 1.125rem 0.875rem 0.3125rem;
}
.field > textarea { padding: 1.5rem 0.875rem 0.625rem; }

/* A 3px tinted ring rather than Sneat's 1px double-border. It reads as focus at
 * arm's length on a shop counter screen, which the hairline does not. */
.field :is(input, select, textarea):not(.input-group *):focus {
  outline: none;
  border-color: var(--sn-primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sn-primary) 18%, transparent);
}

/* Idiom 2 — the group is the box.
 *
 * It keeps forms.css's flex row: `display: block` here was the second half of
 * the bug — it stacked the icon above the control, which is why the icon then
 * needed absolute positioning and why value and icon shared a left edge.
 * `position: static` DOES stay: the label is positioned against `.field`, and a
 * positioned group would become its containing block.
 *
 * The padding stays SYMMETRIC, and that is load-bearing: the group is a flex row
 * with `align-items: center`, which centres children in the CONTENT box.
 * Reserving the floated label's 18px HERE dragged the icon down with the value,
 * to 30.5px in a box whose centre is 24px. Only the control needs that room. */
.field > .input-group {
  position: static;
  min-height: 3rem;
  padding: 0 0.8125rem;
}
.field > .input-group:focus-within {
  border-color: var(--sn-primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sn-primary) 18%, transparent);
}

/* The inner control stays bare, as forms.css intends. Repeating its `:not()`
 * chain is what makes this reach: (0,4,1) against the (0,3,1) that the earlier
 * min-height rule was tying with and winning on load order alone.
 *
 * It carries the label's room as its own padding: 18 + 23 + 5 = 46px, centred in
 * the group's 46px content box, so the value still starts at 19px — where the
 * group's padding used to put it. */
.field > .input-group > :is(input, select, textarea):not([type="checkbox"]):not([type="radio"]) {
  min-height: 0;
  padding: 1.125rem 0 0.3125rem;
  border: 0;
  border-radius: 0;
  background: none;
}
.field > .input-group > textarea { padding: 1.5rem 0 0.625rem; }

/* The icon is a flex item again — no absolute positioning, and no
 * padding-inline-start on the control to clear something that is back in flow. */
.field > .input-group > .icon { color: var(--sn-on-surface-variant); }

/* `button, a` are in this test alongside the controls, and not decoration.
 * A label that contains a help button cannot be a floating one: the float rule
 * sets `pointer-events: none`, which made that button UNCLICKABLE, and the
 * button's height blew the label box down onto the value. Such a label falls
 * through to admin-field-wrapping.css and renders as a block above the field,
 * which is what it always should have been. Two exist today, both on
 * fragments/packages.html. */
.field > label:not(:has(:is(input, select, textarea, button, a))) {
  position: absolute;
  /* `top` positions the MARGIN box. app.css:72 gives every bare <label> a 12px
   * top margin, so this label laid out 12px below where `top` says and landed
   * on the value — the last 12px of the collision. Backoffice-only, which is
   * why signup, trial and auth-service never showed it: they link no app.css. */
  margin: 0;
  inset-inline-start: 0.875rem;
  top: 1.5rem;
  translate: 0 -50%;
  max-width: calc(100% - 1.75rem);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--sn-on-surface-variant);
  font-size: var(--sn-type-body-1-size);
  font-weight: 400;
  pointer-events: none;
  transition: top var(--sn-motion-duration-short) var(--sn-motion-ease),
              font-size var(--sn-motion-duration-short) var(--sn-motion-ease),
              color var(--sn-motion-duration-short) var(--sn-motion-ease);
}
/* Clears the icon, a flex item in the group's row: 1 + 13 + 20 + 9 = 43px. */
.field:has(> .input-group > .icon) > label:not(:has(:is(input, select, textarea, button, a))) {
  inset-inline-start: 2.6875rem;
}

/* THE FLOAT CONDITIONS. :focus and :not(:placeholder-shown) are the obvious two.
 * A <select> always holds something. date/time/month/week render a browser-owned
 * format hint, do not reliably match :placeholder-shown, and carry no
 * placeholder — so they float permanently and showed this bug even at rest. */
.field:is(
  :has(:is(input, select, textarea):focus),
  :has(input:not(:placeholder-shown)),
  :has(textarea:not(:placeholder-shown)),
  :has(select),
  :has(input[type="date"]),
  :has(input[type="time"]),
  :has(input[type="month"]),
  :has(input[type="week"])
) > label:not(:has(:is(input, select, textarea, button, a))) {
  /* `line-height: 1` so the box hugs the 11px glyphs instead of inheriting the
   * body's 23px line box. With `top: 0.75rem` the whole label then sits at
   * 6.5-17.5px — entirely inside the 18px of padding the box reserves for it,
   * and clear of a control that starts at 19px. Left at 23px it cleared the
   * value's glyphs by 0.4px, which is not a fix, it is a coincidence. */
  top: 0.75rem;
  line-height: 1;
  font-size: var(--sn-type-overline-size);
  font-weight: var(--sn-type-overline-weight);
  letter-spacing: var(--sn-type-overline-tracking);
  text-transform: var(--sn-type-overline-transform);
  color: var(--sn-primary-text);
}
/* A textarea is top-aligned, so its resting label sits where the first line of
 * text would be rather than in the middle of an empty box. */
.field:has(> textarea) > label:not(:has(:is(input, select, textarea, button, a))),
.field:has(> .input-group > textarea) > label:not(:has(:is(input, select, textarea, button, a))) { top: 1.375rem; }
.field:has(textarea:focus) > label:not(:has(:is(input, select, textarea, button, a))),
.field:has(textarea:not(:placeholder-shown)) > label:not(:has(:is(input, select, textarea, button, a))) { top: 0.875rem; }
