/* NIOL client webapp — base design system (brand "Onda"). Self-contained: no Bootstrap, no icon framework.
   Tokens come from tokens.css, fonts from fonts.css. This file owns the reset, typography scale, panels,
   controls, and the shell / toast / loading chrome (kept in one sheet for simplicity — no scoped-css split). */

/* ---------- reset-ish ---------- */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
}

body {
    min-height: 100vh;
    background: var(--niol-bg);
    color: var(--niol-text);
    font-family: var(--niol-font-body);
    font-size: var(--niol-fs-body);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

img,
svg {
    max-width: 100%;
    vertical-align: middle;
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--niol-font-display);
    color: var(--niol-text);
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 0 0 var(--niol-space-4);
    line-height: 1.15;
}

h1 { font-size: var(--niol-fs-h1); font-weight: 700; letter-spacing: -0.02em; }
h2 { font-size: var(--niol-fs-h2); }
h3 { font-size: 20px; }

p { margin: 0 0 var(--niol-space-4); }

.lead {
    font-size: var(--niol-fs-lead);
    color: var(--niol-text-muted);
    line-height: 1.55;
}

.text-muted { color: var(--niol-text-muted); }
.text-subtle { color: var(--niol-text-subtle); }

/* ---------- mono / label ---------- */
.mono {
    font-family: var(--niol-font-mono);
    font-variant-numeric: tabular-nums;
}

.label {
    font-family: var(--niol-font-mono);
    font-size: var(--niol-fs-label);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--niol-text-muted);
    display: inline-block;
}

.label--accent { color: var(--niol-accent); }

/* ---------- links ---------- */
a {
    color: var(--niol-accent);
    text-decoration: none;
    transition: color 0.15s ease;
}

a:hover { color: var(--niol-accent-hover); }

/* ---------- panels ---------- */
.niol-panel {
    background: var(--niol-panel);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius);
    padding: var(--niol-space-5);
}

.niol-panel--flush { padding: 0; }

/* ---------- buttons ---------- */
.niol-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--niol-space-2);
    font-family: var(--niol-font-body);
    font-size: 14px;
    font-weight: 600;
    line-height: 1;
    padding: 10px 16px;
    border-radius: var(--niol-radius-sm);
    border: 1px solid transparent;
    background: transparent;
    color: var(--niol-text);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.05s ease;
    text-decoration: none;
}

.niol-btn:active { transform: translateY(1px); }
.niol-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.niol-btn--accent {
    background: var(--niol-accent);
    color: var(--niol-on-accent);
}

.niol-btn--accent:hover {
    background: var(--niol-accent-hover);
    color: var(--niol-on-accent);
}

.niol-btn--ghost {
    border-color: var(--niol-border);
    color: var(--niol-text-muted);
}

.niol-btn--ghost:hover {
    border-color: var(--niol-accent);
    color: var(--niol-text);
}

/* icon-only chrome button */
.niol-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: var(--niol-radius-sm);
    border: 1px solid var(--niol-border);
    background: transparent;
    color: var(--niol-text-muted);
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.niol-icon-btn:hover {
    border-color: var(--niol-accent);
    color: var(--niol-accent);
    background: var(--niol-accent-soft);
}

.niol-icon-btn svg { width: 20px; height: 20px; }

/* keyboard focus — branded accent ring (buttons own theirs; inputs handle focus separately) */
.niol-btn:focus-visible,
.niol-icon-btn:focus-visible,
.niol-menu__trigger:focus-visible,
.niol-menu__item:focus-visible,
.niol-toast__close:focus-visible {
    outline: 2px solid var(--niol-accent);
    outline-offset: 2px;
}

/* ---------- form controls (minimal, brand-styled) ---------- */
.niol-input,
.niol-select {
    font-family: var(--niol-font-body);
    font-size: 14px;
    color: var(--niol-text);
    background: var(--niol-panel-2);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    padding: 9px 12px;
}

.niol-input:focus,
.niol-select:focus {
    outline: none;
    border-color: var(--niol-accent);
}

/* Toggle switch: a real <input type="checkbox"> styled as track + knob (hand-written, no UI library) — state
   reads from the knob's position at a glance, unlike the button-with-its-own-label pattern it replaces (a
   "Sì" button label is ambiguous between "this is the current state" and "click to set it to Sì"). */
.niol-switch {
    position: relative;
    flex: none;
    appearance: none;
    -webkit-appearance: none;
    width: 40px;
    height: 22px;
    margin: 0;
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-pill);
    background: var(--niol-panel-2);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.niol-switch::before {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--niol-text-muted);
    transition: transform 0.15s ease, background 0.15s ease;
}

.niol-switch:checked {
    background: var(--niol-accent);
    border-color: var(--niol-accent);
}

.niol-switch:checked::before {
    background: var(--niol-on-accent);
    transform: translateX(18px);
}

.niol-switch:focus-visible {
    outline: 2px solid var(--niol-accent);
    outline-offset: 2px;
}

.niol-switch:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---------- shell ---------- */
.niol-app {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.niol-header {
    position: sticky;
    top: 0;
    /* Above the click-away backdrop (z 150). The header's backdrop-filter creates a stacking context that traps
       the dropdown panel (z 200) inside the header's own z-index, so at root level the header must outrank the
       backdrop — otherwise the transparent backdrop paints over the open menu and swallows item clicks (the
       "dropdown closes but the action doesn't fire" bug). Toasts (1000) / dialogs (500) stay above. */
    z-index: 200;
    height: var(--niol-header-h);
    display: flex;
    align-items: center;
    gap: var(--niol-space-4);
    padding: 0 var(--niol-space-5);
    background: color-mix(in srgb, var(--niol-bg) 88%, transparent);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--niol-border);
}

.niol-header__lockup {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    min-width: 88px; /* brand min lockup width */
}

