/* ── Statistics: shared, shell and overview ────────────────────────────────
   Split out of css/app.css so one Statistics tab owns its stylesheet. This file holds the
   primitives every tab shares, the shell (the header, the tab strip, the fetch notice) and
   the overview, whose cards are drawn to the approved mockups. Rules keep the order they had
   in app.css; the media wrappers travel with their rules. */

/* ── D1: statistics section ─────────────────────────────────────────────
   A bar chart drawn with CSS, without a library. With 24 points per day a chart
   library would weigh more than the whole rest of the app. */

/* Side margins from the approved mockups: 16 px on a phone (StatsCaveats.dc.html `main`), 24 px on a
   wide screen, where the content also stops at 1440 px and centres (StatsCaveatsDesktop.dc.html).
   The centring is padding, not `max-width` + `margin: auto`: the screen is an absolutely positioned
   box with `inset: 0`, and its background must still reach both edges. */
#view-stats {
    padding: 0 16px 40px;
}

/* The statistics palette. The overview is drawn to the approved mockups, whose card, track and
   class colours have no equivalent among the app tokens; they live here, per theme, so that the
   bars and cards follow the theme like everything else. */
#view-stats {
    --stats-card-bg: #151d2e;
    /* The tap card: #1e293b in every tap mockup (Forecast, Rail, Traffic, Quality), with the card's
       border. It is lighter than the page card, so the grey tracks inside it stay visible. */
    --stats-tap-bg: #1e293b;
    --stats-track: #334155;
    --stats-unknown: #475569;
    /* „Przeczekał" and „spóźnione" are the app's own early and warning colours (the theme test
       forbids those hex values outside the token blocks, and the mockups' values ARE the tokens'). */
    --stats-early: #7dd3fc;
    --stats-waited: var(--early);
    --stats-ontime: #10b981;
    --stats-late: var(--warn);
    --stats-link-hover: #7dd3fc;
    --stats-meter: #94a3b8;
    --stats-chip: #334155;
    --stats-strong: #cbd5e1;
    --stats-rule: rgba(255, 255, 255, 0.06);
    --stats-axis: rgba(255, 255, 255, 0.18);
    --stats-seg-on-bg: #f8fafc;
    --stats-seg-on-fg: #0b0f1a;    --stats-tile-bg: #0f172a;
}

html[data-theme="light"] #view-stats {
    --stats-card-bg: #ffffff;
    /* No light mockup of the tap card: white, the light card's own surface. The track (#e2e8f0) must
       differ from it, which the old shared chip token did not (both were #e2e8f0). */
    --stats-tap-bg: #ffffff;
    --stats-track: #e2e8f0;
    --stats-unknown: #94a3b8;
    --stats-ontime: #059669;
    --stats-link-hover: #0c4a6e;
    --stats-meter: #64748b;
    --stats-chip: #e2e8f0;
    --stats-strong: #334155;
    --stats-rule: rgba(15, 23, 42, 0.08);
    --stats-axis: rgba(15, 23, 42, 0.25);
    --stats-seg-on-bg: #0f172a;
    --stats-seg-on-fg: #f8fafc;    --stats-tile-bg: #f1f5f9;
}

html[data-theme="midnight"] #view-stats {
    --stats-card-bg: #1c1917;
    /* No midnight mockup of the tap card: stone-800, one step lighter than the card, the way the dark
       tap card is lighter than its page. The track (#44403c) must differ from it. */
    --stats-tap-bg: #292524;
    --stats-track: #44403c;
    --stats-unknown: #57534e;
    --stats-early: #bae6fd;
    --stats-link-hover: #fed7aa;
    --stats-meter: #a8a29e;
    --stats-chip: #44403c;
    --stats-strong: #d6d3d1;
    --stats-seg-on-bg: #fafaf9;
    --stats-seg-on-fg: #0c0a09;    --stats-tile-bg: #0c0a09;
}

/* Which layout an element belongs to. ONE DOM serves both (a resize or a rotation is not a
   re-render), so the other layout's copy is hidden by CSS: below 960 px the wide copies, from
   960 px the phone ones. Kept at the 960 px of the sidebar, because that is where the layout
   changes. `!important` because the cards and rows that carry these classes set their own
   `display` (flex, grid) further down, and the source order of two equal selectors must not be
   what decides whether a layout's copy shows. */
@media (max-width: 959px) {
    .stats-only-wide { display: none !important; }

    /* The city pill carries the period too (the mockups' phone „Gdańsk · 7 dni"), so the standalone
       period control is not drawn on a phone; the period options live in the city menu there. */
    .stats-period { display: none !important; }
}

@media (min-width: 960px) {
    .stats-only-phone { display: none !important; }
}

/* The header: the title, the period switch and (wide) the end of the data. */
.stats-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin: 14px 0 10px;
}

.stats-title {
    flex-grow: 1;
    margin: 0;
    font-size: 1.375rem;
    font-weight: 800;
}

.stats-period {
    position: relative;
}

/* The city switch: the phone pill (which also carries the period) and the wide „Miasto …" label
   plus a small drop-down button. Mirrors `.stats-period` and its trigger — same 6 px radius. */
.stats-city {
    position: relative;
}

/* Phone: the pill reads „Gdańsk · 7 dni"; its menu holds BOTH the city and the period groups, so
   the period group's own trigger is hidden (the pill is the single trigger). */
