/* overlays.css: the components ui.js renders on every surface, the Workbench included (docs/DESIGN.md 2.1):
   buttons (5.1), dialogs, confirm and prompt (5.21), the lightbox (5.21), toasts (5.22), tooltips, toggletips,
   popovers and menus (5.23), spinner and busy state (5.27), icons (4.6) and the small form subset the prompt
   dialog needs. Loaded third, after tokens.css and controls.css. It does not depend on ls.css: every rule
   here reads tokens only, so the same markup renders the same on pages that never load ls.css.

   Markup contract (ui.js builds these; the class names are the API):
     <div id="ls-layer" class="ls-ui">            the floating layer, last child of <body>
     <dialog class="modal ls-ui [modal-sm|modal-lg|modal-xl]"> .modal-head / .modal-body / .modal-foot
                                                  (form.modal-form wraps all three when the dialog holds a form)
     <dialog class="modal-media on-dark ls-ui">   the lightbox: .media-bar + .media-stage
     <div class="toast-region ls-ui" popover="manual"> with .toast items
     <div class="tooltip ls-ui" role="tooltip" popover="manual">   one element for every [data-tip]
     <div popover class="menu" role="menu">, <div popover class="popover">
*/

/* ---------- Roots ---------- */
.ls-ui, .ls-ui *, .ls-ui *::before, .ls-ui *::after,
.btn, .btn::after, .menu, .menu *, .popover, .popover *, .tooltip { box-sizing: border-box; }
.ls-ui { font: var(--fw-regular) var(--fs-body)/var(--lh-body) var(--font-body); color: var(--text);
         -webkit-font-smoothing: antialiased; }
/* The floating layer never takes part in the page layout (body is a grid in the Suite, a flex column in the portal). */
#ls-layer { position: absolute; top: 0; left: 0; width: 0; height: 0; }
.ls-ui [hidden], .ls-ui[hidden] { display: none !important; }
.ls-ui .visually-hidden, .ls-ui.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.ls-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
/* Route focus (4.5): after a route change ui.js focuses the view's h1 (or <main>) and marks it data-route-focus, so
   screen readers start there. The heading is not a control, so it shows no ring on any surface. h1 is named so the
   rule outranks a surface's own heading or :focus-visible rules. */
h1[data-route-focus]:focus, [data-route-focus]:focus { outline: none; }

/* ---------- Icons (4.6) ---------- */
.icon {
  width: var(--icon-size); height: var(--icon-size); flex: none; display: inline-block; vertical-align: -.2em;
  fill: none; stroke: currentColor; stroke-width: 1.75px; stroke-linecap: round; stroke-linejoin: round;
}
.icon-sm { --icon-size: 16px; }
.icon-lg { --icon-size: 24px; }
.icon-xl { --icon-size: 32px; stroke-width: 2px; }
.icon-2xl { --icon-size: 48px; stroke-width: 2px; }
.btn .icon { width: 1.25em; height: 1.25em; }

