/* Overlay surfaces — shared/_modal.html.erb and its non-modal sibling,
 * shared/_drawer.html.erb. Two components, one stylesheet: they are the same
 * family (a panel over the page) and differ in exactly one thing — what they do
 * to the page beneath. Keeping them adjacent is what makes that difference
 * reviewable. The drawer section is at the bottom of this file.
 *
 * Presentation follows the BestPath design system (Direction A — Quiet
 * Editorial). The surface mirrors the Component Library `.bp-modal-surface`
 * recipe: paper surface, rule border, large radius, a single soft scrim
 * shadow, Lora display title, ink-2 body, and `.bp-btn` actions.
 *
 * Tokens only — every colour, radius, space, and motion value references a
 * `--bp-*` variable (see app/assets/stylesheets/application.css). The local
 * `--modal-*` custom properties below are thin forwards onto those tokens so
 * per-modal overrides stay possible without reintroducing raw values.
 *
 * Inner-element rules are scoped under `.modal` so this editorial styling stays
 * confined to the shared modal and does not leak into the separate turbo-frame
 * modal that reuses the same `.modal-*` class names from components.css. The
 * `.modal-content` surface is scoped via a zero-specificity `:where(.modal)`
 * ancestor so per-modal overrides (e.g. the capabilities manage modal) still win.
 */

/* Base modal structure — the fixed scrim container, hidden by default */
.modal {
  --modal-scrim: color-mix(in srgb, var(--bp-ink) 38%, transparent);
  --modal-surface-bg: var(--bp-paper);
  --modal-radius: var(--bp-r-lg);
  --modal-shadow: 0 8px 40px color-mix(in srgb, var(--bp-ink) 10%, transparent),
                  0 1px 3px color-mix(in srgb, var(--bp-ink) 6%, transparent);

  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: var(--bp-z-modal);
  visibility: hidden;
  opacity: 0;
  transition: visibility var(--bp-dur-med) var(--bp-easing),
              opacity var(--bp-dur-med) var(--bp-easing);
}

/* Visible state — show modal */
.modal.modal-visible {
  visibility: visible;
  opacity: 1;
}

/* Scrim/backdrop */
.modal .modal-overlay {
  position: absolute;
  inset: 0;
  background-color: var(--modal-scrim);
  cursor: default;
  z-index: var(--bp-z-base);
}

/* Dismissible scrim — pointer cursor only when clicking it does something */
.modal[data-modal-dismissible-value='true'] .modal-overlay {
  cursor: pointer;
}

/* Modal surface container. Scoped to the shared modal via a zero-specificity
   `:where(.modal)` ancestor so it does not restyle the turbo-frame modal that
   reuses `.modal-content`, while keeping the selector at a single class so a
   per-modal override passed as `custom_class:` on `shared/_modal` — e.g.
   `.my-modal .modal-content { max-width: … }` — still wins. */
:where(.modal) .modal-content {
  position: relative;
  z-index: var(--bp-z-raised);
  background-color: var(--modal-surface-bg);
  border: 1px solid var(--bp-rule);
  border-radius: var(--modal-radius);
  box-shadow: var(--modal-shadow);
  max-width: 520px;
  width: 90%;
  max-height: 90vh;
  overflow-y: auto;
  animation: modalSlideIn var(--bp-dur-med) var(--bp-easing);
}

