/* History section (design 2026-07-30). Only tokens the Onda sheet already defines — no hardcoded palette,
   so light and dark follow data-bs-theme like the rest of the design system, and no new token is introduced.
   The pill toggles reuse .niol-tab from app.css: no parallel "chip" component is defined here. */

/* Two groups: the filters that narrow the read on the left, the pills that pick what is shown on the right.
   flex-START, not flex-end: every control here now carries its label above, so it is the FIRST row that must
   line up. Aligning the bottoms instead let the tallest control place all the others — which is how picking
   the "custom" period, whose two date fields grow the period picker downward, used to shove the pills. */
.niol-history__bar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--niol-space-3);
    align-items: flex-start;
    margin-block: var(--niol-space-4);
}

.niol-history__filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--niol-space-3);
    align-items: flex-start;
}

/* margin-inline-start: auto pins the group to the right edge, and lets it drop to its own line when the bar
   runs out of room — the same mechanism .niol-history__export uses on the tab row above. */
.niol-history__pills {
    margin-inline-start: auto;
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-2);
    align-items: flex-start;
}

/* Label ABOVE the select, like the period picker and the pill group beside it — .label is inline-block, so
   left alone it sits on the select's own line and this control alone starts a row higher than the others.
   The same space-2 all three use between their label and their control is what puts the three labels on one
   line and the three controls on the next. */
.niol-source-picker {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-2);
    align-items: flex-start;
}

.niol-history__pill-row { display: flex; flex-wrap: wrap; gap: var(--niol-space-2); }

/* The table scrolls inside its own box: a wide register must never make the page scroll sideways.
   container-type is not styling: it is what gives the expanded detail below a length equal to the VISIBLE
   width (100cqw) rather than to the table's — the only measure CSS has of a scroll box's viewport. */
.niol-aligned {
    overflow-x: auto;
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius);
    container-type: inline-size;
}

.niol-aligned__table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
}

/* Sticky header: scrolling a long register must not cost you the column names. */
.niol-aligned__table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--niol-panel-2);
    text-align: left;
    padding: var(--niol-space-2) var(--niol-space-3);
    white-space: nowrap;
}

.niol-aligned__table td {
    padding: var(--niol-space-2) var(--niol-space-3);
    border-top: 1px solid var(--niol-border);
    white-space: nowrap;
}

/* z-index is load-bearing, not decoration: Bootstrap's reboot gives <sup> position:relative, so the change
   pills are POSITIONED elements too and would otherwise paint over this column as the grid scrolls under it. */
.niol-aligned__instant { position: sticky; left: 0; z-index: 2; background: var(--niol-panel); }

/* The corner cell is sticky on both axes, so it has to outrank the header row AND the instant column. */
.niol-aligned__table thead th.niol-aligned__instant { z-index: 3; background: var(--niol-panel-2); }

/* The row actions mirror the instant column on the right edge: a wide register must not push them off screen.
   Same z-index ladder (2 body, 3 header corner), shrunk to its buttons. */
.niol-aligned__actions {
    position: sticky;
    right: 0;
    z-index: 2;
    width: 1%;
    background: var(--niol-panel);
}
.niol-aligned__table thead th.niol-aligned__actions { z-index: 3; background: var(--niol-panel-2); }
.niol-aligned__table td.niol-aligned__actions { padding-inline: var(--niol-space-2); }
.niol-aligned__actions .niol-icon-btn + .niol-icon-btn { margin-inline-start: var(--niol-space-2); }

/* The edges the grid scrolls under: a hairline, so a value sliding beneath reads as hidden, not cut. */
.niol-aligned__instant { box-shadow: inset -1px 0 0 var(--niol-border); }
/* Breathing room before that hairline: the first value column starts clear of the timestamp. */
.niol-aligned__table td.niol-aligned__instant,
.niol-aligned__table th.niol-aligned__instant { padding-inline-end: var(--niol-space-5); }
.niol-aligned__actions { box-shadow: inset 1px 0 0 var(--niol-border); }

/* A highlighted row keeps its tint across the sticky cells, which paint their own background. */
.niol-aligned__row--open > .niol-aligned__instant,
.niol-aligned__row--open > .niol-aligned__actions { background: var(--niol-panel-2); }

.niol-aligned__unit { margin-inline-start: var(--niol-space-1); opacity: 0.6; font-size: 0.75rem; }

