/*!
 * ZIC overlays — dropdowns, tooltips, popovers, context menus, comboboxes
 * ============================================================================
 * Layer 6 of the theme stack.
 *
 * WHAT THE SHELL ALREADY DOES (measured, not assumed)
 *
 *   - Positioning. Popper v2 ships with Bootstrap 5.3.8 and its `flip`
 *     modifier is on by default. Measured: a menu opened at the bottom of the
 *     viewport reports `data-popper-placement="top-start"` and sits above its
 *     trigger. Nothing here reimplements flipping.
 *   - Keyboard navigation. Arrow keys move through an open menu and Escape
 *     closes it. Measured, working.
 *   - The menu's entrance animation. `zic-dropdown-in` already exists in
 *     nav.css and is applied to `.dropdown-menu`.
 *   - Per-instance tooltip delay and "persistent until click". Bootstrap
 *     already supports both — `data-bs-delay="500"` and
 *     `data-bs-trigger="click"`. Surfaced in the partial and the guide rather
 *     than reimplemented.
 *   - Rich tooltips. `data-bs-html="true"` renders markup, measured with a
 *     link and a <strong> inside.
 *   - Surfaces. `.dropdown-menu`, `.popover`, `.modal`, `.toast` and
 *     `.offcanvas` all already resolve `--bs-*-bg` to `--zic-surface-elevated`.
 *
 * WHAT WAS BROKEN
 *
 *   1. **Tooltips inside htmx-swapped content never worked.** `main.js`
 *      initialises `[data-bs-toggle="tooltip"]` exactly once, at load. htmx
 *      inserts fragments and swaps them in without re-running it, so every
 *      tooltip inside a swapped partial was inert. Measured on an inserted
 *      fragment: `bootstrap.Tooltip.getInstance(el)` was null and the title was
 *      never consumed. This app has 39 tooltips, many of them in table
 *      partials that htmx replaces, so this was a silent and widespread failure.
 *      `overlays.js` re-initialises on `htmx:afterSwap`; the guard against a
 *      second instance is `getInstance`.
 *   2. **The tooltip surface was #1A0E33 with no dark override.** On a dark
 *      page that is 1.24:1 against the surface behind it — a smudge, not a
 *      layer. Now an inverted token pair: 17.4:1 separation on light, 11.5:1 on
 *      dark, text at 18.2:1 / 13.4:1.
 *   3. Menu geometry was Bootstrap's, not the design's: min-width 160px (spec
 *      200), item padding 8.688px (spec 8), no max-height and no scroll (spec
 *      400px + auto). A long menu ran off the bottom of the viewport.
 *   4. `transform-origin` was the element's centre, so a menu scaled out of its
 *      own middle instead of from the corner it is anchored to.
 *   5. No context menu, no combobox/autocomplete, no popover component.
 *
 * ONE SHARED ANIMATION, AND WHY IT MATTERS
 *
 * `zic-dropdown-in` is defined at the end of this file and every floating
 * surface here reuses it rather than declaring a second entrance. That is
 * deliberate: the keyframe animates the independent `scale` property rather
 * than `transform`, because Popper positions a floating element with an inline
 * `transform: translate3d(...)` — animating `transform` would overwrite that
 * and drop the menu at the viewport origin. A duplicated keyframe is an
 * invitation for someone to "fix" one copy with `transform` and silently break
 * Popper's positioning in that surface only.
 *
 * It lives here rather than in nav.css because Bootstrap dropdowns have no
 * entrance animation of their own, so the only surfaces that use it are the
 * ZIC components defined in this file.
 *
 * (`transform-origin` is safe to set here: translation is origin-independent,
 * so changing it moves nothing — it only decides which corner the `scale`
 * grows from.)
 */

/* ===========================================================================
 * 1. Dropdowns.
 * ======================================================================== */
