/* ============================================================
   Lead CRM — console UI
   Palette: cool slate + one blue accent, 2.19.3.0
   Type: LXC Sans (UI and headings) / LXC Mono (machine values) — IBM Plex,
         subset and renamed, shipped in assets/fonts with its licence
   Signature: machine values (IPs, codes, secrets) rendered as tinted mono "chips"
   ============================================================ */

/* ---- The typefaces, 2.19.3.0 --------------------------------
   Shipped with the product, not fetched: IBM Plex Sans and Plex Mono, subset
   to Latin and Cyrillic and renamed LXC Sans / LXC Mono, because the licence
   reserves the name "Plex" for the unmodified fonts (assets/fonts/OFL.txt).
   178 KB for all five. Until 2.19.3.0 the stylesheet named three typefaces
   and delivered none, so every machine without them installed read the
   console in its system font.

   The semibold face answers for 600 *and* 700: there is no bold file, and a
   range here means a `font-weight: 700` or a <strong> picks the semibold as
   drawn instead of the browser smearing a synthetic bold over the regular.
   `swap`, so text is never invisible while a face loads. */
@font-face { font-family: "LXC Sans"; src: url("../fonts/lxc-sans-regular.woff2") format("woff2"); font-weight: 300 400; font-style: normal; font-display: swap; }
@font-face { font-family: "LXC Sans"; src: url("../fonts/lxc-sans-medium.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "LXC Sans"; src: url("../fonts/lxc-sans-semibold.woff2") format("woff2"); font-weight: 600 800; font-style: normal; font-display: swap; }
@font-face { font-family: "LXC Mono"; src: url("../fonts/lxc-mono-regular.woff2") format("woff2"); font-weight: 300 400; font-style: normal; font-display: swap; }
@font-face { font-family: "LXC Mono"; src: url("../fonts/lxc-mono-medium.woff2") format("woff2"); font-weight: 500 800; font-style: normal; font-display: swap; }

:root {
    --bg:          #eef1f6;
    --surface:     #ffffff;
    --surface-2:   #f7f8fb;
    --ink:         #18202e;
    --muted:       #5e6b7e;
    /* Was #8a95a6, which is 2.85:1 on --surface-2 — and what it paints is the
       table headings and the eyebrows, 11px uppercase, where AA asks 4.5.
       Measured on the rendered page while the dark theme was being checked,
       and the light theme turned out to be the worse of the two. 4.65:1. */
    --faint:       #667183;
    --line:        #dee3ec;
    --line-strong: #cdd4e0;

    /* Blue, 2.19.3.0: white on it 5.7:1, --accent-ink on a card 7.8:1.
       The three below are derived from --accent, as the dark theme's always
       were, so a colour the operator picks at Settings reaches the links, the
       chips and the soft fills too — until now only the buttons followed it.
       The literal before each is what a browser without color-mix keeps. */
    --accent:      #2160d4;
    --accent-ink:  #1a4daa;
    --accent-ink:  color-mix(in srgb, var(--accent) 80%, #000000);
    --accent-soft: #ebf1fb;
    --accent-soft: color-mix(in srgb, var(--accent) 9%, #ffffff);

    --ok:          #0f7a52;
    --ok-soft:     #e2f4ec;
    --warn:        #a04a08;   /* 4.46:1 on --warn-soft at #b45309; 11.5px text wants 4.5 */
    --warn-soft:   #fbf0df;
    --danger:      #c0362c;
    --danger-soft: #fbeae8;

    --rail-w: 256px;

    --radius:   10px;
    --radius-s: 7px;
    --shadow-card: 0 1px 2px rgba(var(--shadow-ink),.06), 0 6px 20px rgba(var(--shadow-ink),.05);

    /* One family for text and headings: a second display face was a second
       voice on a screen that is read, not looked at. What follows each name is
       what a browser too old for woff2 falls back to. */
    --mono: "LXC Mono", ui-monospace, SFMono-Regular, "Cascadia Mono", Menlo, Consolas, monospace;
    --display: "LXC Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --body: "LXC Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

    /* ---- lifted out of the rules below, so a theme can swap them -------
       Every one of these was a literal somewhere in this file. A colour
       written into a rule is a colour the dark theme cannot reach, and
       fifty-six of them were. They are grouped by the question they answer
       rather than by where they happened to be used. */

    /* Text that sits on the accent, whatever the accent has been set to.
       The operator picks --accent at Settings, so this cannot be derived. */
    --on-accent:   #ffffff;

    /* The line around a soft status background. Each is the soft fill a
       shade or two further on, and they are what make a pill read as a
       pill rather than as coloured text. */
    --ok-line:     #c6e7d7;
    --warn-line:   #f0dcbb;
    --warn-ink:    #7a4a08;
    --danger-line: #e7c4c0;
    --danger-line-soft: #f1cfcb;
    --accent-line: #cedcf6;
    --accent-line: color-mix(in srgb, var(--accent) 22%, #ffffff);

    /* The rail is dark in both themes, and in the dark one it has to stay
       distinguishable from a dark page rather than merging into it. */
    --rail-bg:      #141b29;
    --rail-edge:    #0e131d;
    --rail-ink:     #c3ccda;
    --rail-ink-max: #ffffff;
    --rail-ink-dim: #8a95a6;
    --rail-ink-low: #5b6678;
    /* 3.81:1 on --rail-bg at #6b778b, and these are the 10.5px group
       labels in the navigation. Measured across 483 elements per theme;
       eight of the nine failures in the light theme were this one. */
    --rail-eyebrow: #8593a9;
    --rail-hover:   #1d2636;
    --rail-line:    #232d3e;
    --rail-user:    #e7ebf2;
    --rail-btn-ink: #9aa6b8;
    --rail-btn-line:#2b3548;
    --rail-btn-hover-line: #3a465c;

    /* Shadows and scrims, as a colour rather than as a rule each time. */
    --shadow-ink:  24,32,46;
    --scrim:       rgba(10,14,22,.5);
    --scrim-soft:  rgba(10,14,22,.45);

    /* The sign-in page's decoration, and the code block on Settings. */
    --auth-glow-1: #e2ebfb;
    --auth-glow-2: #dfe6f5;
    --auth-spine:  var(--accent);
    --code-bg:     #141b29;
    --code-ink:    #c8d2e0;
    --on-light:    #ffffff;
}

* { box-sizing: border-box; }

/* ---- One focus ring, 2.19.1.0 ------------------------------
   Every browser draws its own, they disagree with each other, and on a dark
   surface the default is often invisible. `:focus-visible` and not `:focus`,
   so a mouse click does not leave a ring behind it — the ring is for somebody
   working by keyboard, and it has to reach everything they can reach. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-s);
}
/* Inside the rail the accent on a near-black surface is too quiet to find. */
.rail :focus-visible { outline-color: var(--rail-ink-max); }

html, body { margin: 0; padding: 0; }

/* ---- Figures line up, 2.19.1.0 -----------------------------
   Proportional digits are drawn to sit in a sentence: a 1 is narrow and an 8
   is wide, so a column of them wanders and two numbers on consecutive rows
   cannot be compared by looking. `tabular-nums` gives every digit one width.
   It is one line, it costs nothing, and it is most of the difference between a
   screen of data and an instrument.

   Declared where numbers are *read as numbers* — figures, tables, machine
   values, counters — and not on `body`, because inside a sentence proportional
   digits are the right ones and that is what they are for. */
.stat__value, .grid td, .grid th, .mono, .navlink__count, .chip,
.spark, .kv, .share, input[type="number"] {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

body {
    font-family: var(--body);
    color: var(--ink);
    background: var(--bg);
    font-size: 14px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

/* --accent-ink, not --accent. The raw accent is the colour a filled button is
   made of, chosen to carry white on top; used as text on a surface it was
   2.16:1 on the dark theme, measured across 1356 elements. --accent-ink is the
   readable relative of it in whichever theme is showing. */
a { color: var(--accent-ink); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { font-family: var(--display); font-weight: 600; letter-spacing: -.01em; margin: 0; }

/* ---- Brand ------------------------------------------------- */
.brand-mark {
    width: 22px; height: 22px; border-radius: 6px;
    background:
        var(--accent);
    position: relative; flex: none;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.25);
}
.brand-mark::after {
    content: ""; position: absolute; inset: 6px 6px auto 6px; height: 3px;
    border-radius: 2px; background: rgba(255,255,255,.85);
    box-shadow: 0 5px 0 rgba(255,255,255,.55);
}
.brand-name { font-family: var(--display); font-weight: 600; font-size: 16px; letter-spacing: -.01em; }

/* ============================================================
   App shell — a left rail, 2.0.10.0
   2.0.8.0 made this a horizontal bar after TrackBox. Nine of this product's
   labels do not fit across one, so four groups went behind dropdowns; the rail
   has the vertical room to show all fourteen destinations at once.
   ============================================================ */
/* The first track is `auto`, not the rail's width, so collapsing the rail is a
   property of the rail itself: it goes to zero and the track follows. The
   alternative reads better — `body:has(:checked) { --rail-w: 0 }` — and needs
   :has() to decide the page's layout, which is a worse thing to need than a
   sibling selector. */
.app { display: grid; grid-template-columns: auto 1fr; min-height: 100vh; }

.rail {
    width: var(--rail-w);
    position: sticky; top: 0; height: 100vh;
    background: var(--rail-bg);
    color: var(--rail-ink);
    display: flex; flex-direction: column;
    padding: 18px 14px 14px;
    border-right: 1px solid var(--rail-edge);
}
.rail__brand {
    display: flex; align-items: center; gap: 10px;
    padding: 4px 8px 16px; color: var(--rail-ink-max); flex: none;
}
.rail__brand:hover { text-decoration: none; }

.rail__nav { display: flex; flex-direction: column; gap: 1px; flex: 1; overflow-y: auto; }
.rail__eyebrow {
    font-size: 10.5px; text-transform: uppercase; letter-spacing: .14em;
    color: var(--rail-eyebrow); margin: 14px 10px 5px; font-weight: 600;
}
.rail__nav > .rail__eyebrow:first-child { margin-top: 0; }

/* ---- The console updating itself, 2.18.3.0 -----------------
   A figure that changed while somebody was looking elsewhere says that it did,
   and then stops saying it. Two seconds, and nothing moves: a number that
   slides or grows is a number you cannot read while it is doing so. */
.stat__value.is-fresh { animation: live-fresh 2s ease-out 1; }
@keyframes live-fresh {
    from { color: var(--accent-ink); }
    to   { color: inherit; }
}
@media (prefers-reduced-motion: reduce) {
    .stat__value.is-fresh { animation: none; }
}

/* The line a list shows when it has fallen behind. Above the filter panel,
   because it is about the list rather than about the selection. */
/* `[hidden]` first, and it is not a nicety: the UA rule is `[hidden] {
   display: none }`, and a class that sets `display: flex` beats it. 2.18.3.0
   shipped this element hidden on every list and it drew an empty accent bar
   above all three of them. Found by looking at the light theme while doing
   something else. */
.live-new[hidden] { display: none; }
.live-new {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    margin: 0 0 10px; padding: 8px 12px;
    background: var(--accent-soft); border: 1px solid var(--accent-line);
    border-radius: var(--radius-s); color: var(--accent-ink);
    font-size: 13px;
}

/* ---- A sparkline, 2.18.2.0 ---------------------------------
   Fourteen bars and a height each, the same technique as .share__bar and the
   hours bar. No library and no <canvas>: one is a runtime dependency the
   client's rules forbid, the other cannot be read by anything but a sighted
   person looking at it, which is why the span carries an aria-label. */
.spark {
    display: inline-flex; align-items: flex-end; gap: 1px;
    height: 20px; min-width: 56px;
}
.spark > i {
    width: 3px; min-height: 1px; border-radius: 1px;
    background: var(--accent); opacity: .75;
}
/* A day with nothing is a gap, not a bar — but the gap has to hold its place
   or the fortnight loses its dates. */
.spark > i[style*="height:0%"] { background: var(--line-strong); opacity: .5; height: 1px; }
.spark--empty { color: var(--faint); font-size: 12px; min-width: 0; }

/* ---- Saved views, 2.18.1.0 ---------------------------------
   A row above the filter panel, drawn only when it is carrying something. */
.views {
    display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
    margin: 0 0 8px;
}
.views__label { color: var(--faint); margin-right: 2px; }
.chip--view { display: inline-flex; align-items: center; gap: 2px; }
.chip--view > a { color: inherit; }
.chip--view > a:hover { text-decoration: none; }
.chip--view > form { display: inline-flex; margin: 0; }
.chip--view.is-on {
    background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-ink);
}
.views__save > summary {
    cursor: pointer; list-style: none; font-size: 11.5px; font-weight: 600;
    color: var(--accent-ink); padding: 2px 4px; border-radius: var(--radius-s);
}
.views__save > summary::-webkit-details-marker { display: none; }
.views__save > summary:hover { text-decoration: underline; }
.views__form { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
.views__form .input { width: 200px; }

/* ---- A paragraph that folds to two lines, 2.18.1.0 ----------
   Several screens open by explaining themselves, which is right the first time
   somebody meets them and is a wall between the operator and the numbers every
   time after. Measured on /reports at 1440x900: the first figure sat 358px
   down. So anything over two lines is clamped to two, with the rest one click
   away — the text is never removed, only folded, because it is the only place
   some of these facts are written down.

   Applied by app.js, which is the only thing that can measure a rendered
   height. With scripts off nothing is clamped and every screen reads exactly as
   it did. */
.prose-fold {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
    overflow: hidden; position: relative;
}
.prose-fold.is-open { display: block; -webkit-line-clamp: unset; }

/* Inside the block and over its last line, not under it. Placed below, the
   control gave back exactly the height the fold had saved: measured on
   /reports, the first figure stayed at 358px and the release had done
   nothing. It reads as part of the sentence, which is where the rest of the
   sentence is. */
.prose-more {
    position: absolute; right: 0; bottom: 0;
    border: 0; padding: 0 0 0 24px;
    font: inherit; font-size: 11.5px; font-weight: 600;
    color: var(--accent-ink); cursor: pointer;
    background: linear-gradient(90deg, transparent, var(--surface) 22px);
}
.prose-more.is-open {
    position: static; display: block; margin-top: 2px; padding-left: 0;
    background: none;
}
.prose-more:hover { text-decoration: underline; }

/* ---- A group that folds, 2.18.1.0 ---------------------------
   Measured before it was built: at 1440x900 the navigation needed 1056px and
   had 646px, so twelve of twenty-three destinations were below a fold that
   nothing announced. A <details>, so the keyboard, the disclosure semantics
   and the open state come from the browser. */
.railgroup { border: 0; }
.railgroup > summary {
    /*
     * No `space-between`, fixed in 2.19.1.1. A heading has three children —
     * the label, the chevron, and the waiting dot as an `::after` — and
     * `space-between` shares the free space among all three, which parked the
     * chevron in the middle of the row and the dot at the far edge.
     *
     * The dot belongs to the label (it says something is waiting *in here*);
     * the chevron is the control and belongs at the edge. `order` puts them in
     * that sequence — the dot is the last child in the markup and has to be
     * drawn second — and the auto margin before the chevron takes the slack.
     */
    display: flex; align-items: center;
    cursor: pointer; list-style: none; user-select: none;
    border-radius: var(--radius-s); padding: 3px 10px;
    /* Trimmed in 2.19.1.0. Six headings at twelve pixels of air each is 72px
       of a 646px rail, and with two groups open it was the last 18px that put
       the bottom of the product back below the fold. */
    margin: 10px 0 2px;
}
.railgroup:first-child > summary { margin-top: 0; }
/* Safari draws its own triangle through a pseudo-element rather than through
   `list-style`, and it sits outside the flex row. */
.railgroup > summary::-webkit-details-marker { display: none; }
.railgroup > summary:hover { background: var(--rail-hover); color: var(--rail-ink-max); }
.railgroup > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.railgroup__mark {
    width: 12px; height: 12px; flex: none; opacity: .75;
    order: 2; margin-left: auto;
    transition: transform .15s;
}
.railgroup[open] > .railgroup__mark,
.railgroup[open] > summary .railgroup__mark { transform: rotate(180deg); }
.railgroup__links { display: flex; flex-direction: column; gap: 1px; }
/* A closed group still has to say when something is waiting inside it —
   otherwise folding the navigation hides the one thing it is there to tell
   you. The dot is the badge's presence without its number, which there is no
   room for on a heading. */
.railgroup:not([open]) > summary::after {
    content: ''; width: 6px; height: 6px; border-radius: 50%;
    background: var(--accent); flex: none; margin-left: 7px;
    order: 1;
    display: none;
}
.railgroup:not([open]).has-waiting > summary::after { display: block; }

/* The sheet itself takes no space and must not be drawn. */
.icon-sheet { position: absolute; width: 0; height: 0; overflow: hidden; }

/* One size, one stroke, one colour — the text's. An icon that carries its own
   colour is an icon that stops following the theme. */
.ico {
    /* `block`, not the default inline: an inline SVG sits on the text baseline
       and reserves a descender under itself, which added two pixels to every
       row in the navigation and put twelve of them back below the fold that
       2.18.1.0 had just closed. */
    display: block;
    width: 16px; height: 16px; flex: none;
    fill: none; stroke: currentColor; stroke-width: 1.5;
    stroke-linecap: round; stroke-linejoin: round;
}

/* The row itself is laid out further down, beside the badge it also carries. */
.navlink {
    color: var(--rail-ink); padding: 8px 11px; border-radius: var(--radius-s);
    font-weight: 500; font-size: 13.5px; line-height: 1.25;
    transition: background .15s, color .15s;
}
/* The mark is quieter than its label until the row is the one you are on: a
   column of icons at full strength reads as a column of icons rather than as a
   menu with marks in it. */
.navlink .ico { opacity: .62; transition: opacity .15s; }
.navlink:hover .ico,
.navlink.is-active .ico { opacity: 1; }
.navlink:hover { background: var(--rail-hover); color: var(--rail-ink-max); text-decoration: none; }
.navlink.is-active { background: var(--accent); color: var(--on-accent); }

/* ---- Identity, at the foot of the rail ----------------------- */
.rail__user { border-top: 1px solid var(--rail-line); padding-top: 12px; margin-top: 10px; flex: none; }
.rail__identity { display: flex; align-items: center; gap: 10px; padding: 6px 8px; border-radius: var(--radius-s); color: var(--rail-user); }
.rail__identity:hover { background: var(--rail-hover); text-decoration: none; }
.rail__identity-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.rail__identity-text strong { font-size: 13px; color: var(--rail-ink-max); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rail__identity-text small { font-size: 11.5px; color: var(--rail-ink-dim); }
.avatar {
    width: 30px; height: 30px; border-radius: 8px; flex: none;
    background: var(--accent-soft); color: var(--accent-ink);
    display: grid; place-items: center; font-weight: 700; font-family: var(--display); font-size: 14px;
}
.btn-logout {
    margin-top: 8px; width: 100%; background: transparent; color: var(--rail-btn-ink);
    border: 1px solid var(--rail-btn-line); padding: 8px; border-radius: var(--radius-s);
    font: inherit; font-size: 12.5px; cursor: pointer; transition: all .15s;
}
.btn-logout:hover { background: var(--rail-hover); color: var(--rail-ink-max); border-color: var(--rail-btn-hover-line); }
/* Three small segments, sized like the sign-out button under them. */
.rail__theme { display: flex; gap: 2px; margin-top: 8px; border: 1px solid var(--rail-btn-line); border-radius: var(--radius-s); padding: 2px; }
.rail__theme[hidden] { display: none; }
.rail__theme button {
    flex: 1; background: transparent; color: var(--rail-btn-ink); border: 0; cursor: pointer;
    font: inherit; font-size: 11.5px; padding: 5px 4px; border-radius: 5px;
}
.rail__theme button:hover { background: var(--rail-hover); color: var(--rail-ink-max); }
.rail__theme button.is-on { background: var(--accent); color: var(--on-accent); }

.rail__version { color: var(--rail-ink-low); font-size: 11px; font-family: var(--mono); margin-top: 10px; padding: 0 8px; }

/* ---- Page bar ------------------------------------------------ */
.main { display: flex; flex-direction: column; min-width: 0; }
.pagebar {
    display: flex; align-items: center; gap: 14px;
    padding: 22px 28px 0; width: 100%;
}
.pagebar__title { font-size: 21px; }
.pagebar__actions { margin-left: auto; display: flex; gap: 10px; }
/* No max-width. It was 1320px, which on the monitors this is used on left a
   third of the screen empty beside the widest table in the product. Asked for
   directly: "контейнер основного блока сделать в ширину направо до конца".
   Anything that genuinely reads badly at full width carries its own limit —
   .empty p is 520px, the auth card is 420px — rather than the page carrying
   one on their behalf. */
.content { padding: 20px 28px 28px; width: 100%; }

/* Collapsed: zero width, and invisible rather than merely clipped — a rail
   that is 0px wide with its links still focusable hands the keyboard sixteen
   destinations nobody can see. `visibility` inherits, which is the point.
   No JavaScript is involved in the collapsing itself; app.js only remembers
   the checkbox across pages, so with scripts off the rail still folds and
   simply comes back expanded on the next one. */
.rail-collapse-state { position: absolute; }
.rail-collapse-state:checked ~ .rail {
    width: 0; padding-left: 0; padding-right: 0;
    border-right: 0; overflow: hidden; visibility: hidden;
}

/* hamburger and its scrim (narrow screens only) */
.nav-toggle { display: none; flex-direction: column; gap: 4px; cursor: pointer; padding: 6px; }
.nav-toggle span, .rail-toggle span { width: 20px; height: 2px; background: var(--ink); border-radius: 2px; }

/* The same control for wide screens, where the rail folds rather than slides
   over. Two checkboxes rather than one: below 900px the state means "show the
   rail", above it means "hide the rail", and one checkbox carrying both would
   come back from a resize meaning the opposite of what was clicked. */
.rail-toggle {
    display: flex; flex-direction: column; gap: 4px; cursor: pointer;
    padding: 6px; border-radius: var(--radius-s); flex: none;
}
.rail-toggle:hover { background: var(--surface-2); }
.nav-toggle-state { position: absolute; }
.nav-scrim { display: none; }

/* ============================================================
   Cards, sections
   ============================================================ */
.card {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--radius); box-shadow: var(--shadow-card);
}
.card + .card { margin-top: 20px; }
.card__head { padding: 16px 20px; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 12px; }
.card__head h2 { font-size: 15px; }
.card__head .sub { color: var(--muted); font-size: 12.5px; }
.card__head .right { margin-left: auto; }
.card__body { padding: 20px; }
.card__body--flush { padding: 0; }

.eyebrow { font-size: 10.5px; text-transform: uppercase; letter-spacing: .14em; color: var(--faint); font-weight: 600; }

/* ---- Stat tiles -------------------------------------------- */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; }
.stat {
    background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
    padding: 18px; box-shadow: var(--shadow-card); position: relative; overflow: hidden;
}
.stat::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--accent); opacity: .8; }
/* The label is the quietest thing in the tile and the figure is the loudest,
   which is the order they are read in. Tracked out and upper-cased, a 10.5px
   label stops competing with a 34px number for the same glance — and the
   number gets negative tracking, because a face drawn for text is set too
   loose at display sizes. 2.19.1.0. */
.stat__label {
    font-size: 10.5px; color: var(--muted); font-weight: 600;
    text-transform: uppercase; letter-spacing: .09em;
}
.stat__value {
    font-family: var(--display); font-size: 34px; font-weight: 600;
    margin-top: 10px; line-height: 1; letter-spacing: -.02em;
}
.stat__meta { font-size: 12px; color: var(--faint); margin-top: 8px; }
.stat--ok::before { background: var(--ok); }
.stat--warn::before { background: var(--warn); }

/* ============================================================
   Tables
   ============================================================ */
/* `overflow-x: auto` computes `overflow-y` to `auto` as well, which makes this
   a scroll container on both axes — and a sticky heading inside a scroll
   container sticks to *that* container, not to the page. With the height left
   to the content the container never scrolls vertically, so the heading would
   never move: the rule would be there and do nothing.

   `overflow-y: visible` is not allowed here (a used value of `visible` beside
   a scrolling axis becomes `auto`), so the container is given a height to
   scroll within instead. The table gets its own viewport: the heading stays,
   both scrollbars belong to the table, and the page around it stops growing to
   six thousand pixels for a log.

   The floor keeps it usable on a short window — better to scroll the page to
   the table and then scroll the table than to have a 200px slot. */
.table-wrap { overflow: auto; max-height: max(340px, calc(100vh - var(--table-chrome, 260px))); }
/* `separate` and not `collapse`, and it is the sticky heading that needs it:
   a collapsed border belongs to the table rather than to the cell, so it stays
   behind when the cell is lifted out of the flow and the heading row scrolls
   over the list with no line under it. Nothing else changes — every border in
   this table is a `border-bottom` on one side of a pair, so there is nothing
   here for collapsing to merge. */
table.grid { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13.5px; }
.grid th {
    text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
    color: var(--faint); font-weight: 600; padding: var(--row-pad-y, 12px) 16px; border-bottom: 1px solid var(--line);
    background: var(--surface-2); white-space: nowrap;
}
/* Tighter rows, chosen from any table's toolbar and kept for every table. The
   heading shrinks with the body or it stops looking like the same table. */
html[data-density="compact"] { --row-pad-y: 6px; }
html[data-density="compact"] table.grid { font-size: 13px; }

/* The heading stays while the list moves under it. `--grid-head-top` is 0 on
   an ordinary page; a screen with something of its own above the table sets
   it. z-index over the cells, which carry backgrounds of their own. */
.grid thead th { position: sticky; top: var(--grid-head-top, 0px); z-index: 3; }
.grid td { padding: var(--row-pad-y, 13px) 16px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.grid tr:last-child td { border-bottom: 0; }
.grid tbody tr:hover { background: var(--surface-2); }
.grid .cell-primary { font-weight: 600; }
.grid .cell-sub { color: var(--muted); font-size: 12.5px; }
.grid .actions { display: flex; gap: 6px; justify-content: flex-end; }
.grid .row-self { color: var(--accent-ink); font-size: 11px; font-weight: 600; }

/* ---- Machine value chips (the signature) ------------------- */
.mono { font-family: var(--mono); font-size: 12.5px; }
.chip-code {
    font-family: var(--mono); font-size: 12.5px;
    background: var(--accent-soft); color: var(--accent-ink);
    padding: 2px 7px; border-radius: 5px; border: 1px solid var(--accent-line);
    display: inline-block; letter-spacing: .02em;
}

/* ---- Status pills ------------------------------------------ */
.pill {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 11.5px; font-weight: 600; padding: 3px 9px; border-radius: 999px;
    border: 1px solid transparent; white-space: nowrap;
}
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.pill--on   { color: var(--ok); background: var(--ok-soft); border-color: var(--ok-line); }
.pill--off  { color: var(--danger); background: var(--danger-soft); border-color: var(--danger-line-soft); }
.pill--admin{ color: var(--accent-ink); background: var(--accent-soft); border-color: var(--accent-line); }
.pill--muted{ color: var(--muted); background: var(--surface-2); border-color: var(--line); }
.pill--warn { color: var(--warn); background: var(--warn-soft); border-color: var(--warn-line); }

/* ============================================================
   Buttons
   ============================================================ */
.btn {
    font: inherit; font-weight: 600; font-size: 13.5px; cursor: pointer;
    padding: 9px 16px; border-radius: var(--radius-s); border: 1px solid transparent;
    display: inline-flex; align-items: center; gap: 7px; transition: all .15s; line-height: 1;
    text-decoration: none;
}
.btn:hover { text-decoration: none; }
.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { background: var(--accent-ink); }
.btn--ghost { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
.btn--ghost:hover { background: var(--surface-2); }
.btn--sm { padding: 6px 11px; font-size: 12.5px; }
.btn--danger { background: var(--surface); color: var(--danger); border-color: var(--danger-line); }
.btn--danger:hover { background: var(--danger-soft); }
.btn--block { width: 100%; justify-content: center; padding: 11px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* --accent-ink and not --accent: this is accent-coloured *text on a
   surface*, where the raw accent is a colour picked to carry white on
   top of it. 2.16:1 on the dark surface, measured. A filled button is the
   other case and keeps --accent. */
.btn-link { background: none; border: 0; color: var(--accent-ink); font: inherit; cursor: pointer; padding: 0; }
.icon-btn {
    background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-s);
    padding: 6px 10px; cursor: pointer; font: inherit; font-size: 12.5px; color: var(--ink); transition: all .15s;
}
.icon-btn:hover { background: var(--surface-2); }
.icon-btn--danger { color: var(--danger); border-color: var(--danger-line); }
.icon-btn--danger:hover { background: var(--danger-soft); }

.inline-form { display: inline; }

/* ============================================================
   Forms
   ============================================================ */
.form-grid { display: grid; gap: 18px; }
/* auto-fit, not repeat(2, 1fr). Hardcoded two columns put the third field of
   the box form's cap row on a line of its own at every width — on the full page
   too, since long before the drawer existed. Tracks are found rather than
   declared: three where there is room, two where there is not, one on a phone,
   and no media query anywhere. */
.form-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 18px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field > label { font-weight: 600; font-size: 13px; }
.field .hint { font-size: 12px; color: var(--faint); }
.field--error .input, .field--error textarea, .field--error select { border-color: var(--danger); }
.field__error { color: var(--danger); font-size: 12px; font-weight: 500; }

.input, textarea, select {
    font: inherit; font-size: 14px; color: var(--ink);
    background: var(--surface); border: 1px solid var(--line-strong);
    border-radius: var(--radius-s); padding: 10px 12px; width: 100%; transition: border .15s, box-shadow .15s;
}
textarea { resize: vertical; min-height: 90px; font-family: var(--mono); font-size: 13px; }
.input:focus, textarea:focus, select:focus, .btn:focus-visible, .navlink:focus-visible, .input-mono:focus {
    outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
.input-mono { font-family: var(--mono); letter-spacing: .12em; }

.input-affix { position: relative; display: flex; }
.input-affix .input { padding-right: 70px; }
.input-affix .affix-btn {
    position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    border: 0; background: transparent; color: var(--accent-ink); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; padding: 4px 8px;
}

/* checkbox / toggle line */
.check { display: flex; align-items: flex-start; gap: 10px; padding: 12px; border: 1px solid var(--line); border-radius: var(--radius-s); background: var(--surface-2); }
.check input { margin-top: 2px; width: 16px; height: 16px; accent-color: var(--accent); flex: none; }
.check__text strong { display: block; font-size: 13.5px; }
.check__text small { color: var(--muted); font-size: 12.5px; }

.form-actions { display: flex; gap: 10px; padding-top: 4px; }
.form-actions .spacer { flex: 1; }

/* permission matrix */
.perm-group { border: 1px solid var(--line); border-radius: var(--radius-s); margin-bottom: 14px; overflow: hidden; }
.perm-group__head { background: var(--surface-2); padding: 10px 14px; font-weight: 600; font-size: 12.5px; display: flex; align-items: center; gap: 8px; border-bottom: 1px solid var(--line); }
.perm-group__head .count { color: var(--faint); font-weight: 500; }
.perm-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 2px; padding: 8px; }
.perm-item { display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: 6px; cursor: pointer; }
.perm-item:hover { background: var(--surface-2); }
.perm-item input { width: 15px; height: 15px; accent-color: var(--accent); }
.perm-item code { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.perm-item .lbl { font-size: 13px; }

/* ============================================================
   Auth screens
   ============================================================ */
.auth { background:
        radial-gradient(1100px 600px at 110% -10%, var(--auth-glow-1) 0%, transparent 55%),
        radial-gradient(900px 500px at -10% 120%, var(--auth-glow-2) 0%, transparent 50%),
        var(--bg);
    min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.auth__stage { width: 100%; max-width: 420px; }
/* 2.19.1.0: the first screen anybody sees, given the care the ones behind it
   have. Nothing new — more air, a deeper and softer shadow, and a title set
   the way a title should be rather than as large body text. */
.auth__card {
    background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
    box-shadow: 0 1px 2px rgba(var(--shadow-ink),.08), 0 18px 56px rgba(var(--shadow-ink),.16);
    display: flex; overflow: hidden;
}
.auth__spine { width: 5px; flex: none; background: var(--accent); }
.auth__body { padding: 36px 34px 38px; flex: 1; }
.auth__brand { display: flex; align-items: center; gap: 10px; margin-bottom: 26px; }
.auth__title { font-size: 25px; margin-bottom: 6px; letter-spacing: -.02em; }
.auth__lede { color: var(--muted); font-size: 13.5px; line-height: 1.55; margin: 0 0 26px; max-width: 34ch; }
.auth__foot { text-align: center; color: var(--faint); font-size: 12px; margin-top: 20px; }
.auth .field { margin-bottom: 18px; }
/* Taller here than in the console: this is the one screen with three controls
   on it, so there is room to be comfortable, and it is the screen somebody
   types a password into on a phone. */
.auth .input { padding-top: 11px; padding-bottom: 11px; }
.auth .btn--block { padding-top: 11px; padding-bottom: 11px; margin-top: 4px; }
.auth__meta { text-align: center; font-size: 12.5px; color: var(--muted); margin-top: 18px; }

/* big segmented 2FA code input */
.code-input {
    font-family: var(--mono); font-size: 26px; letter-spacing: .5em;
    text-align: center; padding: 14px 12px 14px 22px; font-weight: 500;
}

/* ============================================================
   2FA setup
   ============================================================ */
.twofa-setup { display: grid; grid-template-columns: 180px 1fr; gap: 24px; align-items: start; }
.qr-box { width: 180px; height: 180px; border: 1px solid var(--line); border-radius: var(--radius-s); display: grid; place-items: center; background: var(--on-light); padding: 8px; }
.qr-box img, .qr-box canvas, .qr-box table, .qr-box svg { width: 100% !important; height: 100% !important; }
.qr-fallback { font-size: 12px; color: var(--faint); text-align: center; padding: 12px; }
.secret-block { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-s); padding: 12px 14px; }
.secret-block .secret { font-family: var(--mono); font-size: 16px; letter-spacing: .14em; color: var(--ink); word-break: break-all; }
.steps { counter-reset: step; padding: 0; margin: 0 0 18px; list-style: none; }
.steps li { position: relative; padding-left: 30px; margin-bottom: 10px; font-size: 13.5px; }
.steps li::before {
    counter-increment: step; content: counter(step);
    position: absolute; left: 0; top: -1px; width: 20px; height: 20px; border-radius: 50%;
    background: var(--accent-soft); color: var(--accent-ink); font-weight: 700; font-size: 11px;
    display: grid; place-items: center; font-family: var(--display);
}

/* ============================================================
   Flash messages
   ============================================================ */
.flash-stack { display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px; }
.flash {
    display: flex; align-items: center; gap: 11px; padding: 12px 14px;
    border-radius: var(--radius-s); border: 1px solid; font-size: 13.5px; font-weight: 500;
    animation: flash-in .25s ease;
}
.flash__icon { width: 8px; height: 8px; border-radius: 50%; flex: none; background: currentColor; }
.flash__text { flex: 1; }
.flash__close { background: none; border: 0; font-size: 18px; line-height: 1; cursor: pointer; color: inherit; opacity: .55; }
.flash__close:hover { opacity: 1; }
/* ---- the lead's path ---------------------------------------------------
   A list, drawn as a line. The marks are ::before on the item so the line can
   run behind them through the whole list and stop at the last one — a border
   on each item would draw a stub under the final step. */
.journey { list-style: none; margin: 0; padding: 0; position: relative; }
.journey__step { position: relative; padding: 0 0 18px 26px; }
.journey__step:last-child { padding-bottom: 0; }
/* The line, between one mark and the next. */
.journey__step:not(:last-child)::before {
    content: ""; position: absolute; left: 5px; top: 16px; bottom: 0;
    width: 2px; background: var(--line);
}
.journey__mark {
    position: absolute; left: 0; top: 5px; width: 12px; height: 12px;
    border-radius: 50%; background: var(--surface); border: 2px solid var(--line-strong);
}
.journey__head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.journey__title { font-size: 13.5px; }
.journey__at { font-size: 11.5px; color: var(--faint); margin-left: auto; white-space: nowrap; }
.journey__detail { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); }

.journey__step--done .journey__mark    { background: var(--ok);     border-color: var(--ok); }
.journey__step--failed .journey__mark  { background: var(--danger); border-color: var(--danger); }
.journey__step--failed .journey__title { color: var(--danger); }
.journey__step--waiting .journey__mark { background: var(--surface); border-color: var(--accent); }
.journey__step--unknown .journey__mark { background: var(--warn);   border-color: var(--warn); }
/* Hollow and dashed: this step did not happen, which is a different thing from
   a step that happened and failed. */
.journey__step--missing .journey__mark  { background: var(--surface); border-style: dashed; border-color: var(--warn); }
.journey__step--missing .journey__title { color: var(--warn); }

.pager { display: flex; justify-content: center; align-items: center; gap: 6px; margin-top: 18px; flex-wrap: wrap; }
.pager__gap { color: var(--faint); padding: 0 2px; }

/* The summary at the top of a form that would not save. Borrowed from the
   flash's shape deliberately — it sits directly under one and two different
   boxes saying two different things would read as one thing said twice. */
.form-errors {
    margin: 0 0 16px; padding: 12px 14px; border-radius: var(--radius-s);
    background: var(--danger-soft); border: 1px solid var(--danger-line); color: var(--danger);
    font-size: 13px;
}
.form-errors__head { margin: 0 0 6px; font-weight: 600; }
.form-errors__list { margin: 0; padding-left: 18px; }
.form-errors__list a { color: inherit; text-decoration: underline; }
.form-errors__list a:hover { text-decoration: none; }
/* Arriving from the summary. The offset stops the field landing under a
   sticky heading, and the mark is what says "this is the one you clicked". */
.field :target, :target > .input, .input:target {
    scroll-margin-top: 90px; outline: 2px solid var(--danger); outline-offset: 2px;
}

.flash--success { color: var(--ok); background: var(--ok-soft); border-color: var(--ok-line); }
.flash--error   { color: var(--danger); background: var(--danger-soft); border-color: var(--danger-line-soft); }
.flash--info    { color: var(--accent-ink); background: var(--accent-soft); border-color: var(--accent-line); }
.flash .flash__text { color: var(--ink); }

@keyframes flash-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

/* ============================================================
   Misc
   ============================================================ */
/* A screen with nothing in it has to read as "nothing here yet" and never as
   "this failed to load". The mark is what does that at a glance, before the
   sentence is read; it is drawn in the page's own faint colour so it is a
   texture rather than a picture. 2.19.1.0. */
.empty { text-align: center; padding: 56px 20px; color: var(--muted); }
.empty__mark {
    width: 44px; height: 44px; margin: 0 auto 16px;
    display: grid; place-items: center;
    border: 1px dashed var(--line-strong); border-radius: 14px;
    color: var(--faint);
}
.empty__mark .ico { width: 20px; height: 20px; }
.empty h3 { font-size: 16px; margin-bottom: 6px; color: var(--ink); letter-spacing: -.01em; }
.empty p { max-width: 520px; margin: 0 auto; line-height: 1.6; }
.empty__action { margin-top: 20px; }
.section-note { font-size: 12.5px; color: var(--muted); margin: 0 0 16px; }
.divider { height: 1px; background: var(--line); margin: 22px 0; border: 0; }
.callout { display: flex; gap: 10px; padding: 12px 14px; border-radius: var(--radius-s); font-size: 13px; background: var(--warn-soft); border: 1px solid var(--warn-line); color: var(--warn-ink); }
.callout--info { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-ink); }
.kv { display: grid; grid-template-columns: auto 1fr; gap: 6px 18px; font-size: 13.5px; }
/* A label column wide enough for the longest label, so the values line up. This
   was an inline style on every one of these lists; it is a class so the drawer
   can answer differently, which an inline style would have won against. */
/* minmax(0, …) on both: a fixed 200px label column does not fit a card 378px
   wide, and the 13px it spilled had nowhere to go. Wide enough for the longest
   label where there is room, narrower where there is not. */
.kv--pairs { grid-template-columns: minmax(0, 200px) minmax(0, 1fr); }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; font-weight: 500; }

.error-stage { text-align: center; padding: 10px; }
.error-stage .code { font-family: var(--display); font-size: 56px; font-weight: 700; color: var(--accent); }
.error-stage p { color: var(--muted); }

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 900px) {
    .app { grid-template-columns: 1fr; }
    .rail {
        position: fixed; z-index: 60; width: 260px; left: 0; top: 0;
        transform: translateX(-100%); transition: transform .2s ease;
    }
    .nav-toggle-state:checked ~ .rail { transform: none; }
    /* Tapping anywhere else closes it — the same checkbox, no JavaScript. */
    .nav-toggle-state:checked ~ .nav-scrim {
        display: block; position: fixed; inset: 0; z-index: 50;
        background: var(--scrim);
    }
    .nav-toggle { display: flex; }
    .rail-toggle { display: none; }
    /* The overlay rail is not the folded one: it must ignore the fold. */
    .rail-collapse-state:checked ~ .rail {
        width: 260px; padding: 18px 14px 14px; overflow: visible; visibility: visible;
    }
    .form-row { grid-template-columns: 1fr; }
    .twofa-setup { grid-template-columns: 1fr; justify-items: center; text-align: left; }
    .pagebar { padding: 18px 16px 0; }
    .content { padding: 14px 16px 18px; }
}

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

/* ============================================================
   Branding (logo / icon) + version  — added in 2.0.1.0
   ============================================================ */
.brand-logo { max-height: 30px; max-width: 168px; width: auto; object-fit: contain; display: block; }
.brand-logo--auth { max-height: 40px; max-width: 200px; }
.brand-icon { width: 24px; height: 24px; border-radius: 6px; object-fit: contain; flex: none; }

/* ---- Settings: section nav (sub-tabs) ---------------------- */
.subnav { display: flex; gap: 4px; margin-bottom: 22px; border-bottom: 1px solid var(--line); }
.subnav a {
    padding: 10px 14px; font-size: 13.5px; font-weight: 600; color: var(--muted);
    border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.subnav a:hover { color: var(--ink); text-decoration: none; }
.subnav a.is-active { color: var(--accent-ink); border-bottom-color: var(--accent); }

/* ---- Branding upload tiles --------------------------------- */
.brand-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 18px; }
.brand-tile { border: 1px solid var(--line); border-radius: var(--radius-s); padding: 16px; }
.brand-tile h3 { font-size: 13.5px; font-family: var(--body); font-weight: 600; margin-bottom: 4px; }
.brand-tile .hint { font-size: 12px; color: var(--faint); margin-bottom: 12px; }
.brand-preview {
    height: 84px; border: 1px dashed var(--line-strong); border-radius: var(--radius-s);
    display: grid; place-items: center; background: var(--surface-2); margin-bottom: 12px; overflow: hidden;
}
.brand-preview img { max-height: 64px; max-width: 90%; object-fit: contain; }
.brand-preview.is-dark { background: var(--code-bg); }
/* This swatch shows the logo against both backgrounds the product is used
   on, so one of the two is always dark whatever the theme — the text on
   it cannot come from a token that follows the theme. */
.brand-preview .none { color: var(--muted); font-size: 12px; }
.brand-preview.is-dark .none { color: var(--rail-ink-dim); }
.file-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
input[type="file"] { font-size: 12.5px; }
input[type="color"] { width: 46px; height: 38px; padding: 2px; border: 1px solid var(--line-strong); border-radius: var(--radius-s); background: var(--surface); cursor: pointer; }

/* ---- Lead fields list -------------------------------------- */
.field-type-badge {
    font-family: var(--mono); font-size: 11px; color: var(--muted);
    background: var(--surface-2); border: 1px solid var(--line); border-radius: 5px; padding: 2px 7px;
}
.move-stack { display: inline-flex; flex-direction: column; gap: 2px; }
.move-stack button {
    border: 1px solid var(--line-strong); background: var(--surface); cursor: pointer;
    width: 24px; height: 18px; line-height: 1; border-radius: 4px; font-size: 10px; color: var(--muted); padding: 0;
}
.move-stack button:hover { background: var(--surface-2); color: var(--ink); }
.tag-req { color: var(--danger); font-weight: 700; margin-left: 3px; }
.row-system { font-size: 10.5px; color: var(--faint); text-transform: uppercase; letter-spacing: .06em; }

/* ---- Update log box (2.0.1.1) ------------------------------ */
.log-box {
    font-family: var(--mono); font-size: 12px; line-height: 1.55;
    background: var(--code-bg); color: var(--code-ink); border-radius: var(--radius-s);
    padding: 14px 16px; margin: 0; white-space: pre-wrap; word-break: break-word;
    max-height: 320px; overflow: auto;
}

/* Update-history detail row: the release notes carried in a package's manifest. */
.grid__detail > td {
    padding-top: 0;
    border-top: 0;
    color: var(--muted);
    font-size: .86rem;
    line-height: 1.5;
}
.grid__detail p { margin: 0 0 .35rem; }
.grid__detail p:last-child { margin-bottom: 0; }

/* ============================================================
   Dashboard: the setup checklist, 2.0.8.0
   Shown while the four things a lead needs do not all exist. A fresh
   installation is the state the client sees first, so it says what to do
   rather than showing a grid of zeroes.
   ============================================================ */
.setup { list-style: none; counter-reset: setup; margin: 0; padding: 0; display: grid; gap: 10px; }
.setup__step {
    display: flex; align-items: center; gap: 14px;
    padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--radius-s);
    background: var(--surface-2);
}
.setup__step.is-done { background: var(--surface); border-color: var(--line); opacity: .72; }
.setup__mark {
    counter-increment: setup; flex: none;
    width: 26px; height: 26px; border-radius: 50%;
    display: grid; place-items: center;
    font-family: var(--display); font-weight: 700; font-size: 12px;
    background: var(--accent-soft); color: var(--accent-ink);
}
.setup__step.is-done .setup__mark { background: var(--ok-soft); color: var(--ok); }
.setup__mark:empty::before { content: counter(setup); }
.setup__text { display: flex; flex-direction: column; line-height: 1.35; min-width: 0; flex: 1; }
.setup__text strong { font-size: 13.5px; }
.setup__text small { color: var(--muted); font-size: 12.5px; }
.setup__step .btn, .setup__step .pill { margin-left: auto; flex: none; }

/* A box that has reached its daily cap, wherever the fraction is shown. */
.is-capped { color: var(--warn); font-weight: 600; }

/* A country heading inside the box grid: boxes compete within a country, so
   that is the grouping the list is read in. */
.grid__group > td {
    background: var(--surface-2); padding: 9px 16px;
    border-bottom: 1px solid var(--line); border-top: 1px solid var(--line);
}
.grid__group strong { font-size: 12.5px; }
.grid__group .cell-sub { margin-left: 10px; font-size: 12px; color: var(--faint); }
.grid tbody tr.grid__group:hover > td { background: var(--surface-2); }

/* ============================================================
   The shared search and filter panel
   Was a <details> drawer that opened over the table (2.0.8.0). It is inline
   and always open now: it pushes the list down rather than covering it, and
   there is nothing to open or dismiss. Two people reported the drawer
   independently — "непонятно вываливается", and "a Search panel (instead of
   the current pop up)" from someone who had not seen the screen.
   ============================================================ */
.filterbar {
    border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--surface-2); padding: 12px 14px 14px;
}
/* The summary is the line that never folds. list-style:none on both selectors
   because Safari draws the marker through ::-webkit-details-marker and ignores
   the standard one. */
.filterbar__fold > summary { list-style: none; cursor: pointer; }
.filterbar__fold > summary::-webkit-details-marker { display: none; }
.filterbar__top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
/* The disclosure triangle, drawn on the eyebrow so the whole line stays a
   click target without a control of its own taking a slot in the row. */
.filterbar__toggle::after {
    content: "▸"; display: inline-block; margin-left: 6px;
    transition: transform .15s ease; color: var(--faint);
}
.filterbar__fold[open] .filterbar__toggle::after { transform: rotate(90deg); }
/* A summary is a flex container badly on some engines: the children collapse
   to their intrinsic widths unless the display is stated here. */
.filterbar__fold > summary.filterbar__top { display: flex; }
.filterbar__fold:not([open]) > summary.filterbar__top { margin-bottom: 0; }
.filterbar__top .eyebrow { margin-right: 4px; }
.filterbar__clear { margin-left: auto; font-size: 12.5px; }
/* Pushed right whether or not "Clear all" is there to do the pushing. */
.filterbar__clear + .filterbar__go { margin-left: 0; }
.filterbar__go { margin-left: auto; }

/* Every filter beside every other one. auto-fit rather than a count, so a list
   with two filters does not leave two empty columns and a narrow window folds
   without a breakpoint. */
.filterbar__body {
    /* 165 and not 190. Every option list is capped at four rows, so a narrower
       column no longer makes a group taller — it only makes the labels inside
       it wrap. That is what buys the fifth column at 1280, and a second row of
       filters costs 186px where a wrapped box name costs nothing. */
    display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
    gap: 14px 12px; align-items: start;
}
.filterbar__group { display: grid; gap: 7px; align-content: start; }
/* The dates are thirteen presets and two inputs: a column of their own would
   wrap every one of them onto its own line. The two inputs sit beside the
   presets rather than under them — the presets wrap either way, so a row of
   their own cost 45px of every list screen to say nothing extra. */
.filterbar__group--wide {
    grid-column: 1 / -1;
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; gap: 6px 14px;
}
.filterbar__hint { grid-column: 1 / -1; }
.filterbar__count { color: var(--faint); letter-spacing: 0; text-transform: none; font-weight: 500; }

.filterbar__presets { display: flex; flex-wrap: wrap; gap: 6px; }
.radio-chip { cursor: pointer; }
.radio-chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.radio-chip span {
    display: inline-block; padding: 5px 11px; border-radius: 999px;
    border: 1px solid var(--line-strong); background: var(--surface);
    font-size: 12.5px; font-weight: 500; color: var(--muted); transition: all .12s;
}
.radio-chip:hover span { border-color: var(--accent); color: var(--ink); }
.radio-chip input:checked + span { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.radio-chip input:focus-visible + span { box-shadow: 0 0 0 3px var(--accent-soft); }

.filterbar__dates { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.filterbar__dates label { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); }
.filterbar__dates .input { width: auto; }
.filterbar__dates .hint { font-size: 12px; color: var(--faint); }

.filterbar__options {
    /* min(190px, 100%) and not 190px: this box is now a column of the panel
       rather than the width of a drawer, and a 190px minimum inside a 170px
       box overflows it — which showed up as a horizontal scrollbar under every
       filter and half the box names cut off. */
    display: grid; grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr)); gap: 2px;
    background: var(--surface);
    border: 1px solid var(--line); border-radius: var(--radius-s); padding: 6px;
}
/* One height for every list, so the panel's height is a property of how many
   filters a screen has and not of how many countries have taken a lead this
   month. Four rows, and the rest scrolls — with the scrollbar left visible,
   because it is the only thing on the screen that says there is more. */