/* The row toggle is a bare affordance: the chevron is the button, nothing else. */
.niol-aligned__toggle {
    background: transparent;
    border: 0;
    padding: 0;
    margin-inline-end: var(--niol-space-2);
    color: var(--niol-text-muted);
    cursor: pointer;
}

.niol-aligned__toggle:hover { color: var(--niol-accent); }

/* The change marker: readable, not shouting — it is a hint that the row is worth expanding. */
.niol-aligned__changes {
    margin-inline-start: 0.25em;
    padding: 0 0.35em;
    border-radius: 999px;
    background: var(--niol-accent-soft);
    color: var(--niol-accent);
    font-size: 0.7rem;
}

.niol-aligned__empty { opacity: 0.4; }

/* A missing poll and a resync are the two rows that say something no value says. */
.niol-aligned__row--missing td { background: var(--niol-warning-soft); font-style: italic; }
/* An INSET SHADOW, not a border: a border on one row's cell widens that column for the whole table —
   border-collapse shares the column width — which shifted the grid and left a white notch in the header. */
.niol-aligned__row--resync .niol-aligned__instant { box-shadow: inset 3px 0 0 var(--niol-warning), inset -1px 0 0 var(--niol-border); }
.niol-aligned__row--open { background: var(--niol-panel-2); }

.niol-aligned__detail td { white-space: normal; background: var(--niol-panel-2); padding: 0; }

/* The detail belongs to the ROW, not to the grid: scrolling the columns sideways must not carry it off screen.
   Its cell spans the whole table, so the cell itself cannot stick (a sticky box has nowhere to shift inside a
   containing block it fills). The inner box can: pinned at the scroll box's left edge and exactly as wide as
   what is visible, it holds still while the columns move under it. Padding lives here, not on the cell, so
   100cqw is the whole budget. */
.niol-aligned__detail-inner {
    position: sticky;
    left: 0;
    width: 100cqw;
    padding: var(--niol-space-2) var(--niol-space-3);
}

/* A COLUMN, so the explicit range appears UNDER the presets instead of beside them: the range block is the
   one thing here that changes the control's height, and growing downward is the only way it can do that
   without moving the controls next to it in the bar. Deliberately not "row + wrap", which would put the
   dates beside the presets on a wide viewport and under them on a narrow one — the same selection laid out
   two different ways. */
.niol-period { display: flex; flex-direction: column; gap: var(--niol-space-2); align-items: flex-start; }
.niol-period__row { display: flex; flex-wrap: wrap; gap: var(--niol-space-2); align-items: center; }
.niol-period__presets { display: flex; flex-wrap: wrap; gap: var(--niol-space-2); }
.niol-period__retention { color: var(--niol-text-muted); font-size: var(--niol-fs-label); }
.niol-period__clamped { color: var(--niol-warning); font-size: var(--niol-fs-label); }

.niol-series-picker { position: relative; }
.niol-series-picker__panel {
    position: absolute;
    /* Above the table's whole sticky stack (1 header, 2 instant column, 3 corner), not equal to its top layer:
       the bar precedes the table in the DOM, so a TIE paints the panel under the corner cell. Well below the
       backdrop (150) — an offcanvas or a dialog must still cover this. */
    z-index: 10;
    min-width: 16rem;
    max-height: 20rem;
    overflow-y: auto;
    padding: var(--niol-space-2);
    background: var(--niol-panel);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius);
    box-shadow: var(--niol-shadow);
}
.niol-series-picker__row { display: flex; gap: var(--niol-space-2); align-items: center; padding: 0.2rem 0; }
.niol-series-picker__class { margin-inline-start: auto; color: var(--niol-text-subtle); font-size: var(--niol-fs-label); }

.niol-events__bar { display: flex; flex-wrap: wrap; gap: var(--niol-space-2); align-items: center; margin-block-end: var(--niol-space-3); }
.niol-events__summary { display: flex; flex-wrap: wrap; gap: var(--niol-space-3); list-style: none; padding: 0; }
.niol-events__list { margin: 0; padding-inline-start: var(--niol-space-4); }
.niol-events__row { display: flex; gap: var(--niol-space-3); align-items: baseline; }
.niol-events__t { color: var(--niol-text-muted); }
.niol-events__more { color: var(--niol-text-muted); font-size: var(--niol-fs-label); }

.niol-interval__head { color: var(--niol-text-muted); font-size: var(--niol-fs-label); }