.dropdown-menu {
  --bs-dropdown-min-width: 200px;
  --bs-dropdown-item-padding-x: var(--zic-space-3); /* 16px */
  --bs-dropdown-item-padding-y: var(--zic-space-2); /*  8px */
  --bs-dropdown-border-radius: var(--zic-radius-md);
  --bs-dropdown-box-shadow: var(--zic-shadow-lg);
  --bs-dropdown-bg: var(--zic-surface-elevated);
  --bs-dropdown-border-color: var(--zic-border-default);
  --bs-dropdown-link-hover-bg: var(--zic-interactive-hover);
  --bs-dropdown-link-active-bg: var(--zic-interactive-active);
  --bs-dropdown-link-active-color: var(--zic-primary-text);
  max-block-size: var(--zic-dropdown-max-height, 400px);
  overflow-y: auto;
  /* A menu that scrolls should not also scroll the page behind it. */
  overscroll-behavior: contain;
}

/* The scale grows from the corner the menu is anchored to, which is what makes
   it read as unfolding from the trigger. A fixed `top right` would scale a
   start-aligned menu out of its far corner. Popper writes the placement, so
   this stays correct through a flip. */
.dropdown-menu {
  transform-origin: top left;
}

.dropdown-menu[data-popper-placement^='bottom'] {
  transform-origin: top left;
}

.dropdown-menu[data-popper-placement^='top'] {
  transform-origin: bottom left;
}

.dropdown-menu[data-popper-placement$='end'] {
  transform-origin: top right;
}

.dropdown-menu[data-popper-placement^='top'][data-popper-placement$='end'] {
  transform-origin: bottom right;
}

.dropdown-menu[data-popper-placement^='right'] {
  transform-origin: left center;
}

.dropdown-menu[data-popper-placement^='left'] {
  transform-origin: right center;
}

/* Header / label inside a menu. */
.dropdown-header {
  padding: var(--zic-space-2) var(--zic-space-3) var(--zic-space-1);
  font-size: var(--zic-font-size-xs);
  font-weight: var(--zic-font-weight-semibold);
  letter-spacing: var(--zic-font-tracking-wide);
  text-transform: uppercase;
  color: var(--zic-text-secondary);
}

/* A shortcut hint on the trailing edge of an item — `ms-auto` in the markup. */
.dropdown-item .dropdown-item-shortcut {
  flex: none;
  margin-inline-start: auto;
  padding-inline-start: var(--zic-space-3);
  font-family: var(--zic-font-family-mono);
  font-size: var(--zic-font-size-xs);
  color: var(--zic-text-disabled);
}

.dropdown-item {
  display: flex;
  align-items: center;
  gap: var(--zic-space-2);
}

/* An item that is the current selection. */
.dropdown-item.active,
.dropdown-item:active {
  background-color: var(--zic-interactive-active);
  color: var(--zic-primary-text);
}

/* Destructive items. Colour alone is not the signal — the item keeps its icon
   and label — but a delete affordance should not look like every other row. */
.dropdown-item.zic-dropdown-item--danger {
  color: var(--zic-status-danger-text);
}

.dropdown-item.zic-dropdown-item--danger:hover,
.dropdown-item.zic-dropdown-item--danger:focus-visible {
  background-color: var(--zic-status-danger-bg-subtle);
  color: var(--zic-status-danger-text);
}

.dropdown-divider {
  margin: var(--zic-space-2) 0;
  border-block-start: 1px solid var(--zic-border-default);
}

/* ===========================================================================
 * 2. Tooltips.
 *
 * The surface is inverted rather than elevated — see `--zic-tooltip-bg` in the
 * palettes for why.
 * ======================================================================== */
