/* The mockup's look, rebuilt rather than lifted. Its styling came from Tailwind;
   what is being followed here is the layout — dark navy ground, teal accent,
   grouped sidebar, title row with pills, four cards, then a titled panel holding a
   table. */

:root {
    /* Every native control on the page — the dropdowns, the file picker, the
       scrollbars — is drawn by the browser, and without this it draws them light.
       That is not cosmetic: a select whose options inherit near-white text and are
       painted on the browser's white popup has invisible options, which reads as a
       control that does not work. It was one. */
    color-scheme: dark;

    --ground:        #0f172a;
    --panel:         #1a2439;
    --panel-raised:  #1e2a42;
    --sidebar:       #131d31;
    --line:          #2a3750;
    --line-soft:     #223049;

    --ink:           #e6edf7;
    --ink-muted:     #94a3b8;
    --ink-faint:     #64748b;

    /* The brand blue, lifted for a navy ground — #1E6AFE itself sits too close to
       #0f172a to read. The accent was the mockup's teal until the logo arrived and
       made it plainly off-brand: the mark is indigo and blue, with no green in it. */
    --accent:        #4D8BFF;
    --accent-dim:    #1E4FB8;
    --accent-rgb:    77, 139, 255;

    --proven:        #f87171;
    --contingent:    #fbbf24;
    --calm:          #34d399;

    --wash:          255, 255, 255;

    --proven-text:     #fca5a5;
    --contingent-text: #fcd34d;
    /* Banner text, softer than a chip: it sits on a large wash rather than a
       small one, so it can afford to be lighter and reads better for it. */
    --proven-soft:     #fecaca;
    --contingent-soft: #fde68a;

    --contingent-note: #b8912f;
    --clean-text:      #6ea78d;
    --failed-text:     #b07b7b;
    --raised-edge:     #2b3a55;

    /* Text on an accent-filled control, where the accent is the background. */
    --on-accent:       #1b0d0d;

    --radius:        10px;
    --sidebar-width: 232px;
}

/* ---- light ------------------------------------------------------------- *

   The same page in daylight. Only the tokens change; not one rule below this
   block knows which theme it is in, which is what makes the toggle a single
   attribute on <html> rather than a second stylesheet to keep in step.

   `color-scheme` switches with it, and that is the part that is not cosmetic:
   every native control the browser draws — the dropdowns, the file picker, the
   scrollbars — follows it. A page that says dark while painting light gets a
   select whose options are near-white text on the browser's white popup, which
   reads as a control that does not work. It was one, in the other direction.

   The semantic colours are darkened rather than reused. #fca5a5 is a red chosen
   to sit on a navy ground; on white it is a pink with no contrast, and money
   that cannot be read is worse than money in the wrong colour. */
:root[data-theme="light"] {
    color-scheme: light;

    --ground:        #f4f6fa;
    --panel:         #ffffff;
    --panel-raised:  #ffffff;
    --sidebar:       #ffffff;
    --line:          #d8dee9;
    --line-soft:     #e5eaf1;

    --ink:           #0d1622;
    --ink-muted:     #3c485d;
    --ink-faint:     #5a6679;

    /* The brand blue exactly, #1E6AFE. It was a darkened teal, which reads green on
       white and matched nothing in the mark. */
    --accent:        #1E6AFE;
    --accent-dim:    #A8C6FF;
    --accent-rgb:    30, 106, 254;

    --proven:        #c0392b;
    --contingent:    #a1660a;
    --calm:          #157347;

    --wash:          15, 23, 42;

    --proven-text:     #a4271b;
    --contingent-text: #7a4d05;
    --proven-soft:     #a4271b;
    --contingent-soft: #7a4d05;

    --contingent-note: #7a4d05;
    --clean-text:      #157347;
    --failed-text:     #a4271b;
    --raised-edge:     #d8dee9;

    --on-accent:       #ffffff;
}


* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--ground);
    color: var(--ink);
    font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial, sans-serif;
    font-size: 14px;
    line-height: 1.5;
}

/* Light text on a dark ground blooms, and thinning it back is what antialiasing is
   for here. Dark text on white does the opposite — it already reads thin, and
   thinning it further is what made the light theme look washed out. So this belongs
   to the dark theme rather than to the page. */
:root[data-theme="dark"] body { -webkit-font-smoothing: antialiased; }

a { color: inherit; text-decoration: none; }

/* Numbers line up in columns only if the digits are the same width. Money that
   does not line up is money nobody scans. */
