/* Shared viewer controls (BPMN, document preview and image lightbox). */
.viewer-toolbar { display:flex; align-items:center; }
.viewer-toolbar--horizontal { flex-direction:row; }
.viewer-toolbar--vertical { flex-direction:column; align-items:stretch; }
.viewer-toolbar--light { overflow:hidden; border-radius:var(--radius-lg);
  background:var(--color-bg); box-shadow:var(--shadow-md); }
.viewer-toolbar--light .viewer-toolbar__button { width:var(--target-min); height:var(--target-min);
  min-height:0; border-radius:0; background:var(--color-bg); }
.viewer-toolbar--light .viewer-toolbar__button + .viewer-toolbar__button { border-top:var(--stroke-1) solid var(--color-secondary-200); }
.viewer-toolbar--negative { gap:var(--sp-1); padding:var(--sp-1); border:var(--stroke-1) solid var(--border-negative);
  border-radius:var(--radius-full); background:var(--color-secondary-900); box-shadow:var(--shadow-viewer); }
.viewer-toolbar__readout { font-variant-numeric:tabular-nums; }
.viewer-toolbar__separator { width:var(--stroke-1); align-self:stretch; background:var(--border-negative); }
/* Icon classes retain their CD size tier; every icon-only overlay control uses
   the same centred square glyph box instead of inheriting inline baselines. */
:is(.viewer-toolbar__button, .docviewer__btn, .pf-lightbox__btn,
  .action-menu__trigger, .modal__close) .icon {
  align-self:center; vertical-align:middle;
}
.viewer-toolbar--light .viewer-toolbar__button:hover { background:var(--color-secondary-50); }
.viewer-toolbar--light .viewer-toolbar__button:active { background:var(--color-secondary-100); }
.viewer-toolbar--negative .viewer-toolbar__button:hover { background:var(--surface-negative-hover); }
.viewer-toolbar--negative .viewer-toolbar__button:active { background:var(--surface-negative-active); }
.viewer-toolbar__button:is(:disabled, [aria-disabled="true"]) { cursor:not-allowed; opacity:.55; }
.viewer-toolbar__button[aria-busy="true"] { cursor:progress; opacity:.7; }
.viewer-toolbar__button[aria-invalid="true"] { color:var(--color-error-400); }
.viewer-toolbar__button[data-empty="true"] { cursor:default; opacity:.55; }
.viewer-toolbar__button:focus-visible { position:relative; z-index:var(--z-local-content);
  outline-offset:calc(-1 * var(--stroke-3)); }

/* Shared dark chrome for the document viewer and portfolio lightbox. These
   rules stay static because the two viewers belong to different lazy apps. */