/* Animation: rise + fade */
@keyframes modalSlideIn {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Header — flat surface, no rule. Contrast comes from type + whitespace. */
.modal .modal-header {
  padding: var(--bp-s-6) var(--bp-s-6) 0;
}

.modal .modal-title {
  margin: 0;
  font-family: var(--bp-display-family);
  font-weight: var(--bp-display-weight);
  font-size: 24px;
  letter-spacing: -0.01em;
  color: var(--bp-ink);
  line-height: 1.25;
}

/* Body/message */
.modal .modal-body {
  padding: var(--bp-s-3) var(--bp-s-6) var(--bp-s-5);
  color: var(--bp-ink-2);
}

.modal .modal-message {
  margin: 0;
  font-size: 14.5px;
  color: var(--bp-ink-2);
  line-height: 1.6;
}

/* Footer — actions row, right-aligned, primary last (destructive-second) */
.modal .modal-footer {
  padding: 0 var(--bp-s-6) var(--bp-s-6);
  display: flex;
  gap: var(--bp-s-2);
  justify-content: flex-end;
  flex-wrap: wrap;
}

.modal .modal-footer .bp-btn--secondary {
  order: 1; /* Secondary on the left */
}

.modal .modal-footer .bp-btn--primary,
.modal .modal-footer .bp-btn--danger {
  order: 2; /* Primary/destructive on the right */
}

/* Responsive — stack actions full width on narrow screens */
@media (max-width: 600px) {
  :where(.modal) .modal-content {
    width: 95%;
    max-height: 85vh;
  }

  .modal .modal-header {
    padding: var(--bp-s-5) var(--bp-s-5) 0;
  }

  .modal .modal-body {
    padding: var(--bp-s-3) var(--bp-s-5) var(--bp-s-4);
  }

  .modal .modal-footer {
    padding: 0 var(--bp-s-5) var(--bp-s-5);
    flex-direction: column;
  }

  .modal .modal-footer .bp-btn {
    width: 100%;
  }

  /* Primary on top, secondary below when stacked */
  .modal .modal-footer .bp-btn--primary,
  .modal .modal-footer .bp-btn--danger {
    order: 1;
  }

  .modal .modal-footer .bp-btn--secondary {
    order: 2;
  }
}

/* Respect reduced-motion preferences */
@media (prefers-reduced-motion: reduce) {
  .modal {
    transition: none;
  }

  :where(.modal) .modal-content {
    animation: none;
  }
}

/* ============================================================================
 * THE NON-MODAL DRAWER — shared/_drawer.html.erb + drawer_controller.js
 *
 * A panel that opens OVER a page that stays live. Decided 2026-08-12,
 * DECISIONS §5.10.2 / DESIGN §2a.1, drawn in
 * docs/Design/mockups/search-execution-dashboard.html.
 *
 * ## Why this is a sibling of `.modal` and not a variant flag on it
 * `.modal` is `position: fixed; inset: 0` — the component IS a full-viewport
 * container, and it paints a scrim child, locks body scroll, and traps Tab.
 * All four are correct for a confirmation and all four are wrong here. The
 * drawer is fixed to its own box only: there is no full-viewport ancestor, so
 * every pixel outside the panel keeps receiving clicks and scrolls with no
 * `pointer-events` trickery. That structural difference is the component.
 *
 * ## FLAT. NO SHADOW. Decided, not an oversight.
 * Direction A's "shadow only on modal scrim surface" is NOT amended for this
 * panel (DECISIONS §5.10.3). A non-modal drawer is by definition not a modal
 * scrim surface, so it ships with a 1px --bp-rule border as its only depth cue,
 * over a live un-scrimmed page. It reads markedly less clearly as "above" than
 * a shadowed panel would, that cost was seen drawn and accepted, and a future
 * report of "the drawer doesn't look like it's floating" is working as decided.
 * Do not fix it locally with a shadow, a scrim, or a "subtle" elevation.
 * ========================================================================== */

.bp-drawer {
  /* Forwards onto tokens, matching the --modal-* pattern above, so a caller can
     override the panel's measure via `custom_class:` without raw values
     reappearing. 600px is the panel's own measure — 600 less 48px of padding is
     552px, ~60ch at 14.5px Inter — and is deliberately not a spacing token. */
  --bp-drawer-width: 600px;
  --bp-drawer-inset: var(--bp-s-4);

  position: fixed;
  top: var(--bp-drawer-inset);
  right: var(--bp-drawer-inset);
  bottom: var(--bp-drawer-inset);
  width: min(var(--bp-drawer-width), calc(100vw - var(--bp-drawer-inset) * 2));
  display: flex;
  flex-direction: column;
  /* Over the page and its chrome, under the modal layer: the page stays live,
     so a real modal must still be able to take over from an open drawer. */
  z-index: var(--bp-z-drawer);
  background-color: var(--bp-paper);
  border: 1px solid var(--bp-rule);
  border-radius: var(--bp-r-lg);
  animation: bpDrawerIn var(--bp-dur-med) var(--bp-easing);
}

/* `.bp-drawer` sets `display: flex`, which beats the UA stylesheet's
   `[hidden] { display: none }`. Closed is the default state, so this is not
   optional. */
.bp-drawer[hidden] {
  display: none;
}

/* Opacity only — no transform. A translate on a full-bleed panel produces
   horizontal overflow for the length of the animation at the phone tier. */
@keyframes bpDrawerIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.bp-drawer__head {
  display: flex;
  align-items: flex-start;
  gap: var(--bp-s-3);
  padding: var(--bp-s-4) var(--bp-s-5) var(--bp-s-3);
  border-bottom: 1px solid var(--bp-rule-soft);
}

.bp-drawer__headings {
  /* Without this a long unbroken heading pushes the flex row wider than the
     panel, which is the phone tier's horizontal-overflow failure. */
  min-width: 0;
}

.bp-drawer__kicker {
  display: block;
  font-family: var(--bp-mono-family);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bp-ink-subtle);
  margin-bottom: var(--bp-s-1);
}

