/* SarTrade — shared UI components (Public + Vendor + Admin), added 2026-09-23
   as part of the approved UI enhancement plan (Project_Status.md, session 61).

   This build's own file, loaded by _PublicLayout, both Vendor layouts and
   (since 2026-09-25) both Admin layouts, so
   each component exists once instead of being copied into public.css and
   vendor.css — same reasoning as phone-field.css. The prototype's own
   css/{style,app,fallback}.css stay untouched (CLAUDE.md trap #6); everything
   here layers on their existing .field / .field__label / .input-st contract.

   1. Floating-label fields  (opt-in per form: <form class="form-float">)
   2. Empty states           (.empty-state)
*/

/* =====================================================================
   1. Floating-label fields
   ---------------------------------------------------------------------
   Opt-in by adding `form-float` to the <form>. No markup restructuring:
   the existing `.field > label.field__label + .input-st` order stays, and
   `:has()` (CLAUDE.md: "CSS :has() selectors" before JS) moves the label
   into the box. Every text input/textarea needs placeholder=" " (or a real
   placeholder) so `:placeholder-shown` can tell "empty" from "filled".

   Fields that are not a plain control — checkbox rows, file inputs, the
   consent box, and a textarea[data-rte] that admin/rich-text-editor.js
   hides behind its own toolbar + surface — never match `:has(> .input-st…)`
   and render unchanged, label above the box.
   The .field.is-invalid contract (validation.js) is untouched; the label
   just turns red with the border.
   ===================================================================== */

.form-float .field:has(> .input-st:not([type="file"], [data-rte])),
.form-float .field:has(> .phone-field) {
  position: relative;
}

.form-float .field:has(> .input-st:not([type="file"], [data-rte])) > .field__label,
.form-float .field:has(> .phone-field) > .field__label {
  position: absolute;
  z-index: 2;
  top: 17px;
  left: 15px;
  right: 40px;
  margin: 0;
  overflow: hidden;
  font-size: 15.5px;
  font-weight: 500;
  line-height: 22px;
  letter-spacing: -.005em;
  color: var(--ink-soft);
  white-space: nowrap;
  text-overflow: ellipsis;
  pointer-events: none;
  transition: top .15s ease, font-size .15s ease, color .15s ease, line-height .15s ease;
}

.form-float .field > .input-st:not([type="file"], [data-rte]) {
  min-height: 56px;
  padding: 24px 14px 7px;
  font-size: 16px; /* 16px stops iOS Safari zooming into the field on focus */
  line-height: 1.5;
}

.form-float .field > textarea.input-st {
  min-height: 128px;
  padding-top: 28px;
}

.form-float .field > select.input-st { padding-top: 22px; padding-bottom: 6px; }

/* A real placeholder (e.g. "e.g. Liebherr") would sit on top of the resting
   label, so it stays invisible until the field is focused and the label has
   moved out of its way. */
.form-float .field > .input-st::placeholder { color: transparent; }
.form-float .field > .input-st:focus::placeholder { color: rgba(0, 0, 0, .4); }

/* Floated state: focused, filled, or a control that always shows a value
   (a <select> always has a selected option; the phone group always shows
   its country). */
.form-float .field:has(> .input-st:focus) > .field__label,
.form-float .field:has(> .input-st:not(:placeholder-shown, [data-rte])) > .field__label,
.form-float .field:has(> select.input-st) > .field__label,
.form-float .field:has(> .phone-field) > .field__label {
  top: 8px;
  font-size: 12px;
  font-weight: 600;
  line-height: 16px;
  color: var(--navy);
}

/* Autofill fills the box before any input event; kept as its own rule so a
   browser that does not know the pseudo-class drops only this rule. */
.form-float .field:has(> .input-st:-webkit-autofill) > .field__label {
  top: 8px;
  font-size: 12px;
  font-weight: 600;
  line-height: 16px;
  color: var(--navy);
}

.form-float .field:has(> .input-st:focus) > .field__label,
.form-float .field:has(> .phone-field:focus-within) > .field__label { color: var(--blue); }

.form-float .field.is-invalid > .field__label { color: var(--red); }

.form-float .field > .input-st:not([type="file"], [data-rte]):focus-visible {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(0, 97, 160, .18);
}

.form-float .field.is-invalid > .input-st:focus-visible {
  border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(237, 28, 36, .14);
}