.tabular { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* ---- shell ------------------------------------------------------------- */

.shell { display: flex; min-height: 100vh; }

.sidebar {
    width: var(--sidebar-width);
    flex: 0 0 var(--sidebar-width);
    background: var(--sidebar);
    border-right: 1px solid var(--line-soft);
    display: flex;
    flex-direction: column;
}

.wordmark {
    padding: 22px 20px 8px;
    line-height: 0;
}

.wordmark img {
    /* Sized on width so the mark keeps its proportions whatever the artwork is
       replaced with later. It is drawn at 132 and shipped at 480, which is the
       retina allowance and no more. */
    width: 132px;
    height: auto;
    display: block;
}

/* One or the other, decided by the theme rather than by anything the server knows.
   Light is the default, so the reverse mark is the one that has to be asked for. */
/* Matched on `.wordmark img` too, because `.wordmark img` above sets display and
   would otherwise outrank a bare class selector — which it did, and both marks
   rendered stacked in light. Specificity, not cascade order. */
.wordmark img.wordmark-dark { display: none; }
:root:not([data-theme="light"]) .wordmark img.wordmark-light { display: none; }
:root:not([data-theme="light"]) .wordmark img.wordmark-dark { display: block; }

.nav-group { padding: 18px 0 4px; }

.nav-group-title {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1.4px;
    color: var(--ink-faint);
    padding: 0 20px 8px;
}

.nav-item {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 9px 20px;
    color: var(--ink-muted);
    border-left: 3px solid transparent;
    cursor: pointer;
}

.nav-item:hover { color: var(--ink); background: rgba(var(--wash), 0.03); }

.nav-item.active {
    color: var(--ink);
    background: rgba(var(--accent-rgb), 0.09);
    border-left-color: var(--accent);
}

.nav-item .dot {
    width: 15px;
    height: 15px;
    border-radius: 4px;
    background: var(--raised-edge);
    flex: 0 0 15px;
}

.nav-item.active .dot { background: var(--accent); }

/* Single-tenant. One manufacturer uses this installation, so the name is a fact
   about the product rather than a selection — stated under the wordmark, with no
   picker beside it. */
.tenant {
    padding: 0 20px 18px;
    margin-top: -14px;
    font-size: 11px;
    letter-spacing: 1.4px;
    color: var(--ink-faint);
    border-bottom: 1px solid var(--line-soft);

    /* The company name and the toggle on one line, pushed apart. The toggle sits
       here because it is a property of the person's view rather than of any screen,
       which is the same thing the line above it says. */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.theme-toggle {
    flex: none;
    width: 24px;
    height: 24px;
    padding: 0;
    display: grid;
    place-content: center;
    font-size: 13px;
    line-height: 1;
    color: var(--ink-faint);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    cursor: pointer;
    /* Letter-spacing is inherited from .tenant and shifts a single glyph off centre. */
    letter-spacing: 0;
}

.theme-toggle:hover {
    color: var(--accent);
    background: rgba(var(--wash), 0.06);
    border-color: var(--line);
}

.theme-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Both glyphs ship in the markup and CSS picks one, so the icon is right on the
   first paint rather than after a round trip. The sun offers daylight while the page
   is dark; the moon offers dark while it is light. */
.theme-toggle .when-light { display: none; }
:root[data-theme="light"] .theme-toggle .when-dark { display: none; }
:root[data-theme="light"] .theme-toggle .when-light { display: inline; }
/* Whose books these are, where that needs saying: an operator viewing a manufacturer
   rather than belonging to one, or nothing resolved at all. Its own line, because a
   qualifier running on from a company name reads as part of the name. */
.tenant-note {
    display: block;
    margin-top: 3px;
    letter-spacing: 0.4px;
    text-transform: none;
    opacity: 0.65;
}

/* Not an error. Nothing has gone wrong when an operator has no manufacturer resolved —
   they are simply not scoped to one yet, and red would say a system fault where there
   is a missing selection. */
.tenant-none { font-style: italic; }

.main { flex: 1; min-width: 0; padding: 26px 30px 60px; }

/* ---- banner ------------------------------------------------------------ */

.banner {
    display: flex;
    gap: 10px;
    align-items: baseline;
    background: rgba(251, 191, 36, 0.09);
    border: 1px solid rgba(251, 191, 36, 0.28);
    border-radius: var(--radius);
    padding: 11px 16px;
    margin-bottom: 22px;
    color: var(--contingent-soft);
    font-size: 13px;
}

.banner strong { color: var(--contingent); }

.banner.problem {
    background: rgba(248, 113, 113, 0.09);
    border-color: rgba(248, 113, 113, 0.3);
    color: var(--proven-soft);
}

.banner.problem strong { color: var(--proven); }

/* ---- title row --------------------------------------------------------- */

.title-row {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 20px;
}

.title-row h1 {
    font-size: 21px;
    font-weight: 600;
    margin: 0;
    flex: 1 1 auto;
}

.pills { display: flex; gap: 8px; flex-wrap: wrap; }

.pill {
    font-size: 12px;
    padding: 6px 13px;
    border-radius: 7px;
    border: 1px solid var(--line);
    background: var(--panel);
    color: var(--ink-muted);
    cursor: pointer;
}

.pill:hover { color: var(--ink); }

.pill.on {
    background: rgba(var(--accent-rgb), 0.12);
    border-color: var(--accent-dim);
    color: var(--accent);
}

/* ---- cards ------------------------------------------------------------- */

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 16px;
    margin-bottom: 24px;
}

.card {
    background: var(--panel);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    padding: 17px 19px 19px;
}

.card-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 1.1px;
    color: var(--ink-faint);
    margin-bottom: 12px;
    min-height: 26px;
}

.card-value {
    font-size: 29px;
    font-weight: 600;
    letter-spacing: -0.5px;
    line-height: 1.1;
}