.filterbar__options { max-height: 132px; overflow: hidden auto; scrollbar-gutter: stable; }
.filterbar__option { display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: 6px; cursor: pointer; font-size: 13px; }
.filterbar__option:hover { background: var(--surface-2); }
.filterbar__option input { width: 15px; height: 15px; accent-color: var(--accent); flex: none; }
.filterbar__option span { min-width: 0; overflow-wrap: anywhere; }
.filterbar__option.is-hidden { display: none; }

.input--sm { padding: 6px 10px; font-size: 13px; }

/* The chips: what is being filtered, without opening the drawer. */
.chip {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 11px; border-radius: 999px; font-size: 12.5px; font-weight: 500;
    border: 1px solid var(--line-strong); background: var(--surface); color: var(--muted);
    white-space: nowrap;
}
.chip--date { font-family: var(--mono); font-size: 12px; }
.chip--count { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-ink); font-weight: 600; }

/* ============================================================
   Country routing rules — one row per country, 2.0.11.0
   Was a flex row of five blocks with min-widths adding up past 900px, so the
   checkbox wrapped onto its own line and Save stranded beside it. A grid with
   an explicit collapse says what happens at every width instead.
   ============================================================ */
.rule {
    display: grid;
    grid-template-columns: minmax(170px, 1fr) minmax(190px, 1.1fr) minmax(220px, 1.3fr) minmax(210px, 1.2fr) auto;
    gap: 16px 20px; align-items: start;
    padding: 18px 22px; border-top: 1px solid var(--line);
}
.rule:first-of-type { border-top: 0; }
.rule--fallback { background: var(--surface-2); }

