/* Just Mercy · menus
   Split out of the single components.css so concurrent work on different
   areas cannot collide in one file, and so a page can eventually load only
   the CSS its elements need. Tokens come from tokens.css; no literal brand
   hex belongs in here (the page-quality gate's D2 criterion enforces it). */

/* =========================================================================
   JM Dropdown and Multi-select — Figma "Molecules / Dropdown menu" (522:1238),
   "Molecules / Multi-select" (522:1266) and "Atoms / Input / Dropdown" (521:1894).

   These two are the only places on the site that get a CUSTOM listbox. Every
   other control in the form suite is native, because a native control already
   does arrow keys, Enter/Space, Esc and focus return on every platform. A
   multi-select and a menu drawn with a tick column are the two things the
   platform does not give us, so they are hand-built to the WAI-ARIA
   combobox/listbox pattern — and each one still ships a real native control
   underneath as its no-JavaScript baseline.

   Built to the Figma "Spec — Forms, states & accessibility" panel (523:1406):
     Focus-visible 3px Purple 500 ring, 2px offset, on every control. Never removed.
     Errors 2px feedback/error border + message under the field; aria-invalid,
       message linked with aria-describedby. Never colour alone.
     Labels always visible above the field.
     Targets minimum 44x44px including padding.
     Dropdown and multi-select menus: arrow keys navigate, Enter/Space select,
       Esc closes, focus returns to the trigger. Multi-select values render as
       Tag / Pill.
     Transitions: border-color var(--motion-fast) var(--motion-ease-out); menu open var(--motion-base) var(--motion-ease) with a
       prefers-reduced-motion fallback to instant.

   Geometry, all from Figma:
     trigger      55 tall, 20 left / 16 right, Stone/100, text 16/1.4
     multi trigger 10 top+bottom / 12 left / 16 right, pills gap 8
     menu         Stone/100, 1px Purple/100 border, padding-block 4, offset 4
     option row   46 tall, 12 top+bottom, 20 left / 16 right, text 16/1.4
     tick         14 Medium
   ========================================================================= */

.jm-select {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;                         /* 8 — the same label gap as .jm-field */
}

/* The menu is positioned against THIS, not against .jm-select — the wrapper also
   holds the label and the helper/error line, so an overlay anchored to it would
   open below those instead of below the trigger. */
.jm-select__ui { position: relative; }

/* Figma draws both molecules 360 wide. That is the specimen board's measure, not
   a rule, so these fill their container like every other JM element. */

.jm-select__label {
  font: 500 var(--text-body)/1.2 var(--font-sans);       /* 16 Medium */
  color: var(--purple-500);
}
.jm-select__required { color: var(--feedback-error); }


/* ---- Trigger, and the native <select> that stands in for it with no script ----
   One rule for both, so the page does not change shape when the script upgrades
   it. States are the Atoms / Input / Dropdown variant set, verbatim:
     default 1px Purple/100 · hover 1px Purple/400 · open 1.5px Purple/500
     filled  Purple/700 text · error 2px feedback/error
     disabled Stone/300 on 1px Stone/600, Stone/700 text
   Figma draws the focus state as a 3px Purple/500 BORDER; the spec panel calls it
   a 3px ring at 2px offset, which is what an outline is and what the rest of the
   site uses, so it is an outline here. */

.jm-select__trigger,
.jm-select__select {
  width: 100%;
  min-height: 3.4375rem;               /* 55 */
  padding: 0.75rem 1rem 0.75rem 1.25rem;   /* 12 / 16 / 12 / 20 */
  border: 1px solid var(--purple-100);
  border-radius: 0;                    /* square, as drawn */
  background-color: var(--stone-100);
  color: var(--purple-700);
  font: 400 var(--text-body)/1.4 var(--font-sans);       /* Body/S 16 */
  text-align: left;
  cursor: pointer;
  appearance: none;
  transition: border-color var(--motion-fast) var(--motion-ease-out);
}

.jm-select__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;                        /* 12 — keeps the value clear of the caret */
}

/* A <select> cannot hold child markup, so it gets the caret drawn over it and the
   right padding to keep its own text clear of it. Not display:flex — flex on a
   replaced control renders differently across engines. */
.jm-select__select {
  display: block;
  padding-right: 3rem;                 /* 48 — 16 gutter + 16 caret + 16 clearance */
}