/* Brand logo — one image per theme (dark is the default). */
.niol-logo { height: 30px; width: auto; display: block; }
.niol-logo--light { display: none; }
[data-bs-theme="light"] .niol-logo--dark { display: none; }
[data-bs-theme="light"] .niol-logo--light { display: block; }

.niol-header__spacer { flex: 1 1 auto; }

/* NIOL wordmark (Space Grotesk, the "I" carries the single accent) */
.niol-wordmark {
    font-family: var(--niol-font-display);
    font-weight: 700;
    font-size: 22px;
    letter-spacing: -0.02em;
    line-height: 1;
    color: var(--niol-text);
    user-select: none;
}

.niol-wordmark__i { color: var(--niol-accent); }

.niol-header__lockup .niol-mark { flex: none; }
.niol-header__lockup .niol-mark rect { fill: #0C1017; } /* dark chip reads on both themes (brand) */

.niol-header__actions {
    display: flex;
    align-items: center;
    gap: var(--niol-space-3);
}

.niol-content {
    flex: 1 1 auto;
    width: 100%;
    max-width: 1280px;
    margin: 0 auto;
    padding: var(--niol-space-5);
}

/* ---------- dropdown menu (tenant switcher / user menu) ---------- */
.niol-menu { position: relative; }

.niol-menu__panel {
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    min-width: 220px;
    background: var(--niol-panel);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius);
    box-shadow: var(--niol-shadow);
    padding: var(--niol-space-2);
    z-index: 200;
}

.niol-menu__item {
    display: flex;
    align-items: center;
    gap: var(--niol-space-3);
    width: 100%;
    padding: 9px 12px;
    border: none;
    border-radius: var(--niol-radius-sm);
    background: transparent;
    color: var(--niol-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
    text-decoration: none;
}

.niol-menu__item:hover { background: var(--niol-panel-2); color: var(--niol-text); }
.niol-menu__item--active { color: var(--niol-accent); }

.niol-menu__header {
    padding: 8px 12px 6px;
    border-bottom: 1px solid var(--niol-border);
    margin-bottom: var(--niol-space-2);
}

.niol-menu__trigger {
    display: inline-flex;
    align-items: center;
    gap: var(--niol-space-2);
    height: 38px;
    padding: 0 12px;
    border-radius: var(--niol-radius-sm);
    border: 1px solid var(--niol-border);
    background: transparent;
    color: var(--niol-text);
    cursor: pointer;
    font: inherit;
    font-size: 14px;
}

.niol-menu__trigger:hover { border-color: var(--niol-accent); }

/* click-away backdrop for open menus */
.niol-backdrop {
    position: fixed;
    inset: 0;
    z-index: 150;
    background: transparent;
    border: none;
}

/* ---------- toast ---------- */
.niol-toasts {
    position: fixed;
    right: var(--niol-space-5);
    bottom: var(--niol-space-5);
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-3);
    max-width: min(380px, calc(100vw - 2 * var(--niol-space-5)));
}

.niol-toast {
    display: flex;
    align-items: flex-start;
    gap: var(--niol-space-3);
    padding: 12px 14px;
    border-radius: var(--niol-radius);
    border: 1px solid var(--niol-border);
    background: var(--niol-panel);
    box-shadow: var(--niol-shadow);
    animation: niol-toast-in 0.18s ease-out;
}

@keyframes niol-toast-in {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
}

.niol-toast__icon { flex: none; width: 20px; height: 20px; margin-top: 1px; }
.niol-toast__body { flex: 1 1 auto; font-size: 14px; }
.niol-toast__close {
    flex: none;
    border: none;
    background: transparent;
    color: var(--niol-text-subtle);
    cursor: pointer;
    padding: 0;
    line-height: 1;
}
.niol-toast__close:hover { color: var(--niol-text); }

.niol-toast--success { border-left: 3px solid var(--niol-ok); }
.niol-toast--success .niol-toast__icon { color: var(--niol-ok); }
.niol-toast--warning { border-left: 3px solid var(--niol-warning); }
.niol-toast--warning .niol-toast__icon { color: var(--niol-warning); }
.niol-toast--error { border-left: 3px solid var(--niol-alarm); }
.niol-toast--error .niol-toast__icon { color: var(--niol-alarm); }

/* ---------- loading (impulse motif) ---------- */
.niol-loading {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    color: var(--niol-text-muted);
    font-size: 14px;
}

.niol-loading--block {
    justify-content: center;
    width: 100%;
    padding: var(--niol-space-6) 0;
}