/* The country, aligned with the tops of the fields rather than their feet. */
.rule__who { padding-top: 22px; min-width: 0; }
.rule__who .cell-sub { margin-top: 2px; }

.rule .field { min-width: 0; }
.rule .hint { line-height: 1.4; }

/* A plain checkbox line, so this column is the same height as a select. */
.rule__check {
    display: flex; align-items: center; gap: 9px;
    min-height: 40px; cursor: pointer; font-size: 13.5px;
}
.rule__check input { width: 16px; height: 16px; accent-color: var(--accent); flex: none; }

.rule__save { padding-top: 26px; }

@media (max-width: 1180px) {
    .rule { grid-template-columns: minmax(170px, 1fr) minmax(190px, 1fr) minmax(210px, 1fr); }
    .rule__who { grid-column: 1 / -1; padding-top: 0; }
    .rule__save { grid-column: 1 / -1; padding-top: 0; }
}

@media (max-width: 720px) {
    .rule { grid-template-columns: 1fr; }
}

/* ============================================================
   The detail panel, 2.0.14.0
   A native <dialog> pinned to the right edge. Opened with showModal(), so the
   escape key, the backdrop and focus containment are the browser's job.
   ============================================================ */
.drawer {
    position: fixed; inset: 0 0 0 auto;
    margin: 0; padding: 0; border: 0;
    /* 900. Width on its own bought nothing: the cap row wrapped at every width
       because .form-row was two hardcoded columns, and the lead panel stayed the
       same height at any width because its .kv is one column of pairs that just
       grew fatter values. Both are fixed above and below, and 900 is where they
       both pay — the cap row stops wrapping, and the lead panel's pairs go
       two-up and lose 194px of height. Past 900 neither improves and only the
       list behind the drawer gets eaten. */
    width: min(900px, 100%); max-width: 100%; height: 100%; max-height: 100%;
    background: var(--bg); color: var(--ink);
    box-shadow: -18px 0 50px rgba(var(--shadow-ink),.18);
    display: flex; flex-direction: column;
}
.drawer::backdrop { background: var(--scrim-soft); }
.drawer:not([open]) { display: none; }

