/* Reusable component primitives — buttons, cards, badges, inputs, modals, toasts.
   Built out starting Phase 1. Everything references tokens.css semantic vars. */

/* Defensive: the [hidden] attribute must always win over a class's own display
   rule (e.g. .modal-row sets display:grid, which otherwise beats the UA
   [hidden] default since our stylesheet loads after it). */
[hidden] { display: none !important; }

/* Minimal button so early auth screens (Phase 1) have a baseline.
   2026-07-13: height trimmed 44px→40px and radius bumped to --radius-lg
   (12px) per polish feedback ("нижче, ширше, radius 12, менше вертикального
   паддінгу") — 40px still clears the WCAG 2.2 24px touch-target minimum by
   a wide margin, so this isn't a shrink to worry about. Wider side padding
   is the "ширше" half of the ask. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: 40px; padding: 0 var(--space-5);
  border: 1px solid transparent; border-radius: var(--radius-lg);
  font-size: var(--text-sm); font-weight: var(--font-semibold);
  background: var(--color-surface); color: var(--color-fg);
  transition: background var(--dur-fast) var(--ease-in-out),
              border-color var(--dur-fast) var(--ease-in-out),
              transform var(--dur-fast) var(--ease-out);
}
.btn:active { transform: scale(0.98); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Generic inline loading spinner for a button's own loading-state text
   (e.g. "Sending invitation…") — .btn:disabled already handles the dimmed/
   not-allowed look, this is just the spinning glyph itself. */
.btn-spinner { display: inline-block; width: 14px; height: 14px; margin-right: var(--space-2); vertical-align: -2px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; opacity: 0.7; animation: btn-spin 0.6s linear infinite; }
@keyframes btn-spin { to { transform: rotate(360deg); } }

.btn--primary { background: var(--color-primary); color: var(--color-on-primary); }
.btn--primary:hover:not(:disabled) { background: var(--color-primary-hover); }

.btn--ghost { background: transparent; border-color: var(--color-border); }
.btn--ghost:hover:not(:disabled) { background: var(--color-surface-hover); }

.btn--orange { background: var(--color-orange); color: #fff; }
.btn--orange:hover:not(:disabled) { background: var(--color-orange-hover); }

/* Card baseline. */
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

/* ── Modal ─────────────────────────────────────────────────────────────── */
.modal-overlay {
  position: fixed; inset: 0; z-index: var(--z-modal);
  display: grid; place-items: center; padding: var(--space-4);
  background: var(--color-overlay);
  opacity: 0; transition: opacity var(--dur-base) var(--ease-in-out);
}
.modal-overlay--in { opacity: 1; }
.modal {
  position: relative;
  width: 100%; max-width: 460px; max-height: 90dvh; overflow-y: auto;
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-xl);
  transform: translateY(12px) scale(0.98); transition: transform var(--dur-base) var(--ease-out);
}
.modal--wide { max-width: 680px; }
/* 2026-07-13 — Leave Approval Detail redesign wants a genuinely large
   modal (900-1000px), not just "wide". */
.modal--xl { max-width: 960px; }
.modal-overlay--in .modal { transform: none; }
.modal__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); padding: var(--space-5) var(--space-6); border-bottom: 1px solid var(--color-border); }
.modal__head-text { min-width: 0; }
/* 2026-07-13 — optional icon + subtitle row (Invite Employee redesign) —
   only rendered when a caller passes them, so every other modal in the app
   is visually unchanged. */
.modal__title-row { display: flex; align-items: center; gap: var(--space-3); }
.modal__icon { display: grid; place-items: center; width: 36px; height: 36px; border-radius: var(--radius-md); flex-shrink: 0; background: var(--color-primary-soft); color: var(--color-primary); }
.modal__icon .icon { width: 19px; height: 19px; }
.modal__title { font-size: var(--text-lg); }
.modal__subtitle { margin: var(--space-1) 0 0; font-size: var(--text-sm); color: var(--color-fg-muted); }
.modal__close { display: grid; place-items: center; width: 32px; height: 32px; border: 0; border-radius: var(--radius-md); background: var(--color-surface-2); color: var(--color-fg-muted); flex-shrink: 0; }
.modal__close:hover { background: var(--color-surface-hover); }
.modal__body { padding: var(--space-6); }