/* ---------- Buttons (5.1) ---------- */
.btn {
  --btn-bg: var(--surface); --btn-fg: var(--text); --btn-border: var(--border-strong);
  --btn-bg-hover: var(--surface-2); --btn-fg-hover: var(--btn-fg); --btn-border-hover: var(--border-hover);
  --btn-bg-active: var(--surface-3);
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-height: var(--btn-h); max-width: 100%; margin: 0; padding: 0 var(--btn-px);
  border: var(--btn-border-w) solid var(--btn-border); border-radius: var(--btn-radius);
  background: var(--btn-bg); color: var(--btn-fg);
  font: var(--fw-bold) var(--fs-btn)/1.2 var(--font-body); letter-spacing: normal; text-align: center; text-decoration: none !important;
  white-space: nowrap; cursor: pointer; user-select: none; -webkit-user-select: none; vertical-align: middle;
  transition: background-color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard);
}
.btn.is-hover:not(:disabled, [aria-disabled="true"], [aria-busy="true"]) {
  background: var(--btn-bg-hover); color: var(--btn-fg-hover); border-color: var(--btn-border-hover);
}
@media (hover: hover) {
  .btn:is(:hover, .is-hover):not(:disabled, [aria-disabled="true"], [aria-busy="true"]) {
    background: var(--btn-bg-hover); color: var(--btn-fg-hover); border-color: var(--btn-border-hover);
    transform: translateY(var(--btn-lift)); box-shadow: var(--btn-hover-shadow);
  }
}
.btn:is(:active, .is-active):not(:disabled, [aria-disabled="true"]) {
  background: var(--btn-bg-active); transform: none; box-shadow: none; transition-duration: var(--dur-instant);
}
.btn:is(:focus-visible, .is-focus) { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; transform: none; box-shadow: none; }
a.btn[aria-disabled="true"] { pointer-events: none; }
.btn[aria-busy="true"] { color: transparent !important; pointer-events: none; }
/* LS.busy also sets disabled (no double submits), but a working button keeps its full color: loading is not disabled */
.btn[aria-busy="true"]:is(:disabled, [aria-disabled="true"]) { opacity: 1; cursor: progress; }
.btn[aria-busy="true"] > * { visibility: hidden; }
.btn[aria-busy="true"]::after {
  content: ""; position: absolute; inset: 0; margin: auto; width: 1.15em; height: 1.15em;
  border: 2px solid var(--btn-fg); border-right-color: transparent; border-radius: 50%;
  animation: ls-spin var(--dur-spin) linear infinite;
}
@keyframes ls-spin { to { transform: rotate(360deg); } }

.btn-primary   { --btn-bg: var(--primary); --btn-fg: var(--primary-contrast); --btn-border: var(--primary);
                 --btn-bg-hover: var(--primary-hover); --btn-border-hover: var(--primary-hover); --btn-bg-active: var(--primary-active); }
/* In the portal, Suite, Workbench and Lab .btn-outline IS the secondary look (the --outline-* tokens default to it);
   public pages (body.site) switch the tokens to a navy outline; navy areas switch them to a white outline. */
.btn-secondary { --btn-bg: var(--surface); --btn-fg: var(--text); --btn-border: var(--border-strong);
                 --btn-bg-hover: var(--surface-2); --btn-border-hover: var(--border-hover); --btn-bg-active: var(--surface-3); }
.btn-outline   { --btn-bg: var(--outline-bg); --btn-fg: var(--outline-fg); --btn-border: var(--outline-border);
                 --btn-border-hover: var(--outline-border-hover); }
.btn-outline-light { --btn-bg: transparent; --btn-fg: var(--white); --btn-border: var(--on-navy-outline);
                 --btn-bg-hover: var(--on-navy-hover); --btn-border-hover: var(--white); --btn-bg-active: var(--on-navy-press); }
.btn-ghost     { --btn-bg: transparent; --btn-border: transparent; --btn-border-hover: transparent;
                 --btn-lift: 0px; --btn-hover-shadow: none; }
.btn-highlight { --btn-bg: var(--highlight); --btn-fg: var(--highlight-contrast); --btn-border: var(--highlight);
                 --btn-bg-hover: var(--highlight-hover); --btn-border-hover: var(--highlight-hover); --btn-bg-active: var(--highlight-active); }
.btn-accent    { --btn-bg: var(--teal-2); --btn-fg: var(--white); --btn-border: var(--teal-2);
                 --btn-bg-hover: var(--teal-3); --btn-border-hover: var(--teal-3); --btn-bg-active: var(--teal-3); }
.btn-danger    { --btn-bg: var(--danger); --btn-fg: var(--white); --btn-border: var(--danger);
                 --btn-bg-hover: var(--danger-hover); --btn-border-hover: var(--danger-hover); --btn-bg-active: var(--danger-hover); }
.btn-danger-ghost { --btn-bg: transparent; --btn-border: transparent; --btn-border-hover: transparent; --btn-fg: var(--danger-ink);
                 --btn-bg-hover: var(--danger-soft); --btn-bg-active: var(--danger-soft); --btn-lift: 0px; --btn-hover-shadow: none; }