.niol-extraction__row { padding-inline-start: calc(var(--tree-depth, 0) * var(--niol-space-3)); }
.niol-extraction__key { font-weight: 600; margin-inline-end: var(--niol-space-2); }
.niol-extraction__null { color: var(--niol-text-subtle); }

.niol-history__pager { display: flex; gap: var(--niol-space-3); align-items: center; justify-content: center; margin-block-start: var(--niol-space-4); }

/* ---------------------------------------------------------------- strips (step 2) */

.niol-strips__panel { margin-block: var(--niol-space-4); }

/* position: relative anchors the one crosshair over the whole stack. */
.niol-strips {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-3);
    margin-block-start: var(--niol-space-3);
}

.niol-strip__head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--niol-space-2);
    align-items: baseline;
    font-size: var(--niol-fs-label);
}

.niol-strip__label { font-weight: 600; }
.niol-strip__unit { color: var(--niol-text-muted); }

/* The readout is written by strip-stack.js; it keeps its line so the layout never jumps on hover. */
.niol-strip__readout { margin-inline-start: auto; min-height: 1em; color: var(--niol-accent); }

/* "Approximate" is a statement about the data, not a fault: warning-coloured, not alarm-coloured. */
.niol-strip__coarse { color: var(--niol-warning); font-size: var(--niol-fs-label); }

.niol-strip__none { color: var(--niol-text-subtle); font-size: var(--niol-fs-label); margin: 0; }

/* The state band: 40 viewBox units tall, stretched to the container's width like the line chart. */
.niol-band { position: relative; }
.niol-band__svg { display: block; width: 100%; height: 2.5rem; }
.niol-band__rect--neutral { fill: var(--niol-panel-2); stroke: var(--niol-border); }

.niol-band__labels { position: absolute; inset: 0; pointer-events: none; }

.niol-band__label {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    padding-inline: 0.25rem;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: 0.7rem;
}

.niol-band__empty { color: var(--niol-text-subtle); font-size: var(--niol-fs-label); margin: 0; }

/* Hidden until a pointer enters the stack, then moved by JS only — the circuit sees no pointer event. */
.niol-strips__crosshair {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--niol-accent);
    opacity: 0;
    pointer-events: none;
}

.niol-strips.is-hovering .niol-strips__crosshair { opacity: 0.7; }

/* The row the crosshair is over: the same instant, in the table. */
.niol-aligned__row.is-crosshair { background: var(--niol-accent-soft); }

/* The min/max wick, behind the average line: same accent, quieter. */
.niol-chart__wick { stroke: var(--niol-accent); stroke-width: 1; opacity: 0.35; }

@media (max-width: 640px) {
    /* The instant column stops being sticky on a narrow viewport: it would eat half the width. */
    .niol-aligned__instant,
    .niol-aligned__actions { position: static; box-shadow: none; }
    /* Their header cells keep the header's sticky top but must drop the side pin, or they float over the
       columns scrolling beneath them. */
    .niol-aligned__table thead th.niol-aligned__instant,
    .niol-aligned__table thead th.niol-aligned__actions { position: sticky; left: auto; right: auto; }
}

/* Band zones sit behind the series: a wash, not a fill — the line must stay the thing you read. */
.niol-chart__zone { opacity: .14; }

/* ---------------------------------------------------------------- KPI tab */

.niol-kpi-group { margin-block: 1.25rem; }
.niol-kpi-group__head { display: flex; align-items: center; gap: .5rem; margin-bottom: .35rem; }
.niol-kpi-group__grid { font-size: .8rem; opacity: .75; }

.niol-kpi-cell { display: inline-flex; align-items: baseline; gap: .35rem; }
/* Absent and gap must not read alike: one is "outside this KPI's history", the other "measured, empty". */
.niol-kpi-cell--absent { display: block; width: 100%; opacity: .25; background: repeating-linear-gradient(
    135deg, transparent 0 4px, currentColor 4px 5px); height: 1em; }
.niol-kpi-cell--gap { opacity: .5; }
.niol-kpi-cell__band { border-radius: .5rem; padding: 0 .4rem; font-size: .72rem; }
.niol-kpi-cell__partial { font-size: .62rem; opacity: .7; }

/* ---------------------------------------------------------------- extractions tab */

/* Room between the tab pills above and the first request row (design feedback pt.1) — same scale as the
   other tabs' top-of-content gap (.niol-history__bar, .niol-strips__panel), not an ad-hoc pixel value. */
.niol-export { margin-block-start: var(--niol-space-4); }