.jm-select__trigger:hover,
.jm-select__select:hover { border-color: var(--purple-400); }

.jm-select__trigger:focus-visible,
.jm-select__select:focus-visible,
.jm-select__select:focus {
  outline: 3px solid var(--purple-500);
  outline-offset: 2px;
}

/* Open — 1.5px Purple/500, as drawn. Raised above the menu so the focus ring,
   which sits 2px outside the trigger, is not painted over by the menu's top edge
   4px below it. */
.jm-select.is-open .jm-select__trigger {
  position: relative;
  z-index: 21;
  border-width: 1.5px;
  border-color: var(--purple-500);
}

.jm-select__trigger[aria-invalid='true'],
.jm-select__select[aria-invalid='true'] {
  border: 2px solid var(--feedback-error);
}

.jm-select__trigger:disabled,
.jm-select__trigger[aria-disabled='true'],
.jm-select__select:disabled {
  border-color: var(--stone-600);
  background-color: var(--stone-300);
  color: var(--stone-700);
  cursor: not-allowed;
}

/* The value, and the placeholder it falls back to. Purple/400 for the
   placeholder and Purple/700 once something is chosen — the atom's Default and
   Filled variants. */
.jm-select__value {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  flex: 1 1 auto;
  gap: 0.5rem;                         /* 8 — the multi-select pill gap */
  min-width: 0;
}
.jm-select__value.is-placeholder { color: var(--purple-400); }

/* The single-select value is one line of text, so it truncates rather than wraps
   and pushes the trigger taller. */