/* .btn-link works with or without .btn: an inline text action ("Forgot your password?") in the size of the text around it */
.btn-link      { --btn-bg: transparent; --btn-bg-hover: transparent; --btn-bg-active: transparent; --btn-border: transparent;
                 --btn-border-hover: transparent; --btn-fg: var(--link); --btn-fg-hover: var(--link-hover); --btn-lift: 0px; --btn-hover-shadow: none;
                 display: inline; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: var(--radius-xs);
                 background: transparent; color: var(--link); font: inherit; font-weight: var(--fw-bold); white-space: normal; cursor: pointer;
                 text-align: inherit; text-decoration: underline !important; text-decoration-thickness: 1px !important;
                 text-underline-offset: .2em; vertical-align: baseline; }
.btn-link:is(:hover, .is-hover):not(:disabled, [aria-disabled="true"]) { color: var(--link-hover); text-decoration-thickness: 2px !important; }
.btn-link:disabled, .btn-link[aria-disabled="true"] { opacity: .6; cursor: not-allowed; }

.btn-sm { min-height: var(--btn-h-sm); padding: 0 var(--btn-px-sm); font-size: var(--fs-btn-sm); gap: 6px; }
.btn-lg { min-height: var(--btn-h-lg); padding: 0 var(--btn-px-lg); font-size: var(--fs-btn-lg); }
.btn-block { display: flex; width: 100%; white-space: normal; }
@media (max-width: 479.98px) { .btn { white-space: normal; } }   /* long labels wrap instead of overflowing at 320px */

/* Owner decision (DESIGN.md 1.2 and 5.1): the header "Sign up" button and the portal's "Create an account" button
   must be impossible to miss. A thin gap in the surface color, a gold ring and a wide gold halo that keep gently
   swelling, for as long as the page is open. The gap keeps the ring from reading as a bigger button, and the halo
   never fades below a clear glow. Hover and focus hold it at full strength; with reduced motion it stays still at a
   strong glow. */
.btn-glow { --glow-gap: 0 0 0 2px var(--surface);
            --glow-rest: var(--glow-gap), 0 0 0 5px var(--glow-ring), 0 0 18px 6px var(--glow);
            --glow-full: var(--glow-gap), 0 0 0 6px var(--glow-ring), 0 0 34px 12px var(--glow-peak);
            --btn-hover-shadow: var(--glow-full);
            box-shadow: var(--glow-rest);
            animation: ls-glow var(--dur-glow) var(--ease-in-out) infinite; }
.btn-glow:is(:hover, :focus-visible, .is-hover, .is-focus) { box-shadow: var(--glow-full); animation: none; }
@keyframes ls-glow {
  0%, 100% { box-shadow: var(--glow-rest); }
  50%      { box-shadow: var(--glow-full); }
}
@media (prefers-reduced-motion: reduce) {
  .btn-glow { animation: none !important; box-shadow: var(--glow-gap), 0 0 0 5px var(--glow-ring), 0 0 26px 9px var(--glow-peak); }
}

/* Icon button: a square .btn holding only an icon (aria-label required; data-tip with the same text) */
.btn-icon { width: var(--btn-h); padding: 0; flex: none; }
.btn-icon.btn-sm { width: var(--btn-h-sm); padding: 0; }
.btn-icon.btn-lg { width: var(--btn-h-lg); padding: 0; }
.btn-icon .icon { width: var(--icon-size); height: var(--icon-size); }

/* Button group (attached) */
.btn-group { display: inline-flex; isolation: isolate; }
.btn-group > .btn { border-radius: 0; }
.btn-group > .btn:first-child { border-start-start-radius: var(--btn-radius); border-end-start-radius: var(--btn-radius); }
.btn-group > .btn:last-child  { border-start-end-radius: var(--btn-radius); border-end-end-radius: var(--btn-radius); }
.btn-group > .btn + .btn { margin-inline-start: calc(var(--btn-border-w) * -1); }
.btn-group > .btn:is(:hover, :focus-visible, .is-hover, .is-focus) { z-index: var(--z-raised); }
.btn-group > .btn:is([aria-pressed="true"], .on, .active) {
  --btn-bg: var(--pressed-bg); --btn-fg: var(--pressed-fg); --btn-border: var(--pressed-bg);
  --btn-bg-hover: var(--pressed-bg); --btn-border-hover: var(--pressed-bg); --btn-bg-active: var(--pressed-bg); z-index: var(--z-raised);
}