.card-note { font-size: 11.5px; color: var(--ink-faint); margin-top: 9px; }

.card.proven .card-value { color: var(--ink); }
.card.contingent .card-value { color: var(--contingent); }
.card.contingent .card-note { color: var(--contingent-note); }

/* A figure that is not somebody's money.

   Outlined rather than filled, and grey rather than red or amber. The three kinds of
   money already own solid red, amber and dim amber, and a fourth filled colour beside
   them would read as a fourth kind. This is not a severity — it says where a number
   came from, and it has to be legible against a figure of any kind without competing
   with the figure's own meaning.

   Its own line under the value rather than trailing the number, because a marker on
   the same line reads as part of the amount. */
.fixture-mark {
    display: inline-block;
    margin-top: 6px;
    padding: 1px 7px;
    border: 1px solid var(--line);
    border-radius: 3px;
    font-size: 10px;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--ink-faint);
    white-space: nowrap;
    cursor: help;
}

/* ---- panel ------------------------------------------------------------- */

.panel {
    background: var(--panel);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    overflow: hidden;
    margin-bottom: 22px;
}

.panel-head {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 16px 19px;
    border-bottom: 1px solid var(--line-soft);
    flex-wrap: wrap;
}

.panel-head h2 { font-size: 15px; font-weight: 600; margin: 0; flex: 1 1 auto; }

.badge {
    font-size: 11px;
    padding: 4px 10px;
    border-radius: 6px;
    background: rgba(248, 113, 113, 0.14);
    color: var(--proven-text);
    white-space: nowrap;
}

.badge.calm { background: rgba(52, 211, 153, 0.14); color: var(--calm); }
.badge.muted { background: rgba(148, 163, 184, 0.13); color: var(--ink-muted); }

.panel-body { padding: 17px 19px; }

/* ---- table ------------------------------------------------------------- */

.table-scroll { overflow-x: auto; }

table.grid {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

table.grid th {
    text-align: left;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 1px;
    color: var(--ink-faint);
    padding: 11px 14px;
    border-bottom: 1px solid var(--line-soft);
    white-space: nowrap;
    background: var(--panel-raised);
}

table.grid th.sortable { cursor: pointer; user-select: none; }
table.grid th.sortable:hover { color: var(--ink-muted); }
table.grid th .caret { color: var(--accent); margin-left: 4px; }

table.grid td {
    padding: 12px 14px;
    border-bottom: 1px solid var(--line-soft);
    vertical-align: top;
    color: var(--ink);
}

table.grid tbody tr:last-child td { border-bottom: none; }
table.grid tbody tr:hover td { background: rgba(var(--wash), 0.02); }

.num { text-align: right; white-space: nowrap; }
.muted { color: var(--ink-muted); }
.faint { color: var(--ink-faint); }
.nowrap { white-space: nowrap; }

.finding-headline { display: block; }

/* Every number is clickable and leads to what produced it.

   Underlined faintly rather than only on hover. It was hover-only, on the argument
   that fifty rows of underlines read as a wall of links — which is true, and cost
   more than it saved: the headline is the only route from the findings list to a
   finding, and from there to the approve control, and a link nobody can see is a
   route nobody can take. A faint underline says "this goes somewhere" without
   shouting; the accent on hover still says which one is under the pointer. */
a.finding-link {
    color: var(--ink);
    text-decoration: underline;
    text-decoration-color: rgba(148, 163, 184, 0.35);
    text-underline-offset: 3px;
}

a.finding-link:hover {
    color: var(--accent);
    text-decoration-color: var(--accent);
}
.finding-sub { display: block; font-size: 11.5px; color: var(--ink-faint); margin-top: 3px; }

/* Money that is owed reads differently from money that might be. Proven is solid;
   contingent is amber and says what it is. Nothing renders them as one number. */
.money-proven { color: var(--proven); font-weight: 600; }
.money-held { color: var(--contingent); }
.money-none { color: var(--ink-faint); }

/* Three kinds of money, three appearances. Proven is owed and reads solid; held is
   a real gap resting on something unverified; contingent is nobody's claim and is
   dimmer again. They sit in separate labelled columns as well, because colour
   alone is not a distinction a reader can rely on. */
.money-contingent { color: var(--contingent); opacity: 0.72; }

.chip {
    display: inline-block;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.3px;
    padding: 2px 9px;
    border-radius: 5px;
    white-space: nowrap;
}

.chip.error { background: rgba(248, 113, 113, 0.16); color: var(--proven-text); }
.chip.warning { background: rgba(251, 191, 36, 0.16); color: var(--contingent-text); }
.chip.info { background: rgba(148, 163, 184, 0.14); color: var(--ink-muted); }
.chip.lane-proven { background: rgba(248, 113, 113, 0.16); color: var(--proven-text); }
.chip.lane-needsdecision { background: rgba(251, 191, 36, 0.16); color: var(--contingent-text); }
.chip.lane-dataquality { background: rgba(148, 163, 184, 0.14); color: var(--ink-muted); }

/* ---- controls ---------------------------------------------------------- */

.controls { display: flex; gap: 9px; align-items: center; flex-wrap: wrap; }

.controls input[type="search"], .controls select {
    background: var(--ground);
    border: 1px solid var(--line);
    border-radius: 7px;
    color: var(--ink);
    padding: 6px 11px;
    font-size: 12.5px;
    font-family: inherit;
}

.controls input[type="search"] { min-width: 210px; }
.controls input:focus, .controls select:focus { outline: 1px solid var(--accent-dim); }

/* Every option on the page, whatever the select is dressed as. The popup is the
   browser's, and an option nobody can read is a control that does not work. */
select option {
    background: var(--panel);
    color: var(--ink);
}

.pager {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 13px 19px;
    border-top: 1px solid var(--line-soft);
    font-size: 12.5px;
    color: var(--ink-muted);
}

.pager button {
    background: var(--panel-raised);
    border: 1px solid var(--line);
    border-radius: 6px;
    color: var(--ink);
    padding: 5px 12px;
    font-size: 12.5px;
    font-family: inherit;
    cursor: pointer;
}

.pager button:disabled { color: var(--ink-faint); cursor: default; }
.pager .spacer { flex: 1 1 auto; }

/* ---- empty and provenance ---------------------------------------------- */

.empty { padding: 40px 20px; text-align: center; color: var(--ink-muted); }
.empty h3 { font-size: 15px; font-weight: 600; color: var(--ink); margin: 0 0 7px; }
.empty p { margin: 0 auto; max-width: 460px; font-size: 13px; }

/* ---- volume ------------------------------------------------------------ */

.bars { display: flex; gap: 4px; align-items: flex-end; }

.bar-slot { display: flex; flex-direction: column; align-items: center; gap: 4px; width: 26px; }

.bar-track {
    width: 100%;
    height: 42px;
    display: flex;
    align-items: flex-end;
    background: rgba(var(--wash), 0.03);
    border-radius: 3px;
}

.bar-fill { width: 100%; background: var(--accent); border-radius: 3px; min-height: 0; }

.bar-label { font-size: 9px; color: var(--ink-faint); letter-spacing: 0.3px; }

/* A month the data covers and this facility is absent from. Outlined rather than
   filled, so a gap reads as a gap and not as a very small purchase. */
.bar-slot.absent .bar-track {
    border: 1px dashed var(--line);
    background: none;
}

.trend-stopped { color: var(--contingent); }
.trend-up { color: var(--calm); }
.trend-down { color: var(--proven); }

.provenance { font-size: 12.5px; color: var(--ink-muted); }
.provenance dl { display: grid; grid-template-columns: auto 1fr; gap: 7px 18px; margin: 0; }
.provenance dt { color: var(--ink-faint); white-space: nowrap; }
.provenance dd { margin: 0; word-break: break-word; }

.notes { margin: 14px 0 0; padding: 0 0 0 18px; }
.notes li { margin-bottom: 7px; }

#blazor-error-ui {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    display: none;
    background: var(--proven);
    color: var(--on-accent);
    padding: 11px 18px;
    z-index: 1000;
}

