/* ==============================================================================
   OPTIONS & CONTENT
   ==============================================================================
   Options list, groups, checkbox, option content, icons, text, and states
*/

/* ==============================================================================
   OPTIONS CONTAINER
   ==============================================================================
*/

/* Options container */
.ms__options {
  padding: var(--ms-options-padding);

  /* Scrollbar styling */
  scrollbar-width: thin;  /* Firefox */
  scrollbar-color: var(--ms-scrollbar-thumb-bg) var(--ms-scrollbar-track-bg);
}

.ms__options::-webkit-scrollbar {
  width: var(--ms-scrollbar-width);
}

.ms__options::-webkit-scrollbar-track {
  background: var(--ms-scrollbar-track-bg);
}

.ms__options::-webkit-scrollbar-thumb {
  background: var(--ms-scrollbar-thumb-bg);
  border-radius: var(--ms-scrollbar-thumb-border-radius);
}

.ms__options::-webkit-scrollbar-thumb:hover {
  background: var(--ms-scrollbar-thumb-bg-hover);
}

/* Virtual scroll - enforce consistent heights */
.ms__options--virtual .ms__option {
  height: var(--ms-option-height, 50px);
  min-height: var(--ms-option-height, 50px);
  max-height: var(--ms-option-height, 50px);
  overflow: hidden;
  box-sizing: border-box;
}

/* Non-virtual rendering while virtual scroll is *enabled* (result count is below
   the threshold): pin rows to the same fixed --ms-option-height so the row height
   doesn't jump as filtering crosses the virtualization threshold. Mirrors the
   rule above; applied only when the consumer opted into virtual scroll, so plain
   non-virtual lists keep their natural content height. */
.ms__options--fixed-height .ms__option {
  height: var(--ms-option-height, 50px);
  min-height: var(--ms-option-height, 50px);
  max-height: var(--ms-option-height, 50px);
  overflow: hidden;
  box-sizing: border-box;
}

/* ==============================================================================
   GROUPS
   ==============================================================================
*/

/* Group - consecutive groups */
.ms__group + .ms__group {
  border-top: var(--ms-group-border-top);
  margin-top: var(--ms-group-margin-top);
  padding-top: var(--ms-group-padding-top);
}

/* Group label */
.ms__group-label {
  padding: var(--ms-group-label-padding);
  font-size: var(--ms-group-label-font-size);
  font-weight: var(--ms-group-label-font-weight);
  color: var(--ms-group-label-color);
  text-transform: var(--ms-group-label-transform);
  letter-spacing: var(--ms-group-label-letter-spacing);
}

/* Selectable group header (group-select-mode="cascade"): a tristate checkbox +
   the label on one clickable row, laid out like an option so the checkbox aligns. */
.ms__group-label--selectable {
  display: flex;
  align-items: center;
  gap: var(--ms-option-gap);
  cursor: pointer;
  user-select: none;
}

@media (hover: hover) {
  .ms__group-label--selectable:hover {
    background: var(--ms-option-bg-hover);
  }
}

/* Non-cascade header that carries a count chip: make it flex (like --selectable) so the
   chip's `margin-inline-start: auto` can park it at the trailing edge. */
.ms__group-label--has-count {
  display: flex;
  align-items: center;
  gap: var(--ms-option-gap);
}

/* Per-group selected-item count "behind the group title" (any grouped list). Reuses the
   in-input count badge look (`.ms__counter` / `--ms-counter-*`) so it's the same chip, not a
   new one. `margin-inline-start: auto` parks it at the trailing edge of the flex header. */
