/**
 * zamplex-modal.css v3.1 — styles for assets/js/zamplex-modal.js (modal + offcanvas)
 * ─────────────────────────────────────────────────────────────────────────
 * Namespace: .zx-mdl-* (no global pollution). Themed by the Zamplex design
 * tokens (--color-*, --radius-*, --shadow-*, --font-*) when present — the
 * light/dark flip follows html[data-theme] automatically — with a complete
 * self-contained fallback palette so the component still looks right on
 * pages without the token sheets (.zx-mdl-light / .zx-mdl-dark set explicitly by JS).
 *
 * Motion language (CMS-Admin design tokens):
 *   --zx-mdl-dur-1 120ms hover · --zx-mdl-dur-2 200ms fade/scale · --zx-mdl-dur-3 320ms slide
 *   --zx-mdl-spring: modal pop (slight overshoot)
 *
 * Last Update: 2026-07-07
 */

/* ═══════════════ THEME BRIDGE — Zamplex tokens with fallbacks ═══════════ */
.zx-mdl-wrap {
  --zx-mdl-primary:        var(--color-primary, #2563eb);
  --zx-mdl-primary-hover:  var(--color-primary-hover, #1d4ed8);
  --zx-mdl-primary-ink:    var(--color-primary-contrast, #ffffff);
  --zx-mdl-primary-ring:   var(--color-primary-ring, rgba(37, 99, 235, 0.28));
  --zx-mdl-danger:         var(--color-danger, #ef4444);
  --zx-mdl-success:        var(--color-success, #22c55e);
  /* Darker fills for WHITE-text buttons so they clear WCAG AA (the raw status
     tokens above are tuned for icons/soft chips, not white-on-solid text).
     Each has a *-h hover shade that goes DARKER (never brighter) so the white
     label GAINS contrast on hover instead of washing out — same principle as
     --zx-mdl-primary-hover, one dedicated colour per variant. */
  --zx-mdl-danger-btn:     #dc2626;  /* red-600   · ~4.8:1 with #fff */
  --zx-mdl-danger-btn-h:   #b91c1c;  /* red-700   · ~6.4:1 with #fff — hover */
  --zx-mdl-success-btn:    #047857;  /* green-700 · ~5.5:1 with #fff */
  --zx-mdl-success-btn-h:  #065f46;  /* green-800 · ~7.3:1 with #fff — hover */
  --zx-mdl-warn:           var(--color-warn, #f59e0b);   /* amber-500 — icon/soft chip */
  --zx-mdl-warn-btn:       #eba914;  /* golden amber — a LIGHT fill: pairs with dark ink, not #fff (white ≈ 2:1, fails) */
  --zx-mdl-warn-btn-h:     #d3910f;  /* deeper golden — hover */
  --zx-mdl-warn-btn-ink:   #422006;  /* dark amber ink · ~7.3:1 on #eba914 (AAA) */

  --zx-mdl-bg:             var(--color-bg, #f8fafc);
  --zx-mdl-surface:        var(--color-surface, #ffffff);
  --zx-mdl-surface-2:      var(--color-bg-subtle, #f1f5f9);
  --zx-mdl-text:           var(--color-text, #0f172a);
  --zx-mdl-text-2:         var(--color-text-muted, #475569);
  --zx-mdl-text-3:         var(--color-text-subtle, #94a3b8);
  --zx-mdl-border:         var(--color-border, #e2e8f0);
  --zx-mdl-border-2:       var(--color-border-strong, #cbd5e1);
  --zx-mdl-hover:          rgba(0, 0, 0, 0.04);
  --zx-mdl-shadow-xl:      var(--shadow-lg, 0 24px 64px rgba(2, 6, 23, 0.22), 0 4px 16px rgba(2, 6, 23, 0.10));
  --zx-mdl-overlay:        rgba(9, 14, 24, 0.55);

  --zx-mdl-r-lg:           var(--radius-lg, 14px);
  --zx-mdl-r-md:           var(--radius, 10px);
  --zx-mdl-r-sm:           var(--radius-sm, 8px);
  --zx-mdl-r-full:         var(--radius-full, 999px);

  --zx-mdl-font:           var(--font-sans, 'Anuphan', Inter, system-ui, sans-serif);
  --zx-mdl-font-display:   var(--font-display, 'Bai Jamjuree', Manrope, sans-serif);

  --zx-mdl-dur-1: 120ms;
  --zx-mdl-dur-2: 200ms;
  --zx-mdl-dur-3: 320ms;
  --zx-mdl-ease:   cubic-bezier(0.4, 0, 0.2, 1);
  --zx-mdl-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
}

/* Resolved dark (marker set by JS; token-following with dark fallbacks). */
.zx-mdl-wrap.zx-mdl-dark {
  --zx-mdl-primary:       var(--color-primary, #4d84f5);
  --zx-mdl-primary-hover: var(--color-primary-hover, #6b9bf7);
  --zx-mdl-primary-ink:   var(--color-primary-contrast, #0b1220);
  --zx-mdl-warn:          var(--color-warn, #fbbf24);   /* amber-400 — brighter on dark */
  --zx-mdl-bg:            var(--color-bg, #1a1a22);
  --zx-mdl-surface:       var(--color-surface, #232330);
  --zx-mdl-surface-2:     var(--color-bg-subtle, #16161d);
  --zx-mdl-text:          var(--color-text, #e6e8ee);
  --zx-mdl-text-2:        var(--color-text-muted, #a7adba);
  --zx-mdl-text-3:        var(--color-text-subtle, #6b7280);
  --zx-mdl-border:        var(--color-border, #333344);
  --zx-mdl-border-2:      var(--color-border-strong, #44445a);
  --zx-mdl-hover:         rgba(255, 255, 255, 0.05);
  --zx-mdl-overlay:       rgba(0, 0, 0, 0.62);
  --zx-mdl-shadow-xl:     0 24px 64px rgba(0, 0, 0, 0.55), 0 4px 16px rgba(0, 0, 0, 0.35);
}

/* EXPLICIT setTheme('light'|'dark') [A8] — pins a literal palette so the modal
   really flips even when the host page's --color-* tokens are the other mode
   (auto mode above keeps following the host tokens). Placed AFTER the marker
   blocks so, at equal specificity, the forced palette wins in the cascade. */
.zx-mdl-wrap.zx-mdl-force-light {
  --zx-mdl-primary: #2563eb; --zx-mdl-primary-hover: #1d4ed8; --zx-mdl-primary-ink: #ffffff;
  --zx-mdl-primary-ring: rgba(37, 99, 235, 0.28);
  --zx-mdl-warn: #f59e0b;
  --zx-mdl-bg: #f8fafc; --zx-mdl-surface: #ffffff; --zx-mdl-surface-2: #f1f5f9;
  --zx-mdl-text: #0f172a; --zx-mdl-text-2: #475569; --zx-mdl-text-3: #94a3b8;
  --zx-mdl-border: #e2e8f0; --zx-mdl-border-2: #cbd5e1;
  --zx-mdl-hover: rgba(0, 0, 0, 0.04);
  --zx-mdl-overlay: rgba(9, 14, 24, 0.55);
  --zx-mdl-shadow-xl: 0 24px 64px rgba(2, 6, 23, 0.22), 0 4px 16px rgba(2, 6, 23, 0.10);
}
.zx-mdl-wrap.zx-mdl-force-dark {
  --zx-mdl-primary: #4d84f5; --zx-mdl-primary-hover: #6b9bf7; --zx-mdl-primary-ink: #0b1220;
  --zx-mdl-primary-ring: rgba(77, 132, 245, 0.4);
  --zx-mdl-warn: #fbbf24;
  --zx-mdl-bg: #1a1a22; --zx-mdl-surface: #232330; --zx-mdl-surface-2: #16161d;
  --zx-mdl-text: #e6e8ee; --zx-mdl-text-2: #a7adba; --zx-mdl-text-3: #6b7280;
  --zx-mdl-border: #333344; --zx-mdl-border-2: #44445a;
  --zx-mdl-hover: rgba(255, 255, 255, 0.05);
  --zx-mdl-overlay: rgba(0, 0, 0, 0.62);
  --zx-mdl-shadow-xl: 0 24px 64px rgba(0, 0, 0, 0.55), 0 4px 16px rgba(0, 0, 0, 0.35);
}

.zx-mdl-wrap, .zx-mdl-wrap *, .zx-mdl-wrap *::before, .zx-mdl-wrap *::after { box-sizing: border-box; }

/* ═══════════════ BACKDROP ═══════════════ */
.zx-mdl-backdrop {
  position: fixed; inset: 0; z-index: 1039;
  background: var(--zx-mdl-overlay);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity var(--zx-mdl-dur-2) var(--zx-mdl-ease);
}
.zx-mdl-backdrop.zx-mdl-in { opacity: 1; }

/* ═══════════════ MODAL OVERLAY + DIALOG ═══════════════ */
.zx-mdl-overlay {
  position: fixed; inset: 0; z-index: 1040;
  display: flex; justify-content: center;
  overflow-y: auto; overflow-x: hidden;
  padding: 28px 16px;
  opacity: 0; pointer-events: none;
  transition: opacity var(--zx-mdl-dur-2) var(--zx-mdl-ease);
}
.zx-mdl-overlay.zx-mdl-in { opacity: 1; pointer-events: auto; }
.zx-mdl-overlay.zx-mdl-pos-center { align-items: center; }
.zx-mdl-overlay.zx-mdl-pos-top    { align-items: flex-start; }

.zx-mdl-dialog {
  position: relative; width: 100%; margin: auto;
  background: var(--zx-mdl-surface);
  color: var(--zx-mdl-text);
  font-family: var(--zx-mdl-font);
  border: 1px solid var(--zx-mdl-border);
  border-radius: var(--zx-mdl-r-lg);
  box-shadow: var(--zx-mdl-shadow-xl);
  display: flex; flex-direction: column;
  overflow: hidden;
  transform: translateY(14px) scale(0.965);
  transition: transform var(--zx-mdl-dur-2) var(--zx-mdl-spring);
}
.zx-mdl-overlay.zx-mdl-in .zx-mdl-dialog { transform: translateY(0) scale(1); }

/* Size scale (Tokens-design policy: confirm ≈ sm/sm2x · form ≈ md/md2x · big ≈ full) */
.zx-mdl-dialog.zx-mdl-sm    { max-width: 24rem; }
.zx-mdl-dialog.zx-mdl-sm2x  { max-width: 27rem; }
.zx-mdl-dialog.zx-mdl-md    { max-width: 32rem; }
.zx-mdl-dialog.zx-mdl-md2x  { max-width: 36rem; }
.zx-mdl-dialog.zx-mdl-lg    { max-width: 42rem; }
.zx-mdl-dialog.zx-mdl-lg2x  { max-width: 48rem; }
.zx-mdl-dialog.zx-mdl-xl    { max-width: 56rem; }
.zx-mdl-dialog.zx-mdl-xxl   { max-width: 64rem; }
.zx-mdl-dialog.zx-mdl-xxl2x { max-width: 72rem; }
.zx-mdl-dialog.zx-mdl-3xl   { max-width: 80rem; }
.zx-mdl-dialog.zx-mdl-3xl2x { max-width: 90rem; }
.zx-mdl-dialog.zx-mdl-4xl   { max-width: 100rem; }
.zx-mdl-dialog.zx-mdl-full  { max-width: min(96vw, 110rem); min-height: 92vh; min-height: 92dvh; } /* vh fallback → dvh */

/* scrollBody [A12]: cap the dialog to the viewport so the head + foot stay pinned
   (flex-shrink:0) and only the body scrolls — ideal for long content, code, logs.
   Without this the whole modal grows and the overlay scrolls instead. */
.zx-mdl-dialog.zx-mdl-scroll { max-height: calc(100vh - 3.5rem); max-height: calc(100dvh - 3.5rem); } /* vh fallback → dvh */
.zx-mdl-dialog.zx-mdl-scroll .zx-mdl-body { overflow-y: auto; }
.zx-mdl-dialog.zx-mdl-scroll .zx-mdl-body::-webkit-scrollbar { width: 8px; }
.zx-mdl-dialog.zx-mdl-scroll .zx-mdl-body::-webkit-scrollbar-thumb { background: var(--zx-mdl-border-2); border-radius: var(--zx-mdl-r-full); }
@media (max-width: 640px) { .zx-mdl-dialog.zx-mdl-scroll { max-height: calc(100vh - 1.5rem); max-height: calc(100dvh - 1.5rem); } }

/* ═══════════════ HEAD ═══════════════ */
/* position:relative → containing block for the absolute close (✕) button.
   The panel head is position:sticky (below), also a valid containing block. */
.zx-mdl-head {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--zx-mdl-border);
  flex-shrink: 0;
}
.zx-mdl-title {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--zx-mdl-font-display);
  font-size: 16px; font-weight: 700; line-height: 1.25;
  color: var(--zx-mdl-text);
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.zx-mdl-title > svg, .zx-mdl-title > i { flex-shrink: 0; width: 19px; height: 19px; }
/* Reserve room so a long title never runs under the absolute ✕ (only when present). */
/* .zx-mdl-has-x is added by the JS template when btnClose is on (no :has() —
   works on browsers without CSS4 selectors). */
.zx-mdl-head.zx-mdl-has-x .zx-mdl-title { padding-right: 28px; }

.zx-mdl-x {
  position: absolute; top: 0.5rem; right: 0.5rem; z-index: 3;
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex-shrink: 0;
  border: none; background: transparent; cursor: pointer;
  color: var(--zx-mdl-text-3);
  border-radius: var(--zx-mdl-r-sm);
  -webkit-user-select: none; user-select: none;
  transition: background var(--zx-mdl-dur-1) var(--zx-mdl-ease), color var(--zx-mdl-dur-1) var(--zx-mdl-ease);
}
.zx-mdl-x svg { width: 16px; height: 16px; }
.zx-mdl-x:hover { background: var(--zx-mdl-hover); color: var(--zx-mdl-text); }
.zx-mdl-x:focus-visible { outline: 2px solid var(--zx-mdl-primary); outline-offset: 1px; }

/* Draggable header */
.zx-mdl-head.zx-mdl-draggable { cursor: grab; user-select: none; touch-action: none; }
.zx-mdl-head.zx-mdl-draggable .zx-mdl-title { cursor: text; user-select: text; }
.zx-mdl-head.zx-mdl-dragging { cursor: grabbing; }

/* ═══════════════ BODY ═══════════════ */
.zx-mdl-body {
  flex: 1; position: relative;
  padding: 18px 24px;
  font-size: 14px; line-height: 1.65;
  color: var(--zx-mdl-text);
  overflow: visible;
}
.zx-mdl-content { color: var(--zx-mdl-text-2); }

/* Confirm-icon mark (Tokens-design: centered round soft chip above the message) */
.zx-mdl-mark {
  width: 46px; height: 46px; margin: 4px auto 12px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--zx-mdl-r-full);
}
.zx-mdl-mark svg { width: 22px; height: 22px; }
/* Each chip declares a static rgba FALLBACK first, then the token-aware
   color-mix; browsers without color-mix() drop the second declaration and
   keep the rgba (slightly off-brand on custom tokens — acceptable). */
.zx-mdl-mark--primary { background: rgba(37, 99, 235, 0.12);  background: color-mix(in srgb, var(--zx-mdl-primary) 12%, transparent); color: var(--zx-mdl-primary); }
.zx-mdl-mark--danger  { background: rgba(239, 68, 68, 0.12);  background: color-mix(in srgb, var(--zx-mdl-danger) 12%, transparent);  color: var(--zx-mdl-danger); }
.zx-mdl-mark--success { background: rgba(34, 197, 94, 0.14);  background: color-mix(in srgb, var(--zx-mdl-success) 14%, transparent); color: var(--zx-mdl-success); }
.zx-mdl-mark--warn    { background: rgba(245, 158, 11, 0.16); background: color-mix(in srgb, var(--zx-mdl-warn) 16%, transparent);    color: var(--zx-mdl-warn); }
.zx-mdl-mark ~ .zx-mdl-content { text-align: center; }

/* Loading */
.zx-mdl-loading {
  position: absolute; inset: 0; z-index: 5;
  display: none; align-items: center; justify-content: center;
  background: var(--zx-mdl-surface);
}
.zx-mdl-loading.zx-mdl-in { display: flex; }
.zx-mdl-spinner {
  width: 34px; height: 34px;
  border: 3px solid var(--zx-mdl-border-2);
  border-top-color: var(--zx-mdl-primary);
  border-radius: 50%;
  animation: zx-mdl-spin 0.8s linear infinite;
}
@keyframes zx-mdl-spin { to { transform: rotate(360deg); } }

/* Fetch error / denied message */
.zx-mdl-fetch-msg { text-align: center; padding: 22px 8px; }
.zx-mdl-fetch-msg__title { font-weight: 700; color: var(--zx-mdl-danger); margin: 0 0 4px; }
.zx-mdl-fetch-msg__detail { font-size: 13px; color: var(--zx-mdl-text-2); margin: 0 0 14px; }

/* ═══════════════ FOOT ═══════════════ */
.zx-mdl-foot {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 20px;
  border-top: 1px solid var(--zx-mdl-border);
  background: var(--zx-mdl-surface-2);
  flex-shrink: 0;
}
.zx-mdl-foot--right   { justify-content: flex-end; }
.zx-mdl-foot--center  { justify-content: center; }
.zx-mdl-foot--left    { justify-content: flex-start; }
.zx-mdl-foot--between { justify-content: space-between; }

/* Preset (icon) dialogs: airy centered foot without the divider */
.zx-mdl-mark ~ .zx-mdl-loading + * { }
/* .zx-mdl-has-mark is added by the JS template when cfg.icon is set (spotlight
   presets) — replaces :has(.zx-mdl-mark) for wider browser support. */
.zx-mdl-dialog.zx-mdl-has-mark .zx-mdl-foot {
  border-top: none; background: transparent;
  justify-content: center; padding-top: 2px; padding-bottom: 22px;
}

/* ═══════════════ COMPACT PRESET (toned title · no icon) ═══════════════ */
/* Alternative to the spotlight (icon) layout — see zamplex-modal.js _dialogPreset.
   The title is tinted by tone; the message stays left-aligned; buttons sit
   bottom-right. Button order matches spotlight (Cancel left, action right). */
.zx-mdl-dialog.zx-mdl-tone-danger  .zx-mdl-title { color: var(--zx-mdl-danger); }
.zx-mdl-dialog.zx-mdl-tone-primary .zx-mdl-title { color: var(--zx-mdl-primary); }
.zx-mdl-dialog.zx-mdl-tone-success .zx-mdl-title { color: var(--zx-mdl-success); }
.zx-mdl-dialog.zx-mdl-tone-warn    .zx-mdl-title { color: var(--zx-mdl-warn); }

.zx-mdl-dialog.zx-mdl-compact .zx-mdl-head { border-bottom: none; padding-top: 24px; padding-bottom: 2px; }
.zx-mdl-dialog.zx-mdl-compact .zx-mdl-body { padding-top: 10px; }
.zx-mdl-dialog.zx-mdl-compact .zx-mdl-foot {
  border-top: none; background: transparent;
  padding-top: 6px; padding-bottom: 18px;
}

/* Subject: the record/entity an action targets (e.g. the row being deleted). */
.zx-mdl-subject {
  margin-top: 12px; padding: 9px 13px;
  border-left: 3px solid var(--zx-mdl-border-2);
  background: var(--zx-mdl-surface-2);
  border-radius: var(--zx-mdl-r-sm);
  font-weight: 600; color: var(--zx-mdl-text);
  word-break: break-word;
}
.zx-mdl-dialog.zx-mdl-tone-danger  .zx-mdl-subject { border-left-color: var(--zx-mdl-danger); }
.zx-mdl-dialog.zx-mdl-tone-primary .zx-mdl-subject { border-left-color: var(--zx-mdl-primary); }
.zx-mdl-dialog.zx-mdl-tone-success .zx-mdl-subject { border-left-color: var(--zx-mdl-success); }
.zx-mdl-dialog.zx-mdl-tone-warn    .zx-mdl-subject { border-left-color: var(--zx-mdl-warn); }
/* Spotlight (icon) layout centres the subject and drops the accent bar. */
.zx-mdl-dialog.zx-mdl-has-mark .zx-mdl-subject {
  border-left: none; background: transparent;
  margin-top: 6px; padding: 4px 0 0;
  text-align: center; color: var(--zx-mdl-text-2);
}

/* ═══════════════ CLASSIC PRESET (Layout C — titled · footer divider) ═══════════════ */
/* Standard confirmation dialog: a plain (untinted) title flowing into the message,
   a divider above a right-aligned footer, and a ghost Cancel beside the filled
   action. Differs from compact: no tint, and it KEEPS the footer divider. */
.zx-mdl-dialog.zx-mdl-classic .zx-mdl-head { border-bottom: none; padding-bottom: 6px; }
.zx-mdl-dialog.zx-mdl-classic .zx-mdl-body { padding-top: 6px; }
.zx-mdl-dialog.zx-mdl-classic .zx-mdl-foot { background: transparent; } /* keep the top divider, drop the grey fill */

/* ═══════════════ BUTTONS ═══════════════ */
.zx-mdl-btn {
  position: relative;                 /* containing block for the ripple host [A11] */
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-width: 84px; height: 38px; padding: 0 18px;
  border-radius: var(--zx-mdl-r-sm);
  border: 1px solid var(--zx-mdl-border-2);
  background: var(--zx-mdl-surface);
  color: var(--zx-mdl-text);
  font-family: var(--zx-mdl-font);
  font-size: 13.5px; font-weight: 600;
  cursor: pointer; white-space: nowrap;
  /* Button labels are controls, not copy — never selectable/draggable (even in
     a draggable dialog where the title stays selectable). */
  -webkit-user-select: none; user-select: none;
  transition: background var(--zx-mdl-dur-1) var(--zx-mdl-ease), border-color var(--zx-mdl-dur-1) var(--zx-mdl-ease),
              color var(--zx-mdl-dur-1) var(--zx-mdl-ease), transform var(--zx-mdl-dur-1) var(--zx-mdl-ease),
              box-shadow var(--zx-mdl-dur-1) var(--zx-mdl-ease);
}
/* Neutral/outline (Cancel): keep the label colour fixed, darken the border for
   a clear hover cue — the plain surface-2 fill alone read as "text sinking". */
.zx-mdl-btn:hover { background: var(--zx-mdl-surface-2); border-color: var(--zx-mdl-text-3); color: var(--zx-mdl-text); }
.zx-mdl-btn:active { transform: translateY(1px) scale(0.99); }
.zx-mdl-btn:focus-visible { outline: 2px solid var(--zx-mdl-primary); outline-offset: 2px; }
.zx-mdl-btn:disabled { opacity: 0.45; cursor: not-allowed; }

.zx-mdl-btn--primary {
  background: var(--zx-mdl-primary); border-color: var(--zx-mdl-primary); color: var(--zx-mdl-primary-ink);
  box-shadow: 0 1px 2px rgba(37, 99, 235, 0.4); /* fallback */
  box-shadow: 0 1px 2px color-mix(in srgb, var(--zx-mdl-primary) 40%, transparent);
}
/* Re-assert the primary ink on hover: the neutral .zx-mdl-btn:hover sets color at
   the same specificity, so without this the label would inherit --zx-mdl-text
   (dark in light theme → unreadable on the blue). --zx-mdl-primary-ink is white in
   light and the dark ink in dark theme, so both themes stay correct. */
.zx-mdl-btn--primary:hover { background: var(--zx-mdl-primary-hover); border-color: var(--zx-mdl-primary-hover); color: var(--zx-mdl-primary-ink); }
.zx-mdl-btn--danger  { background: var(--zx-mdl-danger-btn); border-color: var(--zx-mdl-danger-btn); color: #fff; }
.zx-mdl-btn--danger:hover  { background: var(--zx-mdl-danger-btn-h); border-color: var(--zx-mdl-danger-btn-h); color: #fff; }
.zx-mdl-btn--success { background: var(--zx-mdl-success-btn); border-color: var(--zx-mdl-success-btn); color: #fff; }
.zx-mdl-btn--success:hover { background: var(--zx-mdl-success-btn-h); border-color: var(--zx-mdl-success-btn-h); color: #fff; }
.zx-mdl-btn--warn    { background: var(--zx-mdl-warn-btn); border-color: var(--zx-mdl-warn-btn); color: var(--zx-mdl-warn-btn-ink); }
.zx-mdl-btn--warn:hover    { background: var(--zx-mdl-warn-btn-h); border-color: var(--zx-mdl-warn-btn-h); color: var(--zx-mdl-warn-btn-ink); }
.zx-mdl-btn--ghost { background: transparent; border-color: transparent; color: var(--zx-mdl-text-2); }
/* Ghost darkens the label (never lightens) so it stays readable over the tint. */
.zx-mdl-btn--ghost:hover { background: var(--zx-mdl-hover); color: var(--zx-mdl-text); border-color: var(--zx-mdl-border-2); }

/* Busy state (setFooterBusy) — inline spinner replaces interactivity */
.zx-mdl-btn.zx-mdl-busy { position: relative; color: transparent !important; pointer-events: none; }
.zx-mdl-btn.zx-mdl-busy::after {
  content: ''; position: absolute; inset: 0; margin: auto;
  width: 16px; height: 16px;
  /* Neutral spinner by default so a busy Cancel/ghost button stays visible in
     light theme; filled variants (below) get the white spinner. */
  border: 2px solid var(--zx-mdl-border-2);
  border-top-color: var(--zx-mdl-text-2);
  border-radius: 50%;
  animation: zx-mdl-spin 0.7s linear infinite;
}
.zx-mdl-btn--primary.zx-mdl-busy::after,
.zx-mdl-btn--danger.zx-mdl-busy::after,
.zx-mdl-btn--success.zx-mdl-busy::after {
  border-color: rgba(255, 255, 255, 0.45);
  border-top-color: #fff;
}
/* Warn is a LIGHT fill with dark ink — its spinner is dark too, not white. */
.zx-mdl-btn--warn.zx-mdl-busy::after {
  border-color: rgba(66, 32, 6, 0.35); /* fallback */
  border-color: color-mix(in srgb, var(--zx-mdl-warn-btn-ink) 35%, transparent);
  border-top-color: var(--zx-mdl-warn-btn-ink);
}

/* ═══════════════ RIPPLE (click feedback on in-modal buttons) [A11] ═══════════════ */
/* Host overlay clips the ripple to the button shape (border-radius:inherit) while
   leaving the button's own overflow untouched, so box-shadows survive. Spawned by
   zamplex-modal.js _ripple(); tint = currentColor (white on filled, ink on light). */
.zx-mdl-ripple-host {
  position: absolute; inset: 0; overflow: hidden;
  border-radius: inherit; pointer-events: none;
}
.zx-mdl-ripple {
  position: absolute; border-radius: 50%;
  background: currentColor; opacity: 0.28;
  transform: scale(0); pointer-events: none;
  animation: zx-mdl-ripple 0.55s var(--zx-mdl-ease);
}
@keyframes zx-mdl-ripple { to { transform: scale(1); opacity: 0; } }

/* ═══════════════ SHAKE (static backdrop) ═══════════════ */
@keyframes zx-mdl-shake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-8px); } 30% { transform: translateX(7px); }
  45% { transform: translateX(-5px); } 60% { transform: translateX(4px); }
  75% { transform: translateX(-2px); } 90% { transform: translateX(1px); }
}
.zx-mdl-dialog.zx-mdl-shake, .zx-mdl-panel.zx-mdl-shake { animation: zx-mdl-shake 0.45s ease-in-out; }
@keyframes zx-mdl-shake-y {
  0%, 100% { transform: translateY(0); }
  15% { transform: translateY(-6px); } 30% { transform: translateY(5px); }
  45% { transform: translateY(-3px); } 60% { transform: translateY(2px); }
}
.zx-mdl-panel--top.zx-mdl-shake, .zx-mdl-panel--bottom.zx-mdl-shake { animation: zx-mdl-shake-y 0.4s ease-in-out; }

/* ═══════════════ OFFCANVAS PANEL ═══════════════ */
.zx-mdl-panel {
  position: fixed; z-index: 1040;
  background: var(--zx-mdl-surface);
  color: var(--zx-mdl-text);
  font-family: var(--zx-mdl-font);
  display: flex; flex-direction: column;
  box-shadow: var(--zx-mdl-shadow-xl);
  overflow: hidden;
  transition: transform var(--zx-mdl-dur-3) var(--zx-mdl-ease);
}
.zx-mdl-panel--right  { top: 0; right: 0; bottom: 0; width: min(430px, 92vw); transform: translateX(102%); border-left: 1px solid var(--zx-mdl-border); }
.zx-mdl-panel--left   { top: 0; left: 0; bottom: 0;  width: min(430px, 92vw); transform: translateX(-102%); border-right: 1px solid var(--zx-mdl-border); }
.zx-mdl-panel--top    { top: 0; left: 0; right: 0;   height: min(400px, 72vh); transform: translateY(-102%); border-bottom: 1px solid var(--zx-mdl-border); }
.zx-mdl-panel--bottom { bottom: 0; left: 0; right: 0; height: min(400px, 72vh); transform: translateY(102%); border-top: 1px solid var(--zx-mdl-border); }
.zx-mdl-panel.zx-mdl-in   { transform: translate(0, 0); }

.zx-mdl-panel .zx-mdl-head { position: sticky; top: 0; z-index: 2; background: var(--zx-mdl-surface); }
.zx-mdl-panel .zx-mdl-body { overflow-y: auto; }
.zx-mdl-panel .zx-mdl-foot { position: sticky; bottom: 0; z-index: 2; }
.zx-mdl-panel .zx-mdl-body::-webkit-scrollbar { width: 6px; }
.zx-mdl-panel .zx-mdl-body::-webkit-scrollbar-thumb { background: var(--zx-mdl-border-2); border-radius: var(--zx-mdl-r-full); }

/* ═══════════════ FORM ELEMENTS (inside modal/panel) ═══════════════ */
.zx-mdl-wrap .zx-mdl-label {
  display: block; font-size: 13px; font-weight: 600;
  margin-bottom: 6px; color: var(--zx-mdl-text);
}
.zx-mdl-wrap .zx-mdl-input, .zx-mdl-wrap .zx-mdl-select, .zx-mdl-wrap .zx-mdl-textarea {
  width: 100%; padding: 9px 12px;
  border: 1.5px solid var(--zx-mdl-border-2);
  border-radius: var(--zx-mdl-r-sm);
  font-size: 14px; font-family: inherit;
  background: var(--zx-mdl-bg); color: var(--zx-mdl-text);
  outline: none;
  transition: border-color var(--zx-mdl-dur-1) var(--zx-mdl-ease), box-shadow var(--zx-mdl-dur-1) var(--zx-mdl-ease);
}
.zx-mdl-wrap .zx-mdl-input:focus, .zx-mdl-wrap .zx-mdl-select:focus, .zx-mdl-wrap .zx-mdl-textarea:focus {
  border-color: var(--zx-mdl-primary);
  box-shadow: 0 0 0 3px var(--zx-mdl-primary-ring);
}
.zx-mdl-wrap .zx-mdl-input::placeholder, .zx-mdl-wrap .zx-mdl-textarea::placeholder { color: var(--zx-mdl-text-3); }
.zx-mdl-wrap .zx-mdl-textarea { resize: vertical; min-height: 84px; }

/* ═══════════════ RESPONSIVE + MOTION ═══════════════ */
@media (max-width: 640px) {
  .zx-mdl-overlay { padding: 12px 8px; }
  .zx-mdl-dialog { border-radius: var(--zx-mdl-r-md); }
  .zx-mdl-head { padding: 12px 15px; }
  .zx-mdl-body { padding: 15px; }
  .zx-mdl-foot { padding: 11px 15px; flex-wrap: wrap; }
  .zx-mdl-panel--right, .zx-mdl-panel--left { width: 100vw; }
}
@media (prefers-reduced-motion: reduce) {
  .zx-mdl-wrap *, .zx-mdl-wrap *::before, .zx-mdl-wrap *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