.niol-loading__pulse {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.niol-loading__pulse span {
    width: 5px;
    height: 16px;
    border-radius: var(--niol-radius-pill);
    background: var(--niol-accent);
    animation: niol-impulse 1s ease-in-out infinite;
}

.niol-loading__pulse span:nth-child(2) { animation-delay: 0.12s; }
.niol-loading__pulse span:nth-child(3) { animation-delay: 0.24s; }
.niol-loading__pulse span:nth-child(4) { animation-delay: 0.36s; }

@keyframes niol-impulse {
    0%, 100% { transform: scaleY(0.4); opacity: 0.5; }
    50% { transform: scaleY(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .niol-loading__pulse span,
    .niol-toast { animation: none; }
    .niol-chart__hover,
    .niol-chart__axis { transition: none; }
}

/* ---------- chart (hand-rolled SVG line chart) ---------- */
.niol-chart {
    position: relative;
    width: 100%;
    --niol-chart-h: 140px;
}

.niol-chart__svg {
    display: block;
    width: 100%;
    height: var(--niol-chart-h);
}

.niol-chart__line {
    fill: none;
    stroke: var(--niol-accent);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.niol-chart__dot {
    fill: var(--niol-accent);
    stroke: none;
}

.niol-chart__baseline {
    stroke: var(--niol-border);
    stroke-width: 1;
}

.niol-chart__axis {
    position: absolute;
    top: 0;
    right: 0;
    height: var(--niol-chart-h);
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: flex-end;
    padding: 2px 4px;
    font-size: var(--niol-fs-label);
    color: var(--niol-text-subtle);
    pointer-events: none;
    transition: opacity 0.12s ease;
}

/* While hovering, the tooltip states an exact value: the min/max marks step aside instead of sitting
   half-covered by it. */
.niol-chart.is-hovering .niol-chart__axis {
    opacity: 0;
}

/* Hover overlay — positioned by chart-hover.js, never by the circuit. It covers exactly the SVG box, so a
   viewBox coordinate maps to a percentage of this layer. */
.niol-chart__hover {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: var(--niol-chart-h);
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.12s ease;
}

.niol-chart.is-hovering .niol-chart__hover {
    opacity: 1;
    visibility: visible;
}

.niol-chart__crosshair {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--niol-border);
}

.niol-chart__marker {
    position: absolute;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--niol-accent);
    border: 2px solid var(--niol-panel);
    transform: translate(-50%, -50%);
}

.niol-chart__tip {
    position: absolute;
    top: var(--niol-space-2);
    padding: var(--niol-space-2) var(--niol-space-3);
    background: var(--niol-panel-2);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    box-shadow: var(--niol-shadow-sm);
    white-space: nowrap;
}

/* Flipped below when the hovered sample sits in the upper half of the plot. */
.niol-chart__tip--bottom {
    top: auto;
    bottom: var(--niol-space-2);
}

.niol-chart__tip-value {
    font-family: var(--niol-font-mono);
    font-variant-numeric: tabular-nums;
    font-size: 18px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--niol-text);
}

.niol-chart__tip-unit {
    font-size: 12px;
    color: var(--niol-text-muted);
    margin-left: 2px;
}

/* A gap is not a value: it reads as a remark, not as a number. */
.niol-chart__tip-number--gap {
    font-size: 13px;
    font-weight: 400;
    font-style: italic;
    color: var(--niol-text-muted);
}

.niol-chart__tip-time {
    margin-top: 2px;
    font-family: var(--niol-font-mono);
    font-size: var(--niol-fs-label);
    color: var(--niol-text-subtle);
}

/* ---------- widget cards ---------- */
.niol-widget {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-3);
    min-height: 120px;
}

.niol-widget__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--niol-space-3);
}

.niol-widget__title {
    font-family: var(--niol-font-display);
    font-weight: 600;
    font-size: 15px;
    color: var(--niol-text);
}

.niol-widget__unit { color: var(--niol-text-muted); }

.niol-widget__value {
    font-family: var(--niol-font-display);
    font-weight: 700;
    font-size: clamp(28px, 4vw, 40px);
    line-height: 1.05;
    letter-spacing: -0.02em;
    color: var(--niol-text);
    word-break: break-word;
}

/* The state line: the label under the value, in "beside". One step down from the value and one up from the
   meta — the label is secondary to the number, not to the timestamp. The coloured mark keeps its own class
   here, so all six colour modes work on this line with no rule of their own. */
.niol-widget__state {
    font-family: var(--niol-font-display);
    font-weight: 600;
    font-size: clamp(15px, 1.8vw, 19px);
    line-height: 1.25;
    margin-top: var(--niol-space-1);
    color: var(--niol-text);
    word-break: break-word;
}

.niol-widget__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-2);
    font-family: var(--niol-font-mono);
    font-size: var(--niol-fs-label);
    color: var(--niol-text-subtle);
}

.niol-widget__error {
    color: var(--niol-alarm);
    font-size: 13px;
}

.niol-widget__empty { color: var(--niol-text-muted); font-size: 13px; }

/* small inline badges (quality/kind/band/partial) */
.niol-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: var(--niol-radius-pill);
    border: 1px solid var(--niol-border);
    font-family: var(--niol-font-mono);
    font-size: var(--niol-fs-label);
    letter-spacing: 0.04em;
    color: var(--niol-text-muted);
    background: var(--niol-panel-2);
}

.niol-badge--warning {
    border-color: transparent;
    background: var(--niol-warning-soft);
    color: var(--niol-warning);
}

/* Grid cell span from a widget's `size` (the grid container itself is Phase G). On a narrow viewport a
   widget always takes the full row. */
.niol-widget-cell--span-1 { grid-column: span 1; }
.niol-widget-cell--span-2 { grid-column: span 2; }
.niol-widget-cell--span-3 { grid-column: span 3; }

@media (max-width: 640px) {
    .niol-widget-cell--span-1,
    .niol-widget-cell--span-2,
    .niol-widget-cell--span-3 { grid-column: 1 / -1; }
}

/* ---------- dashboard (Phase G view) ---------- */
/* The grid container the widget cells live in: three tracks on desktop, two on tablet, one on mobile — the
   cells carry their own span class, clamped by the track count. */
.niol-dashboard-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--niol-space-4);
    align-items: start;
}

@media (max-width: 960px) {
    .niol-dashboard-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
    .niol-dashboard-grid { grid-template-columns: 1fr; }
}

.niol-dashboard__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-3);
    margin-bottom: var(--niol-space-5);
    min-height: 40px;
}

/* The title block must be allowed to shrink (min-width: 0) or its heading refuses to wrap and pushes the
   controls out of the bar; wrapping the bar itself then drops the controls to their own line when they no
   longer fit beside it. */
.niol-dashboard__bar > :first-child { min-width: 0; }

.niol-dashboard__title { margin: 0; overflow-wrap: break-word; }

/* The bar's trailing controls (search, primary action), grouped so they wrap as ONE unit onto the next line
   instead of the search leaving the button stranded. margin-left: auto replaces the spacer div when they sit
   on the same line, and is harmless once they wrap. */
.niol-dashboard__bar-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-3);
    margin-left: auto;
}

/* dashboard switcher tabs */
.niol-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--niol-space-2);
}