.ms__group-count {
  margin-inline-start: auto;
  flex: 0 0 auto;
  box-sizing: border-box;
  /* A single digit is too narrow with padding alone — floor the width so it reads as a
     balanced badge (grows for 2+ digits via padding). Scales with --ms-rem. */
  min-width: calc(2 * var(--ms-rem));
  /* Horizontal padding only. The chip INHERITS the header's font-size (rather than pinning
     `--ms-counter-font-size`, which is baked at :host and doesn't scale) so it grows with the
     group label in the fullscreen overlay — and with zero vertical padding its box height equals
     the header's text line, so the row height is identical whether the chip is shown or hidden
     (no layout shift, no oversized-box distortion). Colours/weight/radius still borrow the
     in-input counter so it reads as the same chip. */
  padding-block: 0;
  padding-inline: calc(0.4 * var(--ms-rem));
  background: var(--ms-counter-badge-bg);
  color: var(--ms-counter-badge-color);
  font-weight: var(--ms-counter-font-weight);
  border-radius: var(--ms-counter-border-radius);
  text-align: center;
}

/* ==============================================================================
   INDIVIDUAL OPTION
   ==============================================================================
*/

/* Individual option */
.ms__option {
  display: flex;
  align-items: var(--ms-checkbox-align, center); /* Support checkbox alignment */
  gap: var(--ms-option-gap);
  padding: var(--ms-option-padding);
  position: relative; /* anchor for the matched leading-accent overlay (::before) */
  min-height: var(--ms-option-min-height, auto);
  color: var(--ms-option-text-color);
  background: var(--ms-option-bg);
  cursor: pointer;
  /* A click on a row (or a click-drag across the list) is a selection gesture,
     not a text-selection one — stop the browser from highlighting the label. */
  user-select: none;
  -webkit-user-select: none;
  /* Tap-highlight flash is suppressed once for the whole component on :host (base.css) —
     an inherited property, so it reaches this row without a per-element declaration. */
}

/* Hover states are gated to real pointing devices. On touch, `:hover` "sticks"
   after a tap until the next tap elsewhere, so a tapped row (or a tap on its info
   affordance) would stay highlighted as if selected. `@media (hover: hover)` keeps
   hover on mouse/trackpad and drops it on touch (phones, the fullscreen overlay). */
@media (hover: hover) {
  .ms__option:hover {
    background: var(--ms-option-bg-hover);
    color: var(--ms-option-color-hover, inherit);
  }
}

.ms__option--focused {
  background: var(--ms-option-bg-focused);
  color: var(--ms-option-color-focused, inherit);
  outline: var(--ms-option-outline-focused);
  outline-offset: var(--ms-option-focus-outline-offset);
}

.ms__option--matched {
  background: var(--ms-option-bg-matched);
  color: var(--ms-option-color-matched, inherit);
}

/* Leading-edge accent bar for the current match. Painted as an absolutely-positioned
   overlay rather than a real `border-inline-start` on the row: a border eats into the
   row's inline size only in the matched state, so the checkbox/label jumped ~3px inward
   as focus stepped between matches (navigate mode). The overlay is out of flow, so it
   never shifts content; it still uses the same --ms-option-border-matched shorthand and
   flips in RTL via the logical inset/border. Covers selected+matched too (same class). */
.ms__option--matched::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  border-inline-start: var(--ms-option-border-matched);
  pointer-events: none;
}

.ms__option--selected {
  background: var(--ms-option-bg-selected);
}

@media (hover: hover) {
  .ms__option--selected:hover {
    background: var(--ms-option-bg-selected-hover, var(--ms-option-bg-selected));
  }
}

.ms__option--disabled {
  opacity: var(--ms-disabled-opacity);
  cursor: not-allowed;
  background: var(--ms-option-disabled-bg);
}

.ms__option--disabled:hover {
  background: var(--ms-option-disabled-bg);
}

/* Focused + Hover */
@media (hover: hover) {
  .ms__option--focused:hover {
    background: var(--ms-option-bg-focused-hover);
    color: var(--ms-option-color-focused-hover, var(--ms-option-color-focused, var(--ms-option-text-color)));
  }
}

/* Matched + Hover */
.ms__option--matched:hover {
  background: var(--ms-option-bg-matched-hover);
  color: var(--ms-option-color-matched-hover, var(--ms-option-color-matched, var(--ms-option-text-color)));
}

/* Selected + Focused */
.ms__option--selected.ms__option--focused {
  background: var(--ms-option-bg-selected-focused);
  outline: var(--ms-option-outline-focused);
  outline-offset: var(--ms-option-focus-outline-offset);
}