/* Sans, not the display face. The drawer's heading is the client's own words;
   the coach banner's is the coach's, in Newsreader. The two must never be
   confusable, so this is deliberately NOT --bp-display-family. */
.bp-drawer__title {
  margin: 0;
  font-family: var(--bp-body-family);
  font-weight: 600;
  font-size: 15.5px;
  line-height: 1.35;
  color: var(--bp-ink);
  overflow-wrap: anywhere;
}

.bp-drawer__close {
  margin-left: auto;
  flex: none;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 1px solid var(--bp-rule);
  border-radius: var(--bp-r-md);
  background-color: var(--bp-surface);
  color: var(--bp-ink-muted);
  font: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}

.bp-drawer__close:hover {
  color: var(--bp-ink);
  border-color: var(--bp-ink-muted);
}

/* overscroll-behavior: contain so a scroll that reaches the end of the answer
   can never chain out and move the page underneath. The page stays live for
   clicks and for its OWN scrollbar; it does not get scrolled by proxy. */
.bp-drawer__body {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--bp-s-3) var(--bp-s-5) var(--bp-s-4);
  color: var(--bp-ink-2);
}

.bp-drawer__footer {
  min-width: 0;
  border-top: 1px solid var(--bp-rule-soft);
  padding: var(--bp-s-3) var(--bp-s-5) var(--bp-s-4);
}

.bp-drawer__hint {
  margin: var(--bp-s-2) 0 0;
  font-size: 12px;
  color: var(--bp-ink-subtle);
}

/* ---------------------------------------------------------------------------
 * THE PHONE TIER — where the drawer IS a separate surface, and that is accepted.
 *
 * At ≤620px there is no BESIDE: the panel goes full-bleed and the page is gone
 * behind it, indistinguishable from having navigated somewhere. "The answer
 * arrives where you already are" is a DESKTOP-ONLY statement, and this
 * product's audience is largely on phones (DECISIONS §5.10.3). Inset 0, no
 * radius, no border — drawn and measured at 390×844, not asserted.
 * ------------------------------------------------------------------------- */
@media (max-width: 620px) {
  .bp-drawer {
    inset: 0;
    width: auto;
    border: 0;
    border-radius: 0;
  }

  .bp-drawer__head {
    padding-top: var(--bp-s-5);
  }

  /* Principle 9: hit targets ≥ 44px on the tier that is actually touched. */
  .bp-drawer__close {
    width: 44px;
    height: 44px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bp-drawer {
    animation: none;
  }
}
