diff --git a/web/static/css/custom.css b/web/static/css/custom.css
index 02bc28e7..5e203751 100644
--- a/web/static/css/custom.css
+++ b/web/static/css/custom.css
@@ -474,13 +474,15 @@ footer {
display: none;
}
-/* Global Operation Banner - centered within content-wrapper */
+/* Global Operation Banner - centered within content-wrapper.
+ z-index 100: above page content, below the theme toggle (101), the sidebar (160)
+ and the mobile header (200), so it can never cover the navigation (#216). */
#global-operation-banner {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
- z-index: 9999;
+ z-index: 100;
pointer-events: none;
display: flex;
justify-content: center;
@@ -494,6 +496,28 @@ footer {
pointer-events: auto;
}
+/* Fit mode (#216): the fit check in base.html adds .di-flow when the centred pill
+ would collide with the page title, the action buttons or the theme toggle (narrow
+ windows, high zoom, phones). The banner then becomes a full-width row of the page
+ header, ordered after the buttons, and moves back up as soon as there is room. */
+.page-header > #global-operation-banner.di-flow {
+ position: relative;
+ top: auto;
+ left: auto;
+ right: auto;
+ transform: none;
+ order: 1;
+ flex: 1 1 100%;
+ min-height: 38px;
+ align-items: flex-start;
+}
+
+/* Dashboard/Maintenance start with a light "Updated ... / Refresh" row instead of a
+ card, so in fit mode the space below the pill can be a bit tighter. */
+.content-wrapper:has(#global-operation-banner.di-flow) .stats-header {
+ margin-top: -0.75rem;
+}
+
/* Foreground/background tokens for the pill — dark by default.
Text-legibility tiers (--di-t*) exist because opacity-based text
@@ -1357,6 +1381,18 @@ footer {
border-bottom-color: rgba(0,0,0,0.3);
}
+/* Fit mode: the expanded pill stays an overlay on top of the page content */
+#global-operation-banner.di-flow[data-di-expanded="tier2"] .di-pill,
+#global-operation-banner.di-flow[data-di-expanded="tier3"] .di-pill,
+#global-operation-banner.di-flow .di-pill.di-expanded {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ translate: -50% 0;
+ width: max-content;
+ max-width: min(620px, calc(100vw - 2rem));
+}
+
/* ── Mobile responsive ───────────────────────────────── */
@media (max-width: 768px) {
#global-operation-banner {
@@ -1376,6 +1412,24 @@ footer {
min-width: auto;
}
+ /* The header is a column here: stretch the banner to the full width (the row
+ layout's flex-basis does not apply) so the pill and its overlay get a width. */
+ .page-header > #global-operation-banner.di-flow {
+ flex: 0 0 auto;
+ align-self: stretch;
+ }
+
+ #global-operation-banner.di-flow[data-di-expanded="tier2"] .di-pill,
+ #global-operation-banner.di-flow[data-di-expanded="tier3"] .di-pill,
+ #global-operation-banner.di-flow .di-pill.di-expanded {
+ left: 0;
+ right: 0;
+ translate: none;
+ width: auto;
+ min-width: 0;
+ max-width: none;
+ }
+
.di-pill__detail {
max-height: 150px;
}
diff --git a/web/templates/base.html b/web/templates/base.html
index 8561e1ed..8bfc7e7c 100644
--- a/web/templates/base.html
+++ b/web/templates/base.html
@@ -156,6 +156,71 @@
{% block content %}{% endblock %}
+
+