.stats-city-trigger {
    appearance: none;
    -webkit-appearance: none;
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: 44px;
    margin: 0;
    padding: 0 12px;
    border: 1px solid var(--tint-14);
    /* 6 px, mirroring `.stats-period-trigger` (the mockups were „za okrągłe"). */
    border-radius: 6px;
    background: var(--bg-card);
    color: var(--text-main);
    font: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: pointer;
}

.stats-city-label {
    font-size: 0.8125rem;
    color: var(--text-sec);
}

/* The wide city button: only „Gdańsk ▾" — the same node as the phone pill, minus the period text
   (which CSS hides from 960 px, see the wide block below). */
.stats-city-current {
    white-space: nowrap;
}

.stats-city-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 4px);
    z-index: 6;
    display: flex;
    flex-direction: column;
    min-width: 100%;
    overflow: hidden;
    border: 1px solid var(--tint-14);
    border-radius: 6px;
    background: var(--bg-card);
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25);
}

.stats-city-menu[hidden] { display: none; }

/* Phone: the period group lives INSIDE the city menu, under a heading, and its own trigger is
   gone (the city pill is the only one). Wide: the two groups are separate controls again. */
.stats-city-group { display: flex; flex-direction: column; }

.stats-city-group-heading {
    margin: 0;
    padding: 6px 16px 2px;
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-sec);
}