#blazor-error-ui .dismiss { cursor: pointer; float: right; font-weight: 700; }

/* What the cards below are figures over. Sits between the title and the cards
   because a denominator read after the number is a correction, not a frame. */
.scope-line {
    margin: 0 0 14px;
    font-size: 13px;
}

.scope-line strong { color: var(--ink); }

/* ---- operations dashboard ---------------------------------------------- */

/* Separates the period control from the distributor filter. They are two
   different questions — how far back, and whose files — and a single run of
   pills reads as one control with eight settings. */
.pill-gap {
    width: 14px;
    display: inline-block;
}

/* The standing figures, below the operational content rather than off the page.
   Set apart so the boundary is visible: everything above answers "what happened
   in the period", everything here answers "where do we stand", and the two are
   never scoped the same way. */
.standing {
    margin-top: 34px;
    padding-top: 6px;
    border-top: 1px solid var(--line);
}

.standing .title-row { margin-bottom: 10px; }
.standing .title-row h2 { font-size: 17px; margin: 0; }

/* ---- dashboard counts -------------------------------------------------- */

/* Every panel is a number somebody drills into. A dashboard that lists rows is a
   worse version of the page those rows belong on, and it stops working at the
   scale this product is for. */
.count-body {
    display: flex;
    align-items: baseline;
    gap: 28px;
    flex-wrap: wrap;
}

.count-figure {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
}

.count-value {
    font-size: 30px;
    font-weight: 600;
    color: var(--ink);
    text-decoration: none;
    line-height: 1.1;
}

a.count-value:hover { color: var(--accent); }

/* The unit is named, because "3 arrived" counts files and "21 raised" counts
   problems and a reader given two bare numbers reads them as the same thing. */
.count-unit {
    font-size: 13px;
    color: var(--ink-muted);
    margin-right: 10px;
}

.count-note {
    margin: 0;
    font-size: 13px;
    color: var(--ink-muted);
    max-width: 62ch;
}

.breakdown {
    display: flex;
    gap: 28px;
    flex-wrap: wrap;
}