.niol-tab {
    font-family: var(--niol-font-body);
    font-size: 14px;
    font-weight: 600;
    line-height: 1;
    padding: 9px 14px;
    border-radius: var(--niol-radius-pill);
    border: 1px solid var(--niol-border);
    background: transparent;
    color: var(--niol-text-muted);
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.niol-tab:hover { border-color: var(--niol-accent); color: var(--niol-text); }

.niol-tab--active {
    border-color: var(--niol-accent);
    background: var(--niol-accent-soft);
    color: var(--niol-accent);
}

.niol-tab:focus-visible {
    outline: 2px solid var(--niol-accent);
    outline-offset: 2px;
}

/* system dashboards + the per-user order (design 2026-07-30) */

/* Marks a dashboard published to the whole tenant, in the tab strip and in the order list. */
.niol-tab__badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 8px;
    padding: 2px 8px;
    border-radius: var(--niol-radius-pill);
    border: 1px solid var(--niol-border);
    background: var(--niol-panel-2);
    color: var(--niol-text-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    vertical-align: middle;
}

.niol-tab--active .niol-tab__badge {
    border-color: var(--niol-accent);
    color: var(--niol-accent);
}

/* The two NAMED order scopes ("my order" / "default order"), not one command that changes meaning. */
.niol-segmented {
    display: inline-flex;
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-pill);
    overflow: hidden;
}

.niol-segmented button {
    font-family: var(--niol-font-body);
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
    padding: 8px 14px;
    border: 0;
    background: transparent;
    color: var(--niol-text-muted);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.niol-segmented button:hover { color: var(--niol-text); }

.niol-segmented button.is-active {
    background: var(--niol-accent-soft);
    color: var(--niol-accent);
}

.niol-segmented button:focus-visible {
    outline: 2px solid var(--niol-accent);
    outline-offset: -2px;
}

.niol-order {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-3);
    width: 100%;
}

.niol-hint {
    margin: 0;
    font-size: 13px;
    color: var(--niol-text-muted);
}

.niol-order-list {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.niol-order-list li {
    display: flex;
    align-items: center;
    gap: var(--niol-space-2);
    padding: 8px 12px;
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    background: var(--niol-panel-2);
}

/* The label takes the slack so the two arrows stay pinned right, whatever the name's length. */
.niol-order-list__label {
    flex: 1 1 auto;
    font-weight: 600;
}

.niol-order__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--niol-space-2);
}

/* Inline notice above a panel — "what you edit here, everyone sees". */
.niol-banner {
    display: flex;
    align-items: center;
    gap: var(--niol-space-2);
    padding: 10px 14px;
    margin-bottom: var(--niol-space-3);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    font-size: 14px;
    color: var(--niol-text-muted);
}

.niol-banner--warn {
    border-color: var(--niol-warning);
    background: var(--niol-warning-soft);
    color: var(--niol-text);
}

.niol-banner--warn svg { color: var(--niol-warning); }

/* empty / call-to-action state */
.niol-empty {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--niol-space-3);
    text-align: left;
}

.niol-empty h1,
.niol-empty h2 { margin: 0; }

/* ---------- edit mode (Task G2 arranger) ---------- */
.niol-dashboard__bar-spacer { flex: 1 1 auto; }

/* danger button variants (dashboard delete / widget remove) */
.niol-btn--danger {
    background: var(--niol-alarm);
    color: #fff;
}
.niol-btn--danger:hover { background: color-mix(in srgb, var(--niol-alarm) 85%, #000); color: #fff; }

.niol-btn--danger-ghost { border-color: var(--niol-border); color: var(--niol-text-muted); }
.niol-btn--danger-ghost:hover {
    border-color: var(--niol-alarm);
    color: var(--niol-alarm);
    background: var(--niol-alarm-soft);
}

/* the dashboard-level edit bar (rename / new / delete / add widget) */
.niol-edit-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-3);
    margin-bottom: var(--niol-space-5);
}

.niol-edit-bar__group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-2);
}

.niol-edit-bar__spacer { flex: 1 1 auto; }

.niol-inline-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-2);
}

/* an editable widget cell: toolbar on top, the widget (or hidden placeholder) below */
.niol-edit-cell {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-2);
    padding: var(--niol-space-2);
    border: 1px dashed var(--niol-border);
    border-radius: var(--niol-radius);
    background: var(--niol-panel-2);
}

.niol-edit-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--niol-space-2);
    flex-wrap: wrap;
}

.niol-edit-toolbar__group {
    display: flex;
    align-items: center;
    gap: var(--niol-space-1);
}

.niol-edit-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-height: 30px;
    padding: 4px 8px;
    border-radius: var(--niol-radius-sm);
    border: 1px solid var(--niol-border);
    background: var(--niol-panel);
    color: var(--niol-text-muted);
    font: inherit;
    font-size: 12px;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.niol-edit-btn:hover:not(:disabled) { border-color: var(--niol-accent); color: var(--niol-accent); }
.niol-edit-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.niol-edit-btn:focus-visible { outline: 2px solid var(--niol-accent); outline-offset: 2px; }

.niol-edit-btn--danger:hover:not(:disabled) {
    border-color: var(--niol-alarm);
    color: var(--niol-alarm);
    background: var(--niol-alarm-soft);
}

.niol-edit-btn__text { letter-spacing: 0.02em; }
.niol-edit-confirm { font-size: 12px; color: var(--niol-text-muted); }

/* a hidden widget's recoverable placeholder (edit mode only) */
.niol-widget-placeholder {
    display: flex;
    align-items: center;
    gap: var(--niol-space-3);
    min-height: 120px;
    color: var(--niol-text-subtle);
    border-style: dashed;
}

/* ---------- add-widget dialog ---------- */
.niol-dialog-backdrop {
    position: fixed;
    inset: 0;
    z-index: 500;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: var(--niol-space-6) var(--niol-space-4);
    overflow-y: auto;
    background: rgba(4, 7, 12, 0.55);
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
}

