/* =============================================================================================
   Defect Notification Tracker — dark-first glass, with a light theme of equal weight

   Three rules this file exists to keep:

   1. Type scales with the display. These screens are opened on a phone, a laptop and a videowall
      read from several metres away, so sizes are clamp()ed against the viewport rather than fixed.

   2. Colour is never the only signal. Every status carries a word and a glyph as well, so it
      survives a colour-blind reader and a washed-out projector.

   3. Everything is expressed through the tokens below, never with a literal colour. That is what
      makes the light theme a change of values rather than a second stylesheet to keep in step.

   THEMES
   Dark is the default — a control room is usually dim, and it is what the brief asked for. The
   light theme exists because the same screens are opened in a daylit office and printed. The
   operating system decides unless the reader has chosen; the choice is remembered.

   Light is NOT an inversion. Glass over a dark field reads as depth; the same treatment over a
   pale field reads as dirt. So light uses solid white panels, a faint slate page, and darker
   status colours — the dark palette's greens and reds do not carry enough contrast on white.
   ============================================================================================= */

:root,
:root[data-theme="dark"] {
    color-scheme: dark;

    /* Surfaces */
    --bg: #090d16;
    --bg-deep: #05070d;
    --glass: rgba(15, 23, 42, 0.75);
    --glass-raised: rgba(23, 33, 58, 0.82);
    --edge: rgba(6, 182, 212, 0.30);
    --edge-soft: rgba(148, 163, 184, 0.16);

    /* The page wash. A named token so the light theme can drop it entirely. */
    --page-wash:
        radial-gradient(1200px 600px at 15% -10%, rgba(6, 182, 212, 0.10), transparent 60%),
        radial-gradient(900px 500px at 100% 0%, rgba(56, 189, 248, 0.07), transparent 55%);

    --blur: blur(12px);

    /* Ink */
    --ink: #e2e8f0;
    --ink-dim: #94a3b8;

    /* Measured on the raised panel surface, which is the lightest background this ink sits on and
       therefore the worst case. Slate-500 (#64748b) reached only 3.5:1 there — every use of it is
       small text (11–12px), so it was failing AA across the whole dashboard. */
    --ink-faint: #8b98ad;

    /* Signal — paired with a word and a glyph everywhere they are used */
    --accent: #22d3ee;
    --ok: #34d399;
    --warn: #fbbf24;
    --bad: #f87171;
    --none: #8b98ad;

    /* Tints behind pills and rows, so each theme can pick its own weight */
    --tint-accent: rgba(34, 211, 238, 0.10);
    --tint-ok: rgba(52, 211, 153, 0.10);
    --tint-warn: rgba(251, 191, 36, 0.10);
    --tint-bad: rgba(248, 113, 113, 0.10);
    --tint-none: rgba(139, 152, 173, 0.12);
    --tint-row: rgba(34, 211, 238, 0.05);

    --input-bg: rgba(2, 6, 23, 0.55);
    --shadow: none;

    /* A genuinely opaque surface, for anything that floats over the page.
       --glass-raised is 82% and relies on its backdrop-filter to obscure what is behind it. That
       works for a panel sitting in the flow and NOT for the navigation menu: its backdrop-filter is
       ignored because an ancestor (.topbar) already established one, so the page beneath was
       reading straight through the dropdown at full sharpness. This is the composite of
       --glass-raised over --bg, so it looks identical and hides what is behind it. */
    --surface-solid: #141d34;

    /* The development-identity banner. Loud in both themes, but amber ink that reads on near-black
       is unreadable on white, so it carries its own values rather than reusing --warn. */
    --banner-stripe-a: rgba(251, 191, 36, 0.20);
    --banner-stripe-b: rgba(251, 191, 36, 0.08);
    --banner-edge: rgba(251, 191, 36, 0.55);
    --banner-ink: #fde68a;
}

/* The reader's explicit choice wins; otherwise the operating system decides.
   `data-theme` is always set before first paint by the inline script in the layout, so this
   stylesheet only ever has to answer two explicit cases rather than duplicate the whole palette
   inside a media query. */
:root[data-theme="light"] {
    color-scheme: light;

    --bg: #eef2f7;
    --bg-deep: #e2e8f0;
    --glass: #ffffff;
    --glass-raised: #ffffff;
    --edge: rgba(21, 94, 117, 0.35);
    --edge-soft: rgba(15, 23, 42, 0.12);

    /* No wash: the gradients that give the dark theme depth just look like a smudge on paper-white. */
    --page-wash: none;
    --blur: none;

    --ink: #0f172a;
    --ink-dim: #475569;

    /* Measured, not chosen by eye: slate-500 (#64748b) reaches only 4.23:1 on this page background,
       and every use of it is small text. This is the lightest grey that still clears 4.5:1. */
    --ink-faint: #5b6779;

    /* Darker than the dark theme's. #34d399 on white is roughly 1.9:1 — unreadable. Cyan-700
       (#0e7490) cleared plain white but only reached 4.24:1 on the selected-tab tint, so the
       accent is set from its worst surface rather than its best. */
    --accent: #155e75;
    --ok: #047857;

    /* Amber-700 measured 4.38:1 on its own tint at 11.6px, just under AA. Amber-800 clears it. */
    --warn: #92400e;
    --bad: #b91c1c;
    --none: #5b6779;

    --tint-accent: rgba(21, 94, 117, 0.09);
    --tint-ok: rgba(4, 120, 87, 0.10);
    --tint-warn: rgba(146, 64, 14, 0.10);
    --tint-bad: rgba(185, 28, 28, 0.09);
    --tint-none: rgba(91, 103, 121, 0.10);
    --tint-row: rgba(21, 94, 117, 0.06);

    --input-bg: #ffffff;
    --surface-solid: #ffffff;

    /* Depth comes from a shadow rather than a glow, which is what a pale surface reads as raised. */
    --shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.04);

    --banner-stripe-a: rgba(180, 83, 9, 0.16);
    --banner-stripe-b: rgba(180, 83, 9, 0.07);
    --banner-edge: rgba(180, 83, 9, 0.5);
    --banner-ink: #7c2d12;
}