/* Selected + Matched (the accent bar is drawn by .ms__option--matched::before above) */
.ms__option--selected.ms__option--matched {
  background: var(--ms-option-bg-selected-matched);
}

/* Disabled + Selected */
.ms__option--disabled.ms__option--selected {
  background: var(--ms-option-bg-disabled-selected);
}

/* Disabled + Focused (prevents focus outline on disabled items) */
.ms__option--disabled.ms__option--focused {
  outline: none;
}

/* Checkbox alignment variants (center is default, defined in variables.css) */
.ms__option[data-checkbox-align="top"] {
  --ms-checkbox-align: flex-start;
  /* Nudge the top-aligned checkbox down to sit on the label's first text line
     (line-height leaves the glyph inset from the line-box top). Only meaningful
     for top alignment — center/bottom keep the 0 default so they stay centered. */
  --ms-checkbox-margin-top: calc(0.2 * var(--ms-rem));
}

.ms__option[data-checkbox-align="bottom"] {
  --ms-checkbox-align: flex-end;
}

/* ==============================================================================
   CHECKBOX (Custom Styled)
   ==============================================================================
   Uses a hidden native checkbox with a custom styled pseudo-element overlay.
   This allows full control over background, border, and checkmark colors.
*/

.ms__checkbox {
  /* Hide native checkbox but keep it accessible */
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  /* Declared size IS the rendered outer size (the native <input> already defaults to
     this, pinned so it's deterministic + matches svelte-treeview's span). Shared
     --base-checkbox-scale render contract — scaled border grows inward. */
  box-sizing: border-box;
  flex-shrink: 0;
  position: relative;
  margin-top: var(--ms-checkbox-margin-top);
  margin-inline-end: var(--ms-checkbox-margin-right);
  margin-bottom: var(--ms-checkbox-margin-bottom);
  margin-inline-start: var(--ms-checkbox-margin-left);
  /* Box scaled via calc (shared --base-checkbox-scale contract) — NOT a CSS
     transform. A transform promotes the box to its own compositing layer, which
     pixel-snaps the fractionally-positioned ::after mask and shifts the tick/dash
     off-centre (inconsistently, row to row). Sizing the box directly keeps the
     mark crisp; the border-width and radius scale proportionally too via calc in
     the --ms-checkbox-border* vars. See base-css-variables → "Checkbox render method". */
  width: calc(var(--ms-checkbox-size) * var(--ms-checkbox-scale));
  height: calc(var(--ms-checkbox-size) * var(--ms-checkbox-scale));
  cursor: pointer;

  /* Custom checkbox appearance */
  background: var(--ms-checkbox-bg);
  border: var(--ms-checkbox-border);
  border-radius: var(--ms-checkbox-border-radius);
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

/* Checkmark using ::after pseudo-element */
.ms__checkbox::after {
  content: '';
  position: absolute;
  display: none;
  /* Checkmark glyph — a currentColor-tinted mask so it follows --ms-icon-check →
     the shared --base-icon-check contract (was a CSS-border checkmark). Sized via
     --ms-icon-check-size, which defaults to the shared --base-icon-check-size (68%) —
     the SAME knob pure-admin's .pa-checkbox reads, so the mark renders identically in
     both and a theme swapping the glyph (e.g. an edge-to-edge star) can shrink it once
     for everyone. Was `contain`, which assumed every glyph carried its own viewBox
     padding. The indeterminate dash below inherits this mask box, so it follows the
     same size. */
  inset: 0;
  background-color: var(--ms-checkbox-checkmark-color);
  -webkit-mask: var(--ms-icon-check) no-repeat center / var(--ms-icon-check-size);
  mask: var(--ms-icon-check) no-repeat center / var(--ms-icon-check-size);
}

/* Hover state */
.ms__checkbox:hover:not(:disabled) {
  border-color: var(--ms-checkbox-hover-border-color);
}

/* Checked state */
.ms__checkbox:checked {
  background: var(--ms-checkbox-checked-bg);
  border: var(--ms-checkbox-checked-border);
}

.ms__checkbox:checked::after {
  display: block;
}

/* Indeterminate (tristate) — cascade checkbox-mode: a partially-selected branch.
   Filled like the checked box but with a dash instead of a checkmark. Rendered
   from a modifier class (not the native :indeterminate property) so it survives
   string re-render + virtual scroll. */
.ms__checkbox--indeterminate {
  background: var(--ms-checkbox-checked-bg);
  border: var(--ms-checkbox-checked-border);
}

.ms__checkbox--indeterminate::after {
  display: block;
  /* Swap the check glyph for the dash — follows --ms-icon-indeterminate →
     --base-icon-indeterminate. Inherits the mask box from .ms__checkbox::after. */
  -webkit-mask-image: var(--ms-icon-indeterminate);
  mask-image: var(--ms-icon-indeterminate);
}

.ms__checkbox--indeterminate:hover:not(:disabled) {
  background: var(--ms-checkbox-checked-bg-hover);
  border-color: var(--ms-checkbox-checked-border-color-hover);
}

/* Checked + Hover */
.ms__checkbox:checked:hover:not(:disabled) {
  background: var(--ms-checkbox-checked-bg-hover);
  border-color: var(--ms-checkbox-checked-border-color-hover);
}

/* Focus state (accessibility) */
.ms__checkbox:focus-visible {
  outline: 2px solid var(--ms-checkbox-checked-bg);
  outline-offset: 2px;
}

/* Disabled state */
.ms__checkbox:disabled {
  cursor: not-allowed;
  background: var(--ms-checkbox-disabled-bg);
  border: var(--ms-checkbox-disabled-border);
  opacity: 0.6;
}

.ms__checkbox:disabled:checked {
  background: var(--ms-checkbox-disabled-bg);
}

.ms__option--disabled .ms__checkbox {
  cursor: not-allowed;
}

/* ==============================================================================
   OPTION CONTENT
   ==============================================================================
*/

/* Option content (rich content wrapper) */
.ms__option-content {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--ms-option-content-gap);
  min-width: 0; /* Allow text truncation */
}