.drawer__bar {
    display: flex; align-items: center; gap: 14px; flex: none;
    padding: 0 18px; height: 56px;
    background: var(--surface); border-bottom: 1px solid var(--line);
}
.drawer__link { font-size: 13.5px; font-weight: 600; }
.drawer__close {
    margin-left: auto; background: none; border: 0; cursor: pointer;
    font-size: 26px; line-height: 1; color: var(--muted); padding: 2px 6px; border-radius: var(--radius-s);
}
.drawer__close:hover { background: var(--surface-2); color: var(--ink); }

/* The fragment is a detail page, so it arrives carrying its own cards — and
   stacked in one column they scroll long before the panel runs out of width.
   Two to a line is what puts more on one screen; widening the drawer alone did
   not, because a wider card just grows fatter values. auto-fit collapses the
   empty track, so a panel carrying a single card (the box form) still gets the
   full width, and below ~740px of body there is one column again with no media
   query. align-items: start so a short card is not stretched to match a tall
   one beside it. */
.drawer__body { flex: 1; overflow-y: auto; padding: 20px; }
/* A grid item is min-width: auto, so a card holding a table refuses to shrink
   below it and pushes the panel sideways. The tables scroll inside .table-wrap
   already; this lets them. */

/* A pill is one unbreakable run, so "Reported, not confirmed" poked 13px out of
   a 154px column. In a panel column it may use two lines; on a full page, where
   there is room, it stays on one. */