:root {
    /* Type. Fonts are self-hosted before handover — an air-gapped server reaches no CDN.
       Until then these stacks degrade to the platform UI and mono faces rather than to Times. */
    --font-ui: "Plus Jakarta Sans", "Inter", "Segoe UI Variable", "Segoe UI", system-ui, sans-serif;
    --font-mono: "JetBrains Mono", "Cascadia Mono", "Consolas", ui-monospace, monospace;

    --radius: 14px;
    --gutter: clamp(1rem, 2.2vw, 2rem);
}

*, *::before, *::after { box-sizing: border-box; }

html {
    /* Scales the whole interface up on a videowall and down on a phone. */
    font-size: clamp(15px, 0.55vw + 13px, 22px);
}

body {
    margin: 0;
    min-height: 100vh;
    background: var(--page-wash), var(--bg);
    color: var(--ink);
    font-family: var(--font-ui);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}

/* --- Development identity warning ---------------------------------------------------------- */

.bypass-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    padding: 0.6rem var(--gutter);
    background: repeating-linear-gradient(135deg,
        var(--banner-stripe-a) 0 12px, var(--banner-stripe-b) 12px 24px);
    border-bottom: 1px solid var(--banner-edge);
    color: var(--banner-ink);
    font-size: 0.82rem;
}
.bypass-banner__icon { font-size: 1.1rem; }
.bypass-banner code { font-family: var(--font-mono); color: var(--banner-ink); font-weight: 600; }

.switcher {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem var(--gutter);
    background: var(--banner-stripe-b);
    border-bottom: 1px solid var(--banner-edge);
    font-size: 0.76rem;
}
.switcher__label { color: var(--banner-ink); text-transform: uppercase; letter-spacing: 0.08em; margin-right: 0.3rem; }
.switcher a {
    padding: 0.18rem 0.6rem;
    border: 1px solid var(--banner-edge);
    border-radius: 999px;
    color: var(--banner-ink);
}
.switcher a:hover { background: var(--banner-stripe-a); text-decoration: none; }

/* --- Chrome -------------------------------------------------------------------------------- */

.topbar {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 0.85rem var(--gutter) 0;
    background: var(--glass);
    backdrop-filter: var(--blur);
    border-bottom: 1px solid var(--edge);

    /* The header's backdrop-filter makes it a stacking context, so the dropdown's z-index is
       scoped inside it. Without a z-index here, a later sibling that makes its own stacking
       context — every .panel does, in dark, for the same reason — can paint over the open menu. */
    position: relative;
    z-index: 50;
}

.topbar__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem var(--gutter);
}

.topbar__brand { display: flex; align-items: center; gap: 0.7rem; color: var(--ink); }
.topbar__brand:hover { text-decoration: none; }
.topbar__title { display: flex; flex-direction: column; line-height: 1.2; }
.topbar__title strong { font-size: 1.02rem; letter-spacing: 0.01em; }
.topbar__title small { color: var(--ink-faint); font-size: 0.68rem; }

.topbar__mark {
    width: 0.7rem; height: 1.6rem;
    border-radius: 3px;
    background: linear-gradient(180deg, var(--accent), var(--tint-accent));
}

/* --- Navigation ---------------------------------------------------------------------------- */

/* The bar used to carry eight equal links wrapped in with the brand and the signed-in name. Two of
   those screens are opened daily and six configure the system; giving them the same weight made the
   row read as a crowd. The daily two stay in the bar, the rest sit behind one grouped control. */

.nav {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.25rem;
}

.nav__link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.85rem;
    border: 1px solid transparent;
    border-bottom: none;
    border-radius: 8px 8px 0 0;
    color: var(--ink-dim);
    font-size: 0.86rem;
    white-space: nowrap;
}

.nav__link:hover { color: var(--ink); background: var(--tint-row); text-decoration: none; }

/* Current page: a filled tab, a solid marker and aria-current together. Anyone who cannot tell the
   highlight colour apart still has the raised shape and the marker. */
.nav__link.is-on,
.nav__group.is-on > .nav__summary {
    color: var(--ink);
    background: var(--glass-raised);
    border-color: var(--edge);
    box-shadow: inset 0 2px 0 var(--accent);
    font-weight: 600;
}

.nav__here {
    width: 0.42rem;
    height: 0.42rem;
    border-radius: 50%;
    background: var(--accent);
    flex: none;
}

.nav__divider {
    width: 1px;
    align-self: stretch;
    margin: 0.35rem 0.5rem 0;
    background: var(--edge-soft);
}

/* The administration group. A native <details>, so it opens with no script at all. */
.nav__group { position: relative; }

.nav__summary { cursor: pointer; list-style: none; }
.nav__summary::-webkit-details-marker { display: none; }
.nav__summary::marker { content: ""; }