/* Badge + running pulse + mode/format, on one row (design feedback pt.4: the request-level progress cue). */
.niol-export__status { display: flex; flex-wrap: wrap; align-items: center; gap: var(--niol-space-2); }

/* The files of an open request: a full-width third child of the list row, like the purge panel. */
.niol-export__files {
    flex: 1 1 100%;
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-2);
    padding-block-start: var(--niol-space-3);
    border-block-start: 1px solid var(--niol-border);
}

/* Two lines (name, then details) plus the action pinned to the end — wraps to its own line on narrow
   viewports, margin-inline-start: auto keeps it at the right edge either way (design feedback pt.5). */
.niol-export__file { display: flex; flex-wrap: wrap; align-items: center; gap: var(--niol-space-2) var(--niol-space-3); }

/* ---------------------------------------------------------------- tab row */

/* The tab row carries the tabs on the left and the export action on the right. The action is a sibling of
   the tablist, never a child: inside it, a screen reader announces it as a fourth tab. */
.niol-history__tabrow {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-2);
}

.niol-history__export { margin-inline-start: auto; }

.niol-export__file-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }

.niol-export__file-action { flex: none; margin-inline-start: auto; }

/* A finished export and a failed one must not read alike at a glance. */
.niol-badge--ok { border-color: transparent; background: var(--niol-ok-soft); color: var(--niol-ok); }
.niol-badge--danger { border-color: transparent; background: var(--niol-alarm-soft); color: var(--niol-alarm); }

/* The explicit range, shown only when the custom preset is active. Wraps on a narrow viewport rather than
   overflowing the period bar. */
.niol-period__range {
    display: flex;
    flex-wrap: wrap;
    gap: var(--niol-space-2);
    align-items: flex-end;
}

.niol-period__range-field {
    display: flex;
    flex-direction: column;
    gap: var(--niol-space-1);
    font-size: .8125rem;
}

/* The source narrowing inside the series panel spans it, above the first heading. */
.niol-series-picker__panel .niol-source-filter { width: 100%; margin-block-end: var(--niol-space-2); }

/* The row's vertical view: signal on the left, value on the right, one line each. */
.niol-row-values__instant { margin-block-end: var(--niol-space-3); color: var(--niol-text-muted); }
.niol-row-values {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    margin: var(--niol-space-3) 0 0;
}
.niol-row-values dt,
.niol-row-values dd {
    margin: 0;
    padding: var(--niol-space-2) 0;
    border-top: 1px solid var(--niol-border);
}
.niol-row-values dt { font-weight: 400; overflow-wrap: anywhere; padding-inline-end: var(--niol-space-3); }
.niol-row-values dd { text-align: right; }

/* ---------------------------------------------------------------- data tables (/kpi, /alerts): the register's look */

.niol-data-table { margin-block-start: var(--niol-space-2); }
.niol-data-table .niol-aligned__table td { vertical-align: middle; }

/* The header is the sort control: the whole label clicks, the arrow says which way. */
.niol-data-table__sort {
    display: inline-flex;
    align-items: center;
    gap: var(--niol-space-1);
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
}
.niol-data-table__sort svg { opacity: 0.35; }
.niol-data-table__sort:hover,
.niol-data-table__sort--on { color: var(--niol-accent); }
.niol-data-table__sort--on svg { opacity: 1; }

/* A source heading row: the picker's eyebrow, spanning the table. */
.niol-data-table__group th {
    padding: var(--niol-space-3) var(--niol-space-3) var(--niol-space-2);
    border-top: 1px solid var(--niol-border);
    background: var(--niol-panel);
    color: var(--niol-text-subtle);
    font-size: var(--niol-fs-label);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    text-align: left;
}
/* The first heading sits right under the header (or the box's top edge on a phone): no second rule. */
.niol-data-table tbody tr.niol-data-table__group:first-child th { border-top: 0; }
.niol-data-table__count { margin-inline-start: var(--niol-space-1); font-family: var(--niol-font-mono); opacity: 0.7; }

/* The name column carries two lines and is the only one allowed to wrap. */
.niol-data-table__name { white-space: normal; min-width: 14rem; }
.niol-data-table__label { display: flex; flex-wrap: wrap; align-items: center; gap: var(--niol-space-2); font-weight: 600; }
.niol-data-table__slug { display: block; font-size: 0.75rem; color: var(--niol-text-subtle); }
.niol-data-table__muted { color: var(--niol-text-subtle); }