.stats-city-option {
    appearance: none;
    -webkit-appearance: none;
    min-height: 44px;
    margin: 0;
    padding: 0 16px;
    border: 0;
    background: transparent;
    color: var(--text-main);
    font: inherit;
    font-size: 0.8125rem;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

.stats-city-option[aria-pressed="true"] {
    font-weight: 700;
    box-shadow: inset 3px 0 0 var(--accent);
}

.stats-city-trigger:focus-visible,
.stats-city-option:focus-visible,
.stats-city-period-option:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* Phone: the period group inside the city menu. Same look as `.stats-period-option` (there is no
   separate `.stats-period-options` node there, so the wide segmented rules never apply to it). */
.stats-city-period-option {
    appearance: none;
    -webkit-appearance: none;
    min-height: 44px;
    margin: 0;
    padding: 0 16px;
    border: 0;
    background: transparent;
    color: var(--text-main);
    font: inherit;
    font-size: 0.8125rem;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

.stats-city-period-option[aria-pressed="true"] {
    font-weight: 700;
    box-shadow: inset 3px 0 0 var(--accent);
}

.stats-city-group--period { display: flex; }

/* Phone: a pill with the chosen period; the options drop down from it. (Hidden from 960 px — on a
   wide screen the city pill above carries the city and this is replaced by the segmented control.) */
.stats-period-trigger {
    appearance: none;
    -webkit-appearance: none;
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: 44px;
    margin: 0;
    padding: 0 12px;
    border: 1px solid var(--tint-14);
    /* 6 px, not the phone mockup's 22 px pill: the operator found the mockups "za okrągłe"
       (too round), and the desktop mockup draws this same control at 6 px. */
    border-radius: 6px;
    background: var(--bg-card);
    color: var(--text-main);
    font: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: pointer;
}

.stats-period-options {
    position: absolute;
    right: 0;
    top: calc(100% + 4px);
    z-index: 6;
    display: flex;
    flex-direction: column;
    min-width: 100%;
    overflow: hidden;
    border: 1px solid var(--tint-14);
    border-radius: 6px;
    background: var(--bg-card);
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25);
}

/* An author `display` beats the UA's `[hidden]`; this says it outright (the wide layout turns it
   off again below: there the options are always on screen). */
.stats-period-options[hidden] { display: none; }

.stats-period-option {
    appearance: none;
    -webkit-appearance: none;
    min-height: 44px;
    margin: 0;
    padding: 0 16px;
    border: 0;
    background: transparent;
    color: var(--text-main);
    font: inherit;
    font-size: 0.8125rem;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

.stats-period-option[aria-pressed="true"] {
    font-weight: 700;
    box-shadow: inset 3px 0 0 var(--accent);
}

.stats-period-trigger:focus-visible,
.stats-period-option:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.stats-asof {
    font-size: 0.75rem;
    color: var(--text-sec);
}

@media (min-width: 960px) {
    .stats-head {
        gap: 12px 20px;
        margin: 4px 0 16px;
    }

    .stats-title {
        font-size: 1.0625rem;
        font-weight: 700;
        color: var(--stats-strong);
    }

    .stats-period {
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .stats-period-label {
        font-size: 0.8125rem;
        color: var(--text-sec);
    }

    .stats-period-trigger { display: none; }

    /* City: the wide mockup's „Miasto Gdańsk ▾" — a 36 px button, no period text in it. Its menu
       is a popover in both layouts, so only the group holding the period is hidden here (the wide
       period is the segmented control below). */
    .stats-city {
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .stats-city-trigger { min-height: 36px; padding: 0 10px; }

    /* Wide: the city is „Miasto [Gdańsk ▾]", the period is the segmented control beside it. So the
       pill drops the period text and the phone-only period group in its menu. */
    .stats-city-period { display: none; }

    .stats-city-group--period { display: none; }

    /* From 960 px the city pill carries only the city; the period is the segmented control. */
    .stats-period { display: flex; }

    /* A segmented control: the options are always there, `hidden` or not. */
    .stats-period-options,
    .stats-period-options[hidden] {
        position: static;
        display: flex;
        flex-direction: row;
        min-width: 0;
        border-radius: 6px;
        background: transparent;
        box-shadow: none;
    }

    .stats-period-option {
        min-height: 36px;
        padding: 0 10px;
        color: var(--stats-strong);
    }

    .stats-period-option[aria-pressed="true"] {
        background: var(--stats-seg-on-bg);
        color: var(--stats-seg-on-fg);
        box-shadow: none;
    }
}

.stats-fetch-notice,
.stats-fetch-failed {
    margin: 0 0 12px;
    padding: 10px 12px;
    border-radius: 6px;
    background: var(--warn-muted-bg);
    font-size: 0.85rem;
    line-height: 1.45;
}

.stats-fetch-notice p,
.stats-fetch-failed p {
    margin: 0 0 8px;
}

.stats-fetch-retry-btn {
    display: inline-block;
    margin: 0 0 6px;
    /* 44 px: the minimum touch target (WCAG 2.5.5 / Apple HIG) — this button is pressed on a phone. */
    min-height: 44px;
    padding: 6px 16px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    font-size: 0.85rem;
    cursor: pointer;
}

.stats-fetch-retry-hint {
    margin: 0;
    color: var(--text-sec);
    font-size: 0.8rem;
}

.stats-intro,
.stats-desc {
    color: var(--text-sec);
    font-size: 0.85rem;
    margin: 0 0 14px;
    line-height: 1.5;
}

/* Tiles with the key numbers — they are to be readable without going into the tables. */
.stats-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
    gap: 10px;
    margin-bottom: 26px;
}

.stats-tile {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 12px 14px;
    cursor: help;
}

.stats-tile-value {
    font-size: 1.5rem;
    font-weight: 800;
    line-height: 1.15;
}

.stats-tile-label {
    font-size: 0.75rem;
    color: var(--text-sec);
    margin-top: 2px;
}

.stats-section {
    margin-bottom: 32px;
}

.stats-section h3 {
    font-size: 1.05rem;
    margin: 0 0 4px;
}

.stats-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    font-size: 0.75rem;
    color: var(--text-sec);
    margin-bottom: 8px;
}

.stats-marker {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    margin-right: 6px;
}

.stats-marker--late { background: var(--warn); }

.stats-marker--early { background: var(--early); }

/* Two bars per hour: delays and early arrivals. These are two different
   problems — a delayed service can still be caught, an early one cannot. */
.stats-hours {
    display: flex;
    align-items: flex-end;
    /* The gap shrinks with the screen: a full day is 24 bars, and a phone has
       ~390 px for them. Scrolling stays as a safeguard, but it is not to be
       the norm — a chart whose half you cannot see loses the point of existing. */
    gap: clamp(1px, 0.5vw, 4px);
    overflow-x: auto;
    padding-bottom: 4px;
}

.stats-hour {
    flex: 1 1 0;
    min-width: 13px;
    text-align: center;
    cursor: help;
}

.stats-bar-bg {
    height: 130px;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 2px;
    background: var(--tint-04);
    border-radius: 3px;
    padding: 0 2px;
}

.stats-bar {
    flex: 1 1 0;
    border-radius: 3px 3px 0 0;
    min-height: 1px;
}

/* An hour WITHOUT data. It must be distinguishable from an hour with a zero result —
   otherwise the chart undoes what the read layer did deliberately by passing `null` instead of zero. */
.stats-bar-bg--missing {
    height: 130px;
    border-radius: 3px;
    background:
        repeating-linear-gradient(135deg,
            var(--tint-05) 0 4px,
            transparent 4px 8px);
    border: 1px dashed var(--tint-14);
    box-sizing: border-box;
}

.stats-hour-label--missing { opacity: 0.45; }

.stats-bar--late { background: linear-gradient(180deg, var(--warn), var(--stats-late-end)); }

.stats-bar--early { background: linear-gradient(180deg, var(--early), var(--stats-early-end)); }

.stats-hour-label {
    font-size: 0.68rem;
    color: var(--text-sec);
    margin-top: 4px;
}

.stats-classes {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 8px 18px;
    font-size: 0.85rem;
}

.stats-classes li { cursor: help; }

.stats-class {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 4px;
    font-size: 0.75rem;
    background: var(--tint-08);
    color: var(--text-main);
}

.stats-class--ordinary { background: rgba(34, 197, 94, 0.22); }

.stats-class--terminus { background: rgba(250, 204, 21, 0.22); }

.stats-class--implausible { background: rgba(239, 68, 68, 0.22); }

.stats-class--made-up { background: rgba(56, 189, 248, 0.22); }

.stats-share { color: var(--text-sec); }

/* The table can be wider than a phone — it scrolls ON ITS OWN, it does not drag the page. */
.stats-table-scroll {
    overflow-x: auto;
}

.stats-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.85rem;
}

.stats-table th,
.stats-table td {
    text-align: left;
    padding: 7px 10px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}

.stats-table th {
    color: var(--text-sec);
    font-weight: 600;
    font-size: 0.76rem;
    cursor: help;
}

.stats-table tbody tr:hover {
    background: var(--tint-04);
}

.stats-measure {
    font-weight: 700;
}

.stats-sample-col {
    color: var(--text-sec);
}

.stats-empty {
    color: var(--text-sec);
    font-size: 0.9rem;
}

/* ── Navigation between the sections ───────────────────────────────────
   ONE nav, two layouts (see renderNav): on a phone the four main sections and „Więcej" share a
   row and the other five drop down from that button; from 960 px the same nodes are a sidebar with
   the group headings and every section visible. Group wrappers are `display: contents` so the
   buttons are the grid's own items, and DOM order is the desktop reading order. */
.stats-nav {
    position: relative;
    z-index: 5;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr)) minmax(0, 1.35fr);
    grid-auto-rows: 44px;
    /* One row tall: the dropdown items spill into implicit rows BELOW the nav and cover the
       content instead of pushing it down (and back up when it closes). */
    height: 44px;
    margin-bottom: 14px;
    border-bottom: 1px solid var(--border);
}