.niol-dialog {
    width: 100%;
    max-width: 520px;
    background: var(--niol-panel);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius);
    box-shadow: var(--niol-shadow);
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 2 * var(--niol-space-6));
}

.niol-dialog__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--niol-space-3);
    padding: var(--niol-space-4) var(--niol-space-5);
    border-bottom: 1px solid var(--niol-border);
}

.niol-dialog__title { margin: 0; font-size: 20px; }

.niol-dialog__body {
    padding: var(--niol-space-5);
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-5);
    overflow-y: auto;
}

.niol-dialog__foot {
    display: flex;
    justify-content: flex-end;
    gap: var(--niol-space-2);
    /* Clearance from the last field above the divider (review fix — it read as glued to it). Bigger than the
       padding below, so the rule reads as belonging to the actions it introduces, not floating between the two. */
    margin-top: var(--niol-space-5);
    padding: var(--niol-space-4) var(--niol-space-5);
    border-top: 1px solid var(--niol-border);
}

.niol-field {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-2);
    /* More space ABOVE the label than the gap below it (between the label and its own control) — otherwise a
       label reads as glued to the previous field's control instead of belonging to its own (review fix). A
       shared rule, not a per-panel one: it self-corrects regardless of whether the host supplies its own gap
       (the config detail panel does not; the offcanvas/dialog bodies already do — this only adds to those). */
    padding-top: var(--niol-space-4);
}

/* The acquisition switch's emphasis (decision: one switch size everywhere, see .niol-switch below) — moved
   from a larger control onto the field's own container: a neutral raised card and a bolder label mark this as
   the panel's important field WITHOUT tinting it with the switch's own on/off colour (review fix — the accent
   card duplicated the switch's "on" state, and stayed accent-coloured even when the switch read off). */
.niol-field.niol-field--emphasis {
    /* Outer rhythm as margin, not padding, so it doesn't stack on top of the card's own internal padding
       (review fix — the old padding-top calc() made the card internally lopsided). */
    margin-top: var(--niol-space-4);
    padding: var(--niol-space-3) var(--niol-space-4);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    /* --niol-panel, not --niol-panel-2: in light theme panel-2 equals the page bg, so on a form that sits
       directly on the page (no .niol-panel wrapper, e.g. the config detail column) a panel-2 fill would be
       invisible — only the border would carry it. --niol-panel is the app's actual elevated-surface token,
       distinct from --niol-bg in both themes. */
    background: var(--niol-panel);
}

.niol-field--emphasis > label { font-weight: 700; color: var(--niol-text); }

.niol-choice-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--niol-space-2);
}

.niol-choice {
    font-family: var(--niol-font-body);
    font-size: 13px;
    font-weight: 600;
    padding: 8px 14px;
    border-radius: var(--niol-radius-pill);
    border: 1px solid var(--niol-border);
    background: transparent;
    color: var(--niol-text-muted);
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.niol-choice:hover { border-color: var(--niol-accent); color: var(--niol-text); }
.niol-choice:focus-visible { outline: 2px solid var(--niol-accent); outline-offset: 2px; }

.niol-choice--active {
    border-color: var(--niol-accent);
    background: var(--niol-accent-soft);
    color: var(--niol-accent);
}

.niol-search {
    display: flex;
    align-items: center;
    gap: var(--niol-space-2);
    padding: 0 10px;
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    background: var(--niol-panel-2);
    color: var(--niol-text-subtle);
}

.niol-search:focus-within { border-color: var(--niol-accent); }

.niol-search__input {
    flex: 1 1 auto;
    border: none;
    background: transparent;
    padding: 9px 0;
}

.niol-search__input:focus { outline: none; border: none; }

.niol-picker {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 260px;
    overflow-y: auto;
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    padding: var(--niol-space-2);
}

.niol-picker__item {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px 10px;
    border: 1px solid transparent;
    border-radius: var(--niol-radius-sm);
    background: transparent;
    color: var(--niol-text);
    text-align: left;
    font: inherit;
    cursor: pointer;
}

.niol-picker__item:hover { background: var(--niol-panel-2); }
.niol-picker__item:focus-visible { outline: 2px solid var(--niol-accent); outline-offset: -2px; }

.niol-picker__item--active {
    border-color: var(--niol-accent);
    background: var(--niol-accent-soft);
}

.niol-picker__label { font-weight: 600; font-size: 14px; }
.niol-picker__meta { font-size: var(--niol-fs-label); color: var(--niol-text-subtle); }

/* ---------- form kit + offcanvas (slice 60 s2) ---------- */
.niol-field__hint { font-size: 13px; color: var(--niol-text-subtle); }
.niol-field__error { font-size: 13px; color: var(--niol-alarm); }

.niol-input--invalid { border-color: var(--niol-alarm); }

/* the export panel's kpi-units list (Task 10): one row per definition, no bullets. */
.niol-plain-list {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-2);
    list-style: none;
    margin: 0;
    padding: 0;
}

.niol-plain-list li { display: flex; align-items: center; gap: var(--niol-space-2); font-size: 14px; }
.niol-plain-list svg { flex: none; color: var(--niol-text-subtle); }

.niol-input--block,
.niol-select--block { width: 100%; }

.niol-offcanvas-backdrop {
    position: fixed;
    inset: 0;
    z-index: 500;
    display: flex;
    justify-content: flex-end;
    background: rgba(4, 7, 12, 0.55);
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
}

.niol-offcanvas {
    width: min(560px, 100vw);
    max-width: 100%;
    height: 100vh;
    background: var(--niol-panel);
    border-left: 1px solid var(--niol-border);
    box-shadow: var(--niol-shadow);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.niol-offcanvas__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--niol-space-3);
    padding: var(--niol-space-4) var(--niol-space-5);
    border-bottom: 1px solid var(--niol-border);
}

.niol-offcanvas__title { margin: 0; font-size: 20px; }