.docviewer__heading, .pf-lightbox__heading { display:flex; align-items:center; gap:var(--sp-3); min-width:0; }
.docviewer__heading-icon, .pf-lightbox__heading-icon { color:var(--text-negative-muted); flex:none; }
.docviewer__heading-text, .pf-lightbox__heading-text { min-width:0; }
.docviewer__title, .pf-lightbox__title { margin:0; color:var(--color-text-negative); font-weight:var(--fw-bold); font-size:var(--text-base);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.docviewer__sub, .pf-lightbox__sub { margin:0; color:var(--text-negative-muted); font-size:var(--text-xs); }
.docviewer__docnum { white-space:nowrap; }
.docviewer__actions, .pf-lightbox__actions { display:flex; align-items:center; gap:var(--sp-1); flex:none; }
.docviewer__btn, .pf-lightbox__btn { display:inline-flex; align-items:center; justify-content:center;
  width:var(--target-min); height:var(--target-min); padding:0; background:none; border:0; border-radius:var(--radius-sm);
  color:var(--color-text-negative); cursor:pointer; transition:background var(--duration-fast); }
.docviewer__btn:hover, .pf-lightbox__btn:hover { background:var(--surface-negative-hover); }
.docviewer__btn:active, .pf-lightbox__btn:active, .docviewer__btn.is-active { background:var(--surface-negative-active); }
.docviewer__btn:is(:disabled, [aria-disabled="true"]),
.pf-lightbox__btn:is(:disabled, [aria-disabled="true"]) { cursor:not-allowed; opacity:.55; }
.docviewer__btn[aria-busy="true"], .pf-lightbox__btn[aria-busy="true"] { cursor:progress; opacity:.7; }
.docviewer__btn[aria-invalid="true"], .pf-lightbox__btn[aria-invalid="true"] { color:var(--color-error-400); }
.docviewer__btn[data-empty="true"], .pf-lightbox__btn[data-empty="true"] { cursor:default; opacity:.55; }
.docviewer__btn:focus-visible, .pf-lightbox__btn:focus-visible { outline-offset:calc(-1 * var(--stroke-2)); }

/* Shared metadata-panel chrome; app sheets retain only their distinct padding
   and responsive viewer positioning. :where() keeps the shared recipe at zero
   specificity so either viewer can extend it without selector escalation. */
:where(.docviewer__meta, .pf-lightbox__meta) { flex:0 0 auto; overflow-y:auto; max-height:40vh;
  background:var(--surface-negative); color:var(--color-text-negative);
  border-top:var(--stroke-1) solid var(--border-negative); }
:where(.docviewer__meta-title, .pf-lightbox__meta-title) { margin:0 0 var(--sp-2);
  color:var(--text-negative-muted); font-size:var(--text-sm); font-weight:var(--fw-bold); text-transform:none; }
:where(.docviewer__meta, .pf-lightbox__meta) .kv { margin:0; color:var(--color-text-negative); }
:where(.docviewer__meta, .pf-lightbox__meta) .kv dt { color:var(--text-negative-muted); }
:where(.docviewer__meta, .pf-lightbox__meta) .kv dd { color:var(--color-text-negative); }
:where(.docviewer__meta, .pf-lightbox__meta) .btn { margin-top:var(--sp-3); }
@media (min-width:768px){
  :where(.docviewer__meta, .pf-lightbox__meta) { flex:0 0 20rem; max-height:none;
    border-top:0; border-left:var(--stroke-1) solid var(--border-negative); }
}

/* Shared image gallery. The gallery is used by lazy applications and by static
   knowledge pages, so its complete layout belongs in the always-loaded overlay
   layer rather than an application stylesheet. */
.pf-lightbox { position:fixed; inset:0; z-index:var(--z-viewer);
  --lb-top:var(--sp-14); --lb-bottom:0px;
  background:var(--color-secondary-900); }
.pf-lightbox__bar { position:absolute; top:0; left:0; right:0; z-index:var(--z-local-chrome);
  display:flex; align-items:center; justify-content:space-between; gap:var(--sp-4);
  padding:var(--sp-2-5) var(--sp-3); background:var(--color-secondary-800); color:var(--color-text-negative); }
@media (min-width:768px){ .pf-lightbox__bar { padding:var(--sp-2-5) var(--sp-5); } }
.pf-lightbox__btn.is-active { background:var(--surface-negative-active); }
.pf-lightbox__btn:disabled { opacity:.35; cursor:default; }
.pf-lightbox__btn:disabled:hover { background:none; }
.pf-lightbox__body { position:absolute; inset:0; display:flex; flex-direction:column; min-height:0; }
.pf-lightbox__stage { position:relative; flex:1 1 auto; min-width:0; min-height:0; overflow:hidden; }
/* Busy ring while a cold image decodes (code review 2026-08, F-S20): the
   stage is dark, so the shared grey spinner recipe renders as a light ring;
   pointer-events stay live (the chevrons keep working during the load). */
.pf-lightbox__stage[aria-busy="true"]::after { content:''; position:absolute; top:50%; left:50%;
  width:var(--sp-8); height:var(--sp-8); margin:calc(-1 * var(--sp-4)) 0 0 calc(-1 * var(--sp-4));
  border:var(--stroke-3) solid var(--color-secondary-300); border-top-color:transparent;
  border-radius:var(--radius-full); animation:spin var(--duration-spin) linear infinite; }
.pf-lightbox__scroll { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  min-width:0; min-height:0;
  padding:calc(var(--lb-top) + var(--sp-4)) var(--sp-4) calc(var(--lb-bottom) + var(--sp-4));
  scroll-padding:calc(var(--lb-top) + var(--sp-4)) var(--sp-4) calc(var(--lb-bottom) + var(--sp-4)); }
.pf-lightbox__stage.is-zoomed .pf-lightbox__scroll { overflow:auto; align-items:flex-start; justify-content:flex-start; cursor:grab; }
.pf-lightbox__stage.is-zoomed .pf-lightbox__scroll:active { cursor:grabbing; }
.pf-lightbox__scroll:focus-visible,
.pf-lightbox__stage:focus-visible { outline:var(--stroke-2) solid var(--color-focus-ring-negative); outline-offset:calc(-1 * var(--stroke-2)); }
.pf-lightbox__canvas { position:relative; display:flex; align-items:center; justify-content:center;
  width:100%; height:100%; min-width:0; min-height:0; }
.pf-lightbox__stage.is-zoomed .pf-lightbox__canvas { flex:none; width:auto; height:auto; margin:auto; }
.pf-lightbox__stage.is-zoomed .pf-lightbox__img { max-width:none; max-height:none; }
.pf-lightbox__zoom { position:absolute; left:50%; bottom:calc(var(--lb-bottom) + var(--sp-4)); transform:translateX(-50%);
  z-index:var(--z-local-control); gap:var(--sp-1); padding-inline:var(--sp-1-5); }
@media (min-width:768px){ .pf-lightbox__zoom { bottom:calc(var(--lb-bottom) + var(--sp-6)); } }
.pf-lightbox__zoom .pf-lightbox__btn { width:var(--sp-9); height:var(--sp-9); }
@media (max-width:1023.98px), (hover:none){
  .pf-lightbox__zoom .pf-lightbox__btn { width:var(--target-min); height:var(--target-min); }
}
.pf-lightbox__zoom-val { min-width:3.75rem; text-align:center; color:var(--color-text-negative);
  font-size:var(--text-sm); font-variant-numeric:tabular-nums; }
.pf-lightbox__zoom-sep { margin-inline:var(--sp-1); }
.pf-lightbox__meta { padding:calc(var(--lb-top) + var(--sp-4)) var(--sp-5) var(--sp-4); }
@media (min-width:768px){ .pf-lightbox__body { flex-direction:row; } }
.pf-lightbox__img { display:block; max-width:100%; max-height:100%; object-fit:contain; }
.pf-lightbox__nav { position:absolute; top:50%; z-index:var(--z-local-control); transform:translateY(-50%);
  display:inline-flex; align-items:center; justify-content:center; width:var(--target-min); height:var(--target-min);
  border:0; border-radius:var(--radius-full); background:var(--surface-lightbox-control); color:var(--color-text);
  cursor:pointer; transition:background var(--duration-fast); }
.pf-lightbox__nav:hover { background:var(--color-bg); }
.pf-lightbox__nav--prev { left:var(--sp-3); }
.pf-lightbox__nav--next { right:var(--sp-3); }

/* Share dialogs are opened by the gallery and other overlays. */
.share-url { display:flex; flex-wrap:wrap; align-items:center; gap:var(--sp-3); }
@media (min-width:480px){ .share-url { min-width:22rem; } }
@media (min-width:1280px){ .share-url { min-width:24rem; } }
[data-share-url] { width:100%; }

/* ---- Action menu (reusable kebab dropdown) --------------------------------
   Dedicated `.action-menu` namespace: `.menu`/`.menu__item` belong to CD's
   navigation flyout component (menu.postcss) and must not be overridden, or
   the drawer menu flips from column to row. */
.action-menu { position:relative; display:inline-flex; }
/* On dashboards this kebab is the ONLY route to chart export and full screen,
   and measured 32x32 or 40x40 (28 smallTargets hits). The shrinking
   .dash-header override is gone; only border/surface remain. */
/* :not(--labelled) — the bare kebab only. The labelled variant is a real .btn
   and must keep the button's own border, padding and colours. */
.action-menu__trigger:not(.action-menu__trigger--labelled) { display:inline-flex; align-items:center; justify-content:center;
  min-width:var(--control-h); min-height:var(--control-h);
  padding:var(--sp-1); border:0; border-radius:var(--radius-sm); background:none; color:var(--color-text-muted); cursor:pointer; }
.action-menu__trigger:not(.action-menu__trigger--labelled):hover { background:var(--color-secondary-100); color:var(--color-text); }
.action-menu__trigger:not(.action-menu__trigger--labelled):active { background:var(--color-secondary-200); color:var(--color-text); }
.action-menu__trigger:is(:disabled, [aria-disabled="true"]) { cursor:not-allowed; opacity:.55; }
.action-menu__trigger[aria-busy="true"] { cursor:progress; opacity:.7; }
.action-menu__trigger[aria-invalid="true"] { color:var(--color-error-text); }
.action-menu__trigger[data-empty="true"] { cursor:default; color:var(--color-text-muted); }
.dash-header .action-menu__trigger { border:var(--stroke-1) solid var(--color-secondary-200); background:var(--color-bg); }
.dash-header .action-menu__trigger:hover { background:var(--color-secondary-50); }
/* Labelled variant: .btn owns border, padding, colour and hover. Only the
   chevron rotation is added here — the icon-only rules above exclude it rather
   than being fought with overrides. */
.action-menu__trigger--labelled[aria-expanded="true"] .catbar__chev { transform:rotate(180deg); }
.action-menu__popup { position:absolute; top:calc(100% + var(--sp-1-5)); min-width:13rem; z-index:var(--z-nav);
  background:var(--color-bg); border:var(--stroke-1) solid var(--color-secondary-200); border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lg); padding:var(--sp-1-5); }