.drawer__body .kv .pill { white-space: normal; }
.drawer__body .card { box-shadow: none; }
.drawer__body .card + .card { margin-top: 16px; }
/* "Back to…" links belong to the page, not to a panel that has its own way out. */
.drawer__body .form-actions .btn--ghost[href] { display: none; }

.drawer__loading { padding: 40px 20px; text-align: center; color: var(--muted); }
/* Two pairs to a line, but only where there is room for two. Keyed to the card
   the list sits in rather than to the drawer: cards in the panel are two to a
   line, so "in the drawer" and "wide" stopped meaning the same thing — a two-up
   kv inside a 418px card overflowed it sideways. A browser without container
   queries gets one column, which is the layout this had before, so the failure
   is the old behaviour rather than a broken one. */
.drawer__body .card { container-type: inline-size; }
@container (min-width: 620px) {
    .kv--pairs { grid-template-columns: minmax(0, auto) minmax(0, 1fr) minmax(0, auto) minmax(0, 1fr); }
}

/* ---- Rows that open it ------------------------------------- */
/* Not display:block any more. That came from "the whole first cell is the
   target", and since 2.0.15.0 the whole row is — so all the block did was fill
   the cell, push the copy button onto a second line, and make a bold full-width
   slab look more like the button than the button did. */
.row-open { font-weight: 600; color: var(--ink); }
/* The id and its copy button are one line or they are a bug. The column grows
   to whatever that costs. */
.grid td:has(.copy-id) { white-space: nowrap; }
.row-open:hover { color: var(--accent); text-decoration: none; }
.grid tbody tr:has(.row-open):hover { cursor: pointer; }
/* The one button allowed back into a row after 2.0.14.0 took the column out.
   It earns it by doing what nothing else on the page can do — get an id out —
   and pays for it by staying out of sight until the row is under the pointer.
   Focus shows it too, so it is reachable without one. */
.copy-id {
    margin-left: 8px; padding: 1px 7px; min-width: 52px;
    border: 1px solid var(--line-strong); border-radius: 5px;
    background: var(--surface); color: var(--muted);
    font: inherit; font-size: 11px; line-height: 1.7; cursor: pointer;
    transition: opacity .12s, color .12s, border-color .12s;
}
.copy-id:hover, .copy-id:focus { color: var(--accent); border-color: var(--accent); }
.grid tbody .copy-id { opacity: 0; }
/* :focus, not :focus-visible. Tab does set :focus-visible, but only the browser
   decides when — and the cost of it deciding not to is a button somebody has
   tabbed onto and cannot see. Revealing on any focus cannot be wrong: a click
   focuses it too, and the row is already hovered by then. */
.grid tbody tr:hover .copy-id, .grid tbody .copy-id:focus { opacity: 1; }

/* Except over a cell that does something of its own — send it somewhere,
   confirm a deposit. The pointer there is about that control, not the row. */
.grid tbody tr:has(.row-open) td:has(form) { cursor: default; }

@media (max-width: 640px) {
    .drawer { width: 100%; }
}

/* ============================================================
   Reports
   ------------------------------------------------------------
   No chart library. Drawing thirty rectangles does not need 90kB of
   JavaScript, and CLAUDE.md §2 rules out the CDN that would serve it — so the
   chart is a flex row of stacked spans and the shares are spans with a width.
   Everything here degrades to legible text if the stylesheet never arrives.
   ============================================================ */

/* Two panels side by side where there is room, one below the other where there
   is not. auto-fit rather than a media query: the drawer and the page have
   different widths and neither should need its own breakpoint. */
.report-pair {
    display: grid; gap: 20px; margin-top: 20px;
    grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
    align-items: start;
}

/* Denser than the page-level stat grid: these sit inside a card that already
   has its own padding, so they do not need to hold the width of the screen. */