.breakdown-group {
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.breakdown-label {
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

.breakdown-item {
    font-size: 13px;
    color: var(--ink-muted);
    text-decoration: none;
}

a.breakdown-item:hover { color: var(--accent); }
.breakdown-item .tabular { color: var(--ink); font-weight: 600; }

/* The newest few, for recognition rather than for reading. Not a table: a table
   invites somebody to work in it, and the screen for that is one click away. */
.newest {
    display: flex;
    flex-direction: column;
    gap: 2px;
    border-top: 1px solid var(--line);
    padding-top: 12px;
}

.newest-row {
    display: grid;
    grid-template-columns: 92px 150px 1fr auto;
    gap: 12px;
    align-items: baseline;
    padding: 5px 0;
    font-size: 13px;
    text-decoration: none;
    color: var(--ink-muted);
}

.newest-row:hover { background: rgba(148, 163, 184, 0.06); }
.newest-row .finding-headline { display: inline; }

/* A card is a figure you click. */
a.card { text-decoration: none; display: block; }
a.card:hover { border-color: var(--accent); }

.pill-select, .pill-input {
    background: var(--panel);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 6px 12px;
    font-size: 13px;
}

/* Wide enough for a filename, which is what somebody is usually typing. */
.pill-input { width: 190px; }
.pill-input::placeholder { color: var(--ink-faint); }
.pill-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }


/* ---- dashboard tiles --------------------------------------------------- */

/* A third of the width. Several fit across, which is what a dashboard looks
   like. The row stretches so a tile holding four rows does not leave the ones
   beside it short. */
.tiles {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 14px;
    margin-bottom: 18px;
    align-items: stretch;
}

/* Head, list, upload — stacked. There is no tile-wide link any more: every row is
   its own, because the useful destination differs per row and one stretched link
   behind all of them could only have one.

   A third of the width, with the upload under the list. It is the full width of the
   tile there, so the control's own flex rules lay it out much as they do across the
   Tracings panel — one control, two widths, no variant.

   No horizontal padding of its own: the head's rule has to reach both edges the
   way a panel's does, so the padding sits on the parts rather than the box. */
.tile {
    display: flex;
    flex-direction: column;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 10px;
    text-decoration: none;
}

.tile:hover { border-color: var(--accent); }

/* Below the list, ruled off from it, and pushed to the tile's floor so it sits on
   the bottom edge whatever the list above is doing. */
.tile-foot {
    margin-top: auto;
    border-top: 1px solid var(--line-soft);
    padding: 13px 15px 14px;
}

/* Head, then a rule — the same shape as .panel-head, so a tile reads as one of
   the page's boxes rather than as a different kind of thing. Heading and period on
   one line, the total under it, the rule under both. */
.tile-head {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 14px 15px 12px;
    border-bottom: 1px solid var(--line-soft);
    min-width: 0;
}

/* The period qualifies the heading and belongs beside it, not under it. */
.tile-title {
    display: flex;
    align-items: baseline;
    gap: 7px;
    min-width: 0;
}

/* Plain text, not a figure. The number is the sum of the rows below it, and a
   total that has to be read as prose is not competing with them for attention. */
.tile-total {
    font-size: 12px;
    color: var(--ink-muted);
}

.tile-total span { color: var(--ink); font-weight: 600; }

.tile-heading {
    text-decoration: none;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink);
    white-space: nowrap;
}

/* What the count is over. Quieter than the heading: it qualifies the box rather
   than naming it. It takes the squeeze, because a clipped heading loses the name
   of the thing and a clipped period loses a detail the pills already show. */