.niol-offcanvas__body {
    flex: 1 1 auto;
    padding: var(--niol-space-5);
    display: flex;
    flex-direction: column;
    /* No own gap: .niol-field's padding-top is the single rhythm source (KpiEditor is the only content this
       body ever hosts, and its fields are all .niol-field) — a gap here on top of that padding was the
       "too airy" duplication (review fix). Its non-field children (the server-error line, the foot bar)
       already carry their own spacing: the foot bar its border-top + padding, matching how they read in the
       config detail panel, which supplies no host gap either. */
    overflow-y: auto;
    overflow-x: hidden;
    min-width: 0;
    /* a long unbroken string (e.g. a slug) wraps instead of being clipped by overflow-x: hidden */
    overflow-wrap: break-word;
}

.niol-offcanvas__foot {
    display: flex;
    justify-content: flex-end;
    gap: var(--niol-space-2);
    padding: var(--niol-space-4) var(--niol-space-5);
    border-top: 1px solid var(--niol-border);
}

@media (max-width: 640px) {
    .niol-offcanvas { width: 100vw; }
}

/* generic searchable list (KPI list, and any future config list) */
.niol-list {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-2);
}

.niol-list__item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-3);
    padding: var(--niol-space-3) var(--niol-space-4);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    background: var(--niol-panel);
}

/* a retired row: present but visibly inert — it computes nothing until it is brought back */
.niol-list__item--retired {
    border-style: dashed;
    background: var(--niol-panel-2);
}

.niol-list__item--retired .niol-list__main { color: var(--niol-text-muted); }

/* the permanent-delete panel, a full-width third child of the row it belongs to */
.niol-purge {
    flex: 1 1 100%;
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-3);
    padding: var(--niol-space-3);
    border: 1px solid var(--niol-alarm);
    border-radius: var(--niol-radius-sm);
    background: var(--niol-alarm-soft);
}

.niol-purge__warning {
    display: flex;
    align-items: flex-start;
    gap: var(--niol-space-2);
    margin: 0;
    font-size: 13px;
    color: var(--niol-text);
}

.niol-purge__warning svg { flex: none; color: var(--niol-alarm); }

.niol-purge__confirm {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-2);
}

/* the slug field takes the room, the two buttons keep their own */
.niol-purge__confirm .niol-input { flex: 1 1 200px; min-width: 0; }

.niol-icon-btn--danger:hover {
    border-color: var(--niol-alarm);
    color: var(--niol-alarm);
    background: var(--niol-alarm-soft);
}

/* the "show retired" toggle while it is on */
.niol-btn--ghost-active {
    border-color: var(--niol-accent);
    background: var(--niol-accent-soft);
    color: var(--niol-accent);
}

.niol-list__main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.niol-list__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-3);
    font-size: var(--niol-fs-label);
    color: var(--niol-text-subtle);
}

.niol-list__actions {
    display: flex;
    align-items: center;
    gap: var(--niol-space-1);
    flex: none;
}

/* ---------- configuration navigator (Task 11) ---------- */
/* the two-column shell: nav panel + detail. Collapsed drops to one column, detail taking the full width
   (design 2026-07-27 §7.1 — the desktop "comparsa" panel). */
.niol-config {
    display: grid;
    grid-template-columns: minmax(240px, 320px) 1fr;
    gap: var(--niol-space-5);
    align-items: start;
}

/* DESKTOP-ONLY (review fix round 2): both rules are meaningless on a viewport that is already one column,
   and un-scoped they out-specificity the mobile `.niol-config__nav--open` rule below (a two-class selector
   beats a one-class selector regardless of media query), which made the mobile overlay impossible to open —
   ToggleNavPanel flips navOpen and mobileNavOpen together, so the very first click set both classes at once.
   901px matches the mobile rule's `max-width: 900px` exactly — no gap, no overlap. */
@media (min-width: 901px) {
    .niol-config--nav-collapsed { grid-template-columns: 1fr; }

    .niol-config--nav-collapsed .niol-config__nav { display: none; }
}

.niol-config__nav {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-3);
    /* bounded height (Task 12) so a long tree scrolls INSIDE the panel via .niol-tree's overflow-y, instead of
       growing the whole page — sticky under the header, capped short of the viewport (header + content/panel
       padding + the page's own bar). */
    position: sticky;
    top: var(--niol-space-5);
    max-height: calc(100vh - var(--niol-header-h) - 220px);
}

.niol-config__nav-close { display: none; }

/* click-away backdrop for the mobile overlay (Task 12); a no-op on desktop, where it is never rendered. */
.niol-config__nav-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 399;
    background: transparent;
    border: none;
}

/* the navigator's own toolbar: "show retired" + the one root-level add, "+ plant" (plants are the only
   type added from the tree itself — sources/signals are added from their parent's detail panel, Task 12). */
.niol-tree__toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--niol-space-2);
}

.niol-tree {
    display: flex;
    flex-direction: column;
    gap: 2px;
    overflow-y: auto;
    /* shrinks inside .niol-config__nav's bounded height instead of pushing it taller — the toolbar above stays
       put and only the row list scrolls (Task 12). */
    flex: 1 1 auto;
    min-height: 0;
}

.niol-tree__empty { padding: var(--niol-space-3) 0; }

/* a <div role="treeitem"> (review fix round 2), not a <button> — the caret can't nest inside a real button.
   font-family/font-size are a deliberate smaller type scale for tree rows, not a button-chrome reset: a div
   needs no border/background/text-align reset a <button>'s UA stylesheet would have forced. */
.niol-tree__node {
    display: flex;
    align-items: center;
    gap: var(--niol-space-2);
    width: 100%;
    padding-block: var(--niol-space-2);
    padding-inline-end: var(--niol-space-2);
    /* indentation comes from the --tree-depth custom property the markup sets — no per-level class */
    padding-inline-start: calc(0.5rem + var(--tree-depth) * 0.9rem);
    border-radius: var(--niol-radius-sm);
    color: var(--niol-text);
    font-family: var(--niol-font-body);
    font-size: 14px;
    cursor: pointer;
}