/* Action rows: dialog and card footers (.actions-end) and page forms (.actions) */
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin-top: var(--field-gap); }
.actions-end { justify-content: flex-end; }
.actions > .spacer { flex: 1; }
@media (max-width: 479.98px) {
  .actions { flex-direction: column; align-items: stretch; }
  .actions-end { flex-direction: column-reverse; }
  .actions > .btn { width: 100%; }
  .actions > .spacer { display: none; }
}

/* ---------- Form subset for the prompt dialog (the same values as ls.css 5.12; zero specificity so each
   component class still wins) ---------- */
:where(.ls-ui) :where(label, .label, legend) {
  display: block; margin: 0 0 var(--space-2); padding: 0;
  font: var(--fw-bold) var(--fs-label)/1.3 var(--font-body); color: var(--text);
}
:where(.ls-ui) :where(.opt) { font-weight: var(--fw-regular); color: var(--faint); }
:where(.ls-ui) :where(.field) { margin: 0 0 var(--field-gap); min-width: 0; }
:where(.ls-ui) :where(.field > :last-child) { margin-bottom: 0; }
:where(.ls-ui) :where(.hint) { margin: var(--space-1) 0 0; font-size: var(--fs-caption); color: var(--faint); line-height: 1.45; }
/* Field errors and the error summary are written by ui.js on every surface, including page forms on pages
   without ls.css (the Workbench), so these two are not limited to .ls-ui. */
:where(.field-error) {
  display: flex; gap: var(--space-2); align-items: flex-start; margin: var(--space-1) 0 0;
  font: var(--fw-semibold) var(--fs-small)/1.4 var(--font-body); color: var(--danger-ink);
}
:where(.field-error .icon) { margin-top: 2px; }
:where(.field-error[hidden]) { display: none; }
/* The error summary (5.12): the ls.css .alert.alert-error look, repeated at zero specificity for pages without ls.css */
:where(.ls-error-summary) {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-3); align-items: start;
  margin: 0 0 var(--space-4); padding: var(--space-3) var(--space-4); border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent);
  border-radius: var(--radius-sm); background: var(--danger-soft); color: var(--text); font-size: var(--fs-small); line-height: 1.5;
}
:where(.ls-error-summary > .icon) { margin-top: 1px; color: var(--danger-ink); }
:where(.ls-error-summary .alert-title) { margin: 0; font-weight: var(--fw-heavy); }
:where(.ls-error-summary a) { color: inherit; font-weight: var(--fw-bold); text-decoration: underline; text-underline-offset: .2em; }
.ls-error-summary ul { margin: var(--space-1) 0 0; padding-left: 1.25em; }   /* beats .list-compact's margin: 0 */
:where(.ls-ui) :where(input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="hidden"]), select, textarea) { width: 100%; }
:where(.ls-ui) :where(textarea:not([rows])) { min-height: calc(var(--control-h) * 2.5); }

/* ---------- Dialogs (5.21) ---------- */
dialog.modal { width: min(var(--modal-w, 600px), calc(100vw - 2 * var(--space-4))); max-width: none;
               max-height: min(calc(100dvh - 2 * var(--space-6)), 920px); margin: auto; padding: 0; border: 0;
               border-radius: var(--radius-lg); background: var(--surface); color: var(--text); box-shadow: var(--shadow-lg); overflow: hidden;
               font: var(--fw-regular) var(--fs-body)/var(--lh-body) var(--font-body); }
dialog.modal[open] { display: flex; flex-direction: column; animation: ls-dialog-in var(--dur-slow) var(--ease-out); }
dialog.modal::backdrop { background: var(--scrim); backdrop-filter: blur(2px); animation: ls-fade-in var(--dur-slow) var(--ease-out); }
dialog.modal:focus { outline: none; }
.modal-form { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; margin: 0; }
.modal-sm { --modal-w: 440px; }  .modal-lg { --modal-w: 880px; }  .modal-xl { --modal-w: 1120px; }
.modal-head { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--modal-pad) var(--modal-pad) var(--space-2); }
.modal-head > div { flex: 1; min-width: 0; }
.modal-head :is(h2, h3) { margin: 0; font: var(--h3-weight) var(--fs-h3)/1.25 var(--h3-family); color: var(--text);
                          font-variation-settings: "SOFT" 0, "WONK" 0; text-wrap: balance; }