.stats-nav-group { display: contents; }

.stats-nav-heading { display: none; }

.stats-nav-item,
.stats-nav-more {
    appearance: none;
    -webkit-appearance: none;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    margin: 0;
    padding: 0 4px;
    background: none;
    border: none;
    border-bottom: 2.5px solid transparent;
    border-radius: 0;
    color: var(--text-sec);
    font: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
}

/* An author `display` beats the UA's `[hidden]`; this says it outright. */
.stats-nav-item[hidden] { display: none; }

/* A tab that holds NO data for the chosen city (Gdynia: prognoza, ruch, flota — see
   `CITY_EMPTY_TABS` in js/stats-views.js). `!important` is required: this must win over the plain
   `.stats-nav-item` rule on a phone AND over `.stats-nav-item--more[hidden] { display: flex; }`
   in the desktop block below, which shows folded items regardless of `hidden`. That is precisely
   why the city state is a class and not `hidden`: `hidden` already means „folded while ‚Więcej'
   is closed", and the two meanings must not be overloaded. */
.stats-nav-item--no-city { display: none !important; }

/* A sidebar group all of whose items are hidden hides its heading too (for Gdynia the „Sieć"
   group = ruch + flota). Harmless on a phone, where `.stats-nav-group` is `display: contents`. */
.stats-nav-group--empty .stats-nav-heading { display: none !important; }

.stats-nav-item:hover,
.stats-nav-more:hover { color: var(--text-main); }

.stats-nav-item--active,
.stats-nav-more--active {
    color: var(--text-main);
    border-bottom-color: var(--accent);
    font-weight: 700;
}

/* „Więcej" carrying the active overflow section's name („Jakość danych ▾", „Odwiedziny ▾") is
   wider than its slot. The mockups wrap it to two lines in a slightly smaller font. The ellipsis
   from the shared rule clipped it on BOTH sides instead, because a centred flex line overflows
   symmetrically: „akość danych" without its caret. */
.stats-nav-more--active {
    padding: 0 2px;
    font-size: 0.75rem;
    line-height: 1.15;
    white-space: normal;
    text-align: center;
}

.stats-nav-item:focus-visible,
.stats-nav-more:focus-visible,
.stats-caveats-link:focus-visible,
.stats-ov-link:focus-visible,
.stats-ov-action:focus-visible,
.stats-lt-row:focus-visible,
.stats-ov-collapse-row:focus-visible,
.stats-chart-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* The dropdown: items of the folded sections, in the rows below the nav, under „Więcej". */
.stats-nav-item--more {
    grid-column: 3 / -1;
    justify-content: flex-start;
    padding: 0 14px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-top-width: 0;
    font-size: 0.88rem;
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25);
}

.stats-nav-item--more.stats-nav-item--active {
    border-bottom-color: var(--border);
    box-shadow: inset 3px 0 0 var(--accent), 0 6px 12px rgba(0, 0, 0, 0.25);
}

/* „Skąd te dane": an entry of the sidebar (`--nav`), a link with an icon under the cards on a
   phone (`--panel`) and an arrow link at the foot of the coverage card (`--card`). */
.stats-caveats-link {
    appearance: none;
    -webkit-appearance: none;
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    padding: 0;
    min-height: 44px;
    background: none;
    border: none;
    color: var(--accent);
    font: inherit;
    font-size: 0.875rem;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
}

.stats-caveats-link:hover { color: var(--stats-link-hover); }

.stats-caveats-link--card {
    min-height: 0;
    margin-top: auto;
    font-size: 0.8125rem;
}