.niol-tree__node:hover { background: var(--niol-panel-2); }

.niol-tree__node--active {
    background: var(--niol-accent-soft);
    color: var(--niol-accent);
}

.niol-tree__node--retired { color: var(--niol-text-muted); font-style: italic; }

.niol-tree__node:focus-visible { outline: 2px solid var(--niol-accent); outline-offset: -2px; }

/* a real <button> (keyboard-reachable, review fix round 1) — reset its default browser chrome */
.niol-tree__caret {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 18px;
    height: 18px;
    padding: 0;
    border: none;
    border-radius: var(--niol-radius-sm);
    background: transparent;
    appearance: none;
    color: var(--niol-text-muted);
    cursor: pointer;
    transition: transform 0.15s ease;
}

.niol-tree__caret:hover { color: var(--niol-text); }

.niol-tree__caret:focus-visible { outline: 2px solid var(--niol-accent); outline-offset: 1px; }

.niol-tree__caret--open { transform: rotate(90deg); }

/* Label wins the row's space: flex-shrink: 0 pins it at its own content width, so it is never the one
   squeezed by a long meta (the reported bug — nodeId strings like "ns=2;s=Target01.CONTEGGIO..." used to
   push the label out). Kept shrinkable via ellipsis only as a last-resort fallback for a panel too narrow
   even once .niol-tree__meta has given up all its space (min-width: 0 below lets it go all the way to 0). */
.niol-tree__label {
    flex: 1 1 auto;
    flex-shrink: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.niol-tree__meta {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--niol-fs-label);
    color: var(--niol-text-subtle);
}

/* the type icon: a fixed footprint so it never shrinks as the row's label grows — same class of fix as the
   caret above (flex: none), needed here too because a plain <svg> defaults to flex-shrink: 1 and was the
   next thing squeezed once .niol-tree__meta had given up all its space (the reported bug). */
.niol-tree__node > svg {
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
}

/* the "config not yet published" badge — same tokens as the warning badge, its own class because it means
   something narrower (an agent's authored config outran what got published). */
.niol-badge--pending {
    padding: 2px;
    border-color: transparent;
    background: var(--niol-warning-soft);
    color: var(--niol-warning);
}

@media (max-width: 900px) {
    .niol-config { grid-template-columns: 1fr; }

    /* the same nav becomes a full-page overlay, closed by default — one component, two presentations
       (design 2026-07-27 §7.1, pinned decision 3). */
    .niol-config__nav {
        display: none;
        position: fixed;
        inset: 0;
        z-index: 400;
        flex-direction: column;
        gap: var(--niol-space-3);
        padding: var(--niol-space-4);
        background: var(--niol-bg);
        overflow-y: auto;
        /* the fixed inset already bounds it — no sticky/max-height carried over from the desktop rule */
        max-height: none;
    }

    .niol-config__nav--open { display: flex; }

    /* the mobile overlay's dismiss path (Task 12 carried-over constraint 3): an explicit close button, plus
       the backdrop rendered behind it in the markup. */
    .niol-config__nav-close {
        display: inline-flex;
        align-self: flex-end;
    }

    .niol-config__nav-backdrop { display: block; }
}

/* inline informational callout for a detail panel (the pending-config badge, a source with no agent) —
   same shape as .niol-purge__warning but standalone, not tied to a row (Task 12). */
.niol-notice {
    display: flex;
    align-items: flex-start;
    gap: var(--niol-space-2);
    padding: var(--niol-space-3);
    border: 1px solid var(--niol-warning);
    border-radius: var(--niol-radius-sm);
    background: var(--niol-warning-soft);
    font-size: 13px;
    color: var(--niol-text);
}

.niol-notice svg { flex: none; color: var(--niol-warning); }

/* One band as a CARD, not a row. Four controls on a single line left each of them unusably narrow inside the
   offcanvas, and unreadable on a phone; a KPI carries 2-4 bands, so the vertical cost is small and the fields
   get room. The fields grid reflows on its own: as many ~200px columns as fit, one per line when none do. */
.niol-band-card {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-3);
    padding: var(--niol-space-3);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius);
    background: var(--niol-panel-2);
}

.niol-band-card__head {
    display: flex;
    align-items: center;
    gap: var(--niol-space-2);
}

.niol-band-card__spacer { flex: 1 1 auto; }

.niol-band-card__fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--niol-space-3);
}

/* no track may exceed its share: without this the number/text inputs' intrinsic width wins and the card
   overflows the offcanvas, whose overflow-x: hidden then clips instead of scrolling */
.niol-band-card__fields > * { min-width: 0; }

/* a field that takes the whole row wherever it lands (the band caption) */
.niol-band-card__wide { grid-column: 1 / -1; }

/* the native colour picker beside the free-text field (a named colour is legal, the picker cannot type one) */
.niol-band-color {
    display: flex;
    align-items: center;
    gap: var(--niol-space-2);
    min-width: 0;
}

.niol-color-input {
    flex: none;
    width: 38px;
    height: 38px;
    padding: 2px;
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    background: var(--niol-panel-2);
    cursor: pointer;
}

/* The browser paints the colour chip inside the input as a hard square whatever the element's radius is, so
   it has to be rounded through the vendor pseudo-elements — otherwise it reads as a different shape from the
   swatch in the card head, sitting a few pixels away. */
.niol-color-input::-webkit-color-swatch-wrapper { padding: 0; }

.niol-color-input::-webkit-color-swatch {
    border: none;
    border-radius: calc(var(--niol-radius-sm) - 2px);
}

.niol-color-input::-moz-color-swatch {
    border: none;
    border-radius: calc(var(--niol-radius-sm) - 2px);
}

.niol-band-swatch {
    width: 24px;
    height: 24px;
    border-radius: var(--niol-radius-sm);
    border: 1px solid var(--niol-border);
    flex: none;
}