.stat-grid--tight { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.stat-grid--tight .stat { padding: 14px; }
.stat-grid--tight .stat__value { font-size: 24px; }

.chart {
    display: flex; align-items: flex-end; gap: 2px;
    height: 180px; padding: 0 2px;
    border-bottom: 1px solid var(--line-strong);
}
/* Every column holds the full height so a quiet day still has a hit area and a
   tooltip — a day with no leads is a fact about the week, not a gap in it. */
.chart__col { flex: 1 1 0; min-width: 0; height: 100%; display: flex; align-items: flex-end; }
.chart__stack {
    width: 100%; min-height: 1px; display: flex; flex-direction: column-reverse;
    border-radius: 2px 2px 0 0; overflow: hidden;
}
.chart__seg { display: block; width: 100%; }
.chart__seg.is-routed  { background: var(--ok); }
.chart__seg.is-refused { background: var(--warn); }
.chart__seg.is-failed  { background: var(--danger); }
.chart__seg.is-none    { background: var(--line-strong); }

.chart__axis {
    display: flex; justify-content: space-between; gap: 12px;
    margin-top: 8px; font-size: 12px; color: var(--muted); font-family: var(--mono);
}

.legend { display: inline-flex; align-items: center; gap: 6px; margin-left: 14px; font-size: 12px; color: var(--muted); }
.legend__key { width: 10px; height: 10px; border-radius: 2px; background: var(--line-strong); }
.legend__key.is-routed  { background: var(--ok); }
.legend__key.is-refused { background: var(--warn); }
.legend__key.is-failed  { background: var(--danger); }
.legend__key.is-none    { background: var(--line-strong); }

/* The bar is decoration; the percentage beside it is the value. A reader who
   cannot judge a length still gets the number. */
.share { display: flex; align-items: center; gap: 8px; min-width: 120px; }
.share__bar { flex: 1 1 auto; height: 6px; border-radius: 3px; background: var(--surface-2); overflow: hidden; }
.share__bar > span { display: block; height: 100%; background: var(--accent); opacity: .75; }
.share__pct { flex: none; width: 36px; text-align: right; font-family: var(--mono); font-size: 12px; color: var(--muted); }

/* A card sharing a row is ~620px wide, and "The leads themselves" wrapped its
   button onto two lines there. A wrapped button label is the 2.0.16.0 defect in
   another costume; scoped to the pair so nothing else inherits a nowrap it did
   not ask for. */
.report-pair .card__head .right .btn { white-space: nowrap; }

/* ============================================================
   Traffic
   ============================================================ */

/* The same chip the filter drawer uses, as a link rather than a radio. A list
   filtered by a link survives a bookmark and the back button, which a radio
   inside a form does not, and there is nothing to submit. */
a.radio-chip { text-decoration: none; }
a.radio-chip.is-on > span { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

/* A snippet is meant to be read by somebody outside this company — it gets
   forwarded to whoever maintains a landing page — so it is presented as a
   document, not as a form field: legible, selectable, and with the copy button
   beside the thing it copies rather than floating above a wall of code. */
.snippet { border: 1px solid var(--line); border-radius: var(--radius-s); overflow: hidden; }
.snippet + .snippet { margin-top: 14px; }
.snippet__head {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 14px; background: var(--surface-2); border-bottom: 1px solid var(--line);
    font-size: 13.5px;
}
.snippet__actions { margin-left: auto; }
/* The one place a horizontal scrollbar is right: wrapping a URL makes it look
   like two lines of code, and somebody will paste it that way. */
.snippet__code {
    margin: 0; padding: 14px; overflow-x: auto;
    background: var(--surface); font-family: var(--mono); font-size: 12px; line-height: 1.7;
    color: var(--ink); white-space: pre;
}
.snippet__note {
    padding: 12px 14px; border-top: 1px solid var(--line);
    background: var(--warn-soft); color: var(--ink); font-size: 12.5px;
}
/* The copy button is faint inside a table row, where it appears on hover. Here
   it is the point of the panel and is always visible. */
.snippet__actions .copy-id { opacity: 1; }

/* ============================================================
   The table component — sort, reorder, hide
   The markup is the table the server already sends; everything here styles
   what app.js adds to it. With scripts off none of these selectors match
   anything, which is the intended end state rather than a fallback.
   ============================================================ */
.tablebar { display: flex; justify-content: flex-end; padding: 10px 16px 0; }
.card__body--flush .tablebar { padding: 10px 16px 0; }
.cols { position: relative; }
.cols > summary { list-style: none; cursor: pointer; }
.cols > summary::-webkit-details-marker { display: none; }
.cols[open] > summary { background: var(--surface-2); border-color: var(--accent); }
.cols__panel {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 30;
    width: min(320px, calc(100vw - 40px));
    max-height: min(60vh, 460px); overflow-y: auto;
    background: var(--surface); border: 1px solid var(--line-strong);
    border-radius: var(--radius); box-shadow: 0 16px 44px rgba(var(--shadow-ink),.18);
    padding: 14px 16px;
}
.cols__head { display: flex; align-items: center; margin-bottom: 10px; }
.cols__head .btn-link { margin-left: auto; font-size: 12.5px; }
.cols__list { display: grid; gap: 1px; }
.cols__item { display: flex; align-items: center; gap: 9px; padding: 7px 8px; border-radius: 6px; cursor: pointer; font-size: 13px; }
.cols__item:hover { background: var(--surface-2); }
.cols__item input { width: 15px; height: 15px; accent-color: var(--accent); flex: none; }
.cols__panel .hint { display: block; margin-top: 12px; font-size: 12px; color: var(--faint); }

/* A hidden column. Nothing sets `display` on a cell, so this needs no weight
   to win — and it hides the cell rather than emptying it, because emptying
   one would mean this component touching a value it has no business reading. */
.is-col-off { display: none; }

/* Sortable headings, and the one that is sorted. The arrow is the only thing
   on the screen that says a table is not in the order it arrived in, so it is
   not decorative. */
.grid th.is-sortable { cursor: pointer; user-select: none; }
.grid th.is-sortable:hover { color: var(--ink); }
.grid th[data-dir="asc"]::after  { content: " ↑"; color: var(--accent); }
.grid th[data-dir="desc"]::after { content: " ↓"; color: var(--accent); }
.grid th.is-dragging { opacity: .4; }
.grid th.is-drop { box-shadow: inset 3px 0 0 var(--accent); }

/* A heading that sorts on the server is a link, because it is one: it reloads
   the page with ?sort= and the whole list is ordered, not the rows on screen. */
.th-sort { color: inherit; display: inline-block; }
.th-sort:hover { color: var(--ink); text-decoration: none; }
.th-sort.is-on { color: var(--accent); }
.grid th .th-fixed { cursor: help; border-bottom: 1px dotted var(--line-strong); }

/* ---- A long select, with its search above it ---------------- */
/* ---- A dropdown with the search inside it -------------------
   Asked for as "дропдаун с поиском": click, type, the list narrows in place.
   The first attempt stacked a search box on top of the select and cost 39px on
   every control; this one costs nothing until it is open, which is what makes
   "search on box selects always" affordable.

   The native <select> is still what submits. It stays in the layout at opacity
   0 rather than display:none so the browser can focus it to report a required
   field — hidden outright it refuses to submit and says nothing. */
.combo { position: relative; display: block; }
.combo__native {
    position: absolute; inset: 0; width: 100%; height: 100%;
    opacity: 0; pointer-events: none; z-index: -1;
}
.combo__button {
    display: flex; align-items: center; gap: 8px; text-align: left; cursor: pointer;
    /* A button inherits the page's 1.5 line-height and lands taller than a
       select; `normal` lands shorter. The height that actually matters is set
       from JavaScript, which measures the select it is standing in front of —
       a select's height comes from the font's metrics, and the number is not
       the same when this console's webfont fails to arrive. */
    line-height: normal;
}
/* The arrow a select has and a button does not. */
.combo__button::after {
    content: ''; margin-left: auto; flex: none;
    width: 8px; height: 8px; border-right: 1.5px solid var(--faint); border-bottom: 1.5px solid var(--faint);
    transform: translateY(-2px) rotate(45deg);
}
.combo__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.combo__button.is-empty .combo__value { color: var(--faint); }

.combo__pop {
    position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% + 4px);
    background: var(--surface); border: 1px solid var(--line-strong);
    border-radius: var(--radius-s); box-shadow: var(--shadow-card);
    padding: 6px; display: grid; gap: 6px;
}
/* An author `display` beats the UA stylesheet's [hidden] { display: none }, so
   the rule above left every popup on the page: laid out, painted over whatever
   was under it, and reporting `hidden === true` the whole time. Reading the
   property said closed; elementFromPoint 30px below the button returned the
   search field. The property is what the code sets; this is what the page does. */
.combo__pop[hidden] { display: none; }
.combo--up .combo__pop { top: auto; bottom: calc(100% + 4px); }
.combo__search { width: 100%; }
/* Ten rows before it scrolls: enough to search inside, short enough that the
   popup does not cover the field under it on a form. */
.combo__list {
    margin: 0; padding: 0; list-style: none; max-height: 260px; overflow-y: auto; outline: none;
}
.combo__option {
    padding: 7px 9px; border-radius: 5px; font-size: 14px; cursor: pointer;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.combo__option.is-active { background: var(--accent-soft); color: var(--accent-ink); }
.combo__option[aria-selected="true"] { font-weight: 600; }
.combo__option[aria-selected="true"]::after { content: ' ✓'; color: var(--accent); }
.combo__option[aria-disabled="true"] { color: var(--faint); cursor: default; }
.combo__group {
    padding: 8px 9px 3px; font-size: 11.5px; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--faint);
}
.combo__empty { margin: 0; padding: 8px 9px; font-size: 13px; color: var(--muted); }

/* ---- What is filtering this list, on the bar itself --------- */
.chip--on { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-ink); }
.chip__x {
    color: inherit; opacity: .6; font-size: 15px; line-height: 1;
    margin: -2px -4px -2px 0; padding: 2px 5px; border-radius: 999px;
    /* It is an <a> on a filter chip and a <button> on a saved view — removing
       a filter is a link, forgetting a view is a POST. The button's own
       background and border would draw a grey disc where the link draws
       nothing. */
    background: none; border: 0; font-family: inherit; cursor: pointer;
}
.chip__x:hover { opacity: 1; background: var(--accent-line); text-decoration: none; }

/* A second date range lives in a filter column rather than across the top, so
   its two inputs stack instead of sitting side by side. */
.filterbar__dates--stacked { flex-direction: column; align-items: stretch; gap: 6px; }
.filterbar__dates--stacked label { justify-content: space-between; }
.filterbar__dates--stacked .input { width: auto; flex: 1; min-width: 0; }

/* A short proper noun keeps its line. With seventeen columns the table is wider
   than the window and scrolls inside .table-wrap anyway, so letting "Lab UK
   Prime" break across three lines buys no width back — it only makes the row
   three lines tall. Only cells that hold a name, never ones that hold a
   sentence. */
.grid td.cell-tight { white-space: nowrap; }

/* A row whose field is switched off elsewhere: readable, and not editable here. */
.grid tbody tr.is-off { opacity: .6; }

/* The inherited answer is named inside the option — "As set globally
   (optional)" — so the control needs the room to say it. A select that clips
   its own text at "(" is a control that has to be opened to be read. */
.select-required { min-width: 190px; }