@media (min-width: 960px) {
    /* Two columns: the sidebar and the panel. The header and the fetch notice span
       both; the node right after the nav (the panel, or a message) takes the second column. */
    #view-stats {
        display: grid;
        grid-template-columns: 200px minmax(0, 1fr);
        column-gap: 24px;
        padding-inline: max(24px, calc((100% - 1440px) / 2 + 24px));
        align-items: start;
        /* Rows size to their content. The screen is an absolutely positioned box as tall as the
           viewport, and the default `align-content: normal` of a grid STRETCHES auto rows to fill
           it: with content shorter than the viewport (a line view) the head grew to 146 px and
           left an empty band above the panel. Only this screen: other views are not grids. */
        align-content: start;
    }

    #view-stats > * { grid-column: 1 / -1; }
    #view-stats > .stats-nav { grid-column: 1; }
    #view-stats > .stats-nav + * { grid-column: 2; }

    .stats-nav {
        display: flex;
        flex-direction: column;
        gap: 14px;
        height: auto;
        margin-bottom: 0;
        border-bottom: none;
        position: sticky;
        top: 12px;
    }

    .stats-nav-group { display: flex; flex-direction: column; gap: 2px; }

    .stats-nav-heading {
        display: block;
        margin: 0;
        padding: 0 10px 4px;
        font-size: 0.6875rem;
        font-weight: 700;
        letter-spacing: 0.06em;
        text-transform: uppercase;
        color: var(--text-sec);
    }

    .stats-nav-more { display: none; }

    .stats-nav-item,
    .stats-nav-item--more {
        display: flex;
        align-items: center;
        justify-content: flex-start;
        min-height: 36px;
        padding: 0 10px;
        border: none;
        border-radius: 6px;
        background: none;
        box-shadow: none;
        color: var(--stats-strong);
        font-size: 0.875rem;
        font-weight: 400;
    }

    .stats-nav-item:hover { color: var(--text-main); }

    /* The folded sections are simply there on a wide screen. */
    .stats-nav-item--more[hidden] { display: flex; }

    .stats-nav-item--active,
    .stats-nav-item--more.stats-nav-item--active {
        background: var(--bg-card);
        color: var(--text-main);
        font-weight: 600;
        box-shadow: inset 3px 0 0 var(--accent);
    }
}

.stats-bar--speed {
    background: linear-gradient(180deg, #22c55e, rgba(34, 197, 94, 0.45));
}

.stats-desc--below { margin-top: 10px; }

/* A caveat under the drift table. Smaller than the description, but NOT grey to the
   edge of legibility: it says what this table does not measure, so it must be readable. */
.stats-desc--disclaimer {
    font-size: 0.78rem;
    border-left: 2px solid var(--border);
    padding-left: 10px;
}

/* A one-sentence conclusion above the table. A table with seven columns requires the
   reader to draw the conclusion themselves — and there is one conclusion and it can be written. */
.stats-conclusion {
    font-size: 0.92rem;
    line-height: 1.55;
    margin: 0 0 14px;
    padding: 12px 14px;
    border-radius: 6px;
    background: rgba(56, 189, 248, 0.08);
    border: 1px solid rgba(56, 189, 248, 0.25);
}

/* The two drift directions have different weight for a passenger, so different colours:
   a departure earlier than announced is a missed service, a delay is only a longer wait. */
.stats-cell--early { color: var(--star); font-variant-numeric: tabular-nums; }

.stats-cell--late { color: var(--stats-late); font-variant-numeric: tabular-nums; }

.stats-no-name {
    color: var(--text-sec);
    font-style: italic;
}

/* ── Chart axis labels ──────────────────────────────────────────────────
   Without them two bare numbers stood under the bar one beneath the other ("00" and "16")
   and you had to guess that the first is the hour and the second km/h.
   A chart without labelled axes is pretty, but it is not information. */

.stats-chart {
    margin-bottom: 4px;
}

.stats-axis-y {
    font-size: 0.72rem;
    color: var(--text-sec);
    margin-bottom: 4px;
    letter-spacing: 0.03em;
}

.stats-axis-x {
    font-size: 0.72rem;
    color: var(--text-sec);
    text-align: center;
    margin-top: 6px;
    letter-spacing: 0.03em;
}

/* The value ABOVE the bar — under the bar it would stand next to the hour and again you
   would have to guess which number is which. */
.stats-above-bar {
    font-size: 0.68rem;
    color: var(--text-main);
    margin-bottom: 2px;
    font-variant-numeric: tabular-nums;
}

/* An hour with a colon reads unambiguously as an hour, not as a number.
   Vertically, because "00:00" times 24 will not fit in a row on a phone. */
.stats-hour-label {
    writing-mode: vertical-rl;
    margin: 4px auto 0;
    font-variant-numeric: tabular-nums;
    font-size: 0.66rem;
    letter-spacing: -0.02em;
}

.stats-above-bar {
    font-size: 0.6rem;
}

@media (min-width: 40rem) {
    /* On a wider screen there is room to go back to horizontal hours and larger digits. */
    .stats-hour-label {
        writing-mode: horizontal-tb;
        font-size: 0.7rem;
    }
    .stats-above-bar { font-size: 0.68rem; }
    .stats-hour { min-width: 26px; }
}

/* ── A section waiting for data ──────────────────────────────────────────────
   A chart with one bar is not "less accurate" — it simply does not answer the question
   its title asks. Instead of a dummy: how much is missing and a progress bar, from which
   you can see that the mechanism works and is only waiting. */
.stats-section--waiting {
    opacity: 0.75;
}

.stats-section--waiting h3 {
    font-weight: 600;
}

.stats-progress {
    height: 6px;
    border-radius: 3px;
    background: var(--border);
    overflow: hidden;
    margin-top: 4px;
}

.stats-progress-fill {
    display: block;
    height: 100%;
    border-radius: 3px;
    background: var(--accent);
    transition: width 0.4s ease;
}

/* ── Overview: headline, coverage, daily chart, late lines, rail and ghost cards ──────────
   Drawn to the approved mockups (Stats.dc.html at 390 px, StatsDesktop.dc.html at 1280 px). Cards
   are modest on purpose — 6 px radius, one hairline border — the numbers are the decoration.
   ONE DOM serves both layouts; `.stats-only-phone` / `.stats-only-wide` (above) pick the copy. */
.stats-panel--przeglad {
    display: flex;
    flex-direction: column;
    gap: 14px;
    /* The panel dims while it still shows the previous period's numbers; see `.stats-panel--pending`. */
}

.stats-card {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-width: 0;
    box-sizing: border-box;
    margin: 0;
    padding: 16px;
    background: var(--stats-card-bg);
    border: 1px solid var(--border);
    border-radius: 6px;
}

.stats-card h3 {
    margin: 0;
    font-size: 0.9375rem;
    font-weight: 700;
}

/* The numbers are updating: the previous period's figures stay, but visibly not final. Opacity
   only (no layout change), so the swap back is not a reflow. */
.stats-panel--pending { opacity: 0.55; transition: opacity 0.15s; }

.stats-ov-small {
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--text-sec);
}