.niol-data-table__actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--niol-space-2); }
/* The gap already spaces the buttons: drop the register's sibling margin so the two do not add up. */
.niol-data-table__actions .niol-icon-btn + .niol-icon-btn { margin-inline-start: 0; }

/* A retired definition reads as set aside: dimmed, its actions left at full strength. */
.niol-data-table__row--retired td:not(.niol-aligned__actions) { color: var(--niol-text-muted); }

/* The bands as a strip, in threshold order: what "3 bands" means at a glance. */
.niol-kpi-bands {
    display: inline-flex;
    width: 72px;
    height: 8px;
    border-radius: 4px;
    overflow: hidden;
    border: 1px solid var(--niol-border);
    vertical-align: middle;
}
.niol-kpi-bands i { flex: 1 1 0; }

/* On a phone each row becomes a compact card: labels beside values, actions under them. */
@media (max-width: 640px) {
    /* Clipped, not scrolled: a card list has nothing to scroll sideways, and the clip keeps the first
       heading's square background inside the box's rounded corners. */
    .niol-data-table { overflow: hidden; }
    .niol-data-table thead { display: none; }
    .niol-data-table .niol-aligned__table,
    .niol-data-table tbody { display: block; }
    .niol-data-table tr.niol-aligned__row {
        display: grid;
        grid-template-columns: auto 1fr;
        column-gap: var(--niol-space-3);
        padding: var(--niol-space-2) 0;
        border-top: 1px solid var(--niol-border);
    }
    .niol-data-table tbody > tr:first-child { border-top: 0; }
    .niol-data-table tr.niol-aligned__row td { border: 0; padding: 2px var(--niol-space-3); white-space: normal; }
    /* The name heads the card, whatever column it sits in on desktop. */
    .niol-data-table td.niol-data-table__name { grid-column: 1 / -1; min-width: 0; order: -1; }
    .niol-data-table td[data-label]::before {
        content: attr(data-label);
        display: inline-block;
        min-width: 6rem;
        margin-inline-end: var(--niol-space-2);
        color: var(--niol-text-subtle);
        font-size: var(--niol-fs-label);
        text-transform: uppercase;
        letter-spacing: 0.04em;
    }
    .niol-data-table td[data-label] { grid-column: 1 / -1; }
    .niol-data-table td.niol-aligned__actions { grid-column: 1 / -1; width: auto; box-shadow: none; }
    .niol-data-table__actions { justify-content: flex-start; }
    .niol-data-table tr.niol-data-table__group,
    .niol-data-table tr.niol-aligned__detail { display: block; }
    .niol-data-table tr.niol-data-table__group th,
    .niol-data-table tr.niol-aligned__detail td { display: block; }
    .niol-data-table .niol-aligned__detail-inner { position: static; width: auto; }
}

/* ---------------------------------------------------------------- /alerts */

/* A rule's state as a dot: red in alarm, amber on its way in or out, green when quiet. */
.niol-status-dot {
    display: inline-block;
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--niol-text-subtle);
}
.niol-status-dot--alarm { background: var(--niol-alarm); box-shadow: 0 0 0 3px var(--niol-alarm-soft); }
.niol-status-dot--pending { background: var(--niol-warning); }
.niol-status-dot--normal { background: var(--niol-ok); }

.niol-alerts__summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--niol-space-2);
    margin: var(--niol-space-3) 0 var(--niol-space-2);
}
.niol-alerts__summary .niol-status-dot:not(:first-child) { margin-inline-start: var(--niol-space-3); }

.niol-alerts__status,
.niol-alerts__subject,
.niol-alerts__when { display: inline-flex; align-items: center; gap: var(--niol-space-2); }
.niol-alerts__subject svg { color: var(--niol-text-subtle); }

/* A rule in alarm keeps a red edge on its first cell, the same inset trick as a resync row. */
.niol-alerts__row--alarm > td:first-child { box-shadow: inset 3px 0 0 var(--niol-alarm); }

.niol-alerts__transition { display: inline-flex; color: var(--niol-text-subtle); }
.niol-alerts__transition--alarm { color: var(--niol-alarm); }
.niol-alerts__transition--warning { color: var(--niol-warning); }
.niol-alerts__transition--info { color: var(--niol-accent); }

.niol-alerts__more { display: flex; justify-content: center; margin-block-start: var(--niol-space-3); }

/* A pressed icon toggle (the personal notification bell). */
.niol-icon-btn--on { color: var(--niol-accent); border-color: var(--niol-accent); background: var(--niol-accent-soft); }
