/*
   HETVS brand layer over Metronic 8.
   The green is sampled from the 1.0 login backdrop (img/bg.png) and is the
   official Nigerian flag green; the split-screen login reproduces that
   green/white division rather than loading the bitmap.
*/

:root {
    --hetvs-green: #008751;
    --hetvs-green-dark: #006B40;
    --hetvs-green-light: #E6F4EE;

    /* Neutrals, biased a little toward the brand green so they read as chosen
       rather than inherited. Components reference these by role; without them
       `var(--ink)` resolves to nothing and text silently falls back to the
       inherited link colour, which is how every row in a list turns green. */
    --ink:        #10231A;
    --ink-2:      #3D4F45;
    --ink-3:      #6B7A71;
    --rule:       #D3DAD5;
    --rule-soft:  #E7EBE8;
    --surface:    #FFFFFF;
    --surface-2:  #F3F6F4;
}


:root[data-theme="dark"] {
    --ink:       #E8EFE9;
    --ink-2:     #B2C3B8;
    --ink-3:     #7E9187;
    --rule:      #27392E;
    --rule-soft: #1D2E24;
    --surface:   #121F19;
    --surface-2: #18291F;
}

/* --- Content width --------------------------------------------------------
   The header, every page body and the footer all sit in .container-xxl, so one
   rule sets the measure for the whole application and keeps the logo, the nav
   and the footer aligned with the content between them.

   Metronic caps that container at 1320px from 1400px up, which frames wide
   desktops in empty margin while the voucher and mandate tables run out of
   room. 80% of the viewport instead — the same proportion at every desktop
   size.

   Below 992px the theme already runs full width and that is left alone: on a
   tablet or a phone, giving away a fifth of the screen costs more than the
   margin is worth. */
@media (min-width: 992px) {
    .container-xxl {
        width: 80%;
        max-width: none;
    }
}


/* --- Primary colour override -------------------------------------------- */
.btn.btn-primary {
    background-color: var(--hetvs-green);
    border-color: var(--hetvs-green);
}

.btn.btn-primary:hover:not(.btn-active),
.btn.btn-primary:focus:not(.btn-active),
.btn.btn-primary:active:not(.btn-active) {
    background-color: var(--hetvs-green-dark);
    border-color: var(--hetvs-green-dark);
}

.btn.btn-light-primary {
    color: var(--hetvs-green);
    background-color: var(--hetvs-green-light);
}

.btn.btn-light-primary:hover:not(.btn-active),
.btn.btn-light-primary:focus:not(.btn-active),
.btn.btn-light-primary:active:not(.btn-active) {
    color: #fff;
    background-color: var(--hetvs-green);
}

.text-primary { color: var(--hetvs-green) !important; }
.bg-primary { background-color: var(--hetvs-green) !important; }

.text-hover-primary:hover,
.menu-state-primary .menu-item .menu-link.active,
.menu-state-primary .menu-item .menu-link:hover {
    color: var(--hetvs-green) !important;
}

.form-control:focus {
    border-color: var(--hetvs-green);
    box-shadow: 0 0 0 0.2rem rgba(0, 135, 81, 0.15);
}