/* ============================================================
   Dark theme
   ------------------------------------------------------------
   Named in the brief the outside designer was given — "Общая дизайн-система:
   тёмная тема, мобильный сценарий, состояния, тексты" — and built in 2.9.0.0.

   Only custom properties are redefined. Not one rule below the token block is
   duplicated for the dark theme, and that is the test of whether the tokens
   are the right ones: a theme that needed its own rules would mean a colour
   was still written into a rule somewhere. Fifty-six of them were, before this
   release lifted them out.

   Three states, which is what a theme switch actually has:

     no attribute      follow the operating system
     data-theme=dark   dark, whatever the system says
     data-theme=light  light, whatever the system says

   So the media query is guarded against an explicit light choice, and the
   attribute rule repeats the values for an explicit dark one. Plain CSS has no
   way to name one block from two selectors; the two are generated from one
   source and must be edited together.
   ============================================================ */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
    /* Page darker than the rail, so the rail stays a distinct object. In the
       light theme it is the other way round; either way it is not the page. */
    --bg:          #0d121b;
    --surface:     #151c28;
    --surface-2:   #1a2230;
    --ink:         #e4eaf4;
    --muted:       #9aa7ba;
    /* 4.23:1 against --surface-2 at #78849a, and the headings and eyebrows this
       paints are 11px uppercase, so AA wants 4.5. Measured, not guessed. */
    --faint:       #8894ab;
    --line:        #26303f;
    --line-strong: #354154;

    /* --accent is NOT redefined here. The operator sets it at Settings and it
       arrives as an inline :root override after this file, so anything written
       here would either lose to it or need !important and take their choice
       away. What is derived from it is adjusted instead: an accent picked
       against a white page is too dark to read on this one.

       color-mix is the whole mechanism and it degrades honestly — a browser
       that does not know it ignores the declaration and keeps the line above,
       which is the light value: readable, merely not ideal. */
    --accent-ink:  #9bb7ec;
    --accent-ink:  color-mix(in srgb, var(--accent) 45%, #ffffff);
    --accent-soft: #1a2945;
    --accent-soft: color-mix(in srgb, var(--accent) 24%, var(--surface));
    --accent-line: #29467f;
    --accent-line: color-mix(in srgb, var(--accent) 42%, var(--surface));

    /* Status colours lighten and their fills darken: the same pill, read
       against a dark card instead of a white one. */
    --ok:          #4ec79a;
    --ok-soft:     #14332a;
    --ok-line:     #1e5a45;
    --warn:        #e3a44a;
    --warn-soft:   #35270f;
    --warn-line:   #5d4416;
    --warn-ink:    #f0c281;
    --danger:      #f07a70;
    --danger-soft: #38191a;
    --danger-line: #6d2f2c;
    --danger-line-soft: #5a2724;

    /* The rail sits above the page rather than below it. */
    --rail-bg:      #19212f;
    --rail-edge:    #0a0e16;
    --rail-ink:     #c7d0de;
    --rail-ink-dim: #8c98ab;
    --rail-ink-low: #67738a;
    --rail-eyebrow: #7b889d;
    --rail-hover:   #232d3e;
    --rail-line:    #2b3547;
    --rail-user:    #e9eef6;
    --rail-btn-ink: #a3aec0;
    --rail-btn-line:#333e52;
    --rail-btn-hover-line: #45526a;

    /* Black, and heavier: a shadow tinted with the light theme's slate is
       invisible on a dark surface, so the card edge disappears with it. */
    --shadow-ink:  0,0,0;
    --shadow-card: 0 1px 2px rgba(0,0,0,.4), 0 6px 20px rgba(0,0,0,.35);
    --scrim:       rgba(0,0,0,.6);
    --scrim-soft:  rgba(0,0,0,.55);

    --auth-glow-1: #172238;
    --auth-glow-2: #14202f;
    --code-bg:     #0c111a;
    --code-ink:    #c8d2e0;
    }
}
:root[data-theme="dark"] {
    /* Page darker than the rail, so the rail stays a distinct object. In the
       light theme it is the other way round; either way it is not the page. */
    --bg:          #0d121b;
    --surface:     #151c28;
    --surface-2:   #1a2230;
    --ink:         #e4eaf4;
    --muted:       #9aa7ba;
    /* 4.23:1 against --surface-2 at #78849a, and the headings and eyebrows this
       paints are 11px uppercase, so AA wants 4.5. Measured, not guessed. */
    --faint:       #8894ab;
    --line:        #26303f;
    --line-strong: #354154;

    /* --accent is NOT redefined here. The operator sets it at Settings and it
       arrives as an inline :root override after this file, so anything written
       here would either lose to it or need !important and take their choice
       away. What is derived from it is adjusted instead: an accent picked
       against a white page is too dark to read on this one.

       color-mix is the whole mechanism and it degrades honestly — a browser
       that does not know it ignores the declaration and keeps the line above,
       which is the light value: readable, merely not ideal. */
    --accent-ink:  #9bb7ec;
    --accent-ink:  color-mix(in srgb, var(--accent) 45%, #ffffff);
    --accent-soft: #1a2945;
    --accent-soft: color-mix(in srgb, var(--accent) 24%, var(--surface));
    --accent-line: #29467f;
    --accent-line: color-mix(in srgb, var(--accent) 42%, var(--surface));

    /* Status colours lighten and their fills darken: the same pill, read
       against a dark card instead of a white one. */
    --ok:          #4ec79a;
    --ok-soft:     #14332a;
    --ok-line:     #1e5a45;
    --warn:        #e3a44a;
    --warn-soft:   #35270f;
    --warn-line:   #5d4416;
    --warn-ink:    #f0c281;
    --danger:      #f07a70;
    --danger-soft: #38191a;
    --danger-line: #6d2f2c;
    --danger-line-soft: #5a2724;

    /* The rail sits above the page rather than below it. */
    --rail-bg:      #19212f;
    --rail-edge:    #0a0e16;
    --rail-ink:     #c7d0de;
    --rail-ink-dim: #8c98ab;
    --rail-ink-low: #67738a;
    --rail-eyebrow: #7b889d;
    --rail-hover:   #232d3e;
    --rail-line:    #2b3547;
    --rail-user:    #e9eef6;
    --rail-btn-ink: #a3aec0;
    --rail-btn-line:#333e52;
    --rail-btn-hover-line: #45526a;

    /* Black, and heavier: a shadow tinted with the light theme's slate is
       invisible on a dark surface, so the card edge disappears with it. */
    --shadow-ink:  0,0,0;
    --shadow-card: 0 1px 2px rgba(0,0,0,.4), 0 6px 20px rgba(0,0,0,.35);
    --scrim:       rgba(0,0,0,.6);
    --scrim-soft:  rgba(0,0,0,.55);

    --auth-glow-1: #172238;
    --auth-glow-2: #14202f;
    --code-bg:     #0c111a;
    --code-ink:    #c8d2e0;
}

/* ---- inside a traffic page's archive (2.10.0.0) -------------------------- */

/* The frame the archived page renders in.
   A white ground under it and not the console's: a landing page written for a
   white background, shown on a dark surface, reads as a broken page rather than
   as a page being previewed — and in the dark theme that is exactly what the
   operator would be looking at. */
.archive-view { border-top: 1px solid var(--line); }
.archive-view__bar {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    flex-wrap: wrap; padding: 10px 16px; border-bottom: 1px solid var(--line);
    background: var(--surface-2);
}
.archive-view__frame {
    display: block; width: 100%; height: min(70vh, 760px); border: 0;
    background: var(--on-light);
}

/* The editor. A textarea and nothing else: there is no editor library in this
   product and there is not going to be one — §2 allows no dependency, and a
   syntax highlighter written here would be a second product to maintain. What a
   person editing a landing page actually needs is a monospaced box that does
   not wrap, does not autocorrect, and lets Tab indent. */
.code-edit {
    font-family: var(--mono); font-size: 12.5px; line-height: 1.55;
    min-height: 420px; white-space: pre; overflow-wrap: normal; overflow-x: auto;
    tab-size: 4; background: var(--code-bg); color: var(--code-ink);
    border-color: var(--line-strong);
}

/* The selection bar: what to do with the rows that are ticked.
   Sticky rather than fixed, so it sits at the bottom of the viewport while
   there is list below it and comes to rest at the end of the list rather than
   floating over the page's own footer. `hidden` is honoured — the bar is a
   form element and nothing here fights the attribute. */
.bulkbar {
    position: sticky; bottom: 12px; z-index: 30;
    display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
    margin-top: 12px; padding: 10px 14px;
    background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius);
    box-shadow: 0 8px 24px rgba(24, 32, 46, .18);
}
.bulkbar[hidden] { display: none; }
/* Room under the last row for the selection bar, which is sticky to the
   viewport while the table scrolls inside its own box — so without this it
   stands on the last rows and the table's own scroll ends before them.
   `--bulkbar-room` is the bar's measured height, set by app.js and 0 while the
   bar is down; the bar wraps, so a number written here would be wrong at half
   the widths the console is used at. */
.table-wrap { padding-bottom: var(--bulkbar-room, 0px); }
.bulkbar__count { margin-right: 4px; white-space: nowrap; }

/* One action inside the bar: its own form, so its own row of controls. The
   rule between them is what stops three sets of buttons reading as one row of
   seven, and it is drawn on the left edge rather than as a separate element so
   that a wrapped bar does not leave a rule hanging at the end of a line. */
.bulkbar__action {
    display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
    padding-left: 12px; border-left: 1px solid var(--line);
}
.bulkbar__action:first-of-type { padding-left: 0; border-left: 0; }
.bulkbar__label { color: var(--muted); white-space: nowrap; }
@media (max-width: 640px) {
    .bulkbar { position: static; }
}

/* Open now, or not: the one thing a row's hours and its Active pill together
   do not say. Filled by the engine's own schedule check, so it cannot drift
   from the behaviour it describes. */
/* ---- A box row's day, drawn -------------------------------------------
   Replaces the open/closed dot shipped in 2.12.2.0. TrackBox draws 96
   quarter-hour <div>s per bar; at fifty rows on a page that is 4,800 nodes
   for something a repeating gradient paints identically and for free. The
   filled block is today's window, the line is the server's current minute,
   and the timezone is named beside the bar on the form because a window
   without one is an opinion. */
/* 150px is a floor, not a preference: below it the 96 ticks fall under a pixel
   each and the texture becomes a smear. Theirs is 200px in the same place. */
.hours { display: block; width: 100%; min-width: 150px; max-width: 220px; }
.hours__track {
    position: relative; height: 18px; border-radius: 4px; overflow: hidden;
    border: 1px solid var(--line);
    background:
        repeating-linear-gradient(to right, var(--line) 0 1px, transparent 1px calc(100% / 96)),
        var(--surface-2);
}
.hours__open {
    position: absolute; top: 0; bottom: 0;
    background: var(--ok); opacity: .45; min-width: 1px;
}
/* --ink rather than --accent: the line has to read on top of the filled window
   as well as on the empty track, and indigo on green is the one pairing where
   it disappears. --ink inverts with the theme, so it contrasts with both. */
.hours__now {
    position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px;
    background: var(--ink); box-shadow: 0 0 0 1px var(--surface-2);
}
.hours__labels {
    display: flex; justify-content: space-between; align-items: baseline;
    font-size: 10px; color: var(--faint); margin-top: 4px;
}
.hours__tz { font-family: var(--mono); letter-spacing: .04em; }
.hours--lg { max-width: none; }
.hours--lg .hours__track { height: 30px; }

/* ---- Boxes, as cards --------------------------------------------------
   TrackBox's own box list, which is what an operator reaching for one is
   reading: the name, the id an affiliate is given, how many rows it holds
   and whether anything has come through lately. A table of four columns
   said the same thing in a shape nobody scans. */
.box-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(224px, 1fr)); gap: 14px; }
.box-card {
    display: block; padding: 14px 16px; text-decoration: none; color: inherit;
    background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
    transition: border-color .12s ease, box-shadow .12s ease;
}
/* `text-decoration: none` above is not enough on its own: `a:hover` is a tag
   plus a pseudo-class and outranks it, so hovering a card underlined the name,
   the id, the note and both meta lines at once. The card draws its own hover —
   a border and a shadow — and nothing in it is a link of its own. */
.box-card:hover, .box-card:focus-visible {
    border-color: var(--accent); box-shadow: var(--shadow-card); text-decoration: none;
}
.box-card__name { font-family: var(--display); font-weight: 600; font-size: 16px; }
.box-card__id   { font-family: var(--mono); font-size: 11px; color: var(--faint); margin-top: 2px; }
.box-card__note { font-size: 12px; color: var(--muted); margin-top: 8px; }
.box-card__meta {
    display: flex; justify-content: space-between; gap: 10px;
    font-size: 12px; color: var(--muted); margin-top: 12px;
    padding-top: 10px; border-top: 1px solid var(--line);
}

/* ---- The rail's queue badges ------------------------------------------
   Two entries in the navigation are work rather than a place: a lead nothing
   took, and a deposit nobody has looked at. The figure is drawn only when it is
   above zero — a badge reading 0 on every page teaches an operator to stop
   reading badges, which costs the ones that mean something. */
.navlink { display: flex; align-items: center; gap: 8px; }
.navlink__label { flex: 1; min-width: 0; }
.navlink__count {
    flex: none; font-family: var(--mono); font-size: 10.5px; line-height: 1;
    padding: 3px 6px; border-radius: 999px;
    background: var(--warn-soft); color: var(--warn);
}
.navlink.is-active .navlink__count { background: var(--surface); }

/* ---- Jump to (ctrl/cmd + K) -------------------------------------------
   Their answer to a product with more screens than a rail can hold, and ours
   for the same reason: nineteen destinations under six headings do not fit a
   600px-tall window, and scrolling a menu to find out what exists is not
   finding out what exists.

   It is built from the rendered rail rather than from a list of its own, so it
   can never offer a screen the operator may not open — the rail is already
   gated on permissions, and a second list would drift from it. */