/* Links of the cards and of the lists: plain accent text, no button chrome. */
.stats-ov-link {
    appearance: none;
    -webkit-appearance: none;
    display: flex;
    align-items: center;
    min-height: 32px;
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    color: var(--accent);
    font: inherit;
    font-size: 0.8125rem;
    cursor: pointer;
}

.stats-ov-link:hover { color: var(--stats-link-hover); }

.stats-ov-link--end {
    min-height: 0;
    margin-top: auto;
    font-weight: 600;
    text-align: left;
}

/* The headline card. */
.stats-ov-headline { gap: 12px; }

.stats-ov-headline h3 {
    font-size: 1.1875rem;
    line-height: 1.3;
}

.stats-ov-lead {
    margin: 0;
    font-size: 0.8125rem;
    line-height: 1.45;
    color: var(--text-sec);
}

.stats-ov-lead strong { color: var(--stats-strong); }

/* The bar of the classes: one 2 px gap between segments, the same on both layouts. */
.stats-ov-bar {
    display: flex;
    gap: 2px;
    height: 22px;
    border-radius: 6px;
    overflow: hidden;
}

.stats-ov-seg {
    display: block;
    flex: 0 1 auto;
    min-width: 0;
    height: 100%;
}

.stats-ov-seg--early,
.stats-ov-swatch--early { background: var(--stats-early); }

.stats-ov-seg--waited,
.stats-ov-swatch--waited { background: var(--stats-waited); }

.stats-ov-seg--ontime,
.stats-ov-swatch--ontime { background: var(--stats-ontime); }

.stats-ov-seg--late,
.stats-ov-swatch--late { background: var(--stats-late); }

.stats-ov-seg--none,
.stats-ov-swatch--none { background: var(--stats-unknown); }

.stats-ov-swatch {
    display: inline-block;
    width: 10px;
    height: 10px;
    margin-right: 6px;
    border-radius: 3px;
    flex: 0 0 auto;
}

/* The phone's bar and its table are two existing nodes switched by `hidden`; a `display` set here
   would beat the UA's `[hidden]`, so it is stated outright. */
.stats-ov-plot,
.stats-ov-data { display: flex; flex-direction: column; gap: 12px; }

.stats-ov-plot[hidden],
.stats-ov-data[hidden] { display: none; }

.stats-ov-legend {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px 12px;
    font-size: 0.8125rem;
}

.stats-ov-legend > span { display: flex; align-items: center; }

.stats-ov-wide { display: flex; flex-direction: column; gap: 12px; }

.stats-ov-table {
    border-collapse: collapse;
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
}

.stats-ov-table th {
    padding: 4px 8px;
    font-weight: 600;
    text-align: left;
    color: var(--text-sec);
}

.stats-ov-table td { padding: 6px 8px; }

.stats-ov-table tbody tr { border-top: 1px solid var(--stats-rule); }

.stats-ov-table th:first-child,
.stats-ov-table td:first-child { padding-left: 0; white-space: nowrap; }

.stats-ov-table th:last-child,
.stats-ov-table td:last-child { padding-right: 0; }

.stats-ov-table .stats-ov-num { text-align: right; }

.stats-ov-table .stats-ov-def { color: var(--text-sec); }

/* The phone's footer of the headline: how much of the traffic the numbers rest on. */
.stats-ov-cover {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding-top: 10px;
    border-top: 1px solid var(--stats-rule);
}

.stats-ov-cover-row {
    display: flex;
    justify-content: space-between;
    font-size: 0.8125rem;
}

.stats-ov-cover-label { color: var(--stats-strong); }

.stats-ov-meter {
    display: flex;
    height: 6px;
    border-radius: 3px;
    background: var(--stats-track);
}

.stats-ov-meter-fill {
    display: block;
    height: 100%;
    border-radius: 3px;
    background: var(--stats-meter);
}

/* The coverage card (wide). */
.stats-ov-coverage h3 { font-size: 0.9375rem; }

.stats-ov-big {
    font-size: 1.75rem;
    font-variant-numeric: tabular-nums;
}

.stats-ov-coverage .stats-ov-small {
    font-size: 0.875rem;
    line-height: 1.5;
}

.stats-ov-coverage .stats-ov-meter,
.stats-ov-coverage .stats-ov-meter-fill { border-radius: 2px; }

.stats-ov-kv {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 6px 12px;
    margin: 6px 0 0;
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
}

.stats-ov-kv dt { color: var(--text-sec); }

.stats-ov-kv dd { margin: 0; text-align: right; }

/* The late lines. */
.stats-ov-late { gap: 10px; }

.stats-ov-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.stats-ov-late h3 { font-size: 1rem; }