/* --- Authentication split screen ---------------------------------------- */
.auth-bg { background-color: #fff; }

.stretch-free-height { min-height: 100vh; }

.auth-aside {
    background-color: var(--hetvs-green);
    /* Subtle depth so the flat green does not read as an unstyled block. */
    background-image: radial-gradient(circle at 20% 15%, rgba(255, 255, 255, 0.10), transparent 55%);
}

/* The crest is a dark-outlined mark on a dark green ground — a soft white
   halo keeps its edges legible without altering the artwork itself. */
.auth-crest {
    height: 130px;
    width: auto;
    filter: drop-shadow(0 0 1px rgba(255, 255, 255, 0.9))
            drop-shadow(0 2px 10px rgba(0, 0, 0, 0.25));
}

@media (max-width: 991.98px) {
    .auth-crest { height: 96px; }
}







/* Connector line between the pipeline dots — reads as a sequence, not a list. */


@media (max-width: 991.98px) {
    .stretch-free-height { min-height: auto; }
}


/* --- Header menu active state -------------------------------------------
   This Metronic build paints a 2px green border-bottom on *every* .menu-link,
   in the header bar and inside every dropdown alike. An underline that is
   always on cannot indicate anything, so it is cleared and then reinstated —
   in the brand green — only on the top-level item the user is actually in. */
.header-menu .menu-link,
.menu-sub .menu-link {
    border-bottom: 0;
}

.header-menu > .menu > .menu-item > .menu-link.main-menu-link,
.header-menu > .menu > a.menu-item > .menu-link.main-menu-link {
    border-bottom: 2px solid transparent;
}

.header-menu > .menu > .menu-item.here > .menu-link.main-menu-link,
.header-menu > .menu > .menu-item.show > .menu-link.main-menu-link,
.header-menu > .menu > a.menu-item.here > .menu-link.main-menu-link,
.header-menu > .menu > a.menu-item.show > .menu-link.main-menu-link {
    border-bottom-color: var(--hetvs-green);
    color: var(--hetvs-green);
}

/* Dropdown rows read as a list, so the cue is a background tint, not a rule. */
.menu-sub .menu-item .menu-link:hover,
.menu-sub .menu-item .menu-link.active {
    background-color: var(--hetvs-green-light);
    color: var(--hetvs-green);
}


/* --- Authentication panel width -----------------------------------------
   The green aside carries the identity of the system, so it holds close to
   half the composition rather than the narrow 500px strip it started at.
   Capped in px so it does not sprawl on ultrawide displays. */
:root { --auth-aside-w: min(40%, 680px); }

@media (min-width: 992px) {
    .auth-aside-w {
        width: var(--auth-aside-w) !important;
        flex: 0 0 var(--auth-aside-w);
        max-width: var(--auth-aside-w);
    }
}

/* --- National ribbon ------------------------------------------------------
   Sits at the foot of the green aside, bled to the panel edges so the tails
   run the full width.

   The artwork is green-and-white on transparent and its green — around
   rgb(3,122,61) — is within a few points of the panel's own #008751, with
   roughly two thirds of the image being that green. Dropped in untreated it
   dissolves and only the white stripes survive, reading as loose squiggles.
   A dark drop-shadow reinstates the edge the flat colour cannot carry, so the
   ribbon reads as an embossed relief rather than a pasted cut-out. */
/* The brand block holds the top of the panel and the ribbon holds the bottom;
   the space between them flexes so the pair stay anchored at both ends. */
.auth-ribbon {
    margin-top: auto;
    /* Flush to the bottom edge — the ribbon anchors the panel, so it carries no
       padding of its own and nothing sits below it. */
    padding: 0;
    line-height: 0;
}

.auth-ribbon img {
    display: block;
    width: 100%;
    height: auto;
    filter:
        drop-shadow(0 1px 1px rgba(0, 0, 0, 0.38))
        drop-shadow(0 0 10px rgba(0, 0, 0, 0.18));
    opacity: 0.97;
}

@media (max-width: 991.98px) {
    /* Stacked on small screens the green panel is short; the ribbon would eat
       the space the headline needs, so it steps aside. */
    .auth-ribbon { display: none; }
}



/* --- Sign-in action ------------------------------------------------------
   The 1.0 button is the lighter #50CD89, not the deep panel green — it has to
   lift off a white ground, where #008751 reads as heavy. Set explicitly rather
   than borrowing a theme variant: btn-success in this Metronic build renders
   blue, so relying on it produced a blue button. */
.btn.btn-signin {
    color: #fff;
    background-color: #50CD89;
    border-color: #50CD89;
}

.btn.btn-signin:hover,
.btn.btn-signin:focus,
.btn.btn-signin:active,
.btn.btn-signin[data-kt-indicator="on"],
.btn.btn-signin:disabled {
    color: #fff;
    background-color: #3FB876;
    border-color: #3FB876;
    opacity: 1;
}


/* --- Semantic colour ------------------------------------------------------
   This Metronic build maps its `success` tone to a blue, so `text-success`
   and the light-success surfaces read as information rather than as money in
   hand. Pinned to the brand greens, and the other semantic tones with them so
   a queue's state is legible at a glance. */
.text-success { color: var(--hetvs-green) !important; }
.bg-light-success { background-color: var(--hetvs-green-light) !important; }
.badge.badge-light-success { color: var(--hetvs-green); background-color: var(--hetvs-green-light); }
.progress-bar.bg-success { background-color: var(--hetvs-green) !important; }

.text-warning { color: #B4690E !important; }
.badge.badge-light-warning { color: #B4690E; background-color: #FBF3E6; }
.bg-light-warning { background-color: #FBF3E6 !important; }

.text-danger { color: #9B2C2C !important; }
.badge.badge-light-danger { color: #9B2C2C; background-color: #FBEAEA; }

/* A required field earns a marker; the form is long enough that it matters. */
.required::after { content: " *"; color: #9B2C2C; font-weight: 700; }

/* --- Chart palette --------------------------------------------------------
   Roles, not raw hex, so the charts read one set of tokens and follow the
   viewer's theme. Every value below was checked with the palette validator
   against the surface it actually renders on (white card in light, #121F19 in
   dark) rather than picked by eye.

   --viz-series-1  single-series brand green. 4.58:1 on white, 6.66:1 on the
                   dark card — clears 3:1 both ways.
   --viz-ramp-1..4 sequential ramp for "days waiting", which is an ordered
                   quantity, so it is one hue light→dark rather than four
                   status colours implying four unrelated states. Each mode has
                   its own steps: the light ramp fails the light-end contrast
                   floor on a dark surface, so dark is selected, not flipped.
                   Both sets pass monotone lightness, ΔL gaps and single-hue. */
:root {
    --viz-series-1: #008751;
    --viz-ink:      #3D4F45;
    --viz-grid:     #E4E9E5;
    --viz-ramp-1:   #E8A868;
    --viz-ramp-2:   #D8843A;
    --viz-ramp-3:   #B35F1C;
    --viz-ramp-4:   #7E3610;
}


:root[data-theme="dark"] {
    --viz-series-1: #35B77E;
    --viz-ink:      #B2C3B8;
    --viz-grid:     #27392E;
    --viz-ramp-1:   #F3C79A;
    --viz-ramp-2:   #E19A55;
    --viz-ramp-3:   #C4762F;
    --viz-ramp-4:   #9E5420;
}

/* The day divider in the activity feed leads its group, so the first one
   needs no top gap. */
.first-mt-0:first-child { margin-top: 0 !important; }

details > summary { list-style: revert; }
details > summary::-webkit-details-marker { display: revert; }


/* --- Theme note -----------------------------------------------------------
   This application loads Metronic's *light* stylesheets only and never stamps
   `data-theme` on the root, so it is light-only today. Tokens must therefore
   not follow `prefers-color-scheme`: a viewer whose OS is dark would otherwise
   get near-white ink on cards that stay white, which is unreadable.

   The `[data-theme="dark"]` blocks below are kept and are correct — they take
   effect the day the dark Metronic bundle is loaded and the root is stamped.
   Until then nothing selects them. */

/* --- Record row -----------------------------------------------------------
   The list primitive shared by every dashboard list, queue and detail panel.

   It replaces `border-bottom border-dashed`, which in this Metronic build does
   not mean "a dashed rule underneath": `border-dashed` paints 3px dashed on all
   four sides, so each row rendered as a dotted box and a list of eight read as
   eight placeholders.

   Anatomy: a state rail, a two-line identity, and a right-aligned figure.
   The rail is the only colour in the row, and it never carries meaning alone —
   the badge beside it always states the value in words. */
.rec-list { display: flex; flex-direction: column; }

.rec-row {
    display: grid;
    grid-template-columns: 3px minmax(0, 1fr) auto;
    align-items: center;
    gap: 0 14px;
    padding: 11px 10px;
    margin: 0 -10px;
    border-radius: 6px;
    border-bottom: 1px solid var(--rule-soft);
    text-decoration: none;
    transition: background-color .12s ease;
}
.rec-row:last-child { border-bottom: 0; }
.rec-row:hover { background-color: var(--surface-2); }
.rec-row:focus-visible { outline: 2px solid var(--hetvs-green); outline-offset: -2px; }

/* Severity, read before any text is read. */
.rec-rail {
    width: 3px;
    align-self: stretch;
    min-height: 34px;
    border-radius: 2px;
    background-color: var(--rule);
}
.rec-row.is-warn   .rec-rail { background-color: #B4690E; }
.rec-row.is-urgent .rec-rail { background-color: #9B2C2C; }
.rec-row.is-ok     .rec-rail { background-color: var(--hetvs-green); }

.rec-main { min-width: 0; }
.rec-ref {
    display: block;
    font-weight: 700;
    color: var(--ink);
    font-size: .95rem;
    line-height: 1.3;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rec-row:hover .rec-ref { color: var(--hetvs-green); }
.rec-sub {
    display: block;
    color: var(--ink-3);
    font-size: .8rem;
    line-height: 1.4;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.rec-side { text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
/* Money lines up only if the digits are the same width. */
.rec-amount {
    font-weight: 700;
    font-size: .92rem;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.rec-meta { font-size: .78rem; color: var(--ink-3); white-space: nowrap; }

.rec-empty {
    padding: 34px 10px;
    text-align: center;
    color: var(--ink-3);
}

/* Day divider in the activity feed — a label on a rule, not a heading. */
.rec-day {
    display: flex; align-items: center; gap: 12px;
    margin: 18px 0 6px;
    font-size: .72rem; font-weight: 700; letter-spacing: .09em;
    text-transform: uppercase; color: var(--ink-3);
}
.rec-day::after { content: ""; flex: 1; border-top: 1px solid var(--rule-soft); }
.rec-day:first-child { margin-top: 0; }

/* --- Typography -----------------------------------------------------------
   Source Sans 3 replaces Metronic's Poppins throughout. Poppins is a geometric
   display face; in dense tables of reference numbers its round forms and tight
   apertures cost legibility, and this system is mostly reference numbers.
   Source Sans was drawn for user interfaces and long-form public documents, and
   its figures are clearly differentiated — 1/l/I and 0/O do not collide.

   The base size goes from Metronic's 13px to the browser's own 16px.

   Metronic pins the root with `body, html { font-size: 13px !important }` and
   drops it to 12px under 768px (style.bundle.css). Because that carries
   !important, a plain `html { font-size: ... }` here is ignored no matter how
   late this file loads — the override has to be !important too, and it must
   name both elements so it matches the theme's own selector rather than losing
   to it on specificity. Every `fs-*` utility, and most of the theme's padding,
   is expressed in rem, so this one declaration lifts the whole system by 23%.
   Structural widths (sidebar, header, cards) are set in px and do not move. */
:root {
    --bs-body-font-family: "Source Sans 3", "Segoe UI", system-ui, -apple-system, sans-serif;
    --bs-body-font-size: 1rem;
}

html, body { font-size: 16px !important; }

/* Metronic drops to 12px here; hold the scale on tablets and give phones only
   a small reduction, so a field label never falls back under 13px. */
@media (max-width: 767.98px) {
    html, body { font-size: 15px !important; }
}

body,
.form-control, .form-select, .btn,
.menu, .card-title, .table,
h1, h2, h3, h4, h5, h6,
.fw-bold, .fw-bolder, .fw-boldest, .fw-semibold {
    font-family: "Source Sans 3", "Segoe UI", system-ui, -apple-system, sans-serif;
}

body { font-size: 1rem; line-height: 1.55; }

/* The theme's own small steps are fs-8 .85rem / fs-7 .95rem / fs-6 1.075rem —
   all larger than the values this file used to force on them, which is why the
   earlier attempt at "bigger type" made field labels smaller instead. They are
   left at the theme's values and simply ride the larger root: fs-8, the label
   size used on every card header and table head, lands at 13.6px. */
.table { font-size: .95rem; }

/* Reference numbers, account numbers and money read better with figures that
   share a width and never collide. */
.rec-amount, .num, .text-nowrap.fw-bold,
td .fw-bold, .fs-2hx, .fs-2qx, .fs-2x {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

/* --- Greeting stat tiles --------------------------------------------------
   They sit half over the green panel, so they need a surface and a lift of
   their own or they read as holes punched in the header. */
.greeting-stat {
    background-color: var(--surface);
    border: 1px solid var(--rule-soft);
    box-shadow: 0 2px 10px rgba(16, 35, 26, .06);
    transition: box-shadow .15s ease, transform .15s ease;
}
.greeting-stat:hover {
    box-shadow: 0 6px 18px rgba(16, 35, 26, .12);
    transform: translateY(-2px);
}

/* --- Security codes -------------------------------------------------------
   Authenticator codes, the base32 shared key and recovery codes are transcribed
   by hand from a phone or a printed sheet, so they are set in a monospace face
   with the characters held apart. Source Sans differentiates its figures well,
   but for a string with no words in it, fixed advance width is what stops the
   eye losing its place mid-code. */
.code-input {
    font-family: "SF Mono", ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
    letter-spacing: .18em;
    font-size: 1.15rem;
}

/* --- Administrator dashboard ----------------------------------------------
   The greeting as a band across the top. The previous card sat in a third of
   the width beside the activity feed and, because the row stretched to the
   feed's height, two-thirds of that column rendered as empty white. */
.greet-bar {
    background:
        radial-gradient(120% 160% at 88% -30%, rgba(255,255,255,.16), transparent 60%),
        linear-gradient(100deg, var(--hetvs-green-dark) 0%, var(--hetvs-green) 62%, #10A063 100%);
}

/* A finding, not a statistic: the rail carries severity, the number carries
   weight, and the whole tile is the link to the screen that fixes it. */
.check-card {
    position: relative;
    display: flex;
    gap: 14px;
    height: 100%;
    padding: 16px 18px 16px 0;
    border: 1px solid var(--rule-soft);
    border-radius: 6px;
    background-color: var(--surface);
    transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease;
}
.check-card:hover {
    border-color: var(--rule);
    box-shadow: 0 6px 18px rgba(16, 35, 26, .10);
    transform: translateY(-2px);
}
.check-rail {
    flex: 0 0 4px;
    border-radius: 6px 0 0 6px;
    background-color: var(--rule);
}
.check-card.is-blocking .check-rail { background-color: #9B2C2C; }
.check-card.is-warning  .check-rail { background-color: #B4690E; }
.check-card.is-info     .check-rail { background-color: var(--hetvs-green); }

.check-body { min-width: 0; }
.check-value {
    font-size: 1.6rem; font-weight: 800; line-height: 1;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}
.check-card.is-blocking .check-value { color: #9B2C2C; }
.check-card.is-warning  .check-value { color: #B4690E; }

.check-label { font-size: .95rem; font-weight: 600; color: var(--ink); }
.check-consequence {
    font-size: .85rem; color: var(--ink-3); line-height: 1.45; margin-bottom: 8px;
}
.check-action {
    font-size: .82rem; font-weight: 700; letter-spacing: .01em;
    color: var(--hetvs-green);
}
.check-card:hover .check-action { color: var(--hetvs-green-dark); }

/* A lone finding spans the row, so its call to action sits at the right edge
   instead of leaving two thirds of the card empty. */
.check-card--wide { align-items: center; }
.check-card--wide .check-body { display: flex; align-items: center; gap: 20px; width: 100%; }
.check-card--wide .check-consequence { margin-bottom: 0; flex: 1 1 auto; }
.check-card--wide .check-action { margin-left: auto; white-space: nowrap; }

/* --- Reports --------------------------------------------------------------
   A report is a document, so its table is set tighter and quieter than a
   working list: the figures are the object, not the chrome around them. */
.report-card {
    display: flex; flex-direction: column;
    border: 1px solid var(--rule-soft);
    border-radius: 6px;
    background-color: var(--surface);
    transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease;
}
.report-card:hover {
    border-color: var(--rule);
    box-shadow: 0 6px 18px rgba(16, 35, 26, .10);
    transform: translateY(-2px);
}
.report-body { flex: 1 1 auto; padding: 18px 18px 10px; }
.report-title {
    display: block; font-weight: 700; font-size: 1rem;
    color: var(--ink); text-decoration: none; margin-bottom: 6px;
}
.report-card:hover .report-title { color: var(--hetvs-green); }
.report-desc { font-size: .85rem; color: var(--ink-3); line-height: 1.5; margin: 0; }
.report-actions {
    display: flex; gap: 8px; padding: 0 18px 16px;
}

.report-table { font-size: .88rem; }
.report-table thead th { white-space: nowrap; }
.report-table tfoot .report-total td {
    border-top: 2px solid var(--rule);
    background-color: var(--surface-2);
}
.report-note {
    font-size: .82rem; font-style: italic; color: var(--ink-3);
    border-left: 3px solid var(--rule); padding-left: 12px;
}

/* --- Vote position --------------------------------------------------------
   Money never breaks across lines: "N1,173,932,110" wrapping into
   "N1,173,932,11 / 0" is not a small cosmetic problem on a treasury screen. */
.vote-figure {
    font-size: 1.5rem; font-weight: 800; line-height: 1.15;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.vote-figure.is-good { color: var(--hetvs-green); }
.vote-figure.is-over { color: #9B2C2C; }

/* Three bars on one scale, light to dark: nested magnitudes of the same money,
   so a sequential ramp rather than four unrelated hues. */
.vote-meter { display: flex; flex-direction: column; gap: 4px; }
.vote-track {
    height: 12px; border-radius: 3px;
    background-color: var(--surface-2);
    border: 1px solid var(--rule-soft);
    overflow: hidden;
}
.vote-fill { height: 100%; border-radius: 2px 0 0 2px; }
.vote-fill.is-released  { background-color: #A8D8BF; }
.vote-fill.is-committed { background-color: #3F9E6C; }
.vote-fill.is-drawn     { background-color: var(--hetvs-green-dark); }

.vote-legend {
    display: flex; align-items: center; gap: 8px;
    font-size: .82rem; color: var(--ink-3);
    margin-bottom: 10px;
}
.vote-key { width: 10px; height: 10px; border-radius: 2px; flex: 0 0 auto; }
.vote-key.is-released  { background-color: #A8D8BF; }
.vote-key.is-committed { background-color: #3F9E6C; }
.vote-key.is-drawn     { background-color: var(--hetvs-green-dark); }
.vote-name { flex: 1 1 auto; }
.vote-val { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

/* Money in tables lines up and never breaks. */
.num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* The worked example on the import page reads as a specimen, not as live data. */
.sample-table { background-color: var(--surface-2); border-radius: 6px; }
.sample-table th, .sample-table td { padding-left: 10px; padding-right: 10px; }

/* --- Import flow ----------------------------------------------------------
   The page is a three-step procedure and says so. The numbering encodes a real
   sequence — get a template, upload, check — not decoration. */
.import-steps {
    display: flex; flex-wrap: wrap; gap: 12px;
    list-style: none; margin: 0; padding: 0;
}
.import-step {
    flex: 1 1 220px;
    display: flex; align-items: flex-start; gap: 12px;
    padding: 14px 16px;
    border: 1px solid var(--rule-soft);
    border-radius: 6px;
    background-color: var(--surface);
}
.import-step.is-current { border-color: var(--hetvs-green); box-shadow: 0 2px 10px rgba(0,135,81,.10); }
.import-step.is-done { background-color: var(--surface-2); }

.step-mark {
    flex: 0 0 auto;
    width: 26px; height: 26px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .82rem; font-weight: 800;
    background-color: var(--surface-2); color: var(--ink-3);
    border: 1px solid var(--rule);
}
.import-step.is-current .step-mark {
    background-color: var(--hetvs-green); border-color: var(--hetvs-green); color: #fff;
}
.import-step.is-done .step-mark {
    background-color: var(--hetvs-green-light); border-color: var(--hetvs-green);
    color: var(--hetvs-green-dark);
}
.step-body { display: flex; flex-direction: column; min-width: 0; }
.step-name { font-weight: 700; font-size: .92rem; color: var(--ink); }
.step-note { font-size: .8rem; color: var(--ink-3); }

/* The same numeral inline in a card title, so the sections and the stepper read
   as one sequence rather than two lists that happen to agree. */
.step-inline {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 50%;
    font-size: .75rem; font-weight: 800;
    background-color: var(--hetvs-green); color: #fff;
    margin-right: 6px; vertical-align: 2px;
}
.step-inline.is-quiet { background-color: var(--rule); color: var(--ink-2); }

/* A download is a choice between two files, so it reads as a row with a reason
   attached — not two unlabelled buttons in a header. */
.pick-row {
    display: flex; align-items: center; gap: 14px;
    padding: 14px 4px;
    border-bottom: 1px solid var(--rule-soft);
    text-decoration: none;
}
.pick-row:last-of-type { border-bottom: 0; }
.pick-body { flex: 1 1 auto; min-width: 0; }
.pick-name { display: block; font-weight: 700; color: var(--ink); }
.pick-row:hover .pick-name { color: var(--hetvs-green); }
.pick-note { display: block; font-size: .82rem; color: var(--ink-3); line-height: 1.45; }
.pick-go {
    flex: 0 0 auto; font-size: .78rem; font-weight: 700;
    color: var(--hetvs-green); white-space: nowrap;
}

.rule-list { margin: 0; padding-left: 18px; }
.rule-list li { font-size: .85rem; color: var(--ink-3); line-height: 1.5; margin-bottom: 8px; }
.rule-list li:last-child { margin-bottom: 0; }
.rule-list b { color: var(--ink-2); font-weight: 700; }

/* The native file control is kept, not replaced: it stays in the label so the
   field still works with JavaScript off, and drag-and-drop is an addition. */
.dropzone {
    position: relative; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 4px;
    padding: 28px 20px;
    border: 2px dashed var(--rule);
    border-radius: 8px;
    background-color: var(--surface-2);
    cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}
.dropzone:hover, .dropzone.is-over { border-color: var(--hetvs-green); background-color: var(--hetvs-green-light); }
.dropzone.has-file { border-style: solid; border-color: var(--hetvs-green); background-color: var(--hetvs-green-light); }
.dropzone-input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.dropzone-icon { font-size: 1.4rem; color: var(--hetvs-green); line-height: 1; }
.dropzone-title { font-weight: 700; color: var(--ink); }
.dropzone-note { font-size: .8rem; color: var(--ink-3); }

/* A rejected row is dimmed, not shouted at: it carries its own reason already. */
tr.is-rejected { background-color: rgba(155, 44, 44, .035); }

/* --- Guidelines -----------------------------------------------------------
   A document, so it is set as one: a measured column, real heading hierarchy,
   and a contents list that stays put while the reader moves through it. */
.guide-nav-card { position: sticky; top: 20px; }
.guide-nav { display: flex; flex-direction: column; }
.guide-nav a {
    display: flex; gap: 10px; align-items: baseline;
    padding: 7px 8px; border-radius: 4px;
    font-size: .86rem; color: var(--ink-2); text-decoration: none;
    line-height: 1.35;
}
.guide-nav a:hover { background-color: var(--surface-2); color: var(--hetvs-green); }
.guide-num {
    flex: 0 0 20px; text-align: right;
    font-size: .76rem; font-weight: 700; color: var(--ink-3);
    font-variant-numeric: tabular-nums;
}

/* The measure is set on the body and centred, not on each element. Constraining
   the paragraphs alone kept the line length right but left the card's right half
   empty, which reads as a broken layout rather than a chosen one. */
.guide-body {
    padding: 34px 38px 40px;
    max-width: 80ch;
    margin-inline: auto;
}

.guide-lede {
    font-size: 1.05rem; line-height: 1.65; color: var(--ink-2);
    padding-bottom: 22px; margin-bottom: 6px;
    border-bottom: 1px solid var(--rule-soft);
}

.guide-body h2 {
    display: flex; align-items: baseline; gap: 12px;
    font-size: 1.28rem; font-weight: 700; color: var(--ink);
    margin: 38px 0 14px;
    scroll-margin-top: 20px;
}
.guide-body h2:first-of-type { margin-top: 30px; }
.guide-h-num {
    flex: 0 0 auto;
    font-size: .8rem; font-weight: 800; color: var(--hetvs-green);
    background-color: var(--hetvs-green-light);
    border-radius: 4px; padding: 3px 8px;
    font-variant-numeric: tabular-nums;
}

.guide-body p { font-size: .95rem; line-height: 1.68; color: var(--ink-2); margin-bottom: 14px; }
.guide-body ul, .guide-body ol { padding-left: 20px; margin-bottom: 16px; }
.guide-body li { font-size: .95rem; line-height: 1.62; color: var(--ink-2); margin-bottom: 9px; }
.guide-body b { color: var(--ink); font-weight: 700; }

/* The eight steps are a sequence, so they are numbered and spaced as one. */
.guide-flow { counter-reset: none; }
.guide-flow li { margin-bottom: 12px; }

.guide-rules > li { margin-bottom: 13px; }

/* A regulation reference is a citation, not part of the sentence's voice. */
.guide-ref { color: var(--ink-3); font-size: .86rem; font-style: italic; }

/* Where to click, set apart from the prose that describes why. */
.guide-path {
    font-size: .86rem; font-weight: 600;
    background-color: var(--surface-2);
    border: 1px solid var(--rule-soft);
    border-radius: 3px; padding: 1px 6px;
    color: var(--ink); white-space: nowrap;
}

.guide-note {
    border-left: 3px solid var(--hetvs-green);
    background-color: var(--hetvs-green-light);
    padding: 14px 16px; border-radius: 0 4px 4px 0;
    font-size: .9rem; line-height: 1.6; color: var(--ink-2);
    margin: 18px 0 20px;
}

.guide-table { font-size: .9rem; }
.guide-table td { color: var(--ink-2); line-height: 1.55; }

/* A record row whose target this reader may not open: still shown, but not
   pretending to be a link. */
.rec-row.is-static { cursor: default; }
.rec-row.is-static:hover { background-color: transparent; box-shadow: none; }
.rec-row.is-static:hover .rec-ref { color: var(--ink); }

/* The currency a batch is being assembled in, as a label on its card rather than
   a column repeated down every row. */
.cur-chip {
    display: inline-block;
    font-size: .78rem; font-weight: 800; letter-spacing: .06em;
    background-color: var(--hetvs-green-light);
    color: var(--hetvs-green-dark);
    border-radius: 4px; padding: 3px 8px;
    margin-right: 8px; vertical-align: 1px;
}


/* --- Notifications --------------------------------------------------------
   The bell, its dropdown, and the full page use one row treatment so a
   notification looks the same wherever it is read. The unread marker is a
   left rule rather than a filled background: a list where most rows are
   unread should still read as a list. */
.bell-dot {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 9px;
    background: #C0392B;
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    line-height: 17px;
    text-align: center;
}

.notif-list { max-height: 380px; overflow-y: auto; }

.notif-row,
.notif-page-row {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 12px 20px;
    border-bottom: 1px solid var(--rule-soft);
    border-left: 3px solid transparent;
}

.notif-row { color: inherit; text-decoration: none; }
.notif-row:hover { background: var(--surface-2); color: inherit; }
.notif-row:last-child { border-bottom: 0; }

.notif-row.is-unread,
.notif-page-row.is-unread { border-left-color: var(--hetvs-green); }

.notif-body { display: flex; flex-direction: column; min-width: 0; }
.notif-subject { font-weight: 600; color: var(--ink); }
.notif-detail {
    color: var(--ink-2);
    font-size: 11px;
    /* Two lines and no more: the detail is a prompt to open the thing, not the thing. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.notif-when { color: var(--ink-3); font-size: 10px; margin-top: 2px; }

/* A dot per kind, so the sort of thing it is registers before the words do. */
.notif-kind {
    width: 8px; height: 8px; border-radius: 50%;
    margin-top: 6px; flex: 0 0 auto;
    background: var(--ink-3);
}
.notif-assignment { background: var(--hetvs-green); }
.notif-returned   { background: #C0392B; }
.notif-approval   { background: #B7791F; }
.notif-payment    { background: #2B6CB0; }
.notif-overdue    { background: #C0392B; }

.notif-page-row { padding: 14px 4px; }
.notif-page-row:last-child { border-bottom: 0; }

/* --- Part payment ---------------------------------------------------------
   The amount cell on the pay office's queue is an input, not a figure, because
   a voucher can be settled in instalments. It stays visually a number. */
.pay-amount {
    width: 130px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* --- Warnings that stop a form -------------------------------------------
   Used by the duplicate-payment check and the retirement that does not
   balance. Deliberately not an .alert: it sits inside the form it governs and
   has to read as part of it. */
.check-warn {
    border: 1px solid #E9C46A;
    background: #FDF8EC;
    border-radius: 6px;
    padding: 14px 16px;
}

.check-warn--hard {
    border-color: #E3A9A2;
    background: #FBEFED;
}

.check-warn h4 { font-size: 13px; font-weight: 700; margin: 0 0 6px; color: var(--ink); }

/* --- Vote book ------------------------------------------------------------
   A ledger reads down the balance column, so that column is the one that has
   to be scannable. */
.votebook td.num, .votebook th.num { font-variant-numeric: tabular-nums; }
.votebook .bal { font-weight: 700; }
.votebook tr.is-appropriation { background: var(--hetvs-green-light); }

/* --- Assistant suggestions -----------------------------------------------
   A suggested row is marked, not decided. The tint is deliberately the same
   quiet green used for a highlighted ledger row rather than anything that
   reads as a verdict — the officer is still the one choosing. */
tr.is-suggested {
    background: var(--hetvs-green-light);
    box-shadow: inset 3px 0 0 0 var(--hetvs-green);
}

/* --- Document history -----------------------------------------------------
   Every workflow action already wrote a WorkflowEvent carrying the officer's
   comment; nothing ever displayed it. This is that record: a rail of entries,
   newest first, with the note quoted so it reads as something a person wrote
   rather than another metadata field. */

.tl { list-style: none; margin: 0; padding: 0; position: relative; }

/* The rail itself. Inset to pass through the middle of the dots, and stopped
   short at both ends so it does not float past the first and last entry. */
.tl::before {
    content: "";
    position: absolute;
    left: 7px;
    top: .7rem;
    bottom: .7rem;
    width: 2px;
    background: var(--rule-soft);
}

.tl-item { position: relative; padding: 0 0 1.6rem 2.1rem; }
.tl-item:last-child { padding-bottom: 0; }

.tl-dot {
    position: absolute;
    left: 0;
    top: .35rem;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--surface);
    border: 3px solid var(--rule);
    box-sizing: border-box;
}
.tl-dot.is-good { border-color: var(--hetvs-green); }
.tl-dot.is-bad  { border-color: #D9214E; }
.tl-dot.is-wait { border-color: #F1A33A; }
.tl-dot.is-move { border-color: #3E97FF; }

.tl-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem .75rem;
    margin-bottom: .3rem;
}
.tl-action { font-weight: 700; color: var(--ink); }

.tl-move { display: inline-flex; align-items: center; gap: .35rem; }
.tl-arrow { color: var(--ink-3); font-size: .9rem; }

.tl-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    font-size: .8rem;
}

/* Initials rather than a photo: there are no avatars in this system, and a
   coloured chip still gives the eye something to group repeated actors by. */
.tl-who {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--hetvs-green-light);
    color: var(--hetvs-green-dark);
    font-size: .65rem;
    font-weight: 700;
    letter-spacing: .02em;
    flex: none;
}

.tl-note {
    margin: .55rem 0 0;
    padding: .6rem .85rem;
    border-left: 3px solid var(--hetvs-green);
    border-radius: 0 6px 6px 0;
    background: var(--surface-2);
    color: var(--ink-2);
    font-size: .9rem;
    white-space: pre-wrap;   /* a typed reason keeps its line breaks */
}

/* --- Workflow rail --------------------------------------------------------
   Where the document has got to, and who holds it now. Added because "when HoA
   passes on it shows as reviewed" was a reasonable thing to be confused by: the
   status alone never said that somebody else still had to approve. */

.doc-rail {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.doc-step {
    display: flex;
    align-items: center;
    gap: .45rem;
    padding: .3rem .7rem .3rem .5rem;
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 600;
    color: var(--ink-3);
    background: var(--surface-2);
    white-space: nowrap;
}
.doc-step .doc-tick {
    width: 15px;
    height: 15px;
    border-radius: 50%;
    border: 2px solid currentColor;
    flex: none;
}
.doc-step.is-done { color: var(--hetvs-green-dark); background: var(--hetvs-green-light); }
.doc-step.is-done .doc-tick { background: var(--hetvs-green); border-color: var(--hetvs-green); }

/* The step the document is actually sitting on. Ringed rather than merely
   filled, so it is findable without relying on colour alone. */
.doc-step.is-now {
    color: var(--ink);
    background: var(--surface);
    box-shadow: 0 0 0 2px var(--hetvs-green);
}
.doc-step.is-now .doc-tick { border-color: var(--hetvs-green); }

.doc-step.is-stop { color: #A21B3E; background: #FDECF1; }
.doc-step.is-stop .doc-tick { background: #D9214E; border-color: #D9214E; }

/* Who the document is waiting on. Stated in words, because a status name is not
   the same thing as an instruction. */
.doc-await {
    display: flex;
    align-items: flex-start;
    gap: .6rem;
    padding: .7rem .9rem;
    border-radius: 8px;
    background: var(--hetvs-green-light);
    border: 1px solid rgba(0, 135, 81, .25);
}
.doc-await .doc-await-k {
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--hetvs-green-dark);
    font-weight: 700;
}

/* --- Action dialogs -------------------------------------------------------
   The workflow actions used to be bare textareas stacked in the page margin,
   which gave no clue that what you typed became part of the record. */

.act-modal .modal-content {
    border: 0;
    border-radius: 12px;
    overflow: hidden;
}
.act-modal .modal-header {
    border-bottom: 1px solid var(--rule-soft);
    padding: 1.1rem 1.4rem;
}
.act-modal .modal-body   { padding: 1.3rem 1.4rem; }
.act-modal .modal-footer {
    border-top: 1px solid var(--rule-soft);
    padding: .9rem 1.4rem;
    background: var(--surface-2);
}
/* A hairline of the action's own colour, so a destructive dialog is not the
   same object as a routine one. */
.act-modal .modal-content::before {
    content: "";
    display: block;
    height: 3px;
    background: var(--hetvs-green);
}
.act-modal--danger .modal-content::before { background: #D9214E; }
.act-modal--warn   .modal-content::before { background: #F1A33A; }

/* The action buttons on a details page. They size to their labels and wrap
   together; a single action must not stretch across the whole card, which reads
   as a banner rather than a button. */
.doc-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.doc-actions .btn { flex: 0 0 auto; }
@media (max-width: 575.98px) {
    /* On a phone, full width is the readable shape rather than a wide banner. */
    .doc-actions { display: grid; }
    .doc-actions .btn { width: 100%; }
}

/* The new components mostly read from the tokens, which already flip. These four
   surfaces were set as literals because they carry meaning rather than theme —
   a stopped step and a returned document must look wrong in either mode — so they
   get their own dark values instead of a tinted white that glares. */
:root[data-theme="dark"] .doc-step.is-stop {
    color: #FF9DB4;
    background: #3A1421;
}
:root[data-theme="dark"] .doc-await {
    background: rgba(0, 135, 81, .14);
    border-color: rgba(0, 135, 81, .4);
}
:root[data-theme="dark"] .doc-await .doc-await-k { color: #6FD3A6; }
:root[data-theme="dark"] .tl-who {
    background: rgba(0, 135, 81, .18);
    color: #6FD3A6;
}

/* --- The chain -----------------------------------------------------------
   Nine stages is too many for a stepper with words under every dot, so the
   step reads as a numbered bead and names itself on the one you are at. The
   line behind them is what carries the sense of distance travelled. */
.chain {
    display: flex;
    align-items: flex-start;
    gap: 0;
    overflow-x: auto;
    padding-bottom: 4px;
}

.chain-step {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    flex: 1 1 0;
    min-width: 58px;
    position: relative;
}

/* The connecting rule, drawn behind the beads rather than between them so it
   does not break when a step wraps. */
.chain-step::before {
    content: "";
    position: absolute;
    top: 13px;
    left: -50%;
    width: 100%;
    height: 2px;
    background: var(--rule-soft);
}
.chain-step:first-child::before { display: none; }
.chain-step.done::before,
.chain-step.here::before { background: var(--hetvs-green); }

.chain-dot {
    width: 26px; height: 26px; border-radius: 50%;
    display: grid; place-items: center;
    font-size: 11px; font-weight: 700;
    background: var(--surface-2); color: var(--ink-3);
    border: 2px solid var(--rule);
    position: relative; z-index: 1;
}
.chain-step.done .chain-dot {
    background: var(--hetvs-green); border-color: var(--hetvs-green); color: #fff;
}
.chain-step.here .chain-dot {
    background: #fff; border-color: var(--hetvs-green); color: var(--hetvs-green);
    box-shadow: 0 0 0 3px var(--hetvs-green-light);
}

.chain-name {
    font-size: 9px;
    line-height: 1.2;
    text-align: center;
    color: var(--ink-3);
    /* Only the stage you are at names itself; the rest would be a wall of words. */
    opacity: 0;
}
.chain-step.here .chain-name { opacity: 1; color: var(--hetvs-green); font-weight: 700; }
.chain-step:hover .chain-name { opacity: 1; }

/* --- The thread ----------------------------------------------------------
   A conversation and a record of movement in one column. The mark down the
   left is what separates the two: a note is a dot, a move is a rule. */
.thread { max-height: 520px; overflow-y: auto; padding-right: 6px; }

.thread-entry { display: flex; gap: 12px; padding: 10px 0; }

.thread-mark {
    width: 10px; height: 10px; border-radius: 50%;
    margin-top: 5px; flex: 0 0 auto;
    background: var(--ink-3);
}
.thread-move .thread-mark  { background: var(--hetvs-green); border-radius: 2px; height: 4px; margin-top: 8px; }
.thread-query .thread-mark { background: #B7791F; }
.thread-flag .thread-mark  { background: #C0392B; }
.thread-reply .thread-mark { background: var(--ink-3); opacity: .5; }
.thread-closure .thread-mark { background: var(--hetvs-green); }

.thread-body { flex: 1 1 auto; min-width: 0; }
.thread-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 2px; }

/* ==========================================================================
   Document pages — AIE, voucher, mandate
   ==========================================================================
   These three screens are the ones an officer stands at while deciding
   whether to commit public money, and each had grown its own arrangement of
   the same material: the money below the metadata on one, above it on
   another; the reasons not to proceed — an expired voucher, an unretired
   advance, an overridden duplicate warning — three cards down, under the
   figure they contradict.

   The order below is the order the decision is actually made in: what is
   this, what is it worth, is anything wrong with it, whose desk is it on.
   The classes are shared so the three pages read as one system; the markup
   stays in each view, because their middles are genuinely different and a
   partial covering all three would be mostly branches. */

.doc-mast { padding: 1.5rem 1.75rem; }

.doc-mast-top {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}

.doc-number {
    font-size: 1.5rem;
    font-weight: 800;
    line-height: 1.15;
    color: var(--ink);
    letter-spacing: -0.01em;
    margin: 0;
    /* Document numbers carry separators and are long; they may wrap, but they
       may not push the toolbar off the card. */
    overflow-wrap: anywhere;
}
.doc-kind {
    display: block;
    margin-top: .25rem;
    font-size: .8rem;
    color: var(--ink-3);
}
.doc-kind b { color: var(--ink-2); font-weight: 600; }

.doc-mast-tags { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }

/* --- the figures ----------------------------------------------------------
   One row, the principal amount first and largest. Officers compare these
   against a printed document held beside the screen, so they are set in
   tabular numerals and never wrap mid-figure. */
.doc-figures {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem 2.5rem;
    margin-top: 1.5rem;
}

.doc-figure { min-width: 0; }
.doc-figure-label {
    display: block;
    font-size: .66rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--ink-3);
    margin-bottom: .2rem;
}
.doc-figure-value {
    display: block;
    font-size: 1.6rem;
    font-weight: 800;
    line-height: 1.15;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.doc-figure-note {
    display: block;
    margin-top: .15rem;
    font-size: .76rem;
    color: var(--ink-3);
}
.doc-figure.is-lead .doc-figure-value { font-size: 1.9rem; }
.doc-figure.is-good .doc-figure-value { color: var(--hetvs-green); }
.doc-figure.is-spent .doc-figure-value { color: var(--ink-3); }
.doc-figure.is-minor .doc-figure-value { font-size: 1.15rem; font-weight: 700; color: var(--ink-2); }

/* A separating stroke rather than a box: the figures are one sentence. */
.doc-figure + .doc-figure { padding-left: 2.5rem; border-left: 1px solid var(--rule-soft); }
@media (max-width: 575.98px) {
    .doc-figures { gap: 1rem 1.5rem; }
    .doc-figure + .doc-figure { padding-left: 0; border-left: 0; }
    .doc-figure-value, .doc-figure.is-lead .doc-figure-value { font-size: 1.3rem; }
}

/* --- how much of it is used ---------------------------------------------- */
.doc-meter {
    height: 6px;
    border-radius: 3px;
    background: var(--rule-soft);
    overflow: hidden;
    margin-top: 1.25rem;
}
.doc-meter-fill { height: 100%; background: var(--hetvs-green); border-radius: 3px; }
.doc-meter-fill.is-full { background: var(--ink-3); }

/* --- whose desk it is on -------------------------------------------------
   The one fact an officer opening a record most often came for, and the one
   none of these pages stated outright. */
.doc-holder {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .1rem .5rem;
    margin-top: 1.5rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--rule-soft);
    font-size: .82rem;
    color: var(--ink-2);
}
.doc-holder-mark {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--hetvs-green);
    flex: 0 0 auto;
    align-self: center;
    margin-right: .2rem;
}
.doc-holder-mark.is-stopped { background: #9B2C2C; }
.doc-holder-mark.is-done    { background: var(--ink-3); }
.doc-holder b { color: var(--ink); font-weight: 700; }

/* --- reasons not to proceed ----------------------------------------------
   Directly under the masthead, above everything else on the page. An officer
   who is about to be told the voucher cannot be paid should be told before
   they have read the amount, not after. */
.doc-alert {
    display: grid;
    grid-template-columns: 3px minmax(0, 1fr);
    gap: 0 .9rem;
    padding: .9rem 1rem .9rem .75rem;
    border-radius: 8px;
    border: 1px solid var(--rule);
    background: var(--surface-2);
    margin-bottom: .75rem;
}
.doc-alert::before { content: ""; border-radius: 2px; background: var(--ink-3); }
.doc-alert h4 { margin: 0 0 .2rem; font-size: .84rem; font-weight: 700; color: var(--ink); }
.doc-alert p  { margin: 0; font-size: .8rem; color: var(--ink-2); }
.doc-alert .btn { margin-top: .6rem; }

.doc-alert.is-warn   { border-color: #E4C89A; background: #FDF7EC; }
.doc-alert.is-warn::before { background: #B4690E; }
.doc-alert.is-danger { border-color: #E3A9A2; background: #FBEFED; }
.doc-alert.is-danger::before { background: #9B2C2C; }

:root[data-theme="dark"] .doc-alert.is-warn   { border-color: #5A4520; background: #241C10; }
:root[data-theme="dark"] .doc-alert.is-danger { border-color: #5C2B26; background: #251311; }
:root[data-theme="dark"] .doc-alert h4 { color: var(--ink); }
:root[data-theme="dark"] .doc-alert p  { color: var(--ink-2); }

/* --- the summary rail ----------------------------------------------------
   The particulars that were spread across a grid in the middle of the page,
   moved out of the reading column. Sticky, because the column beside it is a
   thread that can run to any length and the officer keeps referring back. */
@media (min-width: 992px) {
    .doc-side { position: sticky; top: 1.25rem; }
}

.doc-facts { margin: 0; }
.doc-fact {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: .1rem;
    padding: .65rem 0;
    border-bottom: 1px solid var(--rule-soft);
}
.doc-fact:last-child { border-bottom: 0; padding-bottom: 0; }
.doc-fact:first-child { padding-top: 0; }
.doc-fact dt {
    font-size: .66rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--ink-3);
}
.doc-fact dd {
    margin: 0;
    font-size: .86rem;
    font-weight: 600;
    color: var(--ink);
    overflow-wrap: anywhere;
}
.doc-fact dd .sub {
    display: block;
    font-size: .76rem;
    font-weight: 400;
    color: var(--ink-3);
}

/* --- Dashboard masthead ---------------------------------------------------
   The green band, carrying what the muted strip above it used to duplicate.
   Ink is fixed white here rather than tokenised: this panel paints its own
   dark ground in both themes, so it does not follow the surface tokens. */
.dash-mast { padding: 1.6rem 2rem 1.4rem; }

.dash-mast-top {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem 2rem;
}

.dash-hello {
    font-size: 1.6rem;
    font-weight: 800;
    line-height: 1.15;
    color: #fff;
    letter-spacing: -0.01em;
}
.dash-post {
    margin-top: .3rem;
    font-size: .86rem;
    color: rgba(255, 255, 255, .82);
}

.dash-mast-side { text-align: right; }
.dash-year { font-size: .95rem; font-weight: 700; color: #fff; }
.dash-date { font-size: .74rem; color: rgba(255, 255, 255, .7); margin-top: .1rem; }

/* White on green, because the green button the rest of the app uses for a primary
   action would vanish into this band. */
.dash-action {
    background: #fff;
    color: var(--hetvs-green-dark);
    font-weight: 700;
    border: 0;
}
.dash-action:hover { background: rgba(255, 255, 255, .88); color: var(--hetvs-green-dark); }

.dash-state {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: 1.4rem;
    padding-top: 1.1rem;
    border-top: 1px solid rgba(255, 255, 255, .22);
    font-size: .85rem;
    color: rgba(255, 255, 255, .9);
}
.dash-state b { color: #fff; font-weight: 800; }

.dash-state-mark {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: #FFD98A;
    flex: 0 0 auto;
}
.dash-state-mark.is-clear { background: rgba(255, 255, 255, .55); }
.dash-state-mark.is-late  { background: #FF9B8A; }

.dash-state-late {
    background: rgba(0, 0, 0, .22);
    border-radius: 999px;
    padding: .1rem .6rem;
    font-weight: 700;
    color: #fff;
}

@media (max-width: 575.98px) {
    .dash-mast { padding: 1.25rem 1.25rem 1.1rem; }
    .dash-hello { font-size: 1.25rem; }
    .dash-mast-side { text-align: left; }
}

/* ==========================================================================
   Reports — narrowing, and what the figures show
   ========================================================================== */

/* A bar, not a form: it sits above the report it narrows and has to read as a
   row of controls rather than a page of questions. Wraps rather than scrolls,
   because a hidden filter is a filter the officer forgets is applied. */
.report-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .75rem 1rem;
}

.rf-field { display: flex; flex-direction: column; gap: .25rem; min-width: 8.5rem; }
.rf-field.rf-wide { min-width: 16rem; flex: 1 1 16rem; }

.rf-label {
    font-size: .66rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.rf-actions { display: flex; gap: .5rem; align-items: flex-end; margin-left: auto; }

@media (max-width: 575.98px) {
    .rf-field, .rf-field.rf-wide { min-width: 100%; flex: 1 1 100%; }
    .rf-actions { margin-left: 0; width: 100%; }
    .rf-actions .btn { flex: 1 1 auto; }
}

/* --- an advisory finding --------------------------------------------------
   Deliberately quieter than a workflow warning. These are read from figures
   the officer can see for themselves, by something that is allowed to be
   wrong — so they must never carry the weight of the check-warn panels, which
   state facts the system has verified. */
.insight {
    border: 1px solid var(--rule);
    border-left: 3px solid var(--ink-3);
    border-radius: 8px;
    padding: .85rem 1rem;
    background: var(--surface-2);
    height: 100%;
}
.insight.is-concern { border-left-color: #9B2C2C; }
.insight.is-watch   { border-left-color: #B4690E; }
.insight.is-good    { border-left-color: var(--hetvs-green); }

.insight-what {
    font-size: .86rem;
    font-weight: 700;
    color: var(--ink);
    margin-bottom: .25rem;
}
.insight-why { font-size: .8rem; color: var(--ink-2); }
.insight-do {
    margin-top: .5rem;
    font-size: .78rem;
    font-weight: 600;
    color: var(--hetvs-green-dark);
}
:root[data-theme="dark"] .insight-do { color: #6FD3A6; }

/* ==========================================================================
   Dashboard — the redesign
   ==========================================================================
   What this replaces: six full-width cards stacked in one column, each of the
   same visual weight, with the charts appended underneath in a role-specific
   block. An officer saw two things above the fold and neither was ranked
   against the other.

   The shape now is a command layout. A row of headline figures that each
   carry their own twelve-month shape, then a wide column of work beside a
   narrow rail of things to be aware of. The work is a dense list rather than
   tiles, because six items that fit beat three that look impressive. */

.dash-grid {
    display: grid;
    grid-template-columns: minmax(0, 2.1fr) minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
}
@media (max-width: 1199.98px) {
    .dash-grid { grid-template-columns: minmax(0, 1fr); }
}

/* The rail travels with the officer down a long left column. */
@media (min-width: 1200px) {
    .dash-rail { position: sticky; top: 1.25rem; }
}

/* --- headline figures -----------------------------------------------------
   A number, its direction, and the shape behind it. */
.metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.metric {
    position: relative;
    overflow: hidden;
    background: var(--surface);
    border: 1px solid var(--rule-soft);
    border-radius: 10px;
    padding: 1rem 1.1rem 0.9rem;
    min-height: 6.75rem;
    display: flex;
    flex-direction: column;
    text-decoration: none;
    transition: border-color .12s ease, box-shadow .12s ease;
}
a.metric:hover {
    border-color: var(--rule);
    box-shadow: 0 4px 14px rgba(16, 35, 26, .07);
}

.metric-label {
    font-size: .66rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--ink-3);
    margin-bottom: .35rem;
}

.metric-value {
    font-size: 1.55rem;
    font-weight: 800;
    line-height: 1.1;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
    position: relative;
    z-index: 1;
}
.metric.is-good .metric-value { color: var(--hetvs-green); }
.metric.is-warn .metric-value { color: #B4690E; }
.metric.is-bad  .metric-value { color: #9B2C2C; }

.metric-sub {
    font-size: .74rem;
    color: var(--ink-3);
    margin-top: .2rem;
    position: relative;
    z-index: 1;
}

/* Direction, said in one glyph and one figure rather than a sentence. */
.metric-delta {
    display: inline-flex;
    align-items: center;
    gap: .15rem;
    font-size: .7rem;
    font-weight: 700;
    padding: .05rem .35rem;
    border-radius: 999px;
    margin-left: .4rem;
    vertical-align: middle;
    background: var(--surface-2);
    color: var(--ink-3);
}
.metric-delta.is-good { background: var(--hetvs-green-light); color: var(--hetvs-green-dark); }
.metric-delta.is-bad  { background: #FBEFED; color: #9B2C2C; }

/* The sparkline sits behind the figure, bled to the tile's edges. It is the
   shape that matters, so it carries no axes, no labels and no gridlines. */
.metric-spark {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 2.5rem;
    opacity: .5;
    pointer-events: none;
}
.metric-spark svg { display: block; width: 100%; height: 100%; }

:root[data-theme="dark"] .metric { background: var(--surface); }
:root[data-theme="dark"] .metric-delta { background: var(--surface-2); }

/* --- the work -------------------------------------------------------------
   A dense ranked list. The tiles this replaces were 150px tall apiece and
   three of them filled the fold; these fit eight. */
.work-row {
    display: grid;
    grid-template-columns: 3px auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 0 .9rem;
    padding: .8rem .65rem;
    margin: 0 -.65rem;
    border-radius: 8px;
    border-bottom: 1px solid var(--rule-soft);
    text-decoration: none;
    transition: background-color .12s ease;
}
.work-row:last-child { border-bottom: 0; }
.work-row:hover { background: var(--surface-2); }
.work-row:focus-visible { outline: 2px solid var(--hetvs-green); outline-offset: -2px; }

.work-rail {
    align-self: stretch;
    min-height: 2.1rem;
    border-radius: 2px;
    background: var(--rule);
}
.work-row.is-urgent .work-rail { background: #9B2C2C; }
.work-row.is-warn   .work-rail { background: #B4690E; }
.work-row.is-ok     .work-rail { background: var(--hetvs-green); }

.work-count {
    font-size: 1.5rem;
    font-weight: 800;
    line-height: 1;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
    min-width: 2.2rem;
    text-align: right;
}
.work-row.is-urgent .work-count { color: #9B2C2C; }

.work-body { min-width: 0; }
.work-name {
    display: block;
    font-weight: 700;
    font-size: .92rem;
    color: var(--ink);
    line-height: 1.25;
}
.work-row:hover .work-name { color: var(--hetvs-green); }
.work-hint {
    display: block;
    font-size: .76rem;
    color: var(--ink-3);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.work-go {
    font-size: .75rem;
    font-weight: 700;
    color: var(--ink-3);
    white-space: nowrap;
}
.work-row:hover .work-go { color: var(--hetvs-green); }

.work-flag {
    display: inline-block;
    font-size: .66rem;
    font-weight: 700;
    padding: .05rem .4rem;
    border-radius: 999px;
    background: #FBEFED;
    color: #9B2C2C;
    margin-left: .4rem;
}

/* --- the rail -------------------------------------------------------------
   Findings and advice, kept narrow so they never compete with the work. */
.rail-card + .rail-card { margin-top: 1.5rem; }

.rail-finding {
    display: block;
    padding: .75rem .85rem;
    border: 1px solid var(--rule-soft);
    border-left: 3px solid var(--ink-3);
    border-radius: 8px;
    text-decoration: none;
    background: var(--surface);
}
.rail-finding + .rail-finding { margin-top: .6rem; }
.rail-finding:hover { border-color: var(--rule); }
.rail-finding.is-blocking { border-left-color: #9B2C2C; }
.rail-finding.is-warning  { border-left-color: #B4690E; }
.rail-finding.is-info     { border-left-color: var(--hetvs-green); }

.rail-finding-head {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    margin-bottom: .15rem;
}
.rail-finding-value { font-size: 1.05rem; font-weight: 800; color: var(--ink); }
.rail-finding-label { font-size: .82rem; font-weight: 700; color: var(--ink); }
.rail-finding-why   { font-size: .74rem; color: var(--ink-3); }

/* --- work beside the assistant --------------------------------------------
   The two cards that answer "what do I do" and "what should I know" belong on
   one line. The split forms only once the assistant has actually returned
   something: its answer arrives after the page, so committing to two columns
   at render time would leave a role that gets nothing staring at an empty
   half. One column until then, and the grid gap does the spacing so neither
   card needs a margin that has to be undone in the other state. */
.work-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
}
@media (min-width: 1200px) {
    .work-split.has-insight {
        grid-template-columns: minmax(0, 2.1fr) minmax(0, 1fr);
    }
}

/* The card holds its place while the answer is being fetched. */
.insight-waiting {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .35rem 0 .6rem;
}

/* --- report catalogue tags ------------------------------------------------
   A catalogue of thirty returns is only navigable if each entry says what
   shape of answer it gives. An officer after a picture should not have to
   open four registers to find the one chart. */
.report-kind { display: flex; flex-wrap: wrap; gap: .3rem; margin: .35rem 0 .5rem; }

.report-tag {
    font-size: .64rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: .1rem .45rem;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--ink-3);
    border: 1px solid var(--rule-soft);
}
.report-tag.is-chart {
    background: var(--hetvs-green-light);
    color: var(--hetvs-green-dark);
    border-color: transparent;
}
:root[data-theme="dark"] .report-tag.is-chart { background: #163024; color: #6FD3A6; }

/* ==========================================================================
   Payees — suppliers, and the accounts money is sent to
   ==========================================================================
   The account line carries the weight on this screen. A correct payee with a
   mistyped account number is the commonest way public money reaches the wrong
   person, so verification state is marked on the rail rather than left to a
   badge among other badges. */
.payee {
    padding: 1rem 0 1.1rem;
    border-bottom: 1px solid var(--rule-soft);
}
.payee:last-of-type { border-bottom: 0; }

.payee-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: .5rem 1rem;
    margin-bottom: .6rem;
}
.payee-id { min-width: 0; }
.payee-name { display: block; font-weight: 700; font-size: .98rem; color: var(--ink); }
.payee-meta { display: block; font-size: .76rem; color: var(--ink-3); }
.payee-tools { display: flex; align-items: center; gap: .4rem; flex: 0 0 auto; }

.payee-none {
    font-size: .78rem;
    color: #9B2C2C;
    background: #FBEFED;
    border-radius: 6px;
    padding: .5rem .75rem;
}
:root[data-theme="dark"] .payee-none { background: #251311; }

.payee-acct {
    border-left: 3px solid var(--rule);
    padding: .5rem 0 .5rem .75rem;
    margin-left: .1rem;
}
.payee-acct + .payee-acct { margin-top: .5rem; }
.payee-acct.is-ok   { border-left-color: var(--hetvs-green); }
.payee-acct.is-warn { border-left-color: #B4690E; }
.payee-acct.is-bad  { border-left-color: #9B2C2C; }

.payee-acct-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    margin-bottom: .3rem;
    font-size: .84rem;
}