/* Which administration screen is open, readable without opening the group. */
.nav__crumb {
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    background: var(--tint-accent);
    color: var(--accent);
    font-size: 0.74rem;
    font-weight: 600;
}

.nav__chev { font-size: 0.7rem; color: var(--ink-faint); transition: transform 120ms ease; }
.nav__group[open] > .nav__summary .nav__chev { transform: rotate(180deg); }

.nav__menu {
    position: absolute;
    z-index: 40;
    top: calc(100% + 0.35rem);
    inset-inline-start: 0;
    min-width: 17rem;
    max-width: min(22rem, calc(100vw - 2rem));
    display: flex;
    flex-direction: column;
    padding: 0.35rem;

    /* Opaque, and no backdrop-filter. The filter would be ignored anyway — .topbar establishes one
       and a nested backdrop-filter does not see past it — so the 82% glass left the page legible
       straight through the menu. A floating menu has to hide what is under it. */
    background: var(--surface-solid);
    border: 1px solid var(--edge);
    border-radius: 10px;
    box-shadow: var(--shadow), 0 10px 30px rgba(2, 6, 23, 0.35);

    /* A Super Admin sees eight entries. On a laptop that ran 122px past the bottom of the window,
       and because the menu is anchored to the header, scrolling the page took it away rather than
       revealing the rest — Email, Users and Audit were simply unreachable. It now scrolls inside
       itself, which is the one place a scrollbar is the right answer.

       The ceiling is measured on open by nav.js and written to --menu-max, because the space left
       depends on where the menu starts and the header is not a fixed height — the development
       banner alone moves it by 180px. The value here is only the fallback if script is unavailable. */
    max-height: var(--menu-max, calc(100vh - 12rem));
    overflow-y: auto;
    overscroll-behavior: contain;
}

.nav__item {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.45rem 0.6rem;
    border-radius: 7px;
    border-inline-start: 3px solid transparent;
    color: var(--ink);
}

.nav__item:hover { background: var(--tint-row); text-decoration: none; }
.nav__item.is-on { background: var(--tint-accent); border-inline-start-color: var(--accent); }

.nav__item-label { display: flex; align-items: center; gap: 0.4rem; font-size: 0.86rem; font-weight: 600; }

/* The word, not only the tint — the open screen says so. */
.nav__item-now {
    padding: 0.02rem 0.4rem;
    border-radius: 999px;
    background: var(--tint-accent);
    color: var(--accent);
    font-size: 0.66rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.nav__item-note { color: var(--ink-faint); font-size: 0.74rem; line-height: 1.35; }

/* On a phone the floating panel would run off the screen, so it becomes part of the flow instead —
   pushing the page down rather than introducing a horizontal scrollbar.

   The tabs themselves stay side by side. An earlier version stretched each one to the full width,
   which turned two links into two stacked bands: with the brand and the identity above them, a
   390px screen spent four rows on chrome before a single figure appeared. Navigation should cost
   one row on a phone, as it does everywhere else. */
@media (max-width: 33rem) {
    .nav { width: 100%; gap: 0.3rem; }

    /* Static so the open panel joins the flow, but the summary keeps its natural width — closed,
       Administration is a tab beside Reports. Opened, the group takes the whole row so the panel
       spans the screen instead of being indented to the width of its own tab. */
    .nav__group { position: static; }
    .nav__group[open] { flex: 1 0 100%; }

    .nav__link { padding: 0.45rem 0.7rem; font-size: 0.82rem; }

    .nav__menu {
        position: static;
        width: 100%;
        max-width: 100%;

        /* The 17rem floor is for the floating panel, which needs a readable width of its own. In
           flow it takes the row, so the floor only did harm: at 316px it held the box at 255px
           starting 83px in, and the whole thing hung off the right edge. */
        min-width: 0;

        max-height: none;          /* in flow, so the page scrolls it rather than the panel */
        margin: 0.35rem 0 0.5rem;
        box-shadow: none;
    }

    .nav__divider { display: none; }

    /* The header itself is trimmed to what a phone actually needs. The strapline and the account
       name are context, not navigation — the role pill is what a reader checks, and it stays. */
    .topbar { padding-top: 0.6rem; gap: 0.45rem; }
    .topbar__title small { display: none; }
    .topbar__title strong { font-size: 0.95rem; }
    .topbar__identity { gap: 0.35rem; }
    .identity__name { display: none; }
    .theme-toggle__label { display: none; }
    .theme-toggle { padding: 0.3rem 0.45rem; }

    /* --- Touch --------------------------------------------------------------------------------
       Measured at 386px: the theme toggle was 22px tall and the league links 23px, both under the
       24px WCAG 2.5.8 floor. These screens are used one-handed on a walk round the plant, often
       with gloves, so the target is 44px rather than the bare minimum. */
    .btn,
    .tab,
    .chip,
    .nav__link,
    .prio__chip,
    .theme-toggle,
    .input {
        min-height: 44px;
    }

    .btn--chip { min-height: 36px; }

    /* Radio and checkbox sizing is in the block at the end of the file, for the same specificity
       reason as the type scale. */

    /* Inline links that act as controls need the height without becoming block-level. A short
       heading like "Pri" was 20px wide, so the width is set too — a target has two dimensions. */
    .league__name,
    .th-sort { display: inline-flex; align-items: center; min-height: 44px; min-width: 24px; }

    .input { padding-block: 0.55rem; }

    /* iOS Safari zooms the page when a field with type smaller than 16px takes focus, and does not
       zoom back out. 16px here is a fix for that, not a style choice. */
    input.input,
    select.input,
    textarea.input { font-size: 16px; }

    /* The type overrides for this width are at the very end of the file — see the note there.
       They cannot live here, because the base rules they raise are declared later and a media
       query adds no specificity. */
}

.topbar__identity { display: flex; align-items: center; gap: 0.5rem; font-size: 0.82rem; }

/* Carries a word as well as an icon, so it is not a mystery glyph. */
.theme-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.24rem 0.7rem;
    border: 1px solid var(--edge-soft);
    border-radius: 999px;
    background: transparent;
    color: var(--ink-dim);
    font: inherit;
    font-size: 0.76rem;
    cursor: pointer;
}
.theme-toggle:hover { border-color: var(--edge); color: var(--ink); }
.theme-toggle__icon { font-size: 0.9rem; line-height: 1; }
.identity__name { color: var(--ink-dim); }