/* Option icon/SVG */
.ms__option-icon {
  flex-shrink: 0;
  width: var(--ms-option-icon-size);
  height: var(--ms-option-icon-size);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--ms-option-icon-font-size);
}

.ms__option-icon svg {
  width: 100%;
  height: 100%;
  fill: currentColor;
}

/* Empty icon gutter — rendered on icon-less rows only when `reserve-icon-slot` is on,
   so a list mixing icon and icon-less rows keeps one label start column. It inherits the
   fixed box from .ms__option-icon (flex-shrink:0 + --ms-option-icon-size); the modifier
   is just a styling hook (it draws nothing). */
.ms__option-icon--empty {
  visibility: hidden;
}

/* Option text container */
.ms__option-text {
  flex: 1;
  min-width: 0;
}

/* Option title/main text. This is the label hook (cf. `.wtv__node-label` /
   `.stv__node-label` in the treeview packages) — flip the white-space/overflow/
   text-overflow variables to truncate long labels; defaults keep them wrapping. */
.ms__option-title {
  font-size: var(--ms-option-title-font-size);
  color: var(--ms-option-title-color);
  line-height: var(--ms-line-height-relaxed);
  white-space: var(--ms-option-title-white-space, normal);
  overflow: var(--ms-option-title-overflow, visible);
  text-overflow: var(--ms-option-title-text-overflow, clip);
}

/* Title color states (for solid background themes) */
@media (hover: hover) {
  .ms__option:hover .ms__option-title {
    color: var(--ms-option-title-color-hover, var(--ms-option-title-color));
  }
}

.ms__option--selected .ms__option-title {
  color: var(--ms-option-title-color-selected, var(--ms-option-title-color));
}

@media (hover: hover) {
  .ms__option--selected:hover .ms__option-title {
    color: var(--ms-option-title-color-selected-hover, var(--ms-option-title-color-selected, var(--ms-option-title-color)));
  }
}