.tooltip {
  --bs-tooltip-bg: var(--zic-tooltip-bg);
  --bs-tooltip-color: var(--zic-tooltip-text);
  --bs-tooltip-max-width: var(--zic-tooltip-width, 320px);
  --bs-tooltip-border-radius: var(--zic-radius-sm);
  --bs-tooltip-padding-x: var(--zic-space-3);
  --bs-tooltip-padding-y: var(--zic-space-2);
  --bs-tooltip-font-size: var(--zic-font-size-sm);
  --bs-tooltip-opacity: 1;
}

/* Bootstrap sets max-width on `.tooltip-inner`, not on `.tooltip`, so the
   variable has to be consumed where the shell declares the rule. */
.tooltip .tooltip-inner {
  max-inline-size: var(--zic-tooltip-width, 320px);
  text-align: start;
  line-height: var(--zic-line-height-sm);
}

/* Rich tooltips carry prose, links and images. */
.tooltip .tooltip-inner a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.tooltip .tooltip-inner img {
  display: block;
  max-inline-size: 100%;
  block-size: auto;
  margin-block-start: var(--zic-space-2);
  border-radius: var(--zic-radius-xs);
}

.tooltip .tooltip-inner > :first-child {
  margin-block-start: 0;
}

.tooltip .tooltip-inner > :last-child {
  margin-block-end: 0;
}

.tooltip.zic-tooltip--wide {
  --zic-tooltip-width: 420px;
}

/* A tooltip opened by click (`data-bs-trigger="click"`) is a small popover; it
   needs a visible edge because it stays put long enough to be read against the
   page rather than glanced at. */
.tooltip.zic-tooltip--persistent .tooltip-inner {
  border: 1px solid var(--zic-border-default);
  box-shadow: var(--zic-shadow-lg);
}

/* ===========================================================================
 * 3. Popovers.
 * ======================================================================== */
.popover {
  --bs-popover-bg: var(--zic-surface-elevated);
  --bs-popover-border-color: var(--zic-border-default);
  --bs-popover-header-bg: var(--zic-surface-elevated);
  --bs-popover-header-color: var(--zic-text-primary);
  --bs-popover-body-color: var(--zic-text-secondary);
  --bs-popover-border-radius: var(--zic-radius-md);
  --bs-popover-max-width: var(--zic-popover-width, 320px);
  --bs-popover-box-shadow: var(--zic-shadow-xl);
}

.popover {
  box-shadow: var(--bs-popover-box-shadow);
}

.popover-header {
  display: flex;
  align-items: center;
  gap: var(--zic-space-2);
  padding: var(--zic-space-3) var(--zic-space-4);
  font-size: var(--zic-font-size-base);
  font-weight: var(--zic-font-weight-semibold);
  border-block-end: 1px solid var(--zic-border-default);
}

.popover-body {
  padding: var(--zic-space-4);
  font-size: var(--zic-font-size-sm);
  line-height: var(--zic-line-height-sm);
}

/* Close button in the header, pushed to the trailing edge. */
.popover-header .btn-close {
  margin-inline-start: auto;
}

/* Tabs inside a popover. Reuses the shell's `.nav-tabs` so the two do not
   drift, but with the padding a 320px surface can afford. */
.popover .nav-tabs {
  padding-inline: var(--zic-space-3);
  border-block-end: 1px solid var(--zic-border-default);
}

.popover .nav-tabs .nav-link {
  padding: var(--zic-space-2) var(--zic-space-2);
  font-size: var(--zic-font-size-sm);
}

/* A form in a popover: the fields stack, and the actions sit on the trailing
   edge like every other form footer in the app. */
.popover-body .zic-popover-form {
  display: flex;
  flex-direction: column;
  gap: var(--zic-space-3);
}

.popover-body .zic-popover-form .zic-popover-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--zic-space-2);
  margin-block-start: var(--zic-space-1);
}

/* ===========================================================================
 * 4. Context menu.
 *
 * A dropdown menu positioned at the pointer instead of against a trigger. It
 * reuses `.dropdown-menu` wholesale — the same surface, item padding, divider
 * and animation — so the only new thing here is the positioning contract.
 * ======================================================================== */