.modal-head :is(h2, h3):focus { outline: none; }
.modal-desc { margin: var(--space-1) 0 0; font-size: var(--fs-small); color: var(--muted); }
.modal-close { margin: calc(var(--space-2) * -1) calc(var(--space-2) * -1) 0 0; }
.modal-body { flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain;
              padding: var(--space-2) var(--modal-pad) var(--modal-pad); }     /* top padding: focus rings aren't clipped (3.6) */
.modal-body:first-child { padding-top: var(--modal-pad); }             /* no head above it */
.modal-body > :last-child { margin-bottom: 0; }
.modal-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-3);
              padding: var(--space-4) var(--modal-pad); border-top: 1px solid var(--border); background: var(--surface); }
.modal-foot > .spacer { flex: 1; }
.modal-foot-inline { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-3); margin-top: var(--space-4); }
/* Modal dialogs lock the page (a non-modal chat panel keeps it scrolling). ui.js hands over the width the page's
   scrollbar had (--ls-scrollbar-w) so nothing shifts when it goes. scrollbar-gutter: stable is not used: it left an
   undimmed strip beside the scrim and the lightbox, and added a gutter to pages that had no scrollbar. */
html:has(dialog:modal) { overflow: hidden; padding-inline-end: var(--ls-scrollbar-w, 0px); }

/* Confirm: a tone icon in a 40px soft circle, the question as the title, the message, then [cancel] [ok].
   <dialog class="modal modal-sm modal-confirm ls-ui" role="alertdialog"> .modal-head > .modal-icon + div(h2, p.modal-desc) */
.modal-icon { display: grid; place-items: center; flex: none; width: 40px; height: 40px; border-radius: 50%;
              background: var(--info-soft); color: var(--info-ink); }
.modal-icon.is-danger { background: var(--danger-soft); color: var(--danger-ink); }
.modal-icon.is-warn { background: var(--warning-soft); color: var(--warning-ink); }
.modal-icon.is-ok { background: var(--success-soft); color: var(--success-ink); }
.modal-icon .icon { --icon-size: 20px; }
.modal-confirm .modal-head { padding-bottom: var(--space-5); }
.modal-confirm .modal-head > .modal-icon + div { padding-top: var(--space-1); }
.modal-confirm .modal-foot { padding-top: 0; border-top: 0; }
.modal-confirm .modal-body:empty { display: none; }

@media (max-width: 767.98px) {
  dialog.modal { width: 100%; max-height: 92dvh; margin: auto 0 0; border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
  dialog.modal[open] { animation-name: ls-sheet-in; }
  :is(.modal-foot, .modal-foot-inline) { flex-direction: column-reverse; align-items: stretch; }
  .modal-foot { padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom)); }
  :is(.modal-foot, .modal-foot-inline) > .btn { width: 100%; }
  .modal-foot > .spacer { display: none; }
}
@keyframes ls-dialog-in { from { opacity: 0; transform: translateY(8px) scale(.98); } }
@keyframes ls-sheet-in { from { transform: translateY(100%); } }
@keyframes ls-fade-in { from { opacity: 0; } }

/* ---------- Lightbox (5.21). [open] matters: display:grid on the bare selector would override the browser's
   dialog:not([open]) { display: none } and show a closed lightbox on the page. ---------- */
dialog.modal-media { width: 100vw; height: 100dvh; max-width: none; max-height: none; margin: 0; padding: 0; border: 0;
                     background: color-mix(in srgb, var(--stage-bg) 94%, transparent); color: var(--text); overflow: hidden; }
dialog.modal-media[open] { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; animation: ls-fade-in var(--dur-slow) var(--ease-out); }
dialog.modal-media::backdrop { background: transparent; backdrop-filter: blur(8px); }   /* the 6% of page that shows through is a soft blur, not legible text */
dialog.modal-media:focus { outline: none; }
.media-bar { display: flex; align-items: center; gap: var(--space-3); min-height: 64px; padding: var(--space-3) var(--space-4); }
.media-caption { flex: 1; min-width: 0; margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
                 font: var(--fw-bold) var(--fs-small)/1.3 var(--font-body); color: var(--text); }