/* Highlight matched text */
.ms__option-title mark {
  background: var(--ms-option-mark-bg);
  color: var(--ms-option-mark-color);
  font-weight: var(--ms-option-mark-font-weight);
}

/* Option subtitle (multiline support) */
.ms__option-subtitle {
  margin-top: var(--ms-option-subtitle-margin-top);
  font-size: var(--ms-option-subtitle-font-size);
  color: var(--ms-option-subtitle-color);
  line-height: var(--ms-option-subtitle-line-height);
}

/* Subtitle color states (for solid background themes) */
@media (hover: hover) {
  .ms__option:hover .ms__option-subtitle {
    color: var(--ms-option-subtitle-color-hover, var(--ms-option-subtitle-color));
  }
}

.ms__option--selected .ms__option-subtitle {
  color: var(--ms-option-subtitle-color-selected, var(--ms-option-subtitle-color));
}

@media (hover: hover) {
  .ms__option--selected:hover .ms__option-subtitle {
    color: var(--ms-option-subtitle-color-selected-hover, var(--ms-option-subtitle-color-selected, var(--ms-option-subtitle-color)));
  }
}

/* ==============================================================================
   STATES
   ==============================================================================
*/

/* Empty state */
.ms__empty {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--ms-state-min-height);
  padding: var(--ms-empty-padding);
  text-align: center;
  font-size: var(--ms-empty-font-size);
  color: var(--ms-empty-color);
}

/* "Add new" prompt — replaces the empty message when isAddNewAllowed is on and the
   user has typed a term with no matches. Clickable (data-action="add-new" → handleAddNew);
   styled like an accent-coloured option row so it reads as an action. */
.ms__add-new {
  display: flex;
  align-items: center;
  gap: var(--ms-add-new-gap);
  /* Size like a normal option row (padding + content) — NOT the tall empty/loading
     footprint. The empty-state min-height is only for the "no results" / spinner swap. */
  padding: var(--ms-add-new-padding);
  font-size: var(--ms-add-new-font-size);
  color: var(--ms-add-new-color);
  cursor: pointer;
  /* Same rationale as .ms__option: a click here is an action gesture, not text selection. */
  user-select: none;
  -webkit-user-select: none;
}

/* Hover gated to real pointing devices (see .ms__option for the touch rationale). */
@media (hover: hover) {
  .ms__add-new:hover {
    background: var(--ms-add-new-bg-hover);
    color: var(--ms-add-new-color-hover);
  }
}

/* Keyboard focus (arrow-navigated) — mirror the hover surface so the prompt reads
   as the active row, matching .ms__option--focused. */
.ms__add-new--focused {
  background: var(--ms-add-new-bg-hover);
  color: var(--ms-add-new-color-hover);
}

/* Pending state — an async addNewCallback is in flight (spinner + "Adding …"). Not
   clickable (no data-action on the row), so drop the pointer cursor. */
.ms__add-new--loading {
  cursor: default;
}

/* Self-contained spinner (no external .pa-loader dependency — it can't cross the
   shadow boundary). Sized to the prompt's icon box; tinted by the row's currentColor. */
.ms__add-new-spinner {
  flex-shrink: 0;
  width: var(--ms-add-new-icon-size);
  height: var(--ms-add-new-icon-size);
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, currentColor 25%, transparent);
  border-top-color: currentColor;
  animation: ms-spin 0.6s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .ms__add-new-spinner { animation-duration: 1.5s; }
}

.ms__add-new-icon {
  flex-shrink: 0;
  width: var(--ms-add-new-icon-size);
  height: var(--ms-add-new-icon-size);
  background-color: currentColor;
  -webkit-mask: var(--ms-icon-add-new) center / contain no-repeat;
          mask: var(--ms-icon-add-new) center / contain no-repeat;
}

.ms__add-new-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Loading state */
.ms__loader {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: var(--ms-state-min-height);
  padding: var(--ms-loader-padding);
  gap: var(--ms-loader-gap);
}

.ms__loading-text {
  font-size: var(--ms-loading-text-font-size);
  color: var(--ms-loading-color);
}