/* Added 2026-09-25 when Admin and the Vendor portal's internal forms took
   up form-float (the Public/Vendor-auth forms never had these controls).

   Date/time pickers always paint their own "dd/mm/yyyy" text and ignore
   placeholder, so — like a <select> — their label stays floated. */
.form-float .field:has(> .input-st:is([type="date"], [type="datetime-local"], [type="time"], [type="month"])) > .field__label {
  top: 8px;
  font-size: 12px;
  font-weight: 600;
  line-height: 16px;
  color: var(--navy);
}

/* The "$" asking-price box (.input-prefix wraps the input, so the rules
   above never reach it). Treated like the phone group: label always
   floated, sign moved down onto the text line. */
.form-float .field:has(> .input-prefix > .input-st) { position: relative; }

.form-float .field:has(> .input-prefix > .input-st) > .field__label {
  position: absolute;
  z-index: 2;
  top: 8px;
  left: 15px;
  right: 40px;
  margin: 0;
  overflow: hidden;
  font-size: 12px;
  font-weight: 600;
  line-height: 16px;
  color: var(--navy);
  white-space: nowrap;
  text-overflow: ellipsis;
  pointer-events: none;
}

.form-float .field > .input-prefix > .input-st {
  min-height: 56px;
  padding: 24px 14px 7px 30px;
  font-size: 16px;
  line-height: 1.5;
}

.form-float .field > .input-prefix > .input-prefix__sign { top: 36px; }
.form-float .field > .input-prefix > .input-st::placeholder { color: transparent; }
.form-float .field > .input-prefix > .input-st:focus::placeholder { color: rgba(0, 0, 0, .4); }
.form-float .field:has(> .input-prefix > .input-st:focus) > .field__label { color: var(--blue); }
/* Restated at higher specificity so an invalid select/date/price field's
   always-floated label also turns red. */
.form-float .field.is-invalid:is(:has(> .input-st), :has(> .input-prefix)) > .field__label { color: var(--red); }

.form-float .field > .input-prefix > .input-st:focus-visible {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(0, 97, 160, .18);
}

/* The six-digit sign-in/TFA code keeps its large spaced digits (admin.css
   .tfa-code-input) instead of the 16px the floating field would give it. */
.form-float .field > .input-st.tfa-code-input { font-size: 20px; }

@media (prefers-reduced-motion: reduce) {
  .form-float .field > .field__label { transition: none; }
}

/* =====================================================================
   2. Empty states
   ---------------------------------------------------------------------
   Icon in a tinted circle + heading + one line + optional actions, for any
   list/search/filter that returns nothing. Icons are Bootstrap Icons (already
   self-hosted) and always aria-hidden — the heading carries the meaning.

     <div class="empty-state">
       <span class="empty-state__icon" aria-hidden="true"><i class="bi bi-search"></i></span>
       <h3 class="empty-state__title">…</h3>
       <p class="empty-state__text">…</p>
       <div class="empty-state__actions">…buttons…</div>
     </div>

   `.empty-state--compact` is the smaller variant for table rows and home
   page strips.
   ===================================================================== */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 48px 24px 52px;
  text-align: center;
}

.empty-state__icon {
  position: relative;
  display: grid;
  place-items: center;
  width: 88px;
  height: 88px;
  margin-bottom: 10px;
  border-radius: 50%;
  background: rgba(0, 97, 160, .09);
  box-shadow: 0 0 0 12px rgba(0, 97, 160, .04);
  color: var(--navy);
  font-size: 38px;
  line-height: 1;
}

.empty-state__badge {
  position: absolute;
  right: -2px;
  bottom: 4px;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 3px solid #fff;
  border-radius: 50%;
  background: var(--yellow);
  color: var(--navy-deep);
  font-size: 13px;
}

.empty-state__title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -.02em;
  color: var(--navy-deep);
  text-wrap: balance;
}

.empty-state__text {
  max-width: 46ch;
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.6;
  color: var(--ink-soft);
}

.empty-state__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 8px;
}

.empty-state--compact {
  gap: 6px;
  padding: 28px 16px 30px;
}

.empty-state--compact .empty-state__icon {
  width: 56px;
  height: 56px;
  margin-bottom: 6px;
  box-shadow: 0 0 0 7px rgba(0, 97, 160, .04);
  font-size: 24px;
}

.empty-state--compact .empty-state__title { font-size: 15.5px; }
.empty-state--compact .empty-state__text { font-size: 13.5px; }

/* Inside a table the cell supplies the box; drop the cell's own padding so
   the state is centred in the table body. */
td.table-empty:has(> .empty-state) { padding: 0; }