.media-count { font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.media-stage { position: relative; display: grid; place-items: center; min-height: 0; padding: 0 var(--space-16) var(--space-6); }
.media-stage img { display: block; max-width: 100%; max-height: calc(100dvh - 120px); width: auto; height: auto; object-fit: contain;
                   border-radius: var(--radius-sm); box-shadow: var(--shadow-lg); }
/* (0,2,0) so a .btn-icon.btn-lg on the same button can't reshape it: always a 48px circle */
.media-stage > :is(.media-prev, .media-next) { position: absolute; top: 50%; translate: 0 -50%; width: 48px; height: 48px; min-height: 48px;
                           padding: 0; border-radius: 50%; --btn-bg: var(--on-navy-hover); }
.media-stage > :is(.media-prev, .media-next) .icon { --icon-size: 24px; }
.media-stage > .media-prev { left: var(--space-3); } .media-stage > .media-next { right: var(--space-3); }
.media-foot { display: flex; justify-content: center; padding: 0 var(--space-4) var(--space-4); }
@media (max-width: 767.98px) { .media-stage { padding: 0 0 var(--space-4); } .media-stage > :is(.media-prev, .media-next) { display: none; } }

/* Zoom toolbar inside the lightbox (5.31, pill mode). quilt-preview.css styles the same class for the preview viewer. */
.viewer-bar { display: flex; align-items: center; gap: 2px; padding: var(--space-1); color: var(--text);
              border-radius: var(--radius-pill); background: var(--viewer-bar-bg); backdrop-filter: blur(8px); box-shadow: var(--shadow-lg); }
@media (min-width: 768px) {
  .viewer-bar { position: absolute; left: 50%; bottom: var(--space-3); z-index: var(--z-overlay-local); transform: translateX(-50%); flex-wrap: nowrap; }
}
.viewer-bar .btn-icon { width: 40px; min-width: 40px; height: 40px; min-height: 40px; padding: 0;
                        border: 0; border-radius: 50%; --btn-bg: transparent; --btn-fg: var(--text); --btn-bg-hover: var(--on-navy-press); }
.viewer-bar .btn-icon:focus-visible { outline-offset: -3px; }
.viewer-sep { flex: none; width: 1px; height: 20px; margin: 0 var(--space-1); background: var(--on-navy-line); }
@media (max-width: 767.98px), (pointer: coarse) {
  .viewer-bar .btn-icon { width: 44px; min-width: 44px; height: 44px; min-height: 44px; }
}

/* ---------- Toasts (5.22). The region is a manual popover: the UA popover styles are reset first. ---------- */
.toast-region { position: fixed; inset: auto auto calc(var(--space-4) + env(safe-area-inset-bottom) + var(--bottom-ui-h) + var(--toast-offset, 0px)) 50%;
                z-index: var(--z-toast); transform: translateX(-50%); margin: 0; padding: 0; border: 0; background: none; overflow: visible;
                display: flex; flex-direction: column; gap: var(--space-2); width: min(440px, calc(100vw - 2 * var(--space-4))); height: auto;
                pointer-events: none; color: var(--toast-fg); font: var(--fw-semibold) var(--fs-small)/1.45 var(--font-body); }
/* :is() is forgiving, so browsers without the Popover API still read these and fall back to .is-open */
.toast-region:not(.is-open):not(:is(:popover-open)) { display: none; }
.toast { pointer-events: auto; display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; gap: var(--space-2) var(--space-3); align-items: center;
         min-height: 48px; padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4); border-radius: var(--radius-sm);
         background: var(--toast-bg); color: var(--toast-fg); box-shadow: var(--shadow-lg);
         font: var(--fw-semibold) var(--fs-small)/1.45 var(--font-body); animation: ls-toast-in var(--dur-base) var(--ease-out); }
.toast:not(:has(> .icon)) { grid-template-columns: minmax(0, 1fr) auto auto; }
.toast-msg { margin: 0; padding-block: var(--space-1); overflow-wrap: anywhere; }
.toast-title { display: block; font-weight: var(--fw-heavy); }
.toast-ok > .icon   { color: var(--toast-ok); }
.toast-info > .icon { color: var(--toast-info); }
.toast-warn > .icon { color: var(--mustard); }
.toast-err { background: var(--toast-err-bg); color: var(--white); }
.toast-err > .icon { color: var(--white); }
.toast-action { all: unset; cursor: pointer; padding: var(--space-1) var(--space-2); border-radius: var(--radius-xs);
                font-weight: var(--fw-heavy); color: var(--mustard); text-decoration: underline; text-underline-offset: .2em; white-space: nowrap; }