.stats-lt-row {
    appearance: none;
    -webkit-appearance: none;
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr) 52px;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 44px;
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    color: var(--text-main);
    font: inherit;
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
    text-align: left;
    cursor: pointer;
}

.stats-lt-chip {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 28px;
    border-radius: 4px;
    background: var(--stats-chip);
    font-size: 0.8125rem;
    font-weight: 800;
}

.stats-lt-track {
    display: flex;
    height: 10px;
    border-radius: 3px;
    background: var(--stats-track);
    overflow: hidden;
}

.stats-lt-fill {
    display: block;
    height: 100%;
    background: var(--stats-late);
}

.stats-lt-val { text-align: right; }

.stats-lt-n { text-align: right; color: var(--text-sec); }

.stats-lt-cols {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr) 44px 52px;
    gap: 8px;
    padding-bottom: 2px;
    font-size: 0.6875rem;
    color: var(--text-sec);
}

.stats-lt-right { text-align: right; }

/* Beyond the third line a phone has no room: the rows stay in the DOM and CSS leaves them out. */
@media (max-width: 959px) {
    .stats-lt-row--wide { display: none; }
}

/* The rail and ghost cards (wide only). */
.stats-ov-tile { gap: 8px; }

.stats-ov-mid {
    font-size: 1.375rem;
    font-variant-numeric: tabular-nums;
}

.stats-ov-tile .stats-ov-small {
    font-size: 0.8125rem;
    line-height: 1.5;
}

/* „Przystanki, na których linia się rozsypuje": a numbered list whose rows open the line. The rows
   are buttons that look like the list's text, not like chrome; the number comes from the list. */
.stats-ov-collapse-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 4px 0 0;
    padding-left: 20px;
    font-size: 0.8125rem;
}

.stats-ov-collapse-row {
    appearance: none;
    -webkit-appearance: none;
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.stats-ov-collapse-row:hover { color: var(--stats-link-hover); }

/* The phone's two links under the cards. */
.stats-ov-links { display: flex; flex-direction: column; gap: 4px; }

.stats-ov-action {
    appearance: none;
    -webkit-appearance: none;
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    color: var(--accent);
    font: inherit;
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
}

.stats-ov-action:hover { color: var(--stats-link-hover); }

.stats-ov-action svg,
.stats-caveats-link svg { flex: 0 0 auto; }

/* The wide layout: cards flow and wrap by their basis, so a narrower window drops a card to the next
   row instead of squeezing it. */
@media (min-width: 960px) {
    .stats-panel--przeglad {
        flex-direction: row;
        flex-wrap: wrap;
        align-content: flex-start;
        gap: 16px;
    }

    .stats-card { padding: 18px 20px; }

    .stats-ov-headline { flex: 2 1 520px; }
    .stats-ov-coverage { flex: 1 1 260px; }
    .stats-ov-daily { flex: 2 1 520px; }
    .stats-ov-late { flex: 1 1 260px; gap: 4px; }
    .stats-ov-tile { flex: 1 1 300px; }

    .stats-ov-headline h3 { font-size: 1.375rem; }
    .stats-ov-lead { font-size: 0.875rem; line-height: 1.5; }
    .stats-ov-bar { height: 26px; border-radius: 3px; }
    .stats-ov-swatch { border-radius: 2px; }

    .stats-ov-head { padding-bottom: 6px; }
    .stats-ov-late h3 { font-size: 0.9375rem; }
    .stats-ov-link { font-size: 0.75rem; min-height: 0; }
    .stats-ov-link--end { font-size: 0.8125rem; }

    .stats-lt-row {
        grid-template-columns: 40px minmax(0, 1fr) 44px 52px;
        gap: 8px;
        min-height: 32px;
    }

    .stats-lt-chip { height: 22px; border-radius: 3px; font-size: 0.75rem; }
    .stats-lt-track { height: 8px; border-radius: 0; }
    .stats-ov-late .stats-ov-small { padding-top: 6px; }
}

/* The daily chart: one bar per hour of the day, a hairline under them as the axis. */
.stats-ov-daily .stats-chart { display: flex; flex-direction: column; gap: 10px; margin: 0; }

.stats-ov-daily .stats-chart-plot { display: flex; flex-direction: column; gap: 10px; }

.stats-ov-daily .stats-chart-plot[hidden],
.stats-ov-daily .stats-chart-data[hidden] { display: none; }

.stats-ov-daily .stats-ov-small {
    margin: 0;
    font-size: 0.8125rem;
    line-height: 1.4;
}

.stats-ov-hours {
    display: grid;
    grid-template-columns: repeat(24, minmax(0, 1fr));
    grid-template-rows: minmax(0, 1fr);
    gap: 3px;
    align-items: end;
    height: 150px;
    border-bottom: 1px solid var(--stats-axis);
}

.stats-ov-hour { display: block; background: var(--stats-ontime); }

.stats-ov-hour--few { background: var(--stats-unknown); }

.stats-ov-hour--none { height: 0; }

.stats-ov-daily .stats-axis-x {
    margin: 0;
    font-size: 0.6875rem;
    text-align: left;
    letter-spacing: 0;
}

.stats-ov-hours-x {
    display: flex;
    justify-content: space-between;
    font-variant-numeric: tabular-nums;
}

/* A chart's text alternative: the toggle above the bars swaps them for a table. */
.stats-chart-toggle {
    appearance: none;
    -webkit-appearance: none;
    min-height: 32px;
    margin: 0 0 8px;
    padding: 4px 10px;
    background: none;
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text-sec);
    font: inherit;
    font-size: 0.76rem;
    cursor: pointer;
}