/* --- Collapsible lists --------------------------------------------------------------------- */

/*
 * Long lists arrive collapsed. The summary carries the heading, the number of rows behind it and
 * the word Show or Hide, so a closed panel still answers "how many" without being opened — and the
 * state is never signalled by the chevron alone.
 */

/* Matches .panel's own padding, so a collapsed panel is the same height as a plain one. */
.panel--collapse { padding: 0; }
.panel--collapse > .collapse > .collapse__head { padding: clamp(0.9rem, 1.4vw, 1.4rem); }
.panel--collapse > .collapse[open] > .collapse__head { border-bottom: 1px solid var(--edge-soft); }
.panel--collapse > .collapse > .collapse__body {
    padding: 0 clamp(0.9rem, 1.4vw, 1.4rem) clamp(0.9rem, 1.4vw, 1.4rem);
}

.collapse--inline { margin-top: 0.9rem; border-top: 1px solid var(--edge-soft); }
.collapse--inline > .collapse__head { padding-top: 0.8rem; }

.collapse__head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    cursor: pointer;
    list-style: none;
}

.collapse__head::-webkit-details-marker { display: none; }
.collapse__head::marker { content: ""; }
.collapse__head > .panel__title { margin: 0; }
.collapse__head:hover .collapse__hint { color: var(--ink); border-color: var(--edge); }

.collapse__count {
    padding: 0.08rem 0.5rem;
    border-radius: 999px;
    background: var(--tint-none);
    color: var(--ink-dim);
    font-size: 0.74rem;
    font-variant-numeric: tabular-nums;
}

/* The word first, then the chevron. aria-hidden because <details> already announces expanded or
   collapsed on the summary itself — this exists for the eye, not the screen reader. */
.collapse__hint {
    margin-inline-start: auto;
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--edge-soft);
    border-radius: 999px;
    color: var(--ink-dim);
    font-size: 0.75rem;
    white-space: nowrap;
}

.collapse__hint::before { content: "Show"; }
.collapse[open] > .collapse__head .collapse__hint::before { content: "Hide"; }
.collapse__hint::after { content: " \25BE"; }
.collapse[open] > .collapse__head .collapse__hint::after { content: " \25B4"; }

.collapse__body { padding-top: 0.9rem; }

/* --- Page ---------------------------------------------------------------------------------- */

.page {
    /* Wide enough that four departments sit on one row on a videowall. At 1600px the fourth
       tile wrapped onto a line of its own, which reads as a separate group rather than a
       fourth of four. */
    max-width: 1760px;
    margin: 0 auto;
    padding: var(--gutter);
    display: flex;
    flex-direction: column;
    gap: var(--gutter);
}

.page__head h1 { margin: 0; font-size: clamp(1.4rem, 1.1vw + 1rem, 2rem); letter-spacing: -0.01em; }
.page__sub { margin: 0.25rem 0 0; color: var(--ink-dim); font-size: 0.88rem; }

.panel {
    background: var(--glass);
    backdrop-filter: var(--blur);
    border: 1px solid var(--edge-soft);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: clamp(0.9rem, 1.4vw, 1.4rem);
}
.panel__title {
    margin: 0 0 0.5rem;
    font-size: 0.74rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--ink-faint);
}
.panel p { margin: 0; color: var(--ink-dim); font-size: 0.9rem; }
.panel--notice { border-color: var(--edge); }
.panel--empty { border-color: var(--warn); }
.panel--action { border-color: var(--edge); margin: 1rem 0; }

/* --- Department tiles ---------------------------------------------------------------------- */