/* min-width:13rem sits on .action-menu__popup: at 320 the menu sometimes opened
   partly off screen. Clamp it to the viewport (Item 2.29). */
.action-menu__popup { max-width:calc(100vw - var(--sp-6)); }
@media (max-width:479.98px){ .action-menu__popup { min-width:0; width:max-content; } }
.action-menu__popup--end { right:0; }
.action-menu__popup--start { left:0; }
.action-menu__item { display:flex; align-items:center; gap:var(--sp-2-5); width:100%; min-height:var(--target-min); padding:var(--sp-2) var(--sp-2-5); border:0;
  border-radius:var(--radius-sm); background:none; text-align:left; cursor:pointer; color:var(--color-text);
  font-size:var(--text-sm); font-family:inherit; }
.action-menu__item:hover, .action-menu__item:focus-visible { background:var(--color-secondary-50); }
.action-menu__item:active { background:var(--color-secondary-100); }
.action-menu__item:focus-visible { outline-offset:calc(-1 * var(--stroke-2)); }
.action-menu__item:is(:disabled, [aria-disabled="true"]) { cursor:not-allowed; opacity:.55; }
.action-menu__item[aria-busy="true"] { cursor:progress; opacity:.7; }
.action-menu__item[aria-invalid="true"] { color:var(--color-error-text); }
.action-menu__item[data-empty="true"] { cursor:default; color:var(--color-text-muted); }
.action-menu__icon { width:var(--sp-4); height:var(--sp-4); color:var(--color-text-muted); flex:none; }
.action-menu__heading { padding:var(--sp-2) var(--sp-2-5) var(--sp-1); font-size:var(--text-xs); font-weight:var(--fw-bold); color:var(--color-text-muted); }
.action-menu__sep { height:var(--stroke-1); background:var(--color-secondary-100); margin:var(--sp-1-5) var(--sp-1); }