.stats-chart-toggle:hover { color: var(--text-main); }

/* With a heading (the overview's daily card) the toggle shares the title's row, as in the mockup:
   title left, a quiet outlined button right. */
.stats-chart-head {
    display: flex;
    align-items: baseline;
    gap: 12px;
}

.stats-chart-head h3 { flex-grow: 1; margin: 0; }

.stats-chart-head .stats-chart-toggle {
    min-height: 32px;
    margin: 0;
    padding: 0 10px;
    border-color: var(--tint-14);
    color: var(--stats-strong);
    font-size: 0.75rem;
}

/* Phone: the breadcrumb and the box replace the header and the tab strip (the mockup has neither). */
@media (max-width: 959px) {
    #view-stats:has(.stats-panel--line-open) > .stats-head,
    #view-stats:has(.stats-panel--line-open) > .stats-nav { display: none; }

    /* The mockup's header: breadcrumb 10 px above the box, a hairline 12 px under it. */
    .stats-panel--line-open .stats-line-crumb { margin-bottom: -4px; }

    .stats-panel--line-open .stats-line-box {
        position: relative;
        margin: 0 0 13px;
    }

    .stats-panel--line-open .stats-line-box::after {
        content: '';
        position: absolute;
        left: -4px;
        right: -4px;
        bottom: -13px;
        border-bottom: 1px solid var(--border);
    }
}

@media (min-width: 960px) {
    .stats-panel--line-open {
        flex-direction: row;
        flex-wrap: wrap;
        align-content: flex-start;
        gap: 16px;
    }

    .stats-panel--line-open > * { flex: 1 1 100%; min-width: 0; margin: 0; }

    /* The box is a 320 px field here, 40 px tall, the same control as the period switch beside it. */
    .stats-panel--line-open > .stats-line-box {
        flex: 0 1 320px;
        min-height: 40px;
        padding: 0 12px;
        gap: 8px;
        font-size: 0.875rem;
    }

    .stats-panel--line-open > .stats-line-box svg { width: 16px; height: 16px; }

    /* The line chip stands on the box's row (the mockup's 12 px gap = the panel's 16 px less 4):
       it does not take the full-width basis the other panel children have, and it is centred on
       the 40 px box. The number sits on the departure board's own badge colour (`--primary`),
       a smaller badge of the same family (4 px radius). */
    .stats-panel--line-open > .stats-line-chip {
        flex: 0 1 auto;
        align-self: center;
        margin: 0 0 0 -4px;
        display: flex;
        align-items: center;
        gap: 8px;
    }
    .stats-line-chip-num {
        min-width: 36px;
        height: 28px;
        padding: 0 6px;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 4px;
        background: var(--primary);
        color: #fff;
        font-weight: 800;
    }
    .stats-line-chip-text { min-width: 0; font-size: 1rem; font-weight: 700; }

    /* „Ostatnio" stands on the box's row after the chip, like the chip: it does not take the
       full-width basis the cards have, and it is centred on the 40 px box. */
    .stats-panel--line-open > .stats-line-recent {
        flex: 0 1 auto;
        align-self: center;
    }
    .stats-panel--line-open .stats-line-search { font-size: inherit; }

    .stats-line-headline { gap: 14px; }
    .stats-line-headline h3 { font-size: 1.3125rem; line-height: 1.3; }

    .stats-panel--line-open > .stats-line-spread { flex: 3 1 460px; gap: 8px; }

    .stats-line-spread .stats-line-bars { height: 170px; gap: 6px; }
    .stats-line-spread .stats-line-bar { border-radius: 0; }

    /* The wide „Pora dnia" card shares the row with the spread card (3 : 2 on the mockup's basis),
       gap 8 inside, square bars. */
    .stats-panel--line-open > .stats-line-pora-wide { flex: 2 1 340px; gap: 8px; }
    .stats-line-pora-wide .stats-line-range,
    .stats-line-pora-wide .stats-line-range-fill { border-radius: 0; }

    /* The stop table is the full-width card under them (the panel's default basis is 100 %). */
    .stats-line-stops { gap: 8px; }
    .stats-line-stops .stats-line-range,
    .stats-line-stops .stats-line-range-fill { border-radius: 0; }
    .stats-line-spread .stats-line-labels { gap: 6px; font-size: 0.75rem; }
}

/* A3a: the breadcrumb of a level-2 list and a level-3 card. Markup per `crumbs()` in
   `reference/lib.mjs`; the inline styles of the mockup live here as classes. `Statystyki` and the
   tab are real links (a plain left click is turned into an in-app navigation by the delegated
   handler); the current page is a plain span with `aria-current`. */
.stats-crumbs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px 6px;
    font-size: 13px;
    color: var(--text-sec);
}

.stats-crumbs-link {
    min-height: 32px;
    display: inline-flex;
    align-items: center;
    color: var(--text-sec);
    text-decoration: none;
}

.stats-crumbs-link:hover { color: var(--stats-link-hover); }

.stats-crumbs-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.stats-crumbs-here { color: var(--text-main); font-weight: 600; }

.stats-crumbs-sep { color: var(--text-sec); }