.tiles {
    display: grid;
    /* auto-fit + minmax reflows from four across to one down without ever producing a
       horizontal scrollbar, which would hide the right-hand end of the row. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    gap: var(--gutter);
}

.tile {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: clamp(0.9rem, 1.3vw, 1.3rem);
    background: var(--glass-raised);
    backdrop-filter: var(--blur);
    border: 1px solid var(--edge-soft);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    transition: border-color 120ms ease, transform 120ms ease;
}
.tile:hover { border-color: var(--edge); transform: translateY(-2px); }

.tile__head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.tile__short { font-size: 1.3rem; font-weight: 700; letter-spacing: 0.02em; color: var(--ink); }
.tile__name { margin: 0; font-size: 0.86rem; font-weight: 500; color: var(--ink-dim); }
.tile__foot { margin: 0; font-size: 0.74rem; color: var(--ink-faint); }

.tile__meta { display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 0.8rem; margin: 0; font-size: 0.76rem; }
.tile__meta dt { color: var(--ink-faint); }
.tile__meta dd { margin: 0; color: var(--ink); }

/* --- Status pills. Word + glyph + colour, never colour alone. ------------------------------- */

.pill {
    display: inline-flex;
    align-items: center;
    gap: 0.32rem;
    padding: 0.14rem 0.6rem;
    border-radius: 999px;
    border: 1px solid currentColor;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
}
.pill--role   { color: var(--accent); background: var(--tint-accent); }
.pill--ok     { color: var(--ok);     background: var(--tint-ok); }
.pill--warn   { color: var(--warn);   background: var(--tint-warn); }
.pill--bad    { color: var(--bad);    background: var(--tint-bad); }
.pill--nodata { color: var(--none);   background: var(--tint-none); }

/* --- Definition lists ---------------------------------------------------------------------- */

.kv { display: grid; grid-template-columns: auto 1fr; gap: 0.4rem 1.2rem; margin: 0; font-size: 0.86rem; }
.kv dt { color: var(--ink-faint); }
.kv dd { margin: 0; color: var(--ink); overflow-wrap: anywhere; }
.kv--tight { font-size: 0.8rem; margin: 1rem 0; }

/* --- Refusal pages ------------------------------------------------------------------------- */

.refusal { max-width: 46rem; }
.refusal__code {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--warn);
    font-size: 0.76rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
}
.refusal h1 { margin: 0.4rem 0 0.6rem; font-size: clamp(1.3rem, 1.2vw + 0.9rem, 1.9rem); }
.refusal__explain { margin: 0; color: var(--ink-dim); font-size: 0.95rem; }

.btn {
    display: inline-block;
    padding: 0.45rem 1.1rem;
    border: 1px solid var(--edge);
    border-radius: 999px;
    background: var(--tint-accent);
    color: var(--accent);
    font-size: 0.86rem;
}
.btn:hover { background: var(--tint-accent); filter: brightness(1.15); text-decoration: none; }

/* --- Tables -------------------------------------------------------------------------------- */

/* A wide table scrolls inside its own box. The page body must never scroll sideways: the
   right-hand end of a row is where the status and the actions live, and a body-level scrollbar
   hides them without anything on screen saying so. */
.table-scroll { overflow-x: auto; margin: 0 -0.25rem; padding: 0 0.25rem; }

.table { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
.table th, .table td {
    text-align: left;
    padding: 0.5rem 0.7rem;
    border-bottom: 1px solid var(--edge-soft);
    vertical-align: top;
}
.table th {
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-faint);
    font-weight: 600;
    white-space: nowrap;
}
.table td { color: var(--ink); }
.table tbody tr:hover { background: var(--tint-row); }
.table--tight th, .table--tight td { padding: 0.35rem 0.6rem; }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }

/* --- Ingestion runs -------------------------------------------------------------------------- */

.run { padding: 0.8rem 0; border-bottom: 1px solid var(--edge-soft); }
.run:last-child { border-bottom: none; }
.run__head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; font-size: 0.8rem; }
.run__summary { margin: 0.5rem 0; color: var(--ink-dim); font-size: 0.85rem; }

.reason {
    margin-top: 0.35rem;
    padding: 0.45rem 0.6rem;
    border-left: 3px solid var(--bad);
    background: var(--tint-bad);
    color: var(--bad);
    font-size: 0.78rem;
    max-width: 60ch;
}

.actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem; }
.actions form { margin: 0; }

.muted { color: var(--ink-faint); }
.small { font-size: 0.74rem; }

button.btn { font-family: inherit; cursor: pointer; }
.btn--primary { background: var(--tint-accent); font-weight: 600; }

/* --- Forms --------------------------------------------------------------------------------- */

.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    gap: 0.9rem 1.2rem;
    margin-top: 0.6rem;
}
.field { display: flex; flex-direction: column; gap: 0.25rem; }
.field--wide { grid-column: 1 / -1; }

.field label, .checkbox { font-size: 0.8rem; color: var(--ink-dim); }

.input {
    padding: 0.45rem 0.6rem;
    background: var(--input-bg);
    border: 1px solid var(--edge-soft);
    border-radius: 8px;
    color: var(--ink);
    font-family: inherit;
    font-size: 0.86rem;
    width: 100%;
}
.input:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 2px var(--tint-accent); }

.checkbox { display: inline-flex; align-items: center; gap: 0.45rem; cursor: pointer; }
.checkbox input { width: 1rem; height: 1rem; accent-color: var(--accent); }

.hint { font-size: 0.72rem; color: var(--ink-faint); }
.field-error { font-size: 0.74rem; color: var(--bad); }

.validation-summary { margin-bottom: var(--gutter); }
.validation-summary ul { margin: 0; padding-left: 1.1rem; color: var(--bad); font-size: 0.82rem; }
.validation-summary:empty { display: none; }

/* --- Choosing between options -------------------------------------------------------------- */

/* Radio cards rather than a dropdown: the choices are few, they need a line of explanation each,
   and a dropdown hides the alternatives behind a click at the moment of choosing between them. */

.choices {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.5rem;
    margin-bottom: 1rem;
}

.choice {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 0.15rem 0.5rem;
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--edge-soft);
    border-radius: 9px;
    cursor: pointer;
}