/* ---- Toast: CD toast-message (toast-message.postcss:5-18 + ToastMessage.vue).
   Fixed host at bottom 10%, containing a normal .notification (default:
   success + CheckmarkCircle), max 500px wide. This replaces the former dark
   custom pill; #live (announce) handles the screen-reader announcement. */
.toast__message { position:fixed; left:0; right:0; bottom:10%; z-index:var(--z-toast);
  display:flex; justify-content:center; pointer-events:none;
  opacity:0; transition:opacity var(--duration-slow); }
.toast__message--in { opacity:1; }
.toast__message .notification { max-width:500px; margin-inline:var(--sp-5); pointer-events:auto; }
@media (prefers-reduced-motion: reduce) { .icon--spin { animation:none; } }   /* The token kill switch (tokens.css) covers the toast transition */

/* ---- Chart full screen (overlay) ----------------------------------------- */
/* Scroll lock for every full-screen overlay (chart, modal, image gallery).
   `overflow:hidden` on <body> alone is NOT enough: the document scroller here
   is <html> (app.css:33 sets `overflow-y:scroll` there to prevent layout shift
   from the scrollbar). The page scrollbar therefore stayed active behind the
   overlay: useless in fit state and doubled vertically when zoomed in.
   `scrollbar-gutter:stable` reserves the gutter during locking, or the page
   beneath would jump by the scrollbar width. */
html { scrollbar-gutter:stable; }
html:has(> body.body--overlay-open) { overflow:hidden; }
body.body--overlay-open { overflow:hidden; }
/* Opaque full-screen viewers must reach the physical viewport edge. With
   `stable`, the root gutter stays 15px wide even under overflow:hidden;
   fixed/inset:0 stops before it and leaves an empty white strip on the right.
   Modal dialogs retain the stable gutter so their visible background does not
   jump when opened. */