.zic-context-menu {
  position: fixed;
  z-index: var(--zic-z-modal);
  inset-block-start: var(--zic-context-y, 0);
  inset-inline-start: var(--zic-context-x, 0);
  min-inline-size: 200px;
  max-block-size: 400px;
  overflow-y: auto;
  transform-origin: top left;
}

/* Flipped when the pointer is close enough to an edge that the menu would be
   clipped: the anchor moves to the opposite corner, so the menu opens *into*
   the viewport. */
.zic-context-menu[data-zic-flip-x] {
  transform-origin: top right;
}

.zic-context-menu[data-zic-flip-y] {
  transform-origin: bottom left;
}

.zic-context-menu[data-zic-flip-x][data-zic-flip-y] {
  transform-origin: bottom right;
}

/* ===========================================================================
 * 5. Combobox / autocomplete.
 *
 * One widget covers both the brief's "autocomplete" and its "enhanced select":
 * a search input over a listbox, with optional groups, a multi-select mode
 * whose selections are chips, and windowed rendering for long lists.
 * ======================================================================== */
.zic-combobox {
  position: relative;
}

.zic-combobox-list {
  position: absolute;
  /* Float above modals (z-index: 1060) and form controls */
  z-index: 1070;
  inset-inline: 0;
  inset-block-start: calc(100% + var(--zic-space-1));
  max-block-size: var(--zic-combobox-max-height, 280px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--zic-space-1);
  background-color: var(--zic-surface-elevated, #ffffff);
  border: 1px solid var(--zic-border-default, rgba(0, 0, 0, 0.12));
  border-radius: var(--zic-radius-md, 0.625rem);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18), 0 4px 12px rgba(0, 0, 0, 0.08);
  transform-origin: top left;
  animation: zic-dropdown-in var(--zic-transition-fast);
}

/* Flipped when the field sits low on the page and there is more room above it
   than below: the list hangs off the top of the control instead of running off
   the bottom of the viewport, and it grows from the corner it is anchored to.
   `place()` in overlays.js decides this and caps the height either way. */
.zic-combobox-list[data-zic-flip-y] {
  inset-block-start: auto;
  inset-block-end: calc(100% + var(--zic-space-1));
  transform-origin: bottom left;
}

.zic-combobox-list[hidden] {
  display: none;
}

/* Group headers are ordinary rows of the same fixed height as an option, so the
 * windowed renderer's `index * ROW` arithmetic holds whether or not the list is
 * grouped. That rules out `position: sticky`: a sticky header inside an
 * absolutely-positioned window has nothing to stick to. */
.zic-combobox-group {
  display: flex;
  align-items: center;
  block-size: var(--zic-combobox-row, 36px);
  padding: 0 var(--zic-space-3);
  font-size: var(--zic-font-size-xs);
  font-weight: var(--zic-font-weight-semibold);
  letter-spacing: var(--zic-font-tracking-wide);
  text-transform: uppercase;
  color: var(--zic-text-secondary);
}

.zic-combobox-option {
  display: flex;
  align-items: center;
  gap: var(--zic-space-2);
  /* Fixed, because the windowed renderer positions rows arithmetically. */
  block-size: var(--zic-combobox-row, 36px);
  padding: 0 var(--zic-space-3);
  border-radius: var(--zic-radius-sm);
  font-size: var(--zic-font-size-sm);
  color: var(--zic-text-primary);
  cursor: pointer;
}

.zic-combobox-option[aria-selected='true'] {
  background-color: var(--zic-interactive-active);
  color: var(--zic-primary-text);
  font-weight: var(--zic-font-weight-medium);
}

/* The keyboard cursor. Distinct from `aria-selected`, which marks the value
   rather than the position — a list can show both at once. */
.zic-combobox-option.is-active {
  background-color: var(--zic-interactive-hover);
}