.tile-period {
    font-size: 12px;
    color: var(--ink-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.tile-period::before {
    content: "·";
    margin-right: 7px;
}

.tile-list {
    display: flex;
    flex-direction: column;
    padding: 10px 15px 14px;
    gap: 6px;
}

/* Count, sender, route. Baseline-aligned so a two-digit count does not shunt the
   name it belongs to. */
/* One line per file, and each line is its own link — to the tracing, or to the
   mapping screen for a file whose columns nobody has read. The whole line takes the
   outcome's colour rather than only the marker at the end of it. */
.tile-row {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 6px 8px;
    margin: 0 -8px;
    border-radius: 7px;
    font-size: 13px;
    min-width: 0;
    text-decoration: none;
}

.tile-row:hover { background: rgba(var(--wash), 0.04); }

.tile-row-head {
    display: flex;
    align-items: baseline;
    gap: 7px;
    min-width: 0;
}

/* Counts, never finding text. The tile is a summary; the sentence lives on the page
   this line leads to. */
.tile-counts {
    font-size: 11.5px;
    color: var(--ink-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The name takes the squeeze, not the route: a truncated sender is still
   recognisable and a truncated route is not. */
.tile-sender {
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.tile-source {
    color: var(--ink-muted);
    white-space: nowrap;
}

/* A badge where a row needs a person: "Threshold Alert" or "Not analysed". Text in
   a box, and nothing that moves — a dashboard somebody looks at every morning is
   the worst possible place for something that blinks.

   Both are the same red. Colour carries "this needs you" and the words carry which
   of the two ways, so a reader who cannot rely on the colour still gets the whole
   answer. It replaces a pair of glyphs that modelled the two axes accurately and
   was one mark too many to read at a glance. */
.tile-badge {
    margin-left: auto;
    padding: 2px 7px;
    border-radius: 5px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.3px;
    line-height: 1.5;
    white-space: nowrap;
    background: rgba(248, 113, 113, 0.16);
    color: var(--proven-text);
}

/* The tick that means OnVu read and analysed the file, inside the badge on a row
   that ran and is over a threshold.

   It inherits the badge's colour rather than staying green: it is not saying the
   file is good, it is saying the file ran, and the box around it says how that
   turned out. Slightly dimmed so the words stay the loudest thing in the box. */
.tile-badge-tick {
    margin-right: 5px;
    opacity: 0.75;
}

/* Pushed to the right of the row and never squeezed. It is the one part of the
   line that must always be legible. */
.tile-mark {
    margin-left: auto;
    padding-left: 8px;
    font-size: 12px;
    line-height: 1;
    white-space: nowrap;
}

/* The line, not the glyph. A tick in green beside white text reads as a decoration;
   the row carrying the colour reads as a state. Every part of the line takes it —
   sender, route and the line beneath — with the route and the detail quieter than
   the name.

   The row's colour says whether the file ran and nothing else. A blocked file ran,
   so its row is green exactly like a passed one and only its badge is red. Colouring
   a blocked row red said the file had failed when it had done the opposite: it was
   read, analysed, and found to have too much in it. */
.tile-row.clean .tile-sender,
.tile-row.clean .tile-mark { color: var(--calm); }
.tile-row.clean .tile-source,
.tile-row.clean .tile-counts { color: var(--clean-text); }

.tile-row.failed .tile-sender,
.tile-row.failed .tile-mark { color: var(--proven); }
.tile-row.failed .tile-source,
.tile-row.failed .tile-counts { color: var(--failed-text); }

/* Neither succeeded nor failed. A file nobody has mapped is waiting on a person,
   and marking it red would report a person's queue as a fault. */
.tile-row.waiting .tile-sender { color: var(--ink); }
.tile-row.waiting .tile-mark { color: var(--ink-faint); }

.tile-source::before {
    content: "·";
    margin-right: 7px;
}

/* An empty tile says so in a sentence. "Nothing new" is a real answer and it
   should read like one rather than like a box that failed to load. */
.tile-empty {
    padding: 12px 15px 16px;
    font-size: 13px;
    color: var(--ink-muted);
}


/* ---- upload ------------------------------------------------------------ */

/* One control, used on the dashboard tile and on the Tracings page. It wraps, so
   it fits a narrow column and a full-width panel without a second layout — a
   variant flag is how two implementations start.

   The flex bases are what make that work. Each part can shrink below its basis, so
   in the tile's 124px column every one of them takes a row of its own and the
   control reads as a stack; across the Tracings panel they all fit and it reads as
   a row. Nothing switches — the same rules produce both. */
.upload {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-width: 0;
}

/* A real <input type="file"> at zero opacity, stretched across the zone. The
   browser then handles the click and the drop itself, so drag-and-drop needs no
   JavaScript interop at all. */
.dropzone {
    position: relative;
    flex: 1 1 170px;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    min-height: 46px;
    padding: 8px 10px;
    border: 1px dashed var(--line);
    border-radius: 8px;
    background: rgba(var(--wash), 0.02);
    cursor: pointer;
}

.dropzone:hover { border-color: var(--accent-dim); }

.dropzone input[type="file"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

.dropzone-text {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    min-width: 0;
    line-height: 1.35;
}

/* The part that says a click does something. It was prose in a dashed box, and a
   person looking for the way to pick a file went to the button underneath — which
   is the one control here that cannot open a file browser. */
.dropzone-cta {
    font-size: 12px;
    font-weight: 600;
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.dropzone-hint {
    font-size: 10.5px;
    color: var(--ink-faint);
}

/* Clamped rather than ellipsised on one line, because a chosen filename is the
   confirmation that the right file is about to be sent and "Tracing_Headers_50…"
   confirms nothing. */
.dropzone-file {
    font-size: 11px;
    color: var(--ink);
    overflow-wrap: anywhere;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.upload.dragging .dropzone {
    border-color: var(--accent);
    background: rgba(var(--accent-rgb), 0.07);
}

.upload-sender {
    flex: 1 1 140px;
    min-width: 0;
    width: 100%;
    padding: 6px 9px;
    font-size: 12px;
    font-family: inherit;
    color: var(--ink);
    background: var(--panel-raised);
    border: 1px solid var(--line);
    border-radius: 7px;
}

.upload-sender:focus { outline: none; border-color: var(--accent-dim); }
.upload-sender::placeholder { color: var(--ink-faint); }

/* Natural width in both layouts. Growing it to fill the row would stretch it
   across the leftover space on the wide panel, which is worse than a small button
   on its own line in the narrow one. */
.upload .pill {
    flex: 0 0 auto;
    padding: 6px 12px;
}

/* A disabled button has to stop looking like the primary action. Dimming the teal
   pill to 0.45 left it teal — still the brightest thing in the box, still reading
   as the thing to press. It goes grey instead, and .upload-note.hint says what it
   is waiting for. */
.upload .pill:disabled {
    opacity: 1;
    background: rgba(148, 163, 184, 0.08);
    border-color: var(--line-soft);
    color: var(--ink-faint);
    cursor: default;
}

.upload .pill:disabled:hover { color: var(--ink-faint); }

.upload-note {
    flex: 1 1 100%;
    margin: 0;
    font-size: 11.5px;
    color: var(--ink-muted);
}

.upload-note.problem { color: var(--proven-text); }

/* Guidance, not a failure. Quieter than either of the other two. */
.upload-note.hint { color: var(--ink-faint); }

/* ---- tracings accordion ------------------------------------------------ */

/* One row per sender, collapsed. Its files are not fetched until it is opened:
   a page that loads every sender's files to show a count of them has done the
   level 3 work to answer a level 2 question. */
.accordion { display: flex; flex-direction: column; }

.accordion-row { border-bottom: 1px solid var(--line); }
.accordion-row:last-child { border-bottom: none; }

.accordion-head {
    display: grid;
    grid-template-columns: 20px 1fr auto;
    gap: 10px;
    align-items: baseline;
    width: 100%;
    background: none;
    border: none;
    padding: 13px 20px;
    text-align: left;
    cursor: pointer;
    color: var(--ink);
    font-size: 14px;
}

.accordion-head:hover { background: rgba(148, 163, 184, 0.06); }
.accordion-head.open { background: rgba(148, 163, 184, 0.05); }

.accordion-caret { color: var(--ink-faint); }
.accordion-name { font-weight: 600; }

.accordion-counts {
    font-size: 13px;
    color: var(--ink-muted);
}

.accordion-counts .tabular { color: var(--ink); font-weight: 600; }

.accordion-body { padding: 0 20px 16px; }


/* ---- settings ---------------------------------------------------------- */

/* A labelled number. The label sits above rather than beside it: these are policy
   numbers with names longer than the field, and a row of bare boxes is a form
   nobody can read back. */
.setting {
    display: flex;
    flex-direction: column;
    gap: 5px;
    min-width: 0;
}

.setting-label {
    font-size: 11px;
    color: var(--ink-muted);
    white-space: nowrap;
}

.setting input {
    width: 150px;
    padding: 7px 10px;
    font-size: 13px;
    font-family: inherit;
    color: var(--ink);
    background: var(--panel-raised);
    border: 1px solid var(--line);
    border-radius: 7px;
}

.setting input:focus { outline: none; border-color: var(--accent-dim); }
.setting input::placeholder { color: var(--ink-faint); }


/* What a tile row shares with another row shown beside it. Quieter than the counts
   above it and shown only where there is an overlap — which is only when a sender
   has sent the same lines twice, and is exactly when adding two rows' figures gives
   a number that does not exist. */
.tile-overlap {
    font-size: 11px;
    color: var(--ink-faint);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}


/* A tile holding the shape of something unwritten.

   Dimmed, and without the hover accent the live tiles carry: it is not a control and
   must not read as one. A box that looks live and leads nowhere teaches a reader
   their clicks do not matter, which is what the All Specialties pill cost.

   The honesty is in the words rather than in the styling — "not built", "nothing
   here is computed" — because a dimmed box on its own reads as loading, or as empty,
   and empty is a claim this has not earned. */
.tile.placeholder {
    border-style: dashed;
    background: rgba(var(--wash), 0.012);
}

.tile.placeholder:hover { border-color: var(--line); }

.tile.placeholder .tile-heading { color: var(--ink-muted); }
.tile.placeholder .tile-period,
.tile.placeholder .tile-total { color: var(--ink-faint); }
.tile.placeholder .tile-total span { color: var(--ink-faint); font-weight: 400; }


/* ---- sign in ----------------------------------------------------------- */

.bare {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ground);
    padding: 40px 20px;
}

/* ---- sign in ------------------------------------------------------------ *

   A split card: the brand field on one side, the form on the other. It floats on the
   page ground rather than running edge to edge, which keeps it feeling like a door
   into something rather than a landing page. */
.signin-split {
    width: 100%;
    max-width: 940px;
    display: grid;
    grid-template-columns: 1.08fr 0.92fr;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 30px 70px -40px rgba(15, 23, 42, 0.55);
}

/* Dark in both themes, deliberately. It carries the reverse mark, which is drawn for
   a dark ground — a panel that flipped with the theme would need a third piece of
   artwork to say the same thing twice. The gradient runs indigo to blue because those
   are the two colours in the logo and nothing else. */
.signin-brand {
    position: relative;
    overflow: hidden;
    padding: 42px 40px 44px;
    color: #ffffff;
    background: linear-gradient(152deg, #2F0B6B 0%, #2A2A9E 48%, #1E6AFE 100%);
}

/* A single soft light, off the top corner. One light source reads as depth; two read
   as a gradient somebody forgot to finish. */
.signin-brand::after {
    content: "";
    position: absolute;
    top: -180px;
    right: -140px;
    width: 420px;
    height: 420px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(255, 255, 255, 0.22), transparent 62%);
    pointer-events: none;
}

.signin-brand > * { position: relative; }

.signin-logo {
    width: 138px;
    height: auto;
    display: block;
}

.signin-eyebrow {
    margin: 26px 0 0;
    font-size: 10.5px;
    letter-spacing: 2.2px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.62);
}

.signin-headline {
    margin: 10px 0 30px;
    font-size: 25px;
    line-height: 1.24;
    font-weight: 600;
    letter-spacing: -0.3px;
    text-wrap: balance;
}

.signin-props {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 17px;
}

.signin-props li {
    display: grid;
    grid-template-columns: 21px 1fr;
    gap: 12px;
    align-items: start;
    font-size: 12.5px;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.74);

    /* Staggered in on load, once. The order is the argument — data in, checked,
       visible, settled — so arriving in that order is the point rather than decoration. */
    opacity: 0;
    animation: signin-rise 620ms cubic-bezier(.2, .7, .3, 1) forwards;
    animation-delay: calc(180ms + var(--i) * 110ms);
}

.signin-props strong {
    display: block;
    font-size: 13.5px;
    font-weight: 600;
    color: #ffffff;
    margin-bottom: 1px;
}

/* The mark's own V is a check, so the list draws the same stroke. */
.signin-check {
    width: 21px;
    height: 21px;
    margin-top: 1px;
    border-radius: 6px;
    display: grid;
    place-content: center;
    background: rgba(255, 255, 255, 0.13);
}

.signin-check svg {
    width: 13px;
    height: 13px;
    fill: none;
    stroke: #ffffff;
    stroke-width: 3;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.signin-check path {
    stroke-dasharray: 26;
    stroke-dashoffset: 26;
    animation: signin-draw 480ms ease forwards;
    animation-delay: calc(360ms + var(--i) * 110ms);
}

@keyframes signin-rise {
    from { opacity: 0; transform: translateY(9px); }
    to   { opacity: 1; transform: none; }
}

@keyframes signin-draw {
    to { stroke-dashoffset: 0; }
}

/* The form side. Follows the theme, unlike the panel beside it. */
.signin {
    padding: 44px 38px 40px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.signin-title {
    margin: 0;
    font-size: 20px;
    font-weight: 600;
    letter-spacing: -0.2px;
}

.signin-sub {
    margin: 5px 0 22px;
    font-size: 12.5px;
    color: var(--ink-muted);
}

.signin-submit {
    margin-top: 18px;
    width: 100%;
    padding: 10px 13px;
    font-size: 13px;
    font-weight: 600;
    color: #ffffff;
    background: var(--accent);
    border-color: var(--accent);
}

.signin-submit:hover {
    filter: brightness(1.08);
}

.signin .setting input { width: 100%; }

@media (max-width: 820px) {
    .signin-split { grid-template-columns: 1fr; max-width: 420px; }

    .signin-brand { padding: 30px 30px 32px; }

    /* The headline and the claims are the part with room to lose. On a phone the mark
       and what the product is called still arrive first, which is what the panel is
       for; the argument can wait for a screen that can hold it. */
    .signin-headline, .signin-props { display: none; }

    .signin-eyebrow { margin-top: 18px; }
}

@media (prefers-reduced-motion: reduce) {
    .signin-props li { opacity: 1; animation: none; }
    .signin-check path { stroke-dashoffset: 0; animation: none; }
}


/* Who is signed in. Under the tenant, because "as whom am I looking at this" is the
   same kind of fact as "whose installation is this". */
.whoami {
    padding: 12px 20px 16px;
    border-bottom: 1px solid var(--line-soft);
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.whoami-name { font-size: 12.5px; color: var(--ink); }
.whoami-role { font-size: 10.5px; letter-spacing: 1px; color: var(--ink-faint); }

.whoami-out {
    margin-top: 6px;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 11.5px;
    color: var(--ink-muted);
    text-decoration: underline;
    text-decoration-color: rgba(148, 163, 184, 0.35);
    text-underline-offset: 3px;
    cursor: pointer;
    text-align: left;
}

.whoami-out:hover { color: var(--accent); text-decoration-color: var(--accent); }

/* ---- reset the demo ----------------------------------------------------- *

   Beside the page heading, and only on the dashboard. A destructive control on every
   screen is one somebody eventually reaches for by accident. */
.reset-demo {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Beside the heading, not adrift in the middle of the row.

   .title-row h1 grows to fill, which is what pushes a "Back to findings" link to the
   right on every other page — so anything placed after the heading lands over there
   with it. Scoped with :has rather than changed on .title-row itself, because those
   other pages want the heading to keep growing. */
.title-row:has(.reset-demo) h1 { flex: 0 0 auto; }
.title-row:has(.reset-demo) .reset-demo { margin-right: auto; }

/* What it will do, in the confirmation rather than after it. "Are you sure?" on its
   own asks somebody to guess at the consequences of a thing called Reset. */
.reset-note {
    font-size: 12px;
    color: var(--ink-muted);
    max-width: 280px;
    line-height: 1.35;
}

/* The one button on the product that destroys something. It reads as the proven-money
   red rather than as an accent, because the accent is what every ordinary control
   uses and this is not an ordinary control. */
.pill.danger {
    color: var(--proven-text);
    border-color: var(--proven);
    background: rgba(248, 113, 113, 0.12);
}

.pill.danger:hover {
    background: rgba(248, 113, 113, 0.2);
}