.choice:hover { border-color: var(--edge); }
.choice input { grid-row: span 2; width: 1rem; height: 1rem; accent-color: var(--accent); }
.choice__label { font-size: 0.86rem; font-weight: 600; color: var(--ink); }
.choice__note { grid-column: 2; font-size: 0.72rem; color: var(--ink-faint); line-height: 1.3; }

/* The tint is the third signal, behind the radio dot itself and the heavier border. */
.choice.is-on { border-color: var(--accent); background: var(--tint-accent); }

.recur[hidden] { display: none; }
.recur + .recur { margin-top: 0.6rem; }

.fieldset { border: 1px solid var(--edge-soft); border-radius: 9px; padding: 0.7rem 0.8rem; margin: 0; }
.fieldset legend { padding: 0 0.35rem; font-size: 0.72rem; color: var(--ink-faint); text-transform: uppercase; letter-spacing: 0.08em; }
.checks { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; margin-bottom: 0.4rem; }

.lede { font-size: 0.95rem; color: var(--ink) !important; font-weight: 600; }

/* --- Folder test --------------------------------------------------------------------------- */

/* A form button that has to sit in a row of links — a bare <form> would break the line. */
.inline-form { display: inline; }

.stack { display: flex; flex-direction: column; gap: 0.4rem; align-items: flex-start; }

.bullets { margin: 0; padding-left: 1.1rem; color: var(--ink-dim); font-size: 0.88rem; line-height: 1.55; }
.bullets li + li { margin-top: 0.4rem; }

.scope {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.4rem;
    font-size: 0.84rem;
}

.check + .check { margin-top: 0.9rem; padding-top: 0.9rem; border-top: 1px solid var(--edge-soft); }
.check__head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-bottom: 0.4rem; }
.check__head .mono { font-size: 0.76rem; word-break: break-all; }

.inline-form { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-top: 0.7rem; }
.inline-form .input { width: auto; min-width: 16rem; flex: 1 1 16rem; }

form { margin: 0; }

/* --- Dashboard: tabs and slicers ------------------------------------------------------------ */

.page__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 1rem; }

.tabs { display: flex; gap: 0.3rem; }
.tab {
    padding: 0.4rem 1rem;
    border: 1px solid var(--edge-soft);
    border-radius: 999px;
    color: var(--ink-dim);
    font-size: 0.84rem;
}
.tab:hover { color: var(--ink); text-decoration: none; }
.tab--on {
    background: var(--tint-accent);
    border-color: var(--edge);
    color: var(--accent);
    font-weight: 600;
}

/* The filter panel folds away. It is six dropdowns that are usually left alone, and at the top of
   a dashboard they were the first thing on the screen instead of the figures. Collapsed it is one
   line that still says how many filters are in force. */

.filters {
    background: var(--glass);
    backdrop-filter: var(--blur);
    border: 1px solid var(--edge-soft);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.filters__head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: clamp(0.7rem, 1.1vw, 0.95rem) clamp(0.8rem, 1.2vw, 1.1rem);
    cursor: pointer;
    list-style: none;
}
.filters__head::-webkit-details-marker { display: none; }
.filters__head::marker { content: ""; }
.filters__head:hover .collapse__hint { color: var(--ink); border-color: var(--edge); }

.filters__label {
    font-size: 0.74rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--ink-faint);
    font-weight: 600;
}

.filters__count {
    padding: 0.08rem 0.55rem;
    border-radius: 999px;
    background: var(--tint-accent);
    color: var(--accent);
    font-size: 0.74rem;
    font-weight: 600;
}
.filters__count--none { background: var(--tint-none); color: var(--ink-faint); font-weight: 400; }

.filters__panel[open] > .filters__head { border-bottom: 1px solid var(--edge-soft); }

.filters__body {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 9.5rem), 1fr));
    gap: 0.7rem;
    align-items: end;
    padding: clamp(0.8rem, 1.2vw, 1.1rem);
}

/* --- What is currently narrowing the figures ------------------------------------------------ */

.chips { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.chips__label {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-faint);
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--edge);
    border-radius: 999px;
    background: var(--tint-accent);
    color: var(--accent);
    font-size: 0.76rem;
    font-weight: 600;
}
.chip:hover { text-decoration: none; background: var(--tint-row); color: var(--ink); }
.chip__x { font-size: 0.95rem; line-height: 1; opacity: 0.75; }

/* --- Awaiting action ------------------------------------------------------------------------ */

.await { display: flex; align-items: baseline; gap: 0.8rem; margin: 0.4rem 0 0.6rem; }
.await__value { font-size: clamp(2rem, 4vw, 3rem); font-weight: 700; line-height: 1; }
.await__of { display: flex; flex-direction: column; color: var(--ink-dim); font-size: 0.86rem; }

/* --- Priority breakdown ---------------------------------------------------------------------- */

.prio { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.45rem; }

.prio__row {
    display: grid;
    grid-template-columns: 4.2rem 3.2rem minmax(4rem, 1fr) auto;
    align-items: center;
    gap: 0.7rem;
}

.prio__chip {
    display: inline-flex;
    justify-content: center;
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--edge-soft);
    border-radius: 999px;
    color: var(--ink-dim);
    font-size: 0.8rem;
    font-weight: 600;
}
.prio__chip:hover { border-color: var(--edge); color: var(--ink); text-decoration: none; }
.prio__chip.is-on { background: var(--tint-accent); border-color: var(--accent); color: var(--accent); }

.prio__total { text-align: right; font-weight: 600; }