.modal-form { display: flex; flex-direction: column; gap: var(--space-4); }
.modal-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.modal-label { display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--text-sm); font-weight: var(--font-medium); color: var(--color-on-surface); }
.modal-input {
  /* width:100% + min-width:0 so a field with long intrinsic content (e.g. a
     <select> with long option text, like the Company settings timezone
     picker) shrinks to fit its grid/flex column instead of forcing the
     column — and the whole .modal-row — wider than its container. */
  width: 100%; min-width: 0; box-sizing: border-box;
  height: 42px; padding: 0 var(--space-3); font: inherit; font-size: var(--text-sm);
  color: var(--color-fg); background: var(--color-surface-2);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
}
.modal-input:focus { outline: none; border-color: var(--color-ring); box-shadow: 0 0 0 3px var(--color-primary-soft); }
/* The dropdown popup itself always shows full option text regardless of the
   closed box's width — only the closed, selected-value display needs to
   truncate gracefully instead of being hard-clipped by the browser. */
select.modal-input {
  cursor: pointer; overflow: hidden; padding-block: 0;
  line-height: normal; text-overflow: ellipsis; white-space: nowrap;
}
select.modal-input[multiple],
select.modal-input[size]:not([size="1"]) {
  height: auto;
  min-height: 112px;
  padding-block: var(--space-2);
  line-height: 1.4;
  white-space: normal;
}
select.modal-input[multiple] option,
select.modal-input[size]:not([size="1"]) option {
  padding: 6px var(--space-2);
}
/* Native file controls use a different baseline for the browser-provided
   button and filename (especially in Safari). This keeps any legacy/fallback
   file input aligned; new upload surfaces use .file-picker below. */
input[type="file"].modal-input {
  padding: 5px 6px;
  line-height: 32px;
  color: var(--color-fg-muted);
}
input[type="file"].modal-input::file-selector-button {
  height: 32px;
  margin: 0 var(--space-3) 0 0;
  padding: 0 var(--space-3);
  vertical-align: middle;
  border: 0;
  border-radius: 8px;
  background: var(--color-primary-soft);
  color: var(--color-primary);
  font: inherit;
  font-weight: var(--font-semibold);
  line-height: 32px;
  cursor: pointer;
}

/* Cross-browser file picker. The native input remains available to assistive
   technology and handles the actual file selection, while button and filename
   share one predictable flex baseline. */