html:has(> body > .pf-lightbox),
html:has(> body > .docviewer) { overflow:hidden; scrollbar-gutter:auto; }
/* ---- Canonical modal (CD modal.postcss BEM) ------------------------------
   Primitive for new dialogs (confirmations, forms). Existing overlays
   (pf-lightbox, docviewer) remain independent but share the --z-* scale and
   --overlay-scrim token. C.openModal() wires focus trap, Escape, backdrop click
   and focus restoration. */
/* CD modal.postcss:10-35: dialog sits 10vh below the top edge (not centered),
   and scrim is text-900/70. The lighter image scrim reduced the negative focus
   ring to 1.82:1 (WCAG 1.4.11). */
.modal { position:fixed; inset:0; z-index:var(--z-modal); display:flex; align-items:flex-start; justify-content:center; padding:10vh var(--sp-8); }
/* On mobile, 2rem+2rem of framing consumes ~40% of the width. */
@media (max-width:639.98px){ .modal { padding:5vh var(--sp-2); } }
.modal__backdrop { position:absolute; inset:0; background:var(--modal-scrim); }
/* CD anatomy (modal.postcss:31-104): CONTENT is transparent, the header uses
   white text on the scrim, and actual content sits in a white .card within it.
   Previously .modal__content itself was the white box with a divider below the
   title, which does not exist in CD. */
.modal__content { position:relative; z-index:var(--z-local-content); width:min(640px,100%); max-height:80vh;
  display:flex; flex-direction:column; align-items:stretch; }
/* CD's layout="xs" is max-w-screen-xs = 480px. At 340px, .share-url
   (min-width 22rem = 352px) did not fit, causing a horizontal scrollbar. */
/* Size scale = CD screen tokens (modal.postcss:52-70 max-w-screen-*):
   xs 480 · sm 640 · md 768 · lg 1024 · xl 1280. Previously sm (420) < xs (480). */
.modal--xs .modal__content { width:min(480px,100%); }
.modal--sm .modal__content { width:min(640px,100%); }
.modal--md .modal__content { width:min(768px,100%); }
.modal--lg .modal__content { width:min(1024px,100%); }
.modal--xl .modal__content { width:min(1280px,100%); }
/* CD Modal.vue:9-25 + modal.postcss:77-86: the header ALWAYS exists
   (justify-end, close button on the right); only --with-title switches to between. */
.modal__header { display:flex; align-items:flex-start; justify-content:flex-end; gap:var(--sp-4);
  width:100%; margin-bottom:var(--sp-2); color:var(--color-text-negative); }
.modal__header--with-title { justify-content:space-between; }
@media (min-width:1024px){ .modal__header { margin-bottom:var(--sp-4); } }
/* CD's modal title is an h4 = text--lg (Modal.vue:13, typography.postcss:121-124). */
.modal__title { margin:0; font-size:var(--text-lg); font-weight:var(--fw-bold); color:var(--color-text-negative); }
.modal__body { width:100%; overflow:auto; }
/* Chart full screen uses the .modal layer; without this rule, the chart would
   appear there as a bordered shadow card. */
.modal__body .chart { padding:0; box-shadow:none; border:0; }
.modal__body > .card { margin:0; }
.modal__body .card__body { padding:var(--sp-6); }
.modal--xl .modal__body { overflow:visible; }   /* Do not clip the line chart's endpoint label */
/* CD modal.postcss:103-107: p-4 on a WHITE surface with border-t. The footer is
   a sibling of the body on the scrim and provides its own white background. */
.modal__footer { display:flex; align-items:center; justify-content:flex-end; gap:var(--sp-2);
  padding:var(--sp-4); background:var(--color-bg); border-top:var(--stroke-1) solid var(--color-secondary-100); }
/* Optical edge alignment as in CD (modal.postcss:88-97 -mr-3 -mt-1 lg:-mt-2):
   the 44px box overhangs the content edge while the icon sits flush. */
.modal__close { flex:none; display:inline-flex; align-items:center; justify-content:center;
  min-width:var(--target-min); min-height:var(--target-min); padding:var(--sp-1-5); margin:calc(-1 * var(--sp-1)) calc(-1 * var(--sp-3)) 0 0;
  border:0; border-radius:var(--radius-sm);
  background:none; color:var(--color-text-negative); cursor:pointer; }
@media (min-width:1024px){ .modal__close { margin-top:calc(-1 * var(--sp-2)); } }
.modal__close:hover { opacity:.5; }
.modal__close:focus-visible { outline-offset:var(--stroke-2); }

.tabs { display:block; }
.breadcrumb-navigation { width:100%; }