/* Rows a SmartSelect renders on the server. They are `.list-group-item`s, not
   `.zic-combobox-option`s, because they carry a two-line body (a fund's type
   and risk profile, a benefit's type) that the fixed 36px option row would
   squash — so the cursor gets its own rule rather than the option block's. */
.zic-combobox-list [role='option'].is-active {
  background-color: var(--zic-interactive-hover);
}

/* The list host is a `.list-group`; its items inherit a transparent background
   (`--bs-list-group-bg`), which is what made the old inline-styled panel
   see-through. The host's own surface now sits behind them. */
.zic-combobox-list > .list-group-item {
  background-color: transparent;
  border-inline: 0;
}

.plan-card {
  cursor: pointer;
}

.zic-combobox-option[aria-disabled='true'] {
  color: var(--zic-text-disabled);
  cursor: not-allowed;
}

/* The slice of a long list that is actually rendered; its height is the full
   scroll height, so the scrollbar reflects the whole option set. Only inside a
   window are rows taken out of flow — a short list stays a plain block, which
   is why the absolute positioning is scoped here and not on the base class. */
.zic-combobox-window {
  position: relative;
  inline-size: 100%;
}

.zic-combobox-window .zic-combobox-option {
  position: absolute;
  inset-inline: var(--zic-space-1);
}

.zic-combobox mark {
  padding: 0;
  color: inherit;
  background-color: transparent;
  font-weight: var(--zic-font-weight-semibold);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.zic-combobox-empty,
.zic-combobox-loading {
  padding: var(--zic-space-3);
  font-size: var(--zic-font-size-sm);
  color: var(--zic-text-secondary);
  text-align: center;
}

/* Loading: a spinner before the message, drawn in currentColor so it is legible
   on either palette without a rule per theme. The rotation keyframe is the
   generic one from cards.css — there are already several identical `rotate`
   keyframes across the layers, which is a cleanup worth doing once rather than
   adding a fourth here. */
.zic-combobox-loading::before {
  content: '';
  display: inline-block;
  vertical-align: -0.15em;
  margin-inline-end: var(--zic-space-2);
  inline-size: 0.875rem;
  block-size: 0.875rem;
  border: 2px solid currentColor;
  border-block-start-color: transparent;
  border-radius: var(--zic-radius-pill);
  animation: zic-card-spin 700ms linear infinite;
}

/* Multi-select chips, sat inside the control above the input. */
.zic-combobox-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--zic-space-1);
  margin-block-end: var(--zic-space-1);
}

.zic-combobox-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--zic-space-1);
  padding: 2px var(--zic-space-1) 2px var(--zic-space-2);
  font-size: var(--zic-font-size-xs);
  line-height: var(--zic-line-height-xs);
  color: var(--zic-primary-text);
  background-color: var(--zic-interactive-active);
  border-radius: var(--zic-radius-sm);
}

.zic-combobox-chip button {
  display: inline-grid;
  place-items: center;
  padding: 0;
  inline-size: 1rem;
  block-size: 1rem;
  color: inherit;
  background: none;
  border: 0;
  border-radius: var(--zic-radius-pill);
  cursor: pointer;
}

.zic-combobox-chip button:hover {
  background-color: var(--zic-interactive-hover);
}

/* A native <select> that has been upgraded: the real control stays in the DOM
   for form submission, but is not shown. */
.zic-combobox-native {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ===========================================================================
 * The shared entrance animation.
 *
 * `scale` rather than `transform`: Popper writes an inline
 * `transform: translate3d(...)` on every floating surface, so animating
 * `transform` would overwrite the position. `scale` composes with it.
 * ======================================================================== */
@keyframes zic-dropdown-in {
  from {
    opacity: 0;
    scale: 0.96;
  }

  to {
    opacity: 1;
    scale: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .zic-context-menu,
  .zic-combobox-list,
  .zic-popover {
    animation: none;
  }
}