.toast-action:is(:hover, .is-hover) { text-decoration-thickness: 2px; }
.toast-err .toast-action { color: var(--white); }          /* mustard on red-2 is only 4.08:1; white is 7.46:1 */
.toast-action:is(:focus-visible, .is-focus) { outline: 2px solid currentColor; outline-offset: 1px; }
.toast .toast-close { --btn-fg: currentColor; --btn-bg-hover: var(--on-navy-press); --btn-bg-active: var(--on-navy-press); --focus-ring: currentColor; }
.toast.is-leaving { animation: ls-toast-out var(--dur-base) var(--ease-in) forwards; }
body.has-chat { --toast-offset: 72px; }      /* clears the chat launcher on phones */
@media (min-width: 768px) { body.has-chat { --toast-offset: 0px; } }
/* Phones: dialogs are bottom sheets, and a toast at the bottom would cover the sheet's buttons, so while a
   dialog is open the region sits at the top of the screen and toasts drop in from above. */
@media (max-width: 767.98px) {
  dialog.modal[open] > .toast-region { top: calc(var(--space-4) + env(safe-area-inset-top)); bottom: auto; }
  dialog.modal[open] > .toast-region .toast { animation-name: ls-toast-in-top; }
  dialog.modal[open] > .toast-region .toast.is-leaving { animation-name: ls-toast-out-top; }
}
@keyframes ls-toast-in { from { opacity: 0; transform: translateY(8px); } }
@keyframes ls-toast-out { to { opacity: 0; transform: translateY(8px); } }
@keyframes ls-toast-in-top { from { opacity: 0; transform: translateY(-8px); } }
@keyframes ls-toast-out-top { to { opacity: 0; transform: translateY(-8px); } }

/* ---------- Tooltip (5.23): one element in the top layer; ui.js positions it and copies the trigger's
   --tooltip-bg and --tooltip-fg onto it. ---------- */
.tooltip { position: fixed; inset: auto; margin: 0; z-index: var(--z-popover); width: max-content; max-width: 240px; height: auto;
           padding: 6px var(--space-2); border: 0; border-radius: var(--radius-xs); background: var(--tooltip-bg); color: var(--tooltip-fg);
           font: var(--fw-semibold) .75rem/1.35 var(--font-body); text-align: center; overflow-wrap: anywhere; box-shadow: var(--shadow);
           pointer-events: none; opacity: 0; transition: opacity var(--dur-fast) var(--ease-standard); }
.tooltip:is(:popover-open, .is-open) { opacity: 1; }
.tooltip:not(.is-open):not(:is(:popover-open)) { display: none; }
@starting-style { .tooltip:is(:popover-open, .is-open) { opacity: 0; } }

/* No-JavaScript fallback only: html gets .ls-js as soon as ui.js runs, which turns this off. */
:where(html:not(.ls-js) [data-tip]) { position: relative; }        /* :where() keeps specificity at 0 so .media-prev etc. win */
html:not(.ls-js) [data-tip]::before {
  content: attr(data-tip); position: absolute; z-index: var(--z-popover); left: 50%; bottom: calc(100% + 8px);
  width: max-content; max-width: 240px; padding: 6px var(--space-2); border-radius: var(--radius-xs);
  background: var(--tooltip-bg); color: var(--tooltip-fg); font: var(--fw-semibold) .75rem/1.35 var(--font-body);
  text-transform: none; letter-spacing: normal; white-space: normal;
  pointer-events: none; opacity: 0; transform: translate(-50%, 4px);
  transition: opacity var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
@media (hover: hover) { html:not(.ls-js) [data-tip]:hover::before { opacity: 1; transform: translate(-50%, 0); transition-delay: var(--dur-slower); } }
html:not(.ls-js) [data-tip]:focus-visible::before { opacity: 1; transform: translate(-50%, 0); }

/* ---------- Toggletip / popover (5.23) ---------- */
.popover { position: fixed; inset: auto; width: max-content; max-width: min(320px, calc(100vw - 2 * var(--space-4))); height: auto;
           margin: 0; padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-sm);
           background: var(--surface); color: var(--text); box-shadow: var(--shadow-lg); overflow: auto;
           font: var(--fw-regular) var(--fs-small)/1.5 var(--font-body); z-index: var(--z-popover); }