.palette[hidden] { display: none; }
.palette {
    position: fixed; inset: 0; z-index: 200;
    display: flex; justify-content: center; align-items: flex-start;
    padding: 12vh 16px 16px;
    background: rgba(var(--shadow-ink), .45);
}
.palette__panel {
    width: min(520px, 100%); max-height: 70vh; display: flex; flex-direction: column;
    background: var(--surface); border: 1px solid var(--line-strong);
    border-radius: var(--radius); box-shadow: var(--shadow-card); overflow: hidden;
}
.palette__input {
    border: 0; border-bottom: 1px solid var(--line); background: none; color: inherit;
    font: inherit; font-size: 15px; padding: 14px 16px; width: 100%;
}
.palette__input:focus { outline: none; }
.palette__list { overflow: auto; padding: 6px; }
.palette__item {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 9px 10px; border-radius: var(--radius-s);
    color: inherit; text-decoration: none; font-size: 13.5px;
}
.palette__item:hover { text-decoration: none; }   /* same as .box-card:hover — it is the row, not a word in it */
.palette__item[aria-selected="true"] { background: var(--accent-soft); color: var(--accent-ink); }
.palette__where { font-size: 11px; color: var(--faint); }
.palette__empty { padding: 14px 16px; font-size: 13px; color: var(--muted); }
.palette__hint {
    border-top: 1px solid var(--line); padding: 8px 16px;
    font-size: 11px; color: var(--faint); display: flex; gap: 14px;
}


/* ---- Traffic rules on a box row (2.16.0.0) ----------------------------
   Three words to a line, in the order they are read out loud: what, whether,
   which. The four value controls sit in the same column and all but one are
   disabled, so the line is one control wide however it is filled in. */
.cond {
    display: grid;
    grid-template-columns: minmax(140px, 1fr) 84px minmax(160px, 1.3fr) 32px;
    gap: 8px; align-items: center; margin-bottom: 8px;
}
.cond .input { margin: 0; }
.cond__op { text-align: center; }
.cond__x {
    padding: 0; width: 32px; min-width: 32px; line-height: 1;
    font-size: 17px; color: var(--faint); justify-content: center;
}
.cond__x:hover { color: var(--danger); border-color: var(--danger-line); }
.cond-foot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 4px; }
.cond-foot .hint { margin: 0; }
@media (max-width: 720px) {
    .cond { grid-template-columns: 1fr 1fr; }
    .cond [data-rule-value] { grid-column: 1 / -2; }
}

/* The conditions a row carries, printed under its name in the box's list.
   One line, because the question the list answers is "is there anything on
   this row besides its country", and the row's own form answers the rest. */
.rule-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.rule-tag {
    font-size: 10.5px; font-family: var(--mono); line-height: 1.6;
    padding: 0 6px; border-radius: 4px;
    background: var(--accent-soft); color: var(--accent-ink);
}
.rule-tag--not { background: var(--danger-soft); color: var(--danger); }

/* ---- Traffic rules (2.17.0.0) ----------------------------------------
   A card per rule, three bands inside it read downwards: who and where,
   when, what it does. The same shape the system being replaced uses, because
   that is the shape the people using this already read. */
.clause {
    border: 1px solid var(--line); border-radius: var(--radius);
    background: var(--surface); margin-bottom: 14px; overflow: hidden;
}
.clause__head {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 10px 14px; background: var(--surface-2); border-bottom: 1px solid var(--line);
}
/* Numbered by CSS rather than by the template, so a rule added or removed
   renumbers the rest without anything being told to. */
[data-clauses] { counter-reset: clause; }
.clause { counter-increment: clause; }
.clause__title { font-family: var(--display); font-size: 14px; }
.clause__title::before { content: 'Rule ' counter(clause); }
.clause__action { width: auto; min-width: 104px; }
.clause__says { margin: 0; }
.clause__x { margin-left: auto; padding: 0; width: 30px; min-width: 30px; font-size: 17px;
             line-height: 1; justify-content: center; color: var(--faint); }
.clause__x:hover { color: var(--danger); border-color: var(--danger-line); }
.clause__body { padding: 14px; display: grid; gap: 12px; }
.clause__band {
    font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
    color: var(--accent-ink);
}
/* The AND their screen draws between the filter and the hours. It is not
   decoration: it is the only thing on the card that says the two bands are
   both required rather than alternatives. */
.clause__and {
    display: flex; align-items: center; gap: 10px;
    font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--faint);
}
.clause__and::before, .clause__and::after {
    content: ''; flex: 1; height: 1px; background: var(--line);
}
.clause__more > summary, .clause__cond > summary {
    cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--accent-ink);
    padding: 4px 0; list-style: none;
}
.clause__more > summary::-webkit-details-marker,
.clause__cond > summary::-webkit-details-marker { display: none; }
.clause__more > summary::before, .clause__cond > summary::before {
    content: '▸ '; color: var(--faint);
}
.clause__more[open] > summary::before, .clause__cond[open] > summary::before { content: '▾ '; }
.clause__cond {
    border: 1px solid var(--line); border-radius: var(--radius-s);
    padding: 8px 12px; background: var(--surface-2);
}
.clause__cond + .clause__cond { margin-top: -4px; }

/* A filter: a searchable select that adds a chip, and the chips it added.
   The select never submits — the hidden inputs in the chips do — which is how
   244 countries are picked without a native multi-select. */
.pick__chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.pick__chips:empty { display: none; }

/* The week, one row per day, so that a window differing on Saturday can be
   said — which is the thing their own screen cannot express. */
/* One explicit column, or the grid sizes itself to the widest day row and the
   note underneath wraps inside 260px. */
.hours { display: grid; gap: 4px; grid-template-columns: minmax(0, 1fr); }
.hours__day {
    display: grid; grid-template-columns: 18px 44px 1fr 1fr; gap: 8px; align-items: center;
    font-size: 12.5px; max-width: 420px;
}
.hours__day input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--accent); }
.hours__foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.hours__foot .hint { margin: 0; }
@media (max-width: 720px) {
    .hours__day { grid-template-columns: 18px 44px 1fr 1fr; font-size: 12px; }
}

/* ============================================================
   One heading per screen, fewer boxes, denser lists — 2.19.2.0
   From a design review of the running console. Every screen said its
   title twice: once in the page bar and again on the card beneath it,
   and the card then held the list, whose filter sat in a box of its own
   inside that. What follows is layout only.
   ============================================================ */

/* The page is the surface. A grey page under white cards is what made every
   screen a stack of boxes; with the page the colour of a card, a card is only
   drawn where its edge means something. --bg takes the value --surface has in
   each theme, so every contrast measured against a card still holds. */
:root { --bg: #ffffff; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --bg: #151c28; } }
:root[data-theme="dark"] { --bg: #151c28; }

.card, .stat { box-shadow: none; }
.card + .card { margin-top: 28px; }

/* ---- The screen's own card ---------------------------------
   `card--page` marks the card that *is* the screen — Leads on the leads
   list, the lead on its own page. Its heading becomes the page heading and
   the page bar's copy of the title steps aside. Scoped to `.content >` so
   the same view opened in the detail panel keeps its card as it was.

   :has() decides only whether a title is shown twice. A browser without it
   shows both, which is what every screen did before. */
.content > .card--page { background: none; border: 0; border-radius: 0; }
.content > .card--page > .card__head { padding: 0 0 16px; border-bottom: 0; align-items: flex-end; }
.content > .card--page > .card__head h2 { font-size: 24px; letter-spacing: -.02em; line-height: 1.2; }
.content > .card--page > .card__head .sub { font-size: 13px; margin-top: 3px; }
.content > .card--page > .card__body { padding: 0 0 20px; }
/* A list is the one thing on the page that keeps a frame: one box, the data,
   and not a box around a box around it. */
.content > .card--page > .card__body--flush {
    padding: 0; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
}

:where(.main:has(> .content > .card--page)) .pagebar { padding-top: 0; }
:where(.main:has(> .content > .card--page)) .pagebar__title {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* ---- The rail folds from the rail --------------------------- */
.rail__fold {
    position: absolute; top: 20px; right: 12px;
    display: grid; place-items: center; width: 26px; height: 26px;
    border-radius: var(--radius-s); color: var(--rail-ink-dim); cursor: pointer;
}
.rail__fold:hover { background: var(--rail-hover); color: var(--rail-ink-max); }
.rail__brand { padding-right: 36px; }
/* The page bar's toggle is for bringing a folded rail back, and only then. */
.rail-toggle { display: none; }
.rail-collapse-state:checked ~ .main .rail-toggle { display: flex; }
.rail-collapse-state:checked ~ .main .pagebar { padding-top: 18px; }

/* ---- Denser lists -------------------------------------------
   13px of padding above and below every cell put nine leads on a 900px
   screen; an operations list is read by scanning, and scanning wants rows.
   Names hold their line rather than breaking over two. */
.grid th { padding: var(--row-pad-y, 9px) 12px; }
.grid td { padding: var(--row-pad-y, 9px) 12px; }
html[data-density="compact"] { --row-pad-y: 5px; }
.grid .cell-primary { white-space: nowrap; }
.grid th:first-child, .grid td:first-child { padding-left: 16px; }
.grid th:last-child, .grid td:last-child { padding-right: 16px; }

/* The columns and density controls are settings of the table, not actions of
   the page: text, not two more bordered buttons. */
.tablebar { gap: 4px; padding: 6px 10px 2px; }
.card__body--flush .tablebar { padding: 6px 10px 2px; }
.tablebar .btn--ghost { background: none; border-color: transparent; color: var(--muted); font-weight: 500; }
.tablebar .btn--ghost:hover { background: var(--surface-2); color: var(--ink); }

/* The filter is part of the list's toolbar rather than a box inside a box. */
.content > .card--page .filterbar { border-color: transparent; }

/* ---- Figures as one strip -----------------------------------
   Five boxes with a coloured bar each, where the colour said nothing: indigo
   on "refused: 0" and on "failures: 0" alike. One strip, and colour only on a
   figure that asks for attention. */
.stat-grid { gap: 0; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.stat { border: 0; border-radius: 0; box-shadow: inset 1px 0 0 var(--line); padding: 16px 18px; }
.stat::before { display: none; }
.stat:first-child { box-shadow: none; }
.stat__value { font-size: 30px; margin-top: 8px; }
.stat--warn .stat__value { color: var(--warn); }

/* The current screen in the rail: marked, not flooded. A solid accent block
   was the loudest thing on every screen, louder than the data. */
.navlink.is-active { background: var(--rail-hover); color: var(--rail-ink-max); box-shadow: inset 2px 0 0 var(--rail-accent); }
/* The rail is dark in both themes, so its accent is the light one in both. */
:root { --rail-accent: #7fa6ec; --rail-accent: color-mix(in srgb, var(--accent) 55%, #ffffff); }

.btn, .icon-btn { transition: background-color .15s, border-color .15s, color .15s; }

@media (max-width: 900px) {
    .rail__fold { display: none; }
    :where(.main:has(> .content > .card--page)) .pagebar { padding-top: 14px; }
}
/* Whatever follows the title in a page heading is its actions, and actions sit
   at the far edge — some views say `.right`, some `.actions`, some neither. */
.content > .card--page > .card__head > :last-child:not(:first-child) { margin-left: auto; display: flex; gap: 8px; align-items: center; }
/* A form on its own page keeps a reading width: a 1100px text box for a
   brand's name is a field nobody can take the measure of. */
.content > .card--page > .card__body > form:not(.filterbar) { max-width: 880px; }

/* Native controls — checkboxes, scrollbars, a select's list — drawn for the
   theme on screen. They were white squares on the dark theme. */
:root { color-scheme: light; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { color-scheme: dark; } }
:root[data-theme="dark"] { color-scheme: dark; }
/* Cards side by side are not a stack: the gap between them is the grid's. */
.report-pair > .card + .card { margin-top: 0; }
.btn { white-space: nowrap; }
.content > .card--page .prose-fold, .content > .card--page > .card__head .sub { max-width: 110ch; }

/* The version line at the foot of the rail was 2.97:1 (3.38:1 dark) on
   --rail-ink-low, measured with every other text node on twelve screens
   while the accent was changed in 2.19.3.0; it was the only failure left. */
.rail__version { color: var(--rail-ink-dim); }