.prio__bar { display: block; height: 0.5rem; border-radius: 999px; background: var(--tint-none); overflow: hidden; }
.prio__fill { display: block; height: 100%; background: linear-gradient(90deg, var(--warn), var(--bad)); }

.prio__beyond { font-size: 0.78rem; white-space: nowrap; }

@media (max-width: 40rem) {
    .prio__row { grid-template-columns: 4.2rem 3.2rem 1fr; }
    .prio__beyond { grid-column: 2 / -1; white-space: normal; }
}

/* --- Department league ---------------------------------------------------------------------- */

/* Ranked rows rather than a table. The question is "who is leading and who is lagging", and a
   table in seed order makes the reader compare four percentages by eye to answer it. */

.league { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }

.league__row {
    display: grid;
    grid-template-columns: 1.6rem minmax(7rem, auto) minmax(4rem, 1fr) 3.6rem auto auto;
    align-items: center;
    gap: 0.4rem 0.7rem;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--edge-soft);
    border-inline-start: 3px solid transparent;
    border-radius: 10px;
}

.league__row.is-leader { border-inline-start-color: var(--ok); background: var(--tint-ok); }
.league__row.is-laggard { border-inline-start-color: var(--bad); background: var(--tint-bad); }

/* The badge sits on the row's own tint, so its usual translucent fill stacked two tints and cost
   the text its contrast — 4.17:1 in light, measured. On a tinted row it takes a solid surface. */
.league__row.is-leader .pill,
.league__row.is-laggard .pill { background: var(--glass-raised); }

.league__rank { color: var(--ink-faint); font-size: 0.82rem; text-align: center; }
.league__name { display: inline-flex; align-items: center; gap: 0.45rem; color: var(--ink); }
.league__name:hover { text-decoration: none; color: var(--accent); }

.league__bar {
    display: block;
    height: 0.5rem;
    border-radius: 999px;
    background: var(--tint-none);
    overflow: hidden;
}
.league__fill { display: block; height: 100%; background: linear-gradient(90deg, var(--ok), var(--accent)); }

.league__pct { text-align: right; font-weight: 600; font-size: 0.9rem; }
.league__detail { color: var(--ink-dim); font-size: 0.78rem; white-space: nowrap; }

/* Grid children default to min-width:auto, which refuses to shrink below their content — with a
   nowrap detail and a "Leading" pill in the name cell, the row forced the page 251px wider than a
   tablet at 764px. */
.league__row > * { min-width: 0; }

/* Below this the six columns become two rows rather than a horizontal scrollbar. Set at 56rem
   rather than 46rem because 46 was measured against the phone and left tablet portrait — the width
   this is most likely to be read at on a walk-round — still on the six-column layout and
   overflowing. */
@media (max-width: 56rem) {
    .league__row { grid-template-columns: 1.6rem 1fr auto; }
    .league__bar { grid-column: 2 / -1; }
    .league__detail { grid-column: 2 / -1; white-space: normal; }
}

/* Search stays outside the collapsible filter panel — it is the control reached for first. */
.searchbar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: clamp(0.7rem, 1.1vw, 0.95rem) clamp(0.8rem, 1.2vw, 1.1rem);
    border-bottom: 1px solid var(--edge-soft);
}
.searchbar__input { flex: 1 1 18rem; min-width: 0; }

/* --- Sortable table headings ---------------------------------------------------------------- */

.th-sort { display: inline-flex; align-items: center; gap: 0.3rem; color: inherit; }
.th-sort:hover { color: var(--ink); text-decoration: none; }
.th-sort__arrow { color: var(--accent); font-size: 0.8em; }

/* Long free text must wrap rather than stretch the table off the side of the screen. */
.cell--wrap { white-space: normal; min-width: 14rem; max-width: 26rem; }

.pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.7rem;
    margin-top: 0.9rem;
}
.pager__state { color: var(--ink-dim); font-size: 0.82rem; }

.export { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.8rem; }
.export .actions { margin: 0; }

.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.slicer { display: flex; flex-direction: column; gap: 0.25rem; }
.slicer label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-faint); }
.slicer select.input { padding: 0.4rem 0.5rem; }
.slicer select.input:disabled { opacity: 0.5; cursor: not-allowed; }
.slicer--actions { flex-direction: row; gap: 0.4rem; align-items: center; }

/* --- Dashboard: cards ----------------------------------------------------------------------- */

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
    gap: var(--gutter);
}
.card {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: clamp(0.8rem, 1.2vw, 1.1rem);
    background: var(--glass-raised);
    backdrop-filter: var(--blur);
    border: 1px solid var(--edge-soft);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}
.card--warn { border-color: var(--warn); }
.card__caption { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink-faint); }
/* Scales with the viewport so the headline figure is legible from across a control room. */
.card__value { font-size: clamp(1.6rem, 1.7vw + 1rem, 2.6rem); font-weight: 700; line-height: 1.1; }
.card__value--ok { color: var(--ok); }
.card__value--bad { color: var(--bad); }
.card__note { font-size: 0.72rem; color: var(--ink-faint); }

/* --- Dashboard: charts ---------------------------------------------------------------------- */

.chart-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
    gap: var(--gutter);
    align-items: start;
}
.panel--wide { grid-column: span 2; }
@media (max-width: 60rem) { .panel--wide { grid-column: span 1; } }

.panel--chart { display: flex; flex-direction: column; }
.panel__sub { margin: 0 0 0.5rem; font-size: 0.74rem; color: var(--ink-faint); }