.popover p { margin: 0; }
.popover p + p { margin-top: var(--space-2); }
.popover:not(.is-open):not(:is(:popover-open)) { display: none; }

/* ---------- Menu (5.23) ---------- */
.menu { position: fixed; inset: auto; min-width: 200px; max-width: min(320px, calc(100vw - 2 * var(--space-4))); height: auto;
        margin: 0; padding: var(--space-1); border: 1px solid var(--border); border-radius: var(--radius-sm);
        background: var(--surface); color: var(--text); box-shadow: var(--shadow-lg); overflow: auto; z-index: var(--z-popover);
        font: var(--fw-regular) var(--fs-small)/1.4 var(--font-body); }
.menu:not(.is-open):not(:is(:popover-open)) { display: none; }
:is(.menu, .popover).is-open { display: block; }        /* the no-Popover-API fallback (ui.js adds .is-open) */
/* Small enter for menus and toggletips (3.12: dur-base) */
:is(.menu, .popover):is(:popover-open, .is-open) { opacity: 1; transform: none;
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out); }
@starting-style { :is(.menu, .popover):is(:popover-open, .is-open) { opacity: 0; transform: translateY(-4px); } }
.menu-item { display: flex; align-items: center; gap: var(--space-2); width: 100%; min-height: var(--btn-h); margin: 0; padding: 0 var(--space-3);
             border: 0; border-radius: var(--radius-xs); background: none; color: var(--text); text-align: left;
             font: var(--fw-semibold) var(--fs-small)/1.2 var(--font-body); cursor: pointer; text-decoration: none; white-space: nowrap; }
.menu-item .icon { --icon-size: 16px; color: var(--muted); }
.menu-item:is(:hover, .is-hover) { background: var(--surface-2); color: var(--text); text-decoration: none; }
.menu-item:is(:focus-visible, .is-focus) { background: var(--surface-2); outline: 2px solid var(--focus-ring); outline-offset: -2px; }   /* inset: the menu scrolls */
.menu-item.is-danger, .menu-item.is-danger .icon { color: var(--danger-ink); }
.menu-item.is-danger:is(:hover, .is-hover) { background: var(--danger-soft); }
.menu-item:is(:disabled, [aria-disabled="true"]) { opacity: .5; cursor: not-allowed; background: none; }
.menu-item[aria-checked]::after { content: ""; flex: none; width: 16px; height: 16px; margin-left: auto; }
.menu-item[aria-checked="true"]::after {
  background: var(--selected); -webkit-mask: var(--icon-check) center / 16px no-repeat; mask: var(--icon-check) center / 16px no-repeat;
}
.menu-item[aria-checked="true"] { font-weight: var(--fw-bold); }
.menu-sep { height: 1px; margin: var(--space-1) 0; border: 0; background: var(--border); }
.menu-label { margin: 0; padding: var(--space-2) var(--space-3) var(--space-1); font: var(--fw-heavy) var(--fs-overline)/1.2 var(--font-body);
              letter-spacing: var(--ls-caps); text-transform: uppercase; color: var(--faint); }

/* ---------- Spinner and busy state (5.27) ---------- */
.spinner { display: inline-block; flex: none; width: var(--spinner, 20px); height: var(--spinner, 20px); vertical-align: middle;
           border: 2.5px solid var(--track); border-top-color: var(--accent-strong); border-radius: 50%; animation: ls-spin var(--dur-spin) linear infinite; }
.spinner-sm { --spinner: 16px; border-width: 2px; }
.spinner-lg { --spinner: 32px; border-width: 3px; }
.loading-block { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); padding: var(--space-12) var(--space-4);
                 color: var(--muted); font-size: var(--fs-small); text-align: center; animation: ls-fade-in var(--dur-base) var(--delay-reveal) both; }
.loading { opacity: .5; pointer-events: none; }        /* a region that is refreshing */