/* the section bar (dashboard / kpi / …), sits between the header lockup and the spacer */
.niol-nav {
    display: flex;
    align-items: center;
    gap: var(--niol-space-2);
}

.niol-nav__link {
    display: inline-flex;
    align-items: center;
    gap: var(--niol-space-2);
    height: 38px;
    padding: 0 12px;
    border-radius: var(--niol-radius-sm);
    color: var(--niol-text-muted);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    transition: color 0.15s ease, background 0.15s ease;
}

.niol-nav__link:hover { color: var(--niol-text); background: var(--niol-panel-2); }
.niol-nav__link:focus-visible { outline: 2px solid var(--niol-accent); outline-offset: 2px; }

.niol-nav__link--active {
    color: var(--niol-accent);
    background: var(--niol-accent-soft);
}

/* ---------- state colouring on a value (design 2026-07-29) ---------- */
/* The classes and the two custom properties (--niol-color and its contrast-picked --niol-color-text,
   emitted by WidgetColor.Decide behind KpiBands.IsSafeCssColor) sit on the WIDGET CARD, .niol-widget.
   That is one attachment point for two families of mode: three of them colour the whole card (widget,
   edge, edgeTop) and three reach only the value's span through a descendant selector (pill, value, dot).
   When nothing matched there is no style attribute at all, every var() falls back, and the widget renders
   exactly as it did before this feature existed — which is also what an uncoloured --pill does. */

.niol-color {
    /* Fallbacks, so a card carrying a mode class but NO colour is simply a normal widget. */
    --niol-color: transparent;
    --niol-color-text: inherit;
}

.niol-color__mark {
    display: inline-block;
}

/* ---- the three that colour the value ---- */

/* pill — the value in a filled chip (the default, and the pre-existing look) */
.niol-color--pill .niol-color__mark {
    padding: 0.08em 0.4em;
    border-radius: var(--niol-radius-sm);
    background: var(--niol-color);
    color: var(--niol-color-text);
}

/* value — the digits themselves take the colour, nothing else */
.niol-color--value .niol-color__mark {
    color: var(--niol-color);
}

/* dot — a small disc before the value */
.niol-color--dot .niol-color__mark {
    position: relative;   /* the radiating ring below is positioned against it */
}
.niol-color--dot .niol-color__mark::before {
    content: "";
    display: inline-block;
    width: 0.5em;
    height: 0.5em;
    margin-inline-end: 0.35em;
    border-radius: 50%;
    background: var(--niol-color);
    vertical-align: middle;
}

/* ---- the three that colour the whole card ---- */

/* widget — a soft wash over the entire panel; the text stays the normal one, so contrast never depends
   on the authored colour being light or dark */
.niol-color--widget {
    background: color-mix(in srgb, var(--niol-color) 14%, var(--niol-panel));
    border-color: color-mix(in srgb, var(--niol-color) 35%, var(--niol-border));
}

/* edge / edgeTop — a bar along one side of the CARD. An inset box-shadow and not a border: the panel
   already has its own 1px border and a radius, and widening one side would reflow the body and square
   off the corner. */
.niol-color--edge {
    box-shadow: inset 4px 0 0 0 var(--niol-color);
}

.niol-color--edgeTop {
    box-shadow: inset 0 4px 0 0 var(--niol-color);
}

/* ---- the pulse: a MODIFIER, composing with any of the six ---- */
/* Scoped per mode, because "the whole card blinks" is right for a wash and wrong for a chip. The colour
   is never carried by the motion alone — it is still there when the animation is off, which is what makes
   the reduced-motion rule at the bottom safe. */

.niol-color--pulse.niol-color--pill .niol-color__mark,
.niol-color--pulse.niol-color--value .niol-color__mark,
.niol-color--pulse.niol-color--dot .niol-color__mark::before {
    animation: niol-color-pulse 1.8s ease-in-out infinite;
}

.niol-color--pulse.niol-color--widget,
.niol-color--pulse.niol-color--edge,
.niol-color--pulse.niol-color--edgeTop {
    animation: niol-color-card-pulse 1.8s ease-in-out infinite;
}

/* The dot also RADIATES: a second disc over the first, expanding and fading — the "it is live" read a
   1px dot cannot give on its own. Sized in em, so it tracks the value's font size like the dot does. */
.niol-color--pulse.niol-color--dot .niol-color__mark::after {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    top: 50%;
    width: 0.5em;
    height: 0.5em;
    margin-top: -0.25em;
    border-radius: 50%;
    background: var(--niol-color);
    pointer-events: none;
    animation: niol-color-radiate 1.8s ease-out infinite;
}

@keyframes niol-color-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

/* On the card the colour lives in a background / an inset shadow, so fading the ELEMENT would take the
   title and the value with it. Only the colour-bearing property moves. */
@keyframes niol-color-card-pulse {
    0%, 100% { filter: none; }
    50% { filter: saturate(0.45) opacity(0.75); }
}

@keyframes niol-color-radiate {
    0% { transform: scale(1); opacity: 0.5; }
    70% { transform: scale(2.8); opacity: 0; }
    100% { transform: scale(2.8); opacity: 0; }
}

/* Honour the OS setting: every animation goes, every colour stays. */
@media (prefers-reduced-motion: reduce) {
    .niol-color--pulse.niol-color--pill .niol-color__mark,
    .niol-color--pulse.niol-color--value .niol-color__mark,
    .niol-color--pulse.niol-color--dot .niol-color__mark::before,
    .niol-color--pulse.niol-color--widget,
    .niol-color--pulse.niol-color--edge,
    .niol-color--pulse.niol-color--edgeTop {
        animation: none;
    }

    /* The radiating ring has no static form worth keeping — it exists only as motion. */
    .niol-color--pulse.niol-color--dot .niol-color__mark::after {
        display: none;
    }
}