.chart-box { position: relative; height: 17rem; }
.chart-box--tall { height: 22rem; }

/* ECharts measures its container once on init and does not follow it, so the surface must have a
   real height before the script runs — not one inherited from content that does not exist yet. */
.chart-surface { width: 100%; height: 100%; }

/* The donut's total sits in the hole rather than in a caption, so the headline figure and the
   proportion are read in one glance — as they were on the dashboard being replaced. */
.donut { position: relative; height: 12rem; margin-bottom: 0.6rem; }
.donut__centre {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    pointer-events: none;
}
.donut__value { font-size: 1.9rem; font-weight: 700; color: var(--ink); line-height: 1; }
.donut__caption { font-size: 0.7rem; color: var(--ink-faint); }

.legend { display: grid; gap: 0.25rem; margin: 0; font-size: 0.76rem; }
.legend > div { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; }
.legend dt { display: flex; align-items: center; gap: 0.4rem; color: var(--ink-dim); }
.legend dd { margin: 0; color: var(--ink); }

.swatch { width: 0.6rem; height: 0.6rem; border-radius: 2px; display: inline-block; flex: none; }
.swatch--within { background: var(--ok); }
.swatch--beyond { background: var(--bad); }
.swatch--eight  { background: var(--warn); }
.swatch--year   { background: var(--bad); filter: brightness(0.75); }

.is-ok { color: var(--ok); }
.is-bad { color: var(--bad); }

/* --- Administration ------------------------------------------------------------------------- */

.panel__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.6rem; }
.panel__head .panel__title { margin-bottom: 0; display: flex; align-items: center; gap: 0.5rem; }

.actions--tight { margin-top: 0; gap: 0.35rem; }
.actions--tight .btn { padding: 0.28rem 0.7rem; font-size: 0.78rem; }

.btn--chip { padding: 0.24rem 0.7rem; font-size: 0.74rem; }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* Role picker: the description sits with the option, so the choice is made on what it means
   rather than on the name alone. */
.radios { display: grid; gap: 0.5rem; margin: 0.5rem 0 1rem; }
.radio {
    display: flex;
    align-items: flex-start;
    gap: 0.55rem;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--edge-soft);
    border-radius: 10px;
    cursor: pointer;
}
.radio:hover { border-color: var(--edge); }
.radio input { margin-top: 0.18rem; accent-color: var(--accent); }
.radio span { display: flex; flex-direction: column; gap: 0.1rem; }
.radio--blocked { opacity: 0.45; cursor: not-allowed; }

.run-list { margin: 0.4rem 0 0; padding-left: 1.1rem; font-size: 0.82rem; color: var(--ink); }
.run-list li { margin-bottom: 0.15rem; }

/* Audit before/after values, collapsed by default so they do not drown the list. */
.audit-values { margin-top: 0.3rem; }
.audit-values summary { cursor: pointer; font-size: 0.74rem; color: var(--accent); }
.audit-values__label {
    margin-top: 0.35rem;
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--ink-faint);
}
.audit-values pre {
    margin: 0.15rem 0 0;
    padding: 0.4rem 0.55rem;
    background: var(--input-bg);
    border: 1px solid var(--edge-soft);
    border-radius: 8px;
    color: var(--ink-dim);
    white-space: pre-wrap;
    word-break: break-word;
    max-width: 46rem;
}

/* --- Footer -------------------------------------------------------------------------------- */

.footer {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 1rem var(--gutter) 2rem;
    color: var(--ink-faint);
    font-size: 0.74rem;
    border-top: 1px solid var(--edge-soft);
    margin-top: var(--gutter);
}

/* --- Reduced motion ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
}

/* --- Phone type scale ------------------------------------------------------------------------
   LAST IN THE FILE ON PURPOSE. These raise sizes set by base rules declared further up, and a
   media query adds no specificity — an identical selector inside one, written earlier, simply
   loses. The touch-target rules live with the rest of the narrow-screen block because nothing
   else declares min-height; these could not.

   Measured on a real 386px viewport: the role pill was 10.59px and the filter labels 10.89px.
   That is legible on a desk monitor at 60cm and not on a phone held at arm's length on a plant
   walk-round, which is where this application is actually opened. */

@media (max-width: 33rem) {
    .slicer label,
    .filters__label,
    .chips__label,
    .card__caption,
    .panel__title,
    .collapse__count,
    .collapse__hint { font-size: 0.8rem; }

    .pill { font-size: 0.78rem; }
    .hint,
    .panel__sub,
    .muted.small,
    .footer,
    .nav__item-note,
    .league__detail,
    .prio__beyond,
    .card__note,
    .donut__caption,
    .legend,
    .switcher,
    .bypass-banner { font-size: 0.78rem; }

    .table { font-size: 0.82rem; }
    .table th { font-size: 0.76rem; }
    .nav__item-now { font-size: 0.74rem; }
    .choice__note,
    .fieldset legend { font-size: 0.78rem; }

    /* The label around a radio or a checkbox is the real target and is already large, but the
       control itself was 15px — with a gloved finger that is the difference between hitting the
       control and hitting the text beside it. */
    .choice input,
    .checkbox input { width: 1.6rem; height: 1.6rem; }
    .checkbox { min-height: 44px; }

    /* The audit trail's "Before and after" disclosures were 17px tall — one per row, and the row is
       the whole point of the screen. */
    .audit-values__label { font-size: 0.76rem; }
    table summary { min-height: 44px; display: flex; align-items: center; }
}