.file-picker {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
  min-width: 0;
  height: 44px;
  box-sizing: border-box;
  gap: var(--space-3);
  padding: 5px 7px;
  overflow: hidden;
  border: 1px solid var(--color-border);
  border-radius: 11px;
  background: var(--color-surface);
  box-shadow: 0 1px 2px rgba(15,23,42,.025);
  cursor: pointer;
}
.file-picker:hover { border-color: var(--color-border-strong); }
.file-picker:focus-within {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-primary-soft) 76%, transparent);
}
.file-picker input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}
.file-picker__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 32px;
  flex: 0 0 auto;
  padding: 0 var(--space-3);
  border-radius: 8px;
  background: var(--color-primary-soft);
  color: var(--color-primary);
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  line-height: 1;
}
.file-picker__name {
  min-width: 0;
  flex: 1;
  overflow: hidden;
  color: var(--color-fg-muted);
  font-size: var(--text-sm);
  font-weight: var(--font-normal);
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-picker.has-file .file-picker__name { color: var(--color-fg); font-weight: var(--font-medium); }
.file-picker--compact { height: 42px; }
.file-picker--compact .file-picker__button { height: 30px; padding-inline: 10px; font-size: var(--text-xs); }
.file-picker--compact .file-picker__name { font-size: var(--text-xs); }
/* Multi-line fields are content-sized by textarea-autogrow.js. Unlike the
   generic input primitive they must not inherit a fixed 42/44px height. */
textarea.modal-input {
  height: auto;
  min-height: 74px;
  padding-block: var(--space-3);
  line-height: 1.5;
  box-sizing: border-box;
  resize: vertical;
}
textarea.modal-input[data-autogrow-ready] {
  transition: height 120ms var(--ease-out);
}
.modal-error { min-height: 1em; font-size: var(--text-sm); color: var(--color-danger); }
.modal-actions { display: flex; justify-content: flex-end; gap: var(--space-3); margin-top: var(--space-2); }
/* 2026-07-13 — sticky footer for long forms (Edit Employee redesign) — bleeds
   to the edges of .modal__body's own padding so it reads as a full-width bar
   pinned to the bottom of the modal's scroll area, not just another row of
   content. Opt-in modifier — other modals' footers are unaffected. */
.modal-actions--sticky {
  position: sticky; bottom: calc(-1 * var(--space-6)); z-index: 2;
  margin: var(--space-2) calc(-1 * var(--space-6)) calc(-1 * var(--space-6));
  padding: var(--space-4) var(--space-6); background: var(--color-surface); border-top: 1px solid var(--color-border);
}

/* Calendar icon inside a date input — same "icon inside the field" device
   .people-search/.hdr-search already use, just for a date field instead of
   a search field. Purely decorative: the underlying native date input and
   its behavior are unchanged. */
.modal-date-wrap { position: relative; }
.modal-date-wrap input[type="date"] { padding-right: 40px; }
.modal-date-wrap__icon { position: absolute; right: var(--space-3); top: 50%; transform: translateY(-50%); width: 18px; height: 18px; color: var(--color-fg-subtle); pointer-events: none; }

/* "Discard unsaved changes?" guard (Edit Employee redesign) — an overlay
   inside the modal itself (not a second openModal()) so "Continue editing"
   simply removes this panel and the underlying form's typed values are
   still exactly as the user left them. */
.discard-confirm { position: absolute; inset: 0; z-index: 10; display: grid; place-items: center; padding: var(--space-6); background: var(--color-overlay); border-radius: var(--radius-xl); animation: card-in var(--dur-fast) var(--ease-out) both; }
.discard-confirm__box { width: 100%; max-width: 320px; padding: var(--space-6); background: var(--color-surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-xl); text-align: center; }
.discard-confirm__box h3 { font-size: var(--text-base); font-weight: var(--font-bold); margin: 0 0 var(--space-2); }
.discard-confirm__box p { margin: 0 0 var(--space-5); font-size: var(--text-sm); color: var(--color-fg-muted); }
.discard-confirm__actions { display: flex; gap: var(--space-3); justify-content: center; }

/* Was 480px — too narrow to actually cover most phones (many modern devices
   report 481-560px CSS width), so a 2-up row with long-content fields (e.g.
   Company settings' timezone/country selects) stayed squeezed side-by-side
   right where it needed to stack. 560px matches this app's other established
   "small phone" breakpoint (people.css/dashboard.css). */
@media (max-width: 560px) { .modal-row { grid-template-columns: 1fr; } }

/* ── Status chips ──────────────────────────────────────────────────────────
   2026-07-13 — one dot treatment for every colored status/priority pill in
   the app (people.css's .status-badge, leaves.css's .status-badge.lv-*,
   documents.css's .prio-badge/.doc-exp, perform.css's .goal-conf/
   .dev-action__status) — each family already sets its own text color per
   state, so `background: currentColor` on the dot picks that up for free;
   this rule only needs to exist once, here, rather than duplicated per
   family. `.doc-exp--ok` is deliberately excluded — it's a plain expiry
   date, not a colored status, so a dot there would be a false signal. */
.status-badge, .prio-badge, .doc-exp, .goal-conf, .dev-action__status {
  display: inline-flex; align-items: center; gap: 5px;
}
.status-badge::before, .prio-badge::before, .doc-exp:not(.doc-exp--ok)::before,
.goal-conf::before, .dev-action__status::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0;
}

/* ── Toast ─────────────────────────────────────────────────────────────── */
.toast-stack { position: fixed; bottom: var(--space-6); right: var(--space-6); z-index: var(--z-toast); display: flex; flex-direction: column; gap: var(--space-2); }
.toast {
  min-width: 240px; max-width: 360px; padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md); background: var(--color-surface); color: var(--color-fg);
  border: 1px solid var(--color-border); border-left: 3px solid var(--color-info);
  box-shadow: var(--shadow-lg); font-size: var(--text-sm); cursor: pointer;
  opacity: 0; transform: translateX(16px); transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.toast--in { opacity: 1; transform: none; }
.toast--success { border-left-color: var(--color-success); }
.toast--error { border-left-color: var(--color-danger); }
.toast--info { border-left-color: var(--color-info); }