.jm-dropdown .jm-select__value {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.jm-select__caret {
  flex: 0 0 auto;
  width: 1rem;                         /* 16 */
  height: 1rem;
  color: var(--purple-500);
  transition: transform var(--motion-fast) var(--motion-ease-out);
}
.jm-select.is-open .jm-select__caret { transform: rotate(180deg); }

/* The native select's caret has to be positioned, since a <select> cannot hold
   child markup. Same 16 from the right edge as the trigger's. */
.jm-select__native { position: relative; }
.jm-select__native .jm-select__caret {
  position: absolute;
  top: 50%;
  right: 1rem;                         /* 16 */
  margin-top: -0.5rem;
  pointer-events: none;
}


/* ---- The menu — Figma "Molecules / Dropdown menu" ----
   An overlay rather than in flow. Figma draws it in flow below the trigger with a
   4px gap, which is how a static board has to show it; on a real form, pushing
   every field below it down by 200px each time a menu opens is worse than
   covering them, so the 4px becomes the overlay's offset. JUDGEMENT — flagged.

   z-index 20: above sibling fields and any card it sits in, below the sticky
   header (chrome.css uses 1000). */

.jm-menu {
  position: absolute;
  top: calc(100% + 0.25rem);           /* 4 — the drawn gap */
  left: 0;
  right: 0;
  z-index: 20;
  margin: 0;
  padding: 0.25rem 0;                  /* 4 top and bottom */
  list-style: none;
  border: 1px solid var(--purple-100);
  border-radius: 0;
  background-color: var(--stone-100);

  /* Long lists scroll rather than running off the viewport. Figma only ever draws
     four options, so the cap is ours: six 46px rows plus the menu's own padding.
     JUDGEMENT — flagged. */
  max-height: 17.75rem;                /* 284 */
  overflow-y: auto;

  /* Menu open var(--motion-base) var(--motion-ease), per the spec panel. visibility carries the close, held
     back until the fade finishes, so the menu leaves the accessibility tree and
     the tab order the moment it is closed. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-0.25rem);
  transition: opacity var(--motion-base) var(--motion-ease), transform var(--motion-base) var(--motion-ease), visibility 0s linear var(--motion-base);
}

.jm-menu.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity var(--motion-base) var(--motion-ease), transform var(--motion-base) var(--motion-ease), visibility 0s linear 0s;
}

/* The spec's "fallback to instant". */
@media (prefers-reduced-motion: reduce) {
  .jm-menu,
  .jm-menu.is-open,
  .jm-select__caret { transition: none; }
}


/* ---- Option row — Figma "Atoms / Input / Option row" ---- */

.jm-menu__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;                        /* 12 — Figma justifies apart with no gap;
                                          this only stops a long label touching
                                          the tick */
  min-height: 2.75rem;                 /* 44 — the row computes to 46, this holds
                                          the target size if the type ever shrinks */
  padding: 0.75rem 1rem 0.75rem 1.25rem;   /* 12 / 16 / 12 / 20 */
  background-color: var(--stone-100);
  color: var(--purple-700);
  font: 400 var(--text-body)/1.4 var(--font-sans);       /* Body/S 16 */
  cursor: pointer;
  user-select: none;
}

/* Selected — Purple/50 surface, Purple/500 label, tick. Exactly as drawn. */
.jm-menu__option[aria-selected='true'] {
  background-color: var(--purple-50);
  color: var(--purple-500);
}

/* Pointed at, by mouse or by the keyboard's active descendant.
   JUDGEMENT — the Figma option row has only Default and Selected variants, so
   there is nothing drawn for "the keyboard is on this row". Rather than invent a
   colour, this reuses the button spec's existing hover/pressed ladder: Purple 50
   for pointed-at, one step deeper to Purple 100 when that row is also selected,
   so the two states stay tellable apart. Flagged. */
.jm-menu__option:hover,
.jm-menu__option.is-active { background-color: var(--purple-50); }

.jm-menu__option[aria-selected='true']:hover,
.jm-menu__option[aria-selected='true'].is-active { background-color: var(--purple-100); }

.jm-menu__label { min-width: 0; }

/* Held in the layout at all times so choosing an option does not reflow the row.
   aria-selected is what conveys the state to assistive tech; the tick is the
   visible half of the same signal, never the only one. */
.jm-menu__tick {
  flex: 0 0 auto;
  font: 500 var(--text-caption)/1 var(--font-sans);      /* 14 Medium */
  visibility: hidden;
}
.jm-menu__option[aria-selected='true'] .jm-menu__tick { visibility: visible; }


/* ---- Multi-select trigger — Figma "Molecules / Multi-select" (522:1266) ----
   Tighter on the left than the single trigger, because the first thing in it is a
   pill rather than text: 10 top and bottom, 12 left, 16 right. */

.jm-multi-select .jm-select__trigger {
  min-height: 2.75rem;                 /* 44 — the drawn trigger computes to ~45 */
  padding: 0.625rem 1rem 0.625rem 0.75rem;   /* 10 / 16 / 10 / 12 */
}

/* Selected values are Tag / Pill instances — the .jm-tag rules in core.css are
   that pill, so they are reused rather than restyled. Figma uses the Purple tone.
   The pills wrap instead of clipping: Figma's frame is overflow-clip, which on a
   real form silently hides choices somebody has made. JUDGEMENT — flagged. */
.jm-multi-select .jm-tag { cursor: inherit; }


/* ---- Helper and error ----
   Never colour alone: the message is the signal, the border only supports it.
   Same shape as .jm-field's, so a form mixing the two reads as one thing. */

.jm-select__helper {
  margin: 0;
  font: 400 var(--text-label-s)/1.3 var(--font-sans);    /* 13 */
  color: var(--purple-400);
}

.jm-select__error {
  margin: 0;
  font: 500 var(--text-label-s)/1.3 var(--font-sans);
  color: var(--feedback-error);
}
.jm-select__error[hidden] { display: none; }


/* ---- The no-JavaScript baseline ----
   Each element renders a real native control — a <select> for the dropdown, a
   checkbox group for the multi-select — and the script hides it and drives it
   from the custom widget. So with no script you get a working, labelled,
   keyboard-operable control, and with script the value still lives in a real form
   field: submission, validation and form.reset() all keep working for free. */

.jm-select__choices {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;                        /* 4 */
  margin: 0;
  padding: 0;
  border: 0;
  min-inline-size: 0;                  /* a <fieldset> defaults to min-content, which
                                          would stop it shrinking with the column */
}

.jm-select__choice {
  display: flex;
  align-items: center;
  gap: 0.75rem;                        /* 12 */
  min-height: 2.75rem;                 /* 44 — the whole row is the target */
  color: var(--purple-700);
  font: 400 var(--text-body)/1.4 var(--font-sans);
  cursor: pointer;
}

.jm-select__checkbox {
  flex: 0 0 auto;
  width: 1.25rem;                      /* 20 */
  height: 1.25rem;
  accent-color: var(--purple-500);
  margin: 0;
}
.jm-select__checkbox:focus-visible {
  outline: 3px solid var(--purple-500);
  outline-offset: 2px;
}
