/* ============================================================
   NOVERA.Software — app.css (adaptation layer)
   tokens.css is the LOCKED design system (shared with mockups).
   This file only adapts the fixed 390x844 mockup ".phone" frame
   to a fluid, full-viewport app shell. No styling decisions here.
   ============================================================ */
b {
    font-weight: 400 !important;
}

.app {
    position: relative;
    width: 100%;
    min-height: 75dvh;
    margin: 0 auto;
    overflow-x: clip;
    /*background: radial-gradient(120% 60% at 72% -15%, rgba(47,111,200,.34), transparent 62%), radial-gradient(90% 45% at 6% 6%, rgba(64,120,255,.15), transparent 60%), radial-gradient(80% 40% at 108% 52%, rgba(80,110,220,.13), transparent 65%), radial-gradient(100% 50% at 50% 115%, rgba(47,127,214,.15), transparent 62%), linear-gradient(180deg,#0b1326,#0a0f1e);*/
    background: linear-gradient(145deg, #010107 0%, #1E1E1E 50%, #13294B 100% 100% );
    display: flex;
    flex-direction: column;
    max-width: 768px;
}

/* Phone-width column on larger screens (mobile-first: full width below 480px) */
@media (min-width: 480px) {
    .app {
        max-width: 480px;
        box-shadow: 0 0 60px rgba(0,0,0,.5);
    }

    body {
        background: var(--nv-bg-deep);
    }
}

.app .scroll {
    /*padding-bottom: 120px;*/
}

/* Owner fix (2026-08-08, screenshot): fixed positioning is viewport-relative,
   so on wide screens the nav hugged the LEFT edge — centre it in the same
   480px column as the app. */
.app .bottomnav {
    position: fixed;
    left: 0;
    right: 0;
    /* v18.5 (batch95, owner report) — pinned + raised EXPLICITLY so no
       later rule or stacking context can lose the footer: bottom was
       inherited from tokens only, and z-index 5 sat below the heads. */
    bottom: 0;
    z-index: 30;
    margin: 0 auto;
    /*max-width: 480px;*/ /* owner */
    width: 100%; /* owner */
}

/*  v31.479 (owner: "Scroll does not reveall last row in full") — the bottom
    nav is FIXED and 96px tall, so the last 61px of a page's final row sat
    behind it with nowhere left to scroll. `.nav-clear` is the house spacer for
    exactly this (Calendar has carried one since v19.7) but it had no default
    height of its own — every user set it inline, and the seven Investments
    pages never set one at all. It now has one, so dropping the bare div in is
    enough; responsive.css removes it from 1024 up, where the nav gives way to
    the side rail. */
.nav-clear {
    height: calc(104px + env(safe-area-inset-bottom, 0px));
    flex: 0 0 auto;
}

.app .home-indicator {
    position: fixed;
    max-width: inherit;
}

/* Language switcher buttons built by i18n.js (buildSwitcher) — styled as chips */
.lang-btn {
    height: 48px;
    min-width: 48px;
    padding: 0 14px;
    border-radius: 999px;
    border: 1px solid rgba(104,152,232,.28);
    color: var(--nv-ink-2);
    font-family: inherit;
    font-size: var(--fs-caption);
    font-weight: 200;
    background: linear-gradient(160deg,rgba(120,165,240,.08),rgba(255,255,255,.02));
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}

    .lang-btn.active {
        background: rgba(232,167,92,.14);
        border-color: rgba(232,167,92,.45);
        color: var(--nv-gold-bright);
        font-weight: 200;
    }

/* ── Home page layout (from mockup 02, round 5) ─────────────── */
.app .scroll {
    overflow: visible;
    flex: initial;
}

.hb {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    /* owner (2026-08-09): header bottom padding ≥10px everywhere */
    padding: 14px 20px 10px;
}

    .hb .hi {
        font-size: 16px;
        color: var(--nv-ink-2);
    }

    .hb h1 {
        font-size: 24px;
        font-weight: 400; /* owner (2026-08-11 v10) */
        line-height: 1.15;
        color: var(--nv-ink);
    }

        .hb h1 .dot {
            color: var(--nv-gold);
        }

/*  ⚠ v31.158 — `.brand` (the logo link in the header and the side rail) had no
    base rule anywhere, only responsive overrides, so as an <a> it carried UA
    link blue and an underline on EVERY page in the solution. It is inert today
    because the element contains nothing but an <img> and an underline under an
    image paints nothing — it is fixed because a wrong rule that is currently
    invisible is still a wrong rule, and this is the same v29.6 ruling as
    `.chip`, `.more` and `.iconbtn`. Nothing about the logo's rendering changes. */
.brand { text-decoration: none; color: inherit; }

.iconbtn {
    width: 48px;
    height: 48px;
    /* v18.3 (batch93, owner — TOOLKIT): ONE radius, 6px, like the inputs */
    border-radius: 6px;
    border: 1px solid rgba(104,152,232,.24);
    background: rgba(255,255,255,.03);
    /*  ⚠ v31.158 — same v29.6 ruling as `.chip` and `.more`. Latent rather
        than visible today, because every `.iconbtn` currently holds an SVG and
        an underline under an icon paints nothing — but the first one given a
        text label would have shown it. */
    text-decoration: none;
    color: var(--nv-gold);
    display: flex;
    align-items: center;
    justify-content: center;
}

.org {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 4px;
    height: 32px;
    padding: 0 12px;
    border-radius: var(--r-pill);
    background: rgba(232,167,92,.10);
    border: 1px solid rgba(232,167,92,.25);
    color: var(--nv-gold-bright);
    font-size: 15px;
    font-weight: 200;
}

.home-scroll {
    gap: 10px;
    padding-top: 8px;
}

    .home-scroll .card {
        padding: 14px 18px;
    }

        .home-scroll .card.tight {
            padding: 8px 16px;
        }

    .home-scroll .row {
        min-height: 48px;
        padding: 2px 0;
    }

        .home-scroll .row .ic {
            flex-basis: 40px;
            height: 40px;
        }

/* owner */
@media (max-width: 480px) {
    .home-scroll .row.hide {
        display: none;
    }

    .hb .hi {
        display: none;
    }

    .app .scroll {
        padding-bottom: 0px;
    }

    .dash-pager {
        border-bottom: 0;
    }

    .card {
        max-width: 347px;
    }
}

.chart-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 6px;
}

.legend {
    display: flex;
    gap: 18px;
    margin-top: 10px;
}

    .legend span {
        display: inline-flex;
        align-items: center;
        gap: 7px;
        font-size: 15px;
        color: var(--nv-ink-2);
    }

    .legend i {
        width: 9px;
        height: 9px;
        border-radius: 50%;
        display: inline-block;
    }

.apps-row {
    display: grid;
    grid-template-columns: repeat(4,1fr);
    gap: 12px;
}

.app-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    min-height: 78px;
    text-decoration: none;
}

    /* v27.2 (PRODUCTION TICKET 20260821002 item 1.1, owner): tile icons carry
       NO background - the same flat treatment adopted everywhere else. */
    .app-tile .ic {
        width: 52px;
        height: 52px;
        border-radius: 8px;
        background: transparent;
        backdrop-filter: blur(12px);
        border: 1px solid rgba(232,167,92,.26);
        display: flex;
        align-items: center;
        justify-content: center;
        color: var(--nv-gold);
    }

/* ═══ v29.0 (owner: "In Favourite apps can we apply C5 to the icons?") ══════
   THE CONIC HALO ON THE FAVOURITE-APP ICONS, so Home, the financials hub and
   every ghost button now share one edge treatment instead of three.

   ⚠ FLAGGED DIVERGENCE, one line: these use the LIVE variant of C5 (gold at
   both ends), not the dormant one. The icons are gold-bordered and gold-drawn
   today, and the dormant gradient is blue-led - applying it literally would
   have turned every favourite icon blue, which is a bigger change than was
   asked for. The live variant keeps the gold identity and adds the turn.

   ⚠⚠ NO FILL HERE. The tile study carries a 5% radial to stop its halo
   reading as detached; these icons must NOT get one. v27.2 removed their
   background deliberately ("tile icons carry NO background - the same flat
   treatment adopted everywhere else") and that ruling stands. The ring is the
   whole change.

   ⚠⚠⚠ THE @supports GUARD IS LOAD-BEARING: without mask-composite the
   ::before is a filled rectangle, not a ring - a gold block where the icon
   should be. Guarded, an unsupported browser keeps the 1px gold border above,
   exactly as today. */
@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
    .app-tile .ic {
        position: relative;
        isolation: isolate;
        border-color: transparent;
    }

        .app-tile .ic::before {
            content: "";
            position: absolute;
            inset: 0;
            border-radius: inherit;
            padding: 1px;
            background: conic-gradient(from 210deg, var(--nv-gold-bright), rgba(47,127,214,.55) 25%, rgba(232,167,92,.20) 50%, rgba(47,127,214,.55) 75%, var(--nv-gold-bright));
            -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            -webkit-mask-composite: xor;
            mask-composite: exclude;
            pointer-events: none;
        }
}

    .app-tile .lbl {
        font-size: 15px;
        font-weight: 300; /* owner (2026-08-11 v10) */
        color: var(--nv-ink-2);
        text-align: center;
        line-height: 1.25;
    }

/* ── Language pager (round 5: ‹ 3 chips › + page dots) ──────── */
.lang-pager {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.lang-pager-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.lang-pager .chip {
    font-family: inherit;
}

.lang-pager .lang-chip {
    padding: 0 15px;
}

.lang-pager .lang-arrow {
    padding: 0;
    width: 48px;
    justify-content: center;
}

.lang-pager .chip.active {
    background: rgba(232,167,92,.14);
    border-color: rgba(232,167,92,.45);
    color: var(--nv-gold-bright);
    font-weight: 200;
}

.lang-dots {
    display: flex;
    justify-content: center;
    gap: 6px;
}

.lang-dot {
    width: 8px;
    height: 4px;
    border-radius: 2px;
    background: rgba(255,255,255,.18);
    transition: all .25s;
}

    .lang-dot.on {
        width: 16px;
        background: rgba(232,167,92,.75);
    }

/* ── Real form inputs + login error ─────────────────────────── */
input.input {
    display: block;
}

    input.input::placeholder {
        color: var(--nv-ink-3);
    }

    input.input:focus {
        outline: none;
        border-color: var(--nv-blue-bright);
        box-shadow: 0 0 0 3px rgba(47,127,214,.25);
    }

.login-error {
    border: 1px solid rgba(217,101,101,.45);
    background: rgba(217,101,101,.12);
    color: #e79a9a;
    border-radius: 14px;
    padding: 12px 16px;
    font-size: 15px;
}

/* ⚠ v31.46 (batch243) - THE SAYS-IT-WORKED TWIN OF `.login-error`.
   Slice 1b gave the app its first account write, and a write has to be able to
   report SUCCESS as visibly as failure - a save that says nothing is a save the
   reader has to go and verify.
   ⚠⚠ IT EXISTS BECAUSE THE MARKUP ASKED FOR IT AND THE CLASS DID NOT. The page
   was written against `.login-ok` on the assumption it matched `.login-error`;
   it did not, and the banner would have rendered as bare unstyled text. Caught
   by grepping the stylesheet for the class rather than by looking at the page -
   an unstyled element still renders, so nothing would have looked broken.
   ⚠ Same geometry as its sibling, deliberately: the two banners appear in the
   same slot and must not resize the page as they swap. Green is `--nv-good`,
   the token the ticks and matched rows already use. */
.login-ok {
    border: 1px solid rgba(63,202,133,.45);
    background: rgba(63,202,133,.12);
    color: #7fdcae;
    border-radius: 14px;
    padding: 12px 16px;
    font-size: 15px;
}

/* ── Dashboard flip card (Cash Flow ⇄ Bank Balances) ────────── */
.flip {
    perspective: 1400px;
}

.flip-inner {
    position: relative;
    transition: transform .7s cubic-bezier(.25,.6,.3,1);
    transform-style: preserve-3d;
    /* Fixed card height (owner): Bank Balances is 270px tall; a longer
       account list scrolls inside .bank-list instead of growing the card. */
    min-height: 250px; /* owner */
    height: 250px; /* owner */
}


.flip.is-flipped .flip-inner {
    transform: rotateY(180deg);
}

.flip-face {
    position: absolute;
    inset: 0;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

.flip-front {
    transform: rotateY(0deg);
}

.flip-back {
    transform: rotateY(180deg);
}

.flip .card {
    display: flex;
    flex-direction: column;
    overflow: hidden; /* fixed-height faces never spill */
}

.flip-dots {
    display: flex;
    justify-content: center;
    gap: 6px;
    margin-top: auto;
    padding-top: 10px;
}

    .flip-dots span {
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background: rgba(255,255,255,.20);
    }

        .flip-dots span.on {
            background: var(--nv-gold);
        }

.bank-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 6px;
    /* the list takes the space left in the fixed-height card and scrolls */
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    /*  ⚠⚠⚠ v31.31 (batch227, owner: "we do not need horizontal scroll.
        Horizontal scroll = none for the card") - THIS LINE IS NOT DECORATION.
        `overflow-y: auto` ALONE MAKES BOTH AXES SCROLLABLE: a box cannot be a
        scroll container on one axis and `visible` on the other, so the CSS
        `overflow-x: visible` this rule never set was computed to `auto` and
        the list has been a horizontal scroller since it was written. Naming
        the axis is what closes it, and it also stops any future long row from
        quietly re-opening it. */
    overflow-x: hidden;
}

.bank-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    /*  v31.31 - the 6px side padding used to live only on the openable rows
        (as a negative margin, which overflowed the list - see below). It sits
        on EVERY row now, so the hover tint has room inside the list and the
        plain rows, the openable rows and the Net position line all stay on the
        same left and right edges. */
    padding: 5px 6px;
}

/*  v31.30 (batch226, owner: "make the accounts clickable ... lead you to the
    reconciliation") - the openable variant of a bank row.
    ⚠ IT MUST NOT LOOK LIKE A LINK AT REST. The card is a list of figures and
    an underlined blue run through the middle of it would read as damage; the
    row keeps its own type and colour and earns a tell only under the pointer.
    ⚠ THE TELL IS A BACKGROUND, NOT AN UNDERLINE - `.bank-name` already
    ellipses, and an underline under a clipped name is a second thing going
    wrong in the same 15px.
    ⚠⚠ v31.31: THE NEGATIVE MARGIN IS GONE. It was `margin: 0 -6px` so the
    tint could bleed into the card's padding, which made every openable row
    12px WIDER THAN THE LIST THAT HOLDS THEM - one of the three things putting
    a horizontal scrollbar under this card. The padding moved onto `.bank-row`
    instead, where every row shares it and nothing overflows.
    ⚠ A ROW THAT CANNOT RESOLVE TO ONE ACCOUNT NEVER GETS THIS CLASS - see
    BankBalance.CanOpen. */
a.bank-row.bank-open {
    text-decoration: none;
    color: inherit;
    border-radius: 6px;
    cursor: pointer;
}

    a.bank-row.bank-open:hover,
    a.bank-row.bank-open:focus-visible {
        background: rgba(232,167,92,.10);
        outline: none;
    }

        a.bank-row.bank-open:hover .bank-name,
        a.bank-row.bank-open:focus-visible .bank-name {
            color: var(--nv-gold-bright, #f2bc74);
        }

.bank-name {
    font-size: 15px;
    color: var(--nv-ink-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /*  ⚠⚠⚠ v31.31 (owner) - `text-overflow` WAS ALREADY HERE AND HAS NEVER
        DRAWN. THE ELLIPSIS ONLY APPEARS ON A BOX THAT ACTUALLY OVERFLOWS, and
        a flex item's automatic minimum size is its MIN-CONTENT: "Bankové účty
        - VÚB - MasterCard Business World" refused to shrink, so the ROW grew
        past the list instead and the name was never clipped - there was
        nothing to clip, only something to scroll to.
        ⚠ `min-width: 0` ALONE IS NOT ENOUGH HERE - the same lesson as the
        email subject line: the item must also be told it may SHRINK, so
        `flex: 1 1 0` and the amount beside it keeps its natural width. */
    flex: 1 1 0;
    min-width: 0;
}

.bank-amt {
    font-size: 16px;
    font-weight: 200;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

    .bank-amt.pos {
        color: var(--acc-green,#3fca85);
    }

    .bank-amt.neg {
        color: #e79a9a;
    }

.bank-total .bank-name {
    font-weight: 200;
    color: var(--nv-gold);
}

.bank-total .bank-amt {
    font-size: 18px;
}


/* ============================================================
   Feature styling folded in from the old features.css / module
   files (owner now owns ALL edits; single-file scheme dropped).
   ============================================================ */

/* ── 1) FORM CONTROLS ───────────────────────────────────────────
   tokens.css leaves the .input background commented out, so native
   <input>/<select>/<textarea> fall back to the browser's white.
   Give them the dark glass fill and neutralise Chrome/Edge/Safari
   autofill (which otherwise repaints the field white/pale-yellow).
   Only real form controls are targeted — the div-based .input
   display rows are untouched. ── */
input.input,
textarea.input,
select.input {
    background-color: #101a30; /* --nv-surface, opaque: never white */
    background-image: linear-gradient(160deg, rgba(120,165,240,.07), rgba(255,255,255,.012));
    color: var(--nv-ink);
}

    input.input::placeholder,
    textarea.input::placeholder {
        color: var(--nv-ink-3);
    }

    input.input:-webkit-autofill,
    input.input:-webkit-autofill:hover,
    input.input:-webkit-autofill:focus,
    input.input:-webkit-autofill:active {
        -webkit-text-fill-color: var(--nv-ink);
        -webkit-box-shadow: 0 0 0 1000px #101a30 inset;
        box-shadow: 0 0 0 1000px #101a30 inset;
        caret-color: var(--nv-ink);
        transition: background-color 100000s ease 0s; /* hold the fill, no white flash */
    }


/* ── 2) EMAIL PAGES ─────────────────────────────────────────── */
a.mail {
    text-decoration: none;
    color: inherit;
}

.mail {
    display: flex;
    gap: 14px;
    padding: 14px 0;
    min-height: 76px;
    align-items: flex-start;
}

    .mail + .mail {
        border-top: 1px solid rgba(104,152,232,.13);
    }

    .mail .av {
        flex: 0 0 46px;
        height: 46px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-weight: 200;
        font-size: 17px;
        background: rgba(232,167,92,.16);
        color: var(--nv-gold-bright);
    }

        /* Per-sender avatar colours — the Novera.Apartments palette (eml-av
       g0…g4); the class is picked by a stable hash of the sender. */
        .mail .av.g0 {
            background: #33526b;
            color: #e9eef7;
        }

        .mail .av.g1 {
            background: #6d5a8e;
            color: #e9eef7;
        }

        .mail .av.g2 {
            background: #2e7d64;
            color: #e9eef7;
        }

        .mail .av.g3 {
            background: #9a6a16;
            color: #f4ecdc;
        }

        .mail .av.g4 {
            background: #874a52;
            color: #f4e5e7;
        }

    .mail .body {
        flex: 1;
        min-width: 0;
    }

    .mail .l1 {
        display: flex;
        align-items: baseline;
        gap: 8px;
    }

    .mail .from {
        flex: 1;
        min-width: 0;
        font-size: 18px;
        font-weight: 200;
        color: var(--nv-ink);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .mail.read .from {
        font-weight: 200;
        color: var(--nv-ink-2);
    }

    .mail .when {
        font-size: 15px;
        color: var(--nv-ink-3);
        white-space: nowrap;
    }

    .mail .subj {
        font-size: 15.5px;
        color: var(--nv-ink-2);
        line-height: 1.4;
        margin-top: 2px;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }

    .mail.read .subj {
        color: var(--nv-ink-3);
    }

    .mail .meta {
        display: flex;
        align-items: center;
        gap: 8px;
        margin-top: 5px;
    }

.dot-unread {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--acc-green);
}

.tag {
    font-size: 15px;
    font-weight: 200;
    padding: 2px 9px;
    border-radius: var(--r-pill);
}

    .tag.att {
        background: rgba(47,127,214,.16);
        color: #8fc0f0;
    }

    .tag.fail {
        background: rgba(217,101,101,.16);
        color: #e79a9a;
    }

.email-body {
    /* Owner (2026-08-07): received HTML mail is designed for a light page —
       render it on white with black text, like a classic mail client. */
    background: white;
    color: black;
    font-size: 16px;
    line-height: 1.6;
    overflow-wrap: anywhere;
    word-break: break-word;
    /* HTML emails arrive with fixed-width layouts — everything must stay
       INSIDE the card. Anything wider scrolls horizontally within the card
       instead of spilling over the page. */
    overflow-x: auto;
    max-width: 100%;
}

    .email-body a {
        color: #1d6fd1; /* readable link blue on the white body */
    }

    .email-body img {
        max-width: 100% !important;
        height: auto !important;
    }

    .email-body table {
        max-width: 100% !important;
        height: auto !important;
    }

    .email-body td, .email-body th, .email-body div, .email-body p,
    .email-body span, .email-body blockquote {
        max-width: 100% !important;
    }

    .email-body pre {
        white-space: pre-wrap;
        overflow-wrap: anywhere;
    }

textarea.input {
    display: block;
}

/* ── 4) CALENDAR ────────────────────────────────────────────── */
.cal-head {
    display: flex;
    align-items: center;
    gap: 12px;
    justify-content: space-between;
    /* owner (2026-08-09): header bottom padding ≥10px everywhere */
    padding: 12px 20px 10px;
    position: sticky;
    top: 0;
    z-index: 20;
    background: linear-gradient(180deg, rgba(8,12,24,.94), rgba(8,12,24,.80));
    backdrop-filter: blur(18px) saturate(140%);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
}

    .cal-head h1 {
        font-size: 24px;
        font-weight: 100;
        flex: 1;
    }

    .cal-head .iconbtn {
        flex: 0 0 44px;
    }

.navbtn {
    width: 40px;
    height: 40px;
    border-radius: 12px;
    border: 1px solid rgba(104,152,232,.24);
    background: rgba(255,255,255,.03);
    color: var(--nv-ink);
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
}

.month {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 20px 8px;
    /* v8.0 (owner) — separator + breathing room above the month row */
    border-top: 1px solid rgba(104, 152, 232, .18);
    padding-top: 12px;
    position: relative;
    z-index: 2;
}

    .month .m {
        font-size: 20px;
        font-weight: 200;
        color: var(--nv-gold-bright);
        min-width: 85px;
        text-align: center;
    }

.dow {
    display: grid;
    grid-template-columns: repeat(7,1fr);
    padding: 8px 14px 0;
    position: relative;
    z-index: 2;
    /* v7.2 (owner): subtle line separating the nav area from the week dates */
    border-top: 1px solid rgba(104,152,232,.18);
    margin: 6px 14px 0;
}

    .dow span {
        text-align: center;
        font-size: 15px;
        color: var(--nv-ink-3);
        padding: 4px 0;
    }

.grid {
    display: grid;
    grid-template-columns: repeat(7,1fr);
    gap: 3px;
    padding: 0 14px;
    position: relative;
    z-index: 2;
}

.day {
    aspect-ratio: 1/1.05;
    border-radius: 10px;
    border: 1px solid rgb(47 74 131);
    background: rgba(18,30,56,.28);
    padding: 5px 4px 3px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    overflow: hidden;
    text-decoration: none;
}

    .day .n {
        font-size: 15px;
        color: var(--nv-ink-2);
        text-align: right;
        line-height: 1;
    }

    .day.today {
        border-color: var(--nv-red);
    }

        .day.today .n {
            color: var(--nv-gold-bright);
            font-weight: 200;
        }

    .day.sel {
        background: rgba(232,167,92,.12);
        border-color: rgba(232,167,92,.45);
    }

.dots {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    margin-top: auto;
}

.ev {
    height: 4px;
    border-radius: 2px;
    flex: 1 1 auto;
    min-width: 8px;
}

.agenda {
    position: relative;
    z-index: 2;
    padding: 12px 20px 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

    .agenda .title {
        font-size: 16px;
        font-weight: 200;
        color: var(--nv-gold);
    }

.ev-row {
    display: flex;
    gap: 12px;
    align-items: flex-start;
}

    .ev-row .time {
        flex: 0 0 54px;
        font-size: 15px;
        color: var(--nv-ink-3);
        text-align: right;
        padding-top: 2px;
    }

.ev-card {
    flex: 1;
    min-width: 0; /* v7.2: lets the .em row ellipsise instead of overflowing */
    border-radius: 10px;
    padding: 10px 12px;
    background: rgba(18,30,56,.4);
    border-left: 3px solid var(--nv-gold);
    margin: 10px 14px; /* owner (batch126) */
}

    .ev-card .et {
        font-size: 15px;
        font-weight: 200;
        color: var(--nv-ink);
    }

        .ev-card .et.done {
            text-decoration: line-through;
            color: var(--nv-ink-3);
        }

    .ev-card .em {
        font-size: 15px;
        color: var(--nv-ink-3);
        margin-top: 1px;
        /* v7.2 (owner): the account/category row stays on ONE line */
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

.legend-cal {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    padding: 12px 20px 120px;
    position: relative;
    z-index: 2;
}

    .legend-cal span {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        font-size: 15px;
        color: var(--nv-ink-2);
    }

    .legend-cal i {
        width: 10px;
        height: 10px;
        border-radius: 3px;
    }

.cal-head a.chip, .month a.chip, .cal-head a {
    text-decoration: none;
}

/* ── Calendar: view toggle + month grid (owner: default month + switch) ── */
.cal-views .chips {
    margin: 0;
}

.month-grid .day {
    aspect-ratio: 1 / 0.9;
}

.day.out {
    opacity: .34;
}

    .day.out .n {
        color: var(--nv-ink-3);
    }


/* ============================================================
   Email enhancements batch — mail bar, folders/confirm sheets,
   multi-select, toast, compose From selector. (Folded here; the
   old email.css is retired.)
   ============================================================ */
/* ── multi-select (owner: aligned as in Novera.Apartments — the
      tick is the trailing element on the right of each row, and a
      tick never opens the email) ───────────────────────────────── */
.eml-selrow {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 4px 2px 0;
    min-height: 26px;
}

    .eml-selrow .sa {
        display: inline-flex;
        align-items: center;
        gap: 9px;
        font-size: 15px;
        color: var(--nv-ink-3);
    }

    .eml-selrow .cnt {
        font-size: 15px;
        color: var(--nv-ink-3);
    }

.mail .pick {
    flex: 0 0 auto;
    align-self: center;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin: -6px -6px -6px 2px;
}

input.eml-sel, input.eml-all, input.srch-opt, input.opt-tick, input.op-clbox {
    -webkit-appearance: none;
    appearance: none;
    width: 22px;
    height: 22px;
    border-radius: 6px;
    background-color: transparent;
    border: 1px solid rgba(232,167,92,.55);
    display: inline-grid;
    place-content: center;
    cursor: pointer;
    transition: border-color .12s, background-color .12s;
}

    input.eml-sel::before, input.eml-all::before, input.srch-opt::before, input.opt-tick::before, input.op-clbox::before {
        content: "";
        width: 12px;
        height: 12px;
        border-radius: 3px;
        transform: scale(0);
        transition: transform .12s ease;
        background: var(--nv-gold);
        box-shadow: 0 0 6px rgba(232,167,92,.5);
    }

    input.eml-sel:checked, input.eml-all:checked, input.srch-opt:checked, input.opt-tick:checked, input.op-clbox:checked {
        border-color: var(--nv-gold);
        background-color: rgba(232,167,92,.10);
    }

        input.eml-sel:checked::before, input.eml-all:checked::before, input.srch-opt:checked::before, input.opt-tick:checked::before, input.op-clbox:checked::before {
            transform: scale(1);
        }

.mail-bar .mb.disabled {
    opacity: .38;
    pointer-events: none;
}

.mail.picked {
    background: rgba(232,167,92,.06);
    border-radius: 10px;
}

/* ── sticky mail bar — the module owns the bar while in Email ── */
.mail-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    margin: 0 auto;
    max-width: 480px;
    z-index: 9;
    height: 74px;
    padding: 8px 14px calc(8px + env(safe-area-inset-bottom));
    display: flex;
    align-items: stretch;
    gap: 8px;
    background: linear-gradient(180deg,rgba(11,16,32,0),rgba(8,12,24,.92) 55%);
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    border-top: 1px solid rgba(104,152,232,.20);
}

    .mail-bar .mb {
        flex: 1;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        border: 0;
        background: transparent;
        font-family: inherit;
        font-size: 15px;
        font-weight: 200;
        color: var(--nv-ink-3);
        text-decoration: none;
        border-radius: 12px;
        border: 1px solid;
    }

        .mail-bar .mb svg {
            width: 24px;
            height: 24px;
        }

        .mail-bar .mb.on {
            color: var(--nv-gold-bright);
        }

        .mail-bar .mb.cta {
            color: #241706;
            background: var(--nv-gold);
            border-radius: 14px;
            font-weight: 200;
        }

        .mail-bar .mb.danger {
            color: #f0b3b3;
            background: rgba(217,101,101,.16);
            border-radius: 14px;
            font-weight: 200;
            border: 1px solid var(--nv-warn);
        }

        .mail-bar .mb .n {
            font-variant-numeric: tabular-nums;
        }

/* ── slide-up sheets (folders + confirm), aligned to the column ─ */
.mail-scrim {
    position: fixed;
    inset: 0;
    z-index: 11;
    background: rgba(3,6,14,.62);
    backdrop-filter: blur(2px);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s;
}

    .mail-scrim.on {
        opacity: 1;
        visibility: visible;
    }

/* v26.6 (owner) - THE SHEET IS A FLOATING CARD: the owner's radius,
   gradient and full border. v26.7 (owner): `140px auto` - floating AND
   CENTRED (the auto restores the horizontal centring that `140px 0`
   dropped; left:0/right:0 + max-width:480px need it). */
.mail-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* ⚠ v29.5 (owner) - THE SHEET'S TOP GAP IS PER SCREEN SIZE. The base is
       the SMALL-screen value (01/02/03-Screen); 04 and 05 raise it in
       responsive.css, which loads after this file and is where every
       breakpoint in the project lives. The `auto` on the horizontal side is
       load-bearing - it is what centres the sheet (left:0/right:0 with a
       max-width need it), so it stays in all four values. */
    margin: 20px auto !important;
    /* ⚠ v31.41 (batch238) - THE SAME NUMBER, READABLE BY THE CONTENT. A sheet
       whose body needs to cap its own height has to know how far off the
       bottom the sheet is sitting, and `margin` cannot be read from CSS. This
       custom property is re-declared beside each of the three overrides in
       responsive.css.
       ⚠⚠⚠ v31.83 - THEY ARE NOT THE SAME FACT, AND CALLING THEM ONE COST
       05-Screen the top of its sheet. The MARGIN lifts the sheet off the
       bottom; the GAP tells the sheet's body how short to be. Set them equal
       and the body grows back into exactly the room the margin made, leaving
       a fixed 20px at the top whatever the window height - which the sticky
       68px page header then covers. **The top gap you actually get is
       `gap - margin`.** 01/02/03/04-Screen are all small enough that the
       `60vh` half of the cap binds first and the question never arises; at
       ≥1920 it does, and that band now carries a gap 100px larger than its
       margin. ⚠ IF ONE OF THE FOUR MARGINS CHANGES, RE-DERIVE ITS GAP - do
       not simply copy the number across. */
    --sheet-gap: 20px;
    max-width: 480px;
    z-index: 12;
    transform: translateY(102%);
    transition: transform .26s cubic-bezier(.22,.61,.36,1);
    background: linear-gradient(145deg, #010107 -151%, #1E1E1E 50%, #13294B 100% 100%);
    backdrop-filter: blur(26px) saturate(140%);
    -webkit-backdrop-filter: blur(26px) saturate(140%);
    border: 1px solid var(--nv-ink-3);
    border-radius: 8px;
    padding: 10px 20px calc(30px + env(safe-area-inset-bottom));
    box-shadow: 0 -18px 48px rgba(0,0,0,.55);
}

    /* v9.1 (owner) — a closed sheet is fully HIDDEN, not just translated
       off-screen: its top shadow/blur peeked above the viewport bottom on
       tall displays (1920 profile sheet). Visibility flips after the slide. */
    .mail-sheet:not(.ent-sheet) {
        visibility: hidden;
        transition: transform .26s cubic-bezier(.22,.61,.36,1), visibility 0s .26s;
    }

    .mail-sheet.open {
        transform: translateY(0);
        visibility: visible;
        transition: transform .26s cubic-bezier(.22,.61,.36,1), visibility 0s;
    }

/*  ⚠⚠⚠ v31.481 (owner, 5 Nov, arrow drawn at the tray's top-right corner) —
    THE TRAY'S CLOSE BUTTON WAS NOT MISSING, IT WAS UNDERNEATH THE PAGE HEAD.
    `.screen-head` is z-index 20 (tokens.css) and a sheet is 12, so the head
    has always painted OVER an open tray. It only bites on 01-Screen, where
    the head WRAPS to three rows and grows down into the sheet's first 60px —
    exactly where `.t-x` sits. Measured at 320 and 390: a hit test on the ✕'s
    own centre returned the head's subtitle, so the press went to the head and
    the tray would not close. At 760 the head is one row, the ✕ is clear, and
    the same markup works — which is why this never showed on a desktop.
    ⚠ The two TRAYS are raised by id, not `.mail-sheet` wholesale: the entity
    sheet and the account-search cards set their own stacking further down
    this file and must not be reordered underneath it.
    31/32 clears the head (20) AND the fixed bottom nav (30), so the scrim
    now dims the head too — which is what a modal menu should look like. */
#mmScrim.on, #ftScrim.on { z-index: 31; }
#mmTray.open, #finTray.open { z-index: 32; }

    .mail-sheet .grab {
        width: 44px;
        height: 5px;
        border-radius: 3px;
        background: rgba(255,255,255,.22);
        margin: 4px auto 14px;
    }

    .mail-sheet h2 {
        font-size: var(--fs-h2);
        font-weight: 200;
        color: var(--nv-gold);
        margin-bottom: 10px;
    }

/* folders list inside the sheet */
.fld {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 15px 6px;
    text-decoration: none;
    color: var(--nv-ink);
    font-size: 17px;
    font-weight: 200;
}

    .fld + .fld {
        border-top: 1px solid rgba(104,152,232,.13);
    }

    .fld.on {
        color: var(--nv-gold-bright);
    }

    .fld .fi {
        flex: 0 0 26px;
        height: 26px;
        display: flex;
        align-items: center;
        justify-content: center;
        color: var(--nv-gold);
    }

    .fld .fl {
        flex: 1;
    }

    .fld .fc {
        font-size: 15px;
        font-weight: 200;
        color: var(--nv-ink-3);
        background: rgba(104,152,232,.14);
        border-radius: var(--r-pill);
        padding: 2px 10px;
        min-width: 26px;
        text-align: center;
    }

        .fld .fc.gold {
            color: var(--nv-gold-bright);
            background: rgba(232,167,92,.16);
        }

/* confirm sheet */
.confirm-sheet .ctext {
    font-size: 16px;
    color: var(--nv-ink-2);
    line-height: 1.5;
    margin-bottom: 18px;
}

.confirm-sheet .cact {
    display: flex;
    gap: 12px;
}

    .confirm-sheet .cact .btn {
        height: 54px;
        font-size: 17px;
    }

        .confirm-sheet .cact .btn.danger {
            background: var(--nv-danger);
            color: black;
            font-weight: 400;
            border: 0;
            min-width: 140px;
        }

/* ── toast ─────────────────────────────────────────────────── */
.mail-toast {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 92px;
    margin: 0 auto;
    max-width: 440px;
    z-index: 13;
    padding: 0 20px;
    pointer-events: none;
}

    .mail-toast .t {
        display: flex;
        align-items: center;
        gap: 10px;
        background: rgba(20,32,58,.94);
        border: 1px solid rgba(104,152,232,.32);
        border-radius: 14px;
        padding: 13px 16px;
        color: var(--nv-ink);
        font-size: 15px;
        box-shadow: 0 12px 34px rgba(0,0,0,.5);
        backdrop-filter: blur(18px);
        -webkit-backdrop-filter: blur(18px);
        animation: mailToastIn .28s ease, mailToastOut .4s ease 3.6s forwards;
    }

        .mail-toast .t svg {
            flex: 0 0 auto;
            color: var(--nv-gold);
        }

/*  ⚠⚠ v31.496 (owner) — A TOAST THAT WAITS. *"The tooltip messages in this
    area should have a close button rather that close automatically to give
    users variable time to check the information."* A confirmation can fade; a
    refusal carrying an invoice number and four figures cannot.
    ⚠ A MODIFIER, so `.mail-toast` itself is untouched and the mailbox keeps
    its 3.6-second fade — the same scoping rule the Bank Rec toolbar follows.
    The base toast is `pointer-events: none` (it must not swallow a click on
    whatever is under it), which a toast with a button has to give up — so the
    PANEL stays transparent to the mouse and only the card takes clicks. */
.mail-toast.nv-stay { pointer-events: none; }
.mail-toast.nv-stay .t {
    pointer-events: auto;
    animation: mailToastIn .28s ease;
    padding-right: 8px;
}
.mail-toast .t-x {
    flex: 0 0 auto;
    margin-left: auto;
    width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--nv-ink-2, #9fb0cd);
    cursor: pointer;
}
.mail-toast .t-x:hover { color: var(--nv-gold-bright); background: rgba(104,152,232,.14); }
.mail-toast .t-x svg { color: inherit; }

@keyframes mailToastIn {
    from {
        opacity: 0;
        transform: translateY(14px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes mailToastOut {
    to {
        opacity: 0;
        transform: translateY(14px);
    }
}

/* ── compose: From selector ────────────────────────────────── */
/*  ⚠⚠⚠ v31.53 (batch250, owner) - THE CHEVRON GETS THE HAIRLINE TOO.
    Owner: *"The down chevron EVERYWHERE should also implement the hairline
    divider everywhere."*

    ⚠ NO MARKUP CHANGES FOR THIS - and that is the whole reason it is safe to
    do in one go. The chevron is already a background layer, so the divider is
    a SECOND background layer on the same rule: one declaration, every
    `select.input` in the app, nothing to miss and nothing to convert.

    ⚠⚠ THE GEOMETRY IS THE SAME AS `.fld-go`, DELIBERATELY. There the button is
    32px wide and inset 2px, so the divider lands 34px from the right edge and
    the value stops at 46px. A select's chevron is 20px wide at `right 14px`,
    so its box also ends 34px from the right edge - the divider abuts it in
    exactly the same place, and `padding-right` moves 42 → 46 to match. Put a
    text field and a select side by side and the two hairlines line up.
    ⚠ 22px tall: the field is 32px and the divider is inset 5px top and bottom,
    the same inset `.fld-btn::before` uses. */
select.input {
    -webkit-appearance: none;
    appearance: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23e8a75c' stroke-width='1.6' stroke-linecap='round'><path d='m7 10 5 5 5-5'/></svg>"),
                      linear-gradient(rgba(112,160,238,.30), rgba(112,160,238,.30));
    background-repeat: no-repeat, no-repeat;
    /*  ⚠ v31.54 (batch251, owner): *"To center chevron visually, please change
        ... background-position: right 7px center, right 34px center."*
        AND THE ARITHMETIC AGREES WITH HIS EYE, which is worth writing down
        because the old number looked deliberate: the divider sits 34px from
        the right border, so the button zone IS that 34px. Centring a 20px
        chevron in it puts its centre at 17px and therefore its right edge at
        7px. The old `right 14px` had been chosen to make the chevron's box
        END exactly where the divider begins - tidy on paper, 7px too far left
        on screen. */
    background-position: right 7px center, right 34px center;
    background-size: auto, 1px 22px;
    padding-right: 46px;
}

/* Email list header: back-to-dashboard icon sits in front of the title. */
.screen-head.mail-head {
    display: flex;
    align-items: center;
    gap: 12px;
}

    .screen-head.mail-head .htext {
        flex: 1;
        min-width: 0;
    }

/*  ══ v31.552 (owner) — THE EMAIL HEAD AT 01-SCREEN ═══════════════════════════
    *"remove label shown in the image for only 01-Screen (mobileonly)"* and
    *"Never show entity switch for 01-Screen only"* — he confirmed the second
    is the EMAIL page, not all twenty-two heads that carry the toolkit chip.

    ⚠⚠⚠ MEASURED FIRST, AND THE TITLE WAS NOT CRAMPED — IT WAS GONE. At 390 on
    /email the head's five items ask for back 48 + ⋯ 48 + ☰ 48 + chip 156 +
    four 12px gaps + 40px of page padding = 388 of a 390 viewport, so `.htext`
    — the one item with `flex: 1; min-width: 0` — was handed **2px**. Not
    truncated, not wrapped: two pixels, with `Inbox.` inside it. The rule
    directly above is what makes it the item that gives, and that is right
    everywhere else; the fault is that the row was over-subscribed.

    ⚠⚠ AND THE `✦ AI updated` SPAN CANNOT SHRINK AT ALL — it carries
    `white-space: nowrap`, so on a day when it renders (Inbox only, and only
    once the AI has run) it takes ~150px off the title before the title gets a
    say. That is why his capture shows the head WRAPPED with a crushed `47`
    where the name should be: the chip had been pushed to a second row and the
    title still had nothing.

    ⚠ THE COUNT LINE GOES WITH IT, on his word. It frees no width — it sits
    INSIDE `.htext` — but with the title finally on screen a second line of
    `8 messages · 4 unread` under it is the clutter he is clearing. The counts
    are not lost: the folder rows in the ⋯ menu carry them, which is what
    v31.550 built.

    ⚠ `.mail-app` SCOPES ALL THREE. `.mail-head` is NOT the mail page's head —
    Notifications, Periods, Accounts, JournalEntry, BankImport, ArInvoices and
    others all wear it, so an unscoped rule here would silently take the entity
    switch off a dozen screens he has not looked at. `.mail-app` is set by the
    three Email pages only, and of those only the list uses `.mail-head`.      */
@media (max-width: 767.98px) {
    .mail-app .mail-head .htext .sub { display: none; }
    .mail-app .mail-head .eml-aiupd { display: none; }
    /*  the chip stays rendered and the sheet stays hoisted — only the opener
        is off, so nothing downstream has to learn a second arrangement. */
    .mail-app .mail-head > .nv-ent { display: none; }
}

/* Flow-page header (Message / Compose): static at the top with back nav. */
.flowhead {
    position: sticky;
    top: 0;
    z-index: 20;
    /* owner (batch82): background removed — the page gradient runs through
    background: linear-gradient(180deg, rgba(8,12,24,.94), rgba(8,12,24,.80)); */
    backdrop-filter: blur(18px) saturate(140%);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
}

/* Calendar: legend (dynamic, left) + "add new" button (right) share one row
   below the grid — v7.2 (owner); a subtle line separates it from the grid. */
.cal-addrow {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 10px 20px 0;
    padding: 10px 0 2px;
    /* v8.0 (owner) — separator moved up to .month */
    position: relative;
    z-index: 2;
}

    .cal-addrow .legend-cal {
        flex: 1;
        min-width: 0;
        padding: 0;
        gap: 10px;
    }

    .cal-addrow .cal-add {
        flex: 0 0 auto;
    }

.cal-add {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--nv-gold);
    color: #241706;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 8px 22px rgba(232,167,92,.28);
    text-decoration: none;
}

/* ── v6.9 batch (owner) ─────────────────────────────────────── */

/* AI disclosure rows (compose + event form): one row collapsed, expands
   to the controls. Native <details>, no JS. */
.airow {
    border: 1px solid rgba(104,152,232,.22);
    border-radius: var(--r-card);
    background: linear-gradient(160deg,rgba(120,165,240,.09),rgba(255,255,255,.015) 55%),rgba(13,22,42,.30);
}

    /*  ⚠⚠ v31.555 — `.cwa-fold` JOINS EVERY ONE OF THESE SELECTOR LISTS
        RATHER THAN COPYING THEIR DECLARATIONS. The email's Compose-with-AI
        card became a `<details>` on the owner's word (*"collapsed by default …
        a fairly obvious icon to expand"*), and the Calendar's AI row already
        had the summary this house draws: no marker, a chevron pushed right,
        turned 180 degrees when open. Two copies of that is two things to keep
        in step. Only the card's own padding differs, below.             */
    .airow > summary,
    .cwa-fold > summary {
        list-style: none;
        cursor: pointer;
        display: flex;
        align-items: center;
        gap: 10px;
        min-height: 36px; /* owner (batch83): dropdown rows 52 → 36 */
        padding: 0 16px;
        color: var(--nv-ink-2);
        font-size: 16px;
    }

        .airow > summary::-webkit-details-marker,
        .cwa-fold > summary::-webkit-details-marker {
            display: none;
        }

        .airow > summary .chev,
        .cwa-fold > summary .chev {
            margin-left: auto;
            transition: transform .18s;
        }

    .airow[open] > summary .chev,
    .cwa-fold[open] > summary .chev {
        transform: rotate(180deg);
    }

    /*  ⚠ THE CARD ALREADY HAS PADDING, so its summary must not add the
        `.airow` inset on top of it — that is the one declaration the two
        disclosures cannot share. The summary carries the card's own heading
        size so the row still reads as the card's title rather than as a
        control bolted above it.
        ⚠⚠ AND IT IS A REAL TARGET AT 01-SCREEN: 44px of height is the
        smallest thing a finger should be asked to find, which is the half of
        his sentence about small screens. */
    .cwa-fold > summary {
        padding: 0;
        min-height: 44px;
        color: var(--nv-ink-1, #e8edf7);
        font-size: 19px;
        font-weight: 300;
    }

    .cwa-fold:not([open]) > summary { min-height: 40px; }

    .airow .airow-body {
        display: flex;
        flex-direction: column;
        gap: 10px;
        padding: 4px 16px 14px;
    }

    /*  ══ v31.246 (owner, P-25) — THE TWO FULL-WIDTH ROWS GET AIR ABOVE THEM ══
        Owner: *"add spacing above: Periods and Currency and rate … The current
        horizontal border/top edge of those controls is visually too close to
        the fields immediately above it, which makes the rows appear joined to
        the preceding controls."*

        ⚠⚠ IT IS SPACING ON THE ROW, NOT ON WHATEVER PRECEDES IT. The panel is a
        flex column whose `gap` is the same between every child, so the two
        disclosures read as another field in the stack rather than as a band
        across it. Giving the ROW the margin means the rule travels with it —
        Periods and Currency get the same air on the invoice wizard's step 4,
        the standalone payment, the receipt and the posted view, and a surface
        that reorders its fields cannot lose it.
        ⚠ SCOPED TO THE PANEL'S OWN ROWS. `.airow` is used elsewhere in the app
        (the AI cards), and those are not what he is looking at.
        ⚠⚠⚠ AND IT APPLIES ONLY WHERE THE ROW IS ACTUALLY SHOWN — his own point:
        *"where the transaction is base currency … the Currency and rate row
        should ultimately be hidden altogether under P-14. The spacing
        adjustment therefore applies when that row is actually displayed."* A
        margin on a `display:none` element contributes nothing, so P-14's rule
        and this one need no knowledge of each other. */
    /*  ⚠ `[data-nvpp-base]`, NOT `[data-nvpp]`. The mount flag is rendered only
        when the panel is INTERACTIVE (`Model.Mount`), so keying off it would
        have given the entry form the spacing and left the posted view without
        it — the same two rows, differently spaced, one screen apart.
        `data-nvpp-base` is on the panel's root unconditionally. */
    [data-nvpp-base] > .airow {
        margin-top: 6px;
    }

    /*  ⚠⚠⚠ v31.244 (owner, P-14) — AND A CLOSED `<details>` ACTUALLY CLOSES.
        MEASURED before this rule, on a payment with the row collapsed:

            #perRow  open=false  body height 72px
            #curRow  open=false  body height 76px

        The rule above sets `display: flex` on the body, and an author
        declaration beats the hiding a closed `<details>` does for its
        non-summary children. So the Periods row this app has called *"the
        PERIOD row stays minimized"* since v31.220 has never actually been
        minimized — the chevron turned and nothing moved.
        > ⚠ **A DISCLOSURE THAT DOES NOT DISCLOSE IS A HEADING.** P-14 is a
        > *hide this row* requirement, so it could not be built on one.
        ⚠ IT IS SCOPED TO `.airow`, so no other `<details>` in the app changes.
        ⚠⚠ AND IT IS `display: none`, not a height animation: the row holds form
        controls, and a collapsed box that still lays them out is a set of
        fields a reader can tab into but cannot see. */
    .airow:not([open]) > .airow-body {
        display: none;
    }

/* v8.1 (owner) — collapsed recipients on the Message page (matches the
   composer's Cc/Bcc reveal): a quiet chevron row inside the sender card */
.rcpt > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 2px 0;
}

    .rcpt > summary::-webkit-details-marker {
        display: none;
    }

    .rcpt > summary .chev {
        transition: transform .18s;
        color: var(--nv-gold);
    }

.rcpt[open] > summary .chev {
    transform: rotate(180deg);
}

/* v8.3 (owner) — the global error POPUP (v1 EShowMessagePopUp): a centred
   card over a scrim, same visual family as the entity dialog */
.err-scrim {
    position: fixed;
    inset: 0;
    z-index: 40;
    background: rgba(3,6,14,.62);
    backdrop-filter: blur(2px);
}

.err-pop {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 41;
    width: min(400px, calc(100vw - 40px));
    display: flex;
    flex-direction: column;
    gap: 14px;
    border-radius: 8px;
    border: 1px solid rgba(104,152,232,.30);
    padding: 26px 22px;
    /*  ══════════════════════════════════════════════════════════════════════
        ⚠⚠⚠ v31.286 (owner): *"The same background as the tooltip background."*

        THE DECLARATION WAS ALREADY THE TOOLTIP'S, AND THAT IS THE POINT. Both
        surfaces carried `linear-gradient(145deg, #010107, #1E1E1E 50%,
        #13294B)`, and sampled side by side at their real sizes they come back
        identical to within one unit of blue — measured, not assumed.

        ⚠⚠ WHAT DIFFERS IS HOW MUCH OF THE RAMP EACH BOX SHOWS. A gradient's
        stops are percentages of a line that scales with the element, so a
        tooltip — small, and read in a glance — is dominated by the near-black
        start, while a 400×250 dialog spreads the same ramp across the whole
        message and puts the grey middle band directly behind the text. Same
        colours; a different part of them.

        ⚠ SO THE STOPS ARE SCALED, NOT RECOLOURED. The ramp is the house ramp
        unchanged; pushing the mid to 82% and the end past 100% means a
        dialog-sized box reveals the same PART of it a tooltip-sized box does —
        near-black through to a restrained navy corner, never a grey slab.
        Measured against the tooltip: centre 19,19,21 against its 30,30,30 read
        at a size nobody sees the tooltip at.
        ══════════════════════════════════════════════════════════════════════ */
    background: linear-gradient(145deg, #010107 0%, #1E1E1E 82%, #13294B 175%);
    box-shadow: 0 18px 48px rgba(0,0,0,.55);
    font-family: 'Poppins', -apple-system, 'Segoe UI', sans-serif !important;
}

    /*  ⚠ v31.286 (owner): the headline at 300. It was 200 — the same weight as
        the body copy below it, which left the head reading as another paragraph
        rather than as a title. */
    .err-pop h2 {
        font-size: var(--fs-h2);
        font-weight: 300;
        color: var(--nv-gold);
    }

    .err-pop p {
        font-size: 16px;
        font-weight: 200;
        color: var(--nv-ink);
        line-height: 1.5;
    }

    .err-pop button,
    .err-pop a {
        font-family: 'Poppins', -apple-system, 'Segoe UI', sans-serif !important;
    }

/* the assist checkboxes reuse the gold .eml-sel tick */
.aichk {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 40px;
    font-size: 16px;
    color: var(--nv-ink-2);
    cursor: pointer;
}

/* received email renders inside a sandboxed iframe (Message page) */
.email-frame {
    display: block;
    width: 100%;
    height: 360px; /* replaced by the fit script once loaded */
    border: 0;
    border-radius: 4px;
    background: white;
}

/* team-calendar switch row, below the calendar header */
.cal-team {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 20px 8px;
    position: relative;
    z-index: 2;
}

/* event form: date + time share the row without overflowing the page */
.ev-dt {
    display: flex;
    gap: 10px;
}

    .ev-dt .field {
        min-width: 0;
    }

        .ev-dt .field input.input {
            min-width: 0;
            width: 100%;
            padding: 0 4px; /* v25.6 (owner) */
        }

/* v7.2 (owner) — the compose attachments picker, branded: gold pill
   "choose files" button, muted file summary text on the glass input */
input.input[type="file"] {
    height: auto;
    padding: 10px 14px;
    color: var(--nv-ink-3);
    font-size: 15px;
    cursor: pointer;
}

    input.input[type="file"]::file-selector-button {
        font-family: inherit;
        font-size: 15px;
        font-weight: 200;
        color: #241706;
        background: var(--nv-gold);
        border: 0;
        border-radius: 999px;
        /* ⚠ v29.6 (owner: "the Choose File pill height should be reduced to fit
           the height of the input. Do not change input height of 51px, just
           reduce yellow pill dimensions") - THE INPUT IS UNTOUCHED: it keeps
           its height and its 10px padding. Only the browser's own button
           shrinks, to 30px, which is what the input's content box actually
           leaves once that padding is taken. */
        height: 30px;
        padding: 0 14px;
        margin-right: 10px;
        cursor: pointer;
    }

    input.input[type="file"]::-webkit-file-upload-button {
        font-family: inherit;
        font-size: 15px;
        font-weight: 200;
        color: #241706;
        background: var(--nv-gold);
        border: 0;
        border-radius: 999px;
        /* ⚠ v29.6 (owner: "the Choose File pill height should be reduced to fit
           the height of the input. Do not change input height of 51px, just
           reduce yellow pill dimensions") - THE INPUT IS UNTOUCHED: it keeps
           its height and its 10px padding. Only the browser's own button
           shrinks, to 30px, which is what the input's content box actually
           leaves once that padding is taken. */
        height: 30px;
        padding: 0 14px;
        margin-right: 10px;
        cursor: pointer;
    }

/* v7.3 — entity switcher sheet: the .fld rows sit inside per-entity forms,
   so the sibling border comes from the forms */
.mail-sheet form + form .fld {
    border-top: 1px solid rgba(104,152,232,.13);
}

/* v7.6 (owner) — GROUPED entity sheet as a floating CENTRED dialog:
   catalog → group → parent/child, accordion (one node open at a time).
   This presentation is the template for the invoice-processing picker.

   NOTE the base .mail-sheet hides itself with translateY(102%) — that
   cannot clear the screen once bottom is raised to 120px (the top
   ~120px would keep peeking up), which is why the owner's first cut
   "persisted in view". This variant therefore hides with opacity +
   visibility instead, and centres with left:50% / translateX(-50%). */
.ent-sheet {
    max-height: 70vh;
    overflow-y: auto;
    bottom: 120px;
    left: 50%;
    right: auto;
    /* v9.1 (owner) — above the sticky page headers (z-index 20) */
    z-index: 40;
    width: min(440px, calc(100vw - 40px));
    margin: 0;
    border-radius: 8px;
    border: 1px solid rgba(104,152,232,.30);
    padding: 0 20px 24px;
    background: linear-gradient(145deg, #010107 0%, #1E1E1E 50%, #13294B 100%);
    font-family: 'Poppins', -apple-system, 'Segoe UI', sans-serif !important;
    transform: translate(-50%, 14px);
    opacity: 0;
    visibility: hidden;
    transition: opacity .22s, transform .26s cubic-bezier(.22,.61,.36,1), visibility 0s .26s;
}

    .ent-sheet.open {
        transform: translate(-50%, 0);
        opacity: .8;
        visibility: visible;
        transition: opacity .22s, transform .26s cubic-bezier(.22,.61,.36,1), visibility 0s;
    }

    /* buttons do not inherit font by default — force the brand face */
    .ent-sheet button {
        font-family: 'Poppins', -apple-system, 'Segoe UI', sans-serif !important;
    }

/* accordion nodes: only .exp shows its body */
.ent-node > .ent-body {
    display: none;
}

.ent-node.exp > .ent-body {
    display: block;
}

/* v7.7 (owner) — companies sit indented under their gold group header */
.ent-node .ent-node > .ent-body {
    padding-left: 18px;
}

/* v7.7 (owner) — company names never wrap to a second line */
.ent-sheet .fld .fl {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* v7.9 (owner) — tighter rows in the entity list only */
.ent-sheet .fld {
    padding: 9px 9px;
}

/* v7.9 (owner) — the gold X that closes the dialog without a selection */
/* v9.1 (owner) — the scrim must cover the sticky headers too */
.mail-scrim.ent-scrim {
    z-index: 39;
}

/* v9.1 (owner) — the title + close X stay STICKY while the tree scrolls;
   the header is slimmer than the old 50px sheet padding */
.ent-head {
    position: sticky;
    top: 0;
    z-index: 3;
    margin: 0 -20px 6px;
    padding: 12px 46px 9px 20px;
    background: linear-gradient(180deg, rgba(4,6,12,.96), rgba(9,13,24,.88));
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid rgba(104,152,232,.16);
}

    .ent-head h2 {
        margin-bottom: 0;
    }

    .ent-head .ent-close {
        top: 50%;
        right: 8px;
        transform: translateY(-50%);
    }

/*  ══════════════════════════════════════════════════════════════════════════
    v9.1 (owner) - the sheet ANCHORS 10px below the org chip (JS positions it);
    the old bottom-anchored slide becomes a plain fade.

    ⚠⚠⚠ v31.89 (batch284, owner) - `margin: 0` IS LOAD-BEARING, AND ITS ABSENCE
    WAS THE DEFECT. Owner: *"for 'mail-sheet ent-sheet ent-anchored open' and
    ONLY for screen sizes 05-Screen, the anchor is 'off' i.e too far to the
    right and too low from the top."*

    ⚠⚠ THE CAUSE IS MINE, FROM v31.83. That batch answered *"the accMoreSheet
    top disappears in 05-Screen ONLY"* with a rule on **`.mail-sheet`** -
    `@media (min-width: 1920px) { .mail-sheet { margin: 340px auto !important } }`
    - and `.ent-sheet` carries `mail-sheet` too. The JS sets `top` to the
    chip's bottom + 10; **the margin is then ADDED to it.** The entity sheet has
    been landing 340px below where it computed itself ever since.

    > ⚠⚠⚠ **A RULE WRITTEN FOR ONE SHEET LANDS ON EVERY SHEET THAT SHARES ITS
    > CLASS.** `.mail-sheet` is a base class with six users; scoping the fix to
    > the ONE sheet that asked for it was the job, and "only in 05-Screen" was
    > read as a BREAKPOINT when it was also a SELECTOR.

    MEASURED, before, gap from the chip's bottom (should be 10 at every band):

        390    margin 134px   ->  gap 144
        768    margin 134px   ->  gap 144
        1024   margin  20px   ->  gap  30
        1366   margin  40px   ->  gap  50
        1920   margin 340px   ->  gap 350   <- the one that got reported
        2560   margin 340px   ->  gap 350

    So it was wrong at EVERY band and only visibly wrong at the widest, which
    is exactly why it took this long to surface.

    ⚠ `!important` IS REQUIRED HERE and it is not the usual ordering laziness:
    the four `.mail-sheet` margin rules are themselves `!important`, and only
    another `!important` can beat one. **Specificity then decides between
    them** - `.ent-sheet.ent-anchored` is (0,2,0) against `.mail-sheet`'s
    (0,1,0), so this wins wherever it sits in the file and cannot be lost to a
    later edit of those media blocks.
    ⚠ THE OTHER FIVE `.mail-sheet` USERS ARE UNTOUCHED - this names
    `.ent-anchored`, which only the entity sheet carries.
    ══════════════════════════════════════════════════════════════════════════ */
.ent-sheet.ent-anchored {
    bottom: auto;
    transform: none;
    margin: 0 !important;
}

    .ent-sheet.ent-anchored.open {
        transform: none;
    }

.ent-close {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 0;
    cursor: pointer;
    color: var(--nv-gold);
}

/* level headers are buttons now */
.ent-db,
.ent-grp {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    background: transparent;
    border: 0;
    text-align: left;
    cursor: pointer;
    font-weight: 200;
}

    .ent-db::after,
    .ent-grp::after {
        content: "";
        width: 8px;
        height: 8px;
        margin-left: 10px;
        border-right: 1.5px solid currentColor;
        border-bottom: 1.5px solid currentColor;
        transform: rotate(45deg);
        transition: transform .2s;
        flex: 0 0 auto;
    }

.ent-node.exp > .ent-db::after,
.ent-node.exp > .ent-grp::after {
    transform: rotate(225deg);
}

.ent-db {
    margin-top: 14px;
    padding: 8px 0;
    font-size: 15px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--nv-ink-3);
    border-bottom: 1px solid rgba(104,152,232,.18);
}

.ent-grp {
    margin-top: 8px;
    padding: 6px 0;
    font-size: 15px;
    color: var(--nv-gold);
}

.fld.ent-child {
    padding-left: 40px;
}

/* v7.2 — attachments: the arrow at the row's end (2+ files) + the revealed
   list of the selected files */
.file-wrap {
    position: relative;
}

    .file-wrap input.input[type="file"] {
        padding-right: 56px;
    }

.file-toggle {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--nv-gold);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

    .file-toggle .chev {
        transition: transform .18s;
    }

    .file-toggle.open .chev {
        transform: rotate(180deg);
    }

.file-list {
    list-style: none;
    margin: 0;
    padding: 4px 14px;
    border: 1px solid rgba(104,152,232,.22);
    border-radius: var(--r-card);
    background: linear-gradient(160deg,rgba(120,165,240,.09),rgba(255,255,255,.015) 55%),rgba(13,22,42,.30);
}

    .file-list li {
        display: flex;
        align-items: baseline;
        gap: 12px;
        padding: 9px 0;
        font-size: 15px;
    }

/*  ⚠ v31.244 (owner, P-17) — the per-file remove. Quiet until the row is
    pointed at: a red × on every attachment reads as an error state, and these
    rows are a normal, successful thing. It is `.nvtr-go`'s shape and restraint
    rather than a new kind of button. */
.file-list li > .file-x {
    margin-left: auto;
    flex: 0 0 auto;
    border: 0;
    background: none;
    padding: 0 2px;
    color: var(--nv-muted, #9fb0cc);
    cursor: pointer;
    line-height: 0;
    opacity: .55;
    align-self: center;
}

    .file-list li > .file-x:hover { opacity: 1; color: var(--nv-gold); }

    .file-list li > .file-x:focus-visible {
        outline: 2px solid var(--nv-blue-bright);
        outline-offset: 2px;
        border-radius: 3px;
        opacity: 1;
    }

/*  the `fs` size column no longer takes the slack — the × does. */
.file-list li > .fs { margin-left: auto; }
.file-list li > .file-x ~ .fs { margin-left: 0; }


        .file-list li + li {
            border-top: 1px solid rgba(104,152,232,.13);
        }

    .file-list .fn {
        flex: 1;
        min-width: 0;
        color: var(--nv-ink-2);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .file-list .fs {
        flex: 0 0 auto;
        color: var(--nv-ink-3);
        font-variant-numeric: tabular-nums;
    }

/* v7.0 — address search inside the To/Cc inputs (compose) */
.addr-wrap {
    position: relative;
}

    .addr-wrap input.input {
        padding-right: 56px;
    }

.addr-search {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--nv-gold);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.addr-results {
    margin-top: 8px;
    display: flex;
    flex-direction: column;
    max-height: 280px;
    overflow-y: auto;
}

.addr-hit {
    text-align: left;
    background: transparent;
    border: 0;
    padding: 12px 6px;
    color: var(--nv-ink);
    font-size: 16px;
    font-family: inherit;
    font-weight: 200;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

    .addr-hit + .addr-hit {
        border-top: 1px solid rgba(104,152,232,.13);
    }

    .addr-hit .em {
        color: var(--nv-ink-3);
        font-size: 15px;
    }

/* v7.7 (owner) — muted scrollbars: the gold scrollbar-color is retired so
   the thumb stays a quiet blue-grey instead. (The webkit thumb is muted to
   match — Chrome/Edge would otherwise still paint the old gold rules.) */
* {
    scrollbar-width: thin;
    /* v9.1 (owner) — browsers that honour the STANDARD scrollbar properties
       (Chromium 121+ ignores the ::-webkit rules once scrollbar-width is
       set — the 1366 viewport bar looked native) get the same muted pair */
    scrollbar-color: rgba(104,152,232,.28) rgba(16,26,48,.55);
}

    *::-webkit-scrollbar {
        width: 9px;
        height: 9px;
    }

    *::-webkit-scrollbar-track {
        background: rgba(16,26,48,.55);
        border-radius: 8px;
    }

    *::-webkit-scrollbar-thumb {
        background: rgba(104,152,232,.28);
        border-radius: 8px;
        border: 2px solid rgba(16,26,48,.55);
    }

        *::-webkit-scrollbar-thumb:hover {
            background: rgba(104,152,232,.5);
        }

/* ── the NOVERA checkbox (owner, Wave 2.2) — gold border, transparent fill,
   gold tick; replaces the browser accent checkbox wherever forms need it ── */
input.nv-check {
    appearance: none;
    -webkit-appearance: none;
    width: 24px;
    height: 24px;
    border: 1px solid var(--nv-gold);
    border-radius: 6px;
    background: transparent;
    cursor: pointer;
    display: inline-grid;
    place-content: center;
    margin: 0;
    flex: 0 0 auto;
}

    input.nv-check::after {
        content: "";
        width: 12px;
        height: 12px;
        transform: scale(0);
        transition: transform .12s;
        clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
        background: var(--nv-gold);
    }

    input.nv-check:checked::after {
        transform: scale(1);
    }

/* ── AI Briefing dashboard (v10.0, owner) — stat tiles + two-column zone ── */
.ai-tiles {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
}

.ai-tile {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 16px;
}

    .ai-tile .n {
        font-size: 30px;
        font-weight: 200;
        line-height: 1.1;
        color: var(--nv-ink);
    }

        .ai-tile .n.gold {
            color: var(--nv-gold-bright);
        }

    .ai-tile .l {
        font-size: 15px;
        color: var(--nv-ink-3);
    }

/* the two briefing columns stack on phones; responsive.css opens them up */
.ai-cols {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.ai-col {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-width: 0;
}

/* ── AI inbox (v10.4, owner — mock-up 2) ─────────────────────────────── */
.ai-chips {
    display: flex;
    align-items: center;
    gap: 8px;
    overflow-x: auto;
    padding: 0 2px 2px;
    scrollbar-width: none;
}

    .ai-chips::-webkit-scrollbar {
        display: none;
    }

    .ai-chips .chip {
        text-decoration: none;
        white-space: nowrap;
        flex: 0 0 auto;
        width: auto;
    }

        .ai-chips .chip .n {
            color: var(--nv-gold-bright);
            font-variant-numeric: tabular-nums;
        }

/* section header inside the AI-priority grouped list */
.mail-sec {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 4px 4px;
    font-size: 14px;
    font-weight: 200;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--nv-ink-3);
}

    .mail-sec i {
        width: 8px;
        height: 8px;
        border-radius: 50%;
        flex: 0 0 auto;
    }

/* v10.8 r4 (owner) — the AI assistant column shows at EVERY width now:
   stacked below the mail cards on phones/tablets (bottom-nav clearance),
   responsive.css re-places it from 1024 up */
.ai-side {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin: 8px 20px 120px;
}

/* v10.8 r4 (owner) — phones: only the first two chips (v12.7: Newest +
   AI priority — Newest is finally reachable on phones); the rest return
   from tablet up */
@media (max-width: 767px) {
    /* v20.8 (batch117, owner: "We have regressed with the top cards in
       email") — the phone's card COUNT moved out of here. Every width's
       count now lives in ONE ladder in responsive.css (which loads last),
       because three files each hiding a different nth-child is exactly how
       this regressed: the rules named POSITIONS (2, 5, 6) chosen for the
       old six-card order, and the batch113 reorder to seven cards left
       them pointing at the wrong cards. */
    /* v10.8 r5 (owner) — the two chips span the page, equal width, aligned
       with the search box; the Sort by row hides on phones */
    .ai-chips {
        padding: 0 0 2px;
    }

        .ai-chips .chip {
            flex: 1 1 0;
            min-width: 0;
        }
}

.ai-glance {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
}

    .ai-glance .gl {
        border: 1px solid rgba(104,152,232,.22);
        border-radius: var(--r-card);
        padding: 10px 12px;
    }

    .ai-glance .n {
        font-size: 24px;
        font-weight: 200;
        line-height: 1.15;
        color: var(--nv-ink);
    }

        .ai-glance .n.gold {
            color: var(--nv-gold-bright);
        }

    .ai-glance .l {
        font-size: 14px;
        color: var(--nv-ink-3);
    }
/* ── mock layout pass (v10.5–v10.6, owner — structure only, existing palette) ── */
/* v10.6 (owner) — taller chips, two rows: row 1 = icon | count (two
   columns), row 2 = the label full-width */
.ai-chips .chip {
    height: 70px;
    min-width: 120px; /* owner (2026-08-12 v10) */
    display: inline-flex;
    flex-direction: column;
    align-items: center; /* v10.6 r4 owner — content centred */
    justify-content: center;
    gap: 3px;
    padding: 0 14px;
}

    .ai-chips .chip .r1 {
        display: flex;
        align-items: center;
        gap: 8px;
    }

        .ai-chips .chip .r1 .n {
            font-size: 17px;
        }

    .ai-chips .chip .lb {
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        text-align: center; /* v10.6 r4 owner */
    }
/* v10.6 r4 (owner) — "Your AI routine": plain 36px icons, no tint, no
   background (the intro card's tinted sparkle keeps its tint class) */
.ai-side .row .ic {
    background: transparent;
}
/* v10.8 (batch54, Mock B locked) — the FOLDERS PANEL replaces the mail-bar
   from 1024px up; below 1024 the floating bar returns as the phone's mail
   navigation. The bar's sheets (folders / confirm delete) and toast keep
   working everywhere — the panel delegates to them. */
@media (min-width: 1024px) {
    .mail-app .app-main > .mail-bar {
        display: none;
    }
}
/* ── MAIL FOLDERS panel (v10.8) — hidden below 1024 (responsive.css shows
      and places it) ─────────────────────────────────────────────────── */
.mail-folders {
    display: none;
}

    .mail-folders h1 {
        /* v10.8 r3 (owner) — semantic h1 */
        font-size: 22px;
        font-weight: 400; /*(owner)*/
        color: var(--nv-ink);
        margin: 2px 4px 12px;
    }

    .mail-folders .mf-compose {
        height: 48px;
        /*font-size: 20px;*/ /* v10.8 r3 owner */
        display: flex;
        align-items: center;
        justify-content: center;
        text-decoration: none;
        margin-bottom: 12px;
    }

    .mail-folders .mf {
        display: flex;
        align-items: center;
        gap: 11px;
        padding: 9px 12px;
        border-radius: 8px;
        color: var(--nv-ink-2);
        /*font-size: 20px;*/ /* v10.8 r3 owner */
        text-decoration: none;
        border: 0;
        background: none;
        font-family: inherit;
        cursor: pointer;
        text-align: left;
        width: 100%;
    }

        .mail-folders .mf.on {
            color: var(--nv-ink);
            background: rgba(232,167,92,.14);
        }

        .mail-folders .mf .fi {
            display: flex;
            flex: 0 0 auto;
            color: var(--nv-gold); /* toolkit: icons are gold */
        }

            .mail-folders .mf .fi svg {
                width: 32px; /* toolkit: functional icons 32px */
                height: 32px;
            }

        .mail-folders .mf .fl {
            flex: 1;
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .mail-folders .mf .fc {
            margin-left: auto;
            font-size: 18px; /* v10.8 r3 owner */
            color: var(--nv-gold-bright);
            font-variant-numeric: tabular-nums;
            flex: 0 0 auto;
        }

            .mail-folders .mf .fc.dim {
                color: var(--nv-ink-3);
            }

    .mail-folders .mf-del {
        margin-top: 12px;
        border-top: 1px solid rgba(104,152,232,.13);
        border-radius: 0 0 8px 8px;
        padding-top: 14px;
        color: var(--nv-danger);
    }

        .mail-folders .mf-del .fi {
            color: var(--nv-danger);
        }
/* small tinted icon circles inside the category chips */
.ai-chips .chip .ic {
    /* v10.8 r7 (owner) — plain gold 32px svgs: NO circle, border NONE,
       background NONE */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0 !important;
    background: none !important;
    color: var(--nv-gold) !important;
    flex: 0 0 auto;
}
/* v10.6 (owner) — the digest's per-category cards: flush rows like the list
   card, natural height (the middle column scrolls as one) */
.mail-grp {
    padding-top: 2px;
    padding-bottom: 8px;
}

    .mail-grp .mail-sec {
        padding-top: 10px;
    }
/* tint variants missing from the original set — same palette colours the
   inbox already uses (danger badges, muted ink) */
.tint-danger {
    background: rgba(217,101,101,.07) !important;
    border-color: rgba(217,101,101,.42) !important;
    color: var(--nv-danger) !important;
}

.tint-dim {
    background: rgba(18,30,56,.28) !important;
    border-color: rgba(104,152,232,.22) !important;
    color: var(--nv-ink-3) !important;
}
/* the one-line preview snippet under the subject (mock rows) */
.mail .prev {
    font-size: 14px;
    color: var(--nv-ink-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
/* toolbar extras (mock): "Sort by:" dropdown + the funnel button.
   Stacked with the rest of .mail-tools on mobile; on one row from 1024. */
.mt-extra {
    display: flex;
    align-items: center;
    gap: 10px;
}

    .mt-extra .mt-sort {
        display: flex;
        align-items: center;
        gap: 8px;
        margin: 0;
        flex: 1 1 auto;
    }

        .mt-extra .mt-sort .s {
            font-size: 14px;
            color: var(--nv-ink-3);
            white-space: nowrap;
        }

        .mt-extra .mt-sort select.input {
            height: 44px;
            width: auto;
            flex: 1 1 auto;
            padding: 0 12px;
        }

    .mt-extra .mt-filter {
        flex: 0 0 auto;
        width: 44px;
        height: 44px;
        border: 1px solid rgba(104,152,232,.22);
        border-radius: var(--r-input, 14px);
    }
/* "View all insights" / "See all deadlines" footer links on the side cards */
.lnk-more {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid rgba(104,152,232,.13);
    font-size: 14px;
    color: var(--nv-gold-bright);
    text-decoration: none;
}

    .lnk-more svg {
        margin-left: auto;
    }
    /* v10.6 r6 (owner) — the digest's "View all →": bottom RIGHT corner */
    .lnk-more.end {
        justify-content: flex-end;
        padding-right: 4px;
    }

        .lnk-more.end svg {
            margin-left: 0;
        }
/* v10.6 r6 (owner) — the row checkbox rides the DATE LINE (top), not the
   card's vertical centre */
.mail .pick {
    align-self: flex-start;
    margin-top: 0;
}
/* v10.6 r10 (owner) — glance icons ALL GOLD; numbers stay ink */
.ai-glance .n svg {
    color: var(--nv-gold);
}
/* glance tiles: the small icon rides the number line (mock);
   v10.6 (owner) — content centred */
.ai-glance .gl {
    text-align: center;
}

.ai-glance .n {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

    .ai-glance .n svg {
        flex: 0 0 auto;
    }
/* ── READ & REPLY page (v10.9, batch55 — locked Mock A) ─────────────── */
/* header pill buttons (AI summary) */
.hbtn {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    height: 48px;
    padding: 0 16px;
    border: 1px solid rgba(104,152,232,.22);
    border-radius: 8px;
    background: none;
    color: var(--nv-ink-2);
    font-family: inherit;
    font-size: 15px;
    cursor: pointer;
    white-space: nowrap;
}

    .hbtn.gold {
        border-color: var(--nv-gold);
        color: var(--nv-gold-bright);
    }
/* the ⋯ menu */
.msg-menuwrap {
    position: relative;
}

/* v27.2 (PRODUCTION TICKET 20260821002 item 5): the OVERDUE row's quick-move
   menu reuses the ⋯ menu, but a row is not a page header - it opens under a
   26px button, so the panel sits closer and narrower. */
.ov-menuwrap .msg-menu {
    top: 34px;
    min-width: 190px;
}

.ov-menubtn {
    width: 34px;
    height: 34px;
    opacity: .75;
}

    .ov-menubtn:hover {
        opacity: 1;
    }

.msg-menu {
    position: absolute;
    top: 54px;
    right: 0;
    min-width: 250px;
    border: 1px solid rgba(112,160,238,.30);
    border-radius: var(--r-card);
    background: var(--nv-surface);
    box-shadow: 0 16px 34px rgba(0,0,0,.55);
    z-index: 30;
    overflow: hidden;
    display: none;
    flex-direction: column;
}

    .msg-menu.open {
        display: flex;
    }

    .msg-menu form {
        margin: 0;
    }

    .msg-menu button {
        display: flex;
        align-items: center;
        gap: 12px;
        width: 100%;
        padding: 11px 16px;
        background: none;
        border: 0;
        color: var(--nv-ink-2);
        font-family: inherit;
        font-size: 15px;
        cursor: pointer;
        text-align: left;
    }

        .msg-menu button:hover {
            background: rgba(232,167,92,.08);
        }

        .msg-menu button svg {
            color: var(--nv-gold); /* toolkit */
            flex: 0 0 auto;
        }

        .msg-menu button.danger,
        .msg-menu button.danger svg {
            color: var(--nv-danger);
        }
/* Compose-with-AI panel */
.cwa-grid {
    display: grid;
    grid-template-columns: 230px minmax(0, 1fr);
    gap: 16px;
}

@media (max-width: 767px) {
    .cwa-grid {
        grid-template-columns: 1fr; /* stacked on phones */
    }
}

.cwa-int {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.int {
    display: flex;
    align-items: center;
    gap: 9px;
    border: 1px solid rgba(104,152,232,.22);
    border-radius: 8px;
    padding: 10px 12px;
    font-size: 15px;
    color: var(--nv-ink-2);
    cursor: pointer;
}

    .int input {
        position: absolute;
        opacity: 0;
        pointer-events: none;
    }

    .int i {
        width: 14px;
        height: 14px;
        border: 1.5px solid currentColor;
        border-radius: 50%;
        display: inline-block;
        flex: 0 0 auto;
    }

    .int:has(input:checked) {
        border-color: var(--nv-gold);
        color: var(--nv-ink);
        background: rgba(232,167,92,.08);
    }

        .int:has(input:checked) i {
            background: var(--nv-gold);
            border-color: var(--nv-gold);
        }
/* the suggested-reply box (same treatment as the composer's AI panel) */
.aibox {
    border: 1px solid rgba(232,167,92,.55);
    border-radius: 10px;
    background: rgba(232,167,92,.07);
    padding: 12px 14px;
    font-size: 15px;
    color: var(--nv-ink);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.cwa-draft {
    width: 100%;
    background: none;
    border: 0;
    color: var(--nv-ink);
    font-family: inherit;
    font-size: 15px;
    line-height: 1.5;
    resize: vertical;
    min-height: 180px;
    outline: none;
}
/* the uniform control grid: selects + equal-width buttons (toolkit rule) */
.ctl3 {
    display: grid;
    grid-template-columns: repeat(3, minmax(150px, 1fr));
    gap: 10px;
}

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

.ctl3 .lab {
    font-size: 12.5px;
    color: var(--nv-ink-3);
    margin-bottom: 4px;
}

select.ctl {
    width: 100%;
    height: 48px;
    background: var(--nv-surface-3);
    border: 1px solid rgba(112,160,238,.30);
    border-radius: var(--r-input);
    color: var(--nv-ink);
    font-family: inherit;
    font-size: 14px;
    padding: 0 12px;
}

.ctl3 .btn {
    align-self: end; /* v10.9 r2 (owner) — buttons sit level with the selects
                        (the labels above the selects otherwise push rows apart) */
    height: 48px;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    white-space: nowrap;
    padding: 0 12px;
    font-size: 15px;
}

    .ctl3 .btn[disabled] {
        opacity: .45;
        cursor: default;
    }
/* right-column quick actions (32px gold icons, ellipsed labels) */
.cwa-qa {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

    .cwa-qa a {
        display: flex;
        align-items: center;
        gap: 10px;
        border: 1px solid rgba(104,152,232,.22);
        border-radius: 8px;
        color: var(--nv-ink-2);
        font-size: 14px;
        padding: 10px 12px;
        text-decoration: none;
        min-height: 56px;
        min-width: 0;
        overflow: hidden;
    }

        .cwa-qa a svg {
            color: var(--nv-gold); /* toolkit */
            flex: 0 0 auto;
        }

    .cwa-qa .ql {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
/* ── COMPOSE rich-text editor (v11.0, batch56 — locked Mock B) ──────── */
.rt-toolbar {
    display: flex;
    gap: 4px;
    align-items: center;
    border: 1px solid rgba(112,160,238,.30);
    /* owner (batch107): border-bottom restored; the toolbar tucks into the
       surface below via the negative margin instead.
       owner (batch108): the padding line is gone entirely. */
    margin-bottom: -8px;
    border-radius: var(--r-input) var(--r-input) 0 0;
    background: linear-gradient(160deg,rgba(120,165,240,.07),rgba(255,255,255,.01));
    flex-wrap: wrap;
}

    .rt-toolbar button {
        min-width: 38px;
        height: 38px;
        border: 0;
        border-radius: 6px;
        background: none;
        color: var(--nv-ink-2);
        font-family: inherit;
        font-size: 16px;
        cursor: pointer;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

        .rt-toolbar button:hover {
            background: rgba(232,167,92,.10);
        }

/*  ⚠⚠ v31.147 (owner) — THE PENCIL'S CELL, CLOSED, ON A TALL EDITOR.
    Owner, on the invoice's Extended description: *"Top Border needed for top
    section of pencil cell."*

    ⚠ WHY IT ONLY LOOKS WRONG HERE. `.fld-btn::before` draws a 22px divider
    centred on a 26px button — inside a 32px input that reads as a full-height
    rule and the control looks like a cell of the field. Against a 100px+
    editor the same 22px is a fragment floating in a tall box: the editor's
    own border closes the right and the bottom, nothing closes the top, and
    the eye reads an unfinished cell. MEASURED: editor 100px, pencil 26px,
    divider 22px.

    So on this shape only, the divider runs the button's full height and a top
    border closes it. Scoped with `:has(> .rt-ed)` — the wrap of a rich editor
    — so all thirty-one ordinary fields keep the geometry v31.82 settled. */
.fld-wrap[data-nvfe]:has(> .rt-ed) > .nvfe-pen,
.fld-wrap[data-nvfe]:has(> .rt-ed) > .nvfe-act {
    border-top: 1px solid rgba(112, 160, 238, .30);
}

    .fld-wrap[data-nvfe]:has(> .rt-ed) > .nvfe-pen::before,
    .fld-wrap[data-nvfe]:has(> .rt-ed) > .nvfe-act .fld-btn:first-child::before {
        top: 0;
        height: 100%;
        transform: none;
    }

    /*  ⚠ the pair is two buttons; only the FIRST draws the cell's left edge,
        or the ✓ and the ✕ would be separated by a full-height rule the resting
        state never has. */
    .fld-wrap[data-nvfe]:has(> .rt-ed) > .nvfe-act .fld-btn:not(:first-child)::before {
        display: none;
    }

/*  ⚠⚠ v31.147 (owner) — THE TIGHT TOOLBAR, ONE CALL SITE ONLY.
    *"Make the following change ONLY to the Rich Editor used in Bank
    Reconciliation … Do not change the global `.rt-toolbar` styling, as the
    other Rich Editors should remain unaffected."*
    So it is a class the call site asks for (`ViewData["ReBarClass"]`), never a
    change to the base rule — and v31.146's global `padding: 0 6px` has been
    taken back OUT for the same reason: it did not fix the clipping the owner
    reported, and it was a global change to a shared control. */
.rt-toolbar.rt-tight {
    gap: 2px;
    padding: 0 4px;
}

/*  ⚠⚠ v31.500 (owner, messaging) — "expand tool bar for text entry to one row. Tool bar should
    always be one row for any view."

    `.rt-toolbar` wraps by default and must keep wrapping: the AP invoice description, Notes and the
    calendar entry all carry the full row of twelve controls and would run off the side of a narrow
    card without it. So this is a CLASS on the one call site that asked, the same shape as
    `rt-tight` for the bank rec and `ReDrop` for this very toolbar in v31.495 — the owner's own
    standing emphasis is that a messaging rule is a messaging rule.

    The buttons are narrowed rather than scrolled, because a row you have to drag is not a row you
    can see. `overflow-x` is the safety net for a width nobody has measured yet, and it carries no
    visible bar: a scrollbar inside a 38px strip reads as a rendering fault. */
.rt-toolbar.rt-oneline {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
}
.rt-toolbar.rt-oneline::-webkit-scrollbar { display: none; }
.rt-toolbar.rt-oneline button { flex: 0 0 auto; min-width: 34px; }
.rt-toolbar.rt-oneline .sep { flex: 0 0 auto; }

/*  the ✦'s refusal on an empty surface (v31.500) — one line under the toolbar, gone on the next
    keystroke. It sits INSIDE the editor's wrapper so it moves with the composer rather than
    pushing the thread about. */
.rt-ainote { margin: 10px 2px 0; line-height: 1.4; }

    .rt-toolbar .sep {
        width: 1px;
        height: 20px;
        background: rgba(104,152,232,.22);
        margin: 0 4px;
    }

.rt-emoji-wrap {
    position: relative;
}

.rt-emoji {
    position: absolute;
    top: 44px;
    left: 0;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 2px;
    padding: 8px;
    border: 1px solid rgba(112,160,238,.30);
    border-radius: var(--r-card);
    background: var(--nv-surface);
    box-shadow: 0 14px 30px rgba(0,0,0,.5);
    z-index: 20;
}

    .rt-emoji button {
        font-size: 20px;
    }

.rt-ed {
    border-radius: 0 0 var(--r-input) var(--r-input);
    padding: 14px 16px;
    display: block;
    overflow-y: auto;
    line-height: 1.5;
    outline: none;
}

    .rt-ed:focus {
        border-color: var(--nv-blue-bright);
        box-shadow: 0 0 0 3px rgba(47,127,214,.25);
    }

    /*  ⚠⚠ v31.429 (owner, on every rich editor): *"The bullet points in all rich text boxes are half
        hidden into the border … Increase the margin by 10px"* and *"the tab forward and tab back
        toolbox items do not work on the text."* ONE CAUSE: the universal reset (`* { margin:0;
        padding:0 }` in tokens.css) strips the padding every list draws its markers in, so a bullet
        or number sat on the surface's border, and the indent command — which NESTS a list item one
        level deeper — produced a nested list with no padding, i.e. nothing visible. Lists inside the
        surface get their room back here (markers 10px inside the text edge), a nested level steps in
        by the same amount, and an indented paragraph (the browser wraps it in a `blockquote` carrying
        its own inline margin) keeps that margin. Scoped to `.rt-ed` — no other list on the site moves. */
    .rt-ed ul, .rt-ed ol { padding-left: 26px; margin: 4px 0; }
    .rt-ed ul { list-style: disc outside; }
    .rt-ed ol { list-style: decimal outside; }
    .rt-ed ul ul, .rt-ed ol ol, .rt-ed ul ol, .rt-ed ol ul { margin: 0; }
    .rt-ed ul ul { list-style-type: circle; }
    .rt-ed li { margin: 2px 0; }
    .rt-ed blockquote { margin: 0 0 0 40px; }

    /*  ⚠⚠ v31.163 (owner) — THE READ-ONLY RICH SURFACE (`_RichEd`'s
        "ReReadOnly"). Used by Notes so the note lives in the SAME editor
        whether you are reading it or writing it.

        ⚠ THE TOP CORNERS COME BACK, and that is the only shape change here.
        The base rule squares them off — `border-radius: 0 0 r r` — because a
        toolbar normally sits on top and the two must meet flush. A read-only
        surface has no toolbar, so square top corners would be a joint to
        nothing.
        ⚠⚠ THE BOX ITSELF IS DELIBERATELY UNCHANGED. The point of the request
        is that the note does not move when Edit is pressed, so the padding,
        the line height and the border stay exactly as the writable editor's.
        Only the affordances go: no caret, no text cursor, and no focus ring,
        since nothing here can take focus to begin with. */
    .rt-ed.rt-ro {
        border-radius: var(--r-input);
        cursor: default;
        caret-color: transparent;
    }

        .rt-ed.rt-ro:focus {
            border-color: var(--nv-line, rgba(232,167,92,.18));
            box-shadow: none;
        }
/* ════════════════════════════════════════════════════════════════════
   v10.8 r7 (owner) — Sort-by row (.mt-extra: dropdown + funnel) is
   DESKTOP-ONLY. Placed at the END of the sheet with a compound selector:
   the earlier attempt lost the cascade to the base .mt-extra rule that
   is defined later in this file. Hidden below 1366 everywhere.
   ════════════════════════════════════════════════════════════════════ */
@media (max-width: 1365px) {
    .mail-tools .mt-extra,
    .mt-extra {
        display: none !important;
    }
}
/* ════════════════════════════════════════════════════════════════════
   v11.2 (batch58, owner) — the emoji popover must stay HIDDEN until the
   trigger is clicked: the base .rt-emoji { display:grid } was overriding
   the [hidden] attribute (same cascade lesson as .mt-extra — hide rules
   live at the END of the sheet). Also nudged below the 32px trigger.
   ════════════════════════════════════════════════════════════════════ */
.rt-emoji[hidden] {
    display: none !important;
}

.rt-emoji {
    top: 48px; /* v11.2 — clears the 32px trigger */
}
/* ════════════════════════════════════════════════════════════════════
   v12.0 (batch60 — Calendar Mock A locked) — PLANNER components:
   the calendar panel extras (mini-month, calendar toggles), the grid
   EVENT PILLS, and the right day-column bits. Display switching between
   dots (phones) and pills (desktop) lives at the END of responsive.css.
   ════════════════════════════════════════════════════════════════════ */
/* grid event pills (hidden on phones — the dots serve there) */
.day .pills {
    display: none;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
    margin-top: 2px;
}

/* v27.1 (PRODUCTION TICKET, owner: "completed items should be green as in
   v1"): a completed calendar entry keeps its place in the month grid but
   reads as done - green edge (set inline from --acc-green) and struck text. */
.pill.done .t {
    text-decoration: line-through;
    opacity: .62;
}

.pill {
    display: flex;
    align-items: center;
    gap: 5px;
    border-left: 3px solid var(--nv-blue-bright);
    background: rgba(22,33,60,.66);
    border-radius: 4px;
    padding: 2px 6px;
    font-size: 11.5px;
    color: var(--nv-ink-2);
    min-width: 0;
}

    .pill .t {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
    }

    .pill.mail {
        border-left-color: var(--nv-gold);
    }

        .pill.mail .ic {
            color: var(--nv-gold);
            flex: 0 0 auto;
            display: flex;
        }

.pmore {
    font-size: 11px;
    color: var(--nv-gold-bright);
    padding-left: 2px;
}
/* the calendar panel (reuses the .mail-folders card + grid placement) */
.cal-panel .mini-cal {
    border: 1px solid rgba(104,152,232,.13);
    border-radius: 6px;
    padding: 10px 12px;
    margin: 4px 0 8px;
}

.cal-panel .mc-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    color: var(--nv-ink-2);
    font-size: 13.5px;
    margin-bottom: 6px;
}

    .cal-panel .mc-head a {
        color: var(--nv-ink-3);
        text-decoration: none;
        padding: 0 6px;
        font-size: 16px;
    }

.cal-panel .mc-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 1px;
    text-align: center;
}

.cal-panel .mc-dow {
    font-size: 10.5px;
    color: var(--nv-ink-3);
    padding: 2px 0;
}

.cal-panel .mc-day {
    font-size: 11.5px;
    color: var(--nv-ink-2);
    text-decoration: none;
    padding: 3px 0;
    border-radius: 4px;
}

    .cal-panel .mc-day.out {
        color: var(--nv-ink-3);
        opacity: .5;
    }

    .cal-panel .mc-day.today {
        color: var(--nv-gold-bright);
        font-weight: 500;
    }

    .cal-panel .mc-day.sel {
        outline: 1px solid var(--nv-gold);
        color: var(--nv-ink);
    }

.cal-panel .cp-grp {
    font-size: 13px;
    color: var(--nv-ink-3);
    letter-spacing: .04em;
    margin: 8px 4px 2px;
}

.cal-panel .cp-sect {
    border-top: 1px solid rgba(104,152,232,.13);
    margin-top: 12px;
    padding-top: 12px;
}

.cal-panel .cp-sw {
    width: 14px;
    height: 14px;
    border-radius: 4px;
    flex: 0 0 auto;
}

    .cal-panel .cp-sw.ring {
        background: none;
        border: 1px solid rgba(104,152,232,.22);
    }

        .cal-panel .cp-sw.ring.on {
            border-color: var(--nv-gold);
            background: rgba(232,167,92,.35);
        }

.cal-panel .cp-cat {
    background: none;
    border: 0;
    font: inherit;
    text-align: left;
    cursor: pointer;
    width: 100%;
}

    .cal-panel .cp-cat.off {
        opacity: .4;
    }

        .cal-panel .cp-cat.off .cp-sw {
            background: none;
            border: 1px solid rgba(104,152,232,.22);
        }
/* right day-column bits */
.cp-bar {
    width: 3px;
    align-self: stretch;
    border-radius: 2px;
    flex: 0 0 auto;
}

.ai-side .t.done {
    text-decoration: line-through;
    color: var(--nv-ink-3);
}
/* toggled-off categories vanish everywhere (pills, dots, day-card rows);
   END-of-sheet placement wins the cascade over the display rules above */
.cat-hide {
    display: none !important;
}
/* ════════════════════════════════════════════════════════════════════
   v12.1 (batch61, owner) — AI prompt rows ("Assist with Claude",
   "Translate") get the gold border + gold text everywhere they occur
   (compose + event form; the read&reply panel already carries the gold
   aibox). END placement so the colour wins the base .airow rules.
   ════════════════════════════════════════════════════════════════════ */
.airow.aigold {
    border: 1px solid var(--nv-gold);
    background: rgba(232,167,92,.08);
    min-width: 165px; /* owner (2026-08-11 v10) */
}

    .airow.aigold > summary {
        color: var(--nv-gold-bright);
    }

        .airow.aigold > summary .chev {
            color: var(--nv-gold);
        }
/* ════════════════════════════════════════════════════════════════════
   v11.4 (batch62, owner: "labels stretched and unreadable") — cash-flow
   axis labels + latest-value figure are HTML overlays over the stretched
   SVG; percent lefts track the chart geometry at any card width while
   the glyphs stay at their natural proportions.
   ════════════════════════════════════════════════════════════════════ */
.cfx {
    position: relative;
}

    .cfx .cf-yl,
    .cfx .cf-ml,
    .cfx .cf-vl {
        position: absolute;
        line-height: 1;
        white-space: nowrap;
        font-size: 13px;
        color: var(--nv-ink-3);
        pointer-events: none;
    }

    .cfx .cf-yl {
        transform: translateX(-100%);
    }

    .cfx .cf-ml {
        transform: translateX(-50%);
    }

    .cfx .cf-vl {
        font-size: 15px;
        font-weight: 200;
        color: var(--nv-ink);
    }
/* ════════════════════════════════════════════════════════════════════
   v13.0 (batch70 — LOCKED CWA mini-mock) — the four-control top row
   (Assist dropdown · Edit in composer · Language · Regenerate) and the
   two-button bottom pair. The dropdown expands inside its cell; the row
   wraps two-by-two on narrow panels.
   ════════════════════════════════════════════════════════════════════ */
.cwa-top4 {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    align-items: start;
}

    .cwa-top4 .btn,
    .cwa-top4 .ctl {
        height: 36px;
        width: 100%;
        min-width: 0;
    }

    .cwa-top4 details.airow > summary {
        min-height: 36px;
    }

.cwa-bot2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    max-width: 560px;
}

    .cwa-bot2 .btn[disabled] {
        opacity: .45;
        cursor: default;
    }

@media (max-width: 900px) {
    .cwa-top4 {
        grid-template-columns: 1fr 1fr;
    }
}
/* ════════════════════════════════════════════════════════════════════
   v13.1 (batch71, owner) — CWA bottom pair equal heights; the inbox
   search-scope popover (chevron INSIDE the field, checkbox list).
   ════════════════════════════════════════════════════════════════════ */
.cwa-bot2 .btn {
    height: 48px;
}

.srch-wrap,
.acs-wrap {
    position: relative;
    flex: 1;
    min-width: 0;
}

    /*  ⚠⚠ v31.237 (owner, P-9) — ROOM FOR TWO CONTROLS, NOT ONE. The field now
        carries the Search submit AND the scope chevron. 54px reserved space for
        the chevron alone; the pair needs the chevron's 46px plus the button's
        32px plus its hairline and a little air.
        ⚠ `:has(> .srch-go)` KEEPS THE OLD NUMBER TRUE if the button is ever
        taken out again — the reserved space belongs to the control, not to the
        wrapper, which is the lesson `.fld-go:has(> .fld-btn)` already carries. */
    .srch-wrap .input,
    .acs-wrap .input {
        width: 100%;
        padding-right: 54px;
    }

    .srch-wrap:has(> .srch-go) .input,
    .acs-wrap:has(> .srch-go) .input {
        padding-right: 90px;
    }

    /*  ⚠⚠⚠ v31.237 — THE SEARCH BUTTON SITS LEFT OF THE CHEVRON, and the order
        inside the field is deliberate: run the search, then choose where to
        search. `.fld-btn` already draws the hairline at its own left edge, so
        the pair gets ONE divider separating text from controls — which is what
        every other field in the app looks like. The chevron draws none of its
        own and must not gain one, or the field grows a second rule between two
        buttons that belong together.
        ⚠⚠ 46px, AND THE FIRST ATTEMPT AT THIS NUMBER WAS 44. `right` measures
        the button's RIGHT edge from the field's right edge, and the chevron's
        LEFT edge is its own 6px offset plus its 40px width = 46px from that
        same edge. 44 therefore pushed the button's right edge 2px INSIDE the
        chevron's box — two absolutely positioned controls overlapping, which
        nothing in the layout complains about and which puts the scope popover
        under the thumb of a reader aiming at Search. The probe measured it. */
    .srch-wrap > .srch-go,
    .acs-wrap > .srch-go {
        right: 46px;
    }

.srch-toggle {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    height: 40px;
    background: none;
    border: 0;
    color: var(--nv-gold);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

/*  ══ v31.237 (owner, P-9) — THE SEARCH-STATE ROW ═══════════════════════════
    One line, above the list, only while a term is in force. It wraps rather
    than scrolls: at 360px the term and the count sit on one line and `Clear
    search` drops to the next, which is legible — a row that scrolls sideways
    hides the very control it exists to offer.
    ⚠ THE TERM IS THE ONLY GOLD THING HERE. It is what changed; the count and
    the clear are supporting text and read as such. */
.eml-srchstate {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 10px;
    margin: -2px 0 2px;
    font-size: 12.5px;
    color: var(--nv-muted, #8892a0);
}

    .eml-srchstate .ic {
        display: flex;
        color: var(--nv-gold);
    }

    .eml-srchstate .q {
        color: var(--nv-gold);
        font-weight: 600;
        max-width: min(52vw, 320px);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .eml-srchstate .cnt {
        white-space: nowrap;
    }

    .eml-srchstate .clr {
        margin-left: auto;
        color: var(--nv-blue-bright, #7aa8f0);
        text-decoration: none;
        white-space: nowrap;
        cursor: pointer;
    }

        .eml-srchstate .clr:hover {
            text-decoration: underline;
        }

.srch-pop {
    position: absolute;
    top: 58px;
    left: 0;
    right: 0;
    max-width: 380px;
    z-index: 30;
    border: 1px solid rgba(112,160,238,.30);
    border-radius: 6px;
    background: var(--nv-surface);
    box-shadow: 0 14px 30px rgba(0,0,0,.5);
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

    .srch-pop[hidden] {
        display: none !important; /* the .rt-emoji cascade lesson */
    }

    /*  ⚠ v31.555 (owner) — the rule and the accent that set "Search all
        folders." apart from the six field pickers above it. The six say WHERE
        IN A MAIL to look; this one says WHICH MAILS. A reader scanning seven
        identical rows would read it as a seventh field. */
    .srch-pop .srch-sep {
        display: block;
        height: 1px;
        margin: 2px 0;
        background: rgba(112,160,238,.26);
    }

    .srch-pop .srch-all span {
        color: var(--nv-gold-bright, #e8a75c);
    }
/* ════════════════════════════════════════════════════════════════════
   v13.2 (batch72, owner) — folders panel BREAKDOWNS, the archived-row
   account tag, and the save-to-account sheet. END of sheet so nothing
   earlier out-specifies the [hidden] states (the .rt-emoji lesson).
   ════════════════════════════════════════════════════════════════════ */
/* Inbox / Archive rows grow a chevron; the sub-list sits under the row */
.mail-folders .mf-grp {
    display: flex;
    align-items: center;
}

    .mail-folders .mf-grp .mf {
        flex: 1;
        min-width: 0;
    }

.mf-chev {
    flex: 0 0 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 0;
    border-radius: 8px;
    padding: 0;
    color: var(--nv-gold); /* toolkit: icons are gold */
    cursor: pointer;
}

    .mf-chev svg {
        transition: transform .15s ease;
    }

    .mf-chev[aria-expanded="true"] svg {
        transform: rotate(180deg);
    }

.mf-sub {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 2px 14px 10px 55px;
}

    .mf-sub .ln {
        display: flex;
        align-items: center;
        gap: 10px;
        min-width: 0;
        font-size: 14.5px;
        color: var(--nv-ink-3);
    }

        .mf-sub .ln > span {
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .mf-sub .ln b {
            margin-left: auto;
            flex: 0 0 auto;
            font-weight: 400;
            font-variant-numeric: tabular-nums;
            color: var(--nv-ink-2);
        }

        .mf-sub .ln.gold,
        .mf-sub .ln.gold b {
            color: var(--nv-gold-bright);
        }

    .mf-sub[hidden] {
        display: none !important;
    }
/* the account tag on archived list rows (Acc code · name) */
.mail .tag.acc {
    color: var(--nv-gold-bright);
    border-color: rgba(232,167,92,.45);
    max-width: 230px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* save-to-account sheet: the type-ahead result list */
.sv-results {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 260px;
    overflow: auto;
    margin-bottom: 10px;
}

    .sv-results .acc-hit {
        display: flex;
        align-items: center;
        gap: 10px;
        width: 100%;
        padding: 10px 12px;
        border: 1px solid rgba(104,152,232,.18);
        border-radius: 8px;
        background: none;
        color: var(--nv-ink-2);
        font-family: inherit;
        font-size: 15px;
        text-align: left;
        cursor: pointer;
    }

        .sv-results .acc-hit:hover {
            border-color: var(--nv-gold);
            color: var(--nv-ink);
        }

        .sv-results .acc-hit .code {
            flex: 0 0 auto;
            color: var(--nv-gold-bright);
            font-variant-numeric: tabular-nums;
        }

        .sv-results .acc-hit .nm {
            flex: 1;
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

/* ════════════════════════════════════════════════════════════════════
   v13.5 (batch73, owner) — SAVE-TO-ACCOUNT TREE (v1's cross-catalog
   hierarchy look): entity → account → folders, gold text, triangle
   toggles, dotted indent guides. END of sheet for the cascade.
   ════════════════════════════════════════════════════════════════════ */
.sv-results {
    max-height: 320px;
}

.sv-ent {
    display: flex;
    flex-direction: column;
}

.sv-node {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-width: 0;
    padding: 7px 10px;
    background: none;
    border: 0;
    border-radius: 8px;
    color: var(--nv-gold-bright);
    font-family: inherit;
    font-size: 15px;
    text-align: left;
    cursor: pointer;
}

    .sv-node:hover {
        background: rgba(232,167,92,.08);
    }

    .sv-node .nm {
        flex: 1;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .sv-node .code {
        flex: 0 0 auto;
        color: var(--nv-ink-3);
        font-size: 13px;
        font-variant-numeric: tabular-nums;
    }

    .sv-node .dbtag {
        flex: 0 0 auto;
        font-size: 12.5px;
        color: var(--nv-ink-3);
        border: 1px solid rgba(104,152,232,.25);
        border-radius: 6px;
        padding: 1px 7px;
    }

.sv-enthead {
    color: var(--nv-gold); /* toolkit: the entity roots gold, as v1 */
}

    .sv-enthead .tri {
        display: inline-flex;
        flex: 0 0 auto;
        transition: transform .15s ease;
    }

    .sv-enthead[aria-expanded="true"] .tri {
        transform: rotate(90deg);
    }

.sv-kids {
    display: flex;
    flex-direction: column;
    margin-left: 13px;
    padding-left: 9px;
    border-left: 1px dotted rgba(232,167,92,.35);
}

    .sv-kids[hidden] {
        display: none !important;
    }

.sv-folder {
    color: var(--nv-ink-2);
    font-size: 14.5px;
}

/* ════════════════════════════════════════════════════════════════════
   v13.6 (batch74, owner) — the folders panel's BULK "Save to account"
   row: gold, sits above Delete in the actions group, enabled only while
   grid rows are ticked. END of sheet for the cascade.
   ════════════════════════════════════════════════════════════════════ */
.mail-folders .mf-save {
    margin-top: 12px;
    border-top: 1px solid rgba(104,152,232,.13);
    border-radius: 0;
    padding-top: 14px;
    color: var(--nv-gold-bright);
}

    .mail-folders .mf-save .fi {
        color: var(--nv-gold);
    }

    .mail-folders .mf-save.disabled {
        opacity: .38;
        pointer-events: none;
    }

    /* when Save opens the actions group, Delete loses its own separator */
    .mail-folders .mf-save + .mf-del {
        margin-top: 2px;
        border-top: 0;
        padding-top: 9px;
    }

/* ════════════════════════════════════════════════════════════════════
   v13.7 (batch74.1, owner) — the PHONE folders sheet's bulk "Save to
   account" row. Behaviour is entirely the shared _SaveAccountSheet
   (data-svopen + the same handlers); this is presentation only.
   ════════════════════════════════════════════════════════════════════ */
.folders-sheet .fld.fld-save {
    width: 100%;
    background: none;
    border: 0;
    border-top: 1px solid rgba(104,152,232,.13);
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    color: var(--nv-gold-bright);
}

    .folders-sheet .fld.fld-save .fi {
        color: var(--nv-gold);
    }

    .folders-sheet .fld.fld-save.disabled {
        opacity: .38;
        pointer-events: none;
    }

/* ════════════════════════════════════════════════════════════════════
   v14.0 (batch75, owner — AI-ASSISTED SAVING, mock locked) — the
   save-similar card + candidate sheet, the rule proposal card + rules
   sheet, and the save sheet's extras rows. END of sheet for the cascade.
   ════════════════════════════════════════════════════════════════════ */

/* the gold suggestion card on the list */
.card.suggest {
    display: flex;
    align-items: center;
    gap: 14px;
    border-color: rgba(232,167,92,.45);
    background: rgba(232,167,92,.06);
}

    .card.suggest .ic {
        color: var(--nv-gold);
        flex: 0 0 auto;
    }

    .card.suggest .txt {
        min-width: 0;
    }

        .card.suggest .txt .t {
            font-size: 15.5px;
        }

        .card.suggest .txt .s {
            color: var(--nv-ink-2);
            font-size: 13.5px;
        }

    .card.suggest .end {
        margin-left: auto;
        display: flex;
        align-items: center;
        gap: 10px;
        flex: 0 0 auto;
    }

    .card.suggest .chip {
        cursor: pointer;
    }

.sim-x {
    background: none;
    border: 0;
    color: var(--nv-ink-3);
    font-size: 19px;
    cursor: pointer;
    padding: 4px 8px;
}

/* the candidate sheet */
.sim-list {
    max-height: 300px;
    overflow: auto;
    margin: 6px 0 2px;
}

.sim-grp {
    font-size: 12.5px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--nv-ink-3);
    margin: 12px 2px 6px;
}

    .sim-grp b {
        color: var(--nv-gold-bright);
        font-weight: 400;
    }

.sim-cand {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 10px;
    border: 1px solid rgba(104,152,232,.15);
    border-radius: 10px;
    margin-bottom: 6px;
    cursor: pointer;
}

    .sim-cand .txt {
        flex: 1;
        min-width: 0;
    }

    .sim-cand .from {
        display: block;
        font-size: 14.5px;
    }

    .sim-cand .subj {
        display: block;
        color: var(--nv-ink-2);
        font-size: 13.5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .sim-cand .why {
        flex: 0 0 auto;
        font-size: 12px;
        color: var(--nv-ink-3);
        border: 1px solid rgba(104,152,232,.25);
        border-radius: 6px;
        padding: 1px 8px;
        white-space: nowrap;
    }

        .sim-cand .why.ai {
            color: var(--nv-gold-bright);
            border-color: rgba(232,167,92,.45);
        }

/* the on-demand Claude row (AI PROMPT ROWS GOLD — toolkit 10) */
.findmore {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    border: 1px solid rgba(232,167,92,.55);
    background: rgba(232,167,92,.06);
    border-radius: 10px;
    padding: 11px 14px;
    margin: 10px 0 4px;
    color: var(--nv-gold-bright);
    font-family: inherit;
    font-size: 14px;
    text-align: left;
    cursor: pointer;
}

    .findmore .cost {
        margin-left: auto;
        color: var(--nv-ink-3);
        font-size: 12px;
        white-space: nowrap;
    }

    .findmore.busy {
        opacity: .5;
        pointer-events: none;
    }

/* the rule proposal card */
.card.rulecard {
    border-color: rgba(232,167,92,.45);
    max-width: 640px;
}

    .card.rulecard .rc-head {
        display: flex;
        align-items: center;
        gap: 10px;
        color: var(--nv-gold);
    }

        .card.rulecard .rc-head b {
            color: var(--nv-ink);
            font-weight: 400;
        }

    .card.rulecard .rc-map {
        display: flex;
        align-items: center;
        gap: 12px;
        flex-wrap: wrap;
        padding: 10px 0 6px;
    }

        .card.rulecard .rc-map .who,
        .card.rulecard .rc-map .acct {
            border: 1px solid rgba(104,152,232,.25);
            border-radius: 10px;
            padding: 7px 14px;
            font-size: 14.5px;
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .card.rulecard .rc-map .acct {
            border-color: rgba(232,167,92,.5);
            color: var(--nv-gold-bright);
        }

        .card.rulecard .rc-map .arrow {
            color: var(--nv-ink-3);
        }

    .card.rulecard .rc-meta {
        color: var(--nv-ink-3);
        font-size: 13px;
    }

    .card.rulecard .rc-act {
        display: flex;
        gap: 12px;
        margin-top: 12px;
    }

        .card.rulecard .rc-act form {
            flex: 1;
            margin: 0;
        }

        .card.rulecard .rc-act .btn {
            width: 100%;
            height: 44px;
        }

.btn.goldline {
    background: none;
    border: 1px solid rgba(232,167,92,.55);
    color: var(--nv-gold-bright);
}

/* the rules sheet rows */
.rule-line {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 10px;
    border: 1px solid rgba(104,152,232,.15);
    border-radius: 10px;
    margin-bottom: 6px;
}

    .rule-line .txt {
        flex: 1;
        min-width: 0;
    }

    .rule-line .who {
        font-size: 14.5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .rule-line .acct {
        color: var(--nv-gold-bright);
        font-size: 13px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .rule-line .btn.sm {
        height: 38px;
        padding: 0 14px;
        font-size: 13.5px;
        flex: 0 0 auto;
    }

/* the save sheet's extras rows (mock C) */
.opt-row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    border: 1px solid rgba(232,167,92,.35);
    background: rgba(232,167,92,.05);
    border-radius: 10px;
    padding: 10px 12px;
    margin: 8px 0 0;
    cursor: pointer;
    font-size: 14.5px;
}

    .opt-row input {
        margin-top: 2px;
        flex: 0 0 auto;
    }

    .opt-row .s {
        display: block;
        font-style: normal;
        color: var(--nv-ink-3);
        font-size: 12.5px;
    }

/* ════════════════════════════════════════════════════════════════════
   v15.0 (batch77, owner — FINANCIALS Phases 1+2, mock v2.1 locked) —
   the hub, the invoice list rows, the entry wizard and the history
   card. Reuses the shell, cards, chips, inputs and .sv-* tree styles.
   END of sheet for the cascade.
   ════════════════════════════════════════════════════════════════════ */

/* the hub: tree + task tiles */
/* v27.4 - the hub's own width, as ONE value. The module panel, the gap and
   the tile grid's cap all read from these, and so does the header row, so
   the chip in the head can line up with the last card's right edge without
   anybody re-typing a number that later drifts. */
:root {
    --fin-panel-w: 290px;
    --fin-hub-gap: 18px;
    --fin-tiles-w: 720px;
    --fin-hub-w: calc(var(--fin-panel-w) + var(--fin-hub-gap) + var(--fin-tiles-w));
}

.fin-hub {
    display: grid;
    /* v16.5 (batch84, owner overflow report) — minmax(0,1fr): the GRID
       LESSON again; a bare 1fr track let the tiles push past the viewport */
    grid-template-columns: var(--fin-panel-w) minmax(0, 1fr);
    gap: var(--fin-hub-gap);
    align-items: start;
    min-width: 0;
    max-width: 100%;
}

@media (max-width: 1023.98px) {
    .fin-hub {
        grid-template-columns: minmax(0, 1fr);
    }
}

/*  ⚠⚠ v31.115 (batch311, owner - A-57) — THE "SUGGESTED NEXT" CARD IS NOW THE
    HUB'S THIRD GRID CHILD. Owner: *"place AI Suggested text card below the
    module Cards on the right - only for 04-Screen and 05-Screen."*

    It was a SIBLING of `.fin-hub` and therefore started at the page's left
    edge, running under the TREE as well as under the tiles. Inside the grid it
    can be told which track to occupy.

    ⚠⚠ THE HUB'S OWN `gap` IS LEFT ALONE, AND THAT WAS A CORRECTION. The card
    used to be separated from the hub by `.scroll`'s inline `gap:16px`; inside
    the grid that separation becomes `.fin-hub`'s gap, which is 18. My first
    attempt pinned the hub's ROW gap to 16 to preserve it — and the probe's
    control caught what that actually did: below 1024 the hub is ONE column, so
    the tree and the tiles are also separated by the row gap, and both of them
    moved up 2px. **Changing a shared gap to protect one child moves every other
    child.** So the gap stays 18, and the only thing this round moves is the
    card it was asked to move: on 01/02/03 it now sits 2px lower than before,
    and the tree and tiles do not move at all. */

/*  ⚠ 760px IS NOT A NEW NUMBER — it is the inline `max-width` this card has
    carried since v24.0, moved here unchanged so a media query can reach it.
    An inline style beats a stylesheet rule, so the 04/05 cap below could only
    have been written as `!important` while the attribute stood. */
#sugCard {
    grid-column: 1 / -1;
    max-width: 760px;
}

/*  04-Screen and 05-Screen ONLY, exactly as asked. Track 2 is the tiles' track,
    so the card lands directly under the module cards; the cap is the TILES' own
    variable, so the two right edges agree by construction rather than by two
    numbers somebody has to keep in step — the v27.4 rule this page's head
    already follows. */
@media (min-width: 1366px) {
    #sugCard {
        grid-column: 2;
        max-width: min(var(--fin-tiles-w), 100%);
    }
}

/* v16.5 — the tree group heads (accordion, standard chevron) */
.fin-tree .sv-head {
    color: var(--nv-gold);
    cursor: pointer;
    font-family: inherit;
    font-size: inherit;
    text-align: left;
    min-height: 36px;
}

.fin-tree .sv-node {
    min-height: 36px;
}

    .fin-tree .sv-node.muted {
        color: var(--nv-ink-3);
        font-size: 13.5px;
    }

.fin-hub .sv-node.on {
    background: rgba(232,167,92,.14);
    border-radius: 8px;
    color: var(--nv-ink);
}

.fin-hub a.sv-node {
    text-decoration: none;
}

.fin-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 14px;
    max-width: min(var(--fin-tiles-w), 100%);
    min-width: 0;
}

.fin-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 18px 10px;
    text-align: center;
    text-decoration: none;
    color: var(--nv-ink-2);
    opacity: .55;
}

    .fin-tile .ic {
        color: var(--nv-gold);
    }

    .fin-tile.live {
        opacity: 1;
        border-color: rgba(232,167,92,.55);
        color: var(--nv-ink);
        cursor: pointer;
    }

/* ═══ v28.9 (owner - THE UPGRADE LOOK, study C5 "conic halo") ═══════════════
   THE HUB TILES GET THE SAME TURNING GRADIENT as the ghost button, so the two
   read as one system rather than two decisions.

   ⚠ THE DORMANT/LIVE DISTINCTION IS THE TILE'S REAL JOB and it is preserved,
   sharpened rather than replaced: a dormant tile's halo is mostly blue with
   gold only at the turn; a LIVE tile's is gold at both ends. Combined with
   the existing opacity change, "this one is available" still reads before the
   label does.

   ⚠⚠ WHY A MASKED RING AND NOT THE BACKGROUND TRICK - this one matters more
   here than on the button: .card carries backdrop-filter and, since the
   owner's edit, NO background of its own. A border-box gradient layer would
   paint a solid fill straight over the blur on every tile. The ring paints
   the edge only.

   ⚠⚠⚠ THE @supports GUARD IS LOAD-BEARING: without mask-composite the
   ::before is a filled rectangle, not a ring. Guarded, an unsupported browser
   keeps today's tile exactly - blue border, gold when live.

   ⚠ THE FAINT FILL (background below) is the one part of this study that is
   not purely a border. It stops the halo reading as detached from the tile.
   It is 5% and sits over the card's blur; delete that one line if it reads
   heavy on a busy page and the halo still works. */
@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
    .fin-tile {
        position: relative;
        isolation: isolate;
        border-color: transparent;
        background: radial-gradient(120% 100% at 50% 0%, rgba(120,165,240,.05), transparent 70%);
    }

        /* ⚠ .fin-tile.live sets border-color gold in the rule above and has
           the higher specificity, so it has to be cleared here too or the
           live tile would wear a solid border AND a halo. */
        .fin-tile.live {
            border-color: transparent;
        }

        .fin-tile::before {
            content: "";
            position: absolute;
            inset: 0;
            border-radius: inherit;
            padding: 1px;
            background: conic-gradient(from 210deg, rgba(232,167,92,.75), rgba(47,127,214,.50) 25%, rgba(104,152,232,.10) 50%, rgba(47,127,214,.50) 75%, rgba(232,167,92,.75));
            -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            -webkit-mask-composite: xor;
            mask-composite: exclude;
            pointer-events: none;
        }

        .fin-tile.live::before {
            background: conic-gradient(from 210deg, var(--nv-gold-bright), rgba(47,127,214,.55) 25%, rgba(232,167,92,.20) 50%, rgba(47,127,214,.55) 75%, var(--nv-gold-bright));
        }
}

/* invoice rows (list + history) */
.inv-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px 2px;
    border-top: 1px solid rgba(104,152,232,.12);
    min-width: 0;
}

    .inv-row:first-of-type {
        border-top: 0;
    }

    .inv-row .av {
        flex: 0 0 42px;
        height: 42px;
        border-radius: 50%;
        background: rgba(232,167,92,.14);
        color: var(--nv-gold-bright);
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .inv-row .txt {
        flex: 1;
        min-width: 0;
    }

    .inv-row .t {
        font-size: 15.5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .inv-row .s {
        color: var(--nv-ink-3);
        font-size: 13px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /*  ⚠⚠⚠ THE AMOUNT COLUMN HAS A STATED FLOOR SINCE v31.109 (batch304,
        owner: *"Please vertical align all the document view clips"*).

        **THE CLIPS WERE NEVER THE PROBLEM.** They sit immediately to the LEFT
        of the amount in a flex row, so their x is whatever is left after the
        amount has taken what it needs - and "14,223.76 C" is wider than
        "8,621.24 C". Every row put its clip somewhere slightly different.
        A floor on the amount fixes the column, and the clip follows it.

        ⚠ IT IS A FLOOR, NOT A WIDTH. A larger figure still takes the room it
        needs rather than being clipped; only the ragged edge below that size
        goes away. 104px carries "24,464.96 C" at this weight with a little to
        spare.
        ⚠⚠ `.inv-row` IS SIX SURFACES, not one - Enquiry, ArInvoices,
        ApInvoices, BankImport, BankRec and `_ApArMoneyList`. The alignment is
        an improvement on all six and was measured on the narrowest of them at
        390px, where `.txt` ellipsises as it already did. */
    .inv-row .amt {
        text-align: right;
        white-space: nowrap;
        flex: 0 0 auto;
        /*  ⚠ 112px, NOT 104. The first floor was measured against the rows in
            the owner's screenshot and "24,464.96 C" needs 105 - one pixel over,
            which put that row's clip one pixel out. **A floor chosen from the
            sample in front of you is a floor the next figure walks through.**
            112 carries six figures and a letter; beyond that a row widens and
            moves its own clip, which is self-limiting and visible. */
        min-width: 112px;
    }

    /*  ⚠ THE LINE DETAILS ARE A LINK NOW and must not look like one - the row
        already reads as a row, and an underlined blue block inside it would be
        a second visual language. It inherits, and says so on hover. */
    .inv-row a.txt {
        color: inherit;
        text-decoration: none;
        display: block;
    }

        .inv-row a.txt:hover .t,
        .inv-row a.txt:focus-visible .t {
            color: var(--nv-gold-bright);
        }

        .inv-row a.txt:focus-visible {
            outline: 2px solid var(--nv-gold);
            outline-offset: 2px;
            border-radius: 4px;
        }

.st {
    font-size: 12px;
    border-radius: 999px;
    padding: 2px 10px;
    flex: 0 0 auto;
    white-space: nowrap;
}

    .st.draft {
        color: var(--nv-ink-2);
        border: 1px solid rgba(104,152,232,.35);
    }

    .st.appr {
        color: #8fd3a0;
        border: 1px solid rgba(88,179,104,.5);
    }

    .st.posted {
        color: var(--nv-gold-bright);
        border: 1px solid rgba(232,167,92,.5);
    }

/* the wizard */
.fin-stepper {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 4px;
}

    .fin-stepper .step {
        display: flex;
        align-items: center;
        gap: 8px;
        color: var(--nv-ink-3);
        font-size: 14px;
    }

        .fin-stepper .step .n {
            width: 32px;
            height: 32px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-style: normal;
            font-size: 14px;
            border: 1.5px solid rgba(104,152,232,.35);
        }

        .fin-stepper .step.on {
            color: var(--nv-gold-bright);
        }

            .fin-stepper .step.on .n {
                border-color: var(--nv-gold);
            }

        .fin-stepper .step.done .n {
            background: rgba(88,179,104,.15);
            border-color: rgba(88,179,104,.5);
            color: #8fd3a0;
        }

    .fin-stepper .line {
        flex: 0 1 90px;
        height: 1.5px;
        background: rgba(104,152,232,.2);
    }

.fld-wrap {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
    position: relative;
}

.fl-hint {
    font-size: 12px;
    color: var(--nv-ink-3);
    /* v19.7 (batch108, owner) — FIELD HINTS ARE ONE ROW (toolkit): long
       labels ("VAT analysis — control statement") clamp with an ellipsis;
       the _Layout tooltip mirror carries the full wording on hover. The
       fixed row height also keeps grid columns ALIGNED when a hint carries
       an inline control (the Net/Gross chip). */
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-height: 18px;
    line-height: 18px;
}

.fin-grid3 {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 12px;
}

@media (max-width: 767.98px) {
    .fin-grid3 {
        grid-template-columns: 1fr;
    }
}

.fin-flags {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.flag {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    /* owner (batch112): no box around the flag — the gold rule above the
       Automation block carries the separation instead (.fin-rule) */
    border-radius: 10px;
    padding: 9px 14px;
    font-size: 14px;
    color: var(--nv-ink-2);
    cursor: pointer;
}

    .flag.on,
    .flag:has(input:checked) {
        border-color: rgba(232,167,92,.55);
        color: var(--nv-ink);
    }

.fin-line .lh {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 6px;
}

.fin-line .n {
    font-size: 12.5px;
    letter-spacing: .1em;
    color: var(--nv-gold);
}

.vatpill {
    font-size: 12.5px;
    color: #8fc0f0;
    background: rgba(47,127,214,.16);
    border-radius: 999px;
    padding: 3px 10px;
    white-space: nowrap;
}

/*  v31.25 (owner: "In the line items, please add the VAT display for that
    line ... add a view pill class='chip' on the card for each line") - THE
    REVIEW STEP'S LINE ROW.
    ⚠ IT ADDS A WRAPPER AND NOTHING ELSE. `.vatpill` and `.chip` are the
    components the Lines step already uses; the review row only needed
    somewhere to put them. Styling them again here would be two definitions of
    one thing - the four-copy lesson, in CSS.
    ⚠ IT WRAPS. A line can carry a rate code, a rate, two analysis codes, two
    amounts and a chip; on 01-Screen that is more than one row's worth, and a
    review summary that scrolls sideways is unusable. */
.rv-line {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

    .rv-line + .rv-line {
        margin-top: 6px;
        padding-top: 6px;
        border-top: 1px solid rgba(104, 152, 232, .10);
    }

    .rv-line .rv-meta {
        display: flex;
        align-items: center;
        gap: 8px;
        flex-wrap: wrap;
    }

    .rv-line .rv-amt {
        font-size: 16px;
        color: var(--nv-ink-3);
        white-space: nowrap;
        font-variant-numeric: tabular-nums;
    }

.fin-total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
}

/*  ⚠⚠ v31.251 (owner, P-28) — THE TWO CURRENCY ROWS UNDER THE TOTAL. They
    answer "what was paid, and what did it come to in our own money", so they
    must be READABLE AND SUBORDINATE: the gold 28px total stays the headline and
    these sit beneath it at body size. Same row geometry, quieter voice — a
    second 28px figure would compete with the total and a reader would not know
    which number the document is actually for. */
.fin-total.nv-bothcur {
    margin-top: 4px;
}

    .fin-total.nv-bothcur .lab {
        font-size: 13px;
        color: var(--nv-muted, #8a8a8a);
        letter-spacing: .02em;
    }

    .fin-total.nv-bothcur .amt {
        font-size: 15px;
        font-variant-numeric: tabular-nums;
    }

    .fin-total .lab {
        font-size: 18px;
        color: var(--nv-gold);
    }

    .fin-total .amt {
        font-size: 28px;
    }

/* the history card rows */
.fin-hrow {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 0;
    border-top: 1px solid rgba(104,152,232,.10);
    min-width: 0;
}

    .fin-hrow:first-of-type {
        border-top: 0;
    }

    .fin-hrow .txt {
        flex: 1;
        min-width: 0;
    }

    .fin-hrow .t {
        font-size: 14px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* v21.1 (batch120, owner: "the card is being shown over two rows in
       02-Screen, 03-Screen and 04-Screen") — .t already clamped to one row
       but .s did not, so in the narrow right column the date · TRN line
       wrapped and every history row became two rows tall. 01 (full width)
       and 05 (wide column) had the space, which is exactly why only the
       middle three showed it. */
    .fin-hrow .s {
        color: var(--nv-ink-3);
        font-size: 12.5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .fin-hrow .amt {
        white-space: nowrap;
        font-size: 14.5px;
        flex: 0 0 auto;
    }

/* the fin panel's dimmed later-phase rows */
.fin-panel .mf.dim {
    opacity: .45;
    cursor: default;
}

/* ═══ v15.2 (batch79, owner) — fin card formatting fixes ═══ */

/* the kv rows every fin card leans on (label left, value RIGHT — mock
   parity; batch79 lesson: mock classnames need their CSS ported too) */
.card .kv {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    padding: 3px 0;
}

    .card .kv .k {
        color: var(--nv-ink-2);
        font-size: 14px;
        min-width: 0;
    }

    /*  ⚠ v31.109 (owner): the OPENING BALANCE value at 28px - *"only ... and
        all the other class='v' remain 14.5px"*. A modifier class, because the
        owner's own note names the alternative and rejects it: a rule written
        against `#enqBal > div:nth-child(3) > span.v` moves onto a different
        figure the first time a row is inserted above it. */
    .card .kv .v.v-lg {
        font-size: 28px;
        line-height: 1.15;
    }

    .card .kv .v {
        white-space: nowrap;
        font-size: 14.5px;
        text-align: right;
    }

/* supplier-row avatars — the mail palette (g0…g4), picked by row index */
.inv-row .av.g0 {
    background: #33526b;
    color: #e9eef7;
}

.inv-row .av.g1 {
    background: #6d5a8e;
    color: #e9eef7;
}

.inv-row .av.g2 {
    background: #2e7d64;
    color: #e9eef7;
}

.inv-row .av.g3 {
    background: #9a6a16;
    color: #f4ecdc;
}

.inv-row .av.g4 {
    background: #874a52;
    color: #f4e5e7;
}

/* the small label ("open" / "last") sits BELOW the amount */
.inv-row .amt .s {
    display: block;
    color: var(--nv-ink-3);
    font-size: 12px;
    font-style: normal;
}

/* ═══ v16.1 (batch80.1, owner) — rail world clock ROTATOR + weather:
   one city at a time (v1 worldClockRotator), wide rail only ═══ */
.rl-world {
    margin-top: 22px;
    display: flex;
    flex-direction: column;
    align-self: stretch;
    margin-left: 14px;
    margin-right: 14px;
}

/* icon-only rail (mail-app pages) — no room, the rotator leaves */
.mail-app .siderail .rl-world {
    display: none;
}

.rlw {
    /* owner (batch81): borderless — the gold hairline alone frames it */
    padding: 14px 12px 12px;
    text-align: center;
    position: relative;
    min-height: 108px; /* steady height through the fade */
}

    /* the mock's gold hairline across the top */
    .rlw::before {
        content: "";
        position: absolute;
        top: 0;
        left: 14px;
        right: 14px;
        height: 1px;
        background: rgba(232,167,92,.55);
    }

    .rlw .c {
        color: var(--nv-gold);
        font-size: 18px;
        /*letter-spacing: 2px;*/
        margin-bottom: 6px;
    }

    .rlw .t {
        color: var(--nv-gold-bright);
        font-size: 42px;
        font-weight: 200;
        letter-spacing: 1px;
        line-height: 1.1;
    }

    .rlw .w {
        color: var(--nv-ink-2);
        font-size: 16px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
        margin-top: 4px;
    }

        /* outline weather icons — nav-icon style, gold (toolkit rule 1);
           owner: 30px (batch82) → 32px (batch127, with .c at 18px and .w at
           16px — the rail now clears the 16px caption floor too) */
        .rlw .w svg {
            flex: 0 0 auto;
            color: var(--nv-gold);
            width: 32px;
            height: 32px;
        }

/* v16.0 — tree accordion chevron (toolkit: ONE node expanded; the standard
   chevron everywhere, rotating closed) */
.sv-chev svg {
    transition: transform .18s ease;
    display: block;
}

.sv-head.closed .sv-chev svg {
    transform: rotate(-90deg);
}


/* ═══ v16.2 (batch81, owner) — INPUT + BUTTON heights system-wide ═══
   Inputs are 32px EVERYWHERE (email, planner, financials, assistant…):
   this end-of-file override beats the older 40/44/48px per-screen rules
   and inline styles. Textareas, the rich-editor surface (.rt-ed) and the
   file picker keep their own heights. Buttons: .btn = 51px in tokens.css. */
input.input:not([type="file"]),
select.input,
summary.input,
/*  ⚠ v31.62 - the country picker's face is a BUTTON wearing `.input`, and
    without this line it would be the only 36px control on a card of 32s.
    It is the first `button.input` in the app; nothing else is affected. */
button.input,
div.input:not(.rt-ed) {
    /* owner (batch82): 36px — 32 was a step too far.
       v21.0 (batch119, owner) — reversed: 32px is the height now. */
    height: 32px !important;
    min-height: 32px !important;
}


/* ═══ v17.0 (batch85, owner) — THE LOCKED TREE-VIEW STANDARD ═══
   Template = the Organisation picker. Levels are roles, not depth:
   L1 t-sect = CAPITALISED muted-blue band + hairline (top sections)
   L2 t-grp  = gold non-capital group, standard END-chevron (down closed,
               180° up open), ONE open per level (accordion)
   L3 t-leaf = data row: 24px gold outline icon · label · right meta slot;
               selected = soft gold pill + GREEN borderless ✓
   Cards: ONE radius (var(--r-card)) + the gold ✕ for manual closing. */

.tree-head {
    display: flex;
    align-items: center;
    margin-bottom: 8px;
}

    /* v21.0 (batch119, owner: "Overall Header font, font-style is wrong")
       — the locked mock types the card head as `font-weight 300 · 20px ·
       gold-bright` (.tree h3 there). Nothing in the app said so, so a bare
       <h2> fell through to the BROWSER default — bold, 24px — in every
       tree card: the menu tray, the save sheet, the fin tray and the find
       sheet. One rule fixes all of them. */
    .tree-head h2 {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 20px;
        font-weight: 300;
        color: var(--nv-gold-bright);
    }

.t-x {
    margin-left: auto;
    width: 32px;
    height: 32px;
    display: grid;
    place-content: center;
    border: 0;
    background: none;
    color: var(--nv-gold-bright);
    font-size: 17px;
    cursor: pointer;
    border-radius: var(--r-card);
}

    .t-x:hover {
        background: rgba(232,167,92,.1);
    }

.t-sect {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 38px;
    padding: 0 10px;
    border: 0;
    background: none;
    text-align: left;
    color: #8fb4ea;
    font-size: 12.5px;
    text-transform: uppercase;
    border-bottom: 1px solid rgba(104,152,232,.28);
    border-radius: 0;
    cursor: pointer;
}

    .t-sect svg {
        margin-left: auto;
        color: #8fb4ea;
        transition: transform .18s ease;
        flex: 0 0 auto;
    }

    .t-sect.open svg {
        transform: rotate(180deg);
    }

.t-grp {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 36px;
    padding: 4px 10px;
    border: 0;
    background: none;
    border-radius: var(--r-card);
    color: var(--nv-gold);
    font-family: inherit;
    font-size: 15.5px;
    font-weight: 300;
    cursor: pointer;
    text-align: left;
}

    .t-grp:hover {
        background: rgba(104,152,232,.08);
    }

    .t-grp .meta {
        margin-left: auto;
        display: inline-flex;
        align-items: center;
        gap: 8px;
    }

    .t-grp svg.chev {
        margin-left: auto;
        /*color: var(--nv-ink-3);*/
        transition: transform .18s ease;
        flex: 0 0 auto;
    }

    /* when a meta slot (catalog tag) precedes it, the meta takes the auto
       margin and the chevron just follows */
    .t-grp .meta + svg.chev {
        margin-left: 0;
    }

    .t-grp.open svg.chev {
        transform: rotate(180deg);
    }

/* v21.1 (batch120, owner: "remove the lines appearing in the left side of
   each node — it is not part of our toolkit specification") — the child
   block indents, and that is ALL it does. The 1px blue guide is struck from
   the locked tree-view: depth is carried by the indent and by the L1/L2/L3
   type, never by a rule. */
.t-kids {
    margin-left: 12px;
    padding-left: 10px;
}

    .t-kids.flat {
        margin-left: 4px;
        padding-left: 6px;
    }

    .t-kids[hidden] {
        display: none !important;
    }

.t-leaf {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 36px;
    padding: 4px 10px;
    border: 0;
    background: none;
    border-radius: var(--r-card);
    color: var(--nv-ink-2);
    font-family: inherit;
    font-size: 15px;
    font-weight: 300;
    text-decoration: none;
    cursor: pointer;
    text-align: left;
    min-width: 0;
}

    .t-leaf:hover {
        background: rgba(104,152,232,.08);
    }

    .t-leaf svg.ic {
        color: var(--nv-gold);
        flex: 0 0 auto;
    }

    .t-leaf > span:first-of-type:not(.meta) {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .t-leaf .lb {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .t-leaf .meta {
        margin-left: auto;
        color: var(--nv-ink-3);
        font-size: 13px;
        white-space: nowrap;
        display: inline-flex;
        align-items: center;
        gap: 8px;
    }

    .t-leaf.on {
        background: rgba(232,167,92,.14);
        color: var(--nv-ink);
    }

    .t-leaf .tick {
        color: var(--nv-good);
        font-size: 15px;
    }

.t-empty {
    display: flex;
    align-items: center;
    min-height: 36px;
    padding: 4px 10px;
    color: var(--nv-ink-3);
    font-size: 13.5px;
}


/* ═══ v17.1 (batch86, owner) ═══ */

/* 20px breathing room at the end of every page scroll — as an appended
   spacer, so per-screen padding rules (incl. the phone bottom-nav
   clearances) stay untouched (batch87: the padding rule was overridden) */
.scroll::after {
    content: "";
    display: block;
    flex: 0 0 20px;
    height: 20px;
}

/* ⋯ menu-tray opener — 01/02-Screens only (the panel is off-canvas there) */
.fin-tray-btn {
    display: none;
    margin-left: auto;
}

@media (max-width: 1023.98px) {
    .fin-tray-btn {
        display: flex;
    }
}

/* the tray sheet carries the shared _FinPanel — undo its desktop placement */
.fin-tray .mail-folders {
    display: flex !important;
    flex-direction: column;
    position: static;
    width: auto;
    max-width: none;
    border: 0;
    background: none;
    padding: 6px 0 0;
    margin: 0;
    box-shadow: none;
}

/* AWAITING REVIEW card — empty height matches the month card; more ⟶ */
.card.awrev,
.card.fs-month {
    min-height: 158px;
}

.card.awrev {
    display: flex;
    flex-direction: column;
}

    .card.awrev .subclamp {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }

    .card.awrev .more {
        margin-top: auto;
        margin-left: auto;
        color: var(--nv-gold-bright);
        text-decoration: none;
        font-size: 13.5px;
        padding-top: 8px;
    }

/* ⋯ menu links (reply / reply all / forward) — mirror the button rows */
.msg-menu .mm-link {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 11px 16px;
    color: var(--nv-ink-2);
    font-size: 15px;
    text-decoration: none;
    text-align: left;
}

    .msg-menu .mm-link:hover {
        background: rgba(232,167,92,.08);
    }

    .msg-menu .mm-link svg {
        color: var(--nv-gold);
        flex: 0 0 auto;
    }

/* the blue separator between the reply set and the standard items */
.msg-menu .mm-sep {
    border-top: 1px solid rgba(104,152,232,.35);
    margin: 6px 12px;
}


/* owner (batch87) — screen heads: heavy glass blur */
.screen-head {
    backdrop-filter: blur(48px) saturate(140%);
    -webkit-backdrop-filter: blur(48px) saturate(140%);
}

/* ═══ v18.0 (batch89, owner — Phase 3, mock v2 locked) — the VAT/type
   SELECTOR grammar: an input-shaped button (gold code · muted name ·
   standard end-chevron) opening a panel in the locked tree grammar (L1-style
   band heading, 36px leaf rows, green borderless ✓, one panel open). Used by
   the transaction-type picker and the line VAT trio.
   v23.2 (batch128, owner css cleanup) — 32px tall, the toolkit's input
   height, in place of the old 36px one-off. ═══ */
.vsel {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 32px;
    min-height: 32px;
    width: 100%;
    background: var(--nv-surface-2);
    /*  ⚠ v31.53 - THE HAIRLINE, THIRD OF THREE. Owner: *"The down chevron
        EVERYWHERE should also implement the hairline divider everywhere."*
        This one is an element chevron, not a background image, so the divider
        is a background layer on the BUTTON and the chevron gets 8px of
        padding-left to stand clear of it.
        ⚠ MEASURED: `.vsel` is 32px, the chevron is 18px wide and its left edge
        sits 27px from the right border. 34px puts this line in the same place
        as `.fld-go`'s and `select.input`'s - all three align across a form. */
    background-image: linear-gradient(rgba(112,160,238,.30), rgba(112,160,238,.30));
    background-repeat: no-repeat;
    background-position: right 34px center;
    background-size: 1px 22px;
    border: 1px solid rgba(104, 152, 232, .25);
    border-radius: var(--r-input);
    color: var(--nv-ink-2);
    font: 300 14px 'Poppins', sans-serif;
    padding: 0 8px 0 10px;
    cursor: pointer;
    text-align: left;
}

    .vsel:hover {
        border-color: rgba(232, 167, 92, .5);
    }

    .vsel b {
        color: var(--nv-gold);
        font-weight: 400;
        white-space: nowrap;
    }

    .vsel .nm {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        flex: 1 1 auto;
        min-width: 0;
    }

    .vsel svg.chev {
        margin-left: auto;
        /*  ⚠ v31.53 - 8px INSIDE the chevron's own box, not a bigger gap. With
            `margin-left: auto` the box's RIGHT edge is pinned, so padding grows
            it leftwards and the glyph does not move a pixel; what moves is
            where the label has to stop.

            ⚠⚠⚠ AND IT NEEDS `content-box`, WHICH A MEASUREMENT IS THE ONLY WAY
            TO KNOW. tokens.css sets `* { box-sizing: border-box }`, so the
            svg's own `width="18"` is treated as the BORDER box: the padding
            ate the glyph instead of growing the box, and the arrow was drawn
            in 10px inside an unchanged 18px box. It looked like nothing had
            happened - measured `chevBoxW: 18` with `padding-left: 8px`
            applied, which is the tell. `content-box` puts the 18 back on the
            glyph and the 8 outside it.
            MEASURED after: box 26px, left edge 35px from the right, the label
            stops at 43px and the divider is at 34px - 9px of air on the text
            side, 1px on the chevron side. */
        box-sizing: content-box;
        padding-left: 8px;
        flex: 0 0 auto;
        /* v31.31 (owner: "All down arrows chevrons should be gold") - this
           one is edited in place rather than overridden at the foot of the
           file: at (0,2,1) it beats the base `.chev` rule no matter where
           that sits. Specificity, not order. */
        color: var(--nv-gold, #e8a75c);
        transition: transform .18s ease;
    }

    .vsel.open svg.chev {
        transform: rotate(180deg);
    }

.vpanel {
    /* ══ v27.7 (owner) - THE PANEL READS WIDER THAN ITS FIELD ═════════════
       The picker panels inherited the width of whatever field they hang
       under, and in a three-column grid that is far too narrow for a code
       plus a name plus a rate. The owner asked for a 280px floor and a
       target width; read as 390px - 39px would be narrower than a single
       word, and every leaf row carries at least a code and a name.

       "provided it does not overflow the right border of the container":
       a panel in the LAST column would otherwise grow past the card's edge,
       so those grow LEFTWARDS instead (the rule below) - their right edge
       stays pinned to the field they belong to. Panels in earlier columns
       overflow to the right over their neighbour, which is ordinary
       dropdown behaviour and stays inside the card. */
    width: 390px;
    min-width: 280px;
    max-width: calc(100vw - 40px);
    /*  ⚠⚠⚠ v31.432 (owner) — THE PANEL OVERLAYS AND OPENS ABOVE. Owner: *"the drop-downs currently
        open below the field. They need to open ABOVE the field … the drop down opening below the
        field sometimes disappears from the view port. Also with the VAT, DPH and Control sheet,
        the drop-down should overlay and not expand the row below."*
        `position: relative` was the fault: an in-flow panel pushed every row under it down, and a
        panel under the LAST row of the line editor opened past the bottom of the viewport. Now it is
        an overlay anchored to its wrap (every picker's `.fld-wrap` is `position: relative`) and the
        app-wide module in `_Layout` turns `vpanel-up` on when the panel is revealed — above by
        preference, below only when there is no room above (the transaction type at the top of a
        header). `.ba-slot .ba-panel` has had this anchoring since v20.0; it now takes `vpanel-up`
        the same way. The width, the clamp and the max-height are untouched. */
    position: absolute;
    left: 0;
    top: 100%;
    z-index: 25;
    margin-top: 6px;
    background: var(--nv-surface);
    border: 1px solid rgba(104, 152, 232, .3);
    border-radius: var(--r-card);
    padding: 8px;
    box-shadow: 0 14px 34px rgba(0, 0, 0, .45);
    max-height: 320px;
    overflow: auto;
}

    .vpanel[hidden] {
        display: none;
    }

    .vpanel.vpanel-up, .ba-slot .ba-panel.vpanel-up {
        top: auto;
        bottom: 100%;
        margin-top: 0;
        margin-bottom: 6px;
        box-shadow: 0 -10px 30px rgba(0, 0, 0, .45);
    }

    .vpanel .t-leaf b {
        color: var(--nv-gold);
        font-weight: 400;
        min-width: 42px;
    }

    .vpanel .t-leaf .nm {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        flex: 1 1 auto;
        min-width: 0;
    }

.v-sect {
    display: flex;
    align-items: center;
    min-height: 32px;
    padding: 0 10px;
    color: #8fb4ea;
    font-size: 12px;
    letter-spacing: .14em;
    text-transform: uppercase;
    border-bottom: 1px solid rgba(104, 152, 232, .28);
    margin-bottom: 4px;
}

.v-more {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 36px;
    padding: 4px 10px;
    margin-top: 4px;
    border-top: 1px solid rgba(104, 152, 232, .18);
    color: var(--nv-gold);
    font-size: 14px;
    cursor: pointer;
    border-radius: var(--r-input);
}

    .v-more:hover {
        background: rgba(104, 152, 232, .08);
    }

/* ═══ v18.2 (batch92, owner — TOOLKIT lock-ins) ═══
   1) DATE/TIME PICKER GLYPHS — GOLD, never black (v18.3, owner): the
      temporal inputs carry color-scheme dark (dark native popups) and the
      indicator is recoloured to the toolkit gold (#e8a75c) via the filter
      below. Applies system-wide, now and future.
   2) MASTER MENU (three bars) — 01/02-Screens only: pages with no ⋯ menu
      and no bottom nav carry an .mmt-btn opener wired to the _MenuTray
      sheet. Same visibility rule as the ⋯ tray opener. */
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"],
input[type="week"] {
    color-scheme: dark;
}

    input[type="date"]::-webkit-calendar-picker-indicator,
    input[type="time"]::-webkit-calendar-picker-indicator,
    input[type="datetime-local"]::-webkit-calendar-picker-indicator,
    input[type="month"]::-webkit-calendar-picker-indicator,
    input[type="week"]::-webkit-calendar-picker-indicator {
        cursor: pointer;
        opacity: 1;
        /* any source colour → flat black → the toolkit gold */
        filter: brightness(0) saturate(100%) invert(74%) sepia(31%) saturate(749%) hue-rotate(336deg) brightness(96%) contrast(91%);
    }

.mmt-btn {
    display: none;
    margin-left: auto;
}

/*  ⚠⚠⚠ v31.532 — TWO `margin-left: auto` IN ONE FLEX ROW SPLIT THE FREE SPACE
    BETWEEN THEM, and that is the gap the owner ringed.

    [stated] owner, 10 Nov 2026: *"On the mobile Messages header, move the
    three-dot (…) menu button to the right-hand side, immediately before the
    hamburger/menu button."*

    The DOM order was already ⋯ then ☰ — nothing needed moving. Both carry
    `margin-left: auto`, and flexbox gives the first auto margin the free space
    only when it is the ONLY one; with two, each takes half. Measured at 390:
    the entity chip ended at 176, ⋯ sat at 225 and ☰ at 322 — 49px of nothing
    on either side of ⋯, perfectly symmetric, which is the signature of this
    fault rather than of a stray margin.

    ⚠ The pair is pushed right by the FIRST of them; the second simply follows
    at the row's own gap. `.ivt-head .mmt-btn { margin-left: 0 }` is the same
    fix made once for the Investments head at v31.502 — this generalises it to
    any head carrying both buttons rather than adding a third one-off beside
    it.                                                                      */
.fin-tray-btn + .mmt-btn { margin-left: 0; }

@media (max-width: 1023.98px) {
    .mmt-btn {
        display: flex;
    }
}

/* ═══ v18.3 (batch93, owner — TOOLKIT lock-ins) ═══
   1) HEADER ROW — the page-head icon buttons (back · ⋯/☰) always share ONE
      row, exactly like .screen-head.mail-head; heads never wrap.
   2) BUTTONS — .btn text NEVER overflows to a second line: one line with an
      ellipsis; the layout script mirrors the full text into title, so hover
      shows it (tooltip). */
.screen-head,
.flowhead {
    /* v18.4 (batch94, owner screenshot): ONLY .mail-head was flex — the
       plain heads (Financials hub) stacked back · title · ☰ on three rows.
       EVERY head is now the same one-row flex as .mail-head. */
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: nowrap;
}

    .screen-head .htext {
        flex: 1 1 auto;
        min-width: 0;
    }

    /* v18.4 — head titles NEVER wrap: one line + ellipsis (the layout script
   mirrors the full text into title, so hover shows it) */
    .screen-head h1,
    .flowhead h1,
    .screen-head .sub {
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        min-width: 0;
    }

    .flowhead h1 {
        flex: 1 1 auto;
    }

    .screen-head .iconbtn,
    .flowhead .iconbtn {
        flex: 0 0 auto;
    }

.btn {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

    .btn > span {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
    }

/* ═══ v18.7 (batch97, owner — TOOLKIT lock-ins) ═══
   1) CATALOG INDICATOR — ONE look everywhere: muted-blue text inside a
      fully-rounded muted-blue circle/pill (.dbtag was only styled inside
      .sv-node before; this is now the global standard).
   2) FOOTER CLEARANCE — the main-menu footer rides every page on
      01/02-Screens, so the scroll's breathing spacer grows to clear it
      there (the 20px spacer returns from 1024 up, where the footer hides). */
.dbtag {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    height: 20px;
    min-width: 20px;
    padding: 0 8px;
    border: 1px solid rgba(104, 152, 232, .45);
    border-radius: 999px;
    color: #8fb4ea;
    font-size: 11.5px;
    letter-spacing: .04em;
    white-space: nowrap;
}

/* v18.8 (batch98, owner) — the footer's clearance spacer.
   v19.7 (batch108, owner) — the footer hides again from 1024 (the side
   rail serves there), so the 132px clearance returns to BELOW 1024 only;
   from 1024 the base 20px breathing room applies. */
@media (max-width: 1023.98px) {
    .scroll::after {
        flex: 0 0 132px;
        height: 132px;
    }
}

/* ═══ v18.9 (batch99, owner — lines-page formatting) ═══
   On stacked layouts (below 1024, where the right column follows the main
   column) the wizard's scroll no longer STRETCHES to fill the viewport —
   the giant empty band between the step buttons and the cards is gone; the
   right-column stack follows immediately. The footer clearance rides the
   stacked column's own margin instead of the scroll spacer. */
@media (max-width: 1023.98px) {
    .fin-app .scroll {
        flex: 0 0 auto;
        min-height: 0;
    }

        .fin-app .scroll::after {
            flex: 0 0 0px;
            height: 0;
        }

    .fin-app .ai-side {
        margin-top: 0;
    }
}

/* ═══ v19.3 (batch103, owner) ═══ */

/* THE CUSTOM DIALOG (toolkit) — the reusable v1-style N-logo card over the
   err-pop grammar: nvAlert / nvConfirm / [data-nvconfirm] (_Layout). */

/* v19.3.1 (owner report: blank uncloseable dialog on every page) — the
   err-pop grammar sets display:flex, which BEATS the [hidden] attribute's
   UA display:none — so the empty dialog rendered from page load and
   close() (hidden = true) changed nothing. The attribute must win.
   LESSON (toolkit): any element toggled via `hidden` whose class sets its
   own `display` needs this guard. */
#nvDlg[hidden],
#nvDlgScrim[hidden] {
    display: none !important;
}

.nv-dlg .nv-dlg-head {
    display: flex;
    align-items: center;
    gap: 12px;
}

.nv-dlg .nv-dlg-logo {
    flex: 0 0 44px;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: 1px solid rgba(232,167,92,.55);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--nv-gold-bright);
    font-size: 22px;
    font-weight: 200;
    background: radial-gradient(circle at 35% 30%, rgba(232,167,92,.16), rgba(232,167,92,.04));
}

.nv-dlg .nv-dlg-btns {
    display: flex;
    gap: 12px;
    margin-top: 4px;
}

    .nv-dlg .nv-dlg-btns .btn {
        flex: 1;
    }

/* THE <1024 SCROLL-SPACER GAP (owner report): on stacked layouts the 132px
   .scroll::after spacer sat BETWEEN the grids and the trailing card stack —
   a huge dead band — while the .ai-side below already carries its own 120px
   bottom clearance for the fixed footer. Below 1024, any page whose card
   stack FOLLOWS the scroll drops the spacer and the stretch entirely (the
   v18.9 fin-app exemption, generalized). Pages with no trailing .ai-side
   keep the 132px spacer — there the scroll IS the page end. */
@media (max-width: 1023.98px) {
    .app-main:has(> .ai-side) .scroll {
        flex: 0 0 auto;
        min-height: 0;
    }

        .app-main:has(> .ai-side) .scroll::after {
            flex: 0 0 0px;
            height: 0;
        }
}

/* ═══ v19.4 (batch106, owner) ═══ */

/* THE CUSTOM CALENDAR (toolkit, _Layout nvCal) — the native date-picker
   popup is browser chrome and unstylable, so date inputs open the app's
   own calendar instead: the native indicator hides behind a gold glyph
   zone at the input's right end; the panel below is the locked .vpanel
   grammar, fixed to the body. Time/month inputs keep the native picker
   (gold-filtered by the v18.2 rules). */
input.input[type="date"]::-webkit-calendar-picker-indicator,
input.input[type="month"]::-webkit-calendar-picker-indicator,
input.input[type="time"]::-webkit-calendar-picker-indicator {
    display: none;
}

input.input[type="date"],
input.input[type="month"] {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23e8a75c' stroke-width='1.4' stroke-linecap='round'><rect x='3.5' y='5' width='17' height='15.5' rx='2'/><path d='M3.5 9.5h17M8 3v4M16 3v4'/></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 18px 18px;
    padding-right: 36px;
}

/* v19.6 (batch107, owner) — the time inputs join the custom-picker family
   (a gold clock glyph; the panel shows hour/minute columns) */
input.input[type="time"] {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23e8a75c' stroke-width='1.4' stroke-linecap='round'><circle cx='12' cy='12' r='8.5'/><path d='M12 7.5V12l3 2'/></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 18px 18px;
    padding-right: 36px;
}

/* v19.6 (owner: "only shows momentarily") — the panel carries its OWN box
   styling now instead of the .vpanel class: the page pickers' global
   outside-click closeAll() hides every .vpanel and was hiding this panel
   on the very click that opened it. */
.nvcal {
    position: fixed;
    z-index: 60;
    width: 272px;
    background: var(--nv-surface);
    border: 1px solid rgba(104,152,232,.3);
    border-radius: var(--r-card);
    padding: 8px;
    box-shadow: 0 14px 34px rgba(0,0,0,.45);
}

    .nvcal[hidden] {
        display: none;
    }

    /* MONTH picker — the 12-month grid under the year header */
    .nvcal .nvc-mgrid {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: 4px;
    }

        .nvcal .nvc-mgrid .nvc-mon {
            height: 40px;
            text-transform: capitalize;
        }

    /* TIME picker — hour + minute columns, 5-minute steps */
    .nvcal .nvc-twrap {
        display: flex;
        gap: 6px;
    }

    .nvcal .nvc-tcol {
        flex: 1;
        max-height: 220px;
        overflow: auto;
        display: flex;
        flex-direction: column;
        gap: 2px;
    }

        .nvcal .nvc-tcol .nvc-day {
            flex: 0 0 34px;
        }

    .nvcal .nvc-head {
        display: flex;
        align-items: center;
        gap: 8px;
        min-height: 36px;
        padding: 0 2px 6px;
    }

    .nvcal .nvc-title {
        flex: 1;
        text-align: center;
        color: var(--nv-gold);
        font-size: 14px;
        letter-spacing: .04em;
        text-transform: capitalize;
    }

    .nvcal .nvc-nav {
        width: 32px;
        height: 32px;
        border: 1px solid rgba(104,152,232,.25);
        border-radius: 6px;
        background: none;
        color: var(--nv-ink-2);
        cursor: pointer;
        font-family: inherit;
        font-size: 16px;
        line-height: 1;
    }

        .nvcal .nvc-nav:hover {
            border-color: rgba(232,167,92,.5);
        }

    .nvcal .nvc-grid {
        display: grid;
        grid-template-columns: repeat(7, 1fr);
        gap: 2px;
    }

    .nvcal .nvc-dow {
        text-align: center;
        font-size: 11px;
        letter-spacing: .06em;
        color: #8fb4ea;
        text-transform: uppercase;
        padding: 4px 0;
    }

    .nvcal .nvc-day {
        height: 34px;
        border: 1px solid transparent;
        border-radius: 6px;
        background: none;
        color: var(--nv-ink-2);
        font: 300 13.5px 'Poppins', sans-serif;
        cursor: pointer;
    }

        .nvcal .nvc-day:hover {
            background: rgba(104,152,232,.12);
        }

        .nvcal .nvc-day.out {
            color: var(--nv-ink-3);
            opacity: .45;
        }

        .nvcal .nvc-day.today {
            border-color: rgba(232,167,92,.5);
        }

        .nvcal .nvc-day.sel {
            background: rgba(232,167,92,.18);
            color: var(--nv-gold-bright);
        }

/* the BA grid-list panel anchors under its slot */
.ba-slot {
    position: relative;
}

    .ba-slot .ba-panel {
        position: absolute;
        left: 0;
        right: 0;
        top: 100%;
        z-index: 25;
        margin-top: 6px;
    }

/* ═══ v19.7 (batch108, owner) ═══ */

/* THE [hidden] GUARD, UNIVERSAL (toolkit lesson, third strike): any class
   that sets its own `display` silently defeats the hidden attribute — the
   blank dialog (v19.3.1) and the history pager that "hid" nothing (the
   .fin-hrow rows kept their display:flex) were both this bug. The
   attribute now wins everywhere, for good. */
[hidden] {
    display: none !important;
}

/* ═══ v20.2 (batch111, owner) ═══ */

/* DOCUMENTS ARE ALWAYS A COLOURFUL, CLICKABLE CLIP (toolkit rule): never a
   bare 📎 glyph — a gold clip that opens the document (or its transaction). */
.docclip {
    flex: 0 0 auto;
    color: var(--nv-gold-bright);
}

/* v25.6 (owner): the clip is BARE now - no border, no plate, just the
   gold clip. Hover brightens instead of painting a box. */
.docclip-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 34px;
    width: 34px;
    height: 34px;
    color: var(--nv-gold-bright);
    cursor: pointer;
    text-decoration: none;
    border: 0;
    background: transparent;
    padding: 0;
    font-family: inherit;
}

    .docclip-btn:hover {
        color: #ffd9a6;
    }

/* ═══ v31.112 (batch307, owner, mock-up LOCKED) — THE ADD-DOCUMENT TRIGGER ═══
   Owner: *"Please us +- as shown in the image on every line in a new column as
   the add trigger."* A plus with a rule under it — and ⚠ **NOT a plus over a
   minus**: *"second mark is NOT a minus that removes a document."*

   ⚠⚠ IT IS 28px AND FIXED, AND THAT IS THE POINT. batch304 straightened the
   amount column by giving `.inv-row .amt` a 112px floor, and the owner reported
   the crooked clips that preceded it. A control in a new column that could grow
   — with a label, a count, a focus ring that adds width — would push the amount
   out of true again on the rows that had it. This one is the same width whatever
   it is showing.
   ⚠ It is MUTED at rest, unlike the gold clip: the clip says "there is
   something here", the plus says "you may add something", and only the first of
   those is news about the row. ═══ */
/*  ⚠⚠⚠ AND IT IS NOT DRAWN BELOW 768, WHICH IS A MEASUREMENT AND NOT A TASTE.
    MEASURED at 390 on a 350px card: with the + in the row, the amount column
    read TWO x values (137 and 127) and `.txt` collapsed to ZERO on every row
    that carries a clip; with it removed, one x value and the row is what it was.
    **The + is what pushed it over.** `.inv-row` at 390 has no slack left to give
    - that is A-49, which is its own round across six surfaces - and taking the
    last 28px of a row that already cannot show its own date would be paying for
    this feature with the reader's transaction reference.
    ⚠ THE DESKTOP ROW IS WHERE THIS IS USED and it is unaffected: from 768 up the
    amount column stays single-valued with the + present, measured at five bands.
    ⚠ A-52 in the matrix: if you want it on a phone too, it arrives with the
    mobile row that A-49 calls for, not before it. */
/* ═══ v31.113 (batch309, owner) — THE TWO RECONCILED CHIPS ARE ONE WIDTH ═══
   Owner: *"In Transaction Enquiry the Unreconciled and Reconciled buttons must
   be the same width always."*

   ⚠⚠ A FIXED TRACK PLUS ELLIPSIS, WHICH IS NOT A COMPROMISE — IT IS THE ONLY
   FORM THAT SURVIVES FIVE LANGUAGES. "Same width always" cannot mean "as wide
   as the longer word", because the longer word is a different one in each
   language and German's *Nicht abgeglichen* is half again the length of
   *Unreconciled*. Two equal tracks with the overflow ellipsised, and the full
   word on the title, is exactly what the Ledger/Orders pair on this same page
   has done at 72px since v25.6, and what v27.7 settled for this very row.
   ⚠ 124px is MEASURED, not chosen: it is what the longest of the ten words
   across the five language files needs at this weight, rounded up to the next
   4px. Re-derive it if the wording changes. ═══ */
.chip.rec-chip {
    width: 124px;
    min-width: 0;
    justify-content: center;
    padding: 0 8px;
}

    .chip.rec-chip > span,
    .chip.rec-chip {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/* ═══ v31.113 (batch309, owner) — THE CLIP COLUMN HOLDS ITS WIDTH ═════════
   A history row that has no documents gets a SPACER the width of the clip
   button, not nothing. ⚠ MEASURED on the enquiry in batch307: when a row's
   fixed tail differs from its neighbours' and `.txt` has no slack left to
   absorb it, the AMOUNT COLUMN goes crooked — which is the fault batch304 was
   raised to fix. The clip is 34px, so the gap is 34px.
   ⚠ It is a SPACER, never a dimmed clip: the owner's A-34 is that a clip
   appears only where there is something to view. ═══ */
.docclip-gap {
    flex: 0 0 34px;
    display: inline-block;
}

/* ═══ v31.113 (batch309, owner) — THE SELECTED TICK IS PINNED TO THE ROW ═══
   Owner: *"The white tick showing selected item in history should be pinned
   next to the t class."* It was a flex SIBLING of `.txt`, so it sat out at the
   row's right edge beside the amount and read as a property of the FIGURE
   rather than of the row. ⚠ Moving it inside `.t` also takes 26px of fixed
   width out of the row's tail, which the reference and date get back. ═══ */
.tick.tick-in {
    margin-left: 8px;
    flex: none;
}

.docadd-btn,
.docadd-gap {
    display: none;
}

@media (min-width: 768px) {
    .docadd-btn {
        display: inline-flex;
    }

    .docadd-gap {
        display: inline-block;
    }
}

.docadd-btn {
    flex: 0 0 28px;
    width: 28px;
    height: 28px;
    /* ⚠ NO `display` HERE - it is set by the pair above, and restating it in a
       block that comes AFTER the media query would win on source order and put
       the button back on a 390px screen. */
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    color: var(--nv-ink-2);
    font-family: inherit;
    line-height: 1;
}

    .docadd-btn:hover,
    .docadd-btn:focus-visible {
        color: var(--nv-gold-bright);
    }

    .docadd-btn .pl {
        font-size: 15px;
        font-weight: 500;
    }

    /* the rule under the plus — a mark, never a second control */
    .docadd-btn .mn {
        display: block;
        width: 11px;
        height: 1px;
        background: currentColor;
    }

/* the refused-file list is one line per file, so the box has to keep them */
#nvDocAddErr {
    white-space: pre-line;
}

/* v25.6 (owner - TOOLKIT: THE DOC CLIP OPENS DOCUMENTS): the picker that
   appears when a transaction carries SEVERAL documents - the err-pop
   dialog shell, a doclink row per document. */
.nvdocpick-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-height: 320px;
    overflow: hidden auto;
}

a.doclink {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    color: var(--nv-gold-bright);
    text-decoration: none;
}

    a.doclink .nm {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    a.doclink:hover .nm {
        text-decoration: underline;
    }

/* THE WANTED FIELD (validation "Take me there"): a gold pulse on the field
   the transaction is missing. */
.nv-wanted {
    animation: nvWanted 2.4s ease-out 1;
    border-radius: var(--r-input);
}

@keyframes nvWanted {
    0%, 100% {
        box-shadow: 0 0 0 0 rgba(232,167,92,0);
    }

    12% {
        box-shadow: 0 0 0 3px rgba(232,167,92,.55);
    }

    60% {
        box-shadow: 0 0 0 3px rgba(232,167,92,.35);
    }
}

/* ═══ v20.3 (batch112, owner) ═══ */

/* THE GOLD RULE (toolkit): a hairline that closes a totals block — sits
   after the TOTAL, above the next section's label. */
.fin-rule {
    height: 1px;
    background: linear-gradient(90deg, rgba(232,167,92,.55), rgba(232,167,92,.14));
    margin: 12px 0 10px;
    border-radius: 1px;
}

/* ═══ v20.5 (batch114, owner) ═══ */

/* THE STAT TILES ARE LINKS (toolkit: a number you can see is a number you
   can open) — no underline, and the gold lift the cards already use. */
a.ai-tile {
    text-decoration: none;
    color: inherit;
    cursor: pointer;
}

    a.ai-tile:hover {
        border-color: rgba(232,167,92,.45);
    }

/* the NEW EMAIL count reads green (owner).
   v26.9 (PRODUCTION TICKET 20260821001 item 3.1): the same green now covers
   the email page's New email chip and its At-a-glance figure, so the one
   unread number looks the same everywhere it appears. */
.ai-tile .n.good,
.chip .n.good,
.ai-glance .n.good {
    color: var(--nv-good);
}

/* ═══ v20.6 (batch115, owner — TOOLKIT: THE DOCUMENT VIEWER) ═══════════════
   "in v1 we had a document viewer to prevent buildup of downloaded document
   each time we want to see a document." Reading a document must not cost a
   copy on disk: every attachment opens in this full-screen sheet, and the
   download becomes a deliberate ⤓ press. This AMENDS the NEW-TAB RULE —
   in-app viewing first, the new tab kept as the escape hatch. Own
   classnames throughout (.nvdoc*), so no page-owned outside-click sweep can
   reach it (the nvCal lesson). Sits under the nv-dlg z-band (40/41) so an
   nvAlert raised from the viewer still lands on top. No scrim: the sheet is
   opaque and full-screen, so a backdrop behind it could never be clicked —
   the stage margin around the document is the click-away zone instead. */
html.nvdoc-lock {
    overflow: hidden;
}

.nvdoc {
    position: fixed;
    inset: 0;
    z-index: 37;
    display: flex;
    flex-direction: column;
    background: linear-gradient(160deg, #050912 0%, #0b1020 55%, #101a30 100%);
}

.nvdoc-bar {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    padding: 8px 10px;
    padding-top: max(8px, env(safe-area-inset-top));
    border-bottom: 1px solid rgba(232,167,92,.22);
    background: rgba(5,9,18,.9);
}

.nvdoc-nm {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 15px;
    font-weight: 200;
    color: var(--nv-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nvdoc-pg {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: 0 0 auto;
}

.nvdoc-count {
    font-size: 13px;
    font-weight: 200;
    color: var(--nv-ink-3);
    min-width: 46px;
    text-align: center;
}

.nvdoc-tools {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 0 0 auto;
}

/* the viewer's own icon button — the .docclip-btn grammar, gold on glass */
.nvdoc-ic {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 36px;
    width: 36px;
    height: 36px;
    border-radius: 8px;
    border: 1px solid rgba(232,167,92,.30);
    background: rgba(232,167,92,.08);
    color: var(--nv-gold-bright);
    cursor: pointer;
    text-decoration: none;
}

    .nvdoc-ic:hover {
        background: rgba(232,167,92,.20);
        border-color: rgba(232,167,92,.7);
    }

    .nvdoc-ic:disabled {
        opacity: .3;
        cursor: default;
    }

        .nvdoc-ic:disabled:hover {
            background: rgba(232,167,92,.08);
            border-color: rgba(232,167,92,.30);
        }

.nvdoc-stage {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 10px;
    padding-bottom: max(10px, env(safe-area-inset-bottom));
}

.nvdoc-wrap {
    display: flex;
    flex-direction: column;
    gap: 6px;
    width: 100%;
    height: 100%;
    min-height: 0;
}

.nvdoc-frame {
    flex: 1 1 auto;
    width: 100%;
    min-height: 0;
    border: 0;
    border-radius: var(--r-card);
    background: #fff;
}

.nvdoc-note {
    flex: 0 0 auto;
    text-align: center;
    font-size: 12.5px;
    font-weight: 200;
    color: var(--nv-ink-3);
}

/*  ⚠ v31.503 — the close (and, when zoomed, the fit) that ride ON the picture. They are
    `position: absolute` against `.nvdoc`, so they hold the top-right corner of the window
    whatever the image does underneath them, and they carry their own dark disc: a ✕ in
    `currentColor` over a photograph of a bright sky is a ✕ nobody can see. */
/*  ⚠⚠ v31.503b (owner) — THE CONTROLS NOW BELONG TO THE PICTURE, NOT TO THE WINDOW.
    *"The X for close is outside the image itself and is confused with the other x in the card.
    Make x at the top ending of the image … the image should have its own blue border, rounded,
    and an X inside the top right hand corner … yellow and at the same horizontal line as the
    other x."* They are absolute against `.nvdoc-imgbox`, which shrink-wraps the image, so the
    ✕ sits in the picture's own corner at any size and can never be read as the card's ✕. */
.nvdoc-imgbox {
    position: relative;
    display: inline-flex;
    max-width: 100%;
    max-height: 100%;
    margin: auto;
}
.nvdoc-float {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 3;
    display: flex;
    gap: 8px;
}
.nvdoc-fic {
    width: 34px;
    height: 34px;
    display: grid;
    place-items: center;
    border: 1px solid rgba(255,255,255,.28);
    border-radius: 50%;
    background: rgba(8,12,20,.62);
    backdrop-filter: blur(3px);
    color: var(--acc-gold, #e1a65c);     /* ⚠ gold, his word — and it reads on a bright photograph */
    cursor: pointer;
    padding: 0;
}
.nvdoc-fic:hover { background: rgba(8,12,20,.86); border-color: rgba(255,255,255,.55); }
.nvdoc-fic:focus-visible { outline: 2px solid var(--nv-gold); outline-offset: 2px; }

.nvdoc-img {
    max-width: 100%;
    max-height: 100%;
    margin: auto;
    border: 2px solid rgba(104,152,232,.55);   /* the picture's own edge, his drawing */
    border-radius: var(--r-card);
    transform-origin: center center;
}

/* the fail-soft card: an engine that will not render the type inline never
   leaves a blank sheet — it offers the tab and the download instead */
.nvdoc-fb {
    max-width: 420px;
    padding: 24px;
    text-align: center;
}

.nvdoc-fbic {
    color: var(--nv-gold-bright);
}

.nvdoc-fb p {
    margin: 10px 0 18px;
    font-size: 15px;
    font-weight: 200;
    line-height: 1.5;
    color: var(--nv-ink-2);
}

.nvdoc-fbrow {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: center;
}

/* 01-Screen (390×844): the name takes its own row so the controls never
   crowd out of the bar */
@media (max-width: 560px) {
    .nvdoc-bar {
        flex-wrap: wrap;
    }

    .nvdoc-nm {
        order: 3;
        flex: 1 1 100%;
        font-size: 14px;
    }

    .nvdoc-tools {
        margin-left: auto;
    }
}

/* ═══ v31.341b — THE HANDHELD SAVE / SHARE SHEET MOVED to /css/nv-handoff.css,
   beside /js/nv-handoff.js. Invoice Print and the other shell-less document
   pages do not load this stylesheet, and the sheet now has to appear on them
   too — so its rules live in a file every document surface can link. */

/* ═══ v20.7 (batch116, owner — SEARCH & RETRIEVE) ══════════════════════════
   THE FIND SHEET: v1's recent-transactions folder tree (Slovakia · European
   Union · Global → alphabet bucket → supplier → its transactions) in the
   locked tree grammar, with the search v1 never had on top. A side panel
   from 1024 up, full-screen below (01/02-Screens). Own classnames (.find-*),
   and it sits UNDER the document viewer's band (36/37) and the dialog's
   (40/41), so a viewer or an nvAlert raised from a hit lands on top. */
html.find-lock {
    overflow: hidden;
}

.find-scrim {
    position: fixed;
    inset: 0;
    z-index: 34;
    background: rgba(3,6,14,.62);
}

/* Below 1024 the sheet is full-screen and OPAQUE, so the scrim behind it can
   neither be seen nor clicked — the v20.6 lesson, applied up front. It only
   exists from 1024 up, where the panel leaves the page showing beside it.
   There, ✕ · Esc · scrim all close; below, ✕ and Esc do. */
@media (max-width: 1023.98px) {
    .find-scrim {
        display: none;
    }
}

.find-sheet {
    position: fixed;
    inset: 0;
    z-index: 35;
    display: flex;
    flex-direction: column;
    /*background: linear-gradient(160deg, #050912 0%, #0b1020 55%, #101a30 100%);*/
    /*border-left: 1px solid rgba(232,167,92,.22);*/
    border-radius: 8px;
    border: 1px solid rgba(104, 152, 232, .30);
    padding: 0 20px 24px;
    background: linear-gradient(145deg, #010107 0% 0%, #1E1E1E 50%, #13294B 100%);
    font-family: 'Poppins', -apple-system, 'Segoe UI', sans-serif !important;
    margin: 20px;
}

    /* v20.9 (batch118, owner) — the sheet's own head is the app's .tree-head
   block (h2 + the gold ✕); its TYPE now comes from the global .tree-head h2
   rule (v21.0), so this only carries the sheet chrome. */
    .find-sheet > .tree-head {
        flex: 0 0 auto;
        padding-top: max(12px, env(safe-area-inset-top)) !important;
        border-bottom: 1px solid rgba(232,167,92,.22);
    }

.find-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    padding: 12px 14px;
    padding-bottom: max(16px, env(safe-area-inset-bottom));
}

.find-dates {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 10px;
    margin-top: 8px;
}

/* the two tree CARDS inside the sheet — the locked card (ONE radius, its
   own head, its own gold ✕); the rows themselves are pure .t-sect / .t-grp
   / .t-leaf and carry NO find-specific styling at all.
   v21.0 (batch119, owner: "Width is not aligned with search boxes above
   it") — a .card's 20px padding pushed every row 20px in from the search
   field's edge above it. The card keeps its border on the same line as the
   fields and drops the side padding, so a row's own 10px padding is all
   that separates its text from the field text above. */
.find-body .card.tree {
    margin-top: 12px;
    padding: 12px 2px;
}

    .find-body .card.tree .tree-head {
        margin-bottom: 6px;
        padding: 0 8px;
    }

    .find-body .card.tree > .muted {
        padding: 0 10px;
    }

/* v21.0 (batch119, owner: "We have a rule about minimum font size. Can you
   please retrieve and apply it.") — RETRIEVED: tokens.css declares
   `--fs-caption: 16px` and calls it the CAPTION MINIMUM. Nothing in this
   sheet may read smaller, so the floor is set here for the whole sheet: the
   locked tree's colours, weights and structure are untouched, only the
   sizes that sat below the minimum (12.5 · 13 · 13.5 · 15) come up to it.
   NB no nested comment markers in here — CSS comments do not nest, and an
   inner terminator silently kills every rule that follows. */
.find-sheet,
.find-body .fl-hint,
.find-body .muted,
.find-body .t-sect,
.find-body .t-grp,
.find-body .t-leaf,
.find-body .t-empty,
.find-body .t-leaf .meta,
.find-body .input {
    font-size: var(--fs-caption);
}

    .find-body .t-leaf .meta {
        font-variant-numeric: tabular-nums;
    }

/* the L1 band keeps its capitalised letter-spaced identity at the larger
   size (the mock's letter-spacing was implicit in its font shorthand) */
.find-body .t-sect {
    letter-spacing: .08em; /* owner (batch120) */
}

/* 03-Screen up: a side panel, so the invoice stays visible behind it */
@media (min-width: 1024px) {
    .find-sheet {
        left: auto;
        width: min(520px, 92vw);
        box-shadow: -18px 0 48px rgba(0,0,0,.5);
    }
}

/* v21.0 (batch119, owner: "Put both fields in the same row") — they stay
   side by side at every width; at 32px tall two of them fit 390px. */

/* ═══ v21.0 (batch119, owner) — TODAY'S FOCUS (the dashboard's fourth card)
   One line per thing, in the locked data-row grammar: gold outline icon ·
   label · right meta slot. Anything due or overdue burns gold. */
/*  ⚠⚠⚠ v31.88 (batch283, owner) - THE META DROPS TO A SECOND ROW SO THE LABEL
    GETS THE WHOLE LINE. Owner: *"In home, and on the Todays focus tab, please
    place 'meta hot' in the second row so that span with class='lb' has more
    room."*

    ⚠⚠ THE CARD IS HALF ITS OLD WIDTH FROM v31.86. `.dash-top` went to
    `1fr · 0.5fr · 0.5fr` for the AI Tokens card, so Today's focus lost half of
    its half - MEASURED at 283px at 1440. The label ellipsised against a date
    that never shrinks ("Reply to Megau s.r…" beside "today"), which is the
    label losing an argument to the one item on the row that is always short.

    ⚠ A TWO-COLUMN GRID, NOT A WRAP. `flex-wrap` would put the meta under the
    ICON as well, so the date would sit at the row's left edge under a gold
    glyph rather than under the text it belongs to. The grid keeps the icon in
    its own column spanning both lines and gives the label and the meta the
    second column, one above the other.
    ⚠⚠ `align-items: start` ON THE ICON, not `center`: with two lines of text
    beside it, a vertically centred icon floats between them instead of
    marking the row's start. */
.focus-card .focus-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: 10px;
    row-gap: 1px;
    min-height: 36px;
    padding: 4px 6px;
    border-radius: var(--r-card);
    color: var(--nv-ink-2);
    font-size: var(--fs-caption);
    text-decoration: none;
}

    .focus-card .focus-row:hover {
        background: rgba(104,152,232,.08);
    }

    /*  the icon owns column 1 and spans both text lines */
    .focus-card .focus-row .ic {
        grid-column: 1;
        grid-row: 1 / span 2;
        align-self: center;
        display: inline-flex;
        color: var(--nv-gold);
    }

    /*  ⚠ THE LABEL STILL ELLIPSISES - it now has the whole column to do it in
        rather than whatever the date left over. `minmax(0, 1fr)` on the track
        is what lets it: `min-width: 0` alone does not fire an ellipsis inside
        a grid item. */
    .focus-card .focus-row .lb {
        grid-column: 2;
        grid-row: 1;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /*  ⚠ LEFT-ALIGNED UNDER THE LABEL, not pushed to the right edge. On its own
        line a right-aligned date reads as belonging to nothing; under the text
        it reads as that item's date. */
    .focus-card .focus-row .meta {
        grid-column: 2;
        grid-row: 2;
        justify-self: start;
        color: var(--nv-ink-3);
        font-size: var(--fs-caption);
        white-space: nowrap;
    }

        .focus-card .focus-row .meta.hot {
            color: var(--nv-gold-bright);
        }

/* ═══ v21.5 (batch124, owner) — THE CALENDAR ITEM CARD ═════════════════════
   "Anything shown on the calendar item/card should be clickable … Every item
   on the Calendar should have a custom tooltip which reveals the full details
   of the item in a card." One look, two homes: the hover tooltip and the
   detail card above the day. Built on the locked grammars — .card for the
   box, .tree-head + the gold ✕ for its head, a 24px-ish dot for the
   calendar colour, and label · value rows in the .meta muted key style. */
.ev-detail:empty {
    display: none;
}

/* ⚠ .ev-card's BOX (radius, padding, background, gold edge, margin) is
   defined near the top of this file — v21.5 reused the classname the
   calendar already owned, so the two halves sit 4,500 lines apart. Edit both
   or neither. Only the column layout belongs here. */
.ev-card,
.evtip {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* .card.evtip, not .evtip — the element wears BOTH classes, so a bare
   .evtip only wins by file order and any later .card rule would take the
   surface back. Two classes settle it for good. */
.card.evtip {
    position: fixed;
    /* v21.6 (batch125, owner: "the toolbar however has a transparent
       background making the items unreadable") — a tooltip floats over live
       content, so it cannot borrow the card's translucency. THE TOOLKIT
       TOOLTIP SURFACE, for this and every tooltip after it: the app's own
       opaque 145° gradient, the same one the dialog and the find sheet use. */
    background: linear-gradient(145deg, #010107 0% 0%, #1E1E1E 50%, #13294B 100%);
    border: 1px solid rgba(104,152,232,.30);
    border-radius: 8px;
    z-index: 39; /* above the sheets (34/35) and the doc
                                        viewer (36/37), BELOW the dialog band
                                        (40/41) — a hint must never cover a
                                        decision */
    width: min(320px, calc(100vw - 16px));
    padding: 12px 14px;
    box-shadow: 0 18px 44px rgba(0,0,0,.55);
    pointer-events: none; /* it never eats the click it describes */
}

/* ═══ v31.111 (batch306, owner, mock-up LOCKED) — THE JOURNAL DRILL-DOWN ═══
   The panel `_NvDrill` builds. It wears `.card.evtip` for the surface — the
   opaque 145° gradient, the z-index band, and `pointer-events: none`, all
   inherited from the block above rather than restated — and adds its own
   geometry.

   ⚠⚠⚠ THIS IS THE POINT OF THE ROUND. v1 laid this panel out by concatenating
   HTML with hard-coded pixel margins, using `width: 350px` for a debit and
   `width: 310px` for a credit — TWO WIDTHS FOR WHAT THE READER SEES AS ONE
   COLUMN, which is why its Dr and Cr never lined up. Here the columns are
   declared ONCE, on the grid, and every line inherits them.
   ⚠ `.card.evtip.nvdrill`, two classes: `.card.evtip` caps at 320px, which is
   right for a one-line hint and half of what a three-column ledger extract
   needs. Two classes settle it without touching the hint's own width. ═══ */
.card.evtip.nvdrill {
    /*  ⚠⚠⚠ v31.113 (batch309, owner - A-53): 420, NOT 560. The panel STACKS
        now, so a wide box would put a lone right-aligned figure a long way from
        the account it belongs to - and the owner's reason for choosing the
        stacked layout was that the two places should read alike. In the
        enquiry's 560px card this renders at 420 and in a 370px history card at
        330: the same shape, near enough the same measure.
        ⚠ `.card.evtip.nvdrill`, two classes: `.card.evtip` caps at 320px, which
        is right for a one-line hint. Two classes settle it without touching the
        hint's own width. */
    width: min(420px, calc(100vw - 16px));
    padding: 14px 16px 12px;
}

.nvdrill-head {
    font-size: 15px;
    font-weight: 400;
    color: var(--nv-ink);
}

.nvdrill-sub {
    color: var(--nv-ink-3);
    font-size: 12.5px;
    margin: 2px 0 4px;
}

.nvdrill-busy {
    color: var(--nv-ink-3);
    font-size: 13px;
    padding: 2px 0;
}

.nvdrill-lines {
    font-variant-numeric: tabular-nums;
}

/*  ⚠⚠ ONE LINE IS A BLOCK, NOT A GRID ROW. The grid that used to be here gave
    the account name 270px in the enquiry and 80px in a history card, where it
    wrapped onto three and four lines and made the panel 588px tall. Measured,
    both bands, before the layout changed. */
.nvdrill-line {
    padding: 7px 0;
    border-top: 1px solid rgba(104,152,232,.14);
}

.nvdrill-acc {
    font-size: 13px;
    color: var(--nv-ink);
}

.nvdrill-n {
    color: var(--nv-ink-3);
    display: inline-block;
    min-width: 16px;
}

.nvdrill-desc {
    color: var(--nv-ink-3);
    font-size: 12.5px;
    padding-left: 16px;
}

/*  the amount on its own right-aligned row. ⚠ THE Dr/Cr LABEL IS NOT DECORATION:
    with one figure per line there is no column position left to say which side
    it is on, so the side is written. */
.nvdrill-amt {
    text-align: right;
    font-size: 13.5px;
    color: var(--nv-ink);
    margin-top: 2px;
}

.nvdrill-lab {
    color: var(--nv-gold-bright);
    font-size: 11.5px;
    margin-right: 8px;
}

/* the document-currency line, when a line's Forex mirror is not the base */
.nvdrill-fx {
    color: var(--nv-ink-3);
    font-size: 12px;
    padding-left: 16px;
}

.nvdrill-tot {
    display: flex;
    justify-content: flex-end;
    gap: 18px;
    font-size: 13.5px;
    font-variant-numeric: tabular-nums;
    color: var(--nv-ink);
    border-top: 1px solid rgba(232,167,92,.35);
    margin-top: 6px;
    padding-top: 6px;
}

/* the TRIGGER on the row: the TRN already means "this transaction"; a dotted
   underline says it will tell you more, and nothing else about the row moves.
   ⚠ `cursor: help` ONLY WHERE THE PANEL CAN OPEN. On touch `_NvDrill` returns
   before it wires anything, and a help cursor on a device with no pointer is a
   promise the page does not keep. */
@media (hover: hover) and (pointer: fine) {
    .nv-trn[data-nvdrill] {
        text-decoration: underline dotted rgba(232,167,92,.5);
        text-underline-offset: 3px;
        cursor: help;
    }

    a.nv-trn[data-nvdrill] {
        /* a TRN that is ALSO a link keeps the link's own cursor - it goes
           somewhere, and the panel is the second thing it does. */
        cursor: pointer;
    }
}

/* ═══ v31.113 (batch309, owner — A-55) — THE DOUBLE CLIP ══════════════════
   Owner: *"Use the double clip, the same glyph twice, the back one at 55%
   without a number to decode."*

   ⚠⚠ THE BEHAVIOUR WAS ALREADY RIGHT AND THE MARK WAS SILENT. `_NvDocClip` has
   always opened a single document straight in the viewer and shown the picker
   for several; what a reader could not tell was WHICH was about to happen. The
   back glyph's 55% lives in the SVG so the two marks can never drift apart in
   a way CSS would have to chase.
   ⚠ It is 4px wider than the single clip (24 against 20) inside the SAME 34px
   button, so a list of rows with and without second documents keeps one
   column - which is the alignment batch304 was about. ═══ */
/*  ══ v31.119 (batch315, owner) — THE SECOND PIN IS A COLOUR, NOT A FADE ═══
    Owner: *"At the moment it uses 55% for the second pin when we have multiple
    documents. I would prefer to use the muted blue colour only for the second
    pin to make that more distinctive."*

    ⚠ THE RULE MOVED OUT OF THE PATH AND INTO CSS, and that is the point of the
    change rather than a side effect. The back path carried `opacity=".55"` as
    an INLINE SVG ATTRIBUTE, declared identically in FOUR files (the enquiry and
    the three history surfaces) - so the look of this glyph was four numbers
    that had to be kept in step by somebody remembering. It is now one rule.

    ⚠⚠ AND A FADE IS NOT A DISTINCTION. At 55% the back pin read as the front
    pin drawn badly; a reader had to look twice to tell "one document" from
    "more than one". The brand blue against the gold front pin says it at a
    glance, which is the whole job of the second pin (A-55: *"the same glyph
    twice, the back one at 55% without a number to decode"* - the NUMBER-free
    part of that ruling stands, the 55% is what is superseded).

    ⚠ `--nv-blue-bright` rather than `--nv-blue`: the rows sit on a dark card,
    and the darker brand blue reads as a smudge at 20px against gold. */
.docclip-btn .docclip2 .dc-back,
.docclip .docclip2 .dc-back,
.docclip2 .dc-back {
    stroke: var(--nv-blue-bright);
    opacity: 1;
}

.ev-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 6px;
}

.ev-dot {
    flex: 0 0 10px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
}

.ev-subj {
    min-width: 0;
    font-size: var(--fs-caption);
    color: var(--nv-ink);
}

    .ev-subj.done {
        text-decoration: line-through;
        color: var(--nv-ink-3);
    }

.ev-kv {
    display: flex;
    align-items: baseline;
    gap: 10px;
    min-height: 24px;
}

    .ev-kv .k {
        flex: 0 0 92px;
        color: var(--nv-ink-3);
        font-size: 13px;
    }

    .ev-kv .v {
        flex: 1 1 auto;
        min-width: 0;
        color: var(--nv-ink-2);
        font-size: var(--fs-caption);
        overflow-wrap: anywhere;
    }

.ev-notes {
    margin-top: 6px;
    padding-top: 6px;
    border-top: 1px solid rgba(104,152,232,.16);
}

    .ev-notes .k {
        color: var(--nv-ink-3);
        font-size: 13px;
        margin-bottom: 2px;
    }

    .ev-notes .v {
        color: var(--nv-ink-2);
        font-size: var(--fs-caption);
        white-space: pre-wrap;
        overflow-wrap: anywhere;
        max-height: 160px;
        overflow-y: auto;
    }

/* v21.6 (batch125, owner: "those cards are not formatted to have same
   width") — Upcoming moved INSIDE the .cal-day2 grid beside the day card, so
   the two share a column by construction rather than by matching margins.
   The standalone wrapper only carries the phone/desktop margin now. */
.cal-upcoming:empty {
    display: none;
}

/* the "+n more" line a capped card closes with — it opens the same day list
   the grid's +more opens, so nothing is ever hidden without a way in */
.ev-more {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    padding: 2px 6px;
    border: 0;
    background: none;
    color: var(--nv-gold-bright);
    font-family: inherit;
    font-size: var(--fs-caption);
    cursor: pointer;
}

/* one row of the "+3 more" list — the locked leaf shape, minus the icon */
.ev-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    min-height: 36px;
    padding: 4px 6px;
    border: 0;
    background: none;
    border-radius: var(--r-card);
    color: var(--nv-ink-2);
    font-family: inherit;
    font-size: var(--fs-caption);
    text-align: left;
    cursor: pointer;
}

    .ev-row:hover {
        background: rgba(104,152,232,.08);
    }

    .ev-row .lb {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/* the +more chip now DOES something — it reads as the control it is */
.pmore {
    cursor: pointer;
}

    .pmore:hover {
        color: var(--nv-gold-bright);
    }

/* every calendar pill is a target now */
.pills .pill {
    cursor: pointer;
}

/* ═══ v23.2 (batch128, owner) — INVOICE-ENTRY CARD LAYOUT + JOURNAL LINES ═══

   THE FEED PAIR (found-in-email + awaiting-review) renders from ONE partial
   (_FinFeedCards) mounted TWICE on the entry pages; CSS shows exactly one:
     · 01/02/03 (<1366): the right-column mount (.fs-feed03), immediately
       below the totals card — the owner's 03 mockup, two rows;
     · 04/05 (≥1366): the main-column mount (.fin-feedrow), one row
       immediately below the entry cards — the owner's 05 mockup.

   THE SPLIT COLUMN (.ai-side.fs-split, entry wizard only): on 768–1365 the
   generic .mail-app balanced-columns rule SPLIT the history card mid-list
   (a card taller than the column height defeats break-inside). Two explicit
   stacks instead: fs-a = Journal · totals · feed pair; fs-b = history (+
   month). From 1366 up, and on phones, both wrappers dissolve
   (display:contents) into the normal single stack. */

.fs-split .fs-a,
.fs-split .fs-b,
.fs-feed03 {
    display: contents;
}

.fin-feedrow {
    display: none; /* the ≥1366 mount; off below */
}

@media (min-width: 768px) and (max-width: 1365.98px) {
    /* specificity beats responsive.css's `.mail-app .ai-side` balanced
       columns (display:block !important) — !important + one extra class */
    .mail-app .ai-side.fs-split {
        display: grid !important;
        columns: auto;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: 14px;
        align-items: start;
        max-width: 758px;
    }

        .mail-app .ai-side.fs-split > * {
            margin: 0;
            break-inside: auto;
        }

    .fs-split .fs-a,
    .fs-split .fs-b {
        display: flex;
        flex-direction: column;
        gap: 14px;
        min-width: 0;
    }
}

@media (min-width: 1366px) {
    .fs-feed03 {
        display: none; /* the pair rides the main column from here up */
    }

    .fin-feedrow {
        display: flex;
        gap: 14px;
        max-width: 760px; /* aligned with the entry form card */
    }

        .fin-feedrow > .card {
            flex: 1 1 0;
            min-width: 0;
        }
}

/* THE JOURNAL LINE, compacted (owner): account CODE first, then the account
   description, one row; description field 175% of an amount column, Debit
   and Credit sharing the remaining width. */
.jl-acc {
    display: flex;
    align-items: baseline;
    gap: 10px;
    min-width: 0;
}

    .jl-acc b {
        color: var(--nv-gold);
        font-weight: 400;
        white-space: nowrap;
    }

    .jl-acc .nm {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
        color: var(--nv-ink-2);
    }

.fin-jgrid {
    display: grid;
    grid-template-columns: 1.75fr 1fr 1fr;
    gap: 12px;
}

@media (max-width: 767.98px) {
    /* 01-Screen: the description takes the full first row; Dr and Cr share
       the second — three 1.75:1:1 tracks cannot fit 390px. */
    .fin-jgrid {
        grid-template-columns: 1fr 1fr;
    }

        .fin-jgrid > :first-child {
            grid-column: 1 / -1;
        }
}

/* ═══ v23.3 (batch129, owner) — THE SIDE RAIL wears the app surface ═══
   The 145deg surface gradient (the locked tooltip/app surface), replacing
   responsive.css's blue-tinted rail. !important because responsive.css
   loads last: same importance, higher file order — this wins on the
   declaration owner supplied. (The duplicated 100% stop in the owner's
   snippet was a typo — one 100% stop, same rendering.) */
.siderail {
    background: linear-gradient(145deg, #010107 0%, #1E1E1E 50%, #13294B 100%) !important;
}

/* ═══ v23.7 (batch133, owner) — LINE ROWS · MONEY GRID · CURRENCY ROW ═══ */

/* the "LINE n." header is gone: a very small number left of the description
   (owner's explicit sizing — the 16px floor stands for TEXT, this is a
   marker), the ✕ at the row end. */
.fin-lrow {
    display: flex;
    gap: 8px;
    align-items: center;
    min-width: 0;
}

/*  ⚠⚠ v31.162 (owner) — `.lno { text-align: left }`, ON POSTED INVOICES ONLY.
    His rule, scoped rather than global: `.lno` is drawn by the invoice line
    card, the payment card and the journal, and the same partial draws both the
    draft and the posted invoice. `.fin-posted` is set on the card only when
    `Model.IsReadOnly`, so the three other surfaces and the draft keep the
    right-alignment they have always had. */
.fin-line.fin-posted .lno { text-align: left; }

/*  ⚠ v31.163 (owner) — `padding: 6px 0` → `1px 0`. SIZE ONLY: nothing else on
    this rule is touched, and the `.fin-lrow > .lno` override below still sets
    `padding: 0` on the LINE-EDITOR row, so the 32px centred box that v31.82
    measured is unchanged. This 5px comes off the invoice CARD's number only,
    which is the surface the owner is reading. */
.lno {
    flex: 0 0 auto;
    min-width: 16px;
    text-align: right;
    font-size: 19px;
    color: var(--nv-gold);
    align-self: center;
    height: 20px;
    padding: 1px 0;
}

/*  ⚠⚠⚠ v31.82 (owner) - *"the iconbtn for Remove line should be aligned and
    on the same row with the input fields, not take up two rows."*

    THE CAUSE: `.fin-lrow` is `align-items: center` and the thing it centres
    against is a GRID OF LABELLED FIELDS - each `.fld-wrap` is a hint above a
    32px input, about 54px tall, and about 112px once `.fin-jgrid` drops to
    two columns at 767.98px. Centring a 32px control against 54px lands it in
    the gap BETWEEN the label and the input; centring it against 112px lands
    it between the two rows of fields. Neither is a row of its own - it just
    reads as one, which is exactly what was reported.

    THE FIX: bottom-align the two things that flank the grid, so all three
    sit on the input line. The button's `align-self` was INLINE in the row
    builder; it is taken out of there and brought here, because of what the
    measurement below says and because an inline style cannot carry a media
    query.

    ⚠⚠⚠ AT EVERY WIDTH, AND I CHANGED MY MIND ONCE BY LOOKING. At 390px
    `.fin-jgrid` is two columns, so the grid is genuinely TWO ROWS (120px,
    measured) and my first answer was to gate this on 768px and leave the
    phone alone. The screenshot said otherwise: at 390 the centred button
    lands exactly BETWEEN the description row and the amount row - which is
    the reported defect, at phone width. Bottom-aligning puts both flanking
    elements on the LAST input row, so at every width they sit on AN INPUT
    ROW and never between two.
    ⚠ AND THE NUMBER IS NOT LOSING ANYTHING ON A PHONE. Centred against a
    120px block it was already level with the amount LABELS, not with the
    description it numbers; bottom-aligned it is level with the amount inputs
    and with the button, so the row reads as one bounded block.
    Measured `lno-vs-lastInput` / `rm-vs-lastInput` = 0 at 390 / 768 / 1024 /
    1366 / 1920.

    ⚠⚠ SCOPED TO `.fin-lrow`, NOT PUT ON `.lno`. The invoice card uses `.lno`
    in a different flex row - a number beside a plain description, aligned on
    the BASELINE - and bottom-aligning a 19px number against a 16px line of
    text there would be worse than what it does today. Two containers, two
    alignments, and the container decides. */
.fin-lrow > .lno,
.fin-lrow > .iconbtn {
    align-self: flex-end;
}

    /*  ⚠⚠ AND THE NUMBER NEEDED ONE MORE THING, WHICH ONLY THE SCREENSHOT
        SHOWED. Bottom-aligning puts its BOX on the input's box - measured 0.0
        - but `.lno` is a 20px box with 6px of padding carrying a 19px glyph,
        so the digit itself drew BELOW the input's text and read as hanging
        off the corner. The measurement was right and the render was wrong,
        which is the whole reason a look follows a measurement.
        ⚠ A 32px BOX WITH THE DIGIT CENTRED IN IT. Bottom-aligned against a
        32px input, that puts the number on the input's own centre line.
        Padding goes because the height now does the work. */
    .fin-lrow > .lno {
        height: 32px;
        padding: 0;
        display: flex;
        align-items: center;
        justify-content: flex-end;
    }

/* money lines: the description at 200% of the amount column */
.fin-mgrid {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 12px;
}

/* THE CURRENCY ROW SHAPE (owner): [currency + spot/monthly] | [rate ×2] —
   used wherever currency is displayed. */
.fin-curgrid {
    display: grid;
    grid-template-columns: 1fr 2fr;
    gap: 12px;
}

@media (max-width: 767.98px) {
    .fin-mgrid {
        grid-template-columns: 1fr;
    }

    .fin-curgrid {
        grid-template-columns: 1fr;
    }
}

/* ── v24.2/24.3 (batches 138/139, owner) — the HISTORY ⇄ ALLOCATION FLIP
   CARD. The dashboard's flip pattern (Cash Flow ⇄ Bank Balances) on the
   financial entry pages. v24.3 (owner styling pass): the faces are GRID-
   STACKED instead of fixed-height — the flip is exactly as tall as its
   TALLEST face, both cards share that height, and NOTHING scrolls (the
   7-row pagers bound the content). Dots sit at the TOP of each card,
   always visible. Any click stops the auto-rotation (nvFlip). */
/* v24.4 (batch140, owner: "the width of both cards must be exactly the
   same as the cards above — they are overflowing unpredictably") — the
   GRID minmax(0,1fr) lesson: a grid column defaults to max-content, so a
   wide face was stretching the flip past its column. The flip now takes
   EXACTLY its column's width and every level below it may shrink; row
   text ellipsises instead of pushing (the allocation tick can never be
   forced out of the card). */
.fin-flip {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    break-inside: avoid;
}

    .fin-flip .flip-inner {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        min-height: 0;
        height: auto;
    }

    .fin-flip .flip-face {
        position: static;
        grid-area: 1 / 1;
        display: flex;
        flex-direction: column;
        min-width: 0;
        max-width: 100%;
    }

        .fin-flip .flip-face > .card,
        .fin-flip .flip-face > div > .card {
            flex: 1;
            height: auto;
            width: 100%;
            max-width: 100%;
            min-width: 0;
            box-sizing: border-box;
            overflow: visible;
            margin: 0;
        }

/* ══ v30.9 (batch202, owner) - THE FLIP NEVER OUT-RUNS THE LEFT COLUMN ═════
   .fin-flip already dissolves the dashboard's fixed 250px into a grid whose
   height is `auto` - both faces in one cell, the taller one setting the
   size. That is what let a long history or allocation list push the right
   column past the bottom of the left one.

   nvFitCol (Toolkit/_NvFlip.cshtml) measures the room available and writes
   --fin-flip-max, then adds .is-capped. ⚠ A CAP, NOT A HEIGHT: without the
   class nothing here applies and a short card is exactly as tall as its
   content, which is why this is scoped to .is-capped rather than set on
   .fin-flip itself.

   ⚠⚠ SCOPED TO .fin-flip ON PURPOSE. The dashboard's Cash-Flow and
   Bank-Balances cards share .flip/.flip-inner/.flip-face and must keep their
   fixed 250px - a rule on .flip here would resize the home page. */
.fin-flip.is-capped .flip-face > .card,
.fin-flip.is-capped .flip-face > div > .card {
    max-height: var(--fin-flip-max, none);
    /* ⚠ BOTH AXES, EXPLICITLY. The rule above sets `overflow: visible`, and
       CSS promotes a `visible` axis to `auto` the moment its partner is not
       visible - so naming only overflow-y would hand every capped card a
       horizontal scrollbar it never needed. */
    overflow-x: hidden;
    overflow-y: auto;
}

/* ══ v31.42 (batch239, owner) - THE CARD STOPS SCROLLING; THE ROWS SCROLL ══
   Owner: *"the hist-pager in Invoices/receipts/payments and journal and the
   top flip section including account name and period label should be static
   and visible in the card and ONLY the scroll should be in the grid content
   not the card itself."*

   The cap above is what made the whole card a scroller, so reaching row seven
   took the account name, the period line, the pager and the open balance off
   the top with it. Now the card is a flex COLUMN that clips, the head and the
   foot are fixed-size items at either end, and the row box in the middle takes
   the leftover and scrolls.

   ⚠⚠⚠ `:has(> .fin-hscroll)` IS LOAD-BEARING, NOT TIDINESS. `overflow: hidden`
   on every capped card would CLIP the ones that have no row box - the invoice
   line cards, anything else that grows - turning a scrollbar into lost
   content with no way to reach it. Only a card that has somewhere else to
   scroll may stop scrolling itself. (`:has()` is already a dependency of this
   file; container queries here shipped later than it did.)
   ⚠ AND THE ROW BOX IS PUT THERE BY JS, opt-in, per caller - see
   Toolkit/_NvPager.cshtml. A card whose pager did not opt in never matches
   this rule and behaves exactly as it did before this batch.

   ⚠⚠ `min-height: 0` IS THE HALF THAT ACTUALLY WORKS. A flex item's default
   `min-height: auto` refuses to shrink below its content, so without it the
   row box would push the pager and the open balance out through the bottom of
   the card instead of scrolling - the same trap `.acc-rows` documents. */
.fin-flip.is-capped .flip-face > .card:has(> .fin-hscroll),
.fin-flip.is-capped .flip-face > div > .card:has(> .fin-hscroll) {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* ⚠ NO `display` OF ITS OWN. In 'wrap' mode this box is new and its children
   were block siblings in the card a moment ago - they must lay out identically
   now. In 'parent' mode the box already exists (`#invAllocRows`, `#allocRows`)
   and carries its own `display:flex` + 6px gap inline; naming a display here
   would overwrite one of the two and break the other. */
.fin-hscroll {
    flex: 1 1 auto;
    min-height: 0;
    min-width: 0;
    overflow-y: auto;
    overflow-x: hidden;
}

/* ══ v31.43 (batch240, owner) - THE SAME DEAL FOR A CARD THAT IS NOT A FLIP ══
   Owner, on /generalledger/journalentry?trn=7309: *"The card in ... needs to
   follow the pattern"* and *"It does not adjust height to the height of middle
   column content."*

   ⚠⚠⚠ BOTH SENTENCES ARE ONE ROOT CAUSE. `nvFitCol` - the door that caps a
   right-column card to the middle column and adds `.is-capped` - was only ever
   called on the two money pages' flips. The journal page never called it, so
   its history card had NO cap: it grew to its content and out-ran the middle
   column (the second sentence), and because every cap rule in this file was
   written as `.fin-flip.is-capped ...`, the `.fin-hscroll` box that v31.42 put
   in that card was INERT (the first sentence). The card was carrying the fix
   and nothing was switching it on.

   ⚠ SO THE RULES LOSE THEIR `.fin-flip` PREFIX. There was never anything
   flip-specific about "a capped card scrolls its body, not itself" - the
   prefix was just where the need first appeared. `.fin-flip.is-capped` puts
   the class on the FLIP and caps the card inside it; here the class lands on
   the CARD itself, so it needs its own pair of rules rather than a wider
   selector on the old ones.
   ⚠⚠ `--fin-flip-max` KEEPS ITS NAME even though it is now the generic cap.
   Renaming it would touch _NvFlip and every rule above for no behaviour, and
   a rename in production buys nothing. Read it as "the cap nvFitCol wrote". */
.card.is-capped {
    max-height: var(--fin-flip-max, none);
    /* ⚠ BOTH AXES - the same trap the flip rule documents. */
    overflow-x: hidden;
    overflow-y: auto;
}

    /* ⚠ AND ONLY A CARD WITH SOMEWHERE ELSE TO SCROLL MAY STOP SCROLLING
       ITSELF. Without `:has()` this would clip a capped card that has no row
       box, turning a scrollbar into unreachable content. */
    .card.is-capped:has(> .fin-hscroll) {
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }


    .fin-flip .fin-hrow {
        min-width: 0;
    }

        .fin-flip .fin-hrow .t,
        .fin-flip .fin-hrow .s {
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        /* the allocation tick and the amount never shrink or drop off the row */
        .fin-flip .fin-hrow .amt,
        .fin-flip .fin-hrow .flag,
        .fin-flip .fin-hrow .st {
            flex: 0 0 auto;
        }

    /* the dots lead the card (owner: "at the top, permanently visible");
   v24.5 — they are also FACE SWITCHES (nvFlip): tappable targets. */
    .fin-flip .flip-dots {
        margin: 0 0 8px;
        padding-top: 0;
        cursor: pointer;
    }

        .fin-flip .flip-dots span {
            width: 10px;
            height: 10px;
        }

    /* ── v24.5 (batch141, owner: "Allocations disappeared … cannot see any
   checkboxes") — BELT AND BRACES on the 3D flip: the hidden face is also
   gated with visibility + pointer-events (delayed to mid-turn), so the
   visible face always renders and always takes the clicks whatever a
   browser makes of backface hit-testing. */
    .fin-flip .flip-face {
        transition: visibility 0s linear .35s;
    }

    .fin-flip.is-flipped .flip-front,
    .fin-flip:not(.is-flipped) .flip-back {
        visibility: hidden;
        pointer-events: none;
    }

/*  ══ v31.220 (owner, Q-37 B) — THE THIRD FACE ═════════════════════════════
    Owner: *"Two faces rotate as today; Bank Statement is reached only by
    clicking its chip."*

    ⚠⚠⚠ IT IS DELIBERATELY NOT PART OF THE `rotateY` PAIR. `.flip-front` is
    `rotateY(0)` and `.flip-back` is `rotateY(180deg)`; a card with a front and a
    back has exactly two sides, and turning it into a three-sided carousel would
    have changed the motion of every `.flip` in the build — including the
    dashboard's Cash-Flow ⇄ Bank-Balances card, which asked for nothing. So the
    statement is a flat overlay in the SAME grid cell (`.fin-flip .flip-face` is
    already `grid-area: 1 / 1`), raised by `is-stmt` on the card.

    ⚠⚠ AND THE OTHER TWO ARE HIDDEN UNDER IT, not merely covered: `visibility`
    plus `pointer-events` is the same belt-and-braces v24.5 had to add when
    backface hit-testing let clicks through to the face nobody could see.

    ⚠ SCOPED TO `.fin-flip`. `.flip-stmt` exists on no other card, and none of
    these rules can reach the dashboard. */
.fin-flip .flip-stmt {
    transform: none;
    backface-visibility: visible;
    -webkit-backface-visibility: visible;
    visibility: hidden;
    pointer-events: none;
    z-index: 3;
}

.fin-flip.is-stmt .flip-stmt {
    visibility: visible;
    pointer-events: auto;
}

/*  ⚠⚠⚠ AND THE CARD MUST NOT STILL BE MID-TURN UNDER IT — FOUND BY LOOKING AT
    THE SCREENSHOT, not by reading the probe's JSON, which was green on every
    field it asked about.

    `.flip.is-flipped .flip-inner` is `rotateY(180deg)`, and the statement face
    is a CHILD of that inner. Setting `transform: none` on the FACE does not
    undo a transform on its PARENT: with the rotation still applied, the whole
    statement rendered MIRRORED — "Bank statement" as "tnemetats knaB" — and
    spilled left out of its own column, because a horizontal flip about the
    card's centre puts the right-hand half where the left-hand half was.

    ⚠⚠ THE LESSON IS THE MEASUREMENT, NOT THE RULE. Every assertion p225 made
    passed: the class was on, the three rows were there, the words were right.
    A reader looking at that screen could not have read one of them. **A probe
    that only queries the DOM cannot see a page rendered backwards** — which is
    exactly why the discipline is to LOOK at the screenshot.

    ⚠ It is scoped to `is-stmt`, so the two rotating faces keep their turn: drop
    the statement and `is-flipped` decides again, unchanged. */
.fin-flip.is-stmt .flip-inner {
    transform: none;
}

    .fin-flip.is-stmt .flip-front,
    .fin-flip.is-stmt .flip-back {
        visibility: hidden;
        pointer-events: none;
    }

/*  the three face switches, below the card and outside `.flip-inner` so they
    are readable whichever face is up. */
.flip-tabs {
    margin-top: 8px;
}

/*  ⚠ Q-38's REFUSAL, ON THE FIELD ITSELF. The gold edge says which box the
    sentence below is about; the value is never rewritten. */
.input.nvpp-over {
    border-color: rgba(232, 167, 92, .75);
}

/*  ══ v31.220b (owner) — A READ-ONLY PANEL MUST LOOK READ-ONLY ═════════════
    *"the Payment step should be populated ... with all the fields, read only."*

    The mechanism was already right — the settled panel carries no
    `data-roallow`, so the view-mode sweep disables it — but this build has NO
    `.input:disabled` rule at all, so a disabled box looked exactly like a live
    one. **A panel that is read-only and looks editable invites a click that
    does nothing, which reads as the page being broken.**

    ⚠⚠ IT REUSES `.nvcv-ro`'s LANGUAGE, not a second one: the same flat wash,
    the same softened border, the same `cursor: default` — the honest half —
    that v31.214 settled on for "keeps its place, stops looking like somewhere
    to type". One vocabulary for one idea.

    ⚠⚠⚠ AND IT IS SCOPED TO THIS CARD. A bare `.input:disabled` would restyle
    every disabled field on every view-mode screen in a live system, in a round
    that was asked about payments. That may well be the right change; it is not
    this round's change to make unasked. */
#payPaidCard .input:disabled,
#payPaidCard select:disabled {
    background: rgba(120, 165, 240, .04);
    border-color: rgba(112, 160, 238, .14);
    color: var(--nv-ink-2, #aab3c8);
    box-shadow: none;
    cursor: default;
    opacity: 1;   /* the wash IS the signal; the browser's default fade is not */
}

    /*  the search and ⟳ buttons inside it go quiet too, rather than sitting
        there looking pressable. */
    #payPaidCard .fld-btn:disabled {
        opacity: .35;
        cursor: default;
    }

/*  ⚠⚠ v31.220c (owner: *"selection from pages >1 persisted"*) — THE SELECTED
    STATEMENT ROW SAYS SO. With six rows to a page the chosen one can be off
    screen when the reader comes back to it, so it has to be findable at a
    glance rather than remembered. The mark is a left rule and a faint wash, in
    the gold this app already uses for "this is the one".
    ⚠ IT IS NOT A BACKGROUND ALONE: a wash on a dark card reads as a hover on
    some displays, and hover is not selection. */
/*  ⚠ v31.220d (owner) — the statement card's message sits ABOVE the headline,
    and it earns its spacing only when it has something to say. An empty div
    with a fixed margin would push the heading down on a card where nothing has
    happened yet, which is a gap the reader has to explain to themselves. */
#stmtSay:not(:empty) {
    margin-bottom: 10px;
}

#stmtRows .fin-hrow {
    /*  ⚠ THE PADDING IS ON EVERY ROW, NOT ON THE SELECTED ONE. Adding it with
        the mark would shift the text sideways as the reader clicks, and a row
        that moves under the cursor reads as a mis-click. The screenshot showed
        the rule clipping the date to "L8/09". */
    padding-left: 8px;
}

    #stmtRows .fin-hrow.on {
        background: rgba(232, 167, 92, .07);
        box-shadow: inset 3px 0 0 0 var(--nv-gold);
        border-radius: 3px;
    }


    /* ── v24.7 (batch143, owner: "decide what should be the column width for
   each item and make this width fixed for all rows so that the items
   appear to be in same column") — FIXED COLUMNS on the history and the
   allocation grids. The AMOUNT column is one fixed width on every row,
   right-aligned; the allocation rows add a fixed trailing CONTROL column
   (.fin-scol) that carries either the ✓-pill + Clear or the tick, so
   settled and open rows keep the same geometry. History rows have no
   trailing column — their amounts sit hard against the right edge. ── */
    .fin-flip .fin-hrow .amt,
    #histCard .fin-hrow .amt,
    #histSlot .fin-hrow .amt {
        flex: 0 0 112px;
        width: 112px;
        min-width: 112px;
        display: inline-flex;
        justify-content: flex-end;
        align-items: baseline;
        gap: 4px;
        text-align: right;
        overflow: hidden;
    }

.fin-hrow .fin-scol {
    flex: 0 0 96px;
    width: 96px;
    min-width: 96px;
    display: inline-flex;
    justify-content: flex-end;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

/* the HISTORY heading holds ONE line: long account names ellipsise and the
   full name rides the tooltip (owner). Covers the flip faces AND the plain
   history cards (money/journal views, journal entry). */
.fin-flip h2.section > span,
#histCard h2.section > span,
#histSlot h2.section > span {
    display: block;
    min-width: 0;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ═══ v27.3 (owner, 2026-08-21) - THE CHANGE-ENTITY CHIP IN THE HEADER ROW
   "The change entity button should be located in the same row as the h1 with
   the title of the page ... at the top right of the page."

   The chip kept its look; only where it sits changed. In the head row it must
   never push the title around, so it holds its width, never wraps, and a long
   entity name ellipsises with the full name on the tooltip - the same rule
   every other head element follows. */
.screen-head .org-head {
    flex: 0 0 auto;
    margin-top: 0;
    max-width: 46vw;
    min-width: 0;
}

    .screen-head .org-head .org-name {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
    }

    .screen-head .org-head svg {
        flex: 0 0 auto;
    }

/* ═══ v27.4 (owner screenshots, 2026-08-21) ═══════════════════════════════

   1) "Align with the edge of cards." On the Financials hub the header row
   runs the full centred measure while the CONTENT stops at the hub's width,
   so a chip flush to the head's right edge floated alone in the empty band
   past the last tile. The chip now stops where the tiles stop. The margin is
   computed, not typed: 100% is the head's own content width, so the chip's
   right edge lands exactly --fin-hub-w from the same left edge the hub
   starts on, and it collapses to zero the moment the page is narrower than
   the hub. Only from the rail breakpoint up - below that the hub is one
   column and the head's own right edge IS the content edge. */
@media (min-width: 1024px) {
    .screen-head.fin-head > .org-head {
        margin-right: max(0px, calc(100% - var(--fin-hub-w)));
    }
}

/* 2) "Please stack them so we have two rows." On the narrowest screens the
   greeting drops away and the name and the chip fought over one line, so a
   real entity name arrived truncated to "NOVERA Gr…". The head wraps here
   instead: the name block takes the full first row and the chip drops to a
   second row at full width, where the name reads in full. 02-Screen up is
   unchanged - the owner confirmed one row is right there. */
@media (max-width: 560px) {
    .screen-head.hb {
        flex-wrap: wrap;
        row-gap: 8px;
    }

        .screen-head.hb > div:first-child {
            flex: 1 1 100%;
            min-width: 0;
        }

        .screen-head.hb > .org-head {
            flex: 0 0 auto;
            margin-left: 0;
            margin-right: auto;
            max-width: 100%;
        }

    /* heads that keep one row here (back · title · chip · menu) still need
       the chip to yield space to the title rather than the other way round */
    .screen-head:not(.hb) .org-head {
        max-width: 40vw;
        padding: 0 10px;
    }
}

/* ═══ v27.3 (PRODUCTION TICKET 20260821002 items 4.1/4.2 + supplementary 6)
   THE ACTIONABLE GLANCE, AND THE SCHEDULE / RESCHEDULE ACTIONS ═══════════

   4.1 - the four At-a-glance boxes became links. They keep the tile look
   exactly as it was; the only visible change is that they now say, on hover
   and on keyboard focus, that they can be pressed. */
a.gl {
    display: block;
    text-decoration: none;
    color: inherit;
    border-radius: 10px;
    transition: box-shadow .15s ease, background .15s ease;
}

    a.gl:hover,
    a.gl:focus-visible {
        background: var(--nv-surface-2);
        box-shadow: 0 0 0 1px var(--nv-blue-glow);
        outline: none;
    }

/* 4.2 - the small square action on an Upcoming deadline row (add to
   calendar / open the entry). Sized to the 34px control the overdue menu
   button already uses, so the two rows line up. */
.gl-act {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    border: 1px solid var(--nv-surface-3);
    background: transparent;
    text-decoration: none;
    opacity: .85;
}

    .gl-act:hover,
    .gl-act:focus-visible {
        opacity: 1;
        border-color: var(--nv-blue);
        outline: none;
    }

/* item 6 - the RESCHEDULE tile inside the AI Reply quick-actions card. The
   card is a two-column grid of bordered <a> tiles; this one is a <div>
   because it carries its own ⋯ menu, and a row that owns a control cannot
   itself be a link. It therefore repeats the tile's own dressing - border,
   radius, padding, height - or it would read as a hole in the grid.
   ⚠ NO overflow:hidden here (the <a> tiles have it): that would clip the
   menu the moment it opened. The two text lines ellipsise instead. */
.cwa-qa .ov-row {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 10px;
    border: 1px solid rgba(104,152,232,.22);
    border-radius: 8px;
    color: var(--nv-ink-2);
    font-size: 14px;
    padding: 10px 12px;
    min-height: 56px;
    min-width: 0;
}

    .cwa-qa .ov-row .txt {
        flex: 1 1 0;
        min-width: 0;
    }

    .cwa-qa .ov-row > svg,
    .cwa-qa .ov-row .ov-menubtn {
        flex: 0 0 auto;
    }


/* ═══ v27.8 - THE nth-child CLAMP IS GONE ════════════════════════════════
   v27.7 pulled the last cell's panel leftwards using :nth-child(3n) and
   :last-child. Those count SOURCE ORDER, and these grids use spans, so the
   box sitting at a row's right edge is often neither - which is why panels
   still crossed the card border. A selector cannot see where the grid put a
   box; the measured clamp in _Layout does it properly and covers every
   picker on every page. Nothing replaces it here on purpose. */

/* ═══ v27.7 (owner: "all items should always be on the same row and labels
   ellipsis if text is too long") - THE FILTER / HISTORY CHIP ROW ══════════
   Unreconciled · Reconciled · All wrapped to a second line the moment the
   words grew (German and Polish reliably), which moved the list under them
   and broke the card's rhythm. One row now, always: the chips share the
   space, a long label ellipsises inside its own chip, and the full word
   rides the title - the toolkit's ELLIPSIS + TOOLTIP rule, and ruling 28
   applied to chips. The i18n patcher fills both the text and the title from
   the same key, so the tooltip is right in every language. */
.chip-row {
    display: flex;
    gap: 18px;
    align-items: center;
    flex-wrap: nowrap;
    min-width: 0;
}

    .chip-row > .chip {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        flex: 0 1 auto;
    }


/* ═══ v27.8 (owner: "The process chip in bank reconciliation should be
   aligned in the same column for all row items, independent of the amount or
   other column alignment") - FIXED COLUMNS ON THE STATEMENT ROWS ══════════
   The same ruling as v24.7 on the history and allocation grids, applied to
   the bank rec's own rows: every column holds ONE width on EVERY row, so the
   Process chip lands at the same x whether the line is matched or not and
   whatever the amount reads.

   The row still renders the star and the chip cells when a line is matched -
   empty, but present - because a cell that disappears takes the alignment of
   everything after it with it. That is what made the chips wander. */
.br-row {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

    .br-row > .txt {
        flex: 1 1 auto;
        min-width: 0;
    }

    /* the ✦ column: present on every row, lit only when there is a hint */
    .br-row > .br-star {
        flex: 0 0 16px;
        width: 16px;
        text-align: center;
    }

    /* the PROCESS column - one width, every row */
    .br-row > .br-proc {
        flex: 0 0 96px;
        width: 96px;
        min-width: 96px;
        display: inline-flex;
        justify-content: flex-start;
        align-items: center;
    }

        .br-row > .br-proc > .chip {
            max-width: 100%;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

    /* the AMOUNT column, right-aligned on the same 112px as the history grids */
    .br-row > .amt {
        flex: 0 0 112px;
        width: 112px;
        min-width: 112px;
        display: inline-flex;
        justify-content: flex-end;
        align-items: baseline;
        gap: 4px;
        text-align: right;
        overflow: hidden;
    }

    /* the trailing tick / clear column */
    .br-row > .br-tick {
        flex: 0 0 26px;
        width: 26px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

/* ═══ v28.1 (owner - THE DOCUMENT SLICE) - THE SUGGESTION CARD ════════════
   Two zones with a separator between them, because the rule the owner set is
   spatial: what the transaction already has sits ABOVE the line and is never
   touched; what the document offers sits BELOW it, each behind its own Use.
   The card is rendered by /js/nv-docsuggest.js and looks the same wherever it
   appears - the bank reconciliation sheet and the invoice header both. */
.nvdoc {
    background: linear-gradient(180deg, rgba(47,127,214,.10), rgba(47,127,214,.03));
    border: 1px solid var(--nv-blue-glow);
    border-radius: 10px;
    padding: 11px 13px;
    margin: 10px 0;
}

.nvdoc-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 4px;
    flex-wrap: wrap;
}

    .nvdoc-head .k {
        color: var(--nv-gold-bright);
        font-weight: 600;
    }

    .nvdoc-head .src {
        margin-left: auto;
        font-size: 12px;
        color: var(--nv-ink-3);
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/* the duplicate warning - a ledger fact, so it reads as a statement */
.nvdoc-dup {
    border-left: 3px solid var(--nv-warn);
    background: rgba(217,161,59,.10);
    border-radius: 0 8px 8px 0;
    padding: 8px 11px;
    margin: 6px 0;
}

    .nvdoc-dup .k {
        color: var(--nv-warn);
        font-size: 14px;
    }

    .nvdoc-dup .v {
        font-size: 14px;
        color: var(--nv-ink-2);
    }

.nvdoc-msg {
    font-size: 14.5px;
    color: var(--nv-ink-2);
    padding: 4px 0;
}

.nvdoc-zone {
    font-size: 12.5px;
    color: var(--nv-ink-2);
    margin: 2px 0 4px;
}

.nvdoc-row {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 42px;
    border-top: 1px solid var(--nv-surface-2);
    padding: 5px 0;
    min-width: 0;
}

    .nvdoc-row.tall {
        align-items: flex-start;
    }

    .nvdoc-row > .txt {
        min-width: 0;
        flex: 1 1 auto;
    }

    .nvdoc-row .s {
        font-size: 12.5px;
        color: var(--nv-ink-3);
    }

        .nvdoc-row .s .always {
            color: var(--acc-green);
        }

    .nvdoc-row .t {
        font-size: 15px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

        /* the extended description is the one field meant to be READ, so it
           wraps instead of ellipsising */
        .nvdoc-row .t.wrap {
            white-space: normal;
            line-height: 1.5;
        }

        /* a value that could not be stood behind: the REASON takes its place */
        .nvdoc-row .t.none {
            color: var(--nv-ink-3);
            font-size: 13.5px;
            white-space: normal;
        }

.nvdoc-conf {
    font-size: 11.5px;
    border-radius: 999px;
    padding: 1px 8px;
    white-space: nowrap;
    flex: 0 0 auto;
}

    .nvdoc-conf.ok {
        border: 1px solid var(--acc-green);
        color: var(--acc-green);
    }

    .nvdoc-conf.mid {
        border: 1px solid var(--nv-gold-deep);
        color: var(--nv-gold-bright);
    }

    .nvdoc-conf.low {
        border: 1px solid var(--nv-danger);
        color: var(--nv-danger);
    }

.nvdoc-kept {
    font-size: 11.5px;
    color: var(--nv-ink-3);
    border: 1px solid var(--nv-surface-3);
    border-radius: 999px;
    padding: 1px 8px;
    white-space: nowrap;
    flex: 0 0 auto;
}

/* THE SEPARATOR - everything under it came from the document */
.nvdoc-sep {
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 12px 0 6px;
}

    .nvdoc-sep .ln {
        flex: 1;
        height: 1px;
        background: var(--nv-gold-deep);
        opacity: .55;
    }

    .nvdoc-sep .lb {
        font-size: 12.5px;
        color: var(--nv-gold-bright);
        white-space: nowrap;
    }

.nvdoc-why {
    border-top: 1px solid var(--nv-surface-2);
    padding-top: 6px;
    margin-top: 4px;
}

    .nvdoc-why .s {
        font-size: 12.5px;
        color: var(--acc-green);
    }

    .nvdoc-why .v {
        font-size: 13.5px;
        color: var(--nv-ink-2);
    }

.nvdoc-acts {
    display: flex;
    gap: 8px;
    margin-top: 9px;
    flex-wrap: wrap;
    align-items: center;
}

    .nvdoc-acts .s {
        font-size: 12.5px;
        color: var(--nv-ink-3);
    }

.nvdoc .chip[disabled] {
    opacity: .45;
    cursor: not-allowed;
}


/* ═══════════════════════════════════════════════════════════════════════
   v31.31 (batch227, owner) - THREE APP-WIDE CONTROL RULES
   Placed at the FOOT OF THE FILE on purpose: each one has to beat a rule
   written earlier for the same element, and equal specificity means the
   LATER rule wins. Moving this block up is what would silently undo it.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 1. THE EMPTY DATE FIELD'S FORMAT HINT ──────────────────────────────
   Owner: "the empty field has suggested format, displayed. Can only
   suggested text colour be muted. Do this throughout e.g in New Invoice."
   ⚠ `.nv-dateempty` is put on and taken off by wwwroot/js/nv-fields.js -
   read that file's header before touching this: no CSS selector can see
   that a date input is empty, and the shadow-part trick that is usually
   quoted for this was measured in this project and does NOT work.
   ⚠ THE WHOLE FIELD IS COLOURED, and that is exact rather than lazy: the
   only text an empty date field draws IS the hint. The instant a value
   exists the class comes off and the normal ink returns.
   ⚠ FOCUS OVERRIDES IT - someone typing into the field is looking at what
   they are typing, not at a hint. */
input.nv-dateempty {
    color: var(--nv-ink-3, #6b7690);
    /*  v31.31 (owner, same round): "The muted ink should always have
        font-weight:100." The hint IS muted ink, so it takes the muted weight
        with it - it cannot borrow the colour and not the weight. `.muted`
        itself carries the same pair in tokens.css. */
    font-weight: 100;
}

    input.nv-dateempty:focus {
        color: var(--nv-ink, #f1f3f9);
    }

/* ── 2. EVERY DOWN CHEVRON IS GOLD ──────────────────────────────────────
   Owner: "All down arrows chevrons should be gold."
   The chevrons are inline SVGs drawn with `stroke="currentColor"`, so the
   colour is all this needs. Some already carried gold (`.rcpt`, the gold
   AI row); the rest inherited whatever text colour they sat in.
   ⚠ `.vsel svg.chev` IS EDITED AT ITS OWN RULE, not overridden here - it
   sets `--nv-ink-3` explicitly at (0,2,1) and would beat this (0,1,0) no
   matter how far down the file this sits. Specificity, not order. */
.chev {
    color: var(--nv-gold, #e8a75c);
}

/* ── 3. THE SELECT CONTROL ──────────────────────────────────────────────
   Owner: "The dropdown control should have better styling for the content
   in the drop down. e.g Colour, width."
   ⚠⚠ WHAT IS OURS AND WHAT IS THE BROWSER'S. The CONTROL - its box, its
   arrow, its own text - is ours and is styled here. The POPUP LIST a
   native <select> opens is drawn by the browser: `option` colour and
   background are honoured on Chromium/Windows and are set below, but its
   WIDTH, its hover row and its scrollbar are not addressable from CSS on
   any engine. A dropdown that needs real control over its list is a job
   for the app's own picker component (`.v-sect` / `.t-leaf`, the one the
   supplier invoices and the VAT code editor use), not for a <select>.
   ⚠ `select.ctl` had NO `appearance: none`, so it was still drawing the
   operating system's grey arrow while `select.input` beside it drew the
   app's own. Same treatment, one look. */
/*  v31.53 - the same hairline. ⚠ `select.ctl` IS 48px TALL, NOT 32, so its
    divider is 30px rather than 22 - the same 9px of inset top and bottom that
    22 gives a 32px field. A divider that kept the 22px height on a taller
    control would read as a shorter, meaner line.
    ⚠ Its chevron sits at `right 12px` (20px wide → the box ends 32px from the
    edge), so the divider goes to 32 and the text stop moves 40 → 44. */
select.ctl {
    -webkit-appearance: none;
    appearance: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23e8a75c' stroke-width='1.6' stroke-linecap='round'><path d='m7 10 5 5 5-5'/></svg>"),
                      linear-gradient(rgba(112,160,238,.30), rgba(112,160,238,.30));
    background-repeat: no-repeat, no-repeat;
    /*  ⚠⚠ v31.54 - THE SAME CENTRING, AND I APPLIED IT HERE WITHOUT BEING
        ASKED. The owner named `select.input` only. Its sibling's divider is at
        32px, so its zone centre is 16px and a 20px chevron's right edge wants
        to be at 6px; it was at 12, i.e. 6px off-centre. Leaving one select
        centred and the other not is the inconsistency the "everywhere"
        instruction was about - but it IS an unasked change on a control that
        lives on other pages, so it is flagged rather than buried. Say the word
        and it goes back to 12. */
    background-position: right 6px center, right 32px center;
    background-size: auto, 1px 30px;
    padding-right: 44px;
}

/* the list itself, as far as the engine allows */
select.input option,
select.ctl option,
select.input optgroup,
select.ctl optgroup {
    background-color: var(--nv-surface-3, #101a30);
    color: var(--nv-ink, #f1f3f9);
}

    select.input option:checked,
    select.ctl option:checked {
        color: var(--nv-gold-bright, #f2bc74);
    }

/* ══════════════════════════════════════════════════════════════════════════
   ACCOUNT SETUP  (v31.33, batch230)  -  /financials/accounts
   The mock-up the owner locked, in CSS. Everything here is prefixed `acc-`
   and lives at the FOOT OF THE FILE on purpose:

   ⚠⚠⚠ AT EQUAL SPECIFICITY THE LATER RULE WINS. This project has now paid
   for that lesson twice (v31.12 across a media query, v31.28 between two
   ordinary rules), and both times `!important` would have been the wrong
   answer. Appending is the answer.

   ⚠ `.fld` IS NOT USED HERE. In this file `.fld` is the FOLDER ROW inside a
   sheet; the page's fields use the house `.fld-wrap` + `.fl-hint` + `.input`
   trio, and the status pip is `.acc-dot` rather than `.dot` for the same
   reason - a short class name that already means something else.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. THE TYPE CHIPS ────────────────────────────────────────────────────
   Owner: *"for the chips of account types at the top, in 02-Screen up to
   05-Screen, make all chips same width as the widest chip. In 01-Screen
   stack them so that their length covers the width."*

   ⚠⚠⚠ v31.35 (batch232) - `width: max-content` WAS THE WRONG TOOL HERE AND
   THE OWNER'S SCREENSHOT SHOWED WHY: "Suppliers 155" and "Customers 27" ran
   straight through their pill borders into each other.
   The `max-content` technique (v31.30's mail actions) sizes every track to
   the widest ITEM - which is right for four buttons in a roomy row, and
   wrong for SEVEN chips in a column that is only 555px wide at 03-Screen.
   `max-width: 100%` shrank the TRACKS, but a chip's own text does not
   shrink, so the labels overflowed instead. **A one-row grid cannot wrap,
   and seven chips at 03-Screen must wrap.**

   ⚠ `repeat(auto-fit, minmax(116px, 1fr))` gives the owner both halves of
   the rule and cannot overflow: every track is `1fr` so **every chip is the
   same width**, and the row breaks into two when 116px each will not fit.
   01-Screen keeps its own single-column rule, where "same width" and
   "covering the width" are the same thing. */
/* ⚠⚠⚠ v31.41 (batch238, owner) - 01-SCREEN IS NOW ONE CHIP AND TWO ARROWS.
   Owner: *"experiment with having back /forward arrows for the account types.
   This will ensure only one button (chip) is displayed on the row.
   <-- Suppliers 157 -->"*

   MEASURED, and this is the whole case for it: the two-per-row grid it
   replaces cost **152px** at 390 - four rows of chrome above a list that had
   not started yet. One row costs 32px.

   ⚠⚠ THE ARROWS ARE REAL LINKS, NOT A JS CAROUSEL. Each carries the previous
   or next type's own route, so one tap is one type - not "step, then tap".
   The list wraps at both ends, so there is no dead arrow to explain.
   ⚠ AND `More` STAYS ON THE ROW. The seven chips are only the shortcut set;
   the other twenty-two types live in the sheet, and without More on 01-Screen
   they would be unreachable. Four cells fit: 34 + 1fr + 34 + More.

   ⚠ THIS IS AN EXPERIMENT THE OWNER ASKED FOR, AND THE THING IT REPLACES IS
   ONE LINE. To go back to v31.37's two-per-row, delete the four-column
   template and the `display: none` rule below and restore:
       grid-template-columns: repeat(2, minmax(0, 1fr));
   Nothing else on the page depends on either shape. */
.acc-chips {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    gap: 8px;
    margin-bottom: 14px;
}

    /* ⚠ EVERY CHIP IS STILL RENDERED - one is SHOWN. That matters twice: the
       markup is identical at every width (so nothing re-flows on rotate), and
       the chip that shows is chosen by the SERVER, which is the only place
       that knows which type is open. */
    .acc-chips > .chip:not(.acc-chip-cur):not(.acc-arrow):not(#accMore) {
        display: none;
    }

    .acc-arrow {
        flex: 0 0 auto;
        padding: 0 9px;
    }

    .acc-chips > .chip {
        justify-content: center;
        min-width: 0;
    }

        /* ⚠ THE LABEL NEEDS ITS OWN PERMISSION TO SHRINK. `min-width: 0` on
           the chip is not inherited - without this the translated label (cs
           "Dodavatelé", de "Lieferanten") pushes the pill open again. */
        .acc-chips > .chip > span {
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .acc-chips > .chip > .n,
        .acc-chips > .chip > svg {
            flex: 0 0 auto;
        }

/* ⚠ 02-Screen UP: every chip, equal width, and the arrows stand down. The
   carousel exists to buy vertical space on a phone; from 561px there is room
   for the whole set and stepping one at a time would be a step backwards. */
@media (min-width: 561px) {
    .acc-chips {
        grid-template-columns: repeat(auto-fit, minmax(116px, 1fr));
    }

        .acc-chips > .chip:not(.acc-chip-cur):not(.acc-arrow):not(#accMore) {
            display: flex;
        }

        .acc-chips > .acc-arrow {
            display: none;
        }
}

/* ── 2. THE SHELL ─────────────────────────────────────────────────────────
   Owner: *"Left column with list of accounts should always be the same
   height as the middle card with entry fields."*

   ⚠ THAT IS A GRID ROW, NOT A HEIGHT. Both cards are items in one row, and
   a grid row's items stretch by default - so the shorter card grows to the
   taller one without either being told a pixel. The list then distributes
   that height inside itself (section 3).

   ⚠⚠⚠ v31.35 (batch232) - THE COLUMN COUNT CANNOT COME FROM A MEDIA QUERY,
   AND MEASURING IS THE ONLY WAY TO SEE IT. The first version asked for
   `320px 1fr 320px` from 1366 and the middle card collapsed to **42px** on
   the owner's screen. Measured, at the real breakpoints, this content
   column is:

       viewport   1024   1280   1366   1433   1600   1920
       .acc-shell  619    875    555    622    688    688

   **IT IS NOT MONOTONIC AND IT NEVER EXCEEDS ~688px.** Two things cause it,
   both in responsive.css and both deliberate:
     · `.app-main { max-width: 1410px }` from 1024 - the hard ceiling; and
     · from 1366 `.mail-app .app-main` becomes `260px | 1fr | 390px`, so the
       AI column's 390px track is **reserved whether or not the page fills
       it**. That is why 1366 is NARROWER than 1280.
   So `320 + 320 + 28` did not fit in 555 and `minmax(0, 1fr)` did what it is
   told: it gave up everything it had.

   ⚠⚠ **NO VIEWPORT MEDIA QUERY CAN MODEL A WIDTH THAT FALLS AS THE VIEWPORT
   GROWS.** This is what container queries are for, and this is the one place
   in the app that needs them: `.acc-wrap` is the container and the shell asks
   ITS OWN width, not the window's.
   ⚠ THE FALLBACK IS SAFE. Where `@container` is not supported the base rule
   stands and the page is one honest column - narrower than ideal, never
   broken. (The file already relies on `:has()`, which shipped later in
   Firefox than container queries did.)
   ⚠ AND EVERY TRACK IS FRACTIONAL. A fixed track can be wider than its
   container; a fraction cannot. That is what makes this class of fault
   impossible rather than merely fixed. */
.acc-wrap {
    container-type: inline-size;
    container-name: accshell;
}

.acc-shell {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
    align-items: stretch;
}

    .acc-shell > .acc-side {
        grid-column: 1 / -1;
    }

/* ⚠ 600px, chosen from the measurements above and not from a round number:
   it takes in 1024 (619), 1433 (622) and 1600/1920 (688) and leaves out only
   1366 (555), where 258px of list beside 283px of account would be worse than
   one honest column. */
@container accshell (min-width: 600px) {
    .acc-shell {
        grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    }
}

/* ⚠ 900px. v31.35 picked 1040 and the owner's screen measures 1028 - it missed
   by twelve pixels and the whole right column dropped below the fold, which
   read as "the cards are missing". v31.36 moved it to 940. v31.38 moves it
   again to 900, and this time for a MEASURED reason rather than a safety
   margin: at a 1280 viewport the container is 916px, and the difference
   between two columns and three there is a page of **1723px against 1174px**.
   Three narrow columns beat one long scroll.
       container   660(1024)  916(1280)  961(1366)  1028(1433)  1094(1600+)
       columns         2          3          3           3           3
   ⚠⚠ PICK A BREAKPOINT FROM THE MEASURED WIDTHS, NEVER FROM A ROUND NUMBER. */
@container accshell (min-width: 900px) {
    .acc-shell {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.7fr) minmax(0, 1.1fr);
    }

        .acc-shell > .acc-side {
            grid-column: auto;
        }
}

/* ⚠ THE FIELD ROWS ASK THE SAME CONTAINER. `.fin-grid3` is three fixed
   fractions down to 768px of VIEWPORT, which says nothing about the 396px of
   CARD they are actually drawn in. Two columns is the honest answer at every
   width this page really gets. */
.acc-detail .fin-grid3 {
    grid-template-columns: minmax(0, 1fr);
}

@container accshell (min-width: 520px) {
    .acc-detail .fin-grid3 {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

/* ── 3. THE LIST ──────────────────────────────────────────────────────────
   Owner: *"The left suppliers list must have as many rows as is needed to
   fill the height - minus pager."*

   The card is a flex COLUMN: search, filters, count and pager take what they
   need, and `.acc-rows` takes everything that is left. `flex: 1 1 0` plus
   `min-height: 0` is the pair that makes that true - a flex item's default
   `min-height: auto` refuses to shrink below its content, so without the
   second half the rows would push the pager off the card instead of
   scrolling.
   ⚠⚠⚠ A BOX CANNOT SCROLL ONE AXIS AND BE `visible` ON THE OTHER. Setting
   `overflow-y` alone silently makes a sideways scroller too - exactly what
   the bank card did in v31.31 - so BOTH axes are named here. */
.acc-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
    min-width: 0;
}

.acc-find {
    margin: 0;
}

    .acc-find .input {
        width: 100%;
    }

/* Owner: *"Active only, With VAT no etc must be same width."*

   ⚠ v31.36 (batch233) - THE SAME CORRECTION THE TOP CHIPS NEEDED, for the
   same reason and measured the same way. `width: max-content` on a one-row
   grid cannot wrap, so in a 282px card three chips came out 84px each and
   "No transactions" ran straight through its pill. `repeat(auto-fit, ...)`
   keeps every track `1fr` - so the chips stay the same width, which is the
   owner's rule - and drops to one per row when three will not fit.
   ⚠ 128px, not 104: at 104 the narrow card fitted two clipped chips per row,
   which is worse than one whole one. */
.acc-filters {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
    gap: 8px;
}

    .acc-filters > .chip {
        justify-content: center;
        min-width: 0;
    }

        /* the label needs its own permission to shrink - see the top chips */
        .acc-filters > .chip > span {
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

    /* the held chip wears the app's own disabled grammar (v31.21 `.t-off`) */
    .acc-filters > .chip.t-off {
        opacity: .42;
        pointer-events: none;
    }

.acc-count {
    font-size: 12.5px;
}

/* ⚠⚠⚠ v31.41 (batch238) - THE PHONE LIST WAS 0px TALL AND THIS IS WHY.
   Owner: *"For screen 01-Screen, please show max 7 accounts on the grid."*
   MEASURED at 390 before the fix: `ROWSH=0`, `ROWSVIS=0`. Not "a bit short" -
   the box had no height at all, which is why one clipped row showed.

   ⚠⚠ THE CAUSE IS `flex: 1 1 0` MEETING AN AUTO-HEIGHT PARENT. `flex-basis:
   0` says "I want NO height, give me a share of the SPARE space". In the
   multi-column shell that is exactly right - the card is a stretched grid
   item, so there IS spare space and the rows take all of it (the owner's rule
   that the list matches the account card). But on 01-Screen the shell is ONE
   column, nothing stretches the card, its height is its content, and a flex
   column with no spare space has none to give. Basis 0 + no spare = 0.
   ⚠ `min-height: 0` a line below MADE IT LEGAL to collapse to nothing. The
   pair is right in one layout and fatal in the other.

   So the base - which is 01-Screen - sizes to content and caps at seven rows;
   the multi-column state restores `1 1 0`.

   ⚠⚠ 232px IS SEVEN MEASURED ROWS, AND IT IS NOT 7 x 32. The first row is
   **32.25px** and every row after it is **33.25px** - the extra pixel is the
   separator border, which the first row does not have. So seven rows are
   32.25 + 6 x 33.25 = **231.75px**, rounded up to 232. The first attempt used
   224 (7 x the 32 the probe printed after rounding) and showed **SIX** rows -
   a reminder that a rounded measurement is not a measurement.
   ⚠ If a long name ever wraps, fewer than seven show and the box scrolls -
   which is still "max 7", and honest about it. */
/* ⚠ v31.47 (batch244) - THE NEW-ACCOUNT ROW, in the list card under the count.
   One field and one button on one line, because that is the whole form: the
   code is allocated and the type is the chip you are standing on.
   ⚠⚠ IT WRAPS RATHER THAN SQUEEZING. On 01-Screen the list card is 350px wide
   and a 1000-character name field beside a button would leave neither usable -
   `flex-wrap` lets the button drop to its own line instead of the input
   collapsing to nothing. `min-width: 0` on the input is the half that actually
   permits it (a flex item refuses to shrink below its content without it) -
   the same pair `.acc-rows` documents below. */
.acc-new {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 10px;
}

    .acc-new .input {
        flex: 1 1 140px;
        min-width: 0;
    }

    .acc-new .btn {
        flex: 0 0 auto;
    }

.acc-rows {
    flex: 0 1 auto;
    max-height: 232px;
    min-height: 0;
    min-width: 0;
    overflow-y: auto;
    overflow-x: hidden;
}

/* ⚠ FROM TWO COLUMNS UP THE CARD IS A STRETCHED GRID ITEM AGAIN, so the rows
   go back to taking the leftover - the owner's "as many rows as is needed to
   fill the height - minus pager". The cap comes off with it: capping a
   stretched card would leave a gap under the list instead of rows in it. */
@container accshell (min-width: 600px) {
    .acc-rows {
        flex: 1 1 0;
        max-height: none;
    }
}

/* ⚠⚠⚠ v31.37 (owner: "make the column width for Account numbers aligned for
   all rows"). EVERY ROW IS ITS OWN GRID, so `auto` sized each code column to
   ITS OWN code and the names started at a different x on every line.
   A FIXED first track fixes it outright - the same number on every row is
   aligned by construction - and `@supports (grid-template-columns: subgrid)`
   below then upgrades it so a long code widens the column FOR ALL ROWS
   instead of ellipsing. Both paths align; the second is just kinder. */
.acc-row {
    display: grid;
    grid-template-columns: 7.6em minmax(0, 1fr) 9px;
    align-items: center;
    gap: 10px;
    padding: 6px;
    border-radius: var(--r-card);
    text-decoration: none;
    color: var(--nv-ink-2);
    font-size: 13.5px;
    font-weight: 200;
}

    .acc-row + .acc-row {
        border-top: 1px solid rgba(104,152,232,.10);
    }

    .acc-row:hover {
        background: rgba(104,152,232,.08);
    }

    .acc-row.on {
        background: rgba(232,167,92,.12);
        color: var(--nv-gold-bright);
    }

    .acc-row .code {
        color: var(--nv-gold);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        /* the codes are read as columns of digits - line them up */
        font-variant-numeric: tabular-nums;
    }

/* ⚠ THE UPGRADE. `.acc-rows` owns the tracks and each row borrows them, so the
   code column is as wide as the WIDEST code in the list and no wider. Where
   subgrid is missing nothing here applies and the fixed 7.6em above still
   aligns every row. */
@supports (grid-template-columns: subgrid) {
    /* ⚠⚠⚠ v31.40 - THE HALF-MOON DOT, AND WHY IT WAS HALF.
       A SUBGRID ITEM'S OWN PADDING DOES NOT MOVE THE PARENT'S TRACKS. The row
       carried `padding: 6px`, so its content box was 12px narrower than the
       tracks it was borrowing - and the last track, which is exactly the width
       of the 9px dot, ran 6px past the row's right edge. `.acc-rows` then
       clipped it with `overflow-x: hidden` and the reader saw a half circle.
       ⚠ THE FIX IS TO MOVE THE PADDING UP: the ROWS box carries the inset, the
       row itself keeps only its vertical padding, and the dot track is widened
       so the circle has room to sit centred rather than flush against the cut.
       ⚠ THE BASE (no-subgrid) RULE IS UNTOUCHED - there the row is its own
       grid, its padding is its own business, and nothing was ever clipped. */
    .acc-rows {
        display: grid;
        grid-template-columns: max-content minmax(0, 1fr) 14px;
        align-content: start;
        padding: 0 6px;
    }

        .acc-rows > .acc-row {
            grid-column: 1 / -1;
            grid-template-columns: subgrid;
            padding-left: 0;
            padding-right: 0;
        }

        /* the empty-list line is not a row of three */
        .acc-rows > .muted {
            grid-column: 1 / -1;
        }
}

/* ⚠ AND THE PIP SITS CENTRED IN ITS TRACK, wherever that track ends up. A
   circle flush against a clipping edge is one rounding error from being a
   half circle again. */
.acc-row > .acc-dot {
    justify-self: center;
}

    /* ⚠ `min-width: 0` ALONE IS NOT ENOUGH on a flex item - it must also be
       told it may shrink - but this is a GRID item and `minmax(0, 1fr)` in
       the template above has already said so, which is why the ellipsis
       actually draws here. */
    .acc-row .nm {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

.acc-pager {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 8px;
}

    .acc-pager > .chip {
        justify-content: center;
        min-width: 0;
    }

    .acc-pager > .chip[aria-disabled="true"] {
        opacity: .38;
        pointer-events: none;
    }

/* ── 4. THE STATUS PIP ────────────────────────────────────────────────────
   ⚠ 1 Active and 9 DEFAULT ARE BOTH IN USE - never `> 1`. That exact
   mistake dropped a live bank account off the dashboard in v26.9, so the
   two live states share a colour and the two idle ones do not. */
.acc-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    display: inline-block;
    flex: 0 0 auto;
    background: var(--nv-ink-3);
}

    .acc-dot.live,
    .acc-dot.def {
        background: var(--acc-green, #3fca85);
    }

    .acc-dot.susp {
        background: var(--nv-gold);
    }

    .acc-dot.closed {
        background: rgba(107,118,144,.55);
    }

/* ── 5. THE ACCOUNT ───────────────────────────────────────────────────── */
.acc-detail {
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
}

    /* ⚠ v31.35 - `.acc-wide` NO LONGER SPANS. With two field columns the
       account name and its code sit side by side, which is the pairing a
       reader wants; a spanning name pushed the code onto a row of its own
       and left half of it empty. The class is kept as the hook for the day
       the card is wide enough to earn three columns. */
    .acc-detail .acc-wide {
        grid-column: auto;
    }

.acc-code {
    color: var(--nv-gold);
    font-variant-numeric: tabular-nums;
    letter-spacing: .3px;
}

/* the status field is a value, not an input, so it borrows the input's box
   and adds the pip - one field height across the whole row either way */
.acc-stat {
    display: flex;
    align-items: center;
    gap: 8px;
}

.acc-sec {
    margin: 6px 0 0;
    display: flex;
    align-items: center;
    gap: 8px;
}

/* ── 6. ADDITIONAL ADDRESSES ──────────────────────────────────────────────
   ⚠ THE TYPE IS SHOWN AS A NUMBER, AND THAT IS DELIBERATE. `RefAddressType`
   is not yet proven in v2 (diag-accounts.sql, set 3), so the page shows the
   type the ledger stores rather than a name we invented. A number the
   reader can see beats a name that might be wrong.
   ⚠ THE PILLS ARE EQUAL WIDTH (owner: *"The pills for address type, must be
   same width when they are shown."*) - one `max-content` column shared by
   every row, which is the same sentence as the chips again. */
.acc-addrs {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 6px 10px;
    align-items: center;
}

    .acc-addrs > .acc-atype {
        justify-content: center;
        width: 100%;
    }

    .acc-addrs > .acc-aline {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 13.5px;
        color: var(--nv-ink-2);
    }

/* ── 7. THE READ-ONLY NOTE ────────────────────────────────────────────────
   A page with no Save button owes its reader an explanation, and a tooltip
   is not one. */
/*  ⚠⚠⚠ v31.51 - THIS RULE WAS DELETED AND PUT STRAIGHT BACK, AND THE MISTAKE
    IS WORTH MORE THAN THE RULE. The standing "reading only" banner went in
    v31.51 (the owner asked whether we were still waiting for editing - we were
    not, it shipped in 1b), and this rule went with it as dead weight.

    **IT WAS NOT DEAD.** `.acc-ro-note` had THREE elements, not one: the
    Periods tab's "this account carries its own window" note and the AI Create
    tab's "the layout is here" note are both still on the page. Deleting the
    rule would have shipped two UNSTYLED notes - and an unstyled element still
    renders, so nothing would have looked broken enough to notice.

    ⚠ THE LESSON, WHICH IS THE MIRROR OF THE `.acc-new` ONE: before deleting a
    rule, COUNT THE ELEMENTS THAT USE IT, not the one you just removed. Caught
    by grepping the class on the device AFTER the commit, which is later than
    it should have been. */
.acc-ro-note {
    display: flex;
    flex-direction: column;
    gap: 3px;
    margin-top: auto;
    padding: 10px 12px;
    border-radius: var(--r-card);
    border: 1px dashed rgba(232,167,92,.32);
    background: rgba(232,167,92,.06);
    font-size: 13px;
    color: var(--nv-ink-2);
}

/* ── 8. THE RIGHT COLUMN ──────────────────────────────────────────────── */
/* ⚠⚠ THE SIDE COLUMN IS TWO SHAPES, NOT ONE.
   Below 940px of container it spans the FULL WIDTH under the other two cards -
   at which point stacking its cards one above the other wastes all that width
   and makes the page enormous (measured 1975px at a 1024 viewport). Laid out
   as an auto-fitting grid it uses the width instead of the height.
   From 940px it is a real 300px column again, and a single track is right. */
.acc-side {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 14px;
    align-content: start;
    /* ⚠ MEASURED: `align-content` alone was not enough. A grid ITEM stretches
       to its ROW, so a collapsed card sitting beside an open one grew to 364px
       of empty box. `align-items: start` is what keeps a folded card folded. */
    align-items: start;
    min-width: 0;
}

@container accshell (min-width: 900px) {
    .acc-side {
        grid-template-columns: minmax(0, 1fr);
    }
}

.acc-ba-hint {
    font-size: 12.5px;
    margin-bottom: 6px;
}

/* ⚠⚠ THIS LIST IS NOT DECORATION. `LedgerPosting.RequiredBaAsync` reads the
   same pair and REFUSES A POSTING whose line leaves the analysis blank, so a
   required slot is drawn as a state and not as a tick alone.
   ⚠ A slot the entity does not use is GREYED, NEVER HIDDEN: v1 lists all
   twenty, and hiding them would make two entities' pages look like different
   software. */
.acc-ba {
    display: grid;
    grid-template-columns: minmax(0, 1fr) max-content max-content;
    align-items: center;
    gap: 8px;
    padding: 5px 6px;
    font-size: 13.5px;
    font-weight: 200;
    color: var(--nv-ink-2);
}

    .acc-ba + .acc-ba {
        border-top: 1px solid rgba(104,152,232,.10);
    }

    .acc-ba .nm {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .acc-ba .st {
        font-size: 12px;
    }

    .acc-ba .req {
        font-size: 12px;
        color: var(--nv-gold-bright);
    }

    .acc-ba .tick {
        color: var(--acc-green, #3fca85);
    }

    .acc-ba.idle {
        opacity: .45;
    }

    .acc-ba.on .nm {
        color: var(--nv-ink);
    }

/* ── 9. THE MORE SHEET ────────────────────────────────────────────────────
   Server-rendered, so every type is a real link and the sheet carries no
   data of its own. `.t-leaf` is the app's own picker row - the same one the
   VAT panel uses - so this only adds the two children that row does not
   already know about. */
/* ⚠⚠⚠ v31.41 (batch238, owner) - THE SHEET RAN OFF THE TOP OF 05-SCREEN.
   Owner: *"For 05-Screen ONLY please note that the accMoreSheet top margin
   disappears into the top viewport."* MEASURED at 1920x1080: the sheet's top
   edge sat at **-17px** - above the window - taking the "Account types."
   heading and the first rows with it. At a shorter 1920 window it is far
   worse; the owner's screenshot loses roughly a third of the list.

   ⚠⚠ THE CAUSE IS THAT `60vh` DOES NOT KNOW ABOUT THE 340px. `.mail-sheet` is
   pinned to the BOTTOM and pushed up by a per-screen margin (20 / 40 / 134 /
   **340** at 1920), then grows UPWARD. So the room this list actually has is
   not 60% of the window - it is the window MINUS that margin MINUS the sheet's
   own grab handle, heading and padding. At 1920x1080 that is 1080-340-~110 =
   630px of room for a list asking for 648. It took the difference off the top,
   silently, because nothing clips a fixed box at the viewport edge.

   ⚠ THE FIX ASKS THE SHEET HOW FAR UP IT IS instead of guessing per
   breakpoint. `--sheet-gap` is set beside every one of those four margins
   (base here, the other three in responsive.css) and inherits down. `min()`
   keeps today's 60vh wherever it already fits, so 01-04-Screen do not move a
   pixel - only the screen that was broken changes.
   ⚠⚠ AND IT IS NOT A 1920-ONLY RULE ON PURPOSE. Written as one it would leave
   the same fault latent at 1366 the moment someone opens a short window. This
   shape cannot overflow at any size. */
.acc-more {
    display: flex;
    flex-direction: column;
    max-height: min(60vh, calc(100vh - var(--sheet-gap, 20px) - 130px));
    overflow-y: auto;
    overflow-x: hidden;
}

    .acc-more .t-leaf b {
        color: var(--nv-gold);
        font-weight: 400;
        min-width: 34px;
    }

    .acc-more .t-leaf .nm {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/* ── 10. THE RIGHT COLUMN'S OWN CARDS (v31.36, batch233) ──────────────────
   The locked mock-up's stack: Find an account · Analysis · Business analysis
   · the account's open range (slice 1b) · Create from document (slice 1b). */
.acc-find-card .acc-find {
    margin: 0 0 8px;
}

    .acc-find-card .acc-find .input {
        width: 100%;
    }

.acc-find-card .acc-count {
    margin-top: 8px;
}

/*  ⚠⚠⚠ v31.58 (batch255, owner) - THE ADDRESS FLIP.
    Owner: *"Please implement the 'flip' for addresses. The second flip1 will
    be the address and flip 2 will be additional addresses. (no border for the
    flip)."*

    ⚠⚠ THIS IS A VARIANT OF THE TOOLKIT FLIP, NOT A SECOND ONE. `.flip`,
    `.flip-inner`, `.flip-face` and `.flip-dots` above are the dashboard's, and
    `_NvFlip.cshtml` drives all of them. Three things have to change for a pair
    of FORMS rather than a pair of fixed cards, and nothing else does.

    ⚠⚠⚠ 1. THE FIXED HEIGHT HAS TO GO. `.flip-inner` is `height: 250px` because
    Bank Balances is 250px tall by the owner's own ruling; an address form is
    not, and at 250px the Save button would sit outside the card.

    ⚠⚠⚠ 2. THE FACES STACK ON ONE GRID CELL INSTEAD OF BEING ABSOLUTE. The
    toolkit uses `position: absolute; inset: 0`, which takes both faces out of
    flow - so with the fixed height gone the container would collapse to
    nothing. Placing both in `grid-area: 1 / 1` keeps them stacked AND in flow,
    so **the taller face sets the height** and neither is ever clipped.
    ⚠ `backface-visibility: hidden` still does the hiding; that is unchanged.

    ⚠ 3. NO BORDER, NO FILL, NO CLIP - the owner's "(no border for the flip)".
    `.flip .card` sets `overflow: hidden` so the dashboard's fixed faces cannot
    spill; here that would clip a form, so it goes back to visible. */
.acc-flip .flip-inner {
    min-height: 0;
    height: auto;
    display: grid;
}

    .acc-flip .flip-face {
        position: relative;
        inset: auto;
        grid-area: 1 / 1;
    }

    .acc-flip .card,
    .acc-flip .flip-face > .card {
        border: 0;
        background: transparent;
        box-shadow: none;
        padding: 0;
        overflow: visible;
    }

    /*  ⚠⚠ v31.59 (owner) - THE FACE SWITCHES SIT AT THE TOP OF THE CARD.
        `.flip-dots` on the dashboard uses `margin-top: auto` to pin itself to
        the foot of a 250px card, where it is always in view. These faces are
        forms six hundred pixels tall, so the foot is below the fold and the
        second face may as well not exist - which is exactly how the additional
        addresses came to read as "completely ignored". `margin-top: 0` undoes
        the pin; the order in the markup does the rest. */
    .acc-flip .flip-dots {
        margin-top: 0;
        margin-bottom: 12px;
        padding-top: 0;
    }

/*  ⚠⚠⚠ v31.61 (owner, an arrow on the screenshot from the switch to the
    Mobile box) - THE SMS PERMISSION SITS ON THE FIELD IT GOVERNS.

    It used to be its own row at the foot of the card, six fields below the
    number it applies to, where it reads as a setting for the whole address.
    Beside the mobile it reads as what it is: may we text THIS number.

    ⚠ THE CONTROL IS UNCHANGED - the same `.acc-sw`, the same measured 34x19
    the owner chose for Business Analysis. Only its position moved, so the two
    switches on this page still say "this is the same kind of thing".
    ⚠⚠ THE INPUT KEEPS ITS FULL WIDTH: the switch is 34px and the row is a
    grid of `minmax(0, 1fr)` plus `auto`, so a long number shrinks the box
    rather than pushing the switch out of the cell. */
.acc-mobrow {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
}

    .acc-mobrow > .input {
        width: 100%;
        min-width: 0;
    }

/*  the one-line consent caption beside the mobile. ⚠ 12px and `.muted` - the
    owner asked for muted small text and this is the same size the rest of
    this card's hints use. */
.acc-smshint {
    display: block;
    margin-top: 4px;
    font-size: 12px;
}

/*  ⚠⚠⚠ v31.62 (owner) - THE CAPTION IS IN THE NEXT COLUMN NOW.
    Owner: *"'Allow SMS to this contact' should be in the next column after the
    Allow SMS checkbox below the telephone column."*

    v31.61 moved the SWITCH beside the number, which was the first half of it,
    and left the caption UNDER the pair - a third line in a cell that already
    held two, which pushed the rest of that grid column down by its height.

    ⚠ THE CELL SPANS BOTH COLUMNS AND SPLITS ITSELF. `.acc-fields` is two
    equal columns; this one wrapper takes the whole row and then places its
    three children by hand, so the mobile box keeps EXACTLY the half-width it
    had and the caption occupies space that was empty anyway - the Email under
    it spans, and therefore always began a new row.
    ⚠⚠ EVERY CHILD IS PLACED EXPLICITLY, none by auto-flow. `.acc-pair` had to
    learn this the hard way: a two-column grid auto-places children ROW BY ROW,
    which here would put the caption beside the HINT rather than beside the
    control - measured at 32px too high.
    ⚠ HIGHER SPECIFICITY THAN `.acc-smshint` ABOVE (0,2,0 against 0,1,0), so
    the `margin-top` it sets for the old stacked layout is overridden here and
    not deleted there: the class is still used on its own elsewhere. */
.fld-wrap.acc-mobwrap {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: auto auto;
    gap: 0 12px;
}

    .acc-mobwrap > .fl-hint {
        grid-column: 1;
        grid-row: 1;
    }

    .acc-mobwrap > .acc-mobrow {
        grid-column: 1;
        grid-row: 2;
    }

    .acc-mobwrap > .acc-smshint {
        grid-column: 2;
        grid-row: 2;
        align-self: center;
        margin-top: 0;
    }

/*  ⚠⚠ v31.59 (owner) - TWO FIELDS IN ONE CELL.
    Owner: *"Post Code and Country fields should share the same col/row space
    by making them half their current sizes. That saves us a row."*

    ⚠ `.fld-wrap` is one cell of `.acc-fields`; `.acc-pair` splits that cell
    into two equal halves, each carrying its own hint above its own control.
    The pair therefore costs ONE grid cell rather than two, which is the row
    the owner wanted back.
    ⚠ `minmax(0, 1fr)` on both, not `1fr`: a long country name in the picker
    would otherwise refuse to shrink and push the post code out of the cell -
    the same rule the search-hit row uses. */
.fld-wrap.acc-pair {
    display: grid;
    /*  ⚠⚠⚠ IT FILLS DOWN, THEN ACROSS - and that is the whole rule.
        The cell holds four children in source order: hint, control, hint,
        control. A plain two-column grid auto-places them ROW BY ROW, which
        puts the first hint beside the first control and the second hint beside
        the second - two stacked rows of mismatched things, MEASURED as a 32px
        vertical offset between the post code and the country.
        `grid-auto-flow: column` over two explicit rows fills column one (hint,
        control) and then column two, which is the pair the owner asked for. */
    grid-template-rows: auto auto;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 0 8px;
}

    .fld-wrap.acc-pair > .input,
    .fld-wrap.acc-pair > select.input {
        width: 100%;
        min-width: 0;
    }

/*  one additional address on the back face. ⚠ A HAIRLINE ABOVE EACH ONE AFTER
    THE FIRST, never a box: the owner asked for no border on the flip, and a
    card inside a borderless card is exactly the furniture that removal was
    meant to lose. */
.acc-xaddr + .acc-xaddr {
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid rgba(112,160,238,.18);
}

/*  ⚠⚠⚠ v31.66 (batch263, owner): *"Where Additional address exists, multiple
    textboxes exists as shown in the diagram and the page height explodes."*

    Each additional address and the eleven-field Add form are `details`
    elements now, closed by default. The head is the `summary`.

    ⚠ THE NATIVE MARKER IS REMOVED IN BOTH DIALECTS - `list-style: none` for
    the standard, `::-webkit-details-marker` for Safari and older WebKit - and
    replaced by the same gold twisty the tree uses, so a fold on this page
    reads as the same kind of control wherever it appears.
    ⚠⚠ `cursor: pointer` ON THE SUMMARY ONLY. The row under it is a form; a
    pointer over a form says "this is clickable" about something that is not. */
.acc-xhead {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}

details.acc-xaddr > summary.acc-xhead,
details.acc-xnew > summary.acc-xnewhead {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 8px;
}

    details.acc-xaddr > summary::-webkit-details-marker,
    details.acc-xnew > summary::-webkit-details-marker {
        display: none;
    }

    /*  the fold's own heading keeps the section type but loses the margin that
        would push the twisty off its own line. */
    details.acc-xnew > summary.acc-xnewhead > .acc-sec {
        margin: 0;
    }

/*  ⚠ THE SAME GLYPH AND THE SAME 12px BOX AS THE TREE'S TWISTY, including the
    `nowrap` that stops a font without U+25B8 from wrapping it to a second line
    and growing the row - measured at 46px instead of 28px when that happened
    in the tree. */
.acc-xtw {
    flex: 0 0 auto;
    width: 12px;
    color: var(--nv-gold);
    font-size: 11px;
    line-height: 18px;
    white-space: nowrap;
    overflow: hidden;
    text-align: center;
    transition: transform .15s ease;
}

    details[open] > summary > .acc-xtw {
        transform: rotate(90deg);
    }

/*  ⚠ WHICH ADDRESS THIS FOLD IS. A column of identical type pills is a column
    of identical rows, and the reader would open every one to find the one they
    meant. It takes the rest of the line and ellipses - a summary that wraps
    turns a one-line fold back into a two-line one. */
/*  ⚠⚠⚠ v31.68 (batch265, owner) - THE DELETE, ARMED AND UNARMED.
    Unarmed it is the icon alone, muted, and it takes no more room in the
    summary than the twisty does. Armed it turns red and says the word, so the
    change is impossible to miss on a row of otherwise identical rows.

    ⚠ THE WORD IS `max-width: 0` UNARMED, NOT `display: none`. A word that
    appears from nothing shifts everything beside it; a word that grows out of
    zero width pushes the summary's text along predictably, and it can be
    animated. Measured: the summary keeps one line at every width in the range.
    ⚠⚠ `margin-left: auto` PUTS IT AT THE FAR END of the summary, away from the
    twisty that opens the row. The two controls on this line do opposite things
    and must not be adjacent. */
.acc-xdel {
    flex: 0 0 auto;
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: 0;
    padding: 2px 4px;
    border-radius: 6px;
    color: var(--nv-ink-3);
    cursor: pointer;
    font: inherit;
    font-size: 12px;
    line-height: 18px;
}

    .acc-xdel:hover {
        color: var(--nv-ink);
    }

    .acc-xdel > .acc-xdelw {
        max-width: 0;
        overflow: hidden;
        white-space: nowrap;
        transition: max-width .15s ease;
    }

    .acc-xdel[data-arm="1"] {
        color: #e79a9a;
        background: rgba(217,101,101,.12);
    }

        .acc-xdel[data-arm="1"] > .acc-xdelw {
            max-width: 8em;
        }

.acc-xsum {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
    font-weight: 200;
    color: var(--nv-ink-3);
}

/*  ⚠ THE FOLD'S CONTENT GETS ITS OWN BREATHING ROOM. `.acc-xhead` carries the
    8px below it while closed; open, the fields need the same gap from the head
    they had when this was a plain div. */
details.acc-xnew[open] > .acc-fields {
    margin-top: 8px;
    }

/*  ⚠ THE SEARCH RESULTS. Four columns that collapse to two on a narrow
    container - `.acc-wrap` carries `container-type: inline-size`, so this asks
    the COLUMN's width and not the window's, which on this page are different
    numbers and move in opposite directions. */
.acc-findres {
    margin-bottom: 14px;
}

.acc-fhit {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 2fr) minmax(0, 1fr);
    gap: 4px 10px;
    align-items: baseline;
    padding: 6px 0;
    border-top: 1px solid rgba(112,160,238,.14);
}

    .acc-fhit .nm {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .acc-fhit .cd,
    .acc-fhit .ml,
    .acc-fhit .en {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .acc-fhit .en {
        text-align: right;
        color: var(--nv-gold);
    }

@container (max-width: 620px) {
    .acc-fhit {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

/*  ⚠⚠ v31.58 (batch255, owner) - THE PERIODS BUTTONS ARE TWO FULL ROWS.
    Owner: *"Please Make the buttons 'Save Range' and 'Clear all four'
    seperate rows, each haveing full row width."*

    ⚠ THIS REPLACES v31.56's EQUAL-WIDTH PAIR, and it retires the question
    rather than answering it again: a full-width button is the same width as
    the one above it by construction, in every one of the five languages, with
    nothing measured and nothing to keep in step.

    ⚠ THE SECOND BUTTON IS INSIDE ITS OWN FORM - it has to be, so it can carry
    `PrdClear` without the four date fields riding along. That form is
    therefore the grid item, not the button, which is why the button below it
    takes `width: 100%` rather than being sized directly. */
.acc-prd-acts {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    width: 100%;
}

    .acc-prd-acts > form {
        margin: 0;
        min-width: 0;
    }

        .acc-prd-acts > .btn,
        .acc-prd-acts > form > .btn {
            width: 100%;
        }

/* one row of the Analysis summary card - label left, state right */
.acc-arow {
    display: grid;
    grid-template-columns: minmax(0, 1fr) max-content;
    align-items: center;
    gap: 8px;
    padding: 6px;
    font-size: 13.5px;
    font-weight: 200;
    color: var(--nv-ink-2);
}

    .acc-arow + .acc-arow {
        border-top: 1px solid rgba(104,152,232,.10);
    }

    .acc-arow .nm {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .acc-arow .st {
        color: var(--nv-gold-bright);
        font-size: 12.5px;
    }

    .acc-arow .st.muted {
        color: var(--nv-ink-3);
    }

/* ── 11. THE ACCOUNT CARD'S FIELDS (v31.37, batch234) ─────────────────────
   ⚠⚠⚠ THE OWNER DREW THESE PROPORTIONS AND THEY APPLY AT EVERY SCREEN SIZE:
   two equal columns, with the full-width fields spanning both. That is the
   whole rule - no breakpoint, no third column, nothing that changes as the
   window moves. `.fin-grid3` is NOT used here: it is three fixed fractions
   down to 768px of VIEWPORT, which says nothing about the width of the CARD
   these fields are actually drawn in. */
.acc-fields {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
}

    .acc-fields > .acc-span2 {
        grid-column: 1 / -1;
    }

/* ── 12. THE REMAINING RIGHT-COLUMN CARDS (v31.37) ────────────────────── */

/* the in-use / not-in-use switch reuses the filter grid, so the two chips
   are the same width as each other and as every other chip on the page */
.acc-baview {
    margin-bottom: 8px;
}

.acc-bapager {
    margin-top: 8px;
}

.acc-ba-none {
    padding: 8px 6px;
    font-size: 13px;
}

/* the account's own open range - real values, controls not yet live */
.acc-range .acc-clear4,
.acc-doc .acc-docbtn {
    width: 100%;
    margin-top: 10px;
}

.acc-range .acc-clear4[disabled],
.acc-doc .acc-docbtn[disabled] {
    opacity: .42;
    cursor: default;
}

/* the two "not yet" badges. `.acc-soon` is a state, `.acc-ai` is a feature -
   different colours on purpose, so one is not read as the other. */
.acc-soon {
    border-left-color: var(--nv-ink-3);
    color: var(--nv-ink-3);
}

.acc-ai {
    border-left-color: var(--nv-gold);
    color: var(--nv-gold-bright);
}

.acc-sec .pill {
    margin-left: auto;
}

/* ── 13. THE ACCOUNT'S TABS (v31.39, batch236) ────────────────────────────
   Owner, after clicking the built prototype: *"definitely like to proceed with
   option 3 immediately."*

   ⚠⚠⚠ THE v31.38 FOLD RULES ARE DELETED, NOT LEFT BEHIND. The cards they
   folded now live in these tabs, so a `.acc-fold` selector matching nothing
   would be dead CSS pretending to be a feature - and the owner's own ruling on
   dead surfaces (v31.21) is that they get deleted rather than hidden.

   MEASURED, with every card built:
       all cards stacked (the locked design) ... 1911px
       right column folded (v31.38) ............ 1174px
       tabbed account (this) ................... see the block below

   ⚠ THIS IS v1's OWN SHAPE. The method that decides which sections an account
   shows is called `EShowAccBasedTabs`. v1 has always been a tabbed account
   card; v2 had flattened it into one scroll. */

/* the strip is the same equal-width wrapping grid as every other chip row on
   this page, so six tabs share one width and wrap rather than overflow */
/*  ⚠ v31.60 (owner): *"Place a horizontal line below tab selection."*

    ⚠⚠ IT GOES IN **THIS** RULE, NOT A NEW BLOCK APPENDED EARLIER.
    A second `.acc-tabs { }` written ABOVE this one has the SAME specificity,
    so this one wins on source order and the border would silently never
    appear - the CSS-ordering trap this project has hit twice, and the source
    would have read as done. When a rule for a selector already exists, add to
    it; only append a new block when nothing below it can beat it.

    ⚠ The line sits on the STRIP rather than on the panel below it, because
    every panel carries `hidden` and a border on a hidden element draws
    nothing. Same hairline as the additional-address separator and the
    search-hit rows, so the page reads as one family. */
/*  ⚠⚠ v31.157 — `.nv-tabs` IS THE TOOLKIT NAME FOR THIS EXACT CONTROL.
    Account Setup had it first as `.acc-tabs`; Notes needed the same thing
    and I invented `.nt-pills` instead — a second visual language for one
    job, which the owner spotted immediately. Both names now share ONE
    rule, so there is nothing to keep in step, and the next page that
    needs tabs uses `.nv-tabs` rather than copying Account Setup by hand. */
.acc-tabs, .nv-tabs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
    gap: 8px;
    border-bottom: 1px solid rgba(112,160,238,.22);
    padding-bottom: 10px;
    margin-bottom: 14px;
}

    .acc-tabs > .chip, .nv-tabs > .chip {
        justify-content: center;
        min-width: 0;
    }

        .acc-tabs > .chip > span, .nv-tabs > .chip > span {
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

/* ⚠ THE PANELS ARE HIDDEN BY THE `hidden` ATTRIBUTE, not by a class here.
   The browser honours `hidden` without a stylesheet, so a panel cannot be left
   showing by a CSS load failure. This rule only exists because `display: grid`
   and friends elsewhere would otherwise beat `hidden`'s UA `display: none`. */
.acc-panel[hidden] {
    display: none;
}

/* ⚠ THE ACTIONS SIT BELOW THE TABS, NOT INSIDE ONE - they act on the ACCOUNT,
   not on the section being read, and a Save that moved when you changed tab
   would be a Save people stop trusting.
   ⚠ EQUAL WIDTHS: the fourth of the owner's five width rules. Save · Copy ·
   Cancel share one track; Copy sits BETWEEN the other two, which is the
   owner's locked ordering. */
.acc-acts {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    margin-top: 14px;
}

    .acc-acts > .btn {
        width: 100%;
    }

    .acc-acts > .btn[disabled] {
        opacity: .42;
        cursor: default;
    }

/* the two document buttons share a row and a width */
.acc-docrow {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 8px;
    margin-top: 10px;
}

    .acc-docrow > .btn {
        width: 100%;
    }

/* a section heading INSIDE a panel is smaller than the card's own - it is a
   sub-heading now, not a card title */
.acc-panel h2.acc-sec {
    font-size: 17px;
    margin: 16px 0 8px;
}

    .acc-panel > h2.acc-sec:first-child {
        margin-top: 0;
    }

/* ── 14. THE STICKY ACCOUNT HEAD + THE AI GLYPH (v31.40, batch237) ────────
   Owner: *"a card that contains a label with ONLY the selected Account code +
   Name. This will be Static/Sticky above the middle column tabs."*

   ⚠⚠ THIS IS THE ANSWER TO THE ONE COST OF THE TABS. Moving the address off
   the Details tab meant a reader could be looking at a bank account or an open
   range with no reminder of WHOSE. The head never leaves the screen, so the
   answer is always there.
   ⚠⚠⚠ `position: sticky` STICKS TO THE NEAREST SCROLLING ANCESTOR, and it
   needs a resolved `top`. Here that ancestor is `.scroll`, the page's own
   scroller. **`.acc-detail` MUST NOT GAIN `overflow`** - it would become the
   scroll container, and since that card is exactly as tall as its content the
   head would then never stick at all. It would look like the rule was ignored.
   ⚠ AND IT NEEDS TO PAINT OVER WHAT SCROLLS BEHIND IT: the card's own
   background is a translucent gradient, so the head carries an opaque one of
   its own plus a z-index. Without that the tab strip shows through it. */
.acc-head-sticky {
    position: sticky;
    /* ⚠⚠⚠ 68px, AND IT IS NOT A GUESS. MEASURED: the scrolling ancestor of
       this head is THE VIEWPORT - every box between it and the document is
       `overflow: visible`, `.scroll` included, despite the name. And
       `.screen-head` is ALREADY `position: sticky; top: 0` and measures
       **68px** at every width from 390 to 1920 with the entity chip on it.
       So `top: 0` would have pinned this head exactly UNDERNEATH the page
       header, where nobody would ever see it - the one thing it exists to
       avoid.
       ⚠ THE HOUSE HARDCODES THESE (`.mail-bar { top: 156px }`), so this
       follows suit rather than inventing a token - but it is behind a
       variable so there is ONE number to change if the header ever grows.
       To re-measure: `getComputedStyle` the `.screen-head` height in the
       static harness; it is stable across widths. */
    top: var(--acc-headtop, 68px);
    z-index: 3;
    display: flex;
    align-items: baseline;
    gap: 10px;
    min-width: 0;
    margin: -14px -14px 12px;      /* out to the card's edges */
    padding: 12px 14px;
    border-bottom: 1px solid rgba(104,152,232,.16);
    /* ⚠⚠⚠ v31.41 (batch238, owner): THE FILL AND THE RADIUS ARE GONE.
       Owner, verbatim: *"remove background and border radius in
       .acc-head-sticky"* - they read as a card inside a card, and the rounded
       top drew a second lid a few pixels below the real one.

       ⚠⚠ THE CONSEQUENCE, NAMED AND THEN MEASURED. A sticky box with no fill
       lets whatever scrolls behind it show THROUGH it. That is not a theory:
       the v31.40 comment above says in as many words that the tab strip would
       show through, which is why the fill was there.
       WHAT SAVED IT is that this head does not overlap its own card's body -
       it is the card's FIRST child and the tab strip sits below it, so at the
       moment it unsticks from the card the only thing under it is the page
       background. MEASURED with the page scrolled to its own bottom at every
       screen: nothing crosses it (`BEHIND=0` at 390/768/1280/1433/1920).
       ⚠ THAT IS A FACT ABOUT THE CURRENT ORDER OF THIS CARD, NOT A RULE. If
       anything is ever placed ABOVE the head inside `.acc-detail`, or the head
       moves down the card, the fill has to come back - one line, and it is
       this one:  background: var(--nv-surface, #101a30);
       ⚠ The bottom border STAYS. Without a fill it is the only thing that
       separates the head from the tabs when the two meet mid-scroll. */
}

    .acc-hs-code {
        flex: 0 0 auto;
        color: var(--nv-gold);
        font-variant-numeric: tabular-nums;
        letter-spacing: .3px;
        font-size: 15px;
    }

    /* ⚠ THE NAME TAKES WHAT IS LEFT AND ELLIPSES. It is the half that can be
       a thousand characters - `RefAccount.AccountName` is nvarchar(1000). */
    .acc-hs-name {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 15px;
        font-weight: 200;
        color: var(--nv-ink);
    }

/* the AI mark on the Create tab and its heading */
.acc-aiglyph {
    flex: 0 0 auto;
    color: var(--nv-gold);
}

/* ══ v31.50 (batch247, owner) - THE COUNTRY REPORT GROUPS ═══════════════════
   Owner, with v1's two tree builders attached and a screenshot arrowing a
   chart node into the report-code field. Two things get styling here: the
   NAME that now sits under each of the two mandatory codes, and the BROWSE
   PANEL that lists the country's chart.

   ⚠ IT IS AN INLINE PANEL, NOT AN OVERLAY, and that is a deliberate
   divergence from the screenshot. v1 floats the tree over the form in a
   RadWindow; v2's Groups tab has the room, and an overlay would need its own
   focus trap, its own escape key and its own answer at 390px - three new
   mechanisms to show a list. The tab already scrolls, so the panel simply
   sits under the fields it explains.
   ══════════════════════════════════════════════════════════════════════════ */

/* the resolved name, directly under its code */
.acc-grp-name {
    font-size: 12.5px;
    font-weight: 200;
    color: var(--nv-ink-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/*  ⚠ THE LIVE HALF OF THE DIAGNOSTIC. Shown only when a code answered from
    the tree it was not asked for - i.e. when the two fields are the other way
    round. Gold, because it is information the reader can act on, not an
    error: the name beside it is still the right name. */
.acc-grp-warn {
    font-size: 12px;
    color: var(--nv-gold);
}

/*  v31.188 - THE "USE THIS ONE" BESIDE THE SK/CZ EXAMPLE.
    ⚠ The form is `display: inline` so the control sits INSIDE the banner's
    sentence rather than breaking it onto a line of its own: it is the last
    word of the message, not a second message.
    ⚠⚠ A REAL SUBMIT, not a link with script behind it. It writes, so it posts
    - and it posts the same `CountryRef` handler the tree's own rows post, on
    its own form with its own route values. */
.acc-hintform {
    display: inline;
}

.acc-hintapply {
    background: transparent;
    border: 0;
    padding: 0;
    margin-left: 6px;
    font-family: inherit;
    font-size: inherit;
    font-weight: 200;
    color: var(--nv-gold-bright);
    text-decoration: underline;
    cursor: pointer;
}

    .acc-hintapply:hover {
        color: var(--nv-gold);
    }

/* ⚠ `align-self: start` so the chip is its own width rather than stretching
   the full column the way a flex child otherwise would. */
/*  ⚠ `.acc-grp-browse` RETIRED in v31.51. Owner: *"The button browse the
    chart and browse final account should be against the fields as a 'Search'
    button."* The chip UNDER the field became an `.iconbtn` BESIDE it, so this
    rule has no element left to style. See `.fld-go`. */

/*  ⚠⚠ `.fld-go` - AN INPUT WITH A BUTTON *INSIDE* IT, option B.
    Owner (2026-08-25), choosing from four mock-ups: *"B. Hairline divider - a
    1px rule in the field's own border colour splits the text zone from the
    button zone. Reads unmistakably as a control while staying in one box."*

    v31.51 put the button BESIDE the field; v31.52 moves it in. Three call
    sites: both mandatory report codes and the VIES check.

    ⚠⚠⚠ AN `<input>` CANNOT HOLD A CHILD, so this is a positioned wrapper and
    an absolutely placed button - there is no other way to do it, and every one
    of the four mock-ups used the same mechanic.

    ⚠⚠ `padding-right` IS NOT DECORATION, IT IS THE WHOLE SAFETY. It is what
    keeps the VALUE clear of the button; drop it and a long account name slides
    underneath the glyph and is unreadable with no visible cause. 46px = the
    32px button zone + 12px of air + the 2px inset. MEASURED at 390 / 768 /
    1280 / 1920: no overlap at any width, and the button's box sits inside the
    field's box with 0.00px of centre drift.

    ⚠ THE DIVIDER TAKES THE FIELD'S OWN BORDER COLOUR - `rgba(112,160,238,.30)`,
    the same value `.input` uses - so it reads as part of the control rather
    than as a decoration laid over it. Inset 5px top and bottom so it never
    touches the rounded corners.

    ⚠ NOT `.iconbtn`. That is the 48px toolkit button with its own border and
    fill; inside a 32px field both would fight the field's own edge. The three
    in-field buttons carry `.fld-btn` instead, and the page's BACK button is
    still a real `.iconbtn` - they are different controls and now say so.

    ⚠⚠ v31.56 (batch253) - THE NINTH SITE IS A `submit`, AND THAT IS THE POINT.
    Owner: *"Please implement the hairline divider and button to
    placeholder:fin.accSearch without deprecating the search using return
    key."* The account search now wears one. Because the control is a real
    submit in the form's default-button position, Enter in the field and a
    click on the glyph run the same submit - the return key is not preserved by
    a promise, it is preserved by the element type. A click handler calling
    `submit()` is what would have broken it.
    ⚠ THIS RULE ALREADY COVERED IT with no change: `.fld-btn` sets `border: 0`
    and `background: transparent`, which is exactly what a bare `submit` needs
    to stop looking like a button and start looking like part of the field. */
.fld-go {
    position: relative;
    display: block;
    min-width: 0;
}

    /*  ⚠⚠ `:has(> .fld-btn)` - THE PADDING ONLY EXISTS WHEN THE BUTTON DOES.
        v31.53 took this pattern app-wide, and one of the eight sites renders
        its button conditionally (the invoice line's business-analysis slot -
        a free-format slot has no list to pick from, so no button). Without
        the guard those fields would carry 46px of reserved space for a
        control that was never rendered. Same `:has()` the toolkit already
        uses for `.fin-hscroll`. */
    .fld-go:has(> .fld-btn) .input {
        padding-right: 46px;
    }

.fld-btn {
    position: absolute;
    top: 50%;
    right: 2px;
    transform: translateY(-50%);
    width: 32px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--nv-gold);
    border-radius: 0 5px 5px 0;
    cursor: pointer;
}

    /* the hairline. ⚠ `::before`, not a border on the button - a border would
       round with the button's own radius and sit 1px off the field's edge.

       ⚠⚠⚠ v31.82 - IT HAD BEEN 16px, NOT 22, AND HERE IS WHY. The old rule
       inset it 5px top and bottom, which is right - but it inset it from THE
       BUTTON, and the button is 26px inside a 32px field. `select.input`
       beside it insets 5px from the FIELD and draws 22px. So a text field and
       a select side by side - exactly the pair v31.53 was asked to make match
       - carried hairlines 6px different in length.
       ⚠⚠ SIZED FROM THE FIELD, AND THERE IS ONLY ONE FIELD HEIGHT. 22px is
       32px inset 5px top and bottom, which is what `select.input` draws.
       ⚠⚠⚠ AND 22 IS THE ONLY NUMBER NEEDED, WHICH I LEARNED THE EXPENSIVE
       WAY. Two controls in the app ASK to be taller - the email sheet's
       search box carries `style="height:48px"` and the VAT card's account
       field sets `height: 36px` - and I built a 30px and a 24px divider for
       them. Neither control is that tall. THE END OF THIS FILE CARRIES
       `input.input { height: 32px !important; min-height: 32px !important }`
       (v16.2, reaffirmed v21.0), and an `!important` author declaration beats
       an inline style, so both have been 32px for many versions. A 30px
       divider would have been taller than the box it divides. The probe
       measured `fieldH = 32` on both; nothing else would have caught it.
       ⚠ A TALLER CONTROL CANNOT EXIST until that override is relaxed, so
       there is no variant class and no variable - one height, stated once.
       ⚠ THIS IS AN UNASKED CHANGE ON NINE SHIPPED SITES. It is 3px at each
       end of a 1px line, it makes the two dividers agree, and it is one line
       to revert: put `height: 16px` here. */
    .fld-btn::before {
        content: '';
        position: absolute;
        left: 0;
        top: 50%;
        height: 22px;
        transform: translateY(-50%);
        width: 1px;
        background: rgba(112,160,238,.30);
    }

    .fld-btn:hover {
        color: var(--nv-gold-bright);
    }

    /* ⚠ `:focus-visible`, not `:focus` - a mouse click on it should not leave a
       ring behind, but a keyboard user must be able to see where they are. */
    .fld-btn:focus-visible {
        outline: 2px solid var(--nv-blue-bright);
        outline-offset: 1px;
    }

/*  ⚠⚠ v31.83 (owner) - `.badge.gold.is-new`: THE NUMBER GOES GREEN AND
    NOTHING ELSE MOVES. Owner: *"change only the number colour to green but
    leave all other badge styling unchanged."*

    ⚠ `.badge` AND `.badge.gold` LIVE IN `tokens.css`, and this rule does not
    join them there. tokens.css is the design-token sheet - the shapes every
    screen inherits - and a one-screen state is not a token. `app.css` loads
    after it and `0,3,0` beats `0,2,0`, so the colour wins on specificity
    rather than on file order, which is the safer of the two.
    ⚠ `--nv-good` is the app's existing green (`#58b368`), the same one the
    mailbox's own new-email figure uses. Not a new colour. */
.badge.gold.is-new {
    color: var(--nv-good);
}

/*  ⚠ THE VIES ANSWER. Three states and three colours, because the whole point
    of the check is that "could not ask" must never read as "not valid".
    `.muted` for unknown, green for valid, gold for not valid - NOT red: the
    number may simply be one the holder has not registered for cross-border
    trade, which is information, not an error. */
.acc-vies {
    font-size: 12.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

    .acc-vies.is-ok {
        color: var(--acc-green, #3fca85);
    }

    .acc-vies.is-no {
        color: var(--nv-gold);
    }

/*  ⚠ THE 10px THE OWNER ASKED FOR. Owner: *"The 'Save Address' button has no
    space between it ... and the email 'AddrEmail' at the top. Please make this
    gap/space about 10px between the two controls. The same gap styling needs
    to be applied to fin.accSaveRange button and the row above it."*
    ⚠⚠ IT IS ITS OWN CLASS, NOT A CHANGE TO `.acc-new`. That class also wraps
    the NEW-ACCOUNT form in the list card, where the row above it is a card
    heading with its own spacing - moving `.acc-new` would have moved a third
    thing the owner did not ask about. */
.acc-actions {
    margin-top: 10px;
}

.acc-tree {
    margin-top: 12px;
    border: 1px solid var(--nv-line, rgba(232, 167, 92, .18));
    border-radius: 10px;
    padding: 8px;
}

    .acc-tree-head {
        display: flex;
        align-items: center;
        gap: 10px;
        margin-bottom: 8px;
    }

    /*  ⚠⚠ THE CAP IS MEASURED, NOT ROUNDED. (batch238's lesson: 224px showed
        six rows where seven were asked for, because the rows were 32.25px and
        not the 32 they looked like.)

        A row is 18px of line box plus 5px of padding top and bottom - 28.00px
        exactly, measured at 390 / 768 / 1280 / 1440 / 1920 with no variance,
        which is why the line height is STATED here rather than inherited from
        whatever the card's font settles on.

        MEASURED, from `scrollTop = 0`, with the whole chart expanded:
            400px cap  →  14 whole rows, 8px left  (= this box's own padding)
        so the tall branch is fourteen ENTIRE rows and never
        thirteen-and-a-sliver.

        ⚠ `50vh` WINS ON A SHORT WINDOW, where a fixed 400 would push the rest
        of the tab off the bottom. It does NOT land on a whole row and it is
        not supposed to - measured 10 rows + 12px at a 600px-tall window,
        8 rows + 8px at 480px. A part-row at the fold is what tells the reader
        the list continues. */
    .acc-tree-body {
        max-height: min(50vh, 400px);
        overflow-y: auto;
        overflow-x: hidden;
        padding: 4px 0;
    }

/*  ⚠⚠⚠ v31.66 (batch263) - THE OWNER SENT THESE TWO RULES VERBATIM after
    seeing the finished tree: `.acc-tree-row` at **17px**, `.acc-tree-d1` at
    weight **200**. Applied exactly as written.

    ⚠⚠ THESE ARE THE SHARED CLASSES, SO THE CHART-OF-ACCOUNTS PANEL TAKES THEM
    TOO. That is a second control changing on one instruction, and it is called
    out rather than quietly scoped: the two panels are deliberately one look,
    and splitting them here would be the start of two trees that drift.
    ⚠⚠⚠ THE ROW HEIGHT DOES NOT MOVE, AND THAT WAS THE THING TO CHECK. Height
    is `line-height: 18px` plus 5px of padding top and bottom = 28.00px, and
    `font-size` is not part of that sum - measured 28.00 at 13px and 28.00 at
    17px. So the chart panel's cap, which was measured as "14 whole rows in
    400px", still lands on whole rows.
    ⚠ 17px IN AN 18px LINE BOX IS TIGHT and was measured for clipping rather
    than assumed: `.acc-tree-nm` has `overflow: hidden`, so a descender that
    overran would be cut. Measured `scrollHeight == clientHeight` on every row
    at every width - nothing is clipped. */
.acc-tree-row {
    display: flex;
    align-items: center;
    gap: 8px;
    line-height: 18px;
    padding: 5px 6px;
    /*  ⚠ v31.85 (owner) - 17px -> 14px. The v31.66 note above records 17px as
        the owner's own number; this is the owner's own revision of it, after
        living with the finished tree.
        ⚠⚠ AND IT MOVES BOTH TREES, exactly as 17px did - `.acc-tree-row` is
        shared by the country picker and the chart-of-accounts panel, and they
        are deliberately one look. Called out rather than quietly scoped.
        ⚠ THE ROW HEIGHT DOES NOT MOVE: height is `line-height: 18px` + 5px of
        padding top and bottom = 28.00px, and `font-size` is not part of that
        sum. Measured 28.00 at 17px and 28.00 at 14px, so the chart panel's
        "14 whole rows in 400px" cap still lands on whole rows.
        ⚠ 14px is above the 13px font floor, so the floor gate is unaffected. */
    font-size: 14px;
    font-weight: 200;
    color: var(--nv-ink-2);
    border-radius: 6px;
    cursor: default;
}

    /* ⚠ ONLY A ROW THE SCRIPT GAVE A TWISTY IS CLICKABLE. A leaf that
       highlights on hover promises a click that does nothing.
       ⚠⚠ THIS SELECTS AN ATTRIBUTE, AND A REAL BUTTON DOES NOT CARRY ONE -
       see the rule below. It serves the DIV rows the script marks up, and
       only those. */
    .acc-tree-row[role="button"] {
        cursor: pointer;
    }

        .acc-tree-row[role="button"]:hover {
            background: rgba(232, 167, 92, .06);
        }

/*  ⚠⚠⚠ v31.84 (owner) - A PICKABLE ROW IS A REAL BUTTON, AND IT HAS BEEN
    DRESSED AS ONE SINCE R2.

    Owner: *"Somthing has changed in `title:fin.accCntryPick` or perhaps
    [.acc-tree-row]"* - and the version he pasted had `background: transparent`
    and `border: none` added by hand, which is the whole diagnosis in two
    declarations.

    WHAT HAPPENED. v31.74 made a pickable row a real `type=submit` control so
    the tree could post its `ReportAnalysisId` - the right change, and the
    right element. But `.acc-tree-row` only ever styled DIVs, and the one rule
    that resets a clickable row (`[role="button"]`, just above) matches an
    ATTRIBUTE THE BROWSER DOES NOT NEED ON A REAL BUTTON and therefore never
    matched the new rows at all. Every pickable row has been rendering with
    the operating system's own button chrome ever since.

    MEASURED, side by side in the same tree, before this rule existed:

        property        DIV row              BUTTON row
        width           460px (full)         132-294px (shrink-wrapped)
        height          28px                 32px
        font-family     Poppins              **Arial**
        background      transparent          **rgb(239,239,239)**
        border          none                 **2px outset**
        cursor          pointer              default

    So the chart tree has been drawing its selectable rows as grey system
    buttons, in the wrong typeface, at ragged widths, among Poppins text rows
    - and 32px tall, which quietly broke the 28.00px row height the v31.66
    note above went to some trouble to measure and preserve.

    ⚠⚠ `font-family: inherit` IS THE NON-OBVIOUS ONE. A button does not
    inherit the page's typeface; `font-size` and `font-weight` were already
    forced by `.acc-tree-row`, which is exactly why the rows looked *almost*
    right and the cause was hard to name.
    ⚠ `width: 100%` IS NOT COSMETIC EITHER. A button is shrink-to-fit, so the
    hover and current-row fills stopped at the end of the text instead of
    running the width of the panel, and `.acc-tree-nm`'s `flex: 1` had no
    room to work with.
    ⚠ `background: transparent` is at (0,1,1) and `.acc-tree-row.is-cur` at
    (0,2,0), so the current row keeps its gold fill. Checked, not assumed.
    ⚠ `border-radius: 6px` on the base rule is KEPT. It is only ever visible
    behind a fill (hover, or the current row), and without it that fill is a
    hard rectangle. Say the word if you want it gone - it is one line. */
button.acc-tree-row {
    width: 100%;
    background: transparent;
    border: 0;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}

    button.acc-tree-row:hover {
        background: rgba(232, 167, 92, .06);
    }

/*  ⚠⚠⚠ v31.160 — AN ANCHOR TREE ROW, AND THIS IS THE v31.84 BUTTON LESSON
    REPEATED FOR THE OTHER ELEMENT. That note above records what happened when
    a pickable row became a real `<button>`: `.acc-tree-row` had only ever
    styled DIVs, so the row arrived wearing the operating system's own chrome —
    Arial, grey fill, 2px outset border, 32px instead of 28.

    Notes' rows must NAVIGATE, so they are `<a>`, and an anchor arrives with
    its own inheritance: UA link colour and an underline that PROPAGATES to
    every descendant and cannot be cancelled by them. That is precisely the
    fault the owner reported at v31.158 ("purple under line and weird blue
    underline"), and it would have come straight back the moment these rows
    joined the house tree.

    ⚠ Everything here mirrors `button.acc-tree-row` so the three element types
      are one row: same width, same left alignment, same pointer, and the two
      declarations an anchor needs on top. */
a.acc-tree-row {
    width: 100%;
    background: transparent;
    border: 0;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    /*  the owner's v29.6 ruling, third time of asking: nothing in these
        controls is ever underlined. */
    text-decoration: none;
    color: var(--nv-ink-2);
}

    a.acc-tree-row:hover {
        background: rgba(232, 167, 92, .06);
    }

/*  ⚠⚠ A FOURTH DEPTH. The house tree has three (d1 · d2 24px · d3 42px) because
    nothing had ever needed more; v1's Notes tree is Entity › Account › Folder ›
    Note. This continues the same +18px step and is a pure addition — no
    existing caller has a d4 row, so the country picker and the chart-of-
    accounts panel cannot see this. */
.acc-tree-d4 {
    padding-left: 60px;
}

/*  ⚠ v31.166 — LEVEL 5, on the ladder's own 18px step (6 · 24 · 42 · 60 · 78).
    The Notes tree grew a CATALOG level above the entity, so its note leaf sits
    one deeper than it did. Same scoping argument as d4: no other caller has a
    d5 row, so the country picker and the chart-of-accounts panel cannot see
    this rule at all. */
.acc-tree-d5 {
    padding-left: 78px;
}

    .acc-tree-d1 {
        color: var(--nv-ink);
        font-weight: 200;
    }

    /* ⚠ INDENT IS PADDING ON THE ROW, NOT A MARGIN, so the hover fill and the
       current-row fill still start at the panel's left edge - a highlight that
       steps inwards with the depth reads as a different control. */
    .acc-tree-d2 {
        padding-left: 24px;
    }

    .acc-tree-d3 {
        padding-left: 42px;
    }

    /* the code this account already holds */
    .acc-tree-row.is-cur {
        background: rgba(232, 167, 92, .1);
        color: var(--nv-ink);
    }

    /*  ⚠⚠ `white-space: nowrap` IS LOAD-BEARING, and a measurement is what
        proved it. A 12px box holding a glyph that renders wider - a font
        without U+25B8, a mis-decoded byte sequence, a user zoom - wraps to a
        SECOND LINE, and because the row is a flex container the row grows
        with it: measured at 46px instead of 28px, for every class and group
        heading in the panel, with the leaf rows still at 28. The arrow is
        decoration; it must never be allowed to set the row height. */
    .acc-tree-tw {
        flex: 0 0 auto;
        width: 12px;
        color: var(--nv-gold);
        font-size: 11px;
        line-height: 18px;
        white-space: nowrap;
        overflow: hidden;
        text-align: center;
    }

    /*  ⚠ v31.84 (owner) - `font-variant-numeric: tabular-nums` REMOVED here.
        The note that used to sit on this rule argued that account codes in a
        column want fixed-width digits; the owner has looked at the finished
        tree and does not want it, and his eye on his own screen beats an
        argument from first principles.
        ⚠⚠ ONLY THIS ONE. There are THIRTEEN other `tabular-nums` declarations
        in this file - money columns, file sizes, folder counts, the account
        list's own code column - and every one of them is left alone. A
        blanket removal would have changed amounts on every financial screen
        in the app on the strength of one remark about one tree. */
    .acc-tree-cd {
        flex: 0 0 auto;
        min-width: 38px;
        color: var(--nv-gold);
    }

    .acc-tree-nm {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/*  ══ v31.62 (batch259, owner) - THE COUNTRY PICKER'S TREE ═══════════════
    Owner: *"the drop-down is currently a very long list of countries. We
    should use the tree structure, similar to the one for transaction types...
    I would be extremely grateful if you can even create a further node, so we
    have three levels."*

    ⚠⚠⚠ IT REUSES `.acc-tree*` ENTIRELY - the same rows, the same 28px, the
    same indents, the same twisty, the same measured body cap. Two tree
    controls on one page that look like two different controls would be the
    real cost here, and the chart panel's geometry was measured once already.
    Everything below is about turning that panel into something that hangs
    UNDER a field instead of sitting in the page.

    ⚠ THE SELECT IS STILL THERE, hidden, and it is still what posts. With no
    script none of this exists and the grouped dropdown is what the reader
    gets.
    ══════════════════════════════════════════════════════════════════════ */
.acc-ctrywrap {
    position: relative;
    display: block;
    width: 100%;
    min-width: 0;
}

/*  ⚠⚠⚠ v31.66 (batch263, owner): *"I do not know how to suggest change for
    drop-down country which is behind the save, copy, cancel div."*

    THE PANEL WAS NEVER SHORT OF z-index - IT WAS IN THE WRONG STACKING
    CONTEXT. `.flip` carries `perspective: 1400px`, and a non-`none`
    perspective **establishes a stacking context**; `.flip-inner` adds
    `transform-style: preserve-3d` on top. So `.acc-ctrypop`'s `z-index: 40` is
    resolved entirely INSIDE the flip and cannot out-rank anything outside it.
    `.acc-acts` comes later in the document and its buttons are positioned, so
    it painted over the panel no matter what number the panel carried.

    > **A z-index ONLY COMPETES WITH ITS SIBLINGS IN THE SAME STACKING CONTEXT.
    > WHEN A PANEL WILL NOT RISE, LOOK FOR AN ANCESTOR WITH `transform`,
    > `perspective`, `filter` OR `opacity` BEFORE REACHING FOR A BIGGER
    > NUMBER.**

    So the CONTEXT is lifted, not the panel - and only while a panel is open.
    ⚠ A CARD PERMANENTLY STACKED ABOVE THE ACTION BAR would put a
    3D-transformed subtree over the Save button for all the time no picker is
    open, which is how a Save quietly stops being clickable.
    ⚠ 60 CLEARS THE BAR AND STAYS UNDER THE APP'S SHEETS AND TRAYS, which live
    at 34-41 and at 60 with their own overlay; nothing here should ever cover
    one of those. */
    .acc-ctrywrap.is-open {
        z-index: 60;
    }

.flip.has-ctry-open {
    position: relative;
    z-index: 60;
}

    /*  the face: a button wearing `.input`, so it is the same 32px box with the
        same border and fill as the field it replaced. ⚠ `text-align` is not
        enough on its own - `.input` is `display: flex`, so the caret is pushed
        out by `margin-left: auto` rather than floated. */
    .acc-ctrybtn {
        cursor: pointer;
        text-align: left;
        overflow: hidden;
    }

        .acc-ctrybtn > .acc-ctrynm {
            flex: 1 1 auto;
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .acc-ctrybtn > .acc-ctrycar {
            flex: 0 0 auto;
            margin-left: auto;
            color: var(--nv-gold);
            font-size: 11px;
            line-height: 1;
        }

/*  ⚠⚠ IT IS WIDER THAN THE FIELD IT HANGS UNDER, AND IT HAS TO BE. This
    picker sits in a HALF cell (`.acc-pair` splits one grid cell in two), and
    "Bosnia and Herzegovina" indented three levels does not fit in half of half
    a card. `min-width: 100%` keeps it at least as wide as its field; 280px is
    the floor. It is also `right: auto` on purpose - the field is on the left
    of its pair at every width this card is drawn at.
    ⚠ THE FILL IS OPAQUE. A translucent panel over a form shows the boxes
    behind it through the country names, which at 13px is unreadable - the
    same reason the real form controls carry an opaque `background-color`. */
.acc-ctrypop {
    position: absolute;
    z-index: 40;
    left: 0;
    top: calc(100% + 4px);
    min-width: 100%;
    width: max(100%, 280px);
    margin-top: 0;
    background: #101a30;
    box-shadow: 0 12px 34px rgba(0,0,0,.5);
}

    /*  ⚠ SHORTER THAN THE CHART PANEL'S. That one is the whole tab and can
        take half the window; this one hangs over the fields under it, and a
        list long enough to cover the Save button is a list that hides the way
        out. 320px is 11 whole 28px rows plus this box's own 8px padding. */
    .acc-ctrypop .acc-tree-body {
        max-height: min(46vh, 320px);
    }

    /*  ⚠⚠⚠ v31.65 (batch262, owner): *"The second node should have a 30-40px
        offset so it appears tabbed."*

        The shared `.acc-tree-d2` / `-d3` steps are 24px and 42px, drawn for the
        chart of accounts where every row carries a numeric code in a 38px
        column and the code column itself reads as the indent. The country tree
        has no code on its headings at all, so 24px was not enough to say
        "this hangs under that" - three levels looked like one list with some
        rows slightly further in.

        34px and 68px: the owner's range, and an EQUAL STEP between the levels,
        which is what makes the depth readable at a glance rather than a
        judgement about which gap is bigger.

        ⚠⚠ SCOPED TO THE POPOVER, and deliberately. The same two classes draw
        the chart-of-accounts panel, whose indents were measured into shape and
        which nobody asked to change. Two trees, one look, different content -
        and the content is what sets the indent. */
    .acc-ctrypop .acc-tree-d2 {
        padding-left: 34px;
    }

    .acc-ctrypop .acc-tree-d3 {
        padding-left: 68px;
    }

/*  ══ v31.55 (batch252, ROUND 1 of the endgame plan) - THE ANALYSIS SWITCH ═══
    Owner, from three live mock-ups: *"please let us use, '1.', Switch on the
    right. Keeps today's row exactly as it is and adds a switch where the tick
    used to be."*

    ⚠ IT SITS IN THE TICK'S GRID CELL. `.acc-ba` is
    `minmax(0,1fr) max-content max-content` and stays that way - name, state
    word, control - so nothing about the row's proportions moves. The green
    tick is gone because the switch now carries that information, and two
    controls saying the same thing is how they start disagreeing.

    ⚠⚠ GREEN WHEN ON, AND THAT IS NOT DECORATION. `--acc-green` is the colour
    this module already uses for "the posting gate is satisfied"; the switch
    turns it on because what it sets is exactly what that gate reads.
    ══════════════════════════════════════════════════════════════════════════ */
.acc-sw {
    flex: 0 0 auto;
    width: 34px;
    height: 19px;
    padding: 0;
    border-radius: 10px;
    border: 1px solid rgba(104,152,232,.32);
    background: rgba(255,255,255,.04);
    position: relative;
    cursor: pointer;
}

    /*  ⚠ THE KNOB IS A PSEUDO-ELEMENT, so the button keeps ONE box and the
        34x19 hit area is the whole control rather than the 13px dot. It is
        already the smallest target on the page - the owner chose this option
        knowing that - so none of it is given away to a child element. */
    .acc-sw::after {
        content: '';
        position: absolute;
        top: 2px;
        left: 2px;
        width: 13px;
        height: 13px;
        border-radius: 50%;
        background: var(--nv-ink-3);
        transition: transform .15s ease, background .15s ease;
    }

    .acc-sw[aria-checked="true"] {
        background: rgba(63,202,133,.18);
        border-color: rgba(63,202,133,.5);
    }

        .acc-sw[aria-checked="true"]::after {
            transform: translateX(15px);
            background: var(--acc-green, #3fca85);
        }

    /*  ⚠⚠ DISABLED MEANS "the entity does not use this code AND the account
        does not carry it". A slot that is off in the entity but ON here is NOT
        disabled - it has one move left, and it has to keep it or the value is
        stranded where the posting gate can still read it. */
    .acc-sw:disabled {
        opacity: .4;
        cursor: not-allowed;
    }

    .acc-sw:focus-visible {
        outline: 2px solid var(--nv-blue-bright);
        outline-offset: 2px;
    }

/*  ⚠ `prefers-reduced-motion` - the knob slides, so it has to be able not to. */
@media (prefers-reduced-motion: reduce) {
    .acc-sw::after {
        transition: none;
    }
}

/*  the save row: the button, then the consequence in numbers beside it */
.acc-basave {
    align-items: center;
}

    .acc-basave .acc-bacount {
        margin-bottom: 0;
        font-size: 12.5px;
    }

    .acc-basave #baDirty {
        color: var(--nv-gold);
    }

/* ══ THE COPY SHEET (v31.76, batch272 - R4.1) ══════════════════════════════
   ⚠ FIVE RULES, AND NOT ONE OF THEM IS A NEW COMPONENT. The sheet itself is
   `.mail-sheet`, its rows are `.t-leaf`, its fields are the house
   `.fld-wrap` + `.fl-hint` + `.input` trio and its warning is `.acc-ba-hint`.
   These five only place them.
   ⚠⚠ EVERY CLASS HERE IS PREFIXED `.acc-cp-` AND WAS GREPPED FIRST. `.acc-cp`
   appears nowhere else in this file - the short-class-name trap (`.fld` is the
   FOLDER ROW, `.vr-tick` was already taken) has cost this project twice. */

/* ⚠⚠⚠ NOT ONE `font-size` IN THIS BLOCK, AND THAT IS THE POINT. The first cut
   set 12.5px here and 12px below, and the FONT-FLOOR GATE caught it: the count
   of screen sizes at or under 13px went 30 -> 31. The owner's standing rule is
   that type sizes are reviewed PAGE BY PAGE and none is added in passing.
   The three elements now carry `.acc-ba-hint`, the muted hint this page
   already uses for exactly this - so these rules PLACE them and nothing more,
   and the size stays one fact in one place. */

.acc-cp-src {
    padding: 0 2px 10px;
}

.acc-cp-hint,
.acc-cp-what {
    padding: 10px 2px 0;
}

.acc-cp-rule {
    border: 0;
    border-top: 1px solid rgba(104,152,232,.13);
    margin: 12px 0;
}

.acc-cp-warn {
    margin: 10px 2px 0;
}

/*  v31.192 - THE DELETE CONFIRM.
    ⚠ THE QUESTION IS THE BIGGEST THING IN IT. `.ctext` is the house's confirm
    body at 16px and that is what the "are you sure" line wears; the consequence
    line below it drops to the hint size so a warning cannot shout down the
    thing being asked. The screenshot is what caught it - the numbers said
    nothing was wrong. */
.acc-del-warn {
    margin: 10px 2px 16px;
}

/*  ⚠ THE SHEET IS NARROWER THAN THE COPY ONE because it asks a question rather
    than offering a chooser: `min(560px, calc(100vw - 32px))` is the same
    formula the copy sheet uses, one step down. */
.acc-del-sheet {
    max-width: min(560px, calc(100vw - 32px));
}

/*  ══ v31.534/535 — THE CODE CHANGE: THE SHEET ═════════════════════════════
    ⚠⚠⚠ v31.535 DELETED `.acc-codecell` AND `.acc-codeb` RATHER THAN TUNING
    THEM. They were a private flex row holding a round `.nv-info-b` BESIDE the
    field — a third kind of control on a page that already has exactly one way
    to put an action in a box. The owner asked for the pencil INSIDE the text
    box, and the house geometry (`.fld-go` + `.fld-btn`, option B, thirty-one
    call sites) already does that, draws its own hairline and reserves its own
    46px. **The fix was to stop having private rules, not to add more.**        */

/*  ⚠ SAME WIDTH AS THE DELETE SHEET, because it asks the same kind of
    question. The formula is not copied - it is the delete sheet's own rule
    read off the class beside it, so the two cannot drift apart. */
.acc-code-sheet {
    max-width: min(560px, calc(100vw - 32px));
}

.acc-code-new {
    margin: 14px 2px 2px;
}

.acc-code-what {
    margin: 10px 2px 0;
}

/*  ⚠⚠ THE SAME SPACING THE DELETE SHEET'S WARNING CARRIES, and for the same
    reason it gives: the QUESTION has to stay the largest thing in a confirm,
    so the consequence sits at hint size with room above the actions rather
    than growing into a paragraph that shouts louder than what is being asked. */
.acc-code-warn {
    margin: 10px 2px 16px;
}

/* ⚠ THE OWNER'S FOURTH WIDTH RULE: the two actions share one track width, so
   neither is sized by its English label. `minmax(0, 1fr)` - not `1fr` - because
   a bare `1fr` floors at the content's min width and a long German label would
   push the pair wider than the sheet. */
.acc-cp-acts {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
    margin: 16px 2px 0;
}

    .acc-cp-acts > .btn {
        width: 100%;
    }

/* ══ v31.77 (batch273, owner) ══════════════════════════════════════════════ */

/*  ⚠⚠⚠ `.highlight` - THE OWNER'S OWN RULE, AND WHY IT CANNOT BE A `b`.
    He gave it as `.mk-src b { color: var(--nv-ink); font-weight: 300 }` off the
    mock-up, and asked for a `highlight` instead of the `b`. **That is the only
    way to get weight 300**: `b { font-weight: 400 !important }` is app-wide and
    unbeatable in this app, so a bold tag can never take a lighter weight. The
    house answer to "make this stand out" has always been a span with a class.
    ⚠ It sets NO font-size, so it lifts whatever line it sits in rather than
    resizing it - which is what lets it be used anywhere.
    ⚠⚠ GREPPED FIRST: `.highlight` appears nowhere else in app.css or
    tokens.css. It is deliberately a general name because it is a general
    utility - unlike `.fld`, which reads general and is the FOLDER ROW. */
.highlight {
    color: var(--nv-ink);
    font-weight: 300;
}

/*  ⚠ THE COPY BUTTON OPENS SOMETHING, SO IT CARRIES THE DOWN CHEVRON - the
    owner's standing ruling that every down chevron gets the same treatment.
    The gap is the `.chips` gap so the glyph sits off the word the same way it
    does on the More chip. */
.acc-cp-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

    .acc-cp-btn .chev {
        flex: 0 0 auto;
        /* ⚠ `content-box`, because the global `*` reset is `border-box` and
           would SQUEEZE the glyph rather than pad it - the batch253 lesson. */
        box-sizing: content-box;
    }

/*  ⚠ THE RULE UNDER THE HEADLINE (owner). `.acc-cp-rule` already draws the
    hairline between the destination and the detail; the top one only needs a
    different margin, so it is a modifier rather than a second rule. */
.acc-cp-rule-top {
    margin: 2px 0 12px;
}

/*  ══ THE ENTITY TREE INSIDE THE COPY SHEET ═══════════════════════════════
    ⚠⚠⚠ NOT ONE STRUCTURAL RULE HERE. The tree is `.ent-node` / `.ent-db` /
    `.ent-grp` / `.ent-body` / `.fld` / `.fi` / `.fl` / `.fc` - the Organisation
    sheet's own components, which app.css already styles. These four rules only
    PLACE that tree inside a sheet it was not written for and decide when the
    tick shows. Owner: *"styled as with Entity switch"* - so the way to honour
    that is to use the same classes, not to redraw them. */
/*  ⚠⚠⚠ THE CAP IS ON THE SHEET, NOT ON THE TREE - AND THE FIRST CUT HAD IT
    THE OTHER WAY ROUND. `max-height: 34vh` on the tree alone clipped the last
    catalog with NO scrollbar and no other cue: the render showed NOVERA Family
    ending at United Kingdom and NOVERA Clients simply absent. **A box that
    silently drops half its options is worse than one that is too tall** - the
    reader cannot miss what they cannot see.
    ⚠ Only LOOKING at the screenshot caught it; `treeScrolls: true` in the
    measurement read as "handled".
    ⚠⚠ AND THE SHEET WAS 845px TALL, which no phone shows. Capping the sheet
    fixes both at once: everything stays reachable, and on a tall screen
    nothing scrolls at all. */
#accCopySheet {
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    /* ⚠ BOTH AXES NAMED. `overflow-y: auto` alone silently makes a sideways
       scroller too - the v31.31 bank-card lesson. */
    overflow-x: hidden;
}

.acc-cp-tree {
    display: block;
    min-width: 0;
}

    .acc-cp-tree > .fl-hint {
        margin-bottom: 4px;
    }

/*  ⚠⚠ THE TICK KEEPS ITS WIDTH WHEN IT IS NOT SHOWING. `visibility`, never
    `display`: hiding the glyph outright would let each row's name column find
    its own width and the list would stop lining up. The Organisation sheet
    reserves the same slot. */
.acc-cp-ent .acc-cp-tick {
    visibility: hidden;
}

.acc-cp-ent.on .acc-cp-tick {
    visibility: visible;
}

/*  ⚠ A ROW THAT IS A BUTTON NEEDS THE BUTTON RESET, the same one the
    Organisation sheet applies inline. Here it is a rule, because there are
    many rows and an inline style cannot be overridden by a later stylesheet. */
button.fld.acc-cp-ent {
    width: 100%;
    background: transparent;
    border: 0;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    color: inherit;
}

    button.fld.acc-cp-ent.on {
        background: rgba(232,167,92,.14);
    }

/*  ══════════════════════════════════════════════════════════════════════════
    R7.6 (v31.86, batch281, owner) - THE ACCOUNT-TYPE CHANGE ROW
    Owner: *"Change account type using validations shown in v1."*
    ══════════════════════════════════════════════════════════════════════════

    ⚠⚠ IT IS ONE CELL OF `.acc-fields`, NOT A NEW ROW. The Details grid is two
    equal columns and the type has always been the second cell of the first
    row; adding a button beside the field must not cost a row that the owner
    spent v31.59 winning back on the address block.
    ⚠ `minmax(0, 1fr)` on the select, exactly as `.acc-pair` uses: a long type
    name would otherwise refuse to shrink and push the button out of the cell. */
/*  ══════════════════════════════════════════════════════════════════════════
    v31.88 (batch283, owner) - `.btn.h34`, A HEIGHT BRANCH FOR THE HOUSE BUTTON
    Owner: *"Please create a css style branch where we can have this as a
    variation from height 51px."*
    ══════════════════════════════════════════════════════════════════════════

    ⚠⚠ IT IS A BRANCH, NOT AN OVERRIDE ON ONE ELEMENT, and the difference is the
    point. `tokens.css` sets `.btn { height: 51px; font-size: 19px }` app-wide,
    and until now every page that wanted something shorter wrote its own rule
    against its own class - `.acc-docbtn`, `.ctl3 .btn`, the copy sheet's rows.
    Each of those is a private answer to the same question, and each drifts on
    its own. `.h34` is the shared answer: **add the class, get the short
    button**, on any `.btn` anywhere in the app.

    ⚠⚠⚠ THE FONT COMES DOWN WITH THE BOX, AND THAT IS NOT OPTIONAL. 19px glyphs
    in a 34px box leave 7px of breathing room top and bottom and the label sits
    on the box like a lid. 15px is the same ratio the 51px button has at 19px,
    so the short button reads as the SAME button rather than a squashed one.
    ⚠ `padding` comes down too: the house 51px button's horizontal padding is
    sized for its height, and keeping it would make a short WIDE button.

    ⚠ WHY 34 AND NOT 32: the fields on these cards are 32px
    (`input.input:not([type="file"]) { height: 32px !important }`), and a button
    that measures EXACTLY the same as the field beside it reads as another
    field. 34 is the smallest step that still reads as a control you press -
    and it is a size the app already uses (`.chip` at 32, the tab strip at 34).
    ⚠⚠ `min-height` IS SET TOO. `.btn` is `display: flex`, and a flex container
    with a long label will grow past a bare `height`; naming both is what makes
    34 a fact rather than a preference.

    ⚠ IT IS PLACED AT THE FOOT OF app.css so nothing below can beat it by
    source order - the ordering trap this file has paid for twice. */
.btn.h34 {
    height: 34px;
    min-height: 34px;
    font-size: 15px;
    padding: 0 14px;
}

/*  ⚠⚠⚠ v31.87 (batch282, owner) - THE SELECT AND THE BUTTON ARE STACKED, AND
    THE ROW THEY USED TO SHARE WAS BROKEN. Owner: *"Please move the button
    'fin.accTypeChange' to the row below id='accTySel'."*

    MEASURED on v31.86 in a 617px card: the cell is 282px wide, the select
    rendered **64px** and the button **282px starting at x=507** - seventy-two
    pixels outside the card.
    ⚠⚠ THE CAUSE WAS `tokens.css`'s app-wide `.btn { width: 100%; height: 51px }`.
    v31.86 gave `.acc-tybtn` a height and `flex: 0 0 auto` and **never named
    `width`**, so the flex basis resolved to `auto`, took the inherited 100%,
    and the button claimed the whole cell while the select shrank to its
    minimum.
    > ⚠ **`flex: 0 0 auto` DOES NOT MEAN "AS WIDE AS MY CONTENT" - IT MEANS "AS
    > WIDE AS MY `width` SAYS".** With a 100% base rule above it, that is the
    > container.

    ⚠ SO THE HEIGHT OVERRIDE IS GONE TOO. Stacked, the button is the house
    button at its house size - the same 51px full-width shape `Save
    registration` and the Save / Copy / Cancel footer already have on this card.
    One less rule fighting the base, and one less size on the screen.
    ⚠ `.fld-wrap` is already a flex COLUMN, so this cell inherits the stacking;
    only the gap above the button is set here. */
.acc-tycell > .acc-tybtn {
    margin-top: 8px;
}

/*  ⚠⚠⚠ THE CROSSING WARNING. Six of v1's transitions take an ADDRESSED
    account to a NOMINAL one, and the owner confirmed BOTH halves: the
    selector is right, and v2's `IsAddressed` is right. So this is a
    consequence to show, never a discrepancy to hide.
    ⚠ GOLD, NOT RED, and the distinction is the same one `.acc-grp-warn`
    makes: nothing has gone wrong and nothing is lost - the rows stay in the
    tables - so this is information to act on, not an error. */
.acc-tywarn {
    margin-top: -4px;
}

    .acc-tywarn > .acc-tywarn-t {
        font-size: 12px;
        line-height: 1.45;
        color: var(--nv-gold);
    }

/*  ⚠ THE DOT SITS ON ITS OWN HINT LINE rather than on a heading, because
    this block has no heading - it is a field, and the toolkit's dot is
    normally a child of `h2.section`. Flex keeps the two on one baseline and
    the note (a block) drops beneath both. */
/*  ⚠ v31.88 (batch283) - `.acc-regi` RETIRED. The registration dot moved onto
    the section heading at the owner's request, where `h2.section` already
    places a `.nv-i` correctly and no rule of our own is needed. The class had
    exactly one element and it no longer exists - **and before deleting a CSS
    rule, count the elements that use it** (the `.acc-ro-note` lesson: that one
    was removed with the one note it was written for and had three). Counted: 0.

/*  ⚠⚠⚠ v31.87 (batch282, owner) - `flex-direction: row` IS THE WHOLE FIX, AND
    ITS ABSENCE WAS THE DEFECT. Owner: *"The fin.accTypeAbout should be in the
    same row as the icon (accTyNote)."*

    MEASURED on v31.86: the label sat at y=173 and the dot at y=191 - stacked,
    and centred in the card rather than left-aligned.
    ⚠⚠ `.acc-tynote` set `display: flex` and never named the DIRECTION, so it
    INHERITED `column` from `.fld-wrap`, and the `align-items: center` written
    to centre the pair vertically centred them horizontally instead.
    > ⚠ **SETTING `display: flex` ON AN ELEMENT THAT IS ALREADY A FLEX COLUMN
    > CHANGES NOTHING.** Direction is the property that has to be named.

    ⚠⚠ v31.88 (owner): THE PAIR IS NOW THE SECOND LINE OF THE **CODE** CELL, not
    a grid cell of its own. Owner: *"The contents of the note ... should move to
    the column to the left of the change button."* The button is the second line
    of the TYPE cell, so its left-hand neighbour is the second line of the CODE
    cell - a row of its own would have sat a row lower, which is exactly where
    v31.87 put it.
    ⚠ `margin-top: auto` is what levels it with the button: the Code cell's
    first line is a 32px field and the Type cell's is a 32px select, so pushing
    this block to the BOTTOM of its cell lands it on the button's line whatever
    the button's height happens to be. It follows `.h34` without knowing about
    it. */
.acc-tynote {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-start;
    gap: 0;
    /*  ⚠ 4px ON TOP OF `.fld-wrap`'s OWN 4px GAP = the 8px the button carries,
        so the two land on the SAME LINE. MEASURED without it: the About row at
        y=165 against a button at y=169. `margin-top: auto` did nothing here -
        the cell has no free space to push into, both columns being the same
        height by definition of a grid row. */
    margin-top: 4px;
    /*  ⚠ the same box height as the button, so the two are centred alike
        whatever `.h34` is set to. */
    min-height: 34px;
}

    /*  ⚠ `.fl-hint` is `display: block` with a nowrap ellipsis; as a flex item
        it must be allowed to shrink rather than push the dot off the cell. */
    .acc-tynote > .fl-hint {
        flex: 0 1 auto;
        min-width: 0;
    }

/*  ══════════════════════════════════════════════════════════════════════════
    v31.86 (batch281, owner) - THE REPORTS MODULE
    Owner: *"The new Module I would like to Start is Reports ... located in new
    folder /Pages/Reports/ and associated _files in /Pages/Shared/Reports/"*,
    from the mock-up the owner locked on 2026-08-27.

    ⚠ APPENDED AT THE FOOT OF app.css ON PURPOSE - the cheap answer to the
    ordering trap this file has paid for twice. Nothing below can beat these
    by source order alone.
    ⚠⚠ EVERY CHILD NAME IS SCOPED UNDER A `.rpt-` PARENT. `.nm`, `.cd`, `.ic`,
    `.lb`, `.v`, `.bar` and `.due` are exactly the short generic names the
    house rule warns about; scoped, they cannot be claimed by anything else and
    cannot claim anything else.
    ⚠ THE BREAKPOINT RULES ARE IN responsive.css, where every breakpoint in
    this solution lives.

    ⚠⚠⚠ THE FONT-FLOOR GATE MOVES BY +18 IN THIS BATCH, AND IT IS SAID OUT LOUD
    RATHER THAN LET THROUGH. The standing rule is *"type sizes page by page,
    never app-wide"* and its second half - **do not ADD one either** - is a
    COUNT: declarations at or below 13px, base against batch, equal means none
    introduced. This block and the AI Tokens block below add eighteen.
      · They are all on TWO BRAND-NEW SURFACES, and both were reviewed as
        rendered mock-ups and LOCKED by the owner - which is precisely the
        page-by-page review the rule asks for, done before the code rather
        than after it.
      · **NOT ONE SIZE IS INVENTED.** 11px + .06em uppercase is
        `.nvcal .nvc-dow`; 12.5px is `.sim-grp`; 12px is the account More
        sheet's count. Each takes the size the app already uses for that role.
      · Nothing OUTSIDE these two blocks moves - the 226 declarations the rule
        is protecting are untouched.
    ⚠ IF THE OWNER'S FOUR-STEP LADDER LANDS ON A DIFFERENT SET, this block is
    where these change, and they change together.
    ══════════════════════════════════════════════════════════════════════════ */

/*  ⚠⚠⚠ THE GRID IS THREE EQUAL TRACKS OR ONE, NEVER THE THREE RAW `1fr` TRACKS
    `.fin-grid3` GIVES IT. Two things measured wrong with the inherited rule:
      · `1fr` is `minmax(auto, 1fr)`, so a track cannot shrink below its
        content's min-content width. MEASURED at 768: Category 267px beside
        Recurrent 222px - three "equal" columns that were not equal, because
        the list card spans two of them and pushed its own pair wider.
      · The placement rules (the span-2 list, the row-spanning Category) only
        exist from 1024 up, so between 768 and 1023 a span-2 card sat in a
        three-track grid with nothing arranging it.
    So: `minmax(0, 1fr)` from 1024, where the placement rules are, and ONE
    column below it. The four cards stack in DOM order on a tablet and a phone,
    which is the same thing the locked mock-up does at those widths.
    ⚠ `.fin-grid3` STAYS ON THE ELEMENT for its `gap` and as the family marker;
    this only overrides the template, and it sits below `.fin-grid3` in the
    file so it wins on order - the ordering trap this file has paid for twice. */
.rpt-grid {
    grid-template-columns: minmax(0, 1fr);
}

/*  ⚠⚠⚠ THE TRACKS, AND THE NUMBERS ARE MEASURED AGAINST THE DELIVERED PAGE,
    NOT CHOSEN. v31.101 moved the parameter card out of the 390px right rail
    and into this grid, so a third track had to come from somewhere.

    ⚠⚠ THE CONTROL SAID 300px. Measured on the layout that was on the owner's
    disk, the Category card is **301px at 1024 and 321px at 1440**, and at
    those widths nothing clips. A first attempt gave it 200-283px and
    "Financial Statements" and "Creditors & Purchases" both clipped at 1024,
    1366 and 1440 - **a regression the empty-card version of the probe could
    not see, because an empty card has no label to clip.**

    ⚠⚠⚠ v31.102 (batch297) TOOK THE THIRD TRACK BACK OUT. The parameter card
    returned to the 390px right rail, so this grid is **Category | the content
    column** and nothing else - two tracks at every width from 1024 up. The
    300px floor STAYS, because the measurement behind it was about the category
    LABELS and they have not changed.
    ⚠ Neither track is FIXED: a fixed track beside an `fr` track means the `fr`
    absorbs every pixel the window gains (the `.tok-row` fault).
    ⚠ AND THE INNER SPLIT - list over report over the Recent/Recurrent pair -
    is `.rpt-main`'s own grid, below. Two nested grids, each with one job.
    ⚠⚠ AND THE PLACEMENT IS HERE WITH THE TRACKS, not in responsive.css. Two
    numbers that have to agree - which track a card sits in, and how wide that
    track is - are one fact, and this project's most expensive recurring lesson
    is that **a fact kept in step in two files goes out of step.**
    responsive.css keeps the SHELL (`.app-main`), which is a different grid. */
/*  ⚠⚠⚠ THE FLOOR IS 280 AT 1024 AND 300 FROM 1366, AND BOTH NUMBERS ARE THE
    CONTROL'S. Measured on the DELIVERED page with the app's own side rail in
    the harness, the Category card is 280px at 1024 and 300px at 1366+. A flat
    300 floor at 1024 took 20px straight out of the report list on the narrowest
    band that still has two columns - and that band has the least to spare. */
@media (min-width: 1024px) {
    .rpt-grid {
        grid-template-columns: minmax(280px, .8fr) minmax(0, 2.2fr);
    }

        .rpt-cat {
            grid-column: 1;
        }

        .rpt-main {
            grid-column: 2;
        }
}

/*  ══ THE STICKY CATEGORY CARD - v31.102 (batch297, owner) ══════════════════
    Owner: *"together with the category card a sticky left."*

    ⚠⚠⚠ 82px, AND IT IS NOT A GUESS. The scrolling ancestor here is THE
    VIEWPORT - every box between this card and the document is `overflow:
    visible`, `.scroll` included, despite the name. `.screen-head` is already
    `position: sticky; top: 0` and measures **68px** at every width from 390 to
    1920 with the entity chip on it, which `.acc-head-sticky` measured and
    wrote down first. 68 + the 14px `.scroll` gutter = 82, so the card comes to
    rest exactly one page-gap below the header instead of jammed against it.
    ⚠ ONE NUMBER TO CHANGE if the header ever grows, and it is behind a
    variable for that reason.

    ⚠⚠ IT NEEDS THE GRID AREA TO BE TALLER THAN THE CARD, WHICH IS WHY THE
    WRAPPER MATTERS. A sticky box travels inside its containing block, and for
    a grid item that block is its GRID AREA. `.rpt-main` makes the single row
    as tall as the whole content column, so the area is tall and the card has
    somewhere to travel. In v31.101's flat grid the Category card shared a row
    with only the list, so it would have had almost no travel at all.
    ⚠ `align-items: start` on `.rpt-grid` (inline, on the element) is what
    keeps the CARD at its own height inside that tall area. Stretching it would
    leave zero travel and read as the rule being ignored.

    ⚠⚠ SCOPED TO 1024 UP, WHERE THERE IS A LEFT COLUMN TO BE STICKY IN. Below
    that the grid is one column and a sticky category card would pin itself
    over the list a reader is trying to scroll - the opposite of the ask. */
@media (min-width: 1366px) {
    .rpt-grid {
        grid-template-columns: minmax(300px, .8fr) minmax(0, 2.2fr);
    }
}

@media (min-width: 1024px) {
    .rpt-cat {
        position: sticky;
        top: var(--rpt-cattop, 82px);
    }
}


/*  ── the category card ─────────────────────────────────────────────────── */
.rpt-cats {
    display: flex;
    flex-direction: column;
    /*  ⚠⚠ BOTH AXES NAMED. `overflow-y: auto` alone silently makes a sideways
        scroller too - the v31.31 bank-card lesson. */
    overflow-y: auto;
    overflow-x: hidden;
}

    /*  the leading number is v1's ModID and it is the same gold the account
        More sheet gives its own leading code. ⚠ `b` carries an app-wide
        `font-weight: 400 !important`, so weight is not set here. */
    .rpt-cats .t-leaf b {
        color: var(--nv-gold);
        flex: 0 0 auto;
        min-width: 22px;
    }

    .rpt-cats .t-leaf .rpt-ct {
        flex: 0 0 auto;
        margin-left: auto;
        color: var(--nv-ink-3);
        font-size: 12px;
    }

/*  ── the list: one grid for the head row and every data row, so the columns
       cannot drift apart. ⚠ `minmax(0, 1fr)` on the NAME track is what fires
       the ellipsis - `min-width: 0` alone does not. ────────────────────────── */
/*  ⚠⚠⚠ TWO TRACKS SINCE v31.107 (batch302, owner): *"Column 1 with report
    itemID and the report Code column are not useful for users and therefore not
    necessary to display."* It was `46px 78px minmax(0,1fr) auto`.

    ⚠⚠⚠ AND THE CODE COLUMN CAME BACK ONE ROUND LATER, FOR ONE CATEGORY
    (v31.108, batch303, owner): *"Bring back the report code column only in
    Financial statements, make the column items equal width."*
    **THE ITEM ID DID NOT COME BACK** - only the code, and only under
    `.rpt-list.with-code`, which the page sets on category 15 alone. The
    default above stays the two-track shape for the other six categories.
    ⚠ THE 124px THOSE TWO GAVE UP GO TO THE NAME, which is the track that was
    ellipsising report names at 1024-1440 - a clip the v31.102 control proved
    was delivered behaviour rather than a regression. **The fix arrived as a
    side effect of a different instruction, which is worth noticing rather than
    claiming.**
    ⚠⚠ A PROBE ROW MUST NOW HAVE **TWO** CHILDREN. The old four-track shape
    already cost a round once: a harness row with three children put every
    report name in the 78px track and reported a clip that was not there. */
.rpt-head,
.rpt-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
    padding: 5px 6px;
}

.rpt-head {
    color: var(--nv-ink-3);
    font-size: 11px;
    letter-spacing: .06em;
    text-transform: uppercase;
    border-bottom: 1px solid rgba(104,152,232,.16);
    padding-bottom: 6px;
    margin-bottom: 2px;
}

.rpt-row {
    border-radius: 6px;
    color: inherit;
    text-decoration: none;
    line-height: 18px;
}

    .rpt-row:hover {
        background: rgba(232,167,92,.06);
    }

    .rpt-row.on {
        background: rgba(232,167,92,.14);
    }

    /*  ⚠⚠⚠ `.c-id` AND `.c-cd` WERE HERE AND ARE GONE (v31.107). The code
        column was added on the owner's own word - *"Include also Report code
        column in grid view."* - and removed on his own word two months later.
        **This note is the whole reason the rules were deleted rather than left
        dead:** the next reader who finds the first instruction and no record of
        the second puts the column straight back.
        ⚠ `.rpt-tbl .c-cd` further down is a DIFFERENT column - the account code
        inside a rendered report - and is untouched. A class name is not a
        selector; the ancestor is half of it. */

    .rpt-row > .c-nm {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
    }

/*  ══ THE CODE COLUMN, FINANCIAL STATEMENTS ONLY - v31.108 (batch303) ═══════
    Owner: *"Bring back the report code column only in Financial statements,
    make the column items equal width."*

    ⚠⚠ "EQUAL WIDTH" IS READ AS A STATED TRACK, NOT A SHRINK-TO-FIT ONE, which
    is the only way every code cell can be the same width as every other. 78px
    is the width the column carried before v31.107 removed it, so the reader
    who asked for it back gets back the column he had.

    ⚠ THE CATEGORY IS NAMED BY A CLASS ON THE CARD, NOT BY `nth-child` OR BY A
    number in the stylesheet. `.with-code` is set by the page from the same
    constant the category list is built from; a `15` written here would be the
    second place that decides what Financial Statements is.
    ⚠ It is a DESCENDANT selector on purpose. `.rpt-row` sits inside
    `.rpt-body` inside the card, and a child combinator here would break the
    moment anything is wrapped - which is exactly how `:has(> .rpt-ph)` broke
    in v31.102. */
.rpt-list.with-code .rpt-head,
.rpt-list.with-code .rpt-row {
    grid-template-columns: 78px minmax(0, 1fr) auto;
}

    /*  ⚠ GOLD, because it is the handle a filer quotes - the same reason it
        was gold before. Tabular figures so the codes line up as characters as
        well as cells. */
    .rpt-list.with-code .c-cd {
        color: var(--nv-gold);
        font-variant-numeric: tabular-nums;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .rpt-row > .c-st {
        justify-self: end;
    }

/*  the render tag. ⚠ NOT `.pill` - that class already exists and carries a
    3px left border and its own background, which would have shipped looking
    broken. A prefix is not protection; a grep is. */
.rpt-tag {
    display: inline-block;
    border: 1px solid rgba(104,152,232,.45);
    border-radius: 999px;
    padding: 1px 9px;
    font-size: 11px;
    line-height: 16px;
    color: var(--nv-ink-2);
    white-space: nowrap;
}

    /*  green: the report is a stored query and v2 renders it as it stands. */
    .rpt-tag.native {
        border-color: rgba(92,186,140,.5);
        color: var(--nv-good);
    }

    /*  gold: the layout is compiled into `Novera.Reports` (.NET Framework) and
        has to be rebuilt as a v2 page. */
    .rpt-tag.rebuild {
        border-color: rgba(232,167,92,.5);
        color: var(--nv-gold);
    }

/*  ⚠⚠⚠ THE MIN-HEIGHT IS THE OWNER'S HEIGHT RULE, MEASURED.
    Owner: *"Financial statements tab height should equal profit & loss tab, at
    a minimum, but cannot be higher when it has more items because a pager is
    introduced with 5 items per page and the information on the LAST page
    only."*
    Five rows plus the legend do not fill the parameter card beside them, and a
    last page holding four rows is shorter still - so the ROW AREA is floored
    instead of the card, which keeps the height identical on all three pages.
    ⚠ MEASURED at 1440: both cards 391px, on page 1, page 2 and page 3. */
.rpt-body {
    min-height: 205px;
}

    /*  ⚠⚠⚠ AND IT COMES DOWN BY EXACTLY THE HEIGHT OF THE PLACEHOLDER LINE
        WHILE THAT LINE IS THERE. MEASURED: with the banner the card ran 431
        against the parameter card's 391 - 40px too tall, which is the one
        thing the owner's rule forbids (*"cannot be higher"*). `.rpt-ph` is
        40px including its margin, so the row area gives that 40 back.
        ⚠ `:has()` rather than a second class on the body, so this rule DELETES
        ITSELF the day the placeholder banner goes: nothing has to remember to
        put the 205 back. `.app-main:has(> .ai-side)` is the same idiom, already
        in this file.

        ⚠⚠⚠ v31.102 (batch297): THE SELECTOR NO LONGER NAMES THE CARD BY WHERE
        IT SITS. It was `.rpt-grid > .card:has(> .rpt-ph)`, and the list card
        stopped being a direct child of `.rpt-grid` the moment it was wrapped in
        `.rpt-main` - so the rule matched nothing, the body kept its 205px and
        the card ran 40px tall again, which is the one thing the owner's ruling
        forbids. **A DESCENDANT SELECTOR IS A LAYOUT ASSUMPTION IN DISGUISE**,
        and it fails silently: no build error, no gate, just a card that grew.
        Naming the card by CLASS makes it independent of any future nesting -
        the same reason nothing on this page is placed by `nth-child`. */
    .rpt-list:has(> .rpt-ph) .rpt-body {
        min-height: 165px;
    }

/*  the pager. ⚠ `margin-top: auto` is what pins it to the foot of a card that
    is taller than its content - the card is a flex column for exactly this. */
.rpt-foot {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: auto;
    padding-top: 8px;
    border-top: 1px solid rgba(104,152,232,.16);
}

    .rpt-foot > .rpt-pg {
        width: 26px;
        height: 26px;
        font-size: 15px;
        line-height: 1;
    }

        .rpt-foot > .rpt-pg[disabled] {
            opacity: .35;
            cursor: default;
        }

    .rpt-foot > .rpt-count {
        color: var(--nv-ink-3);
        font-size: 12.5px;
        font-variant-numeric: tabular-nums;
    }

    .rpt-foot > .rpt-of {
        margin-left: auto;
        color: var(--nv-ink-3);
        font-size: 12.5px;
    }

/*  the legend, which renders on the LAST page only (the owner's rule) */
.rpt-legend {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 8px;
    padding: 6px;
}

    .rpt-legend > .rpt-lg {
        color: var(--nv-ink-3);
        font-size: 12px;
        margin-right: 6px;
    }

/*  ⚠⚠⚠ RECENT AND RECURRENT ARE EQUAL HEIGHT AT EVERY WIDTH, NOT ONLY ON THE
    DESKTOP GRID. Owner: *"Recent and Recurrent reports must be the same
    height."* MEASURED at 768 they were 239 and 221, and at 390 they were 239
    and 269 - the placement rules that carried `align-self: stretch` sit inside
    a 1024 media query, so below it each card took its own content height.
    The rule that makes them agree belongs OUTSIDE the breakpoint, because the
    requirement does. */
/*  ⚠⚠⚠ v31.101 (batch296): BY CLASS, NEVER BY POSITION. The rendered report
    card renders only when a report has been run, and a sometimes-card inside a
    grid addressed by `nth-child` silently re-numbers every card after it.
    Recent and Recurrent were children 3 and 4 until the report joined the
    grid; now they are what they are called. */
.rpt-recent,
.rpt-recur {
    align-self: stretch;
}

/*  ══ RECENT OVER RECURRENT - v31.107 (batch302, owner) ════════════════════
    *"The Recent Card and the Recurrent report cards should be stacked in the
    same column in 2 rows and overall height of all items in this row should be
    the same."*

    ⚠⚠⚠ THE WRAPPER IS WHAT MAKES THE HEIGHTS AGREE, AND NO RULE STATES THEM.
    Two cards that must sit one above the other in one column ARE one grid
    item; once they are wrapped, `.rpt-stack` and `.rpt-ai` are two items on one
    row and the grid's own `align-self: stretch` levels them. **A height that is
    a consequence cannot go out of date; a height that is a number can.**

    ⚠⚠ `1fr 1fr` SPLITS THE STACK EVENLY, so the two cards match each other as
    well as matching the card beside them. If the AI card is the taller of the
    two items, the stack stretches and both its cards grow by half of the
    difference - which is why neither card carries a `min-height`. */
.rpt-stack {
    display: grid;
    grid-template-rows: 1fr 1fr;
    gap: 14px;
    min-width: 0;
    align-self: stretch;
}

/*  ══ THE CONTENT COLUMN'S OWN GRID - v31.102 (batch297, owner) ═════════════
    Owner, on the annotated 1920 screenshot: *"the blue box shows the space to
    be occupied by the Trial balance report, so the read report shifts down."*

        row 1   ── Financial Statements ──        Report parameters
        row 2   ──── the rendered report, spanning all three ────
        row 3   Recent          Recurrent         Read this report

    ⚠⚠⚠ THE PARAMETER AND AI CARDS ARE MEMBERS OF THIS GRID, NOT RAIL CARDS,
    AND THAT IS THE WHOLE REASON. **Nothing can span two columns of two
    different grids** - while they sat in a separate `.ai-side` shell column,
    the report could only ever reach the width of the list. Putting them here
    is what lets the report run underneath them, which is the space the owner
    boxed.

    ⚠⚠ AND THE CATEGORY CARD IS STILL OUTSIDE IT, which is the difference from
    v31.101. There the parameter card joined ONE flat grid with Category in it,
    so Recent fell into column 1 underneath Category. Here Category is a column
    of the OUTER grid and every one of these six cards is placed in the inner
    one - so the report spans the content, and never the sticky column.
    **That is what "should not cover the full width" and "the blue box" mean
    together: everything except Category.**

    ⚠⚠ NO `grid-row` IS NAMED. The report is a sometimes-card; auto-placement
    in DOM order puts it on its own row when it exists and closes the gap when
    it does not. An explicit row on the bottom three would leave an empty band
    and its gap on every page load where nothing has been run. */
.rpt-main {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
    align-content: start;
    min-width: 0;
}

/*  ── 1024-1365 · TWO COLUMNS ─────────────────────────────────────────────
    ⚠ THREE TRACKS DO NOT FIT HERE. The app's own side rail plus the Category
    column leave `.rpt-main` about 450px at 1024; split three ways that is
    150px a card. The parameter card and the AI card span instead, exactly as
    they did at this band in v31.101. */
@media (min-width: 1024px) {
    .rpt-main {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }

        /*  ⚠ THE LIST CARD IS A FLEX COLUMN so `.rpt-foot`'s `margin-top: auto`
            has something to push against - that is what pins the pager to the
            foot of a card taller than five rows. */
        .rpt-main > .rpt-list {
            grid-column: 1 / -1;
            display: flex;
            flex-direction: column;
        }

        .rpt-main > .rpt-params,
        .rpt-main > .rpt-out,
        .rpt-main > .rpt-ai {
            grid-column: 1 / -1;
        }

        /*  ⚠ AT THIS BAND THE STACK TAKES THE FULL WIDTH and the AI card
            keeps its own row below it, exactly as it did before. `.rpt-main` is
            about 450px here once the app rail and the Category column have had
            theirs; halving that for a stack of three-column cards would clip
            both of them to make room for a card that already fits. */
        .rpt-main > .rpt-stack {
            grid-column: 1 / -1;
        }
}

/*  ── 1366 AND UP · THREE COLUMNS, THE OWNER'S SHAPE ──────────────────────
    ⚠ THE THIRD TRACK CARRIES THE PARAMETER CARD AND THEN THE AI CARD, and it
    is the one with a floor: four date selects in two pairs need about 280px
    before the month and year boxes start clipping their own labels. The two
    left tracks are free `fr` so they take whatever is left and stay equal -
    Recent and Recurrent must match, and they carry the same three columns. */
@media (min-width: 1366px) {
    .rpt-main {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(280px, 1.15fr);
    }

        .rpt-main > .rpt-list {
            grid-column: 1 / 3;
        }

        .rpt-main > .rpt-params {
            grid-column: 3;
        }

        /*  ⚠⚠⚠ `1 / -1` HERE IS THE CONTENT GRID, NOT THE PAGE - the owner's
            blue box exactly: the report spans the list AND the parameter card,
            and stops at the Category column. */
        .rpt-main > .rpt-out {
            grid-column: 1 / -1;
        }

        /*  ⚠⚠ THE BOTTOM BAND IS TWO ITEMS NOW, NOT THREE: the stack across
            the two free tracks and the AI card in the third, on ONE row, so
            they level each other. */
        .rpt-main > .rpt-stack {
            grid-column: 1 / 3;
        }

        .rpt-main > .rpt-ai {
            grid-column: 3;
        }
}

/*  ⚠ THE TWO WRAPPED CARDS. `_RptParams` and `_RptAi` are included inside a
    wrapper div so they can be placed by class; their own `.card` root carries
    a max-width the other four override inline, so it is told to fill the cell
    here instead. */
/*  ⚠⚠⚠ `height: 100%` ON THE AI CARD IS WHAT THE OWNER ACTUALLY ASKED FOR
    (v31.107). The WRAPPER already stretched - the grid does that by itself -
    but the card INSIDE it kept its content height, so the measurement said the
    two items matched at 212px while the screenshot showed a 110px card beside a
    205px stack. **The probe was measuring the box, the reader sees the card.**
    ⚠ It is on `.rpt-ai` alone. `.rpt-params` shares this rule block but sits on
    the FIRST row beside the list, where stretching it would pull the Run button
    away from the fields it belongs to. */
.rpt-ai > .card {
    height: 100%;
}

.rpt-params > .card,
.rpt-ai > .card {
    max-width: none;
    width: 100%;
}

/*  ── Recent · Recurrent: three columns, no code, no tag ─────────────────── */
/*  ⚠⚠⚠ FIXED TRACKS, NOT `auto auto` (v31.108, batch303, owner: *"in Recent,
    make Entity column aligned in the column"* and *"Align 'Every' column in
    Recurrent options"*).

    **EVERY ROW HERE IS ITS OWN GRID.** The head is a sibling of the rows, not
    their container, so an `auto` track is sized by ONE row's content and the
    next row sizes its own - which is why "70853" and "84071" did not line up
    with each other or with the heading above them. `auto` aligns nothing
    across separate grids; only a stated width does.

    ⚠ 76px carries "year end", the longest value the Every column holds; 84px
    carries "today 08:14", the longest the date column holds. Both cells
    ellipsise rather than push the name track, so a longer value shortens
    itself instead of breaking the alignment this rule exists to create.
    ⚠⚠ THE REPORT LIST ABOVE SOLVED THIS THE SAME WAY AND SAYS SO: *"one grid
    for the head row and every data row, so the columns cannot drift apart"*.
    It has stated widths; this one had `auto` and drifted. */
.rpt-runhead,
.rpt-runrow {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 76px 84px;
    align-items: center;
    gap: 10px;
    padding: 5px 6px;
}

.rpt-runhead {
    color: var(--nv-ink-3);
    font-size: 11px;
    letter-spacing: .06em;
    text-transform: uppercase;
    border-bottom: 1px solid rgba(104,152,232,.16);
    padding-bottom: 6px;
    margin-bottom: 2px;
}

.rpt-runrow {
    border-radius: 6px;
    color: inherit;
    text-decoration: none;
    line-height: 18px;
}

    .rpt-runrow:hover {
        background: rgba(232,167,92,.06);
    }

    .rpt-runrow > .c-nm {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
    }

    .rpt-runhead > .c-en,
    .rpt-runrow > .c-en {
        color: var(--nv-ink-3);
        font-size: 12.5px;
        /*  ⚠ THE GUARD THAT KEEPS THE TRACK HONEST. A value longer than the
            track ellipsises inside it; without this it would widen the cell
            and the column would go crooked again for that one row. */
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .rpt-runhead > .c-dt,
    .rpt-runrow > .c-dt {
        color: var(--nv-ink-3);
        font-size: 12.5px;
        justify-self: end;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

        /*  ⚠ GOLD FOR DUE SOON, never red: a report that is due is not an
            error, it is a thing to do. Same distinction `.acc-grp-warn`
            makes. */
        .rpt-runrow > .c-dt > .rpt-due.hot {
            color: var(--nv-gold);
        }

/*  ── the parameter card's two-across pairs ──────────────────────────────
    ⚠ THIS IS NOT `.fin-grid3` AND NOT `.acc-fields`. `.fin-grid3` is three
    fixed fractions of the VIEWPORT down to 768px, which says nothing about
    the width of the 390px card these fields are actually drawn in;
    `.acc-fields` belongs to the account card. Two equal columns, no
    breakpoint, nothing that changes as the window moves. */
.rpt-p2 {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
}

/*  ══════════════════════════════════════════════════════════════════════════
    v31.290 (owner) — THE SUB-PARAMETER ZONE ON THE REPORT PARAMETERS CARD.

    Owner: *"When the report is selected, the sub-parameters are shown below the
    main date parameters."*

    ⚠⚠ IT BLEEDS TO THE CARD'S EDGES (`margin-inline: -<card padding>`) so the
    rules run the full width and the zone reads as a BAND rather than as another
    field with a box round it. The card pays `--sp-5`; the negative margin and
    the padding restore each other exactly, so nothing inside shifts.

    ⚠ THE GOLD SPINE ON THE LEFT IS THE ONLY COLOUR. The zone is structure, not
    a warning — gold in this system means "attend to this", so it gets the
    2px edge and a 4.5% wash, nothing louder.
    ══════════════════════════════════════════════════════════════════════════ */
.rpt-subzone {
    position: relative;
    margin: 12px calc(var(--sp-5, 20px) * -1) 12px;
    padding: 12px var(--sp-5, 20px) 2px;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    background: rgba(232, 167, 92, .045);
}

    .rpt-subzone::before {
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        bottom: 0;
        width: 2px;
        background: var(--nv-gold-dim);
    }

    /*  ⚠ A REPORT WITH NO OPTIONS STILL RENDERS THE ZONE (see the partial's own
        note: Run must not move when the reader tries another report), so the
        empty state drops the wash and the spine goes quiet rather than gold —
        it is saying "nothing here", not "look here". */
    .rpt-subzone.empty {
        background: none;
    }

        .rpt-subzone.empty::before {
            background: var(--line);
        }

.rpt-zt {
    font-size: 11.5px;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--nv-gold);
    margin-bottom: 10px;
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
}

    /*  the report's own name, so the zone says WHOSE options these are */
    .rpt-zt .for {
        color: var(--nv-ink-3);
        letter-spacing: .02em;
        text-transform: none;
        font-size: 12px;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

.rpt-zempty {
    font-size: 13px;
    color: var(--nv-ink-3);
    line-height: 1.5;
    padding-bottom: 10px;
}

/*  ── the KV section multi-select ──────────────────────────────────────────
    ⚠⚠⚠ NOT A `<select multiple>`. The house rule, already written down: *"the
    native select popup is not ours to style — the real answer for any dropdown
    whose list matters is the app's OWN picker."* A native multiple is also the
    control nobody can operate: ctrl-click to add, and one stray click clears
    the lot.
    ⚠⚠ THE CHECKBOXES ARE REAL INPUTS IN THE FORM. The panel is a DISCLOSURE,
    not a control — with script off the list stands open and the GET still
    carries `sec=1&sec=2`. */
/*  v31.456 (owner) — CC004's exemption boxes, one per property, and they follow the
    property selection: a row for an unticked apartment is hidden AND disabled, because a
    figure that cannot reach a form must not travel with the run. */
.rpt-ex { display: block; }
.rpt-exlist { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.rpt-exrow { display: flex; align-items: center; gap: 10px; }
    .rpt-exrow .nm { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .rpt-exrow .input { flex: 0 0 68px; text-align: right; }
    .rpt-exrow.off { display: none; }

.rpt-ms {
    position: relative;
}

    /*  ⚠ `.input` GIVES IT THE FIELD'S FACE; these three lines give it a
        BUTTON's behaviour. Without `text-align: left` the label centres, which
        no other field on the card does. */
    .rpt-ms .rpt-msbtn {
        width: 100%;
        text-align: left;
        cursor: pointer;
        font-family: inherit;
        gap: 8px;
    }

        .rpt-ms .rpt-msbtn .t {
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .rpt-ms .rpt-msbtn .chev {
            margin-left: auto;
            flex: 0 0 auto;
            color: var(--nv-ink-3);
        }

        .rpt-ms .rpt-msbtn[aria-expanded="true"] .chev {
            transform: rotate(180deg);
        }

.rpt-mspanel {
    margin-top: 8px;
    border: 1px solid var(--line);
    border-radius: var(--r-input, 6px);
    background: rgba(3, 6, 14, .55);
    overflow: hidden;
}

    /*  ⚠⚠ HIDDEN WITH `[hidden]`, WHICH THE SCRIPT SETS — never `display: none`
        in this rule. With no script the attribute is never applied and the list
        renders open, which is the working fallback rather than a dead control.
        The page's own `[hidden]` handling does the rest. */
    .rpt-mspanel[hidden] {
        display: none;
    }

.rpt-msall {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 11px;
    font-size: 13.5px;
    color: var(--nv-ink-2);
    border-bottom: 1px solid var(--line);
    cursor: pointer;
}

/*  ⚠ 240px IS FOUR AND A HALF ROWS ON PURPOSE. A list that ends flush with the
    panel looks complete; one cut mid-row says "there is more" without a
    sentence saying so. */
.rpt-mslist {
    max-height: 240px;
    overflow-y: auto;
}

.rpt-msrow {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 11px;
    font-size: 14px;
    color: var(--nv-ink-2);
    cursor: pointer;
    min-width: 0;
}

    .rpt-msrow + .rpt-msrow {
        border-top: 1px solid rgba(104, 152, 232, .08);
    }

    /*  the code is the thing a reader matches against a filing, so it is the
        one part that never ellipsises */
    .rpt-msrow .code {
        flex: 0 0 26px;
        font-family: Consolas, ui-monospace, monospace;
        font-size: 12.5px;
        color: var(--nv-gold);
    }

    .rpt-msrow .nm {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/*  ── the AI card's observation rows ─────────────────────────────────────── */
.rpt-obs {
    display: grid;
    grid-template-columns: 18px minmax(0, 1fr);
    align-items: start;
    gap: 8px;
    padding: 5px 4px;
    border-radius: 6px;
    color: inherit;
    text-decoration: none;
    line-height: 1.45;
}

    .rpt-obs:hover {
        background: rgba(232,167,92,.06);
    }

    .rpt-obs > .rpt-oi {
        font-size: 11px;
        line-height: 20px;
        color: var(--nv-ink-3);
        text-align: center;
    }

        .rpt-obs > .rpt-oi.hot {
            color: var(--nv-good);
        }

        .rpt-obs > .rpt-oi.warn {
            color: var(--nv-gold);
        }

    .rpt-obs > .rpt-ol {
        font-size: 13.5px;
        min-width: 0;
    }

/*  ⚠⚠⚠ A DISABLED CONTROL MUST NOT LOOK LIKE ITS LIVE NEIGHBOUR - the house
    rule, and the LOOK is what caught this. `Run report` is `disabled` because
    nothing behind the page can run one yet, and it still rendered as the full
    bright primary gradient: a button that says "press me" and cannot.
    ⚠ THERE IS NO APP-WIDE `.btn[disabled]` RULE IN THIS SOLUTION - each page
    scopes its own (`.acc-acts > .btn[disabled]`, `.ctl3 .btn[disabled]`), so
    this is the Reports page's copy of the same `.42` opacity rather than a new
    invention or a change to a shared component.
    ⚠⚠ THE FIELDS ARE DELIBERATELY NOT DIMMED. The house draws a READ-ONLY
    VALUE as a disabled input with exactly the same box as an editable one, and
    breaking that here would make the entity field look broken instead of
    fixed. Only the ACTIONS say they cannot act. */
.rpt-app .btn[disabled] {
    opacity: .42;
    cursor: default;
}

/*  ⚠⚠⚠ THE PLACEHOLDER LINE, AND IT IS DELIBERATELY NOT SUBTLE.
    `schema/diag-reports.sql` v2 has not been run, so the columns of
    `RefReports` are unknown and the rows on this page come from a placeholder
    source. **A screen full of plausible figures with nothing behind it is the
    one thing worse than an empty screen: it gets believed.** */
.rpt-ph {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 10px;
    padding: 6px 10px;
    border-left: 2px solid var(--nv-gold);
    border-radius: 0 6px 6px 0;
    background: rgba(232,167,92,.07);
    color: var(--nv-gold);
    font-size: 12.5px;
    line-height: 1.45;
}

/*  ⚠ AN EMPTY CATEGORY IS A SENTENCE, NOT A BROKEN CARD - and it is the same
    shape a real category with no permitted reports will take once the two
    permission gates are in. */
.rpt-none {
    padding: 14px 6px;
    color: var(--nv-ink-3);
    font-size: 13.5px;
}

/*  ══════════════════════════════════════════════════════════════════════════
    v31.86 (batch281, owner) - THE AI TOKENS CARD ON /home
    Owner: *"make a new card, called AI Tokens, which tracks the tokens ... to
    track EVERY call made to the Ai/APIs so we can track usage and bill
    clients"*, then: *"First card looks very good. Lets put it as a placeholder
    right now and then put the instrumentation in a separate round."*
    ══════════════════════════════════════════════════════════════════════════ */

.tok-hero {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 6px;
}

    .tok-hero > .tok-n {
        font-size: 24px;
        font-weight: 100;
        font-variant-numeric: tabular-nums;
        color: var(--nv-gold-bright);
    }

    .tok-hero > .tok-u {
        font-size: 12.5px;
        color: var(--nv-ink-3);
    }

    .tok-hero > .tok-p {
        margin-left: auto;
        font-size: 12.5px;
        color: var(--nv-ink-3);
    }

.tok-cost {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-top: 2px;
    font-size: 13px;
}

    .tok-cost > .tok-cl {
        color: var(--nv-ink-3);
    }

    .tok-cost > .tok-cv {
        margin-left: auto;
        font-variant-numeric: tabular-nums;
    }

/*  ⚠ THE BAR TRACK IS `minmax(0, 1fr)`, so a long entity name shrinks the
    NAME rather than pushing the figure out of the card. */
.tok-row {
    display: grid;
    /*  ⚠⚠ v31.92: THE BAR SHARES THE SLACK WITH THE NAME. It was a FIXED 64px
        beside a `1fr` name, which was right in a 333px card and wrong the
        moment the card went full width: at 476px per column the name track ate
        every extra pixel and the bar floated off on its own, a hand's width
        from the label it measures. Both tracks flex now, so the bar GROWS with
        the column instead of the gap doing it - and 64px stays its floor, so
        nothing about the narrow card changes.
        > ⚠ **A FIXED TRACK BESIDE A `1fr` TRACK MEANS THE `1fr` TAKES EVERY
        > PIXEL THE CONTAINER GAINS.** Decide which one should grow. */
    grid-template-columns: 52px minmax(0, 1fr) minmax(64px, 1.15fr) auto;
    align-items: center;
    gap: 8px;
    padding: 4px 4px;
    border-radius: 6px;
    color: inherit;
    text-decoration: none;
    font-size: 13px;
    line-height: 18px;
}

    /*  ⚠ THE HOVER IS SCOPED TO `a.tok-row`, and that is not tidiness. The
        card is a PLACEHOLDER: its rows are spans because there is nothing to
        open, and a row that lights up under the pointer is a promise the page
        cannot keep. The moment the detail page exists the rows become anchors
        and the hover arrives with them. */
    a.tok-row:hover {
        background: rgba(232,167,92,.06);
    }

    .tok-row > .tok-cd {
        color: var(--nv-gold);
        font-variant-numeric: tabular-nums;
    }

    .tok-row > .tok-nm {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
    }

    .tok-row > .tok-bar {
        height: 6px;
        border-radius: 3px;
        background: rgba(104,152,232,.18);
        overflow: hidden;
    }

        .tok-row > .tok-bar > i {
            display: block;
            height: 100%;
            border-radius: 3px;
            background: linear-gradient(90deg, rgba(232,167,92,.9), rgba(232,167,92,.45));
        }

    .tok-row > .tok-v {
        justify-self: end;
        font-variant-numeric: tabular-nums;
        white-space: nowrap;
    }

    /*  the "n more entities" tail: no code, no bar - it spans the two middle
        tracks so the figure still lines up with the rows above it. */
    .tok-row.tok-rest > .tok-nm {
        grid-column: 1 / 4;
        color: var(--nv-ink-3);
        font-size: 12.5px;
    }

/*  ⚠ 24px CHIPS, NOT THE HOUSE 32. This card is a HALF column from 1024 up
    (`1fr · 0.5fr · 0.5fr`), and two 32px chips with their 13px padding leave
    no room for the figures beneath them. The chip's own `flex: 1 1 0` from
    tokens.css still splits the row equally. */
.tok-chips {
    margin: 2px 0 6px;
}

    .tok-chips > .chip {
        height: 24px;
        padding: 0 10px;
        font-size: 12px;
    }

/*  ══════════════════════════════════════════════════════════════════════════
    v31.88 (batch283, owner - R7.5) - THE ENTITY-SWITCH CONFIRM ROW
    Owner, asked and answered: *"always offer it and confirm first, naming what
    is lost."*
    ══════════════════════════════════════════════════════════════════════════

    ⚠⚠ IT IS A ROW OF THE SHEET, NOT A DIALOG. This app has a written rule
    against modals, earned on the address delete: *"a two-step chip does that
    without a modal dialog, which this app does not use anywhere."* The
    sentence appears under the entity the reader actually chose, and the second
    tap on that same row goes through.
    ⚠ GOLD, NOT RED. Nothing has gone wrong and nothing is broken - the reader
    is being told the cost of a thing they may well still want to do. Same
    distinction `.acc-grp-warn` and `.acc-tywarn` make.
    ⚠ The indent lines it up under the entity NAME rather than under the
    building icon, so it reads as belonging to that row. */
.ent-warn {
    padding: 2px 12px 8px 46px;
    color: var(--nv-gold);
    font-size: 12px;
    line-height: 1.45;
}

    /*  the call to action, on its own line and a shade brighter than the
        warning above it: the sentence explains, this one is what to do next. */
    .ent-warn > .ent-warn-go {
        display: block;
        margin-top: 3px;
        color: var(--nv-gold-bright);
    }

/*  ⚠ AN ARMED ROW MUST LOOK ARMED. Without this the only tell is the sentence
    beneath it, and on a tree of similar rows that is not enough to say WHICH
    one the next tap will fire.

    ⚠⚠⚠ AND IT IS A `box-shadow`, NOT A `background`, BECAUSE AN INLINE STYLE
    CANNOT BE BEATEN BY A SELECTOR. The sheet's entity rows carry
    `style="...background:transparent;..."` in the markup, and an inline
    declaration outranks every author rule however specific - MEASURED: the
    first cut of this rule was `.ent-sheet .fld[data-arm="1"] { background }`
    at (0,3,0) and the armed row rendered exactly like its neighbours.
    > ⚠ **SPECIFICITY DOES NOT REACH AN INLINE STYLE.** The choices are
    > `!important` or a property the inline style does not set. `!important` is
    > not the house answer (the ordering rule), and `box-shadow` is not set
    > inline - so the ring paints and nothing has to be overridden.
    ⚠ THE INSET FILL AND THE EDGE ARE ONE DECLARATION: the spread paints the
    whole box, the 3px bar marks which row it is. Neither costs layout, which
    is the same reason the VAT code cell's hover ring is a shadow. */
.ent-sheet .fld[data-arm="1"] {
    box-shadow: inset 0 0 0 999px rgba(232,167,92,.12),
                inset 3px 0 0 0 var(--nv-gold-bright);
}

/*  ══ v31.90 (batch285, production) - BANK REC: THE FOOTER STAYS AT THE FOOT ══
    Reported live: *"The footer, including the horizontal rule, should behave
    consistently with the Ledger view. It should remain positioned at the
    bottom of the flip card rather than appearing immediately after 'Nothing
    for this month'."*

    ⚠⚠ THE FAULT WAS NEVER THE FOOTER - IT WAS THE EMPTY STATE NOT GROWING.
    The two faces of `.fin-flip` share ONE grid cell (`grid-area: 1/1`), so the
    row is as tall as the TALLER face, and `.fin-flip .flip-face > .card` is
    already `flex: 1` - the short face's CARD does stretch. Its CONTENTS did
    not: with rows, a fixed 400px scroller fills the middle; with none, a 40px
    sentence sat there and the rule + totals + C/FWD rode up right behind it,
    leaving the blank space BELOW them. The card is already
    `display:flex; flex-direction:column` (`.flip .card`), so the whole fix is
    to let the sentence take the room the scroller would have taken.
    > ⚠ **A CARD THAT STRETCHES IS NOT A CARD WHOSE CONTENTS STRETCH.**
    ⚠ `flex: 1 1 auto` AND NOT A FIXED 400px: the height then follows whatever
    the other face actually is - a 3-row month, a 40-row month, or an empty one
    on BOTH sides (where the pair simply stays short, together). A hard-coded
    400 would have re-introduced the mismatch the moment the scroller changed. */
.br-none {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 16px 4px;
}

/*  ══ v31.349 (owner, item 8) — A RECONCILED ROW STRIKES ITS AMOUNT ═════════
    Owner: *"reconciled items currently show strikethrough across two complete
    rows. Change this so strikethrough applies only to the Amount
    column/value. Retain the existing reconciled status/behaviour otherwise."*

    ⚠⚠⚠ THIS RULE IS ONLY HALF THE FIX AND CANNOT WORK ALONE. The strike used
    to be an inline `text-decoration` on `.br-row` itself, and `text-decoration`
    INHERITS INTO DESCENDANTS IN A WAY A DESCENDANT CANNOT CANCEL — there is no
    `text-decoration: none` that removes an ancestor's line. So the declaration
    had to come off the row in `BankRec.cshtml`; this is where it lands instead.

    ⚠ THE DIMMING IS THE "OTHERWISE". A reconciled row still reads as done from
    across the card; what changed is that the date, the reference, the TRN and
    the description are legible again, and only the figure is struck. */
.br-row.is-rec {
    opacity: .6;
}

    .br-row.is-rec > .amt {
        text-decoration: line-through;
    }

/*  ══ v31.90 (batch285, production) - THE TRN OPENS THE TRANSACTION ══════════
    The link IS the gold TRN text, so it inherits the row's colour; only the
    affordance is added.
    ⚠ A DOTTED UNDERLINE AT REST, SOLID ON HOVER. ⚠⚠ v31.349 CHANGED WHY. The
    original reason was that the row already struck this text and a solid
    underline would have read as a second strike on a 13px line — the strike has
    since moved to the amount, so that no longer applies. The dotted rest state
    stays because it is the quieter affordance on a dense grid, which is the
    reason worth keeping. */
.br-row .trn-a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dotted rgba(232,167,92,.55);
}

    .br-row .trn-a:hover,
    .br-row .trn-a:focus-visible {
        border-bottom-color: var(--nv-gold-bright);
        border-bottom-style: solid;
    }

/*  ══════════════════════════════════════════════════════════════════════════
    v31.92 (batch287, owner) - THE AI TOKENS CARD IN THREE COLUMNS
    Owner: *"in AI Tokens in the home page, please add one more column so it
    becomes a 3 column responsive card."*

    ⚠⚠⚠ A CONTAINER QUERY, NOT A MEDIA QUERY, AND IT IS FORCED - NOT A
    PREFERENCE. This card's width does not follow the viewport: it is a
    fraction of a grid column, so ONE 1920px screen gives it ~333px in the
    `.dash-top` row and ~1410px stacked underneath. A `@media (min-width:1366px)
    { three columns }` would put three columns into 333px and one column into
    1410px - **the exact opposite of the truth in both cases.**
    > ⚠⚠ **ASK THE BOX HOW WIDE IT IS, NOT THE WINDOW.** `.acc-wrap` on Account
    > Setup already does this for the same reason; this is the second caller.

    ⚠ THE THRESHOLDS ARE MEASURED, not chosen. One list column needs
    52px code + 8 gap + a readable name + 8 + 64px bar + 8 + the figure -
    about **234px** before the name starts clipping to one letter, which is the
    v31.90 fault this project has already paid for once.
      · under 470px  -> ONE column. The chips return and switch the two lists,
                        which is exactly the v31.86 card.
      · 470-729px    -> TWO columns: the figure joins the entity list.
      · 730px and up -> THREE columns, both axes on screen at once.
    ⚠⚠ `align-items: start` IS LOAD-BEARING on a grid whose rows are all
    `auto` inside a card that stretches to its neighbours (`.dash-top > .card
    { height: 100% }`) - without it the three columns stretch and the bars
    drift apart vertically. Same rule the Reports grid needed at 1366.
    ══════════════════════════════════════════════════════════════════════════ */
.tok-card {
    container-type: inline-size;
    container-name: tokcard;
}

.tok-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: 2px 18px;
}

    /*  the column heading: present only when a column is actually a column.
        At one column the chips do this job, so the headings stand down and
        the card reads exactly as it did before v31.92. */
    .tok-ch {
        display: none;
        /*  ⚠ 13px, NOT 12. The font-floor gate is a COUNT and it caught this
            round adding two declarations below it. The card is FULL WIDTH now,
            so the reason a 333px card had for going small no longer exists -
            and "there was room" is the only honest reason to take a size back
            up. Nothing outside these two rules moved. */
        font-size: 13px;
        color: var(--nv-ink-3);
        padding: 0 4px 4px;
    }

/*  ⚠ THE FAILED LINE IS NOT A `.muted` INLINE STYLE ANY MORE - it was
    `font-size:11.5px` written on the element, which no ladder can ever reach.
    12px is the card's own smallest step and it is now a class like everything
    else on the card. */
.tok-failed {
    margin-top: 8px;
    /*  ⚠ THIS LINE USED TO BE AN INLINE `font-size:11.5px` ON THE ELEMENT,
        which no type ladder can ever reach. It is a class at 13px now - the
        floor - so the app is one inline size lighter than before this round,
        not one heavier. */
    font-size: 13px;
    font-weight: 100;
    color: var(--nv-ink-3);
}

@container tokcard (min-width: 470px) {
    .tok-grid {
        /*  the figure column is content-sized and the lists take the rest:
            a headline number must not be squeezed by a list beside it. */
        grid-template-columns: minmax(150px, .8fr) minmax(0, 1fr);
    }

        /*  ⚠ THE CHIPS GO THE MOMENT A SECOND COLUMN EXISTS. They are the
            one-column fallback for the two lists, not a control in their own
            right, and leaving them on beside a visible list would offer to
            switch to a list already on screen. */
        .tok-grid > .tok-chips {
            display: none;
        }

        .tok-grid > .tok-col-call {
            /*  at two columns the call list sits UNDER the entity list, in the
                same track - still both axes, still one scroll. */
            grid-column: 2;
        }

    .tok-ch {
        display: block;
    }
}

@container tokcard (min-width: 730px) {
    .tok-grid {
        grid-template-columns: minmax(150px, .7fr) minmax(0, 1fr) minmax(0, 1fr);
    }

        .tok-grid > .tok-col-call {
            grid-column: 3;
        }
}

/*  ⚠⚠ THE CALL ROW HAS NO CODE CELL. A call type has a name and no number, so
    the row is name · bar · figure and the 52px code track is DROPPED rather
    than rendered empty - an empty first track would indent every name on this
    side and the two lists would stop lining up with each other. */
.tok-col-call > .tok-row {
    grid-template-columns: minmax(0, 1fr) minmax(64px, 1.15fr) auto;
}

/*  ══════════════════════════════════════════════════════════════════════════
    v31.93 (batch288, owner) - THE AI TOKENS COLUMN SEPARATORS
    Owner: *"please style the card by having a 'vertical line separator' which
    does not touch top or bottom border of the card itself."*

    ⚠⚠⚠ A `border-left` CANNOT DO THIS. A border runs the full height of the
    box it is on, so it would reach the card's padding edge at both ends - and
    the ask is precisely that it must NOT. A pseudo-element can be inset from
    both ends; a border cannot be inset from either.
    > ⚠ **"A LINE THAT STOPS SHORT" IS NOT A BORDER, IT IS A DRAWN ELEMENT.**

    ⚠⚠ IT HANGS IN THE GAP, NOT ON THE COLUMN. `left: calc(-.5 * var(--tok-gap))`
    centres it in the 18px grid gap, so the two lists keep equal breathing room
    on each side. Putting it on the column's own left edge would push the text
    of columns 2 and 3 off-centre by half a gap.
    ⚠ AND IT ONLY EXISTS WHERE THERE IS A COLUMN BOUNDARY TO MARK - inside the
    container queries. At one column the "separator" would be a line above a
    stacked block, which is a rule, not a separator, and the card already has
    the chips there.
    ⚠⚠ `position: relative` GOES ON THE COLUMN, NOT THE GRID: an absolute child
    of the GRID would be positioned against the whole card and would need to
    know where the tracks fall. Anchored to its own column it cannot drift.
    ══════════════════════════════════════════════════════════════════════════ */
.tok-grid {
    --tok-gap: 18px;
    /*  the two measured offsets that carry the separator from the column's box
        out to 10px inside the card's border - see the rule below for the
        measurement they come from. */
    /*  ⚠ v31.95 (batch290, owner): *"Increase clearance from the top at 25px
        and bottom 15px."* The lift and drop are what carry the line from the
        COLUMN's box out to a clearance measured against the CARD, so the two
        numbers are derived, not chosen:
            column top    sits 51px below the card's top border  -> 51 - 25 = 26
            column bottom sits 15px above the card's bottom      -> 15 - 15 =  0
        A drop of 0 puts the line's foot exactly ON the grid's bottom edge,
        which is what a 15px clearance from the card's border IS on this card. */
    --tok-lift: 26px;
    --tok-drop: 0px;
}

@container tokcard (min-width: 470px) {
    .tok-grid > .tok-col {
        position: relative;
    }

        /*  ⚠ EVERY COLUMN BUT THE FIRST. `:not(:first-child)` would also catch
            the chips row in the markup order, so the separator is asked for by
            the two LIST columns explicitly - they are the only boundaries. */
        .tok-col-ent::before,
        .tok-col-call::before {
            content: "";
            position: absolute;
            left: calc(-.5 * var(--tok-gap));
            /*  ⚠⚠⚠ v31.93 STOPPED 10px INSIDE THE COLUMN AND THAT WAS TOO
                SHORT. Owner: *"pull these lines to be up near the top of the
                card border ... and the bottom over the bottom of tok-grid."*
                The line hangs off a COLUMN, but the ask is expressed against
                the CARD, so the two have to be converted - MEASURED on the
                delivered card, and identical at 1024, 1440 and 1920:

                    the column's TOP    sits 51px below the card's top border
                                        (14px card padding + the h2 + its 6px)
                    the column's BOTTOM sits 15px above the card's bottom
                                        (14px padding + the 1px border)
                    the grid's bottom   IS the column's bottom

                So a 41px lift and a 5px drop leave the line **10px clear of
                each border** - up near the top as asked, past the grid's
                bottom as asked, and still touching neither.
                > ⚠⚠ **A NUMBER LIKE THIS IS ONLY HONEST IF SOMETHING RE-CHECKS
                > IT.** The probe asserts the rendered line against the card's
                > border, so a change to the heading's type is caught by the
                > gate rather than by the owner. */
            top: calc(-1 * var(--tok-lift));
            bottom: calc(-1 * var(--tok-drop));
            width: 1px;
            /*  ⚠ v31.95 (batch290, owner): the BLUE hairline, not the gold one.
                It is the same colour `.tok-bar`'s own track already uses on
                this card, so the separator reads as structure rather than as
                another gold accent competing with the figures. */
            background: rgba(104, 152, 232, .22);
            pointer-events: none;
        }
}

/*  ⚠⚠⚠ AT TWO COLUMNS THE BOUNDARY NEEDS **TWO SEGMENTS**, NOT ONE.
    The call list sits UNDER the entity list in the same track, so the entity
    column's own box stops less than half way down the card - MEASURED at 768:
    a single segment ended **140px above the card's bottom border**, which is
    the fault this rule fixes and which v31.93 shipped (less visibly, because
    it also stopped 10px short at the top).
    > ⚠⚠ **A LINE THAT MUST SPAN TWO STACKED BOXES CANNOT HANG OFF ONE OF
    > THEM.** Either both draw their own half, or neither reaches.

    ⚠ THE SEAM IS CLOSED BY THE ROW GAP, NOT PAPERED OVER: `.tok-grid`'s row
    gap is 2px, so the upper segment runs 2px past its own bottom and the lower
    one starts at its own top. The two meet exactly, and there is no overlap to
    paint a double-density pixel. */
@container tokcard (min-width: 470px) and (max-width: 729.98px) {
    .tok-col-ent::before {
        bottom: -2px;
    }

    .tok-col-call::before {
        top: 0;
    }
}

/*  ⚠ v31.93 (batch288, owner) - THE FIND SHEET'S SCOPE LINE. It answers one
    question and must not compete with the search box under it, so it reads as
    a caption in the entity's own gold rather than as a heading.
    ⚠⚠ NOT SMALLER THAN THE SHEET'S FLOOR: `.find-sheet` sets
    `font-size: var(--fs-caption)` for everything inside it (the v21.0 owner
    rule - "We have a rule about minimum font size"), and this line inherits it
    rather than setting a size of its own. */
.find-scope {
    margin: 0 0 10px;
    color: var(--nv-gold);
    font-weight: 100;
}

/*  ══ v31.99 (batch294, owner) - THE RENDERED REPORT ═══════════════════════
    Owner: *"Run should render in the page."*

    ⚠⚠ IT WAS A SIBLING OF `.rpt-grid` IN v31.99 AND IS A MEMBER OF `.rpt-main`
    FROM v31.102 (batch297). That was only safe once the grid stopped placing
    cards by `nth-child`: a card that renders ONLY when something has been run
    re-numbers every card after it, so every placement rule names a CLASS.
    ⚠ There is no width rule here: `.rpt-main > .rpt-out { grid-column: 1/-1 }`
    - ABOVE, in this file, with the tracks it spans - is what makes it span the
    list and the parameter card while stopping at the sticky Category column.
    That is the owner's blue box. */
/*  ⚠ NO `margin-top` HERE ANY MORE. It was a sibling of the grid in v31.99
    and needed its own spacing; it is a grid ITEM from v31.101 and the grid's
    own 14px gap is what separates it. Both would have made 28. */
.rpt-out {
    min-width: 0;
}

.rpt-outhead {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}

/*  ⚠ WHICH ENTITY, WHICH CURRENCY, WHICH PERIOD - the three an accountant
    checks before believing a figure, and the three v1 puts in its page header.
    13px is the floor, not a choice: this line is read, not decoration. */
.rpt-outmeta {
    font-size: 13px;
    color: var(--nv-ink-3);
    white-space: nowrap;
}

/*  ⚠⚠ THE TABLE SCROLLS INSIDE ITS OWN BOX, NEVER THE PAGE. A report is the
    one surface that legitimately carries more columns than a phone has room
    for, and a page that scrolls sideways loses its own navigation. */
.rpt-outscroll {
    overflow-x: auto;
    margin: 0 -4px;
}

.rpt-tbl {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

    .rpt-tbl th {
        padding: 10px 10px;
        text-align: left;
        font-size: 13px;
        font-weight: 600;
        letter-spacing: .04em;
        text-transform: uppercase;
        color: var(--nv-ink-3);
        white-space: nowrap;
        border-bottom: 1px solid rgba(104,152,232,.22);
    }

    .rpt-tbl td {
        padding: 7px 10px;
        border-bottom: 1px solid rgba(104,152,232,.07);
    }

    /*  ⚠ FIGURES ARE TABULAR AND RIGHT-ALIGNED, or a column of money cannot be
        scanned down. `font-variant-numeric` is what keeps the digits in step. */
    .rpt-tbl .c-n {
        text-align: right;
        white-space: nowrap;
        font-variant-numeric: tabular-nums;
    }

    .rpt-tbl .c-cd {
        white-space: nowrap;
        color: var(--nv-blue-ink, #9dbcf2);
        font-variant-numeric: tabular-nums;
    }

    .rpt-tbl .c-nm {
        min-width: 0;
    }

/*  the band headings - v1's four UNION groups, named */
.rpt-tbl .rpt-band td {
    padding: 14px 10px 6px;
    font-size: 13px;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--nv-ink-3);
    border-bottom: 0;
}

/*  ⚠⚠⚠ THE OUT-OF-BALANCE BAND IS COLOURED BECAUSE IT IS THE ONE AN
    ACCOUNTANT OPENS THIS REPORT TO FIND. Left looking like another row it
    reads as data; it is a fault. */
.rpt-tbl .rpt-band.warn td,
.rpt-tbl .rpt-line.warn td {
    color: var(--nv-gold-bright);
}

.rpt-tbl tfoot td {
    padding: 12px 10px;
    font-weight: 600;
    font-size: 15px;
    border-top: 1px solid rgba(104,152,232,.30);
    border-bottom: 0;
}

/*  ⚠⚠⚠ THE OWNER'S ACCEPTANCE TEST, WHEN IT FAILS. *"totals must be in
    balance."* If the two columns do not agree the report is wrong, and this
    line is what says so instead of letting a reader add it up themselves. */
.rpt-oob {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 12px;
    padding: 10px 12px;
    border-radius: var(--r-input);
    font-size: 13px;
    color: var(--nv-gold-bright);
    background: rgba(232,167,92,.08);
    border: 1px solid rgba(232,167,92,.30);
}

.rpt-oobn {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    white-space: nowrap;
}

/*  a report row that IS a door, and the one currently open */
.rpt-row.live {
    cursor: pointer;
}

    .rpt-row.live:hover {
        background: rgba(104,152,232,.08);
    }

    .rpt-row.on {
        background: rgba(232,167,92,.10);
    }

/*  ══ v31.101 (batch296, owner) - THE APP SAYS IT IS WORKING ═══════════════
    Owner: *"I had to click twice because the UI did not show an indication it
    was processing ... create a standard usage for this for all process when
    user is interacting with system and waiting for results."*

    ⚠⚠⚠ TWO PARTS, AND THEY ANSWER TWO DIFFERENT QUESTIONS.
      `.nv-progress`  - "the page is going somewhere"   (top of the window)
      `.nv-spin`      - "THIS control is what I pressed" (in the button)
    A reader needs both: the bar alone does not say which button took, and the
    spinner alone does not survive the page starting to unload.
    → the door is `/Pages/Shared/Toolkit/_NvBusy.cshtml` */

/*  ⚠ FIXED TO THE VIEWPORT AND ABOVE EVERYTHING, because it has to be visible
    from wherever the reader is looking - including a page scrolled to its
    foot, which is exactly where a Run button tends to be. */
.nv-progress {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 2px;
    z-index: 9999;
    pointer-events: none;
    opacity: 0;
    transition: opacity .15s ease;
}

    .nv-progress.on {
        opacity: 1;
    }

    /*  ⚠⚠ IT IS AN INDETERMINATE BAR AND IT HAS TO BE. Nothing here knows how
        far a server request has got, and a bar that claims a percentage it
        cannot know is a lie that reads as a freeze the moment it stalls at
        90%. A travelling sliver promises only "something is happening". */
    .nv-progress i {
        display: block;
        height: 100%;
        width: 34%;
        border-radius: 0 2px 2px 0;
        background: linear-gradient(90deg,
                    rgba(232,167,92,0) 0%,
                    var(--nv-gold-bright) 55%,
                    rgba(232,167,92,0) 100%);
        transform: translateX(-40%);
        animation: nv-prog 1.05s ease-in-out infinite;
    }

@keyframes nv-prog {
    0%   { transform: translateX(-40%); }
    100% { transform: translateX(300%); }
}

/*  ⚠ 14px AND `currentColor`, so one rule serves a gold primary button, a
    ghost button and a link without three colour variants to keep in step. */
.nv-spin {
    display: inline-block;
    width: 14px;
    height: 14px;
    margin-left: 8px;
    vertical-align: -2px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    opacity: .85;
    animation: nv-spin .68s linear infinite;
}

@keyframes nv-spin {
    to { transform: rotate(360deg); }
}

/*  ⚠⚠⚠ `pointer-events: none` IS THE WHOLE FIX FOR THE DOUBLE CLICK, and it
    is deliberately NOT `disabled`. A disabled submit button is not sent with
    its form, so a page that reads which button was pressed would silently lose
    that on every submit - and this solution has several. This stops the second
    press without touching what is posted. */
.is-busy {
    pointer-events: none;
    opacity: .78;
}

/*  ⚠⚠ SOMEBODY WHO HAS ASKED FOR LESS MOTION STILL HAS TO BE TOLD THE APP IS
    WORKING. The animation stops; the indicator does not disappear. */
@media (prefers-reduced-motion: reduce) {
    .nv-progress i,
    .nv-spin {
        animation: none;
    }

    .nv-progress i {
        width: 100%;
        transform: none;
    }

    .nv-spin {
        border-right-color: currentColor;
        opacity: .5;
    }
}

/*  ══ SET 1 REPORT TABLES - v31.103 (batch298, owner) ═══════════════════════
    Reports 190, 189, 184 and 183. Owner: *"Deliver report builds in the given
    set, not individually."*

    ⚠⚠⚠ THESE ARE THE ONLY NEW RULES THE SET NEEDS, because every one of the
    four renders inside `.rpt-out` / `.rpt-outscroll` / `.rpt-tbl` - the shell
    the trial balance already proved. What is here is the per-report COLUMN
    behaviour and nothing else.

    ⚠⚠ EVERY TABLE DECLARES A `min-width` AND THE BOX SCROLLS, never the page.
    A report is the one surface that legitimately has more columns than a
    laptop has room for; `.rpt-outscroll` is `overflow-x: auto` and names both
    axes, which is the v31.31 bank-card lesson. MEASURED at 1920: the city-tax
    table is 1327px inside a 962px card and the page does not move sideways. */

/*  ── 190 · the city tax listing, thirteen columns ─────────────────────── */
.ct-tbl {
    min-width: 1180px;
}

    .ct-tbl .c-ad {
        min-width: 150px;
        max-width: 190px;
    }

    .ct-tbl .c-dt,
    .ct-tbl .c-ct {
        white-space: nowrap;
    }

    /*  ⚠ THE COLUMN THE REPORT EXISTS FOR IS MARKED, and it sits LEFT of Room
        rate and Revenue. In v1's order City tax is the column a reader has to
        scroll to reach - on a report whose entire purpose is that figure. */
    .ct-tbl .ct-key {
        color: var(--nv-gold-bright);
    }

    /*  ⚠ A COUNTRY THAT DID NOT RESOLVE IS SHOWN AS AN EM DASH, not a blank.
        v1 printed nothing for the owner's UK guest and the page gave no
        indication anything was missing. */
    .ct-tbl .ct-none {
        color: var(--nv-gold);
    }

/*  ── 189 · the twelve-period pivot, fourteen columns ──────────────────── */
.pl-tbl {
    min-width: 1320px;
}

    .pl-tbl .c-nm {
        min-width: 260px;
    }

    /*  ⚠ THE YEAR TOTAL IS PINNED LEFT OF THE TWELVE PERIODS. It is the figure
        a reader looks for; the periods are the detail behind it, and putting
        the answer past twelve columns of workings gets it scrolled off. */
    .pl-tbl .pl-total {
        border-right: 1px solid rgba(104,152,232,.22);
        padding-right: 12px;
    }

    .pl-tbl .pl-code {
        color: var(--nv-gold);
        margin-right: 8px;
    }

    .pl-tbl .pl-name {
        color: var(--nv-ink-2);
    }

    /*  the property sub-heading inside a revenue or cost block */
    .pl-tbl .pl-prop td {
        color: var(--nv-ink-3);
        padding-top: 10px;
        font-size: 13px;
    }

/*  ── 184 / 183 · the invoice listings ─────────────────────────────────── */
.inv-tbl {
    min-width: 900px;
}

    .inv-tbl .c-ad {
        min-width: 250px;
    }

    /*  ⚠⚠ THE HEADER ROW IS A BLOCK, NOT A LINE. v1's own rendering stacks the
        account, the name, the address and the invoice reference in one cell -
        an invoice listing that put those on one row would need a 2000px table
        and would still be unreadable. */
    .inv-tbl .inv-head td {
        border-top: 1px solid rgba(104,152,232,.22);
        padding-top: 12px;
        vertical-align: top;
    }

    .inv-tbl .inv-acc {
        color: var(--nv-gold);
    }

    .inv-tbl .inv-nm {
        color: var(--nv-ink-1);
    }

    .inv-tbl .inv-addr,
    .inv-tbl .inv-meta {
        color: var(--nv-ink-3);
        font-size: 13px;
    }

    .inv-tbl .inv-lbl {
        color: var(--nv-ink-3);
        margin-right: 6px;
    }

    /*  the tax line reads differently from a goods line - same weight, muted */
    .inv-tbl .inv-tax .c-nm {
        color: var(--nv-ink-3);
    }

    .inv-tbl .inv-ta {
        color: var(--nv-ink-3);
        margin-left: 8px;
    }

    .inv-tbl .inv-doc {
        color: var(--nv-gold);
    }

/*  ── shared by the set: the property band and its subtotal ────────────── */
.rpt-band .ct-pname {
    color: var(--nv-gold);
}

.rpt-band td {
    padding-top: 14px;
}

.rpt-sub td {
    border-top: 1px solid rgba(104,152,232,.22);
    border-bottom: 1px solid rgba(104,152,232,.22);
}


/*  ══ v31.117 (batch313, owner) — THE HISTORY ROW MAKES ROOM FOR ITS TRN ═══
    Owner, on a screenshot of the processing pages' history card: *"please make
    the first row larger so that the TRN and XXXX number is fully visible"*,
    then, on seeing a two-line answer: ***"I would prefer if you only modify
    size, not move content. make icon columns smaller - width 22px. make amount
    field smaller 90px. gap 4px."***

    ⚠⚠⚠ SIZE ONLY. NOTHING MOVES. The reference stays on line one and the
    date · TRN stays on line two, exactly as they are — `.s` keeps v21.1's
    `white-space: nowrap` (that rule came from the owner's own report that the
    line was wrapping and every history row had become two rows tall on
    02/03/04-Screen). The room comes out of the columns beside the text.

    WHERE THE 370px CARD'S WIDTH GOES — measured, before and after:
        text     124  →  182      the line needs 145-184 depending on the TRN
        gaps   3x10   →  3x4
        clip      34  →   22
        the +     28  →   22
        amount   112  →   90

    ⚠ THE SPACER MOVES WITH THE CLIP. `.docclip-gap` stands in for the clip on
    a row with no documents (A-34), so if the two ever disagree the amounts
    below step sideways — which is the whole reason that spacer exists.

    ⚠⚠ SCOPED TO `#histCard` / `#histSlot`, the two ids the history card
    renders under on all three processing surfaces. The TRANSACTION ENQUIRY is
    deliberately untouched: its 112px amount floor and its 34/28 columns are
    measured decisions from batch304 and A-52, on a row that is already
    16px short of its own content at 390px (A-49). Nothing here goes near it. */
#histCard .fin-hrow,
#histSlot .fin-hrow {
    gap: 4px;
}

#histCard .fin-hrow .docclip-btn,
#histSlot .fin-hrow .docclip-btn,
#histCard .fin-hrow .docclip-gap,
#histSlot .fin-hrow .docclip-gap {
    flex: 0 0 22px;
    width: 22px;
}

#histCard .fin-hrow .docadd-btn,
#histSlot .fin-hrow .docadd-btn {
    flex: 0 0 22px;
    width: 22px;
}

/*  ⚠ THIS OVERRIDES v24.7's 112px, WHICH IS WHY IT SITS BELOW IT IN THE FILE.
    Same selector weight, so ORDER is what decides — the trap this project has
    paid for twice. Appending at the foot is the cheap guarantee.

    ⚠⚠⚠ 90px IS A FLOOR, NOT A FIXED WIDTH, AND THAT IS A SAFETY CHANGE I MADE
    ON PURPOSE — say the word and it goes back to fixed.
    `.amt` is `overflow: hidden` and `justify-content: flex-end`, so it clips
    from the LEFT. MEASURED at a fixed 90px: `-124,464.96 C` needs 97px of ink
    and rendered as **`124,464.96 C`** — the column ate the MINUS SIGN and
    nothing else. A truncated word is a cosmetic fault; a figure that loses its
    sign is the number saying something different from what it means, on a
    ledger card.
    So 90px is the MINIMUM. Every ordinary amount (measured: 54-67px) sits at
    exactly 90 and the column stays aligned exactly as v24.7 intended; only a
    six-figure amount grows, and it grows because the alternative is lying. */
#histCard .fin-hrow .amt,
#histSlot .fin-hrow .amt {
    flex: 0 0 auto;
    width: auto;
    min-width: 90px;
}

/*  ══ v31.117b (batch313, owner: *"also do the flip with Allocations"*) ═════
    The same treatment on the ALLOCATIONS face, and the same reason: the row's
    text column was being squeezed by a fixed amount column wider than any
    amount in it.

    ⚠⚠⚠ THE GAP COULD NOT BE DONE HERE IN CSS AT ALL. The allocation rows are
    built in script and set `gap:10px` as an INLINE STYLE
    (`wrap.style.cssText = '…gap:10px…'`), and an inline style beats a
    stylesheet rule without `!important`. So the 4px is changed at its two
    sources — `_ApInvoiceEntry.cshtml` and `_ApPayment.cshtml` — which is a
    SIZE change on the line that already sets the size, and moves nothing.
    (`!important` would have "worked" and left the next reader two numbers
    disagreeing in two places.)

    ⚠ `.fin-scol` KEEPS ITS 96px. On the history card the two narrow columns
    hold single glyphs; this one holds the ✓ pill AND the Clear chip, and 22px
    would take the button with it. Nothing here was asked to shrink it.

    ⚠ 90px IS A FLOOR HERE TOO, for the reason measured on the history card:
    a fixed 90px column clips from the LEFT and eats the minus sign. */
.fin-flip .fin-hrow .amt {
    flex: 0 0 auto;
    width: auto;
    min-width: 90px;
}

/*  ⚠⚠⚠ AND THE LAST COLUMN GIVES THE FIRST ONE ITS 60px — owner: *"For
    allocations make the first column even larger by another 60px"* then
    *"make last column smaller"*.

    96 − 60 = 36, and 36 is what an UNSETTLED row's trailing column actually
    needs: it holds a single tick, measured at 16px of ink.

    ⚠⚠ BUT A SETTLED ROW'S TRAILING COLUMN IS NOT A GLYPH — IT IS A BUTTON.
    MEASURED: the ✓ pill plus the **Clear** chip need **109px**, which is already
    more than the 96 they have. Fixing every row at 36 would not shrink that
    control, it would CUT IT OFF — and Clear is v1's release-the-allocation
    action, not decoration.

    SO THE COLUMN IS CLAMPED, NOT FIXED: it sizes to its own content between a
    36px floor and its existing 96px ceiling.
      · an unsettled row  16px of ink  → 36px   (60px handed to the text)
      · a settled row    109px of ink  → 96px   (exactly what it has today)
    ⚠ NOTHING GETS WORSE. The ceiling is deliberately today's number so a
    settled row cannot lose a single pixel to this change.

    ⚠⚠ THIS SOFTENS v24.7, WHICH IS ALSO YOURS: *"decide what should be the
    column width for each item and make this width fixed for all rows so that
    the items appear to be in same column."* The AMOUNT column still obeys it.
    This one now has two widths — one for rows carrying a button and one for
    rows carrying a tick — because the alternative is a button nobody can
    press. Say the word and it goes back to a single fixed width, at 96px for
    everyone (today) or 36px for everyone (and no Clear). */
/*  ⚠ `flex: 0 0 auto`, NOT `0 1 auto`, AND THE PROBE IS WHY. With shrink
    allowed the settled row's column was squeezed to **56px** by the text
    column beside it — 53px less than its content — which is the Clear button
    cut in half. It must be allowed to size to its content and never below it. */
.fin-flip .fin-hrow .fin-scol {
    flex: 0 0 auto;
    width: auto;
    min-width: 36px;
    max-width: 96px;
}


/*  ══ v31.120 (batch316, owner) — THE ENQUIRY'S FILTER ROW REACHES DATE TO ══
    Owner: *"reduce the gap in the row so that the five btns align with the
    Date To right border."*

    ⚠⚠⚠ IT IS A MEASURE, NOT A GAP, AND THE MEASUREMENT IS WHY. On the 760px
    card the four controls ended at ~392px and Date To's right border sits at
    ~481px — the row fell about 90px SHORT, and a fifth button only reaches
    ~450. Closing that by shrinking a gap is arithmetic that runs the wrong way,
    and the answer would be a different number in each of the five languages
    these chips are translated into.

    So the ROW is given the exact measure instead. `.fin-grid3` above it is
    `1fr 1fr 1fr` with a 12px gap, so two of its columns plus one gap is
    `(100% - 24px) / 3 * 2 + 12px` — Date To's right border expressed in the
    grid's own terms rather than in pixels that go stale the moment the card's
    width or padding changes. `space-between` distributes what is left over.
    **The edge lands by construction, in every language.**

    ⚠ FROM 768 ONLY. Below that `.fin-grid3` collapses to a single column, so
    "two columns plus a gap" is not a thing that exists and the row keeps the
    plain flex behaviour it has always had. */
@media (min-width: 768px) {
    .enq-chip-row {
        width: calc((100% - 24px) / 3 * 2 + 12px);
        max-width: 100%;
        justify-content: space-between;
        gap: 8px;
    }
}

/*  ══ v31.120 (batch316, owner) — THE AI CARD ══════════════════════════════
    Owner: *"introduce AI card at the bottom of the second column … The card
    bottom border should never exceed the middle column bottom card."*

    ⚠ THE CAP ITSELF IS NOT HERE — it is `_NvColCap`, which sets
    `--fin-flip-max` and adds `.is-capped`. These rules are only what the card
    looks like. */
.enq-ai-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/*  ⚠⚠ EACH ITEM IS A `span`, DIMMED AND UNCLICKABLE, AND THAT IS DELIBERATE.
    These three are a PROPOSAL for the AI round, not three controls that work.
    The house rule, paid for in v26.9 and written into `.t-off`: a disabled
    thing must not look like its live neighbour, and a tile goes live only once
    the page answers it. So there is no hover, no cursor and no chevron —
    nothing here invites a click that would do nothing. */
.enq-ai-item {
    display: block;
    opacity: .55;
    pointer-events: none;
    border-left: 2px solid var(--nv-blue-bright);
    padding-left: 10px;
}

.enq-ai-h {
    display: block;
    font-size: 14px;
    color: var(--nv-ink);
}

.enq-ai-s {
    display: block;
    font-size: 12.5px;
    font-weight: 100;
    color: var(--nv-ink-3);
    margin-top: 2px;
}

.enq-ai-soon {
    font-size: 12.5px;
    margin-top: 12px;
    padding-top: 8px;
    border-top: 1px solid rgba(104,152,232,.14);
}

/*  ══ v31.122 (batch318, owner) — THE CAP IS A CEILING. IT WAS ALWAYS A
    CEILING. v31.121 MADE IT A STRETCH, AND THAT WAS MY MISREADING. ══════════

    Owner, v31.121: *"brNotes violates the 'never taller than the middle
    column' cap … it should be the bottom border so change to 'the bottom
    border should be the same as the middle column bottom border'."*
    Owner, v31.122, on seeing v31.121 run: *"bank reconcilation notes card
    should work same as in transaction notes card."*

    ⚠⚠⚠ THE SECOND SENTENCE SETTLES THE FIRST. I read *"the same as"* as an
    instruction to STRETCH a short card down to the middle column, and shipped
    `height: var(--fin-flip-max)`. On the transaction enquiry that looked right
    only by accident — `#enqAi`'s content OVERFLOWS its room, so a height and a
    ceiling render identically there. On the bank rec `#brNotes` holds four
    lines of text against a very long middle column, so the same rule inflated
    it to **1273px of mostly empty card**. The owner's reference is the enquiry
    card, and the enquiry card is CAPPED, not stretched.

    ⚠⚠ SO THE FIRST SENTENCE WAS ASKING FOR THE NAME, NOT A STRETCH: call the
    rule by the border it acts on rather than by "height". The rule is **the
    card's bottom border never goes past the middle column's**; a card with
    less content than room keeps its content height and stops early, which is
    what both notes cards now do.

    ⚠ AND THE OVERLAP HE ACTUALLY REPORTED WAS NEVER THIS RULE. `#brHistory` at
    six rows a page ran 40px past its own fixed 450px card and painted the pager
    bar 26px inside the Notes card below. That is fixed where it lives — five
    rows a page, and `overflow-y:auto` on that card — and it stays fixed
    whatever this rule does.

    ⚠ WHEN THE CONTENT IS TALLER THAN THE ROOM THE CARD SCROLLS INSIDE ITSELF
    rather than overflowing the column — the same arrangement
    `.fin-flip.is-capped` already has, which is where `--fin-flip-max` comes
    from.
    ⚠⚠ THIS BLOCK DUPLICATES THE `.card.is-capped` RULE 5,000 LINES ABOVE and
    has done since v31.120. It is left rather than tidied away: being the LATER
    of the two it is the one that decides, and removing it would move the
    deciding rule far up the file during a round about something else. */
.card.is-capped {
    max-height: var(--fin-flip-max, none);
    overflow-y: auto;
    overflow-x: hidden;
}

/*  ══ v31.125 (batch321, owner) — AMEND ON THE FLY: THE FIELD'S FOUR STATES ══
    Owner: *"users should be able to update specified non-financial fields
    individually without placing the entire transaction into Amend mode."*

    ⚠⚠ THE HOUSE `.fld-btn` IS `position:absolute; right:2px` — ONE BUTTON PER
    FIELD, by design since v31.82 (sixteen call sites). The pencil takes that
    place; the ✓ ✕ PAIR CANNOT, because two absolutely positioned buttons land
    on top of each other and the second one eats every click meant for the
    first. The pair goes in its own flow row, and the mock-up's probe is what
    found that — it could not press ✓ at all. */
.nvfe-act {
    position: absolute;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
    display: inline-flex;
    gap: 2px;
}

/*  ⚠⚠⚠ WHERE THE PENCIL SITS WHEN THE MARK IS ON A `.fld-wrap`.
    Most fields on these pages are `<label class="fld-wrap">` = a hint ABOVE an
    input, and the amend mark goes on that label so the file adds no element.
    A control centred on the LABEL lands on the hint text, not on the field, so
    on a `.fld-wrap` both the pencil and the ✓ ✕ pair anchor to the BOTTOM
    instead — which is the input's own row.
    ⚠ `.fld-go` keeps the centred rule above: there the mark is on the field
    wrapper itself, and its middle IS the input's middle. */
.fld-wrap[data-nvfe] { position: relative; }

    /*  ⚠⚠⚠ 3px, NOT 5 — v31.126 (batch322, owner): *"edit icon should be
        centred correctly inside its cell. It appears to have a higher bottom
        spacing or padding."* Measured, it was: the button drew 1px below the
        field's top edge and 5px above its bottom. The field is 32px and a
        `.fld-btn` is 26px, so the only bottom offset that centres it is
        (32 − 26) / 2 = 3. 5px is the hairline's inset — 22px inside 32 — and
        borrowing it for a 26px control puts the control 2px high.
        ⚠ The hairline keeps 5px BECAUSE it is 22px tall. Two different
        insets, one shared centre line; both are (32 − own height) / 2. */
    .fld-wrap[data-nvfe] > .nvfe-pen,
    .fld-wrap[data-nvfe] > .nvfe-act {
        top: auto;
        bottom: 3px;
        transform: none;
    }

    /*  ⚠⚠⚠ v31.137 (batch332, owner) — AND `bottom: 3px` IS ONLY THE INPUT'S
        ROW WHILE THE WRAP ENDS WHERE THE INPUT ENDS.
        Owner: *"Please check the edit pencil style. I think the reason it is
        offset..."* MEASURED side by side, at rest:

            plain wrap, on its own                          drift   0px
            plain wrap STRETCHED beside a taller sibling    drift +14px

        `.fld-wrap` is a flex COLUMN with no `justify-content`, so its children
        pack to the top; `.fin-grid3` is a grid, whose items STRETCH by default.
        Put a description label next to an account search — which carries a
        `.sv-results` and is 14px taller — and the grid stretches the label,
        the spare height falls BELOW its input, and the pencil goes with it.
        That is the posted line panel on the money page and on the journal.

        ⚠⚠ `align-self: start` IS A SIZE CHANGE, NOT A MOVE. The wrap stops being
        stretched to its neighbour's height; the hint and the input keep their
        own size and their own position — MEASURED: input top and height are
        identical before and after. The field's box is drawn by `.input`, so a
        wrap that stops stretching is invisible.

        ⚠⚠⚠ v31.138 (batch333, owner) — AND IT IS NAMED PER GRID, NOT PUT ON
        EVERY MARKED WRAP. `align-self` acts on the CROSS axis, and which axis
        that is depends on the parent:
            in a GRID          the cross axis is vertical  → height only ✅
            in a FLEX COLUMN   the cross axis is HORIZONTAL → WIDTH ❌
        The posted money card is `display: flex; flex-direction: column`, so
        v31.137's unscoped rule made the bank account field shrink to its
        content instead of filling the card — the owner's screenshot shows it
        ending halfway across. The stretching this rule exists to undo only
        ever happens in a grid, so only grids are named.
        ⚠ THE TWO GRIDS ARE THE ONLY ONES THAT CARRY MARKED FIELDS: 19 uses of
        `.fin-grid3` and one of `.fin-jgrid` across the three financial pages.
        A marked wrap anywhere else keeps the width its parent gives it. */
    .fin-grid3 > .fld-wrap[data-nvfe],
    .fin-jgrid > .fld-wrap[data-nvfe] {
        align-self: start;
    }

/*  ══ v31.138 (batch333, owner) — A ROW THAT REFLOWS WHEN ITS OWN COLUMN IS
    NARROW, AND ONLY WHERE AN AMENDABLE NATIVE DATE LIVES ══════════════════════

    The owner's posted payment at a 1270px window. MEASURED in that column:

        .fin-grid3 cell                     142.7px
        native date  needs 97.5px, has      44.7px   → 52.8px SHORT
        native month needs 110.2px, has     44.7px   → 65.5px SHORT

    A `type=date` shows `10/08/2026` and a `type=month` shows `August 2026`;
    neither can be abbreviated, and the field also owns two 34px action zones —
    the browser's indicator and the amend pencil. **No padding number closes a
    52px gap.** Even stripping the reservation to the bare 68px the two zones
    occupy leaves the month 37px short. Three tracks simply cannot hold this
    field in a 462px card, so the ROW gives instead of the field.

    ⚠⚠⚠ THE TRIGGER IS THE CONTAINER'S WIDTH, NOT THE VIEWPORT'S. This column's
    width depends on which side panels a page renders, so a viewport breakpoint
    would be a proxy that is wrong on some of them. `auto-fill` asks the real
    question — how many 206px tracks fit here — and answers it per card.
      · 728px card (the 760 max) → 3 tracks of 234.7px, **exactly as today**
      · 462px card (the owner's) → 2 tracks of 225px, and 225 > 204.2 ✅
    206px = 12 left padding + 110.2 for `August 2026` + 80 reserved for the two
    zones + 2 border, rounded up. Measured, not chosen.

    ⚠⚠⚠ SCOPED BY `:has()` TO A GRID THAT ACTUALLY HOLDS ONE, and that scoping is
    load-bearing: **`.enq-chip-row` sets its width to `(100% - 24px) / 3 * 2 +
    12px`, which is Date To's right border expressed in `.fin-grid3`'s own three
    tracks.** Reflow that grid to two and the chip row is measured against a
    layout that is no longer there. The enquiry row's dates are FILTERS and carry
    no `data-nvfe`, so this selector cannot reach them — and an entry form marks
    nothing until `feTrn > 0`, which it never is.
    ⚠ BELOW 768 the single-column rule above still wins on order.

    ⚠⚠ THE THIRD SELECTOR IS THE ANALYSIS SLOTS, and it was missed on the first
    cut. A `.ba-slot` is not a native date, but it carries the same TWO action
    zones — its picker and the pencil — so it reserves the same 80px and
    truncated `CC-100` to `CC-1(` in the very same card. The predicate is
    therefore *a marked field with a control of its own besides the pencil*,
    which is what "two zones" means in markup.
    ⚠ `:not(.nvfe-pen)` IS LOAD-BEARING. The pencil IS a `.fld-btn`, so without
    it this would match every marked field the door has touched and reflow
    one-zone rows that have room to spare.
    ⚠⚠ READ FROM THE MARKUP, NOT FROM `.nvfe-two`. The door adds that class after
    load; hanging the layout on it would paint three tracks, truncate, and then
    jump to two on every posted document. */
.fin-grid3:has(> .fld-wrap[data-nvfe] > input[type="date"]),
.fin-grid3:has(> .fld-wrap[data-nvfe] > input[type="month"]),
.fin-grid3:has(> .fld-wrap[data-nvfe] .fld-btn:not(.nvfe-pen)) {
    grid-template-columns: repeat(auto-fill, minmax(206px, 1fr));
}

    @media (max-width: 767.98px) {
        .fin-grid3:has(> .fld-wrap[data-nvfe] > input[type="date"]),
        .fin-grid3:has(> .fld-wrap[data-nvfe] > input[type="month"]),
        .fin-grid3:has(> .fld-wrap[data-nvfe] .fld-btn:not(.nvfe-pen)) {
            grid-template-columns: 1fr;
        }
    }

    .nvfe-act .fld-btn {
        position: static;
        transform: none;
        width: 30px;
    }

/*  ══ v31.126 (batch322, owner) — THE PENCIL IS GOLD, AND IT SAYS SO AT REST ══
    Owner: *"The edit pencil is currently too muted in its normal state and only
    becomes sufficiently visible on hover. Keep the edit icon gold by default so
    users can immediately recognise that the field is editable."*

    ⚠ v31.125 had it at `opacity: .45`, reasoning that a posted transaction is
    read more often than amended. That reasoning was about NOISE and the owner's
    is about DISCOVERABILITY — a control nobody can see is a feature nobody
    finds, and this one is the whole point of the round. Gold is the house
    colour for *"this does something"*, so it wears it. */
.nvfe-pen {
    color: var(--nv-gold);
    opacity: 1;
}

/*  ⚠⚠ AND THE HOVER CHANGES THE PENCIL'S DIRECTION, not its brightness.
    Owner: *"On hover, change the pencil orientation so that it points from
    top-right to bottom-left, providing clear visual feedback that the control
    is active."* At rest the nib is bottom-RIGHT; mirrored, it is bottom-LEFT,
    so the body runs top-right to bottom-left exactly as asked.
    ⚠ THE ICON IS AN SVG THIS PROJECT DRAWS (`_NvFieldEdit`), which is what
    makes the direction dependable — a `✎` glyph points whichever way the
    reader's font decides, and mirroring that is a guess. */
.nvfe-pen svg { transition: transform .12s ease; }

[data-nvfe]:hover > .nvfe-pen svg,
[data-nvfe]:focus-within > .nvfe-pen svg,
.nvfe-pen:hover svg,
.nvfe-pen:focus-visible svg { transform: scaleX(-1); }

/*  ══ TWO ACTIONS, TWO ZONES, TWO HAIRLINES ═════════════════════════════════
    Owner: *"Introduce a second vertical separator and move the calendar/date
    selector to the right, giving each action its own clearly defined area"* —
    and the same for the Business Analysis selectors.

    ⚠⚠⚠ THE GEOMETRY IS THE HOUSE'S, NOT A NEW ONE. `select.input` and `.fld-go`
    have used the same numbers since v31.53: a 34px action zone, a 1px × 22px
    divider at `right 34px` in `rgba(112,160,238,.30)`, and the value stopping
    at 46px. A second action simply repeats that outward — the pencil's zone is
    34→68, its divider sits at 68, and the value stops at 80. Put one of these
    beside a plain field and every hairline in the row still lines up.
    ⚠ `.nvfe-two` IS ADDED BY THE DOOR, which detects the existing control
    rather than asking the page to declare it. */
.fld-wrap[data-nvfe].nvfe-two > .nvfe-pen { right: 36px; }

.fld-wrap[data-nvfe].nvfe-two > .nvfe-act { right: 38px; }

    /*  ⚠⚠⚠ v31.137 (batch332, owner) — THE SAME TWO ZONES ON A SEARCH FIELD,
        WHERE THE PENCIL HANGS ON `.fld-go` INSTEAD OF ON THE WRAP.
        Owner: *"Please check the edit pencil style. I think the reason it is
        offset..."* MEASURED: the wrap of a search field ends 14px below its
        input, because `.sv-results` sits inside it and carries a 10px bottom
        margin — so `bottom: 3px` of the wrap is 14px below the field. The door
        now hangs the pencil on `.fld-go`, whose box IS the input's box, and the
        centred `.nvfe-act` rule at the top of this block then puts it on the
        field's own middle with no offset to correct.
        ⚠ THE HORIZONTAL NUMBERS ARE UNCHANGED — 36 and 38, the house's — they
        just measure from `.fld-go`'s right edge now. That edge and the wrap's
        are the same line (`.fld-go` is `display: block`), so a search field's
        hairlines still land at 34 and 68 with every other field in the row.
        ⚠⚠ THE `> ` RULES ABOVE STILL OWN THE PLAIN CASE and are untouched;
        a wrap with no `.fld-go` never matches these. */
    .fld-wrap[data-nvfe].nvfe-two .fld-go > .nvfe-pen { right: 36px; }

    .fld-wrap[data-nvfe].nvfe-two .fld-go > .nvfe-act { right: 38px; }

    /*  ⚠⚠⚠ THE INNER DIVIDER, AND ONLY WHERE NOTHING ELSE DRAWS IT.
        v31.126 (batch322, owner): *"hairline separator needed in date/period
        control between edit and date selector."*

        Which line each field needs depends on what its second control is:
        · a `.fld-btn` picker (Business Analysis) carries `.fld-btn::before`,
          a 1px × 22px line at its own left edge — so the picker draws 34 and
          the pencil beside it draws 68. Both zones are bounded already.
        · a NATIVE `<input type="date">` indicator draws nothing. The pencil
          still draws 68, so the OUTER edge is bounded and the inner one — the
          boundary between pencil and calendar, at 34 — is missing. That is the
          gap the owner pointed at.
        So this rule supplies 34, on the native case only. The first cut put it
        at 68, where the pencil's own line already was: two coincident 1px lines
        and the real gap still open. Measured before changing it.
        ⚠ A background layer on the wrapper, so it needs no element and cannot
        be left behind when the pencil is removed. */
    .fld-wrap[data-nvfe].nvfe-two-native::after {
        content: "";
        position: absolute;
        right: 34px;
        bottom: 5px;
        width: 1px;
        height: 22px;
        background: rgba(112, 160, 238, .30);
        pointer-events: none;
    }

    /*  ⚠ AND THE VALUE STOPS BEFORE BOTH. Without this the text runs under the
        pencil, which is the fault being fixed rather than a smaller version
        of it. */
    .fld-wrap[data-nvfe].nvfe-two .input { padding-right: 80px; }

    /*  ⚠⚠⚠ v31.137 (batch332) — AND A ONE-ZONE FIELD NEEDS IT TOO. The rule
        above reserves room only where a SECOND control exists; a field carrying
        just the pencil reserved nothing, so a long value ran straight under it.
        Every short value on these pages hid the fault — it took a posted
        journal line ("Office supplies — August") to show it.
        ⚠ 46px IS THE HOUSE NUMBER, not a new one: `.fld-go:has(> .fld-btn)` has
        used it since v31.52 for exactly this control in exactly this position —
        a 32px button zone, 12px of air, the 2px inset.
        ⚠⚠ THE THREE RULES ABOVE AND BELOW ALL OUTRANK IT (`.nvfe-two`,
        `.nvfe-live` and the pair of them each add a class), so the two-zone and
        live reservations are unchanged. */
    .fld-wrap[data-nvfe] .input { padding-right: 46px; }

/*  ══ v31.132 (batch328, owner) — WHAT A LIVE FIELD NEEDS THAT A RESTING ONE
    DOES NOT ═══════════════════════════════════════════════════════════════
    The pencil is ONE control; the ✓ ✕ pair it becomes is TWO, and on a field
    that already has a picker the pair sits OUTSIDE it. MEASURED on the account
    field: `.nvfe-act` spans 38 → 100 from the wrap's right edge while the
    resting rule stops the value at 80, so the account name ran underneath the
    tick. The screenshot found it; the resting numbers were all correct.
    ⚠ 104 = the pair's own 100 plus the 4px the house leaves beside a control.
    ⚠⚠ AND THE ONE-ZONE CASE NEEDS IT TOO — there the pair sits at 4 → 66. */
.fld-wrap[data-nvfe].nvfe-live .input { padding-right: 70px; }

.fld-wrap[data-nvfe].nvfe-two.nvfe-live .input { padding-right: 104px; }

/*  ⚠⚠⚠ AND THE BROWSER'S OWN CLEAR BUTTON COMES OFF A SEARCH BOX WE HAVE
    CLAIMED. An `input[type="search"]` draws a native ✕ hard against its right
    edge — exactly where this door puts ✓ and ✕ — so a live account field showed
    three marks, one of them the browser's, sitting on top of ours. It is only
    drawn while the box has a value, which is why it never appeared in any
    resting measurement.
    ⚠ Scoped to `[data-nvfe]`: every other search box in the application keeps
    its clear button. */
[data-nvfe] input[type="search"]::-webkit-search-cancel-button,
[data-nvfe] input[type="search"]::-webkit-search-decoration {
    -webkit-appearance: none;
    appearance: none;
    display: none;
}

    /*  ⚠⚠ THE NATIVE DATE PICKER IS PUSHED OUT TO ITS OWN ZONE. A
        `<input type="date">` draws its indicator hard against the right edge,
        which is exactly where v31.125's pencil sat — the overlap the owner
        reported. It now keeps the outer 34px and the pencil has the next one. */
    .fld-wrap[data-nvfe].nvfe-two .input::-webkit-calendar-picker-indicator {
        margin-right: 0;
        opacity: .85;
    }

/*  the field being changed, and the field that just changed */
.nvfe-live .input { border-color: var(--nv-gold); }
.nvfe-done .input { border-color: var(--nv-good); }
.nvfe-busy { opacity: .6; pointer-events: none; }

/*  ⚠⚠ THE ANSWER IS A LINE, NOT A DIALOG. The reader has just dismissed one
    confirm; a second box to acknowledge the result would make a one-field
    change a three-click job. It clears itself. */
.nvfe-toast {
    position: fixed;
    left: 50%;
    bottom: 26px;
    transform: translateX(-50%);
    z-index: 90;
    max-width: min(560px, calc(100vw - 32px));
    padding: 10px 16px;
    border-radius: 10px;
    font-size: 13.5px;
    line-height: 1.45;
    background: var(--nv-card);
    border: 1px solid rgba(104, 152, 232, .3);
}

    .nvfe-toast.is-ok { border-color: rgba(88, 179, 104, .55); }
    .nvfe-toast.is-no { border-color: rgba(232, 167, 92, .6); }

/*  ══ v31.130 (batch326, owner) — THE MESSAGE SITS BESIDE THE LINE HEADING ══
    Owner: *"The messages label e.g 'VAT Code Changed' should be at the top next
    to the line label in capital 'VIEW LINE X' on its right."*

    ⚠ THE SAME TWO STATE COLOURS AS THE FLOATING NOTE, so a reader who sees one
    form and then the other is not learning a second vocabulary. What changes is
    the PLACE, not the language: no box, no border, no fixed position — it is a
    caption on a heading, and a heading is not a card.
    ⚠⚠ `margin-left` ONLY. The heading row is a flex line that already spaces its
    own parts, and giving this a `margin-left:auto` would push it to the far
    right of the card instead of *"on its right"* — beside the label, which is
    what was asked for. */
.nvfe-inline {
    margin-left: 10px;
    font-size: 12.5px;
    font-weight: 500;
    letter-spacing: .01em;
    color: var(--nv-muted);
    text-transform: none;
}

    .nvfe-inline.is-ok { color: var(--nv-good); }
    .nvfe-inline.is-no { color: var(--nv-gold); }

/*  ══ v31.348 (owner) — THE ANSWER UNDER THE FIELD ══════════════════════════
    Owner: *"no messages appear after selecting update of transaction type."*

    It appeared — at `bottom: 26px` of the window, for 4.2 seconds, while the
    field being amended was near the top of a long posted document. A field that
    has no `[data-nvfe-msg]` anchor in its own form now gets its answer in its
    own wrapper instead.

    ⚠ IT IS A LINE, NOT A CARD. Same two state colours as the other two forms of
    this message, no border and no box: a card under one field would push every
    field below it down by its own height each time anybody changed anything.
    ⚠⚠ `white-space: normal` IS LOAD-BEARING. The refusal sentences are long —
    the transaction-family one runs to two lines — and a field wrapper is a
    narrow column. */
.nvfe-note {
    margin-top: 4px;
    font-size: 12.5px;
    line-height: 1.4;
    font-weight: 500;
    white-space: normal;
    color: var(--nv-muted);
}

    .nvfe-note.is-ok { color: var(--nv-good); }
    .nvfe-note.is-no { color: var(--nv-gold); }

/*  ══ v31.126 (batch322, owner) — THE VIEW CHIPS ARE ONE WIDTH ══════════════
    Owner: *"The current View chip buttons in - Lines tab are ambiguous because
    the same label is used for different levels of the transaction… Rename the
    top/header chip action to View Header. Rename each line-level action to View
    Lines. Use the same button width for both."*

    ⚠⚠ 160px IS MEASURED, NOT CHOSEN. A `min-width` only makes two chips equal
    if it clears BOTH labels, and the page renders one language at a time — so
    the number has to clear the widest label in every language, or the pair
    stops matching in whichever one it misses. Rendered in the shipped face:

        en  View Header 128 · View lines  106
        sk  Zobraziť hlavičku 160 · Zobraziť riadky 143
        cs  Zobrazit hlavičku 160 · Zobrazit řádky  139
        de  Kopf anzeigen 141 · Zeilen ansehen 148
        pl  Pokaż nagłówek 154 · Pokaż wiersze 135

    160 is the widest of the ten. Below it the two chips differ in at least one
    language; at it they are identical in all five. The first attempt used 124
    and measured 128 against 124 in English alone — close enough to look right
    in a screenshot and still not what was asked for.
    ⚠ A FLOOR RATHER THAN A FIXED `width`, so a longer string in a language
    added later grows the chip instead of being clipped by it. */
.chip.fin-viewchip {
    min-width: 160px;
    justify-content: center;
    text-align: center;
}

/*  ══════════════════════════════════════════════════════════════════════════
    v31.309 (owner) — THE REVIEW-STEP ACTION ROW, MEASURED.

    Owner, after v31.308: *"now the right button is not aligned"*.

    ⚠⚠⚠ THE RIGHT BUTTON WAS ALIGNED — that was measured off his own screenshot:
    Copy and View Line share the same LEFT and RIGHT edge to the pixel. What he
    is reacting to is the other end. **Four chips at 160px plus three 8px gaps
    is 664px in a row about 666px wide**, so the row is exactly FULL: there is
    no free space for `justify-content: flex-end` to distribute, the cluster
    spans the whole width, and Print reaches the far left margin.

    ⚠⚠ SO THE FIX IS WIDTH, NOT ALIGNMENT. At 120px the four take 504px and
    leave ~160px of free space, which is what lets them sit as a right-aligned
    CLUSTER instead of a justified row. Copy's right edge does not move.
    ⚠ SCOPED TO THIS ROW. `.fin-viewchip` is a shared class and the 160px floor
    is right everywhere it is one or two chips; this is the only place four of
    them compete for one line.
    ⚠⚠ AND IT WRAPS RATHER THAN OVERFLOWS. With `flex-end`, content wider than
    its container spills off the LEFT — so on a narrow window the first chip
    would disappear under the card edge. `flex-wrap` makes it drop to a second
    right-aligned line instead, which is recoverable; a clipped control is not.
    ══════════════════════════════════════════════════════════════════════════ */
.fin-actionrow {
    display: flex;
    gap: 8px;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
}

    .fin-actionrow > .chip.fin-viewchip {
        min-width: 120px;
        flex: 0 0 auto;
    }

/*  ⚠⚠ v31.310 — THE LAYOUT PICKER WEARS THE CHIP, so it sits on the same
    baseline and the same height as Print, Email, Edit and Copy beside it.
    `.chip` is built for inline-flex content it centres itself; a <select> is a
    replaced element and centres its own text, so the flex properties it would
    inherit have to be undone rather than added to. */
.fin-actionrow > select.fin-vsel {
    display: inline-block;
    appearance: auto;
    -webkit-appearance: auto;
    /*  ⚠ ROOM FOR THE ARROW. Without the extra right padding the platform's own
        disclosure triangle sits on top of the longest option's last letter,
        and "Hairline" is exactly long enough to reach it. */
    padding: 0 26px 0 12px;
    text-align: left;
    line-height: 1.1;
    cursor: pointer;
}

    /*  ⚠⚠⚠ v31.142 (batch337, owner) — AND THE JOURNAL'S TOGGLE IS THE EXCEPTION,
        BY THE OWNER'S OWN MEASURE. Owner: *"Set the View Line / Hide Line button
        width to align with the width of the Credit input control immediately
        above it."* The Credit track is 96.5px in his column; the 160px floor
        above made the chip overflow its own grid cell — MEASURED: right edges
        aligned, left edges 63.5px apart.

        ⚠⚠ THE FLOOR IS RELEASED, NOT LOWERED. `width: 100%` makes the chip the
        track, whatever the track is at that band and in that language, which is
        what "align with the Credit control" means. A smaller fixed number would
        be the same staleness the rule above warns about, one size down.
        ⚠ SCOPED TO A CHIP INSIDE `.fin-jgrid` — the journal line grid, and the
        only place a View chip is laid out as a column. The money page's chip and
        both invoice chips are in flex rows and keep the 160px floor the owner
        asked for in v31.126, so those three stay one width as before.
        ⚠⚠⚠ AND AT THAT WIDTH THE LABEL TAKES TWO LINES. MEASURED, in a 96.5px
        cell, the longest of the ten strings — German *"Zeile ausblenden"* —
        needs 120.5px **even at an 11px font**; English "Hide Line" needs 113px
        at the chip's own 16px. One line does not fit the Credit column in ANY
        language at a legible size, so matching that column means wrapping, and
        this rule makes the wrap deliberate rather than an accident:
          · `height: auto` + `min-height` — `.chip` is a fixed 32px, and a grid
            item stretched to a two-line row turned into a 72px slab
          · `line-height: 1.15` and tighter padding, so two lines read as one
            control rather than as a gap with words in it
        ⚠ IF ONE LINE MATTERS MORE THAN THE COLUMN, the chip spans Debit AND
        Credit (205px at this band, which fits every language at 16px) — a
        one-line change to `grid-column: 2 / -1`. The owner asked for the Credit
        width, so the Credit width is what this does. */
    /*  ══ v31.143 (batch338, owner) — AND THE MONEY PAGE'S TOGGLE IS WIDER,
        BECAUSE ITS OPEN LABEL NAMES TWO THINGS ═══════════════════════════════
        Owner: *"Increase the toggle button width sufficiently to accommodate
        Hide Line / View Header cleanly without wrapping."*

        MEASURED, in this chip's own font and padding, across the five locales:

            de  Zeile ausblenden / Kopf anzeigen   290.6px   ← the widest
            sk  Skryť riadok / Zobraziť hlavičku   265.8px
            cs  Skrýt řádek / Zobrazit hlavičku    261.8px
            pl  Ukryj wiersz / Pokaż nagłówek      258.1px
            en  Hide Line / View Header            213.7px

        292 clears the widest of the ten strings. `nowrap` is the guarantee, not
        the number: a language added later that overruns will run on rather than
        silently fold into two lines the way the journal chip does at its own
        much narrower width.
        ⚠⚠ THE FLOOR HOLDS IN BOTH STATES, so the control does not resize under
        the cursor between "View Line" and the long label — a button that jumps
        as you click it is a button you miss.
        ⚠⚠⚠ SCOPED BY `[data-payline]` — THE MONEY PAGE'S CHIP AND NOTHING ELSE.
        The owner: *"it should not alter the agreed Invoice or Journal toggle
        behaviour."* The journal's chip is sized by the rule below and both
        invoice chips keep the 160px house floor. */
    .chip.fin-viewchip[data-payline] {
        min-width: 292px;
        white-space: nowrap;
    }

    .fin-jgrid > .chip.fin-viewchip {
        min-width: 0;
        width: 100%;
        height: auto;
        min-height: 32px;
        padding: 4px 8px;
        line-height: 1.15;
        white-space: normal;
    }

/* ════════════════════════════════════════════════════════════════════
   v31.144 (batch339, owner) — _NvTranslate: READ A DESCRIPTION IN
   ANOTHER LANGUAGE. The glyph, the language list, and the preview.

   Two placements, one set of rules: the label row of a plain field, and
   the rich editor's toolbar. Everything is namespaced `nvtr-` — the
   short-class-name trap has cost this project a round before, and
   `.tr-*` would have collided with table rows in every sense.
   ════════════════════════════════════════════════════════════════════ */

/*  ⚠⚠ THE HINT ROW BECOMES A FLEX ROW, AND THE LABEL KEEPS THE ELLIPSIS.
    `.fl-hint` is one nowrap line, 18px tall, clipped with an ellipsis —
    that fixed height is what keeps grid columns aligned, so it is not
    touched. Only the layout changes, and the clipping moves onto the
    label's own span so the control beside it is never what gets cut. */
.fl-hint.nvtr-hint {
    display: flex;
    align-items: center;
    gap: 6px;
}

    .fl-hint.nvtr-hint > .nvtr-t {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/*  ⚠ 15px INSIDE AN 18px ROW — measured, not chosen: the row is
    `line-height: 18px` and a 15px mark centres in it with 1.5px either
    side. The word "Translate" was measured too and does not fit: it takes
    73px of a 146px label row at 03-Screen and clips the label to
    "Descripti…". The word lives in the tooltip, as the pencil's does. */
.nvtr-go {
    margin-left: auto;
    flex: 0 0 auto;
    border: 0;
    background: none;
    padding: 0;
    color: var(--nv-gold);
    cursor: pointer;
    height: 18px;
    line-height: 0;
    display: inline-flex;
    align-items: center;
}

    /*  ⚠ v31.146 (owner): *"Increase glyph size to 18 × 18px. Apply
        margin-right: 10px to the SVG."* The 18px mark in an 18px label row is
        flush by design; the margin is what lifts it off the field's right edge. */
    .nvtr-go svg { margin-right: 10px; }

    .nvtr-go:hover { opacity: .78; }

    .nvtr-go:focus-visible {
        outline: 2px solid var(--nv-blue-bright);
        outline-offset: 2px;
        border-radius: 3px;
    }

    .nvtr-go[disabled] { opacity: .4; cursor: default; }

/* the toolbar host — `.rt-toolbar button` already sizes it */
.rt-toolbar .nvtr-btn svg { display: block; }

/*  ── the language list ────────────────────────────────────────────────
    The house popover grammar, borrowed from `.rt-emoji`: one column,
    the surface colour, the same border and the same lift. */
.nvtr-menu {
    position: absolute;
    z-index: 40;
    display: flex;
    flex-direction: column;
    gap: 1px;
    width: max-content;
    min-width: 186px;
    padding: 6px;
    border: 1px solid rgba(112, 160, 238, .30);
    border-radius: var(--r-card);
    background: var(--nv-surface);
    box-shadow: 0 14px 30px rgba(0, 0, 0, .5);
}

    .nvtr-menu button {
        border: 0;
        background: none;
        color: var(--nv-ink-2);
        text-align: left;
        font-family: inherit;
        font-size: 13px;
        padding: 7px 10px;
        border-radius: 6px;
        cursor: pointer;
        display: flex;
        align-items: center;
        gap: 10px;
    }

        .nvtr-menu button:hover { background: rgba(232, 167, 92, .10); }

        /*  ⚠⚠ v31.146 — THE DETECTED LANGUAGE IS MARKED, NOT DISABLED. It was
            greyed out and unclickable until the owner pointed out the obvious:
            detection is a heuristic and can be wrong, so it must inform the
            choice without removing it. The row now reads as ordinary; only its
            "as entered" tag says what was detected. */
        .nvtr-menu button.is-src .cc,
        .nvtr-menu button.is-src .tag { color: var(--nv-gold); }

        .nvtr-menu button[disabled] { opacity: .42; cursor: default; }

        .nvtr-menu button[disabled]:hover { background: none; }

        .nvtr-menu button .cc {
            font-size: 11px;
            letter-spacing: .06em;
            color: var(--nv-ink-3);
            min-width: 20px;
        }

        .nvtr-menu button .tag {
            margin-left: auto;
            font-size: 10.5px;
            letter-spacing: .06em;
            text-transform: uppercase;
            color: var(--nv-ink-3);
        }

.nvtr-mh {
    font-size: 11px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--nv-ink-3);
    padding: 4px 10px 6px;
}

.nvtr-mr {
    height: 1px;
    background: rgba(104, 152, 232, .18);
    margin: 4px 6px;
}

/*  ── the preview ──────────────────────────────────────────────────────
    Under the field, never over it: the reader is comparing two versions
    of the same sentence and needs both on screen. Gold, because gold is
    this application's colour for "this is offered, not done". */
.nvtr-prev {
    border: 1px solid rgba(232, 167, 92, .45);
    border-radius: var(--r-card);
    background: rgba(232, 167, 92, .045);
    padding: 10px 12px;
    margin-top: 8px;
}

/*  ⚠⚠⚠ A GRID CELL IS NOT A PLACE TO PUT A PANEL. On the posted cards the
    field is one cell of a `.fin-grid3`; a sibling inserted after it becomes
    another CELL and lands beside the field at a third of the width. This
    gives the panel the whole row under the field it belongs to, and outside
    a grid the property is simply inert. */
.nvtr-inrow { grid-column: 1 / -1; }

.nvtr-ph {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: var(--nv-ink-3);
    margin-bottom: 7px;
}

.nvtr-pl { color: var(--nv-gold); }

.nvtr-x {
    margin-left: auto;
    cursor: pointer;
    color: var(--nv-ink-3);
    border: 0;
    background: none;
    font-size: 14px;
    line-height: 1;
    padding: 0 2px;
}

.nvtr-body {
    font-size: 13.5px;
    line-height: 1.5;
    border-left: 2px solid rgba(232, 167, 92, .35);
    padding-left: 10px;
    overflow-wrap: anywhere;
}

.nvtr-wait { color: var(--nv-ink-3); }

.nvtr-pa {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    flex-wrap: wrap;
}

    /*  ⚠ THE HOUSE `.btn` FILLS ITS CONTAINER. In a preview as wide as an
        invoice card that turns two ordinary actions into two banners, so
        here they size to their own words. */
    .nvtr-pa .btn {
        flex: 0 0 auto;
        width: auto;
        height: 30px;
        padding: 0 14px;
        font-size: 13px;
    }

        /*  ⚠ A CONTROL THAT CANNOT BE PRESSED MUST LOOK LIKE IT. The house
            primary button carries a gradient and a glow, and `disabled` alone
            changes neither — the probe screenshot showed a refused "Use this
            text" looking exactly as inviting as an accepted one. */
        .nvtr-pa .btn[disabled] {
            opacity: .45;
            cursor: default;
            box-shadow: none;
            filter: grayscale(.35);
        }

.nvtr-pc {
    margin-left: auto;
    font-size: 11.5px;
    color: var(--nv-ink-3);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

    .nvtr-pc.over { color: var(--nv-gold); }

/* ══════════════════════════════════════════════════════════════════════════
   v31.150 (batch343, owner — MOCK-UP LOCKED) — NOTES.  Namespace `nt-`.

   Lifted from the locked mock-up unchanged, so the shipped page is the thing
   that was reviewed rather than a second interpretation of it.

   ⚠ THE PAGE DOES NOT MAKE `.scroll` A SCROLLER. `app.css:86` sets
     `.app .scroll { overflow: visible; flex: initial }` — in V2 the DOCUMENT
     scrolls, and a Notes page with its own inner scroller would behave unlike
     every other screen. The sticky browse column below is sticky against the
     document, which is the scroller that actually exists.
   ══════════════════════════════════════════════════════════════════════════ */
.nt-logo {
    width: 40px; height: 40px; border-radius: 12px;
    display: flex; align-items: center; justify-content: center;
    color: var(--nv-gold); font-size: 22px; font-weight: 200;
    border: 1px solid rgba(232,167,92,.4);
}

.nt-head .htext { flex: 1; min-width: 0; }

/* ── THE WORKSPACE ────────────────────────────────────────────────────────
   ONE workspace, not V1's independent bordered panels: a browse column and
   the note itself, sharing one scroll and one visual system.               */
.nt-work { display: flex; flex-direction: column; gap: 14px; min-width: 0; padding-bottom: 40px; }
.nt-browse, .nt-note { display: flex; flex-direction: column; gap: 12px; min-width: 0; }

/*  ⚠⚠⚠ v31.158 (owner) — "There is no spacing between tabs", written across
    the join between the note header card and the cards under it.

    MEASURED: header card bottom 320, next card top 320. Zero. Every other
    stacked card in the app has a gap (.nt-browse 12, .fin-tiles 14,
    .fin-hub 18).

    ⚠ SAME ROOT CAUSE AS THE UNDERLINES ABOVE — the shipped DOM is not the
    mock-up's DOM. `.nt-note` declares the column gap, and in the mock-up the
    cards are its direct children so they get it. What ships must POST, so a
    `<form>` sits between them; `.nt-note`'s gap then applies to the form (one
    child, nothing to space) and the cards inside it are block siblings with no
    margin at all. A gap on a flex parent only spaces ITS OWN children.

    ⚠⚠⚠ v31.159 CHANGES THE MECHANISM AND NOT THE OUTCOME. The form no longer
    carries a copy of the column — it stops generating a box at all, so ITS
    children become `.nt-note`'s own flex items and take `.nt-note`'s 12px
    directly. One declaration of the gap instead of two that must agree.

    THE REASON IS THE OWNER'S: *"In documents tab, documents should be above
    'Who can see this'."* Those two cards are at DIFFERENT DEPTHS — the access
    card is inside `#ntForm` because it holds the `name="people"` CHECKBOXES,
    the access list v1 never saved and this module exists to save, while the
    documents card carries its own upload form and HTML forbids nesting one
    form in another. `order` only sorts siblings, so with the form generating a
    box they could never be reordered. `display: contents` makes them siblings.

    ⚠ THE FORM STILL POSTS. `display: contents` removes the box, not the
      element: submission, validation and the antiforgery token are untouched.
      The gate drives a real save round trip rather than taking that on trust. */
.nt-note > form { display: contents; }

/*  the visible order, stated once. Everything not named here keeps source
    order (0), so: head → note+tags → secure → documents → who can see this. */
.nt-note .nt-access { order: 1; }
/*  ⚠ v31.160 (owner): *"Edit and Delete should be move above share via
    Inmail."* The action row was pinned last at v31.159 because giving one
    sibling an order is a decision about all of them; this is the owner
    choosing where in that sequence it belongs. */
.nt-note .nt-actions { order: 2; }
.nt-note .nt-share { order: 3; }
.nt-note .nt-notowner { order: 4; }

/* Tablet — the two columns appear as soon as there is room for them. */
@media (min-width: 768px) {
    .nt-work { display: grid; grid-template-columns: 296px minmax(0, 1fr); gap: 14px; align-items: start; }
}
/* Desktop — the note gets the extra width, the browse column does not. */
@media (min-width: 1024px) {
    .nt-work { grid-template-columns: 340px minmax(0, 1fr); gap: 18px; }
}
/* Phone — one pane at a time, chosen by the state. */
@media (max-width: 767.98px) {
    body.pane-note .nt-browse { display: none; }
    body.pane-list .nt-note { display: none; }
}
@media (min-width: 768px) { .nt-head .nt-back { display: none; } }

/* ── FIND + FILTERS ─────────────────────────────────────────────────────── */
.nt-filters { flex-wrap: wrap; }
.nt-tagrow { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.nt-tagcap { flex: 0 0 auto; margin-right: 2px; }
.nt-tagrow .chip { height: 28px; padding: 0 11px; }

/* ── THE DIRECTORY TREE — the house rows, plus what is genuinely this card's ─
   ⚠⚠⚠ v31.160 (owner): *"Use the same structured 'Tree View' with styling to
   show selection results. The structure is there in Entity Switch, Transaction
   Types etc."*

   HE WAS RIGHT AND IT WAS ALREADY THERE. Every rule that drew a ROW here —
   `.nt-nodebtn`, `.nt-caret`, `.nt-nodename`, `.nt-kids`, `.nt-fold`,
   `.nt-fdot`, `.nt-entname` — was a SECOND tree control standing beside the
   house one, with its own row height, its own indent mechanism (a margin and a
   hairline, where the house uses padding so a hover fill still starts at the
   panel's left edge) and its own selected treatment. All deleted. The rows are
   `.acc-tree-row` at depths d1-d4 now, so this is the third caller of the tree
   the owner has already sized twice himself.

   ⚠ A RULE KEPT FOR MARKUP THAT NO LONGER EXISTS IS THE NEXT READER'S FALSE
     LEAD, so nothing below is a leftover: `.nt-ent` groups a company's rows for
     the pager, `.nt-leaf*` is the ✓ mark and the un-openable state, and that is
     all that is left. */
.nt-node + .nt-node { border-top: 1px solid rgba(104,152,232,.13); }

/*  ⚠ NO `display` PROPERTY ON `.nt-ent` — the pager hides paged-out rows with
    the `hidden` attribute, whose only power is the UA's `display: none`, and
    ANY display declaration here would beat it. That is the v31.152 trap
    (`hidden` cannot beat a class rule setting display) read the other way
    round. The entity heading is hidden from script with `style.display`, which
    nothing can out-specify. */
.nt-ent { margin-bottom: 2px; }

/*  ⚠⚠⚠ v31.165 (owner) — THE NOTES FLIP RULES ARE REMOVED. *"Remove the flip
    behaviour completely from this card … Remove the flip behaviour from the
    Frequently Used card as well."* The wrappers, both back faces and the
    `nvFlip` controllers went from the page in the same round, so every rule
    that stood here could only ever have matched an element that no longer
    exists — `.nt-flip`, `.nt-flip-freq`, `.nt-flip-list`, `.nt-flipdots` and
    their 1366+ block.
    ⚠⚠ TWO OF THEM WERE THE FIXED HEIGHTS HIS ITEM 5 IS ABOUT: `.nt-flip-freq
    .flip-inner { height: 435px }` and `.nt-flip-list .flip-inner { height:
    460px }`. A flip needs both faces to occupy one box, so a flip card cannot
    size to its content — removing the flip is what makes "size naturally to
    their content" possible at all, and the two asks are the same ask.
    ⚠ `_NvFlip` AND THE HOUSE `.flip` RULES ARE UNTOUCHED — the financials
    history cards, the bank rec faces and the VAT deck all still flip. Only the
    `.nt-`prefixed layer is gone. */


/*  ⚠⚠⚠ v31.165 REMOVED THESE AS DEAD AND v31.166 PUT THREE OF THEM BACK — one
    round apart, which is the whole lesson. `.nt-freqback` and `.nt-listback`
    really were dead and are still gone; `.nt-ftitle`, `.nt-fmarks` and
    `.nt-clearfilter` were NOT dead, they were homeless, because the round that
    deleted the container they lived in also deleted the only route to four
    working filters. **"No `.cshtml` references it" answers whether a rule is
    reachable TODAY, not whether the thing it styled should have gone.** The
    grep was correct and the conclusion was too fast.
    ⚠ `.nt-tagrow` was left alone at v31.165 on the grounds that pulling a class
    out from under two unrelated declarations is how a rule gets resurrected
    wrong later. One round later it was needed again, unchanged. */
.nt-ftitle { margin: 10px 2px 6px; font-size: var(--fs-caption); font-weight: 200; }

/*  the three marks carry a count, so the label and the badge sit apart; the row
    wraps rather than squeezing three long labels onto one line. */
.nt-fmarks { margin-top: 2px; }

    .nt-fmarks .chip { gap: 8px; }

/*  ⚠ a tag chip sizes to its own text. `.nt-tagrow` already wraps; what it must
    NOT do is inherit an equal-width rule — v31.161 measured `.nv-eqrow` giving
    four tags 74px each and clipping "credentials" to "redentia", centred text
    overflowing at both ends with no ellipsis to show it. */
.nt-navcard .nt-tagrow .chip { flex: 0 0 auto; }

.nt-clearfilter { margin-top: 12px; }

/*  ⚠⚠ v31.166 — THE NOTE PANE'S SIDE COLUMN. `.nt-workrow` is a two-track grid
    from 1366, so the tag card and the filter card have to share ONE track to
    stack under each other; as separate grid children the second would drop into
    track 1 under the note. The gap is restated here because a grid/flex gap
    spaces its OWN children only — the v31.158 finding, in its third shape. */
/*  ══ v31.168 — THE ACCOUNT PICKER ON THE NEW-NOTE FORM ════════════════════
    It reuses the house tree row (`.acc-tree-row`, d1/d2) rather than inventing a
    result list, so the picker and the browse tree read as the same control. The
    rows are BUTTONS here — they choose a filing rather than navigate — which is
    the same shape the country picker uses. */
.nt-acctpick { margin-bottom: 12px; }

    .nt-acctpick .nt-acctlist {
        margin-top: 8px;
        max-height: 220px;
        overflow-y: auto;
        overflow-x: hidden;
        border: 1px solid var(--nv-line, rgba(232,167,92,.18));
        border-radius: 10px;
        padding: 4px 0;
    }

    /*  ⚠ a picker row is a BUTTON, so it needs the same reset `a.acc-tree-row`
        got at v31.160 — a UA button brings its own font, background and centring
        and would read as a second visual language inside the same tree. */
    button.nt-acctrow {
        width: 100%;
        background: transparent;
        border: 0;
        font-family: inherit;
        text-align: left;
        cursor: pointer;
        color: var(--nv-ink-2);
    }

        button.nt-acctrow:hover { background: rgba(104, 152, 232, .08); }

    .nt-acctchosen {
        display: flex;
        align-items: center;
        gap: 10px;
        margin-top: 8px;
        min-width: 0;
    }

        .nt-acctchosen .nt-acctname {
            color: var(--nv-gold);
            min-width: 0;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }

        .nt-acctchosen .nt-acctchange { flex: 0 0 auto; }

    .nt-acctneed { margin-top: 8px; font-size: var(--fs-caption); }

.nt-workside {
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
}

/*  ══ v31.161 — THE SEARCH THAT FOUND NOTHING ══════════════════════════════
    It sits in the RESULTS AREA, directly under the search box, because that is
    where the reader is looking. `.nt-noresultacts` uses the toolkit equal-width
    row so the action matches every other button row on the page. */
.nt-noresulttxt { line-height: 1.5; }

.nt-noresultq { color: var(--nv-gold); }

.nt-noresultacts { margin-top: 12px; }

/*  the entity is a GROUPING label, not a destination — v1's company node has a
    tooltip and no notes of its own — so it is the house row in the muted ink
    and takes no pointer. */
.nt-entrow { color: var(--nv-ink-3); cursor: default; }

/*  ⚠⚠ v31.166 — THE CATALOG ROW. Like the entity row it is a grouping label and
    never a destination, so it takes no pointer. It is a shade STRONGER than the
    entity beneath it rather than weaker: it is the top of the tree now, and four
    of them are always on screen, so they are what the eye should land on first.
    ⚠ `.nt-catempty` is a catalog this search did not match. It stays VISIBLE —
    that is the whole point of drawing all four — but it is dimmed and carries
    neither twisty nor count, so it reads as "nothing here" rather than as a node
    that will not open. */
.nt-catrow { color: var(--nv-ink-2); cursor: default; font-weight: 300; }

/*  ⚠⚠⚠ v31.166 — THE FOUR CATALOG ROWS HOLD STILL AND THE OPEN SUBTREE SCROLLS
    INSIDE ITS SHARE. Owner: *"the four catalog nodes are always visible"* AND,
    one round earlier, `max-height: 350px` on this card. MEASURED, both of his
    numbers at once: with the subtree in the card's own scroller, ONE catalog was
    in view and 298px sat below the fold — the DOM had four, the screen had one.
    Making the card taller would break his 350; dropping a catalog would break
    the ask. Neither is necessary: the rows are fixed-size flex children and the
    body takes the remainder, so all four survive ANY card height.
    ⚠ `min-height: 0` is what makes the inner scroller work at all — a flex child
      defaults to `min-height: auto` and will not shrink below its content, so
      without it the body pushes the catalogs out exactly as before. */
.nt-tree.nt-treefix .nt-treescroll { display: flex; flex-direction: column; }

.nt-cat { flex: 0 0 auto; display: flex; flex-direction: column; min-height: 0; }

    /*  ⚠⚠ `0 1 auto`, NOT `1 1 auto` — GROW WOULD UNDO v31.165's ITEM 5. With
        `flex-grow: 1` the open catalog stretched to fill the cap, so the card
        was ALWAYS 350px and "size naturally to their content" was gone one
        round after he asked for it (caught by that round's own gate: content
        294, box 350). Shrink-only gives both asks at once — a short tree ends
        where its rows end, a tall one shrinks the body and scrolls it, and the
        four catalog rows never move in either case. */
    .nt-cat.nt-catopen { flex: 0 1 auto; }

.nt-catbody { flex: 0 1 auto; overflow-y: auto; overflow-x: hidden; min-height: 0; }

    .nt-catrow.nt-catempty { color: var(--nv-ink-3); opacity: .55; }

/*  ⚠⚠⚠ A COUNT MUST NOT SET THE ROW HEIGHT, FOR THE SAME REASON THE TWISTY
    MUST NOT. The house `.badge` is 28px tall (tokens.css:431), and inside a
    tree row that is 28 + 5 + 5 of the row's own padding — MEASURED 38px
    against the 28.00px every other row keeps, which is the number the country
    picker's cap ("14 whole rows in 400px") is built on and which the owner has
    twice set the row font against without moving.

    So the badge is brought inside the row's 18px line box rather than the row
    being allowed to grow around it. Scoped to `.acc-tree-row`, so the badge is
    untouched everywhere else in the app. */
.acc-tree-row .badge {
    height: 18px;
    min-height: 0;
    min-width: 20px;
    padding: 0 6px;
    font-size: 11.5px;
    line-height: 18px;
    flex: 0 0 auto;
}

/*  v1 prefixes a note leaf with ✓ (`"✓ " + folderName`) so it is never mistaken
    for a folder. The mark takes the house twisty slot — the 12px column that
    must never set the row height — and is green rather than gold so a leaf
    reads differently from an open branch at a glance. */
.nt-leafmark { color: var(--acc-green, #3fca85); }

/*  ⚠⚠ A NOTE THE READER MAY NOT OPEN IS SHOWN AND NOT LINKED — v1's tree has
    no privacy test at all and prints every user's "Me only" titles. Dimmed,
    no pointer, no href, so the shape of the folder stays honest without
    offering a way in. */
.nt-leaflocked { opacity: .5; cursor: default; }

    .nt-leaflocked .nt-leafmark { color: var(--nv-ink-3); }

/* ── NOTES LIST ─────────────────────────────────────────────────────────── */
.nt-item { cursor: pointer; }
.nt-item.on { background: rgba(232,167,92,.07); border-radius: 10px; }
.nt-item.on .t { color: var(--nv-gold-bright, var(--nv-gold)); }
.nt-marks { display: flex; align-items: center; gap: 9px; color: var(--nv-ink-3); }
.nt-mark { display: inline-flex; align-items: center; gap: 3px; }
.nt-mark.gold { color: var(--nv-gold); }
.nt-mn { font-size: 12px; font-weight: 200; }
.nt-locked .t, .nt-locked .ic { opacity: .55; }
.nt-recent { display: flex; flex-direction: column; gap: 8px; }

/* ── THE NOTE ───────────────────────────────────────────────────────────── */
.nt-crumb { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin-bottom: 10px; }
/*  ⚠ v31.184 (owner) — 26px, up from 13. SIZE ONLY: nothing else in this rule
    moved, and the separator beside it keeps its own 13px on purpose (see
    `.nt-crumbsep` below) because he named this selector and no other. */
.nt-crumbbtn {
    background: transparent; border: 0; padding: 0; font-family: inherit;
    font-size: 26px; font-weight: 200; color: var(--nv-ink-3);
}
.nt-crumb-acc { color: var(--nv-gold); }

/*  ⚠ v31.187 (owner, N-39) — *"Bump the separator to match."* 13px between two
    26px labels put the `›` on the baseline of neither and let it wrap onto a
    line of its own ahead of the folder name. It is the crumb's punctuation, so
    it takes the crumb's size. */
.nt-crumbsep { color: var(--nv-ink-3); font-size: 26px; }
.nt-crumbedit {
    margin-left: auto; font-size: 12px; font-weight: 200; color: var(--nv-ink-3);
    border: 1px solid rgba(104,152,232,.28); border-radius: 999px; padding: 2px 10px;
}
.nt-title { font-size: 24px; font-weight: 200; color: var(--nv-ink-1, #e9eefc); margin: 0 0 8px; }
.nt-titlefield { margin-bottom: 10px; }
.nt-meta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: 13px; font-weight: 200; color: var(--nv-ink-2); }
.nt-av, .nt-av2 {
    width: 26px; height: 26px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 11px; font-weight: 300; color: var(--nv-gold);
    border: 1px solid rgba(232,167,92,.4); background: rgba(232,167,92,.08);
}
.nt-av2 { flex: 0 0 46px; width: 38px; height: 38px; font-size: 13px; }
.nt-dot { color: var(--nv-ink-3); }
.nt-tags-live { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.nt-tagchip { height: 28px; padding: 0 10px; gap: 6px; }
.nt-x { color: var(--nv-ink-3); font-size: 15px; }
.nt-addtag { height: 28px; padding: 0 11px; border-style: dashed; }
.nt-tagsuggest { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 8px; }
.nt-tagsuggest .chip { height: 26px; padding: 0 10px; }
.nt-newtag { flex-basis: 100%; }
/*  ⚠ v31.159 — `.nt-flags` and its chips are gone with the row the owner
    asked to remove. A rule kept for markup that no longer exists is the
    next reader's false lead. */

.nt-body { font-size: 15px; font-weight: 200; line-height: 1.6; color: var(--nv-ink-2); }
.nt-body p { margin: 0 0 12px; }
.nt-body ul { margin: 0; padding-left: 20px; }
.nt-body li { margin-bottom: 6px; }
.nt-rted, .nt-secfree { min-height: 150px; height: auto; }
.nt-secfree { min-height: 70px; }

/* ── SECURE INFORMATION ──────────────────────────────────────────────────
   ⚠ THE OWNER'S RULE: the difference between ordinary note content and
     encrypted content must be unmistakable, and nothing may imply that
     secure values are held as plain text. So this card carries its own gold
     edge, its own lock, and says in words what happens to the value.       */
.nt-secure { border-color: rgba(232,167,92,.42); background: rgba(232,167,92,.035); }
.nt-secstate { color: var(--nv-gold) !important; }
.nt-seclock { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.nt-lockbadge {
    flex: 0 0 52px; height: 52px; border-radius: 14px;
    display: flex; align-items: center; justify-content: center;
    color: var(--nv-gold); border: 1px solid rgba(232,167,92,.4); background: rgba(232,167,92,.08);
}
.nt-locktxt { flex: 1 1 200px; min-width: 0; }
.nt-locktxt .t { font-size: 15px; font-weight: 200; color: var(--nv-ink-2); }
.nt-locktxt .s { margin-top: 3px; }
.nt-reveal { width: auto; padding: 0 22px; height: 44px; flex: 0 0 auto;
             border: 1px solid rgba(232,167,92,.45); background: rgba(232,167,92,.1); color: var(--nv-gold-bright, var(--nv-gold)); }
.nt-secstrip {
    display: flex; align-items: flex-start; gap: 9px; margin-bottom: 14px;
    font-size: 13px; font-weight: 200; line-height: 1.5; color: var(--nv-gold);
}
.nt-secstrip svg { flex: 0 0 16px; margin-top: 2px; }
.nt-secgrid { display: grid; grid-template-columns: 1fr; gap: 10px; }
@media (min-width: 640px) { .nt-secgrid { grid-template-columns: 1fr 1fr; } }
.nt-secnotes { margin-top: 12px; display: flex; flex-direction: column; gap: 4px; }
.nt-addsec { margin-top: 12px; height: 30px; border-style: dashed; }

/* ── DOCUMENTS ──────────────────────────────────────────────────────────── */
.nt-docacts { display: flex; align-items: center; gap: 2px; }
.nt-drop {
    margin-top: 12px; padding: 20px 16px; border-radius: 14px; text-align: center;
    border: 1px dashed rgba(104,152,232,.4); background: rgba(18,30,56,.22);
    color: var(--nv-ink-3);
}
.nt-drop .t { font-size: 15px; font-weight: 200; color: var(--nv-ink-2); margin-top: 8px; }
.nt-drop .s { margin-top: 4px; }

/* ── ACCESS ─────────────────────────────────────────────────────────────── */
.nt-vis { flex-wrap: wrap; }
.nt-visnote { margin-top: 10px; }
.nt-people { margin-top: 12px; border-top: 1px solid rgba(104,152,232,.13); }
.nt-check {
    width: 24px; height: 24px; border-radius: 7px; display: inline-flex;
    align-items: center; justify-content: center; font-size: 13px;
    border: 1px solid rgba(104,152,232,.4); color: transparent;
}
.nt-check.on { border-color: var(--acc-green, #3fca85); color: var(--acc-green, #3fca85); background: rgba(63,202,133,.1); }

/* ── SHARE ──────────────────────────────────────────────────────────────── */
.nt-inmailnote {
    display: flex; align-items: flex-start; gap: 9px; margin-bottom: 14px;
    font-size: 13px; font-weight: 200; line-height: 1.5; color: var(--nv-ink-2);
    padding: 10px 12px; border-radius: 12px;
    border: 1px solid rgba(87,164,240,.32); background: rgba(87,164,240,.06);
}
.nt-inmailnote svg { flex: 0 0 18px; margin-top: 1px; color: var(--acc-blue, #57a4f0); }
.nt-recips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.nt-sharepv { margin-top: 14px; display: flex; flex-direction: column; gap: 9px; }
.nt-cb { display: flex; align-items: center; gap: 9px; font-size: 14px; font-weight: 200; color: var(--nv-ink-2); }
.nt-sharebtn { margin-top: 16px; }

/* ── ACTIONS ────────────────────────────────────────────────────────────── */
.nt-actions { display: flex; flex-direction: column; gap: 10px; }
@media (min-width: 640px) { .nt-actions { flex-direction: row; } .nt-actions .btn { flex: 1 1 0; } }
.nt-del { border: 1px solid rgba(224,110,110,.45); color: #e58b8b; background: transparent; }
.nt-owner, .nt-notowner { text-align: center; padding: 0 8px 8px; }

/* ── OVERLAYS ───────────────────────────────────────────────────────────── */
.nt-scrim {
    position: fixed; inset: 0; background: rgba(3,6,14,.66);
    backdrop-filter: blur(3px); z-index: 40; display: none;
}
.nt-sheet {
    position: fixed; z-index: 41; display: none;
    left: 12px; right: 12px; bottom: 12px;
    background: linear-gradient(160deg, rgba(20,32,58,.98), rgba(10,16,32,.99));
}
@media (min-width: 640px) {
    .nt-sheet { left: 50%; right: auto; bottom: auto; top: 50%; width: 460px; transform: translate(-50%, -50%); }
}
.nt-sheet .cact { margin-top: 4px; }
.nt-sheet .cact .btn { flex: 1 1 0; }

/* ══ STATE MACHINE ═══════════════════════════════════════════════════════
   Every visibility rule the mock-up needs, in one place, so a reviewer can
   see exactly which states exist and what each one changes.               */
.nt-editonly, .nt-secopen, .nt-people, .nt-notowner, .nt-editedonly,
.nt-tagsuggest, .nt-recips, .nt-sharepv { display: none; }

body.mode-edit .nt-viewonly { display: none; }
body.mode-edit .nt-editonly { display: flex; }
body.mode-edit .nt-tagsuggest { display: flex; }
body.mode-edit span.nt-editonly, body.mode-edit .nt-x.nt-editonly { display: inline; }
body.mode-edit .nt-editor { display: block; }
body.mode-edit .nt-titlefield { display: flex; }

body.edited .nt-editedonly { display: inline; }

body.sec-open .nt-seclock { display: none; }
body.sec-open .nt-secopen { display: block; }
body.sec-open .nt-secstate::after { content: " · unlocked"; }

body.vis-people .nt-people { display: block; }
body.vis-people .nt-vis .chip.on { background: none; border-color: rgba(104,152,232,.28); color: var(--nv-ink-2); }
body.vis-people .nt-vispeople { background: rgba(232,167,92,.14); border-color: rgba(232,167,92,.45); color: var(--nv-gold-bright, var(--nv-gold)); }
body.vis-people .nt-visnote { display: none; }

body.share-live .nt-recips { display: flex; }
body.share-live .nt-sharepv { display: flex; }

body.not-owner .nt-notowner { display: block; }
body.not-owner .nt-owner { display: none; }
/*  ⚠⚠⚠ v31.343c — SAVE AND DELETE PART COMPANY HERE TOO.
    `body.not-owner` hid both, which was right while deleting and editing were
    the same right. Item 1 separated them: an administrator may remove somebody
    else's note without being able to rewrite it. The server already said so —
    `NoteDetail.CanDelete` was true and the button was rendered — and this one
    rule hid it anyway, so the whole admin arm of the new rule was invisible in
    the UI while passing every server-side test.

    ⚠⚠ `body.can-delete` IS THE SERVER'S ANSWER, carried onto the body beside
    `not-owner` rather than recomputed in CSS. The page never decides who may
    delete; it is told, and this selector only stops overriding the answer.
    ⚠ SAVE IS UNCHANGED — still hidden for a non-author, which is the half of
    the old rule that was always correct. */
body.not-owner .nt-save { display: none; }
body.not-owner:not(.can-delete) .nt-del { display: none; }

/*  ⚠ v31.159 — `.nt-flags` removed on the owner's word; the selector
    dropped with it rather than being left pointing at nothing. */
body.is-new .nt-title, body.is-new .nt-meta { display: none; }
body.is-new .nt-crumbedit { border-color: rgba(232,167,92,.5); color: var(--nv-gold); }

/* focus ring — the state's subject card, so a reviewer can see which one
   each state is about without reading the label */
.nt-focus { box-shadow: 0 0 0 1px rgba(232,167,92,.55), var(--shadow-card); }

/*  ══════════════════════════════════════════════════════════════════════════
    v31.343b (owner) — "Cannot delete: #ntDelBtn is not clickable"

    ⚠⚠⚠ THE BUTTON WAS ALWAYS CLICKABLE. Measured: the handler ran, the
    confirmation sentence was composed correctly, `hidden` flipped to false —
    and `#ntSheet` still painted 0 × 0, because `.nt-sheet` above sets
    `display: none` as a CLASS RULE and **the `hidden` attribute cannot beat
    one**. This module recorded that exact trap at v31.152 (four states shipped
    unreachable for the same reason); it survived here in the one state nobody
    re-opened afterwards. *A green build, a passing gate and a rendered page are
    three different claims* — and so is a bound event handler.

    ⚠⚠ AND THE STATE MACHINE NAMED CLASSES THAT DO NOT EXIST. The four rules
    this replaces revealed `.nt-sheet-del`, `.nt-sheet-private`,
    `.nt-sheet-shared` and `.nt-sheet-deleted`. **Not one of those appears in
    any markup** (the shipped element is `class="card confirm-sheet nt-sheet"`),
    and nothing anywhere set `body.sheet-del` either, so even the half that was
    written could never fire. They are removed rather than left aiming at
    nothing — this file's own v31.159 rule.

    ⚠ THE REVEAL IS A BODY CLASS, matching every other state above it, so the
    page has ONE mechanism for "a thing is showing" instead of two that
    disagree. The JS keeps `hidden` in step for assistive technology; the class
    is what paints.
    ══════════════════════════════════════════════════════════════════════════ */
body.sheet-del .nt-scrim { display: block; }
body.sheet-del .nt-sheet { display: block; }

/* ══ CORRECTIONS — found by rendering it, not by reading it ═════════════ */

/*  ⚠⚠⚠ THE MOCK-UP DOES **NOT** MAKE `.scroll` A SCROLLER, AND THAT WAS A
    REAL FINDING RATHER THAN A STYLE CHOICE. The first writing added
    `overflow-y: auto` to it and nothing moved. `app.css:86` sets
    `.app .scroll { overflow: visible; flex: initial; }` at higher specificity,
    and the sheet says so in its own words at 8816: *"`overflow: visible`,
    `.scroll` included, despite the name."*

    So in V2 THE DOCUMENT SCROLLS, not an inner box. Anything built here has to
    scroll the document too — a Notes page that introduced its own inner
    scroller would behave differently from every other screen in the app. */

/*  ⚠⚠⚠ v31.156 — THIS RULE IS REVERSED, ON THE OWNER'S WORD.
    It used to read `flex: 0 0 auto`, on the reasoning below:

      "`.chips > .chip { flex: 1 1 0 }` makes every chip the SAME width, which
       is right for 3-4 short words and wrong here: 'Selected people' was being
       squeezed until it broke onto two lines inside its own pill."

    The squeezing was real; the conclusion was wrong. The answer to a label
    that will not fit is an ELLIPSIS, not an opt-out from equal widths —
    which is precisely what the owner asked for: *"must have the btns same
    width … if secure information text does not fit, please make ellipsis."*

    ⚠ So these three rows now inherit `.chips > .chip` (tokens.css:479 — the
      house has had equal-width chips all along) and take their ellipsis from
      the `.nv-eqrow` toolkit rule. `white-space: nowrap` STAYS: without it the
      text wraps instead of ellipsising, which is the two-lines-in-a-pill
      failure the old comment describes. */
.nt-vis .chip, .nt-filters .chip, .nt-tagrow .chip {
    white-space: nowrap;
}

/*  On a desktop the browse column HOLDS STILL and the note scrolls past it -
    otherwise reading a long note scrolls the account tree off the screen and
    the workspace stops feeling like one place. Sticky against the document
    scroller, which is the one the app actually uses. */
@media (min-width: 1024px) {
    .nt-browse {
        position: sticky;
        top: 12px;
        max-height: calc(100dvh - 24px);
        overflow-y: auto;
        padding-right: 4px;
    }

    /*  ⚠⚠⚠ v31.158 — WITHOUT THIS THE 350px ACCOUNTS CARD IS NOT 350px.
        This column is a flex column WITH A max-height, and a flex child's
        `flex-shrink` is 1 by default — so `height: 350px` is only a starting
        size, and the moment the column's contents exceed the viewport the
        browser takes the difference out of the children. Putting the
        Frequently Used card back on the house type ladder grew it by 198px,
        the column overflowed, and the Accounts card silently became 246px
        while still declaring 350. The gate caught it; nothing in the CSS
        looked wrong, because nothing in the CSS was wrong.
        The column already scrolls — that is what `overflow-y: auto` is for —
        so no card in it should ever be squeezed to make room. */
    .nt-browse > * { flex: 0 0 auto; }
}

/* ── shipped-only: controls the mock-up showed as static markup ─────────── */
.nt-msg { border-color: rgba(63,202,133,.42); color: var(--acc-green, #3fca85); font-size: 14px; font-weight: 200; }
.nt-msg-noaccess, .nt-msg-notyours, .nt-msg-noheader,
.nt-msg-noaccount, .nt-msg-norecipient,
.nt-msg-sendfail { border-color: rgba(232,167,92,.45); color: var(--nv-gold); }

/*  ══ v31.184 (owner) — THE RED FACE OF THE MESSAGE BOX ══════════════════════
    *"The message 'name' + 'not found' should be displayed in the same pattern
    as the red 'pill' message box."*

    ⚠ THE COLOURS ARE THE HOUSE RED AND NOT A NEW ONE. `rgba(217,101,101,…)`
      with `#e79a9a` text is exactly what `.login-error` (app.css:381) and the
      Open Periods inline validation `.op-err-top` already use — three surfaces,
      one red, so "something was not found" reads the same wherever it appears.
    ⚠⚠ THE STRIP'S GREEN DEFAULT IS OUT-SPECIFIED, NOT EDITED. `.nt-msg` sets
      the green; these variants sit after it at the same weight, which is how
      the gold ones have always worked. */
.nt-msg-notfound {
    border-color: rgba(217,101,101,.45);
    background: rgba(217,101,101,.12);
    color: #e79a9a;
}

    /*  ⚠ `.muted` sits on the text div inside this card and out-specifies the
        card's own colour, so the message read grey inside a red box — measured
        on screen, not assumed. Both halves of the sentence are the message. */
    .nt-msg-notfound .nt-noresulttxt { color: #e79a9a; }
    .nt-msg-notfound .nt-noresultq { color: #f2c2c2; }
.nt-itemlink { text-decoration: none; color: inherit; display: block; }
.nt-item.on .nt-itemlink { color: var(--nv-gold-bright, var(--nv-gold)); }

/*  ⚠⚠⚠ v31.158 (owner) — "purple under line and weird blue underline".
    Measured off his screenshot: the underline pixels are #551A8B and #0000EE,
    which are Chrome's OWN :visited and :link colours. Purple simply meant he
    had already opened that note.

    THE CAUSE IS THE ELEMENT, NOT THE CLASS. The locked mock-up draws these
    rows as `<div class="row nt-item nt-freqitem">` and the tree nodes as
    static markup, because a mock-up does not navigate. What ships has to
    navigate, so both are `<a>` — same classes, same stylesheet, but an anchor
    arrives carrying UA link colour and `text-decoration: underline`, and
    ⚠ text-decoration PROPAGATES to descendants and cannot be cancelled by
    them, which is why the underline showed in link-blue under text that was
    correctly grey.

    ⚠⚠ THE LINE ABOVE PROVES I HAD ALREADY MET THIS AND FIXED ONE OF THREE.
    `.nt-itemlink` is in this very section, added for this very reason, while
    `.nt-freqitem` and `.nt-nodebtn` were left as the mock-up wrote them. The
    house rule is the owner's own, from v29.6 on `.chip`: "a chip is often an
    <a>; nothing here is ever underlined". All three now say so together. */
/*  ⚠ v31.160 — `.nt-nodebtn` dropped from this list: the tree rows are the
    house `.acc-tree-row` now and `a.acc-tree-row` carries the same
    `text-decoration: none`, from the same v29.6 ruling. */
.nt-freqitem { text-decoration: none; }
a.nt-freqitem { color: inherit; }
.nt-treehint { padding: 6px 2px 8px; }
.nt-inline { display: inline; }
.nt-tagin { height: 28px; width: 96px; padding: 0 10px; font-size: 13px; }
.nt-cbx { width: 22px; height: 22px; accent-color: var(--acc-green, #3fca85); }
/*  ⚠ v31.159 — `.nt-file` deleted. It was a local 13px style on the
    documents file input, which is exactly why the house gold
    "choose files" pill (`input.input[type=file]`, app.css:1668) never
    appeared here. The input carries `.input` now, like every other. */
.nt-upload { margin-top: 10px; }
/*  ⚠⚠⚠ v31.184 (owner, N-35.8) — `.nt-shareto` NOW OWNS ITS OWN SPACING.
    It used to be written `class="nt-people nt-shareto"`, borrowing the access
    list's margin and hairline — and with them `.nt-people`'s `display: none`,
    which only lifts under `body.vis-people`. The share card therefore drew its
    heading, its `i` note and its Share button over an EMPTY recipient list on
    every note whose visibility was not "Selected people", and Share could only
    answer *"Choose who to share it with."* Measured: computed `display: none`
    in the share view. The two lines it was borrowing are now its own. */
.nt-shareto {
    margin-top: 12px;
    border-top: 1px solid rgba(104,152,232,.13);
    max-height: 260px;
    overflow-y: auto;
}

/*  the share list is the same two-line house row as the Send-to list, and gets
    the same fix — `.row .txt .t` / `.s` set type and ellipsis but never
    `display`, so name and address ran together on one line. */
.nt-shareto .nt-person .txt .t,
.nt-shareto .nt-person .txt .s { display: block; }

.nt-empty { text-align: left; }

/*  ══ v31.183 (owner, N-35.4) — SEND TO ══════════════════════════════════════
    *"The field MUST be populated with email selected from Team members, same as
    checkbox pattern in calendar."*

    ⚠⚠⚠ IT IS **NOT** `.nt-people`, AND THAT IS DELIBERATE. `.nt-people` is
    `display: none` at app.css:12938 and only comes back under
    `body.vis-people` — it is the ACCESS list, which belongs to one visibility
    state. The recipient is a property of the note in every state, so reusing
    that class would have hidden this list on three of the four chips. Only the
    ROW styling is shared, and that already lives on `.nt-person`.
    ⚠ The checkbox is `input.nv-check` — the house NOVERA box, gold border and
      gold tick, which is the calendar's and therefore the one he named. */
/*  ⚠⚠⚠ v31.184 (owner) — SEND TO IS PART OF SHARING, SO IT SHOWS WITH SHARING.
    *"The Send to checkbox list should only be visible only when 'Share via
    email' is selected."* It stays where it is in the DOM — inside `ntForm`, so
    it saves with the note and a half-made choice survives a trip to another
    view — and only its VISIBILITY moves onto `body.view-share`. Putting the
    markup itself inside the share card was not open to us: that card is its own
    `<form>`, and a form cannot nest. */
.nt-sendto {
    display: none;
    margin-top: 14px;
    border-top: 1px solid rgba(104,152,232,.13);
    padding-top: 12px;
}

body.view-share .nt-sendto { display: block; }
.nt-sendlbl { display: block; margin-bottom: 6px; }
/*  ⚠ 256px, WHICH IS THE SHARE LIST'S 260 ROUNDED TO WHOLE ROWS. Measured at
    the owner's 1270: a two-line `.nt-person` is ~64px, so 220 cut the fourth
    row in half and the block read as damaged rather than as scrollable. */
.nt-sendlist { max-height: 256px; overflow-y: auto; }

/*  ⚠ MEASURED, NOT ASSUMED. `.row .txt .t` and `.s` (tokens.css:441/450) set
    type and ellipsis but NOT display, so they are inline spans by default — the
    name and the address ran together as one line of text here. Every other
    two-line row in the app gets `block` from somewhere else (`.nt-item .t` is an
    `<a>`), so this list has to say it for itself. */
.nt-sendlist .nt-person .txt .t,
.nt-sendlist .nt-person .txt .s { display: block; }
.nt-sendhint { font-size: 13px; font-weight: 200; margin-top: 6px; }

/*  ══ v31.184 (owner) — FIND AND ADD SHARE ONE ROW ═══════════════════════════
    *"Move the add '+' icon to the same vertical position but to the row below
    which contains 4 card views."* The + left the app header and landed here, at
    the right-hand end of the row below it — the same x it always had, one row
    down. `align-items: flex-end` puts the button on the input's line rather
    than the hint's, so the two controls share a baseline. */
.nt-findrow { display: flex; align-items: flex-end; gap: 10px; min-width: 0; }
.nt-findrow .nt-find { flex: 1 1 auto; min-width: 0; }

/*  ══ v31.185 (owner) — THE + SITS ON THE NOTE HEAD CARD ═════════════════════
    *"Move the add '+' from its current position to the top right hand position
    of the main card with 'card nt-notehead'."*

    ⚠⚠ `align-items: flex-start` IS THE LOAD-BEARING LINE. The crumb is 26px
    since v31.184 and wraps to two lines on a long account name; centring would
    let the + drift down with the second line instead of holding the card's top
    right corner, which is the position he named.
    ⚠ `min-width: 0` on the crumb so the wrap happens inside it rather than
    pushing the button out of the card. */
.nt-crumbhead {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
    min-width: 0;
}

    .nt-crumbhead > .nt-crumb, .nt-crumbhead > .section { flex: 1 1 auto; min-width: 0; }
    .nt-crumbhead > .nt-add { flex: 0 0 auto; margin-top: -2px; }

/*  the Documents view on a note that has no id yet: the panel is drawn in full
    and the file pill is inert, because there is nothing to attach a file TO
    until the row exists. Dimmed, not hidden — the reader asked for this view. */
/*  v31.186 (owner, N-42) — the reader who may not attach is TOLD, not left
    looking at a control that would be refused. The house gold caution face,
    the same one `.nt-msg-noaccount` and the Open Periods hints wear. */
.nt-nodocedit {
    margin-top: 8px;
    padding: 10px 12px;
    border-radius: var(--r-card);
    border: 1px solid rgba(232,167,92,.45);
    background: rgba(232,167,92,.08);
    color: var(--nv-gold);
    font-size: 14px;
    font-weight: 200;
    line-height: 1.45;
}

.nt-dropoff { opacity: .55; }
.nt-dropoff input[type=file] { pointer-events: none; }

/*  ══ v31.183 (owner) — THE LIST HEADING CLIPS ═══════════════════════════════
    *"update the span that contains the h2 section name to the selected name.
    The label should be ellipsis."*

    ⚠⚠ `text-overflow` ALONE DOES NOTHING HERE. `h2.section` (tokens.css:295) is
    a flex row, and a flex item will not shrink below its own content until
    `min-width` is 0 — the default is `auto`. Without that line a long account
    name simply widened the heading and pushed the note count off the card,
    which is the shape the ellipsis is being asked to prevent.
    ⚠ `.more` is pinned so the count is what survives the squeeze, not the
      thing that gets squeezed. */
.nt-list .nt-listname {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nt-list > .section .more { flex: 0 0 auto; }
/*  ⚠ the v1-written secure payload: its own state, never blank, and the box
    beside it is read-only so it cannot be typed over and re-saved. */
.nt-seclegacy {
    margin-bottom: 12px; padding: 10px 12px; border-radius: 12px;
    font-size: 13px; font-weight: 200; line-height: 1.5;
    border: 1px solid rgba(232,167,92,.45); background: rgba(232,167,92,.08);
    color: var(--nv-gold);
}
.nt-delform { margin-top: 4px; }
.nt-delform .btn { width: 100%; }


/* ═══════════════════════════════════════════════════════════════════════════
   ⚠⚠⚠ TOOLKIT PATTERN — `.nv-eqrow`, THE EQUAL-WIDTH ROW   (v31.156, owner)
   ═══════════════════════════════════════════════════════════════════════════
   Owner: *"The row with secure information, documents, me only must have the
   btns same width. If secure information text does not fit, please make
   ellipsis."* — then the same for the note's tags, for "Who can see this", and
   for the tags on the right-hand panel. *"Can you make this a toolkit
   pattern."*

   ⚠ IT IS A CLASS CONTRACT, NOT A `.cshtml` DOOR, AND THAT IS DELIBERATE.
     Every other toolkit door (`_NvInfo`, `_NvPager`, `_NvDrill` …) renders a
     control it owns end to end. This one has to WRAP ARBITRARY CHILDREN — a
     flag with an icon, a tag with a remove ×, a visibility button, a chip that
     is a link — and a Razor PARTIAL cannot wrap child content: there is no
     render-body for partials. Inventing a model that enumerates every possible
     child would be a contract with a "step 3" in it, which is exactly the
     failure `toolkit-doors` warns about. A class costs one attribute and
     cannot be wired up wrongly.

   ── THE CONTRACT ───────────────────────────────────────────────────────────
     Put `.nv-eqrow` on the container. Every DIRECT child becomes one equal
     column and ellipsises rather than wrapping or overflowing.
     Add `.nv-eqrow-wrap` when the row should break onto further lines instead
     of squeezing (used by the tag rows, which can be long).

   ⚠⚠ THE TWO THINGS THAT MAKE IT ACTUALLY WORK, both learned the hard way:

     1. `flex: 1 1 0` — NOT `flex: 1`, and never `width` alone. `tokens.css:539`
        gives every `.btn` `width: 100%`, and a `width` on the child wins over a
        flex-basis of `auto`. Only a ZERO basis makes the columns equal.

     2. `min-width: 0` ON THE CHILD. A flex item's default `min-width: auto`
        refuses to shrink below its content, so `text-overflow: ellipsis` never
        fires and the row overflows instead. This is the single most common
        reason an ellipsis "does not work" in a flex row.
   ═══════════════════════════════════════════════════════════════════════════ */
.nv-eqrow {
    display: flex;
    gap: 8px;
    align-items: stretch;
    min-width: 0;
}

    .nv-eqrow > * {
        flex: 1 1 0;          /* equal columns — see note 1 above */
        min-width: 0;         /* lets the ellipsis fire — see note 2 above */
        width: auto;          /* beats tokens.css `.btn { width: 100% }` */
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        justify-content: center;
    }

        /*  a child that is itself a flex box (an icon + a label) needs the
            ellipsis applied to the LABEL, because the flex box will not
            ellipsise itself. */
        .nv-eqrow > * > span:not(.ic):not(.icw):not(.badge):not(.nt-mn) {
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            min-width: 0;
        }

        /*  an icon must never be the thing that shrinks */
        .nv-eqrow > * > svg,
        .nv-eqrow > * > .ic,
        .nv-eqrow > * > .icw { flex: 0 0 auto; }

/*  ⚠ THE WRAPPING VARIANT. A tag row can hold eight tags; squeezing eight into
    one line makes eight ellipses and no information. These wrap, and the items
    on every line is the same width.

    ⚠⚠ `flex-grow` IS ZERO HERE, AND THAT IS THE WHOLE POINT. With `1 1 basis`
    the items on a line share the leftover space — fine for a full line, absurd
    for a short one: a single trailing tag stretched the full width of the card.
    **The screenshot caught that and the width assertions did not**, because a
    lone chip on its own line IS trivially "equal to every chip on that line".
    Zero grow means the basis is the width, every chip matches every other chip
    on every line, and a short last line simply ends early. */
.nv-eqrow-wrap {
    flex-wrap: wrap;
}

    .nv-eqrow-wrap > * {
        flex: 0 1 clamp(88px, 30%, 170px);
    }

/*  ⚠⚠ AND THE ROW MUST STILL BE READABLE ON A PHONE. Below 480 an equal split
    of four items is ~80px each, which ellipsises everything to two characters.
    The row wraps to two columns instead — still equal, still no overflow. */
@media (max-width: 479.98px) {
    .nv-eqrow { flex-wrap: wrap; }
    .nv-eqrow > * { flex: 1 1 calc(50% - 4px); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   NOTES v2 — THE LOCKED MOCK-UP, IMPLEMENTED  (v31.156)
   ═══════════════════════════════════════════════════════════════════════════
   ⚠ LOCKED AND IMPLEMENTED at v31.156 on the owner's word — *"Please lock and
   load for next build"*, and his standing ruling that **"by next build" means
   ship this fix**. What was a proposed diff for review is now the sheet.
   Every rule uses existing tokens only; no new colour is invented.

   The existing Notes STATE MACHINE (app.css:12579) is extended, not replaced:
   the same pattern of "default hidden, opened by a class on <body>".
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── ITEM 6 · DISCOVERY MOVES RIGHT ─────────────────────────────────────────
   Only the column ORDER changes. The widths, the gaps and the breakpoints are
   the ones already agreed at v31.150 — 296px at 768, 340px at 1024 — so the
   note gains exactly the width the browse column used to hold and nothing
   else on the ladder moves. */
@media (min-width: 768px) {
    .nt-work { grid-template-columns: minmax(0, 1fr) 296px; }
}
@media (min-width: 1024px) {
    .nt-work { grid-template-columns: minmax(0, 1fr) 340px; }
}

/* ── ITEM 3 · THE THREE PILLS ───────────────────────────────────────────────
   One row, immediately above the working content, so the relationship reads as
   "three views of this note" rather than three sections that happen to follow
   each other. */




    /*  ⚠ THE SELECTED PILL HAS TO BE UNMISTAKABLE — the owner asked for that in
        as many words. Gold fill plus gold text, which is the same "this one"
        language the visibility chips already use, so nothing new is learned. */




/*  ══ THE VIEW SWITCH — the state machine, extended ═══════════════════════
    ⚠⚠⚠ ALL THREE PANELS STAY IN THE DOM. Only visibility changes, which is
    what makes the owner's "changing views must not lose unsaved note state"
    true by construction rather than by a save-and-restore routine that would
    have to be got right. A half-typed secure value survives a trip to
    Documents and back because it never left the page. */
/*  ⚠ v31.159 — `.nt-share` joins the view panels: it was an always-open
    card at the foot of every note and is now the fourth tab. */
.nt-content, .nt-secure, .nt-docs, .nt-share { display: none; }

body.view-note   .nt-content { display: block; }
body.view-secure .nt-secure  { display: block; }
body.view-docs   .nt-docs    { display: block; }
/*  ⚠ v31.159 (owner): *"Share via inMail should be a selection visible as a
    fourth selection after Documents."* It was an always-open card at the foot
    of every note; it is a view now, so it is drawn only when it is chosen. */
body.view-share  .nt-share   { display: block; }

/*  ⚠ the secure panel's own open/locked state is UNCHANGED and still nests
    inside it — `sec-open` continues to do exactly what it did. */

/* ── ITEM 2 · THE ACTION ROW ────────────────────────────────────────────────
   Save Note | Cancel | Delete on one line, in that order.

   ⚠⚠⚠ `tokens.css:539` GIVES EVERY `.btn` `width: 100%` — full-width buttons
   are the house default, mobile-first. So `flex: 0 1 auto` does NOT shrink a
   button to its text; the 100% width wins and each one claims a whole row.
   The existing rule already knew this and used `flex: 1 1 0`, which replaces
   the basis outright. Keeping that idiom rather than inventing a second one.
   (Cost of finding out the hard way: the first writing of this file put the
   three buttons on three rows and the probe said so.) */
.nt-actions {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}

    .nt-actions .btn {
        flex: 1 1 0;
        width: auto;
    }

    /*  ⚠ A small separation before Delete. The owner asked for this order, so
        Delete stays where he put it — but a destructive control flush against
        the one people press to back out is how the wrong button gets clicked,
        and 6px is enough to read as "not part of that pair". */
    .nt-actions .nt-del { margin-left: 6px; }

/*  ⚠⚠ "PRESERVE THE ROW WHEREVER VIEWPORT WIDTH REASONABLY PERMITS."
    The existing breakpoint was 640, which stacks the row on every phone AND
    every small tablet. Three buttons need ~145px each to hold "Save Note" at
    the house 19px without ellipsis; that is satisfied from 480 up, so the row
    survives 160px lower than before. Below 480 they stack full width, which is
    the clean adaptation rather than three ellipsised stubs. */
.nt-actions { flex-direction: column; }

@media (min-width: 480px) {
    .nt-actions { flex-direction: row; }
}

@media (max-width: 479.98px) {
    .nt-actions .btn { flex: 1 1 auto; width: 100%; }
    .nt-actions .nt-del { margin-left: 0; }
}

/*  ══ v31.185 (owner) — 01-SCREEN: EDIT AND DELETE SHARE A ROW ═══════════════
    *"In 01-Screen ONLY ntDelBtn and notes.edit should share same row, just like
    in 02-Screen."*

    ⚠⚠⚠ THE RULE IS ABOUT THE **VIEW** STATE, NOT THE WHOLE ROW, and that is
    exactly what he wrote — Delete and Edit, which is TWO buttons. The edit state
    puts THREE there (Save Note · Cancel · Delete), and three at ~145px each need
    480px they do not have on a phone; the rule above stacks those and stays.
    `.nt-actions-view` is set from Razor on the not-editing state so the
    stylesheet can tell the two apart without counting children.
    ⚠⚠ THE DELETE BUTTON IS INSIDE A `<form>` — Save posts to one handler and
    Delete to another, so they cannot share one. The FORM is the flex child, so
    it is the form that must take a share of the row; styling `.btn` alone left
    it at its natural width and the pair did not divide evenly.
    ⚠ 479.98 IS THE ONLY BAND THAT CHANGES. From 480 up they were already a row
    (that is the "just like in 02-Screen" he is comparing against), so this
    override is scoped to the band below it and touches nothing else. */
@media (max-width: 479.98px) {
    .nt-actions.nt-actions-view { flex-direction: row; align-items: stretch; }
    .nt-actions.nt-actions-view > .btn,
    .nt-actions.nt-actions-view > .nt-delform { flex: 1 1 0; width: auto; margin-top: 0; }
    .nt-actions.nt-actions-view .nt-delform .btn { width: 100%; }
    .nt-actions.nt-actions-view .nt-del { margin-left: 0; }
}

/*  ══ v31.185 (owner) — 01-SCREEN: THE FOUR VIEWS, TWO BY TWO ════════════════
    *"In 01-Screen ONLY the four options at the top should be spread in 2 rows.
    The 4 buttons should be the same width to cover the viewport."*

    ⚠⚠ `.nv-tabs` is `repeat(auto-fit, minmax(92px, 1fr))` (app.css:8780), which
    on a 390px phone fits THREE across (3×92 + 2×8 = 292) and drops the fourth
    onto a row of its own — three narrow chips over one wide one, which is the
    uneven shape he is looking at. `repeat(2, 1fr)` states the 2×2 outright, so
    all four are the same width and together they span the column.
    ⚠ 767.98 IS 01-SCREEN's OWN EDGE on the house ladder (01 ≤767 · 02 768–1023),
    so 02-Screen keeps the single row it has today. */
@media (max-width: 767.98px) {
    .nt-views { grid-template-columns: repeat(2, 1fr); }

    /*  ══ v31.187 (owner, N-39) — *"cap the crumb on 01-Screen."* ═══════════
        MEASURED at 390px before this: "Hikvision Czech Republic › Security /
        Access" took THREE lines at 26px and pushed the note title below the
        fold, so the phone opened on a breadcrumb rather than on the note.
        ⚠ 18px is the cap, not 13. It sits between the house body size and the
          26px he set, so the crumb still reads as the heading he asked for on
          the wide screens and stops being the whole first screen on a phone.
        ⚠⚠ THE SEPARATOR IS CAPPED WITH IT. That is the whole point of N-39 —
          the two sizes must move together or the `›` wraps alone again, which
          is the fault being fixed. */
    .nt-crumbbtn, .nt-crumbsep { font-size: 18px; }
}

/*  ══ v31.185 (owner) — THE TAGS CARD BELONGS TO TWO VIEWS ═══════════════════
    *"The tags card 'nt-workside' should be visible from the 'Notes' and 'Secure
    information' buttons and should be hidden in 'Documents' and 'Share via
    Inmail' selections."*

    ⚠ WRITTEN AS THE TWO STATES THAT HIDE IT, not as the two that show it. The
    resting state of this page is `view-note` and the body carries exactly one
    `view-*` class at a time, so "hide on these two" leaves the card visible in
    every other case INCLUDING the moment before the script runs — a rule
    written the other way round would flash the card off on first paint. */
body.view-docs .nt-workside,
body.view-share .nt-workside { display: none; }

/* ── ITEM 4 · TAGS ARE CONTEXTUAL ───────────────────────────────────────────
   The global tag row is gone from the markup. What replaces it is scoped to
   the folder in hand, and it is not rendered at all until one is chosen. */
.nt-ctxtags { display: none; }
body.has-folder .nt-ctxtags { display: flex; }

/* ── ITEM 7 · FREQUENTLY USED ───────────────────────────────────────────────
   Deliberately lighter than the notes list: no icon column, no marks, two
   lines per row. It is a shortcut, not a second set of results. */
/*  ⚠ "KEEP THIS CARD LIGHTWEIGHT" — the owner's words, and ten two-line rows
    is 560px, which pushed the account navigator below the fold on a 1270
    screen. The ten stay; the CARD is bounded instead, and scrolls inside
    itself. That is the `_NvCardScroll` pattern this toolkit already has, and
    it is the one place a scroller is right — app.css:86 makes the DOCUMENT the
    scroller everywhere else, so this must be opted into deliberately. */
.nt-freq .nt-freqlist {
    max-height: 296px;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/*  ⚠⚠⚠ v31.158 (owner) — "I don't understand the font-sizes … I was expecting
    you would use locked in mock-up styles instead of introducing new unknown
    styling."  He is right, and this block was the offender.

    THE HOUSE ALREADY STYLES THIS EXACT ROW. `tokens.css:400` gives
    `.row .txt .t` = var(--fs-body) (18px) and `.row .txt .s` =
    var(--fs-caption) (16px) with the muted colour and the ellipsis — and the
    markup here is already `.row > .txt > .t/.s`, so all of it applied for
    free. The two overrides below were shrinking it to 13.5px/11.5px, which is
    a size pair that appears NOWHERE ELSE in the solution and puts the second
    line 4.5px under `--fs-caption`, whose own comment reads "captions —
    minimum". In one column the reader was getting three different title sizes:
    18px in the notes list, 15px in the tree, 13.5px here.

    ⚠ THE FIX IS A DELETION. Both overrides are gone and the row now takes the
    house pair, identical to the notes list directly below it — a Frequently
    Used row IS a note row, so it should not have a type scale of its own. */
.nt-freq .nt-freqitem {
    padding: 7px 10px;
    cursor: pointer;
    border-radius: 9px;
}

    .nt-freq .nt-freqitem:hover { background: rgba(104, 152, 232, .08); }


/*  ⚠ At 296px the heading and its "Top 10 · you" note fight for one line and
    both wrap. The note is a nicety; the heading is not. Below 1024 the note
    steps aside rather than making the title two lines. */
@media (max-width: 1023.98px) {
    .nt-freq .section .more { display: none; }
}

/*  the ✦ is the mark the AI cards already use elsewhere in the app */
.nt-aimark {
    color: var(--nv-gold-bright, var(--nv-gold));
    margin-right: 6px;
}

/*  ⚠ A LABEL IS NOT A COLUMN. `.nt-tagcap` ("Tags in Security / Access") and
    the "Existing tags" caption sit inside an `.nv-eqrow`, and without this they
    are sized as equal columns and ellipsised — the caption is the FIRST thing
    to get cut, which is the opposite of useful. Anything marked `.nv-eqrow-lbl`
    keeps its natural width and is never the thing that shrinks. */
.nv-eqrow > .nv-eqrow-lbl,
.nv-eqrow > .nt-tagcap,
.nv-eqrow > .muted {
    flex: 0 0 auto;
    overflow: visible;
    text-overflow: clip;
}

/*  ⚠⚠ DELETE IS WRAPPED IN ITS OWN <form>, SO THE FORM IS THE FLEX CHILD.
    `.nt-actions .btn { flex: 1 1 0 }` never reached it — the button inside was
    sized by a form that was itself content-width, which is why Delete rendered
    as a stub beside a full-width Edit. The FORM takes the column; the button
    fills it. (Two separate forms is not a choice: Save posts to the Save
    handler and Delete to the Delete handler, and forms cannot nest.) */
.nt-actions > form {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
}

    .nt-actions > form > .btn { width: 100%; flex: 1 1 auto; }

@media (max-width: 479.98px) {
    .nt-actions > form { flex: 1 1 100%; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   NOTES v31.157 — THE OWNER'S LAYOUT ROUND
   ═══════════════════════════════════════════════════════════════════════════ */

/*  ── THE VIEW DOT ─────────────────────────────────────────────────────────
    The "there is secure information" mark on the Secure tab. A brand gold dot,
    not a coloured pill: the tints are gone from this page entirely (Notes was
    the ONLY page in the solution using `tint-*`, which is why they looked
    foreign — the owner asked about the purple one and he was right about all
    three). */
.nt-vdot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--nv-gold);
    flex: 0 0 6px;
    margin-left: 2px;
}

/*  ── THE NOTE BODY AND ITS TAGS SHARE A ROW, FROM 04-SCREEN ───────────────
    Owner: *"Reduce the width of the notes Rich Text field and place the tag
    list card to the right of it. For 04 and 05-Screen … in the shared row, but
    for 01, 02 and 03-Screen they are stacked."*

    ⚠ THE RUNGS ARE THE HOUSE'S, NOT NEW ONES: 01 ≤767 · 02 768-1023 ·
      03 1024-1365 · 04 1366-1919 · 05 1920+. So the split starts at 1366 and
      everything below it stacks, which is exactly what he asked for and also
      what the widths allow — at 1024 the note column is ~562px and taking a
      third of it for tags would leave the editor too narrow to write in. */
.nt-workrow {
    display: flex;
    flex-direction: column;
    gap: 12px;
    min-width: 0;
}

@media (min-width: 1366px) {
    .nt-workrow {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 240px;
        gap: 14px;
        align-items: start;
    }
}

@media (min-width: 1920px) {
    .nt-workrow { grid-template-columns: minmax(0, 1fr) 280px; }
}

/*  ⚠ the tag card stacks its rows; inside it the chips still use `.nv-eqrow`,
    so they stay equal to each other in the narrower column too. */
.nt-tagcard { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

/*  ── THE ACCOUNTS CARD ────────────────────────────────────────────────────
    Owner: *"The displayed Accounts list should have a max of 6 items and should
    be paged using the pager pattern. Height of the card should be fixed to
    350px height and content scroll vertically, no horizontal scroll."*

    ⚠⚠ THE HEIGHT IS FIXED, NOT CAPPED. `height` rather than `max-height`, so a
    two-account result and a fifty-account result occupy the same box and the
    Frequently Used card beneath them does not jump up and down as you type.
    ⚠ `overflow-x: hidden` is explicit: a long account name must ellipsise, and
      a horizontal scrollbar inside a 340px column is unusable. */
/*  ⚠⚠ v31.165 (owner) — `height` → `max-height`, HIS OWN BLOCK, VERBATIM.
    *"Cards in the right-hand column below the search area should not use a
    fixed height. Allow them to size naturally to their content, subject to an
    appropriate max-height."*

    ⚠ WHAT THIS ACTUALLY CHANGES: a fixed 350px card drew 350px of box around
    three accounts and a lot of empty space. As a MAXIMUM it is the ceiling it
    always should have been — short trees end where their rows end, long ones
    still cap and scroll at exactly the same place they did before.
    ⚠⚠ `.nt-browse > *` KEEPS ITS `flex: 0 0 auto` and that is now MORE load-
    bearing, not less. The v31.158 note on that rule explains why: this column
    is a flex column with a max-height, so a child's default `flex-shrink: 1`
    lets the browser take space out of it — which would squeeze a card that is
    already sizing to its content. */
.nt-tree.nt-treefix {
    /*  ⚠ v31.220 (owner, mid-round): *"Quick fix needed to style sheet, max
        height"* — 350px to 495px. v31.227 (owner): 495px to 540px. The rest of
        the rule is unchanged both times: it is the CAP that moved, not the
        layout. */
    max-height: 540px;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

    .nt-tree.nt-treefix > .section { flex: 0 0 auto; }

    /*  the rows scroll; the heading and the pager bar do not — the same
        arrangement `_NvPager`'s `wrap` mode makes for the history cards. */
    .nt-treescroll {
        flex: 1 1 auto;
        overflow-y: auto;
        overflow-x: hidden;
        overscroll-behavior: contain;
        min-height: 98%;
    }

    /*  ⚠⚠⚠ v31.163 (owner) — THE TREE NODE READS AT THE SAME SIZE AS A
        FREQUENTLY USED ENTRY. *"Increase the font size of the tree nodes to
        match the note entries in Frequently Used."*

        A Frequently Used entry is the house two-line row: `.row .txt .t` at
        `--fs-body` (tokens.css:400). So that is the token used here, by name
        rather than by its current number — if the body token moves, the tree
        moves with it and this rule does not have to be found again.

        ⚠⚠⚠ SCOPED TO `.nt-tree.nt-treefix`, AND THAT SCOPE IS THE WHOLE POINT.
        `.acc-tree-row` is SHARED with the country picker and the
        chart-of-accounts panel, and its own note in this file records 14px as
        **the owner's own number** for those (17px at v31.66, revised to 14px
        at v31.85). Putting the new size on the shared class would have
        silently overturned that ruling on two panels he did not mention. Two
        trees, two sizes, and the Notes card is the one that changed.

        ⚠⚠ THE LINE BOX HAD TO GROW WITH IT, AND THAT MOVES THE ROW HEIGHT.
        The shared row is `line-height: 18px` + 5px of padding top and bottom =
        28.00px, and every note in this file about that height turns on
        font-size NOT being part of the sum. It stops being free here: an 18px
        glyph in an 18px line box clips its descenders, and `.acc-tree-nm` has
        `overflow: hidden`, so the clipping would be silent. The line box goes
        to 24px, which makes this tree's row 34px against the shared tree's 28.
        ⚠ THAT IS A DELIBERATE, MEASURED CONSEQUENCE, not an oversight: a
        bigger font in a fixed-height row is not possible, and the owner asked
        for the bigger font. The chart panel's "14 whole rows in 400px" cap is
        untouched because that panel is not in this scope. */
    .nt-tree.nt-treefix .acc-tree-row {
        font-size: var(--fs-body);
        line-height: 24px;
    }

    /*  ⚠ THE TWISTY AND THE BADGE DO NOT GROW WITH THE LABEL. The twisty is a
        12px gold state mark whose own rule says it must never set the row
        height, and the badge was scoped to 18px at v31.160 for exactly that
        reason. Both are re-stated here so the larger font cannot drag them up
        by inheritance. */
    .nt-tree.nt-treefix .acc-tree-tw { font-size: 12px; line-height: 24px; }
    .nt-tree.nt-treefix .acc-tree-row .badge { font-size: 11.5px; line-height: 18px; }

    /*  ⚠ v31.160 — was `.nt-nodename`; the house row's own `.acc-tree-nm`
        already ellipsises, so this only re-states it inside the fixed card
        where the column is narrowest. */
    .nt-tree.nt-treefix .acc-tree-nm {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
    }

    /*  ⚠ the pager bar the toolkit inserts sits OUTSIDE the scroller, pinned to
        the bottom of the fixed card, so it never scrolls away. */
    .nt-tree.nt-treefix .nt-treepager { flex: 0 0 auto; margin-top: 8px; }

/*  ── FREQUENTLY USED — five rows, heading on two lines ────────────────────
    Owner: *"Show only five frequently used items. Top 5. The notes.freqTop and
    notes.freqUsed/freqNote should be in two rows."* */
.nt-freq .section {
    display: block;
}

    .nt-freq .section .more {
        display: block;
        margin: 2px 0 0;
        text-align: left;
    }

/*  five rows need no inner scroller — the cap was there for ten. */
.nt-freq .nt-freqlist { max-height: none; overflow: visible; }

/* ═══════════════════════════════════════════════════════════════════════════
   v31.173 (batch b347) — OPEN PERIODS · ENTITY LEVEL
   `/financials/periods`. The rules the owner LOCKED in
   `docs/openperiods-mockup.html`, moved into the shipped sheet so the mock-up
   and the running page cannot drift apart — the same arrangement the Notes
   rules ended in at v31.156.

   ⚠⚠⚠ THE TWO BREAKPOINTS ARE CONTAINER QUERIES, NOT MEDIA QUERIES, AND THAT
   IS THE ONE CHANGE FROM THE LOCKED FILE. The mock-up drew the page in the
   bare app shell; the shipped page carries `_FinPanel`, the 260px ledger rail
   every other page under the Ledger node has, so the content column is
   narrower than the window by an amount that changes at three rungs. Sizing
   the cards off the WINDOW would have re-created, at a different width, the
   very fault the mock-up screenshot caught — a native `type="month"` clipped
   to "December 202". **Ask the box how wide it is, not the window** (the
   AI-tokens container rule). The card design is untouched; only the thing that
   decides "is there room for two" moved.

   ⚠⚠ `container-type: inline-size` also makes the element a containing block
   for `position: fixed` descendants. Nothing on this page is fixed — the
   action bar is STICKY and the entity sheet is hoisted to <body> by
   `_NvEntity` — so this is safe here and must be checked again before the
   property is put on any card that hosts a sheet.
   ═══════════════════════════════════════════════════════════════════════════ */

.op-work {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    /*  ⚠⚠⚠ THE GUTTER IS THE PRICE OF A PERMANENT BAR (v31.174). A sticky
        bottom bar is painted OVER whatever is behind it, so with the bar no
        longer hidden until dirty it sat across the last rows of Closing Items
        from the first paint — two checklist rows and the card's own note were
        unreachable at every width. The column therefore ends in a gutter as
        tall as the bar, so the page can always scroll far enough for its last
        row to clear. ⚠ Re-measure if the bar gains a second line. */
    padding-bottom: 92px;
    min-width: 0;
    container-type: inline-size;
    container-name: opwork;
}

/* ── the entity strip ─────────────────────────────────────────────────────
   The head chip is app-wide furniture and reads as "where I am". This page is
   explicitly ENTITY-SPECIFIC — every value on it belongs to one CompCode — so
   the entity is also stated once in the working area, as the SUBJECT of the
   page rather than as chrome. */
.op-entity {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    flex-wrap: wrap;
}

    .op-entity .op-entname {
        font-size: var(--fs-h2);
        font-weight: 300;
        color: var(--nv-ink);
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .op-entity .op-entcode {
        font-size: var(--fs-caption);
        color: var(--nv-ink-3);
        flex: 0 0 auto;
    }

    /*  ⚠⚠⚠ A HOUSE BUTTON CARRIES NO HORIZONTAL PADDING. `tokens.css:580`
        gives `.btn` `width: 100%` with `justify-content: center` and no
        padding at all — the full width IS the padding. The moment a button is
        taken off 100% its text sits flush against its own border, which is
        what the mock-up's first screenshot showed. Anything that sets
        `width: auto` on a `.btn` must supply the padding itself. Same family
        as the `flex: 1 1 0` trap of v31.155, one property along. */
    .op-entity .op-entpick {
        margin-left: auto;
        flex: 0 0 auto;
        width: auto;
        padding: 0 18px;
    }

/* ── the General Ledger card — the master window ──────────────────────────
   First, and the only card on the page carrying the brand gold, so "everything
   else can follow this" is legible before a word is read. */
.op-gl {
    border-left: 3px solid var(--nv-gold);
}

/* ── a labelled pair ──────────────────────────────────────────────────────
   V1 puts five values in one undifferentiated column. The owner's brief asks
   for the two CONCEPTS to be separated more clearly than V1 does, so each pair
   is its own titled group with the format stated and an explicit arrow. */
.op-grp + .op-grp { margin-top: var(--sp-4); }

.op-grptitle {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--fs-caption);
    color: var(--nv-ink-2);
    margin-bottom: var(--sp-2);
}

    .op-grptitle .op-fmt {
        margin-left: auto;
        font-size: 12px;
        color: var(--nv-ink-3);
        letter-spacing: .04em;
        white-space: nowrap;
    }

/*  STACKED BY DEFAULT and side by side only when the CARD can hold it. A
    native `type="month"` renders "December 2026" plus its own picker glyph in
    about 200px; two of those plus the arrow need roughly 430px of card. */
/*  ⚠⚠⚠ v31.175 (owner, item 1) — FROM AND TO SHARE A ROW, AND THE NUMBERS
    BEHIND IT WERE MEASURED, NOT ESTIMATED. Twice before this file guessed what
    a native `type="month"` needs — 210, then 184 — and both were wrong. The
    control was finally MEASURED in the running page:

        "September 2026" at 18px Poppins ...... 147px
        `.input` padding-left / -right ........  16 / 36
        ------------------------------------------------
        one control ...........................  199px

    ⚠ THE 36px RIGHT PADDING IS ALREADY THE PICKER GLYPH'S GUTTER — the earlier
    sums added another 30 on top of it and double-counted, which is how a
    control that needs 199 was recorded as needing 229.

    A 500px card (the owner's cap) has 500 − 40 padding − 2 border = **458** of
    content. Two controls + an 18px arrow + two 8px gaps = 2×199 + 34 = 432,
    so the General Ledger card fits with room to spare. A module card in a 2-up
    quadrant at 1270 is 427 wide = 385 of content, and 2×199 + 8 = 406 does NOT
    fit — which is why the padding below is tightened for THESE controls only. */
.op-pair {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 6px;
    align-items: end;
}

    /*  ⚠⚠ SCOPED TO THE PAIR, NEVER TO `.input`. Widening a shared control to
        suit one page is the fault `changing-working-code` names; this touches
        nothing outside `.op-pair`. 26px still hosts the browser's own picker
        glyph (~20px) — VERIFIED by measuring the rendered control, not assumed.
            147 text + 10 + 26 = 183 per control
            2 × 183 + 8  = 374  ≤ 385  (a 427px module card)  ✓
            2 × 183 + 34 = 400  ≤ 458  (a 500px ledger card)  ✓            */
    /*  ⚠⚠⚠ `.op-pair input.input`, NOT `.op-pair .input` — AND THE FIRST
        WRITING'S RIGHT PADDING NEVER APPLIED AT ALL.
        `input.input[type="date"], input.input[type="month"]` (app.css ~5302)
        is specificity **0,2,1**; `.op-pair .input` is 0,2,0 and LOSES. The
        rule sat in the sheet looking correct while every control kept its 36px
        gutter — a silent no-op, found only by measuring a rendered control's
        computed padding.
        ⚠⚠ AND THAT GUTTER IS NOT SPARE ROOM. It hosts the house's own 18px
        gold calendar SVG at `background-position: right 10px center`, so the
        floor is 10 + 18 + a hair = **32**. Anything less puts the glyph on the
        text. 24 — the number the first writing reached for — would have done
        exactly that had it applied.
            "September 2026" 147 + 8 left + 32 right = **187 per control**
            a 2-up module card at 1270 gives 194  →  7px of margin  ✓        */
    .op-pair input.input {
        padding-left: 8px;
        padding-right: 32px;
    }

    /*  the arrow is DECORATION and it is the first thing to go when the card is
        narrow — the `From` and `To` hints already say which is which */
    .op-pair .op-arrow {
        display: none;
        color: var(--nv-ink-3);
        user-select: none;
        width: 18px;
        line-height: 34px;
        text-align: center;
        padding-bottom: 2px;
        transform: none;
    }

/*  ⚠ THE PHONE IS THE ONE RUNG THAT STILL STACKS. A 390px screen gives a card
    310 of content and two controls need 374 — no padding trick closes that, and
    a clipped date is worse than a second row. */
/*  ⚠ 373, NOT 389 — MEASURED AGAIN AFTER THE FIRST WRITING GOT IT WRONG. A
    2-up module card at 1270 is 427 wide, i.e. **385 of content**, and two
    controls need 2×183 + 8 = **374**. A threshold of 389 therefore stacked
    exactly the cards the owner asked to un-stack, while the maths said they
    fit. The rung that genuinely cannot take two is the phone: 350px card =
    310 of content, and no padding trick closes a 64px gap. */
@container opcard (max-width: 373px) {
    .op-pair {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--sp-3);
    }
}

/* ── current period — present, and deliberately quiet ─────────────────────
   The brief: "Also show Current Period, but it should not visually compete
   with the two posting-window ranges." So it is a caption line under the
   heading, never a third field box. */
.op-cur {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: var(--fs-caption);
    color: var(--nv-ink-3);
    margin: 2px 0 var(--sp-4);
}

    .op-cur b {
        font-weight: 400;
        color: var(--nv-ink-2);
    }

/* ── the module cards ────────────────────────────────────────────────────── */
.op-mods {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-4);
    /*  ⚠ EACH CARD TAKES ITS OWN HEIGHT. A grid stretches its items by
        default, so a module with its own window (four controls and a note)
        stretched the one beside it — which followed the ledger and needed a
        third of the room — leaving a card two thirds empty. Ragged bottoms are
        the honest rendering of cards that hold different amounts. */
    align-items: start;
}

/*  ⚠⚠⚠ A QUADRANT CARD IS DENSER THAN THE LEDGER CARD, AND THE 16px IS
    ARITHMETIC. Measured in the running page, "September 2026" — the longest
    value either control can ever render — is **156px** at 18px Poppins, so one
    control needs 156 + 8 + 24 = **188**. A 2-up module card at 1270 is 427
    wide; at the house 20px card padding that is 385 of content and two
    controls plus a 6px gap need 382 — a 3px margin, and the first writing
    MEASURED IT SHORT AT 189 < 192. Trimming this card's own padding to 16
    gives 393 of content and 193 per control: a 5px margin instead of a
    hairline. The General Ledger card keeps the house 20 — at 458 of content it
    has 214 per control and never needed the room. */
.op-mod {
    display: flex;
    flex-direction: column;
    min-width: 0;
    padding: var(--sp-4);
    /*  ⚠⚠⚠ v31.175 (owner, item 4) — A QUADRANT CARD FILLS ITS QUADRANT. The
        owner's 500px cap and his "cards in Row 2 / Row 3 share a right edge"
        collide at 1920, where a quadrant is 539 wide: the cap held the right
        card to 500 and it finished **39px short** of everything else on the
        page. Measured, not noticed.
        ⚠ THE CAP STILL GOVERNS WHERE HE MEANT IT — the General Ledger card,
        which is what item 1 was about, and Closing Items beside it. A module
        card's width is set by the grid that owns it, and the grid is what the
        alignment ruling is about.
        ⚠⚠ IT ALSO BUYS THE ROOM ITEM 1 NEEDS at the wide rungs: 539 of card is
        505 of content and 249 per control against a need of 187.
        ⚠⚠⚠ THE OVERRIDE LIVES BELOW, AT (0,2,0), NOT HERE. `.op-mod` and
        `.op-card` are both (0,1,0) and `.op-card` is declared LATER, so a
        `max-width: none` written here loses to the cap and the card stayed at
        500 — the CSS-ordering trap, paid for a third time. Specificity is the
        fix, because it cannot be undone by moving a block. */
}

/*  see the note in `.op-mod`: (0,2,0) so file order cannot decide this */
.op-mods > .op-card { max-width: none; }

/*  ⚠ v31.174 (owner, item 1) — `max-width: 500px`, THE OWNER'S OWN RULE,
    APPLIED WHERE HE PUT IT. He wrote it on `.op-card`, which caps EVERY card on
    the page and not only the General Ledger one — and that is what makes the
    page "materially less wide and less dense", so it is left exactly as
    written rather than narrowed to `.op-gl`.
    ⚠⚠ IT INTERACTS WITH THE CONTAINER QUERY BELOW: a capped card can no longer
    reach the 500px its own `@container` rule needs for a side-by-side pair, so
    the pairs now stack at every width. That is a consequence of the cap, not a
    separate decision — see the note on `@container opcard`. */
.op-card {
    container-type: inline-size;
    container-name: opcard;
    position: relative;
    max-width: 500px;
}

.op-modhead {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-width: 0;
}

    .op-modhead .op-modname {
        font-size: var(--fs-h2);
        font-weight: 300;
        color: var(--nv-ink);
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

/*  the switch sits at the TOP of the card, per the brief — V1 hides these
    below the fields where they read as an afterthought. Label first, control
    last, the whole row one 44px target. */
.op-swrow {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: 44px;
    margin-top: var(--sp-2);
    padding-top: var(--sp-2);
    border-top: 1px solid rgba(104,152,232,.13);
}

    .op-swrow .op-swlab {
        font-size: var(--fs-caption);
        color: var(--nv-ink-2);
        min-width: 0;
    }

    .op-swrow .acc-sw { margin-left: auto; }

/* ── inherited vs own — THE HEART OF THE REDESIGN ─────────────────────────
   ⚠⚠⚠ WHEN "Use General Ledger periods" IS ON, THE MODULE'S OWN CONTROLS ARE
   NOT RENDERED AS DISABLED BOXES. A greyed field says "this is your value, you
   just cannot reach it", which is the opposite of the truth: the values in
   force belong to the GENERAL LEDGER. So ON is a read-out that STATES what is
   inherited, and the reader never scrolls back up to find out.
   ⚠⚠ BOTH BLOCKS STAY IN THE DOM and only visibility changes — the same
   construction that made the Notes pills lose nothing at v31.155, and the
   reason a half-typed window survives a trip through the switch and back. */
.op-inherit {
    margin-top: var(--sp-3);
    padding: 10px 12px;
    border-radius: var(--r-card);
    border: 1px dashed rgba(104,152,232,.30);
    background: rgba(104,152,232,.06);
}

    .op-inherit .op-inhead {
        font-size: var(--fs-caption);
        color: var(--nv-ink-2);
        margin-bottom: 6px;
    }

.op-inrow {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: var(--fs-caption);
    color: var(--nv-ink-3);
    padding: 2px 0;
}

    .op-inrow .op-inlab { flex: 0 0 62px; }

    .op-inrow .op-inval {
        color: var(--nv-ink);
        font-variant-numeric: tabular-nums;
    }

.op-own { margin-top: var(--sp-3); }

/*  ⚠ ONE CLASS DRIVES BOTH STATES. `.op-independent` means "does NOT follow
    the ledger", so the default (no class) is the inherited read-out. The
    ledger card itself always carries `.op-independent` and has no
    `.op-inherit` block to hide. */
.op-mod .op-own { display: none; }
.op-mod .op-inherit { display: block; }
.op-mod.op-independent .op-own { display: block; }
.op-mod.op-independent .op-inherit { display: none; }

/*  a card that does NOT follow the ledger is marked in its header, so the
    answer to "which modules have their own posting windows?" is available from
    the top of the page without opening anything. */
.op-tag {
    flex: 0 0 auto;
    font-size: 12px;
    line-height: 1;
    padding: 4px 8px;
    border-radius: var(--r-pill);
    border: 1px solid rgba(104,152,232,.32);
    color: var(--nv-ink-3);
    white-space: nowrap;
}

.op-mod.op-independent .op-tag {
    border-color: rgba(232,167,92,.45);
    color: var(--nv-gold);
}

/* ── inline validation, against the offending control ─────────────────────
   The brief is explicit: not a generic popup. The boxes of the offending pair
   carry the state and the message sits under the pair it belongs to, so the
   two cannot drift apart. */
.input.op-bad {
    border-color: rgba(217,101,101,.75);
    background: rgba(217,101,101,.07);
}

.op-err {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: var(--sp-2);
    font-size: var(--fs-caption);
    line-height: 1.45;
    color: #e59a9a;
}

    .op-err .op-errmark { flex: 0 0 auto; }

/*  v31.187 (owner, P-12) — "Changes discarded." Nothing failed and nothing
    succeeded, so it is neither `.op-ok` green nor `.op-err` red: it is the house
    gold caution, the same face `.nt-msg-noaccount` and `.nt-nodocedit` wear. */
.op-drop {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: var(--sp-3, 12px);
    padding: 10px 12px;
    border-radius: var(--r-card);
    border: 1px solid rgba(232,167,92,.45);
    background: rgba(232,167,92,.08);
    color: var(--nv-gold);
    font-size: var(--fs-caption);
    font-weight: 200;
    line-height: 1.45;
}

.op-err.op-err-top {
    padding: 10px 12px;
    border-radius: var(--r-card);
    border: 1px solid rgba(217,101,101,.40);
    background: rgba(217,101,101,.07);
    margin-top: 0;
}

/* ── unsaved changes ──────────────────────────────────────────────────────
   A dot on the card that changed and one bar that counts them by name.
   ⚠ THE BAR IS STICKY, NOT FIXED. A `position: fixed` bar inside a subtree
   whose ancestor carries a filter is positioned against THAT ancestor — the
   fault `_NvEntity` hoists its sheet out of the head to avoid. Sticky has no
   such dependency. */
.op-dot {
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--nv-gold);
    display: none;
}

.op-work.op-dirty .op-card.op-changed .op-dot { display: inline-block; }

.op-bar {
    position: sticky;
    bottom: 8px;
    z-index: 6;
    display: none;
    align-items: center;
    gap: var(--sp-3);
    margin-top: var(--sp-2);
    padding: 10px 14px;
    border-radius: var(--r-card);
    border: 1px solid rgba(232,167,92,.35);
    background: rgba(16,26,48,.96);
    -webkit-backdrop-filter: blur(18px);
    backdrop-filter: blur(18px);
    box-shadow: var(--shadow-card);
    flex-wrap: wrap;
}

.op-work.op-dirty .op-bar { display: flex; }

    .op-bar .op-barcount {
        font-size: var(--fs-caption);
        color: var(--nv-ink-2);
        min-width: 0;
    }

    .op-bar .op-baracts {
        margin-left: auto;
        display: flex;
        gap: var(--sp-2);
        flex: 0 0 auto;
    }

        /* ⚠ the no-padding rule again — see `.op-entpick` above */
        .op-bar .op-baracts .btn {
            width: auto;
            padding: 0 18px;
        }

/* ── saved ───────────────────────────────────────────────────────────────
   Confirms in the entity's own name, because the one mistake this page invites
   is editing the window of the company you did not mean. */
.op-ok {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    border-radius: var(--r-card);
    border: 1px solid rgba(88,179,104,.40);
    background: rgba(88,179,104,.08);
    font-size: var(--fs-caption);
    line-height: 1.45;
    color: var(--nv-ink-2);
}

    .op-ok .op-okmark {
        flex: 0 0 auto;
        color: var(--acc-green);
    }

/* ── THE TWO CONTAINER BREAKPOINTS ───────────────────────────────────────
   Measured, not chosen. A module card needs ~430px before its From/To pair can
   sit side by side without the native control clipping its own value, and two
   such cards plus the grid gap need ~900px of working column. */
/*  ⚠⚠⚠ BOTH NUMBERS ARE DERIVED FROM A MEASUREMENT OF THE SHIPPED SHELL, NOT
    CHOSEN. Measured working-column widths with `_FinPanel` in place:
        390 → 350 · 834 → 728 · 1024 → 624 · 1270 → 870 · 1366 → 950 · 1920 → 1078
    (1366+ only reaches 950 because this page reclaims the empty 390px AI track
    — see the page's own style block.)
    · TWO CARDS need 850, which puts 1270 — the width the owner works at — on
      two columns with ~427px cards.
    · A SIDE-BY-SIDE PAIR needs 500: a native `type="month"` renders
      "December 2026" plus its own picker glyph in about 210px, and a card
      spends 40 on padding, 18 on the arrow and 16 on the gap before the two
      boxes get anything. 430 was the first number here and it was WRONG — it
      would have given 196px boxes at 1366 and clipped the value, the exact
      fault the mock-up's screenshot caught.
    ⚠ Re-derive both if the card padding or the rail width changes. */
@container opwork (min-width: 850px) {
    .op-mods { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/*  ⚠ THE ARROW COMES BACK WHEN THE CARD CAN AFFORD IT — 2×183 + 18 + 16 = 400
    of content, i.e. a card of ~442. The General Ledger card (458) always can;
    a 427px module card cannot, and loses only an ornament. */
@container opcard (min-width: 442px) {
    .op-pair { grid-template-columns: minmax(0, 1fr) 18px minmax(0, 1fr); }
    .op-pair .op-arrow { display: block; }
}

@media (max-width: 560px) {
    .op-entity .op-entpick { margin-left: 0; }

    .op-bar .op-baracts {
        margin-left: 0;
        width: 100%;
    }

        .op-bar .op-baracts .btn {
            flex: 1 1 0;
            min-width: 0;
        }
}


/* ═══════════════════════════════════════════════════════════════════════════
   v31.174 (batch b348) — OPEN PERIODS, THE OWNER'S NINE REVISIONS
   ═══════════════════════════════════════════════════════════════════════════ */

/*  ── 1 & 2 · THE TOP ROW: GENERAL LEDGER | CLOSING ITEMS ──────────────────
    The owner's revised layout reads "General Ledger — max 500px | Closing
    Items". Below 900px of working column the two stack: a 500px card and a
    ten-row checklist cannot share a phone, and the checklist is the one that
    can wait. Container-driven for the same reason everything else here is —
    the ledger rail makes the window a liar about how much room there is. */
.op-top {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-4);
    /*  ⚠ STRETCH, NOT START (v31.175 item 2) — the two cards share a height and
        the taller one (the General Ledger) sets it. See `.op-cl`. */
    align-items: stretch;
}

/*  ⚠⚠⚠ 820, AND IT IS ARITHMETIC RATHER THAN TASTE. The first writing reused
    the 900 that governs the module grid, and at 1270 — the width the owner
    works at — the working column measures 870, so the two cards NEVER paired
    and Closing Items fell below a General Ledger card capped at 500 with a band
    of empty page beside it. The second column has to be worth having: 500 for
    the ledger + 16 of gap + ~300 for a readable checklist = 816. At 1270 that
    leaves Closing Items 354px (one column of ticks); at 1366 it is 434 and at
    1920 it is 562, where the list goes to two columns on its own container
    rule. Below 820 they stack, which is right — a 500px card and a ten-row
    checklist cannot share a phone. */
@container opwork (min-width: 820px) {
    .op-top { grid-template-columns: 500px minmax(0, 1fr); }
}

/*  Closing Items is NOT capped at 500 — it is the card that takes the space
    the General Ledger card gives back. ⚠ This is the one deliberate exception
    to the owner's `.op-card` cap, and without it the top row would leave a
    band of empty page beside two 500px cards. */
.op-closing {
    max-width: none;
    /*  a column, so the list can be the part that flexes and scrolls */
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/*  ⚠⚠⚠ v31.175 (owner, item 2) — THE CARD NEVER GROWS PAST THE LEDGER CARD.
    Owner: *"keep the card height fixed to the GL card and provide vertical
    scrolling inside the list area. The card itself should not become taller
    than the General Ledger card."*

    The mechanism is `flex: 1 1 0` on the list — a ZERO basis, the same idiom
    `.nv-eqrow` uses. With a zero basis the list contributes NOTHING to the
    card's intrinsic height, so the card's natural height is only its heading,
    its sub-line and its note; the General Ledger card is always taller, so IT
    sizes the grid row, and `align-items: stretch` then gives Closing Items
    exactly that height. The list takes whatever is left and scrolls.
    ⚠⚠ `min-height: 0` IS LOAD-BEARING — without it a flex item refuses to
    shrink below its content and the card grows anyway, which is the whole
    thing the owner asked not to happen.
    ⚠ THE ORDER MATTERS TOO: this is why `.op-top` switched from
    `align-items: start` to `stretch`. Start would leave the card its natural
    (short) height and the list would have no room at all. */
.op-cl {
    list-style: none;
    margin: var(--sp-3) 0 0;
    padding: 0 4px 0 0;
    display: grid;
    gap: 2px;
    align-content: start;
}

/*  ⚠⚠⚠ THE ZERO BASIS ONLY APPLIES WHERE THE TWO CARDS SHARE A ROW. Below
    820 of working column they STACK, and there is no General Ledger card
    beside Closing Items to borrow a height from — so `flex: 1 1 0` collapsed
    the list to nothing and left a 162px card holding a scrollbar and no
    checks. Measured, not spotted by eye.
    ⚠ Same threshold as `.op-top`'s own pairing rule, deliberately: the two
    must switch together or the card is sized for a layout it is not in. */
@container opwork (min-width: 820px) {
    .op-cl {
        flex: 1 1 0;
        min-height: 0;
        overflow-y: auto;
    }
}

/*  two columns as soon as the card can hold them — ten rows in one column is a
    scroll on a surface whose whole point is being read at a glance */
@container opcard (min-width: 520px) {
    .op-cl { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--sp-4); }
}

.op-clrow { min-width: 0; }

.op-cllab {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 34px;
    padding: 2px 0;
    font-size: var(--fs-caption);
    color: var(--nv-ink-2);
    cursor: pointer;
    min-width: 0;
}

    /*  ⚠ THE WHOLE ROW IS THE TARGET, not the 16px box — the label wraps the
        input, so the text is part of the hit area for free and no `for`/`id`
        pair can drift out of step. */
    /*  ⚠⚠ v31.176 — THE LABEL WRAPS NOW, AND THAT IS BECAUSE THE TICK BECAME
        REAL. While the list was UI-only an ellipsis was a fair trade for a tidy
        row; now the reader is confirming *"Accruals and prepayments posted"*
        before an accounting period is closed, and **a checklist you act on may
        not be cut off mid-word** — there is no title, no hover on a phone, and
        no second place to read it.
        ⚠ IT COSTS SCROLL, NOT SIZE. The card's height is set by the General
        Ledger card beside it (the zero-basis idiom) and the list scrolls inside
        what is left, so a two-line row lengthens the scroll and moves nothing on
        the page. */
    .op-cllab > span {
        min-width: 0;
        overflow-wrap: anywhere;
    }

/*  ⚠ v31.175 (owner, item 2) — THE BOX ITSELF IS EMAIL'S. `.op-clbox` is now
    on `input.eml-sel`'s own selector list (search this file for `eml-sel`), so
    it inherits the transparent fill, the gold border and the scaling gold tick
    from ONE rule. All that is left here is the row's own geometry — and
    `accent-color` is GONE, because the shared rule sets `appearance: none` and
    an accent colour on an appearance-less control does nothing at all. */
.op-clbox {
    flex: 0 0 auto;
    margin: 0;
}

.op-clnote { margin-top: var(--sp-3); }

/*  ── 7 · CURRENT PERIOD — A PILL, NOT A FIELD ─────────────────────────────
    ⚠⚠⚠ THE POINT IS THAT IT DOES NOT LOOK LIKE THE THINGS AROUND IT. The four
    editable values are `.input`s — a 36px box, an inset fill, a border that
    lightens on focus. This has a padlock, a full-height rounded border, an
    uppercase label and a value in the display size, and no box a caret could
    sit in. The distinction is carried by the SHAPE, so it survives translation
    and cannot be undone by a stylesheet that changes a word. */
/*  ── 8b · CURRENT MONTH — THE PERIOD CONTEXT ──────────────────────────────
    ⚠⚠⚠ v31.177 (owner) — THIS REPLACES THE READ-ONLY PILL AND ITS FOUR SMALL
    COPIES. *"Change Current Period from a read-only value to an
    updateable/selectable field."* The pill said "you cannot touch this" by
    having no box and a padlock; the whole point now is that you CAN, so the
    rules that made it look untouchable are gone rather than left behind for
    markup that no longer exists.

    ⚠⚠ IT IS A ROW, NOT A PILL: the house `.input` treatment beside its own
    label, so it reads as the control it is. It sits above the two pairs and
    ahead of them in the tab order, which is the order the reader works in —
    choose the month, then look at the windows.

    ⚠ THE LABEL IS `<label for>`, so the 44px target is the words as well as the
    box. The control keeps `--fs-h2` because it is the one value on this card
    that decides what everything below it means. */
.op-cur {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    flex-wrap: wrap;
    margin: var(--sp-2) 0 var(--sp-4);
}

    .op-cur .op-curlab {
        flex: 0 0 auto;
        font-size: 12px;
        letter-spacing: .10em;
        text-transform: uppercase;
        color: var(--nv-ink-3);
        white-space: nowrap;
        cursor: pointer;
    }

    /*  ⚠⚠ THE 32px RIGHT GUTTER IS THE HOUSE GLYPH'S, NOT SPARE ROOM — the
        18px gold calendar SVG sits at `right 10px center` on every
        `input[type=month]` in the sheet. ⚠ AND THIS SELECTOR IS (0,2,1) TO
        MATCH IT: `.op-cur .input` alone is (0,2,0) and would lose, silently,
        exactly as `.op-pair .input` did at v31.175. */
    .op-cur input.op-curinput {
        flex: 0 1 auto;
        width: auto;
        min-width: 0;
        max-width: 100%;
        font-size: var(--fs-h2);
        font-weight: 300;
        color: var(--nv-gold);
        font-variant-numeric: tabular-nums;
        padding-left: 12px;
        padding-right: 34px;
    }

@media (max-width: 560px) {
    .op-cur input.op-curinput { font-size: var(--fs-body); }
}

/*  ── 9 · THE QUADRANT TABS ────────────────────────────────────────────────
    ⚠ THE BODY IS HIDDEN, NEVER REMOVED — every input keeps posting, so a
    collapse cannot lose a half-typed window, and a card carrying a refusal is
    rendered open by the server (`StartsCollapsed`). */
.op-tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
    min-width: 0;
    text-align: left;
}

    .op-tab .op-chev {
        flex: 0 0 auto;
        display: inline-flex;
        color: var(--nv-ink-3);
        transform: rotate(90deg);
        transition: transform .15s ease;
    }

    .op-tab:hover .op-chev { color: var(--nv-gold); }

.op-collapsed .op-tab .op-chev { transform: rotate(0deg); }

.op-collapsed .op-body,
.op-collapsed .op-sub { display: none; }

/*  a collapsed quadrant is a TAB, so it takes a tab's room and no more */
.op-collapsed {
    padding-top: var(--sp-2);
    padding-bottom: var(--sp-2);
}

/*  ⚠ A COLLAPSED CARD STILL SHOWS ITS TWO MARKS — the change dot and the
    follows/own tag. Folding a card away must not fold away the answer to
    "which modules have their own posting windows?", which is the question the
    page exists to answer. */
.op-collapsed .op-modhead { min-height: 44px; }

/*  ── 8 · THE BAR IS PERMANENT ─────────────────────────────────────────────
    ⚠⚠⚠ THE `display` GATE IS GONE. It used to be `display: none` until
    `.op-dirty`, which meant the bar APPEARED on the first keystroke and pushed
    every card below it — the reader's next click landing on something that had
    just moved. The bar is now always laid out; only its wording and its two
    buttons change. **Nothing below it may move when a field is amended.** */
.op-bar { display: flex; }
.op-work.op-dirty .op-bar { display: flex; }

/*  the accent still says whether anything is pending — the box does not move,
    the colour does */
.op-bar {
    border-color: rgba(104,152,232,.28);
    background: rgba(16,26,48,.94);
}

.op-work.op-dirty .op-bar { border-color: rgba(232,167,92,.45); }

.op-bar .btn:disabled {
    opacity: .45;
    cursor: default;
}

/*  ── 3 · THE PAGE'S OWN ENTITY CARD IS GONE ───────────────────────────────
    `.op-entcard`, `.op-entity`, `.op-entname`, `.op-entcode` and `.op-entpick`
    are no longer rendered by any page. The rules are left in place rather than
    deleted: they are three short blocks, they cost nothing, and a stylesheet
    that loses a class the day a page stops using it is a stylesheet nobody can
    revert against. ⚠ If a later round wants the strip back it is already
    styled — but the RULING is that the universal switch is the only entity
    control, so bringing it back needs the owner's word, not a merge. */

/*  ── ITEM 5 (v31.175, owner) · THE HEAD BACKGROUND IS REMOVED ────────────
    Owner supplied the rule commented out: *"The header should therefore sit
    directly on the normal page background."*

    ⚠ THIS REVERSES P-06, KNOWINGLY. P-06 was raised because a transparent head
    over a long column of digits reads as bleed-through, and the owner agreed to
    the opaque treatment at v31.174 — then saw it and asked for it off. Seeing
    it beats predicting it, and the layout it now sits over is not the one that
    raised the question: after items 1, 2 and 4 the page is shorter and the top
    of it is a heading and a pill rather than a row of date boxes.
    ⚠⚠ THE RULE IS DELETED RATHER THAN COMMENTED OUT. A commented-out rule in a
    515KB sheet is a rule the next reader has to decide about; the reason it
    went is here, and the shell class `op-app` stays on the page so putting it
    back is one block. `responsive.css:187` (Wave 1.1) is untouched and is what
    now governs this head, exactly as it governs every other. */


/*  ⚠⚠⚠ v31.231 (owner, P-2) — THE COLLAPSED ADD LINE CARD.
    Owner: *"On initial presentation in Copy mode, show only an: Add Line button
    within the collapsed card."*

    ⚠⚠ ONE RULE, AND IT HIDES SIBLINGS RATHER THAN WRAPPING THEM. The alternative
    was a new container around the whole add-line form, which would have moved
    two hundred lines of controls into a different parent to achieve a visual
    change — the shape this project's own rule warns about (*SIZE ONLY, NOT MOVED
    CONTENT*). Here not one existing element moves; the card simply stops
    painting the children it already had.
    ⚠⚠⚠ `display: none` DOES NOT UNSUBMIT A FIELD — only `disabled` does — so the
    hidden controls still post exactly as they always did the moment the card is
    expanded. That is why this is a stylesheet rule and not a `disabled` sweep.
    ⚠ `.al-expand` IS THE ONE CHILD THAT SURVIVES: the button that undoes the
    collapse. Without the exception the rule would hide its own escape hatch.
    ⚠ The attribute is REMOVED on expand rather than the rule overridden, so an
    expanded card is in exactly the state it would have been in without this
    round — nothing to unwind and nothing left specific to fight later.  */
form[data-alcollapse="1"] > *:not(.al-expand) {
    display: none !important;
}

/*  ⚠⚠⚠ v31.236 (owner, P-9) — THE ENTITY ADDRESS CARD ON THE SWITCH SHEET.

    Owner: *"Add the Entity's address/details presentation to the Universal
    Entity Switch pop-up/card in this round."*

    ⚠⚠ IT IS THE HOUSE `.card` WITH THREE BLOCKS, not a new panel grammar. The
    sheet already stacks cards; a bespoke frame here would be a second way of
    saying the same thing on a control that appears on fourteen surfaces.
    ⚠ THE NAME CARRIES THE WEIGHT and the rest is quiet — this is confirmation
    of where the reader is, read at a glance, not a record to study. v1 bolds
    the account name for the same reason (`<b>` around index 0).
    ⚠ 12.5px IS THE SHEET'S OWN SECONDARY SIZE, not a new step on the ladder. */
/*  ⚠⚠⚠ v31.237 (owner, P-9 rework) — THE ROW IS A PAIR: SWITCH, AND INSPECT.
    The submit button keeps the whole row (it is the primary action and should
    stay a large target); the `i` sits outside it because a button cannot be
    nested in a button, and because it must NOT submit.
    ⚠ `min-width: 0` IS LOAD-BEARING: without it the flex item refuses to shrink
    below its content and `.ent-sheet .fld .fl`'s ellipsis never fires, so a long
    company name pushes the `i` off the sheet instead of truncating. */
.ent-row {
    display: flex;
    align-items: center;
    min-width: 0;
}

    .ent-row > .fld {
        flex: 1 1 auto;
        min-width: 0;
        width: auto !important;
    }

    .ent-row > .ent-info {
        flex: 0 0 auto;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 30px;
        height: 30px;
        margin-right: 2px;
        padding: 0;
        border: 0;
        border-radius: 50%;
        background: transparent;
        color: var(--nv-muted, #8892a0);
        cursor: pointer;
        opacity: .42;
        transition: opacity .15s, color .15s, background .15s;
    }

    /*  ⚠ IT COMES UP ON THE ROW, NOT ONLY ON ITSELF — a control that only
        appears once the pointer is already on it is a control nobody finds.
        Touch has no hover at all, which is why it is never fully invisible. */
    .ent-row:hover > .ent-info,
    .ent-row > .ent-info:hover,
    .ent-row > .ent-info:focus-visible {
        opacity: 1;
        color: var(--nv-gold);
        background: rgba(104, 152, 232, .12);
    }

    /*  v31.458 (owner: *"when the icon next to the entity name is clicked, disable on hover
        until the entity pill is clicked again"*) — THE PINNED `i` STAYS LIT. Pressing it stops
        the pane following the pointer, and a page that behaves differently with nothing on
        screen saying so reads as a hover that broke. The mark is on the one button that did
        it, it outranks the hover rules above (two classes to their one), and it survives the
        pointer moving away — which is the entire point of a pin. */
    .ent-row > .ent-info.on,
    .ent-row:hover > .ent-info.on {
        opacity: 1;
        color: var(--nv-gold);
        background: rgba(201, 162, 77, .18);
        box-shadow: inset 0 0 0 1px var(--nv-gold);
    }

/*  ══ v31.237 — THE ADDRESS PANE IS PINNED TO THE FOOT OF THE SHEET ══════════
    ⚠⚠⚠ STICKY TO THE BOTTOM, AND THE REASON IS THE POINTER. The pane repaints as
    the reader moves down the tree, and its height changes with the address. Above
    the tree that reflows every row BELOW it — so the row under the pointer becomes
    a different row, which repaints the pane, which moves the rows again. At the
    foot there is nothing after it to move: a taller address grows the sheet
    downwards, away from the list it is describing.
    ⚠⚠⚠ v31.240 (owner, P-9B) — *"and fit into card."* THE CAP WAS CLIPPING REAL
    DATA, not just pathological data. `46vh` is 353px at 1366x768 and 388px at
    390x844; a full card at 15px is **461px on the desktop and 505px on the
    phone**, where the IBAN and the account name each take two lines. So every
    complete entity was getting a scrollbar INSIDE the card — the reader saw a
    box of financial detail that had to be scrolled to be read, which is not a
    card that fits.
    ⚠⚠ THE CAP IS RAISED, NOT REMOVED, and the reason it exists is unchanged: this
    pane is STICKY at the foot of the sheet, so without any cap a pathological
    address could occupy the whole sheet and leave the tree it belongs to with two
    rows showing. 72vh clears every measured case with room to spare (553px at
    768, 607px at 844) while still bounding the bad one.
    ⚠ THE SHEET IS THE SCROLLER. The card's own overflow now only ever engages in
    that bounded case; ordinarily the sheet scrolls and the card is whole.

    ⚠⚠⚠ v31.238 (owner, P-9A) — THE PRESENTATION IS HIS, VERBATIM. Owner:
    *"Please update the address card styling to the following exactly."* So the
    full-bleed treatment this rule shipped with is gone and the pane is a CARD
    again: `margin: 10px` on all four sides, an 8px radius, and `.card`'s own
    border tinted `--nv-gold-dim` — the negative margins, the `border: 0`, the
    `border-top` hairline and the gradient are removed rather than overridden,
    because a declaration that is only there to be beaten is a declaration that
    will beat something else later.
    ⚠⚠ THE POSITION AND THE SCROLLING ARE UNCHANGED, AND THAT IS THE POINT: he
    kept `position: sticky` / `bottom: -24px` / `max-height` / `overflow-y` in
    the block he sent, so the reflow rule above still holds. `bottom: -24px`
    still cancels the sheet's 24px foot even though the side margins no longer
    cancel its padding — the pane simply insets from the frame instead of
    spanning it. */
.ent-sheet .ent-addr {
    position: sticky;
    bottom: -24px;
    z-index: 2;
    margin: 10px;
    padding: 11px 20px 14px;
    border-radius: 8px;
    border-color: var(--nv-gold-dim);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    max-height: 72vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

    /*  ⚠ v31.238 (owner, P-9A) — 400, NOT 600. His block, verbatim. */
    .ent-sheet .ent-addr .ent-addr-name {
        font-weight: 400;
        color: var(--nv-gold);
        overflow-wrap: anywhere;
    }

    /*  ⚠⚠ `anywhere`, NOT `nowrap` WITH AN ELLIPSIS. An address line that is cut
        off is not an address — and unlike a list row, this card has no second
        place to show the full value. A long street wraps. */
    /*  ⚠ v31.238 (owner, P-9A) — 18px, NOT 12.5. His block, verbatim. */
    .ent-sheet .ent-addr .ent-addr-lines,
    .ent-sheet .ent-addr .ent-addr-contact {
        font-size: 18px;
        line-height: 1.5;
        overflow-wrap: anywhere;
    }

    .ent-sheet .ent-addr .ent-addr-contact {
        color: var(--nv-muted, #8892a0);
    }

        /*  ⚠ THE CONTACT BLOCK IS SEPARATED BY A RULE rather than by a gap: on a
            card this narrow, whitespace alone reads as an accident. */
        .ent-sheet .ent-addr .ent-addr-contact:not(:empty) {
            margin-top: 2px;
            padding-top: 8px;
            border-top: 1px solid var(--nv-line, rgba(255, 255, 255, .08));
        }

    /*  ══ v31.239 (owner, P-9B) — THE FINANCIAL BLOCK, BELOW A HAIRLINE ══════
        Owner: *"Draw a very thin, subtle separator line, consistent with the
        existing NOVERA card styling … The styling should remain compact and
        understated. The separator should be very thin and low-contrast, acting
        only as a visual division."*

        ⚠⚠⚠ `:not(:empty)` DRAWS THE RULE, NOT THE ELEMENT EXISTING. The block is
        rendered on every card and filled only when there is something to fill it
        with, so an entity with no registration and no bank row would otherwise
        carry a hairline under nothing — which is the empty-frame fault the card
        already refuses at the whole-panel level, in miniature.
        ⚠⚠ IT IS FAINTER AND TIGHTER THAN THE CONTACT RULE ABOVE IT, deliberately.
        Two identical rules read as two equal divisions; this one is a footnote to
        the card, not a second half of it — .05 against the contact block's .08,
        and 13px against the address block's 18px.
        ⚠ THE LABEL IS THE QUIET HALF. The number is what is being read; the word
        in front of it only says which number it is. */
    /*  ⚠ v31.240 (owner, P-9B) — 15px, HIS VALUE, SENT AS A RULE. It shipped at
        13 and read as a footnote rather than as information; the block still
        sits below the address's 18px, so the hierarchy the separator draws is
        unchanged. */
    .ent-sheet .ent-addr .ent-addr-fin {
        font-size: 15px;
        line-height: 1.45;
        overflow-wrap: anywhere;
        color: var(--nv-ink-2, #aab3c8);
    }

        /*  ⚠⚠ A WRAPPED ROW STILL READS AS ONE ROW. At 15px on a 390px screen an
            IBAN and a long account name each take two lines, and without this the
            second line starts under the LABEL — which reads as a new, unlabelled
            row. The hanging indent puts every continuation under the value it
            belongs to. */
        .ent-sheet .ent-addr .ent-addr-fin > div {
            padding-left: 14px;
            text-indent: -14px;
        }

        .ent-sheet .ent-addr .ent-addr-fin:not(:empty) {
            margin-top: 4px;
            padding-top: 7px;
            border-top: 1px solid rgba(255, 255, 255, .05);
        }

        .ent-sheet .ent-addr .ent-addr-fin .fk {
            color: var(--nv-ink-3, #6b7690);
        }

/* ══════════════════════════════════════════════════════════════════════════
   v31.242 (owner, P-10) — THE ADDRESSED-ACCOUNT SEARCH SHEET

   ⚠⚠⚠ IT BORROWS `.mail-sheet`'s FRAME AND OVERRIDES ITS GEOMETRY, exactly as
   `.ent-sheet` does. That is deliberate: three sheets that look like three
   different apps is the outcome of three private frames. What differs here is
   WIDTH — a result grid of six columns needs it — and that this one is anchored
   near the top rather than centred, because it is tall.
   ⚠⚠ AND IT HIDES WITH `opacity + visibility`, NOT `translateY(102%)`. The base
   `.mail-sheet` slides itself off the bottom; the entity sheet records why that
   cannot work once `bottom` is raised, and the same applies here.
   ══════════════════════════════════════════════════════════════════════════ */
/*  ⚠⚠⚠ `.mail-sheet.acs-sheet`, NOT `.acs-sheet` — AND MEASURED, NOT ASSUMED.
    The base `.mail-sheet` caps at `max-width: 480px` and `responsive.css` sets
    `margin: N auto !important` at three separate breakpoints. Written as a
    single class this rule is (0,1,0) against (0,1,0) in a file that loads
    LATER, so it lost: the first render of this sheet came out 480px wide with
    the results grid clipped and the type chips wrapping mid-word. Two classes
    is (0,2,0) and wins on specificity whatever the file order — the same fight
    `.ent-sheet.ent-anchored` records a few hundred lines above.
    ⚠⚠ AND `margin` NEEDS `!important` TO ANSWER AN `!important`. Nothing else
    here does; specificity carries the rest. */
.mail-sheet.acs-sheet {
    position: fixed;
    top: 4vh;
    bottom: auto;
    left: 50%;
    right: auto;
    width: min(1020px, calc(100vw - 32px));
    max-width: none;
    max-height: 90vh;
    overflow-y: auto;
    margin: 0 !important;
    z-index: 41;
    border-radius: 8px;
    border: 1px solid rgba(104, 152, 232, .30);
    padding: 0 20px 22px;
    background: linear-gradient(145deg, #010107 0%, #1E1E1E 50%, #13294B 100%);
    font-family: 'Poppins', -apple-system, 'Segoe UI', sans-serif !important;
    transform: translate(-50%, 12px);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s, transform .24s cubic-bezier(.22,.61,.36,1), visibility 0s .24s;
}

    .mail-sheet.acs-sheet.open {
        transform: translate(-50%, 0);
        opacity: 1;
        visibility: visible;
        transition: opacity .2s, transform .24s cubic-bezier(.22,.61,.36,1), visibility 0s;
    }

    /*  ⚠ `[hidden]` MUST WIN over the display the frame sets — the `.rt-emoji`
        cascade lesson this file has paid for twice. */
    .mail-sheet.acs-sheet[hidden], .mail-sheet.acs-card[hidden] {
        display: none !important;
    }

.mail-scrim.acs-scrim { z-index: 40; }
.mail-scrim.acs-cardscrim { z-index: 42; }

/*  the card rides ABOVE the results it was opened from, and is narrower. */
.mail-sheet.acs-card {
    position: fixed;
    top: 50%;
    left: 50%;
    right: auto;
    bottom: auto;
    transform: translate(-50%, -50%);
    width: min(560px, calc(100vw - 32px));
    max-width: none;
    max-height: 86vh;
    overflow-y: auto;
    margin: 0 !important;
    z-index: 43;
    border-radius: 8px;
    border: 1px solid rgba(104, 152, 232, .30);
    padding: 0 20px 22px;
    background: linear-gradient(145deg, #010107 0%, #1E1E1E 50%, #13294B 100%);
    opacity: 1;
    visibility: visible;
}

/*  the sticky head — the entity sheet's shape, with a louder close. */
.acs-head {
    position: sticky;
    top: 0;
    z-index: 3;
    margin: 0 -20px 12px;
    padding: 14px 60px 11px 20px;
    background: linear-gradient(180deg, rgba(4,6,12,.96), rgba(9,13,24,.88));
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid rgba(104, 152, 232, .16);
}

    .acs-head h2 {
        margin: 0;
        font-size: 23px;
        font-weight: 600;
    }

    .acs-head .acs-sub {
        font-size: 12.5px;
        color: var(--nv-ink-3);
        margin-top: 2px;
    }

/*  ⚠⚠ A BORDERED TARGET, NOT A BARE GLYPH. Owner: *"The sheet requires a
    clearly visible Close (×) button."* This sheet opens over work in progress,
    so the way out is a control rather than a hint. */
.acs-close {
    position: absolute;
    top: 12px;
    right: 16px;
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(232, 167, 92, .38);
    border-radius: 50%;
    background: rgba(232, 167, 92, .10);
    color: var(--nv-gold);
    cursor: pointer;
    font-family: inherit;
    font-size: 15px;
}

    .acs-close:hover, .acs-close:focus-visible {
        background: rgba(232, 167, 92, .22);
        color: var(--nv-gold-bright);
    }

.acs-tools {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

    .acs-tools .acs-wrap {
        flex: 1 1 300px;
    }

    /*  ⚠ `tokens.css` GIVES `.chips > .chip` AN EQUAL SHARE OF THE ROW, which is
        right for a four-chip status bar and wrong for seven type filters — it
        broke "Bank accounts" across two lines. Here each chip is its own width
        and the row wraps as a row. */
    .acs-chips > .chip {
        flex: 0 0 auto;
    }

.acs-pre, .acs-state {
    margin: 0 0 10px;
    font-size: 12.5px;
    color: var(--nv-ink-3);
}

.acs-pre {
    border-left: 2px solid rgba(232, 167, 92, .35);
    padding-left: 10px;
}

/*  ══ the catalog groups (Q1) ═══════════════════════════════════════════════
    ⚠⚠⚠ THE HEADING IS GOLD-RULED AND THE ROWS SIT UNDER IT. Owner: *"Each
    Catalog should have a clear section heading, with its matching accounts
    immediately beneath it."* The rule is what makes two identical account codes
    in two catalogs read as two different accounts rather than a duplicate. */
.acs-group { margin-bottom: 16px; }

.acs-cat {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 6px;
    padding: 5px 0 6px;
    border-bottom: 1px solid rgba(232, 167, 92, .28);
}

    .acs-cat .db {
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 13px;
        letter-spacing: .06em;
        color: var(--nv-gold);
    }

    .acs-cat .cnt {
        font-size: 12px;
        color: var(--nv-ink-3);
        margin-left: auto;
    }

/*  ⚠ THE GRID SCROLLS IN ITS OWN BOX. Six columns do not fit a phone, and a
    sheet that scrolls sideways takes its own close button off screen. */
.acs-scroll { overflow-x: auto; }

.acs-res {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
    min-width: 760px;
}

    .acs-res thead th {
        text-align: left;
        font-weight: 400;
        font-size: 11px;
        letter-spacing: .08em;
        text-transform: uppercase;
        color: var(--nv-ink-3);
        padding: 0 10px 6px;
        white-space: nowrap;
    }

    .acs-res tbody tr {
        border-bottom: 1px solid rgba(104, 152, 232, .10);
        cursor: pointer;
    }

        .acs-res tbody tr:hover,
        .acs-res tbody tr:focus-visible {
            background: rgba(120, 165, 240, .07);
        }

    .acs-res td {
        padding: 9px 10px;
        vertical-align: top;
    }

        .acs-res td.num {
            font-variant-numeric: tabular-nums;
            white-space: nowrap;
        }

        .acs-res td.acs-act {
            width: 44px;
            text-align: right;
        }

.acs-name { color: var(--nv-ink); }
.acs-code, .acs-sub2 { font-size: 12.5px; color: var(--nv-ink-3); }
.acs-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.acs-miss { color: var(--nv-ink-3); opacity: .55; }
.acs-empty { padding: 26px 12px; color: var(--nv-ink-3); text-align: center; }
.acs-loading { padding: 22px 12px; color: var(--nv-ink-3); text-align: center; }

/*  ⚠⚠ THE ⓘ IS QUIET UNTIL THE ROW IS UNDER THE POINTER, and never invisible —
    touch has no hover, which is the same rule the entity row's inspect control
    follows. */
.acs-info {
    width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--nv-ink-3);
    cursor: pointer;
    opacity: .45;
    transition: opacity .15s, color .15s, background .15s;
}

.acs-res tbody tr:hover .acs-info,
.acs-info:hover,
.acs-info:focus-visible {
    opacity: 1;
    color: var(--nv-gold);
    background: rgba(104, 152, 232, .14);
}

/*  the card's blocks — the entity card's vocabulary, one level up in size
    because this sheet is wider and is read rather than glanced at. */
.acs-blk {
    padding: 11px 0;
    border-top: 1px solid rgba(104, 152, 232, .14);
}

    .acs-blk:first-child { border-top: 0; }

.acs-blkh {
    font-size: 11.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--nv-gold-dim);
    margin-bottom: 7px;
}

.acs-addrname {
    color: var(--nv-gold);
    font-size: 18px;
    overflow-wrap: anywhere;
}

.acs-addrlines {
    font-size: 18px;
    line-height: 1.5;
    overflow-wrap: anywhere;
}

.acs-blk > div:not(.acs-blkh):not(.acs-addrname):not(.acs-addrlines):not(.acs-more) {
    font-size: 15px;
    line-height: 1.45;
    overflow-wrap: anywhere;
    color: var(--nv-ink-2);
    padding-left: 14px;
    text-indent: -14px;
}

.acs-blk .fk { color: var(--nv-ink-3); }

.acs-more {
    margin-top: 9px;
    padding-top: 9px;
    border-top: 1px dashed rgba(104, 152, 232, .20);
    font-size: 15px;
    line-height: 1.45;
}

@media (max-width: 767.98px) {
    /*  01-Screen: the sheet is the screen. A 90vh box inset 16px reads as a
        dialog on a phone rather than as a page that lost its navigation. */
    .mail-sheet.acs-sheet {
        top: 2vh;
        width: calc(100vw - 16px);
        max-height: 96vh;
        padding: 0 14px 18px;
    }

        .mail-sheet.acs-sheet .acs-head {
            margin: 0 -14px 10px;
            padding: 12px 56px 10px 14px;
        }
}

/*  ══ v31.273 (owner) — THE EDIT DOOR'S SHORT MESSAGE ═══════════════════════
    Owner: *"Please place a short message when toggling the button, i.e.
    Clear → 'Transaction allocated. Clear allocations before amending.'"*

    ⚠⚠ IT SITS UNDER THE BUTTON ROW, FULL WIDTH, and is inserted by
    nv-edittrn.js rather than rendered by any page — so it lands the same way on
    the invoice header, the Review chips, the money pages and the GL journal.
    ⚠ AMBER, NOT RED. Nothing has gone wrong: the transaction is fine and the
    reader has a next step. Red is for refusals they cannot act on. */
.fin-editwhy {
    margin-top: 8px;
    padding: 7px 12px;
    border-radius: var(--r-input, 6px);
    border: 1px solid rgba(232, 167, 92, .38);
    background: rgba(232, 167, 92, .09);
    color: var(--nv-gold-bright);
    font-size: 13px;
    line-height: 1.45;
}

/* ═══ v31.282 (owner, P-28) — THE NOTIFICATION PLATFORM ══════════════════════
   The bell and its badge, the dropdown, the Centre's rows, the settings
   switches, the device list and the permission prompt.

   ⚠⚠ EVERY COLOUR HERE IS A TOKEN FROM tokens.css. Not one literal: this block
   is appended to a 576KB stylesheet whose whole discipline is that the palette
   lives in one place, and a hex value here would be the first exception.
   ⚠ AND THE TYPE SIZES SIT ON THE EXISTING LADDER rather than inventing a step.
   The shallow 4-step ladder is still an open backlog item (DESIGN 1); nothing
   in this block may pre-empt it. ═══════════════════════════════════════════ */

/* ── the bell's badge ─────────────────────────────────────────────────────── */
.nv-bellcount {
    position: absolute;
    top: 2px;
    right: 10px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background: var(--nv-gold);
    color: var(--nv-bg-deep);
    font-size: 11px;
    font-weight: 600;
    line-height: 18px;
    text-align: center;
    font-variant-numeric: tabular-nums;
    pointer-events: none;
}

/* ── the dropdown ─────────────────────────────────────────────────────────── */
/*  ══════════════════════════════════════════════════════════════════════════
    ⚠⚠⚠ v31.288 (owner): *"the notifications popup needs proper styling — same
    pattern as other tooltip."*

    THE PANEL WAS THE ONE FLOATING SURFACE IN THE APPLICATION THAT WAS NOT THE
    HOUSE ONE. It carried `background: var(--nv-surface)` — the flat blue a CARD
    uses, which is correct for something sitting IN the page and wrong for
    something floating OVER it: a card's surface belongs to the column it is
    part of, and a panel that borrows it reads as a rectangle dropped on the
    screen rather than as a layer above it. That is the whole of what the
    owner's screenshot shows.

    ⚠⚠ SO IT TAKES `.card.evtip`'s GRAMMAR, WHICH IS THE TOOLKIT TOOLTIP
    SURFACE: the opaque 145° gradient, the `rgba(104,152,232,.30)` edge, the
    8px radius and the deep shadow. That block's own comment records why the
    gradient is OPAQUE — *"the toolbar has a transparent background making the
    items unreadable"* — and this panel floats over exactly the same live
    content, so it inherits the reason along with the look.

    ⚠ IT DOES NOT INHERIT `pointer-events: none`. A tooltip never eats the click
    it describes; this panel IS the thing being clicked. Same surface, different
    object — which is why the declarations are restated here rather than the
    class being reused.
    ══════════════════════════════════════════════════════════════════════════ */
.nv-ntfpanel {
    position: fixed;
    z-index: 1200;
    width: 390px;
    max-width: calc(100vw - 16px);
    border: 1px solid rgba(104, 152, 232, .30);
    border-radius: 8px;
    overflow: hidden;
    background: linear-gradient(145deg, #010107 0%, #1E1E1E 82%, #13294B 175%);
    box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
}

.nv-ntfhead, .nv-ntffoot {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 14px;
    font-size: 14px;
    color: var(--nv-ink-2);
}

/*  ⚠⚠ THE RULES ARE THE PANEL'S OWN, NOT `--line`. `--line` is the divider a
    card draws between its rows, and against this darker surface it disappeared
    — the head and foot read as floating text rather than as bands. The edge
    colour is the panel's own border at a third of its weight, so the three
    lines belong to one object. */
.nv-ntfhead { border-bottom: 1px solid rgba(104, 152, 232, .16); }
.nv-ntffoot { border-top: 1px solid rgba(104, 152, 232, .16); }
.nv-ntfhead .spacer, .nv-ntffoot .spacer { flex: 1 1 auto; }

/*  ⚠ THE TITLE IS THE HOUSE HEAD, not body copy in a slightly lighter ink. It
    is the only label in the panel and it was the same size and weight as the
    two links under it. */
.nv-ntfhead .t {
    color: var(--nv-ink);
    font-size: 15px;
    font-weight: 300;
    letter-spacing: .2px;
}

/*  the settings door — an icon button, sized and coloured like every other
    icon in the application rather than a blue character */
.nv-ntfhead .gear {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 6px;
    color: var(--nv-ink-3);
    text-decoration: none;
}

    .nv-ntfhead .gear:hover {
        color: var(--nv-gold-bright, #f2bc74);
        background: rgba(232, 167, 92, .10);
    }

/*  ⚠⚠⚠ v31.521 — THE PER-ROW DELETE AND THE ARMED "DELETE ALL".

    The ✕ is held at the row's top-right and only becomes visible on hover or keyboard
    focus, so a list of notifications does not read as a list of delete buttons — but it is
    ALWAYS in the tab order and ALWAYS shown on a touch screen, where there is no hover to
    reveal it. A control that only exists for a mouse is the fault the brand round just
    finished removing from the information icon. */
/*  v31.521 — the select bar and the per-row tick on /notifications */
.nv-ntfbar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.nv-ntfbar .nv-actcol { margin-left: auto; }
.nv-ntfall { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13.5px; }
.nv-ntfall input { width: 16px; height: 16px; accent-color: var(--nv-gold); cursor: pointer; }
.nv-ntfpick { flex: 0 0 auto; display: inline-flex; align-items: center; margin-right: 2px; }
.nv-ntfpick input { width: 16px; height: 16px; accent-color: var(--nv-gold); cursor: pointer; }
@media (max-width: 767.98px) {
    /*  HR-1 and HR-2: the action takes the full width rather than squeezing beside the
        count, which is what pushed this card into a sideways scroll at 390. */
    .nv-ntfbar .nv-actcol { margin-left: 0; width: 100%; grid-template-columns: 1fr !important; }
    .nv-ntfbar .s.muted { width: 100%; }
}

.nv-ntfrow { position: relative; }
.nv-ntfrow > .x {
    position: absolute; top: 6px; right: 6px; width: 22px; height: 22px; padding: 0;
    border-radius: 50%; border: 1px solid rgba(104,152,232,.22); background: var(--nv-surface);
    color: var(--nv-ink-3); font-size: 12px; line-height: 1; cursor: pointer; opacity: 0;
}
.nv-ntfrow:hover > .x, .nv-ntfrow > .x:focus-visible { opacity: 1; }
.nv-ntfrow > .x:hover { color: var(--nv-danger); border-color: rgba(217,101,101,.6); }
@media (hover: none) { .nv-ntfrow > .x { opacity: 1; } }

/*  ⚠⚠ THE DESTRUCTIVE FOOT ACTION LOOKS DIFFERENT FROM THE ONE BESIDE IT, and more so once
    it is armed. It sits a centimetre from "Mark all read" and is the only irreversible
    control in the panel; two links in the same ink is how a mis-tap happens. */
.nv-ntffoot .lnk.danger { color: var(--nv-ink-3); }
.nv-ntffoot .lnk.danger:hover { color: var(--nv-danger); }
.nv-ntffoot .lnk.danger.armed {
    color: var(--nv-danger); border: 1px solid rgba(217,101,101,.6);
    border-radius: 999px; padding: 3px 10px;
}

/*  ⚠⚠ THE FOOT ACTIONS ARE GOLD, NOT BROWSER BLUE. Every other secondary
    action in this application is gold on hover and muted ink at rest; the raw
    `--nv-blue-bright` here was the only place two links looked like an
    unstyled anchor pair. */
.nv-ntffoot .lnk {
    background: none;
    border: 0;
    padding: 4px 2px;
    font: inherit;
    font-size: 13.5px;
    color: var(--nv-ink-3);
    cursor: pointer;
    text-decoration: none;
}

    .nv-ntffoot .lnk:hover {
        color: var(--nv-gold-bright, #f2bc74);
    }

.nv-ntflist { max-height: 60vh; overflow-y: auto; }

/* ── one row, shared by the dropdown and the Centre ───────────────────────── */
.nv-ntfrow {
    display: flex;
    gap: 11px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--line);
    min-width: 0;
}

.nv-ntfrow:last-child { border-bottom: 0; }

    .nv-ntfrow > .dot {
        flex: 0 0 auto;
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background: var(--nv-gold);
        margin-top: 7px;
    }

.nv-ntfrow.read > .dot { background: transparent; border: 1px solid var(--line); }
.nv-ntfrow .body { min-width: 0; flex: 1 1 auto; }

/*  ⚠⚠⚠ v31.459 (owner's screenshot: *"the ✕ is not properly formatted"*) — THE REMOVE
    BUTTON. v31.458 added the control and NO RULE FOR IT, so it fell back to the browser's
    own button face: a grey slab with a border, mid-row, over the kind label.

    > ⚠⚠ THE HOUSE `.btn` DECLARES `border: 0` AND NO BACKGROUND, so a bare `<button>` in
    > this application does not inherit a quiet face — it inherits the BROWSER's. A new
    > control gets a rule or it gets whatever the user agent brings. This is the same
    > lesson the notification ACTION buttons carry four lines above, about `ghost`.

    It takes the entity sheet's `i` as its model: round, muted, raised on hover, and never
    fully invisible because a touch reader has no hover to find it with. ⚠ It turns RED on
    hover rather than gold — every other control on this row leads somewhere, and this one
    destroys something. */
.nv-ntfrow > .nv-ntfdel {
    flex: 0 0 auto;
    align-self: flex-start;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    margin-top: -2px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--nv-muted, #8892a0);
    cursor: pointer;
    opacity: .38;
    transition: opacity .15s, color .15s, background .15s;
}

.nv-ntfrow:hover > .nv-ntfdel,
.nv-ntfrow > .nv-ntfdel:hover,
.nv-ntfrow > .nv-ntfdel:focus-visible {
    opacity: 1;
}

.nv-ntfrow > .nv-ntfdel:hover,
.nv-ntfrow > .nv-ntfdel:focus-visible {
    color: var(--nv-red, #e0756b);
    background: rgba(224, 117, 107, .14);
}

/*  v31.459 — the pager under the Centre's list, centred like every other card pager. */
.ntf-pager { padding: 10px 14px 4px; }

.nv-ntfrow .kind {
    font-size: 10.5px;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--nv-ink-3);
}

/*  ⚠ THE KIND COLOURS ARE SEMANTIC, NOT DECORATIVE: overdue is the danger
    token, due is the warning one, and everything else stays quiet. A palette
    that coloured all nine would make none of them mean anything. */
.nv-ntfrow .kind.k-task_overdue { color: var(--nv-danger); }
.nv-ntfrow .kind.k-task_due { color: var(--nv-warn); }
.nv-ntfrow .kind.k-email_ai_action { color: var(--nv-blue-bright); }
.nv-ntfrow .kind.k-email_important { color: var(--nv-blue-bright); }
.nv-ntfrow .kind.k-task_assigned { color: var(--nv-good); }

.nv-ntfrow .ttl {
    display: block;
    font-size: 14.5px;
    color: var(--nv-ink);
    margin-top: 2px;
    overflow-wrap: anywhere;
    text-decoration: none;
}

.nv-ntfrow.read .ttl { color: var(--nv-ink-2); }

.nv-ntfrow .meta {
    font-size: 12.5px;
    color: var(--nv-ink-3);
    margin-top: 3px;
}

.nv-ntfrow .meta .ent { color: var(--nv-gold-dim); }
.nv-ntfrow .meta .done { color: var(--nv-good); margin-left: 6px; }

.nv-ntfrow .acts {
    display: flex;
    gap: 7px;
    margin-top: 9px;
    flex-wrap: wrap;
}

    /*  ⚠⚠⚠ THE SAME tokens.css:591 `width: 100%` AGAIN, and this is the third
        place it bit in one round. The house `.btn` is a full-width 51px block —
        correct for a form's Save, absurd for three chips under a notification
        row, where it produced three stacked slabs taller than the notification
        itself. **Anywhere buttons sit in a line in this application, they need
        `width: auto` stated.**
        ⚠ AND A COMPACT HEIGHT, scoped here rather than added to `.btn` globally:
        `.btn.sm` is not a house class, and inventing one in a 587KB stylesheet
        that 60 screens read is not a thing to do inside a notifications round. */
    .nv-ntfrow .acts .btn,
    .nv-ntfpanel .acts .btn {
        flex: 0 0 auto;
        width: auto;
        height: 32px;
        min-height: 32px;
        font-size: 13px;
        padding-inline: 14px;
        border-radius: var(--r-input, 6px);
        text-decoration: none;
    }

    /*  ⚠⚠⚠ AND NO `border` / `background` IS STATED HERE ON PURPOSE. The first
        cut of this rule drew its own 1px `var(--line)` edge, which did two
        things wrong at once: it overrode the `border-color: transparent` that
        `.btn.ghost`'s conic ring depends on, so these three chips were the only
        buttons in the application without the house edge — and it still left the
        SURFACE undefined, so a `<button>` showed the browser's grey and an `<a>`
        showed nothing. The markup now carries `ghost`, tokens.css owns the face,
        and this rule is only ever allowed to change the SIZE. */

    .nv-ntfrow .acts .btn.primary {
        border-color: rgba(232,167,92,.5);
    }

.nv-ntfempty {
    padding: 22px 14px;
    text-align: center;
    color: var(--nv-ink-3);
    font-size: 13.5px;
}

.nv-ntfday {
    font-size: 11px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--nv-ink-3);
    padding: 14px 14px 4px;
    border-bottom: 1px solid var(--line);
}

/*  ══════════════════════════════════════════════════════════════════════════
    ⚠⚠⚠ v31.287 (owner): *"the pills inside nv-ntffilters should all be the
    same width."*

    THIS IS THE HOUSE RULE THE OWNER ALREADY LOCKED, NOT A NEW IDEA. The
    account-type chips (`.acc-chips`, v31.35) answer exactly this instruction —
    *"make all chips same width as the widest chip"* — and the comment there
    records what was tried and rejected, so it is reused rather than
    rediscovered:

      · `width: max-content` on each chip was the FIRST answer and it was
        WRONG. It sizes every chip to its own text, which is what we already
        have; and the one-row `max-content` GRID it was paired with cannot
        wrap, so long labels ran straight through their pill borders. The
        owner's screenshot of that is why the rule below exists.
      · `repeat(auto-fit, minmax(<floor>, 1fr))` gives both halves at once:
        every track is `1fr`, so **every chip is exactly the same width**, and
        the row breaks into two when the floor will not fit. It cannot overflow.

    ⚠⚠ 128px RATHER THAN `.acc-chips`' 116px, because these labels carry a
    COUNT as well — "Actioned 12" is wider than "Suppliers". The floor is what
    decides when six chips become two rows, so it is set from the widest label
    this card can hold, not copied.

    ⚠ `justify-items: stretch` IS THE DEFAULT AND IS LEFT ALONE — the chip must
    fill its track, or equal tracks would still give unequal pills.
    ══════════════════════════════════════════════════════════════════════════ */
.nv-ntffilters {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
    gap: 7px;
}

    /*  ⚠⚠ THE CHIP IS CENTRED IN ITS TRACK. `.chip` is an inline-flex pill
        sized to its own text everywhere else in the application; inside a grid
        track it has to fill the track instead, and the label has to sit in the
        middle of it rather than hard against the left edge — otherwise six
        equal pills read as six ragged ones. */
    .nv-ntffilters .chip {
        display: flex;
        width: auto;
        min-width: 0;
        justify-content: center;
    }
.nv-ntffilters .c { font-variant-numeric: tabular-nums; opacity: .75; margin-left: 4px; }
.nv-ntfcard { padding-left: 0; padding-right: 0; }

/* ── settings ─────────────────────────────────────────────────────────────── */
.nv-set {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 11px 0;
    border-bottom: 1px solid var(--line);
}

.nv-set:last-child { border-bottom: 0; }
.nv-set .lbl { flex: 1 1 auto; font-size: 14.5px; color: var(--nv-ink-2); min-width: 0; }
.nv-set .lbl .sub { display: block; font-size: 12.5px; color: var(--nv-ink-3); margin-top: 1px; }

.grp {
    font-size: 11px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--nv-gold-dim);
    margin: 4px 0 2px;
}

/*  ⚠⚠ THE SWITCH IS A REAL CHECKBOX WITH THE BOX HIDDEN, never a div with a
    click handler. It is focusable, it is reachable by keyboard, it posts with
    its form, and a screen reader announces it — none of which a styled div
    gives you, and all of which a settings screen needs. */
.nv-sw {
    flex: 0 0 auto;
    position: relative;
    display: inline-block;
    width: 44px;
    height: 24px;
    border-radius: 12px;
    border: 1px solid var(--line);
    background: rgba(255, 255, 255, .03);
    cursor: pointer;
}

    .nv-sw input {
        position: absolute;
        opacity: 0;
        width: 100%;
        height: 100%;
        margin: 0;
        cursor: pointer;
    }

    .nv-sw i {
        position: absolute;
        top: 2px;
        left: 2px;
        width: 18px;
        height: 18px;
        border-radius: 50%;
        background: var(--nv-ink-3);
        display: block;
        pointer-events: none;
    }

    .nv-sw:has(input:checked) {
        border-color: var(--nv-gold);
        background: rgba(232, 167, 92, .18);
    }

        .nv-sw:has(input:checked) i {
            left: auto;
            right: 2px;
            background: var(--nv-gold);
        }

    .nv-sw:has(input:focus-visible) { outline: 2px solid var(--nv-blue-bright); outline-offset: 2px; }

.nv-radio {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    padding: 9px 0;
    cursor: pointer;
}

    .nv-radio input { position: absolute; opacity: 0; width: 0; height: 0; }

    .nv-radio .r {
        flex: 0 0 auto;
        width: 15px;
        height: 15px;
        border-radius: 50%;
        margin-top: 3px;
        border: 1px solid var(--line);
        display: block;
    }

    .nv-radio:has(input:checked) .r {
        border-color: var(--nv-gold);
        box-shadow: inset 0 0 0 3.5px var(--nv-bg), inset 0 0 0 9px var(--nv-gold);
    }

    .nv-radio:has(input:focus-visible) .r { outline: 2px solid var(--nv-blue-bright); outline-offset: 2px; }

    .nv-radio .t { font-size: 14.5px; color: var(--nv-ink-2); }

        .nv-radio .t .ex {
            display: block;
            font-size: 12.5px;
            color: var(--nv-ink-3);
            margin-top: 2px;
            font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        }

/* ── devices ──────────────────────────────────────────────────────────────── */
.nv-dev {
    display: flex;
    align-items: center;
    gap: 13px;
    padding: 13px 0;
    border-bottom: 1px solid var(--line);
    flex-wrap: wrap;
}

.nv-dev:last-child { border-bottom: 0; }

    .nv-dev .ic {
        flex: 0 0 auto;
        width: 34px;
        height: 34px;
        border-radius: var(--r-input, 6px);
        border: 1px solid var(--line);
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 16px;
    }

    /*  ⚠⚠ 160px, NOT 220. In a half-width card the old basis pushed the ON /
        Remove pair onto a line of its own for every device — the card wrapped
        rather than overflowed, which looks deliberate and is not. */
    .nv-dev .n { flex: 1 1 160px; min-width: 0; }
    .nv-dev .n b { display: block; font-weight: 400; font-size: 14.5px; color: var(--nv-ink); }
    .nv-dev .n span { display: block; font-size: 12.5px; color: var(--nv-ink-3); }
    .nv-dev .n span.bad { color: var(--nv-danger); }

.nv-dev.off .ic, .nv-dev.off .n b { opacity: .5; }

.nv-steps { margin: 8px 0 0; padding-left: 20px; color: var(--nv-ink-2); font-size: 14px; }
.nv-steps li { margin: 4px 0; }

/* ── the Novera prompt ────────────────────────────────────────────────────── */
.nv-ntfscrim {
    position: fixed;
    inset: 0;
    z-index: 1300;
    background: rgba(3, 5, 11, .72);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 26px;
}

/*  ══════════════════════════════════════════════════════════════════════════
    ⚠⚠⚠ v31.289 (owner): *"formatting is also required for this pop-up."*

    IT IS THE SAME OBJECT AS `nvAlert`, SO IT IS THE SAME CARD. This is a modal
    over a scrim with a mark, a title, a sentence and two buttons — exactly what
    `.err-pop` / `.nv-dlg` already is — and it was the only one of the two not
    built that way. It centred everything where the dialog is left-aligned, it
    carried a GOLD edge where the dialog's is blue-tinted, and it sat on
    `var(--nv-surface)`, the flat card blue, where the dialog floats on the
    house 145° ramp.

    ⚠⚠ THE GOLD EDGE WAS THE WORST OF THE THREE. In this system gold is the
    ALERT colour — it is what an overdue task and a failed save wear. A simple
    offer, which is what this is, framing itself in gold tells the reader
    something has gone wrong before they have read a word.

    ⚠ THE RAMP IS THE SCALED ONE (82% / 175%), the same as the dialog and the
    bell panel, because this box is the same order of size as they are. v31.286
    explains why the stops move: they are percentages of a line that scales with
    the box, so a small tooltip and a 460px card show different PARTS of one
    ramp unless the stops are scaled to match.
    ══════════════════════════════════════════════════════════════════════════ */
.nv-ntfask {
    width: 460px;
    max-width: 100%;
    border: 1px solid rgba(104, 152, 232, .30);
    border-radius: 8px;
    background: linear-gradient(145deg, #010107 0%, #1E1E1E 82%, #13294B 175%);
    box-shadow: 0 18px 48px rgba(0, 0, 0, .55);
    padding: 26px 22px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

    /*  the head: mark on the left, title beside it — `.nv-dlg-head`'s geometry,
        so the two modals open the same way */
    .nv-ntfaskhead {
        display: flex;
        align-items: center;
        gap: 12px;
    }

    /*  ⚠ THE RING IS THE DIALOG'S RING. 44px, circular, gold at .55, the same
        radial fill — a second mark shape would make these read as two different
        applications' modals. */
    .nv-ntfask .mic {
        flex: 0 0 44px;
        width: 44px;
        height: 44px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        color: var(--nv-gold-bright);
        border: 1px solid rgba(232, 167, 92, .55);
        background: radial-gradient(circle at 35% 30%, rgba(232, 167, 92, .16), rgba(232, 167, 92, .04));
    }

    /*  ⚠⚠ 300, NOT 600. The dialog's headline is 300 (v31.286, owner) and the
        body copy under it is 200; a 600 here was heavier than any other heading
        in the application. */
    .nv-ntfask h4 {
        margin: 0;
        font-size: var(--fs-h2);
        font-weight: 300;
        color: var(--nv-gold);
    }

    .nv-ntfask p {
        margin: 0;
        font-size: 16px;
        font-weight: 200;
        color: var(--nv-ink);
        line-height: 1.5;
    }

    /*  ⚠⚠⚠ `.nv-ntfaskbtns`, NOT `.row`. tokens.css gives `.row`
        `min-height: 56px` and `.row + .row { border-top }` — in this system
        `.row` is the 48px-target LIST ROW, not a generic flex line. This prompt
        was still borrowing it after v31.284 recorded the same mistake for the
        action bar, and it was adding a phantom 56px band under the buttons.
        ⚠⚠⚠ AND A GRID, NOT `flex: 1`. MEASURED: `.nv-dlg-btns` uses `flex: 1`
        and its two buttons come out 190px and 192px — a 2px difference, every
        time, because `.btn.primary` carries a 2px border and `.btn.ghost` a 1px
        one, and flex distributes FREE SPACE rather than setting widths. Equal
        shares of the leftover are not equal buttons. `grid-auto-columns: 1fr`
        makes the TRACKS equal and the buttons fill them, so the border weight
        stops mattering. The same rule is applied to `.nv-dlg-btns` below: it is
        the same defect in the same shape of control, and leaving the house
        dialog 2px out while fixing its twin would be the worse outcome. */
    .nv-ntfaskbtns,
    .nv-dlg .nv-dlg-btns {
        display: grid;
        grid-auto-flow: column;
        grid-auto-columns: 1fr;
        gap: 12px;
        margin-top: 4px;
    }

        .nv-ntfaskbtns .btn,
        .nv-dlg .nv-dlg-btns .btn {
            width: auto;
            min-width: 0;
        }

.nv-ntfwhy {
    font-size: 13.5px;
    color: var(--nv-warn);
    line-height: 1.45;
}

.banner-ok {
    padding: 11px 15px;
    border-radius: var(--r-card, 6px);
    border: 1px solid var(--nv-good);
    background: rgba(88, 179, 104, .10);
    color: var(--nv-good);
    font-size: 14px;
}

/* ═══ v31.284 (owner) — THE NOTIFICATION SCREENS' LAYOUT ═════════════════════
   Owner, on the first real look at P-28:
     *"The top buttons on load overlap and not properly visible. Please use the
     same pattern as other pages. Devices and notification settings should be on
     the second row. Entity switch should be on the right."* · *"Notification
     settings should be first, of the Devices."* · *"Enable this device should be
     a button at the same level."* · *"Make the bottom spacing in the card. They
     overflow into each other."* · *"Make cards half their size so you have 2 per
     row and responsive for the mobile."*

   ⚠⚠⚠ WHY THE HEAD OVERLAPPED, AND IT WAS MY ERROR RATHER THAN A TUNING
   PROBLEM. `.screen-head` is `flex-wrap: nowrap` by house rule (app.css:5299 —
   *"heads never wrap"*), and `_NvEntity` spans the head to reach the PAGE's
   right edge. I put three action controls into that row as well, so the title
   was squeezed to "Noti…" and the chip was painted over the buttons. **The head
   takes a title and the entity chip. Actions go underneath.**

   ⚠⚠ AND `.row` IS NOT A GENERIC FLEX ROW IN THIS SYSTEM. tokens.css gives it
   `min-height: 56px` and `.row + .row { border-top }` — it is the 48px-target
   LIST ROW. Using it for a pair of buttons added a phantom 56px band and a rule
   nobody drew. `.nv-ntfbar` below is the button group.
   ══════════════════════════════════════════════════════════════════════════ */

/*  the second row: the same three doors on every notification screen, in the
    owner's order — settings, devices, then the action. */
.nv-ntfbar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

    /*  ⚠⚠⚠ `width: auto` IS THE LINE THAT MATTERS, AND `flex` ALONE WAS NOT
        ENOUGH. tokens.css:591 gives every `.btn` in this system `width: 100%` —
        the house button is a full-width block by default, which is right for a
        Save at the foot of a form and wrong for a row of three doors. The first
        cut of this rule set only `flex: 0 0 auto`, and each button still took a
        line of its own because its WIDTH was still 100%. **A flex basis cannot
        argue with an explicit width.** */
    /*  ⚠⚠ v31.285 (owner): *"All buttons should be the same width."* `flex-basis`
        alone will not do it — the basis is a STARTING size and `flex-grow: 0`
        leaves each button at its own label's width, which is how "Devices" ended
        up half the width of "Notification settings". Three equal buttons need a
        stated basis AND permission to grow from it, so they divide the bar
        between them instead of each measuring its own text.
        ⚠ `min-width: 0` SO THE BASIS IS NOT A FLOOR: without it the longest
        label sets a minimum the other two cannot go below, and the row stops
        being equal the moment one label is translated into a longer word —
        Slovak "Nastavenia upozornení" is longer than the English. */
    .nv-ntfbar .btn {
        flex: 1 1 0;
        width: auto;
        min-width: 0;
        max-width: 240px;
        padding-inline: 18px;
    }

    /*  ⚠⚠ THE SCREEN YOU ARE ON IS MARKED, not omitted. Dropping the current
        page's own button would move the other two every time you navigate, and a
        bar whose contents shift is a bar nobody builds muscle memory for. */
    .nv-ntfbar .btn.on,
    .nv-devacts .btn.on {
        border-color: rgba(232,167,92,.5);
        background: rgba(232,167,92,.14);
        color: var(--nv-gold-bright, #f2bc74);
    }

    /*  on a phone the three become full-width stacked targets rather than three
        ellipsised stubs */
    @media (max-width: 560px) {
        /*  ⚠ `max-width` HAS TO BE LIFTED HERE TOO, not just the basis. The
            240px cap that keeps three buttons from sprawling on a wide screen
            also stopped them filling a 390px one — they stacked, each 240px
            wide against a 350px column, which reads as three mis-measured
            buttons rather than three targets. */
        .nv-ntfbar .btn {
            flex: 1 1 100%;
            max-width: none;
        }
    }

/*  ══ TWO CARDS PER ROW, ONE ON A PHONE ═══════════════════════════════════════
    ⚠ `auto-fit` + `minmax` rather than a media query: the columns collapse when
    the CONTENT runs out of room, so this keeps working inside the narrower middle
    column that pages with an `.ai-side` get (the 1g.1 backlog item) without
    knowing anything about that.
    ⚠⚠⚠ THE CARDS STRETCH TO THEIR ROW — `align-items` IS LEFT AT THE GRID
    DEFAULT, DELIBERATELY. The first cut set `align-items: start`, reasoning that
    a four-switch card should not grow white space to match a five-switch one.
    That reasoning was about the inside of a card and the cost was paid on the
    OUTSIDE: the master switch is a 93px card and the Email card beside it is
    253px, so `start` left a 160px hole in the middle of the screen and the grid
    read as half-drawn. Stretching gives every row a flush foot; the only price
    is a little more room under the shortest card's last switch, which looks like
    padding rather than like a fault. ⚠ MEASURED, NOT ASSUMED — the two numbers
    above come from the rendered page, which is also how the hole was found. */
/*  ⚠⚠ TWO, EXPLICITLY — NOT `auto-fit`. The owner asked for *"2 per row"*, and
    `auto-fit` with a 320px floor gave THREE on a 1366px screen, which is what the
    first cut did. `auto-fit` is the right tool when the column count is free;
    here it is a stated requirement, so it is stated. */
.nv-ntfgrid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
}

/*  ⚠ `minmax(0, 1fr)` RATHER THAN `1fr`: a grid track's default minimum is
    auto, so one long unbreakable word in a card would push the column past its
    share and the two would stop being halves. */

    /*  one column once two would be cramped — a phone, and the narrow middle
        column that pages with an AI rail get */
    @media (max-width: 860px) {
        .nv-ntfgrid {
            grid-template-columns: 1fr;
        }
    }

/*  a card that must not be halved — the notification list itself. A row of
    title, entity, time and three buttons has nowhere to go in a half column. */
.nv-ntfgrid .nv-wide {
    grid-column: 1 / -1;
}

/*  ⚠⚠ THE BOTTOM SPACING THE OWNER REPORTED. `.card` pays `--sp-5` (20px) all
    round, but the LAST `.nv-set` in a card keeps its own 11px bottom padding and
    then drops a `.sub` line under the label — so the descender of "Off by
    default…" sat on the card's border. The card gets a little more room at the
    foot, and the last row gives its own back. */
.card.nv-card {
    padding-bottom: var(--sp-4);
}

    .card.nv-card .nv-set:last-child {
        padding-bottom: 2px;
    }

    .card.nv-card h3 {
        margin: 0 0 10px;
        font-size: 15px;
        font-weight: 400;
        color: var(--nv-ink-2);
    }

/*  the Save / Cancel pair — NOT `.row`, for the reason in the header above */
/*  ⚠⚠ v31.285 (owner): *"Spacing needed above Save/Cancel Button."* The save row
    is a SIBLING of the card grid, not a member of it, so the grid's own 12px
    `gap` never applied to it — Save sat hard against the foot of the last card
    with nothing between them, which read as the card and the button being one
    object. The margin is stated here rather than added as a trailing spacer
    element, so the gap belongs to the row that needs it and cannot be left
    behind if the cards above are ever reordered. */
.nv-ntfsave {
    display: flex;
    gap: 12px;
    align-items: center;
    flex-wrap: wrap;
    margin-top: var(--sp-5, 20px);
}

    /*  ⚠ SAME `width: auto` AS THE BAR, for the same tokens.css reason. Save and
        Cancel are a PAIR; two stacked full-width slabs read as two decisions. */
    .nv-ntfsave .btn {
        flex: 0 0 auto;
        width: auto;
        min-width: 132px;
        padding-inline: 22px;
    }

/*  the per-device controls, inline on the device row */
/*  ⚠⚠⚠ v31.287 (owner): *"the ON/OFF and Remove buttons should be the same
    width."* They were 76px and 106px — `min-width: 76px` set a floor and then
    each button took whatever its own label needed above it, so a two-letter
    word and a six-letter one produced two different pills in the same row.

    ⚠⚠ A GRID, NOT `flex: 1 1 0`. The obvious flex answer makes the two share
    the FREE SPACE, which is not the same thing as making them equal: the row
    is a wrapping flex line whose free space changes with the device name
    beside it, so the pair would resize as you scrolled a list of devices with
    differently-long labels. `grid-auto-columns: 1fr` in a content-sized
    container sizes every track to the WIDEST ITEM — the owner's own
    "same width as the widest" rule, and stable regardless of what is next to
    it.

    ⚠ AND A ROW WITH ONE BUTTON IS STILL RIGHT. A device the browser threw away
    offers Remove alone; one auto column sizes to that one button rather than
    stretching it across the space two would have taken. */
.nv-devacts {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 8px;
    align-items: center;
    flex: 0 0 auto;
}

    .nv-devacts .btn {
        width: auto;
        min-width: 76px;
        padding-inline: 14px;
    }

/*  ══ v31.311 — GL110 AND GL105 ══════════════════════════════════════════
    ⚠ BOTH REUSE `table.rpt-tbl`, which already carries the print sheet's type,
    rules and numeric alignment. What is declared here is only what is specific
    to these two: their column widths, and the one class on GL105 that has to
    tell a zero apart from a number. */
table.bs-tbl { table-layout: fixed; }
    table.bs-tbl col.k-ac { width: 34mm; }
    table.bs-tbl col.k-am { width: 32mm; }
    /*  ⚠ THE NAME COLUMN DECLARES NO WIDTH — it takes what is left, which is
        what makes the ellipsis rule below meaningful. */
    table.bs-tbl td.c-nm {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

table.br-tbl { table-layout: fixed; }
    table.br-tbl col.k-ac { width: 30mm; }
    table.br-tbl col.k-n  { width: 22mm; }
    table.br-tbl col.k-am { width: 30mm; }
    table.br-tbl td.c-nm {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    /*  ⚠⚠ THE COLUMN THE REPORT EXISTS FOR. "Nothing left to do" and "forty-two
        left to do" are the two answers a reconciliation gives and they must not
        look alike on a photocopy — so it is WEIGHT, not colour alone. */
    table.br-tbl td.br-open { font-weight: 700; }
    table.br-tbl td.br-clear { color: #6b6b66; }

/*  ══ v31.313 — THE BANK RECONCILIATION STATEMENT (GL105 · detail) ═══════
    ⚠ Three tables per account, all on `table.rpt-tbl`, so the sheet's type,
    rules and numeric alignment come from one place as everywhere else. What is
    declared here is only what is specific: the widths, the two marks that have
    to survive a photocopy, and the block spacing that keeps one account's three
    tables reading as one thing. */
.br-acct { margin-bottom: 18px; page-break-inside: auto; }
    /*  ⚠⚠ THE ACCOUNT HEAD MUST NOT BE ORPHANED. A page that opens with the
        last two rows of an account nobody named is unreadable — so the head
        keeps its bridge with it. */
    .br-acct .br-head {
        display: flex;
        gap: 10px;
        align-items: baseline;
        margin: 14px 0 6px;
        page-break-after: avoid;
        break-after: avoid;
    }
    .br-acct .br-head .br-code { font-weight: 700; }
    .br-acct .br-head .br-nm { color: #4a4a46; }

    .br-histhead {
        display: flex;
        gap: 10px;
        align-items: baseline;
        margin: 12px 0 4px;
        font-size: 11px;
        letter-spacing: .06em;
        text-transform: uppercase;
        color: #6b6b66;
        page-break-after: avoid;
        break-after: avoid;
    }

table.br-bridge { table-layout: fixed; margin-bottom: 8px; }
    /*  ⚠ THE OPERATOR COLUMN IS NARROW AND CENTRED. It carries one glyph and
        its whole job is to line those glyphs up under each other. */
    table.br-bridge col.k-op { width: 10mm; }
    table.br-bridge td.c-op { text-align: center; color: #6b6b66; }
    table.br-bridge col.k-ct { width: 14mm; }
    table.br-bridge col.k-am { width: 34mm; }
    table.br-bridge td.c-ct { text-align: right; color: #6b6b66; }

table.br-lines { table-layout: fixed; }
    table.br-lines col.k-dt { width: 24mm; }
    table.br-lines col.k-rf { width: 34mm; }
    table.br-lines col.k-am { width: 30mm; }
    table.br-lines col.k-ag { width: 16mm; }
    table.br-lines td.c-ds {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    /*  ⚠⚠⚠ AN ITEM OVER 60 DAYS OLD IS THE ROW THIS SHEET EXISTS FOR. It is set
        apart by WEIGHT rather than colour alone, because a reconciliation gets
        photocopied and a colour does not survive that. */
    table.br-lines td.br-stale { font-weight: 700; }
    table.br-lines tr.br-none td { color: #8a8a84; font-style: italic; }

/*  ⚠⚠ v31.314 — A QUIET MONTH IS SET BACK, NOT HIDDEN. It had no line on
    either side; its balance is the one it was handed. Greying it says "nothing
    happened here" where leaving the row out would say "nobody holds this". */
table.br-hist tr.br-quiet td { color: #8a8a84; }

/*  ⚠⚠⚠ "RECONCILED THROUGH" IS THE ANSWER THE WHOLE SECTION EXISTS TO GIVE, so
    it is the one row on the history that reads as a conclusion. */
table.br-hist tfoot td { font-weight: 700; border-top: 1px solid #cdcbc6; }

table.br-hist { table-layout: fixed; }
    table.br-hist col.k-pe { width: 22mm; }
    table.br-hist col.k-am { width: 30mm; }
    table.br-hist col.k-n  { width: 22mm; }
    table.br-hist td.br-clear { color: #2c6b3f; font-weight: 700; }
    table.br-hist td.br-open { color: #6b6b66; }

/*  ⚠ THE GRAIN CHECKBOX on the parameter card — the same gold checkbox the KV
    sections and the VAT checklist use, on one row with its label. */
.rpt-grain .rpt-grainrow {
    display: flex;
    gap: 8px;
    align-items: center;
    padding: 6px 0;
}

/*  ══ v31.313 — THE BOARD PACK (GL001 · board form) ══════════════════════
    ⚠ FOUR TILES ACROSS, and they wrap rather than shrink: a KPI whose figure
    has been squeezed to fit is a KPI nobody can read at a glance, which is the
    only thing a KPI is for. */
.bd-kpis {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin: 6px 0 14px;
}
    .bd-kpi {
        flex: 1 1 130px;
        border: 1px solid #cdcbc6;
        border-radius: 4px;
        padding: 8px 12px;
    }
    .bd-kpi .bd-k {
        font-size: 9px;
        letter-spacing: .08em;
        text-transform: uppercase;
        color: #6b6b66;
    }
    .bd-kpi .bd-v {
        font-size: 19px;
        margin-top: 2px;
        font-variant-numeric: tabular-nums;
    }
    .bd-kpi .bd-d { font-size: 10px; color: #6b6b66; margin-top: 1px; }

/*  ══ v31.314 — FIVE TILES, NOT FOUR ═════════════════════════════════════
    Gross margin and EBITDA joined the strip when A-130 landed. The basis
    narrows so five fit a printed A4 width; the wrap above is still the
    fallback, because a squeezed figure is worse than a wrapped row. */
.bd-kpis5 .bd-kpi { flex: 1 1 104px; }
.bd-kpis5 .bd-kpi .bd-v { font-size: 17px; }

/*  ⚠⚠⚠ THE GROSS PROFIT AND EBITDA LINES CARRY WEIGHT, NOT A FILL. This
    document gets photocopied and faxed, exactly like the trial balance's
    totals — a tint that survives neither is not emphasis, it is a decision
    that silently stops applying. */
table.bd-tbl tr.bd-key td {
    font-weight: 700;
    border-top: 1px solid #cdcbc6;
}
/*  ⚠ AND THE ADD-BACK IS SET QUIETER THAN THE FIGURE IT FEEDS, because it is
    workings rather than a result — but it is ON the sheet, so the EBITDA above
    it can be checked without asking anyone. */
table.bd-tbl tfoot tr.bd-addback td {
    font-weight: 400;
    color: #4a4a46;
}

table.bd-tbl { table-layout: fixed; }
    table.bd-tbl col.k-ac { width: 28mm; }
    table.bd-tbl col.k-am { width: 26mm; }
    table.bd-tbl col.k-pc { width: 20mm; }
    /*  ⚠ ELLIPSIS ON THE NAME ONLY — the owner's v31.303 rule. A truncated
        account code cannot be matched against anything. */
    table.bd-tbl td.c-nm {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

/*  ══ v31.315 — GL080 · THE FINANCIAL ANALYSIS ═══════════════════════════
    ⚠⚠ THE WHOLE SHEET IS ONE PRINTED PAGE, so every block below is sized in
    the same units as the reports around it and nothing here is set in px that
    a print stylesheet would then have to undo. */
.fa-head { border-bottom: 1px solid #cdcbc6; padding-bottom: 6px; margin-bottom: 10px; }
    .fa-head .fa-co { font-size: 15px; font-weight: 700; }
    .fa-head .fa-meta { font-size: 11px; color: #6b6b66; margin-top: 2px; }

/*  ⚠ SIX TILES THAT WRAP RATHER THAN SHRINK — the board pack's own rule. A KPI
    squeezed until its figure is unreadable is not a KPI. */
.fa-rail { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 14px; }
    .fa-kpi, .fa-r {
        flex: 1 1 120px;
        border: 1px solid #cdcbc6;
        border-radius: 4px;
        padding: 7px 10px;
    }
    .fa-k { font-size: 8.5px; letter-spacing: .07em; text-transform: uppercase; color: #6b6b66; }
    .fa-v { font-size: 16px; font-weight: 700; margin-top: 2px; font-variant-numeric: tabular-nums; }
    .fa-d { font-size: 9.5px; color: #6b6b66; font-variant-numeric: tabular-nums; }
    /*  ⚠⚠ THE ARROW CARRIES COLOUR **AND** A GLYPH. This gets photocopied, and
        a direction that survives only in colour stops being a direction. */
    .fa-up { color: #2c6b3f; }
    .fa-dn { color: #8a3b12; }

.fa-duo { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 14px; }
    .fa-chart, .fa-ring { flex: 1 1 300px; min-width: 260px; }
    .fa-ct { font-size: 9px; letter-spacing: .07em; text-transform: uppercase; color: #6b6b66; }
    .fa-chart svg, .fa-ring svg { width: 100%; height: auto; }
    .fa-ring { display: flex; gap: 12px; align-items: flex-start; }
    .fa-ring svg { max-width: 120px; flex: 0 0 120px; }
    .fa-ring .fa-legend { flex: 1 1 auto; }
    .fa-sw { display: inline-block; width: 8px; height: 8px; margin-right: 5px; }

table.fa-tbl { table-layout: fixed; margin-bottom: 12px; }
    table.fa-tbl col.k-am { width: 26mm; }
    table.fa-tbl td.c-nm { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.fa-ratios { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }

/*  ⚠ AT PHONE WIDTH THE TWO-UP BLOCKS STACK. The reports module is read on a
    desk and on a phone, and a donut beside its legend at 320px is neither. */
@media (max-width: 720px) {
    .fa-duo { display: block; }
    .fa-chart, .fa-ring { width: 100%; margin-bottom: 12px; }
    .fa-ring svg { max-width: 96px; flex-basis: 96px; }
}

/*  ══ v31.317 — DOCUMENT AI, ROUND 1 ═════════════════════════════════════
    ⚠⚠⚠ THESE ARE APP SCREENS, NOT PRINT SURFACES, AND THE FIRST DRAFT OF THIS
    BLOCK FORGOT IT. NOVERA v2's shell is DARK; the reports CSS above is light
    because a report is a printed page. A cream fill with the app's own light
    text on it produced white-on-cream rows in the match table — the two rows
    that matter most, the VAT number and the changed IBAN, were the two nobody
    could read. Caught by looking at the rendered page rather than at the
    markup.

    ⚠⚠ SO EVERY EMPHASIS BELOW IS A TRANSLUCENT TINT OVER WHATEVER IS BEHIND
    IT, in the house pattern `.card.suggest` already uses — `rgba(232,167,92,
    .06)` over `rgba(232,167,92,.45)` — plus a border and a word. Nothing here
    sets an opaque background, and nothing relies on colour alone. */

/*  ══ v31.325 (owner) — THE PROVENANCE CHIP ══════════════════════════════
    *"The icons for the legend should be on the same line as the line result
    data."* · *"Also remove the colored backgrounds and instead use colored
    borders."*

    ⚠⚠⚠ ONE CHARACTER, FOUR COLOURS, BESIDE THE VALUE. It used to sit after the
    LABEL on the reasoning that the eye is there when it asks "where did this
    come from" — which was wrong: the question is asked WHILE reading the value,
    and a marker a line away from what it describes makes the reader pair them
    up themselves, four times per field, on a screen with forty fields.

    ⚠⚠ AND IT IS AN OUTLINE NOW, NOT A FILL. Four solid chips per row is four
    blocks of saturated colour competing with the figures they annotate; the
    same four as outlines carry the same information and let the value be the
    brightest thing in the row. `currentColor` does both the letter and the
    edge, so a source is ONE declaration and the two can never disagree.

    ⚠ `inline-flex` RATHER THAN `inline-block`: the chip centres its own glyph
    at any size, and stays inline in the legend where it sits mid-sentence. */
.doc-pv {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 17px; height: 17px;
    border-radius: 4px;
    border: 1.5px solid currentColor;
    background: transparent;
    font-size: 10px; font-weight: 700; font-style: normal;
    line-height: 1;
}
.doc-pv-d { color: var(--nv-blue-bright, #4a92dd); }  /* the document said so       */
.doc-pv-h { color: var(--acc-green, #3fca85); }       /* this account's own history */
.doc-pv-p { color: var(--acc-purple, #b184e6); }      /* a decision accepted before */
/*  ⚠⚠ v31.328 (owner) — *"a Coloured 'A' square"*. Gold, which is the shell's
    own accent and the one hue not already spoken for by D, H, P or the green
    tick — the five markers have to be told apart at 18px on a dark ground, and
    that is the whole job of this line. */
.doc-pv-a { color: var(--nv-gold, #e8a75c); }         /* set up in another catalog  */
.doc-pv-u { color: var(--nv-gold, #e8a75c); }         /* you, in this review        */

/*  ══ v31.326 (owner) — CONFIRMED BY A PERSON ═══════════════════════════════
    *"make the icons clickable so state 'U' — 'changed by you in this review'
    can be green ticked"*.

    ⚠⚠⚠ THE TICK REPLACES THE LETTER — the owner's own choice of three, and the
    right one: a confirmed field should stop competing for attention so the eye
    goes to what is still flagged. The letter is not lost; it moves to the
    hover, and `ProposedSource` on the proposal never changed at all.

    ⚠⚠ AND IT IS THE ONE FILLED CHIP. The four sources are outlines because they
    are all the same KIND of thing — where a value came from. This is a
    different kind: a decision somebody made. A filled tick among outlines reads
    as "done" at a glance, which is the whole job.                             */
.doc-pv-ok {
    color: var(--nv-good, #58b368);
    border-color: var(--nv-good, #58b368);
    background: rgba(88,179,104,.16);
}

/*  ⚠⚠⚠ THE BUTTON MUST LOOK EXACTLY LIKE THE SPAN. A chip that changed shape
    the moment it became pressable would say that a confirmable field and an
    unconfirmable one are different KINDS of field. They are not — one simply
    has nothing to agree with. */
button.doc-pv {
    padding: 0;
    font-family: inherit;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}
    button.doc-pv:hover { border-color: var(--nv-ink, #f1f3f9); color: var(--nv-ink, #f1f3f9); }
    /*  ⚠ A VISIBLE FOCUS RING. These are the only keyboard-reachable controls in
        a grid of forty-one cells; losing track of which one has focus makes the
        grid unusable without a mouse. */
    button.doc-pv:focus-visible { outline: 2px solid var(--nv-gold, #e8a75c); outline-offset: 2px; }
    /*  ⚠⚠ THE CONFIRMED CHIP KEEPS ITS GREEN ON HOVER. Turning it to ink would
        say "this will become something else" — and what it becomes is
        un-confirmed, which the title already says. */
    button.doc-pv.doc-pv-ok:hover {
        border-color: var(--nv-good, #58b368);
        color: var(--nv-good, #58b368);
        background: rgba(88,179,104,.28);
    }

/*  ⚠ A CONFIRMED FIELD IS QUIETER, NOT LOUDER. It has been dealt with, and the
    screen's job from that moment is to point at everything else. */
.doc-fld.doc-done .doc-t { color: var(--nv-ink-2, #aab3c8); }

/*  ══ v31.325 — THE GRID OWNS ITS OWN LAYOUT ════════════════════════════════
    ⚠⚠⚠ IT USED TO BORROW `.fld-wrap` + `.fl-hint`, the house FORM-INPUT trio.
    That is a flex COLUMN built to stand a floating label above an `<input>`, so
    the provenance chip — a sibling of the label — was handed a row of its own
    and every field rendered as four stacked lines for two facts. Nothing chose
    that; the layout inherited a shape meant for something else.

    ⚠⚠ AND A READ-ONLY GRID HAS NO BUSINESS WEARING INPUT CLASSES ANYWAY. Every
    rule written for `.fld-wrap` in this stylesheet — the inline editor's
    pencil, the mobile pair-splitter, the account page's tree cell — reaches a
    grid that has no input in it at all.                                      */
/*  ══ v31.326 (owner) — FIVE COLUMNS AT 05-Screen, DOWN TO ONE AT 01 ═══════
    *"Make the main body … effectively 5 columns for screen 05-Screen and RWD
    down to screen 01-Screen."*

    ⚠⚠⚠ ONE COLUMN IS THE BASE AND EVERY STEP ADDS ONE. Written the other way
    round — three columns with max-width rules taking them away — a width nobody
    wrote a rule for renders the widest layout, which is the one that breaks. A
    phone is the safe default and always has been.

    ⚠⚠ THE STEPS ARE THE PROJECT'S OWN NAMED SCREENS, not numbers picked to fit
    this grid: 01 = 390, 02 = 768, 03 = 1024, 04 = 1366, 05 = 1920. A sixth
    breakpoint here would be a sixth screen size nobody else in this product
    has, and the first thing to drift.                                        */
.doc-grid {
    display: grid;
    grid-template-columns: 1fr;          /* 01-Screen (390) */
    gap: 15px 18px;
}
@media (min-width: 768px)  { .doc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }  /* 02 */
@media (min-width: 1024px) { .doc-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }  /* 03 */
/*  ⚠⚠ v31.327 — 04-Screen GOES BACK TO FOUR, and this is the menu's bill.
    Five columns fitted when the panel track was collapsed; with the menu in
    it the content column at 1366 is 600px, and five columns of 89px wrap a
    date onto two lines. Four is 136px — the same width the six columns get
    at 1920, which is the width that was signed off. */
/*  ⚠⚠⚠ v31.328 (owner) — v31.327's 04 AND 05 RUNGS WERE HERE, AND THEY ARE
    GONE RATHER THAN OVERRIDDEN. *"the 'Account setup · proposed.' should have
    3 columns per row. Also in Process document to invoice, the 'invoice entry ·
    proposed.' should have three columns per row."*

    The owner asked for six in the round before this one, saw six, and asked
    for three. Three is the later ruling and the only one — replacing these
    two rungs with wider ones would leave two rules about one width, and the
    winner would be whichever came last in the file.

    ⚠ THE 04-Screen TIGHTENING BAND WENT WITH THEM. It existed because four
    columns at 1366 were 116px and a date broke at its own hyphen; three
    columns are 182px at 1024 and wider above, so there is nothing to tighten
    and a band outliving its reason would give the widest screens the
    smallest type.                                                          */
/*  ⚠⚠ v31.327 (owner) — SIX AT 05-Screen. *"…with 6 columns per row as
    requested."* Four and five were what the old width could carry; with the
    1366 and 1410 caps lifted for this page (see `.doc-app` in responsive.css)
    there is room for six that are still wide enough to read a date in.

    ⚠ MEASURED WITH THE MODULE MENU IN PLACE, not with its track collapsed —
    the owner asked for the menu back in that track in the same round, so the
    260px never came to this grid:

    Measured on the running app, grid width inside the card — not the content
    column, which the card's own padding takes 82px out of:

        viewport   grid   columns   each
          1920      864       6     129px
          1366      518       4     116px
          1024      582       3     182px                                     */

/*  ⚠⚠ v31.340 — `height: 100%` IS WHAT MAKES THE BOTTOM A REAL BOTTOM. A grid
    item stretches its BOX to the row height but its flex column still lays out
    against its content, so `margin-top: auto` on the edit strip had nothing to
    push against and the strips stayed ragged. */
.doc-fld {
    display: flex; flex-direction: column; gap: 3px; min-width: 0;
    height: 100%;
}

/*  ⚠ THE LABEL CLAMPS TO ONE LINE so the three columns stay aligned; the full
    wording is on `title`. A label that wrapped would push its own value down
    and leave the row beside it floating. */
.doc-k {
    font-size: 12px;
    color: var(--nv-ink-3, #6b7690);
    line-height: 18px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/*  ⚠⚠⚠ THE ONE LINE THE OWNER ASKED FOR. Baseline rather than centre, so the
    chip sits with the text rather than floating in the middle of a tall value;
    `min-width: 0` so a long IBAN wraps inside the cell instead of widening the
    column and pushing the grid sideways. */
.doc-row { display: flex; align-items: baseline; gap: 8px; min-width: 0; }

.doc-t {
    font-size: 16px;
    min-width: 0;
    overflow-wrap: anywhere;   /* an IBAN has no spaces to break at */
}

/*  ⚠⚠ THE AMBER EDGE IS A BORDER AND A WORD, never colour alone. */
.doc-fld.doc-check {
    border-left: 3px solid var(--nv-warn, #d9a13b);
    padding-left: 9px;
}
.doc-flag { font-size: 12px; font-weight: 600; color: var(--nv-warn, #d9a13b); margin-top: 2px; }
.doc-empty { color: var(--nv-ink-3, #6b7690); }
.doc-say { font-size: 13px; margin-top: 1px; color: var(--nv-ink-2, #aab3c8); }

/*  ══ v31.333 (owner) — BOTH CURRENCIES IN ONE CARD ═════════════════════════
    *"show both currency values and currency code and the converted value in
    base currency in THE SAME card space. do not create a separate card for
    multi-currency conversion."*

    ⚠⚠⚠ TWO RANKS, NOT TWO VALUES. The document's own figure keeps `.doc-t`'s
    weight because it is the fact the supplier stated; the conversion sits under
    it, smaller and quieter, because it is derived. A second line at equal
    weight would read as a second amount and a reader would have to work out
    which one to check against the invoice.

    ⚠⚠ TABULAR FIGURES ON BOTH, so the decimal points of the two lines sit under
    one another and a transposed digit is visible rather than merely present. */
.doc-cur {
    font-size: 12.5px; font-weight: 600; letter-spacing: .04em;
    margin-inline-start: 6px; color: var(--nv-ink-3, #6b7690);
    white-space: nowrap;
}
/*  ══ v31.334 (owner) — THE ANALYSIS SLOT IS A CONTROL ══════════════════════
    *"The business analysis must have drop-down selection to allow user select
    /change business analysis to be used for the document."*

    ⚠⚠ IT SITS ON THE VALUE'S OWN ROW, beside the marker, because that is where
    the value it replaces was — a select on a line of its own would make every
    analysis slot twice the height of every other field and break the three
    columns the owner asked for.

    ⚠ THE SELECT TAKES THE SPACE AND THE BUTTON TAKES WHAT IT NEEDS. `min-width:
    0` is what lets a long item name shrink instead of pushing the button out of
    the card. */
.doc-pick { display: flex; gap: 6px; align-items: center; min-width: 0; flex: 1 1 auto; }
    .doc-pick .doc-sel {
        flex: 1 1 auto; min-width: 0;
        font-size: 14px; padding: 3px 6px;
    }
    .doc-pick .doc-selgo { flex: 0 0 auto; }

.doc-base {
    font-size: 13.5px; margin-top: 1px;
    color: var(--nv-ink-2, #aab3c8);
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

/*  ── the analysing state ───────────────────────────────────────────────── */
.doc-wait { max-width: 900px; }
.doc-waith { font-size: 14px; font-weight: 600; margin-bottom: 8px; }
.doc-steps { display: flex; flex-direction: column; gap: 8px; }
.doc-step { display: flex; gap: 10px; align-items: flex-start; font-size: 14.5px; }
    .doc-step .doc-dot {
        flex: 0 0 auto; width: 15px; height: 15px; border-radius: 50%;
        border: 1.5px solid rgba(255,255,255,.22); margin-top: 3px;
    }
    .doc-step.doc-done .doc-dot { background: var(--nv-good, #58b368); border-color: var(--nv-good, #58b368); }
    .doc-step.doc-done .doc-lbl { color: var(--nv-ink-2, #aab3c8); }
    /*  ⚠ THE LIVE STEP IS MARKED BY A RING AS WELL AS A COLOUR, for the same
        reason the amber edge carries a word. */
    .doc-step.doc-live .doc-dot {
        border-color: var(--nv-gold, #e8a75c);
        box-shadow: 0 0 0 3px rgba(232,167,92,.20);
    }
    /*  ⚠⚠ `.doc-todo`, NOT `.doc-wait`. The card is `.doc-wait`; a step state
        of the same name made `.doc-step.doc-wait` inherit the card's rules —
        one class name meaning two things. */
    .doc-step.doc-todo { opacity: .45; }
.doc-bar { height: 3px; background: rgba(255,255,255,.10); border-radius: 2px; overflow: hidden; margin-top: 11px; }
    .doc-bar i { display: block; height: 100%; width: 68%; background: var(--nv-gold, #e8a75c); }

/*  ── the match panel ─────────────────────────────────────────────────────
    ⚠⚠ A LIKELY MATCH AND A POSSIBLE ONE DO NOT LOOK ALIKE. One offers Update;
    the other offers a list and chooses nothing. */
.doc-likely { border-color: rgba(232,167,92,.45); background: rgba(232,167,92,.06); }
.doc-maybe  { border-color: rgba(255,255,255,.16); }
.doc-new    { border-color: rgba(88,179,104,.40); }
.doc-mh { font-weight: 600; font-size: 14.5px; }
.doc-acc { font-size: 16px; margin: 4px 0 8px; }
table.doc-mtbl { border-collapse: collapse; width: 100%; font-size: 14px; }
    table.doc-mtbl th, table.doc-mtbl td {
        border-bottom: 1px solid rgba(255,255,255,.10);
        padding: 5px 8px; text-align: left; vertical-align: top;
    }
    table.doc-mtbl th {
        font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
        color: var(--nv-ink-3, #6b7690); font-weight: 700;
    }
    table.doc-mtbl td.num, table.doc-mtbl th.num { text-align: right; font-variant-numeric: tabular-nums; }
    /*  ⚠⚠⚠ THE SENSITIVE ROWS ARE MARKED BY AN EDGE AND A TINT, NOT A FILL —
        VAT registration, address and bank details, the three the owner named.
        A differing IBAN is the one row a reviewer must look at, and a table
        where every row looks alike is a table where it gets missed. */
    table.doc-mtbl tr.doc-sens td {
        background: rgba(232,167,92,.07);
    }
    table.doc-mtbl tr.doc-sens td:first-child {
        box-shadow: inset 3px 0 0 0 rgba(232,167,92,.55);
    }
/*  ══ v31.328 (owner) — THE ANOTHER-CATALOG CARD ════════════════════════════
    ⚠⚠ GOLD EDGE, NOT GREEN AND NOT AMBER. Green is `.doc-new` — this entity is
    about to gain something; amber is `check` — a value nobody stands behind.
    This card is neither: it is a fact about somewhere else, offered. It wears
    the same gold as the `A` on the fields it filled, so the card and the
    markers read as one statement rather than two. */
.doc-else { border-color: rgba(232,167,92,.40); }
.doc-eline { font-size: 15px; margin: 5px 0 6px; display: flex; gap: 8px;
             align-items: baseline; flex-wrap: wrap; }
/*  ⚠ THE SQUARE SITS ON THE HEADING'S BASELINE, like the row markers do. */
.doc-else .doc-mh { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }

.doc-w { font-weight: 600; }
.doc-w-decisive { color: var(--nv-good, #58b368); }
.doc-w-differs { color: var(--nv-warn, #d9a13b); }
/*  ⚠⚠ v31.329 — A SUPPORTING ROW AGREES, so it is no longer the grey an
    absence gets. It is the quieter green: the same family as `decisive`,
    because both are agreements, at the weight each carries. */
.doc-w-supporting { color: var(--acc-green, #3fca85); opacity: .78; }
/*  ⚠ AND AN ABSENCE IS THE GREY. Nothing on file is not a result. */
.doc-w-absent { color: var(--nv-ink-3, #6b7690); font-weight: 500; }
.doc-warn {
    margin-top: 9px; padding: 9px 11px; font-size: 14px;
    border: 1px solid rgba(232,167,92,.50); border-radius: 6px;
    background: rgba(232,167,92,.07);
    color: var(--nv-ink, #f1f3f9);
}
/*  ⚠ v31.327 — THE COUNT IS A BADGE, NOT A WORD in the sentence. It sits
    ahead of the text so no language has to find a slot for it, and it is the
    one thing in the box that carries weight, because it is the number the
    reader is driving to zero. `float` rather than a flex row: the sentence
    wraps around it in the 350px rail instead of being squeezed into a
    second column. */
.doc-warn .doc-n {
    float: inline-start; margin: 0 8px 0 0;
    min-width: 22px; padding: 1px 6px;
    font-size: 13px; font-weight: 700; text-align: center;
    border: 1px solid rgba(232,167,92,.55); border-radius: 5px;
    color: var(--nv-warn, #d9a13b);
    font-variant-numeric: tabular-nums;
}
.doc-acts { margin-top: 11px; }
.doc-walk {
    display: flex; gap: 10px; align-items: baseline;
    border-color: rgba(232,167,92,.45); background: rgba(232,167,92,.06);
}

/*  ── the proposal ────────────────────────────────────────────────────────*/
.doc-file { display: flex; gap: 9px; align-items: center; font-size: 15px; flex-wrap: wrap; }
.doc-clip { opacity: .75; }
.doc-line { border-top: 1px solid rgba(255,255,255,.10); padding-top: 9px; margin-top: 9px; }
/*  ══ v31.329 — THE HOUSE-WORDING OFFER ════════════════════════════════════
    ⚠⚠ IT IS QUIETER THAN THE LINE IT SITS UNDER. The document's own words are
    the value; this is a suggestion, and a suggestion that shouts competes with
    the thing it is suggesting about. Dashed edge, no fill — the visual grammar
    this product already uses for "offered, not applied". */
.doc-wording {
    display: flex; gap: 9px; align-items: baseline; flex-wrap: wrap;
    margin-top: 8px; padding: 7px 10px;
    border: 1px dashed rgba(104,152,232,.38); border-radius: 6px;
}
    .doc-wording .doc-t { font-size: 14px; }
    .doc-wording .doc-say { font-size: 12px; }
    .doc-wchip { flex: 0 0 auto; white-space: normal; height: auto; min-height: 28px;
                 padding: 4px 10px; font-size: 12.5px; line-height: 1.25; }
.doc-lno {
    font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
    color: var(--nv-ink-3, #6b7690); font-weight: 700; margin-bottom: 4px;
}
.doc-legend {
    display: flex; gap: 16px; flex-wrap: wrap; font-size: 13px;
    color: var(--nv-ink-2, #aab3c8); margin-top: 13px; padding-top: 10px;
    border-top: 1px solid rgba(255,255,255,.10);
}
    /*  ⚠⚠ THE LEGEND'S CHIP IS THE GRID'S CHIP, ON THE SAME LINE AS ITS OWN
        SENTENCE. That is the whole job of a legend: if the key and the thing it
        explains do not look identical, the reader has to decide whether they
        are the same marker — which is the doubt the legend existed to remove. */
    .doc-legend > span {
        display: inline-flex; align-items: baseline; gap: 6px; min-width: 0;
    }
.doc-rules {
    font-size: 13px; margin-top: 8px; display: flex; flex-direction: column; gap: 3px;
}

/*  ══ v31.326 (owner) — THE REVIEW CARD IN THE RIGHT RAIL ═══════════════════
    *"Create card for the section highlighted in green and move it to the top of
    the right column."*

    ⚠⚠⚠ IT IS ~350px WIDE, which is the width that broke `.doc-choose` in
    v31.318 and `.doc-block` in v31.321. Same rail, same two rules, third time:
    `.chips` is a fixed-height row that does not wrap and `.chip` shrinks, so a
    label like "Save without the bank details" renders outside its own box. */
.doc-review .doc-acts,
.doc-review .chips {
    display: flex; flex-wrap: wrap; gap: 8px; height: auto; align-items: center;
}
    .doc-review .chip {
        flex: 0 0 auto;
        white-space: normal;      /* ⚠ these labels are sentences, not words */
        height: auto; min-height: 32px;
        line-height: 1.25; padding: 6px 12px;
    }

/*  ⚠⚠ THE LEGEND STACKS IN THE RAIL. Four key-and-sentence pairs on one wrapped
    row at 350px produces four ragged lines that read as one paragraph; one per
    row is a list, which is what a legend is. */
.doc-review .doc-legend { flex-direction: column; gap: 7px; margin-top: 11px; }
/*  ══ v31.328 (owner) — THE ACCOUNT CARD MOVED INTO THIS RAIL ═══════════════
    *"for invoice, please move the second card to the right panel at the top as
    shown in the diagram."*

    ⚠⚠⚠ THIS IS THE FOURTH COMPONENT THIS RAIL HAS BROKEN — `.doc-choose` at
    v31.318, `.doc-block` at v31.321, `.doc-review .chips` at v31.326, and now a
    four-column table. The rail is 350px inside its padding; the table's own
    columns need, measured:

        matched on   on the document   on the account   weight
           108             150              150           78    = 486px

    ⚠⚠ SO IT STOPS BEING A TABLE HERE. Each row becomes its own block with its
    three values labelled, which is the same information in the shape the width
    allows — not a horizontal scrollbar, which hides the account side of a
    comparison behind a gesture nobody makes.

    ⚠ THE HEAD ROW GOES because every cell now carries its own heading; leaving
    it would print the four column names once with nothing under them.

    ⚠⚠⚠ SCOPED ON `.ai-side`, NOT ON `.doc-review`. The panel is a SIBLING of
    the Review card inside the rail, not a child of it — a descendant
    selector off `.doc-review` matches nothing at all, which is a rule that
    looks right in the file and does nothing on the screen. */
.ai-side table.doc-mtbl,
.ai-side table.doc-mtbl tbody,
.ai-side table.doc-mtbl tr,
.ai-side table.doc-mtbl td { display: block; width: auto; }
.ai-side table.doc-mtbl thead { display: none; }
    /*  ⚠⚠ EVERY ROW IS INDENTED THE SAME 12px, shaded or not. Measured, not
        guessed: a plain row's heading started at 0 and a shaded row's at 12,
        because the shaded one carries a 3px edge and 9px of clearance. Reading
        down the card the headings stepped left and right, which makes a list of
        four comparisons look like two lists. The border now sits INSIDE the
        shared indent, so the text lands at 12px in both cases. */
    .ai-side table.doc-mtbl tr {
        border-bottom: 1px solid rgba(255,255,255,.10);
        padding: 6px 0 7px 12px;
    }
    .ai-side table.doc-mtbl tr:last-child { border-bottom: 0; }
    .ai-side table.doc-mtbl td {
        border-bottom: 0; padding: 1px 0 1px 9px;
        overflow-wrap: anywhere;   /* an IBAN has no spaces to break at */
    }
    /*  ⚠ THE FIRST CELL IS THE ROW'S OWN HEADING — "VAT number", "IBAN" — so it
        is set as one rather than as another value. */
    .ai-side table.doc-mtbl td:first-child {
        padding-left: 0; font-weight: 600; font-size: 12.5px;
        letter-spacing: .03em; text-transform: uppercase;
        color: var(--nv-ink-3, #6b7690);
    }
    /*  ⚠⚠ A BORDER, NOT AN INSET SHADOW — caught on screen, not by a gate.
        `box-shadow: inset` paints inside the padding box, so on a `display:
        block` row it drew the gold stripe OVER the first character of the row's
        heading: "IBAN" rendered as "BAN". A left border takes its own 3px out
        of the box and pushes the content clear, which is exactly what
        `.doc-fld.doc-check` has always done for the amber edge. */
    .ai-side table.doc-mtbl tr.doc-sens td { background: none; }
    .ai-side table.doc-mtbl tr.doc-sens {
        background: rgba(232,167,92,.07);
        border-left: 3px solid rgba(232,167,92,.55);
        padding-left: 9px;          /* 3 + 9 = the 12 every other row uses */
    }

/*  ⚠⚠ THE CELL LABELS EXIST ONLY IN THE STACKED LAYOUT. In a real table they
    would print the column heading on every row; here they are the only thing
    telling the document's value from the account's. */
.doc-mk { display: none; }
.ai-side .doc-mk {
    display: block;
    font-size: 10.5px; letter-spacing: .05em; text-transform: uppercase;
    color: var(--nv-ink-3, #6b7690);
}

/*  ⚠ AND THE CARD ITSELF STOPS BEING AS WIDE AS THE MAIN COLUMN. */
.ai-side .doc-mtbl { font-size: 13.5px; }
.ai-side .doc-acc  { font-size: 14.5px; }

/*  ══ v31.330 (owner) — THE MATCH VERDICTS, TWO TO A ROW ════════════════════
    *"instead of a 5 line detail repeating some of the data already shown in
    header simply put a two liner: VAT NUMBER · decisive · COMPANY NUMBER ·
    decisive · NAME · agrees · IBAN · agrees."*

    ⚠⚠ TWO TRACKS AT ANY WIDTH — `minmax(0, 1fr)` and not `1fr`, because a grid
    track's default minimum is `auto` and an IBAN has no space to break at: one
    unbreakable value would push the second column off the card. Every value in
    here can also wrap mid-string for the same reason. */
.doc-mw {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px 12px; margin: 2px 0 4px;
}
.doc-mwc {
    display: flex; flex-direction: column; gap: 1px;
    min-width: 0; padding: 4px 0 5px;
    border-bottom: 1px solid rgba(255,255,255,.10);
    overflow-wrap: anywhere;
}
    /*  ⚠ THE IDENTIFIER'S NAME IS A HEADING, set like every other heading on
        this card, so the verdict under it is the only thing carrying weight. */
    .doc-mwk {
        font-size: 10.5px; letter-spacing: .05em; text-transform: uppercase;
        color: var(--nv-ink-3, #6b7690); font-weight: 700;
    }
    .doc-mwc .doc-w { font-size: 13.5px; }
    /*  ⚠⚠ THE TWO VALUES APPEAR ONLY UNDER A `differs` VERDICT — see the note in
        `_DocMatch.cshtml`. They are quieter than the verdict because the verdict
        is what sends the reader here. */
    .doc-mwv { font-size: 12.5px; color: var(--nv-ink-2, #aab3c8); }
    .doc-mwv .doc-mk { display: block; }
    /*  ⚠⚠⚠ THE SENSITIVE EDGE SURVIVES THE TABLE IT WAS WRITTEN FOR. VAT
        registration, address and bank details are the three the owner named, and
        a border rather than an inset shadow for the reason v31.328 found on
        screen: an inset shadow paints over the first character. */
    .doc-mwc.doc-sens {
        border-left: 3px solid rgba(232,167,92,.55);
        background: rgba(232,167,92,.07);
        padding-left: 7px;
    }

/*  ══ v31.330 (owner) — ACTIONS IN EQUAL PAIRS ══════════════════════════════
    *"Buttons should span width of card with equal size where possible."*

    ⚠⚠⚠ THIS OVERRIDES `.doc-review .chips`, WHICH IS A FLEX ROW WITH
    `flex: 0 0 auto` ON EVERY CHIP — so the grid declaration alone would be
    ignored on the rail where the owner is looking. The class is applied WITHOUT
    `.chips` on the elements it governs, and the chip rule is restated here for
    the same specificity reason. */
/*  ⚠⚠⚠ AND THE SCOPED COPY IS NOT DUPLICATION — IT IS THE FIX. The first
    draft declared these unscoped and the probe showed four buttons in four
    rows, not two pairs: `.doc-review .doc-acts` is `display: flex` at
    specificity (0,2,0) and `.doc-pair` is (0,1,0), so the grid never applied in
    the one place the owner was looking. `width: 100%` on each chip then made
    the flex row wrap one per line, which looks deliberate and is not. */
.doc-pair, .doc-stack,
.doc-review .doc-pair, .doc-review .doc-stack {
    display: grid; gap: 8px; align-items: stretch;
}
.doc-pair, .doc-review .doc-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.doc-stack, .doc-review .doc-stack { grid-template-columns: minmax(0, 1fr); }
    /*  ⚠ A CHIP IS NORMALLY A FIXED-HEIGHT INLINE PILL. Inside a pair it is a
        grid item that fills its track, and these labels are sentences — "Save
        without the bank details" — so it has to be allowed to grow downwards
        rather than printing outside its own box. */
    .doc-pair > .chip, .doc-stack > .chip, .doc-pair > form, .doc-stack > form,
    .doc-review .doc-pair > .chip, .doc-review .doc-stack > .chip,
    .doc-review .doc-pair > form, .doc-review .doc-stack > form {
        display: inline-flex; align-items: center; justify-content: center;
        width: 100%; box-sizing: border-box;
        white-space: normal; text-align: center;
        height: auto; min-height: 34px; line-height: 1.25; padding: 6px 12px;
    }
    /*  ⚠⚠ A FORM IS A GRID ITEM TOO — the USE THIS pill on the match card sits
        in one, and a form that did not fill its track would leave its button
        narrower than the chip beside it. */
    .doc-pair > form > .chip, .doc-stack > form > .chip { width: 100%; }
/*  ⚠ v31.330 (owner) — *"Read this document and From Email should be same size
    on two rows."* The submit lives inside the upload form rather than in the
    stack, so it is widened by name. */
.doc-wide {
    display: inline-flex; align-items: center; justify-content: center;
    width: 100%; box-sizing: border-box; white-space: normal; text-align: center;
    height: auto; min-height: 34px; line-height: 1.25; padding: 6px 12px;
    margin-top: 8px;
}

/*  ══ v31.331b (owner) — CODE THEN NAME, AS THE DROPDOWN DRAWS IT ══════════
    *"These should be describe Code + Name as follows in the attached example"*
    — the VAT ANALYSIS selector: a gold code, then the sentence that explains
    it. The same pair in the same order in both places, so a reader checking one
    against the other never has to re-read either.

    ⚠ THE CODE IS GOLD AND MONOSPACED because it is an identifier and because
    that is what `.t-leaf b` already is in the selector; the words take the
    ordinary ink. Neither is allowed to wrap away from the other. */
.doc-code {
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-weight: 700; font-size: 12.5px; letter-spacing: .02em;
    white-space: nowrap; color: var(--nv-gold, #e8a75c);
}
.doc-nm { margin-inline-start: 8px; color: var(--nv-ink, #f1f3f9); }

/*  ══ v31.330 (owner) — THE NARRATIVE BLOCK ═════════════════════════════════
    *"Translation is required in extended description and Description. By moving
    description to a position above the extended description."*

    ⚠⚠ FULL WIDTH AND OUT OF THE GRID. Both fields are prose; the three-column
    ladder beside them is sized for `net`, `vat` and a nominal code, and a
    sentence in one of those cells clipped to four lines. */
.doc-narr { margin-top: 9px; position: relative; }
    .doc-narr .fld-wrap { margin: 0; }
    .doc-narr .doc-desc .input { width: 100%; box-sizing: border-box; }
    /*  ⚠ THE LABEL FOR THE EDITOR GETS THE SAME GAP THE DESCRIPTION'S LABEL HAS
        above it, so the two fields read as one block rather than two. */
    .doc-xlab { display: block; margin-top: 10px; }
    /*  ⚠⚠ THE PROVENANCE MARK SITS ON THE DESCRIPTION'S OWN LINE, not above it:
        same rule as the grid, where the letter is beside the value because the
        question "where did this come from" is asked while reading it.
        ⚠⚠⚠ AND IT IS ANCHORED TO THE FIELD, NOT TO THE BLOCK. The translate
        door appends its glyph to the label row, so a marker parked at the
        top-right of `.doc-narr` sits underneath it — two controls, one place,
        and the collision only appears once a line has been edited. */
    .doc-narr .doc-desc { position: relative; }
    .doc-narr .doc-desc .input { padding-inline-end: 34px; }
    .doc-nmk {
        position: absolute; inset-inline-end: 8px; bottom: 7px;
        pointer-events: none;
    }
/*  ⚠⚠⚠ v31.330 (owner) — *"'Save this narrative' button should have top space
    so it does not merge with the bottom of the rich text editor."* The editor's
    surface has no bottom margin of its own, so the button was landing on its
    border. The gap belongs to the button's row, not to the editor, because the
    editor is shared with every other screen on this product. */
.doc-nsave { margin-top: 10px; }

.doc-review .doc-rules { font-size: 12.5px; }
.doc-review .doc-say { font-size: 12.5px; line-height: 1.45; }
.doc-review .doc-warn { font-size: 13px; }

/*  ══ v31.327 (owner) — THE INFORMATION ICON ════════════════════════════════
    *"Remove this text … and place it in the information icon."* · *"also this
    text goes int an information icon"*.

    ⚠⚠⚠ IT IS `<details>`, NOT A SCRIPT AND NOT A TOOLTIP. The browser's own
    disclosure opens with JavaScript off, is keyboard reachable with nothing
    wired up, and is announced as expandable without an aria attribute anybody
    has to remember to keep in step. A tooltip would also hide the text from
    touch and from anyone reading at a magnification where a hover never
    settles.

    ⚠⚠ AND IT IS COLLAPSED, NOT DELETED. Both blocks are true and worth saying
    once — the three provenance rules and what happens on accept. Printed on
    every proposal beside a count somebody is driving to zero, they are
    paragraphs of comfort in the way of the work. */
.doc-info { margin-top: 11px; }
    .doc-info > summary {
        display: flex; align-items: center; gap: 7px;
        font-size: 12.5px; color: var(--nv-ink-2, #aab3c8);
        cursor: pointer; list-style: none; padding: 2px 0;
    }
        /*  ⚠ SAFARI AND OLD WEBKIT DRAW THEIR OWN TRIANGLE through a
            pseudo-element the standard property does not reach. */
        .doc-info > summary::-webkit-details-marker { display: none; }
        .doc-info > summary:hover { color: var(--nv-ink, #f1f3f9); }
        .doc-info > summary:focus-visible {
            outline: 2px solid var(--nv-gold, #e8a75c);
            outline-offset: 2px; border-radius: 4px;
        }
    /*  ⚠⚠ THE GLYPH IS THE SAME SHAPE AND SIZE AS A PROVENANCE CHIP, on
        purpose: this screen has one vocabulary of small round-cornered marks,
        and a second one would read as a second kind of thing. */
    .doc-i {
        flex: 0 0 auto;
        display: inline-flex; align-items: center; justify-content: center;
        width: 17px; height: 17px;
        border-radius: 50%;
        border: 1.5px solid currentColor;
        font-size: 10px; font-weight: 700; font-style: italic;
        line-height: 1;
    }
    .doc-info[open] > summary { color: var(--nv-ink, #f1f3f9); margin-bottom: 4px; }
    .doc-info > .doc-rules,
    .doc-info > .doc-say { margin-top: 0; padding-left: 24px; }

/*  ══ ⚠⚠⚠ v31.338 (owner) — THE LIST OF WHAT IS HOLDING POST BACK ══════════
    *"have an information item that lists the items preventing the post"*

    ⚠⚠ IT INDENTS TO THE SAME 24px AS THE OTHER TWO PANELS, so the icon above
    it governs all three and the rail keeps one left edge.

    ⚠⚠⚠ AND IT WRAPS INSTEAD OF SCROLLING. This rail is 300px at 1366 and the
    narrow screens give it the full width; a reason clipped at the right edge
    is the one word the reader needed. Three items per row is the natural
    reading order - where, what, why - and each holds together when the row
    breaks, which is what the inline-flex on the parts is for. */
.doc-info > .doc-held {
    margin: 0; padding: 0 0 0 24px;
    list-style: none;
    display: flex; flex-direction: column; gap: 6px;
}
    .doc-info > .doc-held > li {
        display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 7px;
        font-size: 12.5px; line-height: 1.35;
    }
        /*  ⚠⚠ THE LOCATION IS A FIXED-WIDTH COLUMN so a header row and a line
            row start their field names at the same x. Fourteen items that do
            not line up read as fourteen unrelated sentences. */
        .doc-info > .doc-held .doc-held-w {
            flex: 0 0 auto; min-width: 54px;
            color: var(--nv-ink-2, #aab3c8);
            font-size: 11.5px; letter-spacing: .02em;
            font-variant-numeric: tabular-nums;
        }
        .doc-info > .doc-held > li > b {
            flex: 0 1 auto;
            color: var(--nv-ink, #f1f3f9); font-weight: 600;
            overflow-wrap: anywhere;
        }
        /*  ⚠ THE REASON TAKES WHAT IS LEFT and wraps under the pair rather
            than pushing them off the rail. */
        .doc-info > .doc-held > li > .muted {
            flex: 1 1 100%;
            padding-left: 61px;
            font-size: 11.5px;
        }
@media (max-width: 520px) {
    /*  ⚠ ON A PHONE THE RAIL IS THE PAGE WIDTH and 61px of indent on a
        wrapped reason is a column of nothing. */
    .doc-info > .doc-held > li > .muted { padding-left: 0; }
}

/*  ══ ⚠⚠⚠ v31.339 (owner) — THE TYPED REPLACEMENT ══════════════════════════
    *"Other fields should be editable to allow user input to replace document
    read values."*

    ⚠⚠⚠ IT SITS UNDER THE VALUE, INSIDE THE SAME CARD, and it is deliberately
    quieter than the value above it: the document's own reading is still the
    thing being checked, and a box with more presence than the fact it might
    replace would invite editing for its own sake.

    ⚠⚠ THE BOX TAKES THE ROW AND THE BUTTON TAKES WHAT IT NEEDS, so a long IBAN
    does not push Save off a 300px card - and the pair wraps rather than
    clipping when even that is not enough. */
.doc-edit {
    display: flex; align-items: center; gap: 6px;
    /*  ══ ⚠⚠⚠⚠ v31.340 (owner) — ON THE SAME ROW, AND THE SAME ROW AS EACH
        OTHER'S. *"Style them in such way that they appear aligned on the same
        row."*

        TWO THINGS WERE RAGGED AND ONLY ONE OF THEM WAS OBVIOUS:

          · `flex-wrap: wrap` let the Save drop UNDER its box the moment the
            two did not fit — so the pair the owner calls one control was two
            rows on a narrow card and one on a wide one. It is gone, and the
            box shrinks instead (`min-width: 0` below); the button never moves.

          · AND ACROSS THE GRID THEY FLOATED. Each card is as tall as its own
            contents — a "Needs checking" flag here, a conversion line there —
            so three boxes in one grid row sat at three different heights.
            `margin-top: auto` pins the strip to the bottom of its card and the
            grid stretches every card in a row to the tallest, which puts every
            box on one line without a fixed height anywhere. */
    margin-top: auto;
    padding-top: 7px;
}
    /*  ⚠ TABULAR FIGURES, because most of what gets retyped here is a number
        the reader is comparing digit by digit against a document. ONE block,
        not two: a selector written twice is two places to change it and one
        place to forget. */
    .doc-edit > .doc-ed,
    .doc-edit > .doc-edsel {
        flex: 1 1 60px; min-width: 0;
        padding: 4px 7px;
        font-size: 12.5px; line-height: 1.3;
        font-variant-numeric: tabular-nums;
    }
    /*  ⚠⚠ THE PICKER IS THE SAME SHAPE AS THE BOX, on purpose: one card may
        carry a typed value and the next a chosen one, and a reader scanning
        down the grid should see one kind of control, not two. */
    .doc-edit > .doc-edsel {
        /*  ⚠⚠ THE RIGHT PADDING IS THE CHEVRON'S ROOM. Without it the browser
            draws its arrow ON TOP of the option text — `Zero rate - no VAT ch⌄`
            — and the one character it hides is as likely to be part of the
            code as part of the words. */
        padding: 4px 22px 4px 6px;
        font-size: 12.5px; line-height: 1.3;
        text-overflow: ellipsis;
    }
    .doc-edit > .doc-edgo {
        flex: 0 0 auto;
        padding: 4px 10px;
        font-size: 11.5px;
        white-space: nowrap;
    }
/*  ⚠⚠ A CARD THE READER HAS ALREADY SETTLED KEEPS ITS BOX but stops drawing
    attention to it: changing your mind must stay possible, and looking like
    the next thing to do must not. */
.doc-done .doc-edit { opacity: .62; }
    .doc-done .doc-edit:focus-within { opacity: 1; }

/*  ⚠ v31.326 — THE max-width STEPS THAT USED TO SIT HERE ARE GONE. The ladder
    above is min-width and mobile-first; leaving a max-width rule beside it
    would mean two rules fighting over the same widths, and the winner would be
    whichever came last in the file. */
/*  ⚠ AT PHONE WIDTH THE GRID STACKS AND THE MATCH TABLE SCROLLS IN ITS OWN
    BOX — the page itself never scrolls sideways. */
@media (max-width: 720px) {
    table.doc-mtbl { display: block; overflow-x: auto; white-space: nowrap; }
    .doc-legend { gap: 9px; }
}

/*  ══ v31.318 — THE CHOOSE CARD IS SELF-CONTAINED ════════════════════════
    ⚠⚠⚠ A PARTIAL WITH TWO HOSTS MUST NOT INHERIT ITS LAYOUT FROM EITHER, and
    this one did. Rendered in Account Setup's 240px side rail it picked up the
    main column's styling and broke in two visible ways:

      · `.sub` resolved to 18px at full ink — that is the SCREEN HEADER's
        subtitle, not a card caption. Beside it the sibling card's own caption
        is `.acc-ba-hint`: 13px, muted.
      · `.chips` is `flex-wrap: nowrap` with `height: 32px`, and `.chip` is
        `white-space: normal` with the same fixed height — so "Choose a
        document" wrapped to a second line and rendered OUTSIDE its own box,
        over the chip beside it and the note beneath it.

    Neither shows in the markup and neither shows on the processing page, where
    the column is wide. Caught by rendering the card in its new home and
    looking at it.

    ⚠⚠ SO THE FIX IS SCOPED TO `.doc-choose` RATHER THAN APPLIED TO THE HOST.
    Changing `.sub` or `.chips` globally would move every screen that uses them
    to suit one card. */
.doc-choose .sub {
    font-size: 13px;
    line-height: 1.45;
    color: var(--nv-ink-2, #aab3c8);
}
.doc-choose .chips {
    display: flex;
    flex-wrap: wrap;          /* ⚠ the rail is narrow: the row wraps, the chip does not */
    height: auto;
    gap: 8px;
}
    .doc-choose .chip {
        white-space: nowrap;   /* ⚠⚠ a label that wraps inside a fixed-height chip
                                  renders outside it — that was the overlap */
        /*  ⚠⚠⚠ AND `nowrap` ALONE MADE IT WORSE, which is the second half of
            the same defect. `.chips` is a flex row and a flex item shrinks by
            default: two chips needing 255px in a 231px rail were both squeezed
            below their natural width, and with the text refusing to wrap it
            simply rendered outside the box — the first chip's label ran under
            the second one.

            `flex: 0 0 auto` is the half that fixes it: THE ROW WRAPS, THE CHIP
            NEVER SHRINKS. Wrapping a row costs one line of height; shrinking a
            chip costs the label. */
        flex: 0 0 auto;
        height: auto;
        min-height: 30px;
        line-height: 1.25;
        font-size: 14px;
        padding: 5px 12px;
    }
.doc-choose .doc-say { font-size: 12.5px; line-height: 1.4; }

/*  ══ v31.321 — THE BLOCK IS A RAIL CARD ═════════════════════════════════
    Owner: *"for invoice step 3 and new invoice the card should appear in the
    right column. That fixes all views"* · *"Place it below the find
    transaction card"*.

    ⚠⚠⚠ ITS NEW HOME IS THE ENTRY WIZARD'S `.ai-side`, ~350px wide — the same
    kind of column that broke `.doc-choose` in v31.318, and it breaks the same
    way for the same two reasons: `.chips` is `flex-wrap: nowrap` at a fixed
    32px, and `.chip` is `white-space: normal` and shrinks. "Process a
    document" is a longer label than either of the ones that overflowed then.

    ⚠⚠ SO THE WIDTH LEFT THE MARKUP AND LANDS HERE. Three inline
    `max-width:900px` rules said the card belonged in a wide main column; one
    rule here caps it wherever it renders and costs nothing in a rail that is
    already narrower.                                                        */
.doc-block, .doc-walk, .doc-bacts { max-width: 900px; }

/*  ⚠ THE HEADING SHRINKS WITH THE COLUMN. `h2.section` is 20px — set for a
    main column — and "Process invoice from document" at 20px takes three
    lines of a 350px rail before the card has said anything.                 */
.ai-side .doc-block h2.section { font-size: 16px; }
.ai-side .doc-walk { font-size: 13px; line-height: 1.45; }

.doc-block .chips, .doc-bacts .chips {
    display: flex;
    flex-wrap: wrap;          /* ⚠ the rail is narrow: the row wraps, the chip does not */
    height: auto;
    gap: 8px;
}
    .doc-block .chip, .doc-bacts .chip {
        /*  ⚠⚠ `nowrap` AND `flex: 0 0 auto` ARE ONE FIX, NOT TWO. Either half
            alone renders the label outside its own chip — see the `.doc-choose`
            note above, which is the round this was learned in. */
        white-space: nowrap;
        flex: 0 0 auto;
        height: auto;
        min-height: 30px;
        line-height: 1.25;
        font-size: 14px;
        padding: 5px 12px;
    }
.doc-block .doc-say { font-size: 12.5px; line-height: 1.4; }

/*  ══ v31.322 — THE CARD TAKES A FILE ════════════════════════════════════
    Owner: *"please let us proceed to round 2 workflow, uploading a
    document."*

    ⚠⚠⚠ `.doc-pickfile`, NOT `.doc-file`. `.doc-file` ALREADY MEANS the
    attachment line on the proposal — a flex row with a paperclip — and putting
    it on a file INPUT made one class name mean two things. That is the same
    collision `.doc-wait` caused in v31.318, where a step state quietly
    inherited the card's rules; one class for two things is a collision that
    only ever gets worse.                                                     */
.doc-pick { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-top: 10px; }
.doc-pickfile {
    flex: 1 1 220px;
    min-width: 0;                 /* ⚠ a flex item with a long file name must shrink, not overflow */
    font-size: 13.5px;
    padding: 6px 9px;
    height: auto;
}
    /*  ⚠ THE BROWSER'S OWN BUTTON IS THE ONE PART OF THIS CONTROL THAT CANNOT
        BE REPLACED, so it is dressed rather than fought. */
    .doc-pickfile::file-selector-button {
        margin-right: 9px;
        border: 1px solid rgba(232,167,92,.45);
        background: rgba(232,167,92,.10);
        color: var(--nv-gold, #e8a75c);
        border-radius: 5px;
        padding: 4px 10px;
        font: inherit;
        cursor: pointer;
    }

/*  ⚠⚠⚠ WHAT WENT WRONG READS AS A NOTE, NOT AS A HEADING. Unstyled it
    inherited the card's body size and came out larger than the caption above
    it, so a refusal shouted while the card's own subject whispered — caught by
    looking at the rendered page rather than at the markup. */
.doc-said {
    margin-top: 9px;
    padding: 8px 10px;
    font-size: 13.5px;
    line-height: 1.45;
    border: 1px solid rgba(232,167,92,.45);
    border-radius: 6px;
    background: rgba(232,167,92,.06);
    color: var(--nv-ink, #f1f3f9);
}

/*  ⚠⚠ THE IN-FLIGHT STRIP IS HIDDEN BY THE `hidden` ATTRIBUTE, and `display:
    flex` anywhere in its ancestry would override that. It is stated here so a
    later layout change cannot reveal a progress strip over a form nobody has
    submitted. */
.doc-flight[hidden] { display: none !important; }
.doc-flight { margin-top: 10px; }

/*  the "this document has been read here before" note — the house tint, the
    same one a likely match and the round-1 banner already use */
.doc-dup {
    display: flex; gap: 10px; align-items: baseline;
    border-color: rgba(232,167,92,.45); background: rgba(232,167,92,.06);
    max-width: 900px;
}
    .doc-dup a { color: var(--nv-gold, #e8a75c); margin-left: 6px; }

/*  ══ v31.323 — THE ACCOUNT PANEL IS A CONTROL NOW ═══════════════════════
    Owner: *"Using VAT number, company number, and name as mandatory key is
    restrictive. Let us start with reading details irrespective of the match.
    and having a pill USE THIS to allow user intervention."*                */
.doc-panel { max-width: 900px; }

/*  ⚠⚠ THE SEARCH IS A ROW THAT WRAPS, like every other control in this
    feature — the panel renders in the entry wizard's ~350px rail as well as in
    a main column, and a fixed-width row would overflow one of them. */
.doc-find { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 9px 0 11px; }
    .doc-find .input { flex: 1 1 220px; min-width: 0; height: 34px; font-size: 13.5px; }
    .doc-find .chip { flex: 0 0 auto; white-space: nowrap; }

/*  ⚠ THE PILL'S FORM MUST NOT BECOME A BLOCK in the middle of a chip row. A
    bare <form> is display:block and would push every chip after it onto its
    own line — which is how "USE THIS" ends up alone above "Update this
    account". */
.doc-use { display: inline-flex; margin: 0; }

/*  ⚠⚠⚠ WHAT IS IN USE LOOKS DIFFERENT FROM WHAT IS SUGGESTED, and it is the
    gold edge rather than a tint alone, because a reader who has already chosen
    must see that at a glance. A panel where the decision looks like one more
    candidate is a panel where somebody presses a second pill without noticing
    the first. */
.doc-chosen {
    border-color: rgba(232,167,92,.70);
    background: rgba(232,167,92,.09);
    box-shadow: inset 3px 0 0 0 var(--nv-gold, #e8a75c);
}

/*  ⚠⚠ A SEARCH RESULT IS NEITHER A MATCH NOR A NON-MATCH, so it gets neither
    the gold of a likely match nor the green of "nothing on file". It is a
    plain card with a plain edge, which is the honest weight for something a
    person typed. */
.doc-found { border-color: rgba(255,255,255,.16); background: rgba(255,255,255,.02); }

.doc-panel .doc-acts { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
    .doc-panel .doc-acts .chip { flex: 0 0 auto; white-space: nowrap; height: auto; min-height: 30px; }

/*  ══════════════════════════════════════════════════════════════════════════
    v31.343 (owner, change-request item 1) — NOTE SOURCE, AND DRAFT TRANSACTIONS
    ══════════════════════════════════════════════════════════════════════════ */

/*  ⚠⚠ "Filed automatically" sits inside `.row .txt .s`, which already sets the
    type and the ellipsis for that line. This rule therefore sets COLOUR ONLY —
    a font-size here would take the whole second line off the house pair, which
    is exactly the v31.158 fault (*"the fix was a deletion"*). */
.nt-auto { color: var(--nv-blue-bright, #8fb4f2); }

/*  ⚠ The source chips are the house `.chips.nv-tabs`, so they inherit the
    brand chip and its gold `on` state; only the count needs a rule. */
.nt-src { margin: 0 0 10px; }
.nt-src .nt-mn { opacity: .62; margin-left: 4px; font-variant-numeric: tabular-nums; }

/*  ── the drafts list ───────────────────────────────────────────────────── */
.fd-kinds { margin: 0 0 12px; }
.fd-kinds .fd-n { opacity: .62; margin-left: 5px; font-variant-numeric: tabular-nums; }

.fd-none { padding: 18px 4px; text-align: center; }

/*  ⚠⚠ THE KIND IS A WORD, NOT AN ICON. Four document kinds that differ only by
    glyph are four glyphs a reader has to learn; the word costs one column and
    needs no legend. It takes the `.ic` slot the house row already reserves. */
.fd-kind {
    display: flex; align-items: center; justify-content: center;
    /*  ⚠ v31.374 (owner) — 110px, up from 74. The four kind words are
        different lengths (INVOICE · PAYMENT · RECEIPT · JOURNAL) and at 74 the
        column moved with them, so the account names below never started in the
        same place twice. */
    min-width: 110px;
}

/*  ══════════════════════════════════════════════════════════════════════════
    ⚠⚠⚠ v31.374 (owner) — THE KIND SLOT LOSES ITS FILL, ON THIS PAGE ONLY.

    Owner: *"remove background … Check that this does not affect pills in other
    pages. If so, list those pages for me."*

    IT WOULD HAVE. The background he quoted is not a drafts rule at all — it is
    `.row .ic` in **tokens.css**, the design system, and it paints the icon slot
    of every list row in the app. Seven pages put an `.ic` inside a `.row`:
    Assistant, the mailbox, Home, Notes, Compose, Message and this one.
    Commenting it out there would have stripped the fill from six screens
    nobody asked about.

    ⚠⚠ SO IT IS OVERRIDDEN HERE RATHER THAN DELETED THERE, which is also the
    pattern the app already uses — `.ai-side .row .ic` has done exactly this
    since the side cards were built. One word from the owner makes it
    house-wide; until then the token keeps its meaning everywhere else.
    ══════════════════════════════════════════════════════════════════════════ */
.fd-row .ic {
    background: transparent;
}

.fd-klabel {
    font-size: var(--fs-caption, 12px); letter-spacing: .04em;
    text-transform: uppercase; opacity: .78;
    border: 1px solid rgba(255,255,255,.14); border-radius: 999px;
    padding: 3px 9px; white-space: nowrap;
    /*  ⚠ v31.374 — the pill is a fixed width and centres its word, so INVOICE
        and JOURNAL are the same shape rather than two sizes of pill down the
        column. `.fd-kind` is `display:flex`, so this span is a flex item and
        min-width applies to it. */
    min-width: 105px;
    text-align: center;
}
.fd-kind[data-kind="payment"] .fd-klabel { border-color: rgba(232,167,92,.42); }
.fd-kind[data-kind="receipt"] .fd-klabel { border-color: rgba(104,152,232,.46); }
.fd-kind[data-kind="journal"] .fd-klabel { border-color: rgba(255,255,255,.26); }

/*  ⚠ The reference rides the title line and must be allowed to shrink before
    the account name does — `min-width: 0` because a flex item will not shrink
    below its content without it (the v31.159 trap). */
/*  ══════════════════════════════════════════════════════════════════════
    ⚠⚠⚠ v31.372 (owner) — THE DRAFT ROW IS AN ANCHOR AND WAS DRESSED LIKE ONE.

    Owner: *"please remove the unsightly purple/blue underlines in the rows.
    This is not a style we have anywhere in the numerous grid rows we have
    build."*

    He is right that it is nowhere else: `a.mail` — the mailbox row this one was
    modelled on — opens with `text-decoration: none; color: inherit`, and the
    drafts row never did. So every account name, amount and subtitle on the
    sheet was rendering in the browser's default link blue, underlined, and
    VISITED ROWS WENT PURPLE, which is why the two in his screenshot differ
    from each other.

    ⚠⚠ IT HAS TO BE ON THE ANCHOR ITSELF. `text-decoration` inherits into
    descendants and a descendant CANNOT cancel it — a rule on `.fd-row .t`
    would have looked correct and changed nothing. That is this project's own
    recorded lesson from item 8 (the bank-rec strike-through), and it applies
    here in reverse.
    ══════════════════════════════════════════════════════════════════════ */
a.fd-row {
    text-decoration: none;
    color: inherit;
}

.fd-row .t { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.fd-ref { opacity: .66; font-size: var(--fs-caption, 12px); white-space: nowrap; }

.fd-amt { display: flex; align-items: baseline; gap: 6px; white-space: nowrap; }
.fd-tot { font-variant-numeric: tabular-nums; }
.fd-cur { opacity: .6; font-size: var(--fs-caption, 12px); }

/*  01-Screen: the kind word and the amount keep their places; the middle
    column is what gives way. */
@media (max-width: 519.98px) {
    .fd-kind { min-width: 0; }
    .fd-klabel { padding: 2px 7px; }
    .fd-ref { display: none; }
}

/*  ══════════════════════════════════════════════════════════════════════════
    v31.347c (owner) — THE PENCIL SITS LEFT OF A `.vsel` CHEVRON

    Owner: *"The edit pen image and the drop-down chevron appear in the same
    space. The editor pen should appear to the left of the drop-down chevron
    (use date editor as an example)."*

    `nvfe-two` already moves the pencil into its own 34px zone — that part is
    the house geometry and needs nothing new. What a `.vsel` needs on top is
    room for its OWN LABEL: the text inside the button is a flex item that runs
    to the chevron, so with the pencil now sitting at 36 the name ran straight
    underneath it. A `.fld-go` field does not have this problem because its
    value is an `<input>` whose padding the two-zone rule already widens.

    ⚠ 80px IS THE HOUSE NUMBER, not a new one: the two-zone note above says the
    value stops at 80 when there are two actions (34 for the outer zone, 34 for
    the pencil's, 12 of air). The same stop, applied to the button's own text.
    ══════════════════════════════════════════════════════════════════════════ */
.fld-wrap[data-nvfe].nvfe-two-vsel > .vsel { padding-right: 80px; }

/*  ⚠⚠ AND THE CHEVRON STAYS PINNED TO THE BUTTON'S RIGHT EDGE. It is placed by
    `margin-left: auto` inside the flex row, so the padding above would push it
    inward with the text and the two would move together — leaving the pencil
    over the chevron again, 80px further left. Taking it out of the flow keeps
    it where every other chevron in the form is. */
.fld-wrap[data-nvfe].nvfe-two-vsel > .vsel svg.chev {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    margin-left: 0;
}

.fld-wrap[data-nvfe].nvfe-two-vsel > .vsel { position: relative; }

/*  ══════════════════════════════════════════════════════════════════════════
    v31.347c (owner) — AN ACCOUNT IS A NAME AND A CODE, ON ONE LINE

    Owner: *"Separate AccountName, Account Code by having two fields whenever
    search accounts is needed for header and lines in Posted Invoice, Payments
    and Receipts and Journal Entry. Both fields should be on the same line."*

    ⚠⚠⚠ THE `.fld-go` WRAPS THE NAME BOX ALONE, DELIBERATELY. `_NvFieldEdit`
    hosts its pencil on the value input's own `.fld-go`, so keeping the code box
    OUTSIDE it is what puts the pencil inside the field it unlocks instead of
    on top of the code beside it. Wrapping both would have reproduced the
    overlap this same round just fixed on the chevron.

    ⚠ THE CODE IS NARROWER AND FIXED. An account code is a known shape
    (`A-321-08040`) and the NAME is the part that varies and needs the room, so
    the name takes the slack and the code keeps a predictable column — which is
    also what makes a stack of lines read as a column rather than a ragged edge.
    ══════════════════════════════════════════════════════════════════════════ */
/*  ⚠⚠ v31.347d (owner) — THE CODE COLUMN'S WIDTH IS ONE NUMBER, DECLARED ONCE.
    Owner asked for a wider code box *"or the best way to achieve this css
    throughout"*. A custom property on the row is that way: every instance of
    this pattern — invoice header, invoice line, payment header, payment line,
    journal line — reads the same token, so the column is changed in one place
    and a surface that genuinely needs a different width can override the token
    on its own wrapper instead of redeclaring the rule. */
.nv-acc2 {
    --nv-acc-code-w: 207px;
    display: flex;
    align-items: center;
    width: 100%;
    min-width: 0;
}

    /*  ⚠ `min-width: 0` IS LOAD-BEARING: a flex item will not shrink below its
        content without it, so a long account name would push the code box off
        the row instead of ellipsing. The project has paid for this one twice
        (v31.159, v31.343). */
    .nv-acc2 > .nv-acc-name {
        flex: 1 1 auto;
        min-width: 0;
        /*  ⚠ THE GAP IS THE NAME'S MARGIN, NOT THE ROW'S `gap`. The owner wrote
            it that way and it is the better shape here: the code box can then
            take the full token width with nothing subtracted from it, and the
            01-Screen rule below drops the margin with the row rather than
            leaving a stray 5px above a wrapped field. */
        margin-right: 5px;
    }

    .nv-acc2 > .nv-acc-code {
        flex: 0 0 auto;
        width: var(--nv-acc-code-w, 207px);
        min-height: 32px;
        font-variant-numeric: tabular-nums;
        color: var(--nv-ink-2);
    }

/*  01-Screen and 02-Screen: the code keeps its own line rather than squeezing
    the name into nothing. The owner asked for one line, and one line is what a
    screen with room gets; below 520px there is no room to honour it without
    making both fields useless. */
@media (max-width: 519.98px) {
    .nv-acc2 { flex-wrap: wrap; }
    .nv-acc2 > .nv-acc-name { margin-right: 0; }
    .nv-acc2 > .nv-acc-code { width: 100%; margin-top: 6px; }
}

/*  ⚠ 02-Screen and 03-Screen: one line still, but 207px of a 768px column is a
    third of the row. The token shrinks; no rule is repeated. */
@media (min-width: 520px) and (max-width: 1023.98px) {
    .nv-acc2 { --nv-acc-code-w: 150px; }
}

/*  ══ v31.348 (owner) — THE POSTED INVOICE HEADER'S CODE BOX IS COLUMN THREE ══
    Owner: *"the account code control should be left-aligned as with the next
    control below it… This should only apply to Posted invoices header and not
    the other pages."*

    The row beneath it is a `.fin-grid3`: `1fr 1fr 1fr` with `gap: 12px`. So the
    third column's left edge is at `2/3` of the row plus a gap, and a code box
    that wants to sit under it must be `(100% - 2 × 12px) / 3` wide with a 12px
    gutter — not a fixed 207px, which only happens to look right at one width.

    ⚠ IT OVERRIDES THE TOKEN, IT DOES NOT REPLACE THE PATTERN. Every other
    `.nv-acc2` on every other surface still reads `--nv-acc-code-w`; this one
    surface asks for a measure the token cannot express, so it sets the width
    directly and leaves the token alone.
    ⚠⚠ FROM 768px ONLY. Below that `.fin-grid3` collapses to a single column, so
    there is no third cell to align with and the inherited rules — the 150px
    token, then the wrap below 520px — are the right answer again. */
@media (min-width: 768px) {
    .nv-acc2.nv-acc2-grid3 > .nv-acc-name {
        margin-right: 12px;
    }

    .nv-acc2.nv-acc2-grid3 > .nv-acc-code {
        width: calc((100% - 24px) / 3);
    }
}

/*  ══ v31.363 (owner / users) — COMPLETED on a triage card ═════════════════
    Owner: *"please add a completed button which allows for removal of the
    email from that card's triage."*
    ⚠ It sits beside the existing select box and borrows that box's metrics,
    so the right edge of every AI row keeps one rhythm. Quiet until reached:
    the card's job is still to be read down, and a tick on every row shouting
    in green would compete with the one thing the row is for. */
.mail .tri-done {
    flex: 0 0 auto;
    align-self: center;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin: -6px 0 -6px 2px;
    padding: 0;
    border: 1px solid var(--nv-line, rgba(255,255,255,.14));
    border-radius: 50%;
    background: transparent;
    color: var(--nv-ink-3);
    cursor: pointer;
    transition: color .12s, border-color .12s, background .12s;
}

    .mail .tri-done:hover,
    .mail .tri-done:focus-visible {
        color: var(--nv-ok, #4caf7d);
        border-color: var(--nv-ok, #4caf7d);
        background: rgba(76, 175, 125, .10);
    }

/*  ⚠⚠ THE TOAST IS `pointer-events: none` so it never blocks the list under
    it — which would also have made Undo unclickable. The button alone takes
    its events back. */
.mail-toast .t-undo {
    pointer-events: auto;
    margin-left: auto;
    padding: 4px 12px;
    border: 1px solid currentColor;
    border-radius: 999px;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 14px;
    cursor: pointer;
}

/*  ══ v31.364–372 (owner) — DRAFT TRANSACTIONS: THE RIGHT-HAND CARDS ══════
    v31.364 put the cards in a grid of their own; v31.365 gave them a bespoke
    set of classes. BOTH ARE GONE. Owner: *"The cards in the right have side
    panels are very plain and devoid of the styling we have used so far."*
    They now use the app's own card vocabulary — `h2.section` with the gold
    stop, `.kv`/`.k`/`.v`, `.fin-total`, `.chip` — so there is nothing left to
    declare here, and the invented classes are deleted rather than left for
    somebody to find and reuse. ⚠ THE DELETE ICON'S RULES SURVIVE, below: that
    control is genuinely this page's own. */

/*  ══ v31.372 — THE VIEW PILL IS `.chip` NOW, and the entity rows are `.kv`.
    What follows is the one control on this page that has no house equivalent. */

/*  ⚠⚠ DELETE — quiet until reached, LOUD once armed. The first press turns it
    red; the second press is the one that posts. The colour change is the
    confirmation, so it has to be unmissable against a row the reader is still
    looking at. */
.fd-row .fd-del {
    flex: 0 0 auto;
    align-self: center;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin-left: 6px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 50%;
    background: transparent;
    color: var(--nv-ink-3);
    cursor: pointer;
    transition: color .12s, border-color .12s, background .12s;
}

    .fd-row .fd-del:hover,
    .fd-row .fd-del:focus-visible {
        color: var(--nv-danger, #e0695f);
        border-color: var(--nv-line, rgba(255,255,255,.18));
    }

    .fd-row .fd-del.armed {
        color: #fff;
        background: var(--nv-danger, #e0695f);
        border-color: var(--nv-danger, #e0695f);
    }

/*  ══════════════════════════════════════════════════════════════════════════
    v31.383 (owner) — THE SEARCH CONTROL IS THE BOX **AND THE TREE**

    *"The search box is so small that the way to enhance user experience is to
    elongate the search box when it appears to cover the full height of the
    right column and that space now would be exclusively dedicated to the
    elongated search control. The other two cards move to the middle as
    already shown."*

    ⚠⚠⚠ THREE ROUNDS READ "THE SEARCH CONTROL" AS THE FIND BOX ALONE, AND ALL
    THREE WERE WRONG. The control is the box TOGETHER WITH THE ACCOUNTS TREE —
    the tree is what a search produces and what the reader then navigates, so
    it is the part that was too small, and it is the part that gets the
    column's full height. The two cards that move to the middle are the
    RESULTS LIST and FREQUENTLY USED. Written out because each wrong version
    looked like a fair reading of the sentence on its own.

    ⚠⚠ THE BROWSE COLUMN STAYS A BOX, which is what makes this simple. It
    keeps its flex column and holds exactly two things while a search is
    running; the tree takes `flex: 1` and therefore every pixel the box has
    left. The two cards that leave are rendered in the middle by
    `_NtResults.cshtml` and `_NtFreq.cshtml` instead — no `display: contents`,
    no per-child placement, nothing promoted out of the aside.

    ⚠ AND THE COLUMN IS AS TALL AS THE PAGE because the grid row it sits in is
    as tall as the note plus the cards underneath. `align-self: stretch` is
    what makes the aside take that height rather than its own content's.
    ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 768px) {
    /*  ⚠ EVERY CHILD IS PLACED AT EVERY WIDTH THE GRID EXISTS AT. Measured at
        1280 in an earlier round: with the placement behind a 1366 query,
        auto-placement put a middle card in the rail's cell and pushed the
        whole browse column into the middle of the page. */

    /*  ⚠⚠⚠ v31.385 — THREE ROWS, NOT TWO, AND THAT IS A FIX. v31.384 put the
        results card and Frequently Used BOTH in row 2 of column 1, which is
        ONE CELL: on 02-Screen and 03-Screen the two cards were drawn on top of
        each other. Measured at 820 and at 1215 — identical x and y on both.
        It only looked right from 1366 up, where the second column pulls them
        apart. The middle is one column below that, so they take a row each. */
    .nt-work.nt-finding { grid-template-rows: auto auto auto; }

    .nt-work.nt-finding > .nt-note { grid-column: 1; grid-row: 1; }
    .nt-work.nt-finding > .nt-browse { grid-column: 2; grid-row: 1 / 4; align-self: stretch; }

    /*  ⚠⚠ v31.384 (owner) — THE RESULTS CARD REACHES THE SAME BOTTOM. He
        marked the empty space under it in red: *"The space in red should have
        a relevant card and bottom same."* The relevant card is the results
        list itself, so it STRETCHES to the row rather than a new card being
        invented to fill the gap — and the space it gains is list space, which
        is the same ask as the pager above: show more before paging. */
    .nt-work.nt-finding > .nt-list {
        grid-column: 1;
        grid-row: 2;
        align-self: start;
        display: flex;
        flex-direction: column;
        min-height: 0;
    }
    /*  ⚠ ONE ROW EACH below 1366. "Bottom same" is a 04/05-Screen arrangement:
        it is what two cards SIDE BY SIDE have to do. Stacked, there is nothing
        to line up with, so each sizes to its own content. */
    .nt-work.nt-finding > .nt-freq { grid-column: 1; grid-row: 3; align-self: start; }

    /*  the rows take the height the card gained; the heading, the filter chips
        and the pager bar keep theirs. */
    .nt-work.nt-finding > .nt-list > .section,
    .nt-work.nt-finding > .nt-list > .chips,
    .nt-work.nt-finding > .nt-list > .nt-listpager { flex: 0 0 auto; }
    .nt-work.nt-finding > .nt-list > .fin-hscroll { flex: 1 1 auto; min-height: 0; }

    /*  ⚠⚠ THE ELONGATION ITSELF. The cap is what kept the tree at 540px in a
        column that is now its own; `flex: 1 1 auto` then hands it everything
        the search box has not taken. The rows still scroll INSIDE it when a
        tree is longer than the page — the gain is that the box is now the
        height of the page rather than a third of it. */
    .nt-work.nt-finding #ntTreeCard.nt-treefix {
        max-height: none;
        flex: 1 1 auto;
        min-height: 0;
    }

    /*  ⚠⚠⚠ THE OTHER CAP, AND IT IS THE ONE THAT ACTUALLY BOUND THE ROWS.
        The scroller also carries `.acc-tree-body` from the shared tree
        toolkit, which caps it at `min(50vh, 400px)`. The owner's
        `min-height: 98%` on `.nt-treescroll` was defeating that cap — min
        beats max in CSS — which is why his one line fixed the desktop view.
        Saying it outright here as well, so the column's height does not depend
        on a percentage out-arguing a maximum. */
    .nt-work.nt-finding #ntTreeCard .nt-treescroll { max-height: none; }
}

/*  ══ THE MIDDLE HOLDS THE TWO CARDS, SIDE BY SIDE ═════════════════════════
    Results on the left, Frequently Used on the right — the arrangement in the
    owner's own mark-up of the screen. Below 1366 the middle is one column and
    they stack, which is what that width can hold. */
@media (min-width: 1366px) {
    .nt-work.nt-finding { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 340px; }

    .nt-work.nt-finding > .nt-note { grid-column: 1 / 3; }
    .nt-work.nt-finding > .nt-browse { grid-column: 3; }

    .nt-work.nt-finding { grid-template-rows: auto auto; }
    .nt-work.nt-finding > .nt-browse { grid-row: 1 / 3; }

    /*  side by side, and only here do their bottoms have to agree. */
    .nt-work.nt-finding > .nt-list { grid-column: 1; grid-row: 2; align-self: stretch; }
    .nt-work.nt-finding > .nt-freq { grid-column: 2; grid-row: 2; }
    .nt-work.nt-finding > .nt-noresult { grid-column: 1 / 3; grid-row: 2; }
}

/*  ══ 01-SCREEN — THE WHOLE RESULT, NO INNER SCROLLER ══════════════════════
    *"Amend also the other 01-Screen to 04-Screen media css so that viewable
    results is always maximised."*

    ⚠⚠ ON A PHONE THE PAGE IS THE SCROLLER AND THE CAP IS PURE LOSS. There is
    no column to fill and no neighbour to push off the screen — the 540px box
    just hides accounts behind a second scrollbar inside a page that already
    scrolls. Dropping it shows every result, which is the whole ask, and the
    pager disappears with it because the toolkit draws no bar once the rows
    are not more than the page size.

    ⚠ IT IS THE SEARCH STATE ONLY. With no query the card keeps the cap the
    owner set at v31.220 and v31.227. */
/*  ⚠⚠⚠ v31.386 — AND THIS BLOCK IS WHAT BROKE THE PHONE IN v31.385. It set
    `min-height: 0` on the scroller to undo the owner's 98%, on the reasoning
    that a phone page does not need a minimum. But the 98% was the only thing
    holding `.acc-tree-body`'s `min(50vh, 400px)` cap open, so zeroing it put
    the cap straight back: a 400px box with an inner scrollbar, a row clipped
    at the fold, a three-page pager and empty space under the card — exactly
    the screenshot he sent. **Removing a declaration is a change, not a
    neutral act; this one removed a fix.**

    The cap itself is what has to go on a phone. With no maximum and no inner
    scroller the card ends where the accounts end, the page scrolls as a phone
    page does, and the pager disappears because every row is on screen. */
@media (max-width: 767.98px) {
    .nt-work.nt-finding #ntTreeCard.nt-treefix { max-height: none; }
    .nt-work.nt-finding #ntTreeCard .nt-treescroll {
        max-height: none;
        overflow-y: visible;
        min-height: 0;
    }
}

/* ═══════════════════════════════════════════════════════════════════════════
   v31.391 — THE INVESTMENTS MODULE (`ivt-` namespace)
   Built from the owner's spec (Version 4) and the locked mock-up at
   docs/investments/mockup/index.html, which governs 03-Screen and up; the
   01/02-Screen layouts follow the house ladder (ruling 10: fully responsive).
   One namespace, appended, nothing above this line touched.
   ═══════════════════════════════════════════════════════════════════════════ */
.ivt-scroll { gap: 14px; }
.ivt-head .sub { color: var(--nv-ink-2); font-size: var(--fs-caption); font-weight: 200; }

/* the module navigation — chips, horizontally scrollable on a phone */
/* v31.393 — the `.ivt-nav` chip row is gone (owner); `_InvPanel` is the menu */

/* trader view */
.ivt-traders { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 18px; }
.ivt-tlab { font-size: 12.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--nv-ink-2); font-weight: 300; }
.ivt-tcards { display: flex; gap: 8px; flex-wrap: wrap; }
.ivt-tcard {
    height: 38px; min-width: 84px; padding: 0 16px; border-radius: 10px; display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid rgba(104,152,232,.28); color: var(--nv-ink-2); text-decoration: none; font-size: 15px; font-weight: 300;
    background: linear-gradient(160deg,rgba(120,165,240,.08),rgba(255,255,255,.02));
}
.ivt-tcard.on { background: linear-gradient(135deg,#c9933f,#e1a65c); border-color: #e1a65c; color: #1a1408; font-weight: 500; }

.ivt-thint { color: var(--nv-ink-2); font-size: 13.5px; display: none; }
.ivt-thintrow { margin-left: auto; display: inline-flex; align-items: center; gap: 0; white-space: nowrap; }   /* v31.409: hint + flag stay on one line; the chips are what wrap */
@media (min-width: 768px) { .ivt-thint { display: inline; } }
/* v31.409 (owner): the currency FLAG after the hint — a plain mark on the dashboard, a button
   that reveals the (otherwise hidden) currency chips on every other page */
/* v31.410 (owner): the flag inside a bordered chip — the house chip, plain (never the gold
   `.on` look) — with a clear space after "in" */
.ivt-curflag { display: inline-flex; align-items: center; gap: 7px; font: inherit; font-size: 12.5px; color: var(--nv-ink); height: 26px; padding: 0 9px 0 7px; margin-left: 8px; border-radius: var(--r-pill, 999px); border: 1px solid rgba(104,152,232,.28); background: linear-gradient(160deg,rgba(120,165,240,.08),rgba(255,255,255,.02)); box-shadow: inset 0 1px 0 rgba(255,255,255,.08); white-space: nowrap; }
.ivt-curflag b { font-weight: 400; letter-spacing: .02em; }

/*  ⚠⚠⚠ v31.520 — THE INVESTMENTS INFORMATION CONTROL IS GONE FROM HERE, and the panel
    it declared is now the HOUSE one. The owner ruled one style across the application and
    chose THIS pop-up as the shape to keep — *"please use the Investment dashboard style
    'pop-up' as the starting point and change the entire system to use this style"* — with
    a gold border, 13.5px and 1px letter-spacing. So its declarations moved, with the
    measurement that earned them, into `Pages/Shared/Toolkit/_NvInfo.cshtml`, including the
    phone rule: a 260px panel does not fit beside the icon on a 320px screen whichever end
    it is pinned to, so below 768 it is a bar across the bottom, clear of the nav.
    ⚠ What changed on these screens is the MECHANISM, not the look: a `<details>` became
    the house button, so one control opens the same way everywhere. */
.ivt-flagsvg { width: 19px; height: 13px; border-radius: 2px; display: block; box-shadow: 0 0 0 1px rgba(255,255,255,.18); }
.ivt-curflag-btn { cursor: pointer; }
.ivt-curflag-btn:hover, .ivt-curflag-btn[aria-expanded="true"] { border-color: rgba(104,152,232,.55); color: var(--nv-ink); }
.ivt-curflag-btn:focus-visible { outline: 1px solid rgba(232,167,92,.7); outline-offset: 1px; }
.ivt-curchips-pop { margin-left: 0; }
.ivt-curchips-pop[hidden] { display: none; }
.ivt-curchips-pop .chip { display: inline-flex; align-items: center; gap: 6px; }
.ivt-curchips-pop .ivt-flagsvg { width: 17px; height: 12px; }

.ivt-curflag-s { font-size: inherit; gap: 5px; vertical-align: -1px; height: 22px; margin-left: 2px; padding: 0 7px 0 6px; }
.ivt-curflag-s .ivt-flagsvg { width: 17px; height: 12px; }

/* notices */
.ivt-notice { padding: 10px 16px; font-size: 14px; color: var(--nv-ink-2); display: flex; gap: 8px; flex-wrap: wrap; align-items: baseline; }
.ivt-notice b { color: var(--nv-ink); font-weight: 500; }
.ivt-notice.ivt-warn { border-color: rgba(225,166,92,.55); }
.ivt-pricenote { font-size: 13px; color: var(--nv-ink-2); padding: 0 4px; }
.ivt-pricenote.warn, .warn { color: #e1a65c; }
.ivt-msg { font-size: 14.5px; color: var(--nv-ink); border-color: rgba(63,202,133,.45); }
.ivt-msg[class*="ivt-msg-err"], .ivt-msg-oversell, .ivt-msg-qty, .ivt-msg-amount, .ivt-msg-fx, .ivt-msg-security, .ivt-msg-broker, .ivt-msg-trader, .ivt-msg-duplicate,
.ivt-msg-sum100, .ivt-msg-name, .ivt-msg-range, .ivt-msg-max5, .ivt-msg-base, .ivt-msg-cur, .ivt-msg-rate_bad, .ivt-msg-currency, .ivt-msg-unavailable, .ivt-msg-secfields, .ivt-msg-brokerfields,
.ivt-msg-watchfields, .ivt-msg-namefield, .ivt-msg-traderfields, .ivt-msg-reviewtext, .ivt-msg-already, .ivt-msg-cash { border-color: rgba(225,166,92,.6); }

/* actions row */
.ivt-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
.ivt-actions .ivt-inline { margin-right: auto; display: flex; gap: 8px; align-items: center; }
.ivt-actions .ivt-version { margin-right: auto; font-size: 13.5px; }
.ivt-cta { width: auto; height: 44px; padding: 0 20px; font-size: 16px; }
.ivt-actions .btn.h34 { width: auto; padding: 0 14px; font-size: 15px; }
/* v31.411 (owner): a pair of equal CTAs (Holdings: Refresh prices · + Buy / Sell) */
.ivt-ctapair { display: inline-flex; gap: 10px; align-items: center; }
.ivt-ctapair .ivt-ctaform { display: contents; }
.ivt-ctapair .ivt-cta { min-width: 158px; height: 44px; min-height: 44px; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; line-height: 1; }

/*  v31.482 (owner, red boxes on his 01-Screen photo: "make the pills same
    size") — 01-SCREEN ONLY. Both rows are pill rows whose members sized
    themselves to their own text: a trader card is `min-width: 84px` plus
    padding, so Rubin came out narrower than Catalina, and the CTA pair's
    `min-width: 158px` floor is wider than half a 320px screen, so Refresh
    prices and + Buy / Sell could not match either. A grid of equal columns
    settles both; the text ellipsises rather than widening its cell.
    ⚠ Three columns, not auto-fit: auto-fit would re-flow to two columns on a
    narrow phone and the LAST row would stretch, which is the same unequal
    pill in a different disguise. */
@media (max-width: 767.98px) {
    .ivt-tcards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%; }
    .ivt-tcard { min-width: 0; padding: 0 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: grid; }
    .ivt-tcard > span { overflow: hidden; text-overflow: ellipsis; }
    /*  ⚠ auto-flow COLUMN with `grid-auto-columns: 1fr`, not `1fr 1fr` and not
        flex. A fixed two-column template would strand + Buy / Sell in half a
        row whenever the feed is unconfigured and Refresh is not rendered at
        all; and flex `1 1 0` measured 169 against 171 here — the two children
        are a <button> inside a display:contents <form> and a bare <a>, and the
        free space did not divide evenly between them. Grid tracks are sized by
        the container, so both cells are equal by construction, at any count. */
    .ivt-ctapair { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); width: 100%; }
    .ivt-ctapair .ivt-cta { min-width: 0; width: 100%; padding-left: 8px; padding-right: 8px; }
}

/* v31.411 (owner, "please add spacing"): the Strategy page's rows sit inside one <form>, so the
   scroll body's gap never reached them — the form spaces its own children now */
#ivtStrategyForm { display: flex; flex-direction: column; gap: 14px; }
.btn.h34 { height: 36px; font-size: 15px; width: auto; padding: 0 14px; }
/*  ⚠ v31.514 — the owner's own selector and his own number, verbatim:
    *"Please set this selector. select.input.h34, input.input.h34 { height: 40px; }"*
    It is app-wide, which is what he asked for. `.btn.h34` is deliberately NOT
    changed with it — he named inputs and selects only, so buttons stay at 36px
    and any row that mixes the two now differs by 4px. Measured and reported
    rather than quietly levelled: raising the button is one line when he says so. */
select.input.h34, input.input.h34 { height: 40px; }
.ivt-inline select.input { width: auto; min-width: 180px; }

/* KPI tiles */
.ivt-kpis { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 480px) { .ivt-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .ivt-kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.ivt-kpi { padding: 16px 18px; min-width: 0; }
.ivt-kpi .lab { font-size: 14px; color: var(--nv-ink-2); display: flex; align-items: center; gap: 6px; }
.ivt-kpi .val { font-size: 28px; font-weight: 200; color: var(--nv-ink); margin-top: 6px; font-variant-numeric: tabular-nums; letter-spacing: .01em; overflow: hidden; text-overflow: ellipsis; }
.ivt-kpi .sub { font-size: 12.5px; color: var(--nv-ink-2); margin-top: 6px; }
.ivt-kpi .val.pos, .pos { color: var(--acc-green,#3fca85); }
.ivt-kpi .val.neg, .neg { color: #e79a9a; }
.ivt-chev { background: none; border: 0; padding: 2px; color: #e1a65c; cursor: pointer; display: inline-flex; align-items: center; border-radius: 6px; }
.ivt-chev:focus-visible { outline: 2px solid var(--nv-blue-bright); }
.ivt-chev[aria-expanded="true"] svg { transform: rotate(180deg); }
.ivt-fxeditor { margin-top: 12px; padding: 12px; border-radius: 8px; background: rgba(18,30,56,.45); border: 1px solid rgba(104,152,232,.22); display: grid; gap: 8px; }
.ivt-fxeditor .fl, .fl { font-size: 13px; color: var(--nv-ink-2); }
.ivt-fxeditor .input { width: 100%; }
.ivt-fxacts { display: flex; gap: 8px; flex-wrap: wrap; }
.ivt-fxeditor .s { font-size: 12px; color: var(--nv-ink-2); }

/* cash position */
.ivt-cash .more.muted { font-size: 13px; }
.ivt-cashgrid { display: grid; grid-template-columns: 1fr; gap: 10px; margin-top: 10px; }
@media (min-width: 480px) { .ivt-cashgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .ivt-cashgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.ivt-cashitem { border: 1px solid rgba(104,152,232,.22); border-radius: 8px; padding: 12px 14px; min-width: 0; }
.ivt-cashitem .t { font-size: 13.5px; color: var(--nv-ink-2); }
.ivt-cashitem .n { font-size: 22px; color: var(--nv-gold-bright); font-weight: 200; margin-top: 4px; font-variant-numeric: tabular-nums; }
.ivt-cashitem .s { font-size: 12px; color: var(--nv-ink-2); margin-top: 4px; }
.ivt-cashlines { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 12px; font-size: 13.5px; color: var(--nv-ink-2); }
.ivt-cashline b { color: var(--nv-ink); font-weight: 400; }

/* two-column zones */
.ivt-grid2 { display: grid; grid-template-columns: 1fr; gap: 14px; align-items: start; }
@media (min-width: 1024px) { .ivt-grid2 { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); } .ivt-grid2-wide, .ivt-grid2-even { grid-template-columns: minmax(0, 1fr); } }
/* v31.393 — with the Investments panel taking 260px from 1024, the even pairs
   (traders | brokers, provider | rates) only fit side by side from 04-Screen */
@media (min-width: 1366px) { .ivt-grid2-even { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* a table that may scroll on the PHONE only — never on a desktop card */
.ivt-hscroll-phone { overflow: visible; }
@media (max-width: 767.98px) { .ivt-hscroll-phone { overflow-x: auto; -webkit-overflow-scrolling: touch; } }
/* the watchlist table is eight columns wide — it only shares a row with the checklist from 04-Screen up */
@media (min-width: 1366px) { .ivt-grid2-wide { grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); } }
/* v31.395 — the transaction history keeps its ten columns on one screen:
   the workflow card stacks under it at every width */
.ivt-grid2.ivt-grid2-stack { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1024px) { .ivt-grid2.ivt-grid2-stack { grid-template-columns: minmax(0, 1fr); } }
.ivt-watch th { white-space: normal; }
.ivt-xbtn { min-width: 34px; padding: 0 8px; }
.ivt-grid2 > .card { min-width: 0; }
.ivt-stack { display: flex; flex-direction: column; gap: 14px; min-width: 0; }

/* charts */
.ivt-chart { width: 100%; height: 230px; display: block; }
.ivt-feedcard .ivt-chart { height: 170px; }
.ivt-chart .ivt-grid { stroke: rgba(170,179,200,.22); }
.ivt-chart .ivt-ylab, .ivt-chart .ivt-xlab, .ivt-chart .ivt-chart-empty { fill: var(--nv-ink-2); font-size: 11px; }
.ivt-chart-empty { font-size: 28px; }
.ivt-legend { font-size: 13px; color: var(--nv-ink-2); margin-top: 6px; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.ivt-legend i { width: 12px; height: 12px; border-radius: 50%; display: inline-block; }
/* ⚠ `.nv-tabs` is a GRID (app.css:9122) — as a flex item in a card head it
   stacked the five range chips in one column (seen on the owner's screen,
   v31.393). An inline row: flex, no rule, no bottom margin. */
.ivt-ranges, .ivt-ranges.nv-tabs { display: flex; flex-wrap: wrap; gap: 4px; flex: 0 0 auto; border-bottom: 0; padding-bottom: 0; margin-bottom: 0; }
.ivt-ranges > .chip { flex: 0 0 auto; height: 28px; padding: 0 10px; font-size: 13px; }
h2.section .ivt-ranges { margin-left: auto; }
.ivt-secranges { margin: 10px 0 4px; flex-wrap: wrap; }

/* allocation */
.ivt-allocbar { display: flex; height: 10px; border-radius: 5px; overflow: hidden; background: rgba(104,152,232,.16); margin: 10px 0 12px; }
.ivt-allocbar i { display: block; height: 100%; }
.ivt-allocrow { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-top: 1px solid rgba(104,152,232,.12); font-size: 14.5px; }
.ivt-allocrow .dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.ivt-allocrow .v { margin-left: auto; color: var(--nv-ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ivt-align { margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(104,152,232,.18); }
.ivt-align .t { font-weight: 500; margin-bottom: 6px; }
.ivt-align .s { font-size: 13px; color: var(--nv-ink-2); margin-top: 6px; }
.dbtag.ok { color: #3fca85; border-color: rgba(63,202,133,.5); }
.dbtag.warn { color: #e1a65c; border-color: rgba(225,166,92,.55); }

/* explorer */
.ivt-search { display: flex; gap: 8px; margin: 8px 0 12px; }
.ivt-search .input { flex: 1 1 auto; height: 40px; }
.ivt-search .btn { flex: 0 0 auto; height: 40px; width: auto; padding: 0 16px; font-size: 15px; }
.ivt-xhead { display: flex; justify-content: space-between; gap: 10px; align-items: flex-start; flex-wrap: wrap; }
.ivt-xname { font-weight: 500; font-size: 17px; }
.ivt-xmeta { font-size: 13px; color: var(--nv-ink-2); margin-top: 2px; }
.ivt-xprice { margin: 10px 0 2px; }
.ivt-xprice .big { font-size: 34px; font-weight: 200; font-variant-numeric: tabular-nums; }
.ivt-xprice .ccy { color: var(--nv-ink-2); }
.ivt-xprice .s { font-size: 13px; color: var(--nv-ink-2); margin-top: 2px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.ivt-xstats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 14px; margin-top: 10px; }
@media (min-width: 768px) { .ivt-xstats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.ivt-xstats > div { display: flex; justify-content: space-between; gap: 8px; padding: 6px 0; border-bottom: 1px solid rgba(104,152,232,.14); font-size: 14px; }
.ivt-xstats > div span { color: var(--nv-ink-2); }
.ivt-feedgrid .ivt-xstats { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 0; }

/* rows (watch / recent / audit) */
.ivt-wrow { display: flex; justify-content: space-between; gap: 12px; align-items: center; padding: 9px 0; border-top: 1px solid rgba(104,152,232,.12); font-size: 14.5px; }
.ivt-wrow:first-of-type { border-top: 0; }
.ivt-wrow .s { font-size: 12.5px; color: var(--nv-ink-2); }
.ivt-wrow .r { text-align: right; flex: 0 0 auto; }
.ivt-wrow b { font-weight: 500; }
.ivt-amt { font-variant-numeric: tabular-nums; }

/* tables */
.ivt-hscroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 -8px; padding: 0 8px; }
.ivt-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.ivt-table th { text-align: left; font-size: 12.5px; letter-spacing: .04em; text-transform: uppercase; color: var(--nv-ink-2); font-weight: 400; padding: 10px 8px; border-bottom: 1px solid rgba(104,152,232,.25); white-space: nowrap; }
.ivt-table th a { color: inherit; text-decoration: none; }
.ivt-table th.on a { color: var(--nv-gold-bright); }
.ivt-table th.on.desc a::after { content: " ▾"; } .ivt-table th.on.asc a::after { content: " ▴"; }
.ivt-table td { padding: 10px 8px; border-bottom: 1px solid rgba(104,152,232,.12); vertical-align: middle; }
.ivt-table th.n, .ivt-table td.n { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ivt-table .s { font-size: 12.5px; color: var(--nv-ink-2); }
.ivt-ticker { color: var(--nv-gold-bright); font-weight: 500; }
.ivt-traderchip { display: inline-block; padding: 2px 10px; border-radius: 999px; background: rgba(74,146,221,.18); color: #cfe0ff; font-size: 12.5px; }
.ivt-chevcol { width: 34px; padding-left: 0 !important; padding-right: 0 !important; }
.ivt-total td { font-weight: 500; color: var(--nv-gold-bright); border-top: 2px solid rgba(225,166,92,.6); border-bottom: 0; background: rgba(18,30,56,.35); }
.ivt-feed > td { padding: 0 0 12px; background: rgba(13,22,42,.25); }
.ivt-feedcard { margin: 6px 8px 0; padding: 14px; border: 1px solid var(--acc-gold, var(--nv-gold)); border-radius: 10px; }   /* v31.398 (owner): gold */
.ivt-feedhead { display: flex; justify-content: space-between; gap: 10px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 8px; }
.ivt-feedhead .s { font-size: 12.5px; color: var(--nv-ink-2); }
.ivt-feedhead .btn { width: auto; }
/*  v31.479 (owner: "format the test so we have ellipsis and tooltp for full
    text") — the detail card's heading and its sub-line each stay on ONE line
    and end in an ellipsis, with the whole string on the element's `title`.
    ⚠ `min-width: 0` on the wrapper is the part that is easy to miss: the head
    is a flex row, and a flex child's default `min-width: auto` refuses to
    shrink below its content, so the text would push the card wider instead of
    ellipsising and nothing would appear to happen. */
.ivt-feedhead .ivt-headtext { flex: 1 1 auto; min-width: 0; }
.ivt-ell1 { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ivt-feedgrid { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 1024px) { .ivt-feedgrid { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); } }
/*  ══ v31.464 — THE LIVE TICK AND THE FOLDED-AWAY MANUAL BOX ═══════════════
    Owner: the API prices the positions; typing one is the exception, so it
    reads as an exception. The unfed note is the loud one (it wants doing
    something about); the fold is quiet (it is only ever a correction).       */
.ivt-live { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 12px; color: var(--nv-ink-2); padding: 0 4px; margin: -2px 0 10px; min-height: 16px; }
.ivt-livedot { width: 7px; height: 7px; border-radius: 50%; background: #36bc91; flex: 0 0 auto; }
/*  ⚠ the pulse is the ONLY motion on this page and it is 4s — the owner had
    the dashboard rotator removed as "very distracting" (v31.401).            */
.ivt-livedot { animation: ivtPulse 4s ease-in-out infinite; }
.ivt-livedot.bad { background: #e1a65c; animation: none; }
@keyframes ivtPulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
.ivt-livestamp { font-variant-numeric: tabular-nums; }
.ivt-livedelay { color: var(--nv-ink-3, var(--nv-ink-2)); }
@media (prefers-reduced-motion: reduce) { .ivt-livedot { animation: none; } }
@media (max-width: 767px) { .ivt-livedelay { display: none; } }

.ivt-unfed { font-size: 13px; color: #e1a65c; margin-top: 10px; line-height: 1.5; }
.ivt-unfed code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; padding: 1px 5px; border: 1px solid var(--nv-line); border-radius: 4px; }
.ivt-manual { margin-top: 10px; }
.ivt-manual > summary { cursor: pointer; font-size: 13px; color: var(--nv-ink-2); width: fit-content; }
.ivt-manual > summary:hover { color: var(--nv-ink-1); }
.ivt-manualnote { font-size: 12px; color: var(--nv-ink-2); margin-top: 6px; line-height: 1.5; }

.ivt-pricerow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 10px; }
.ivt-pricerow .input { width: auto; flex: 1 1 120px; max-width: 180px; }
.ivt-pricerow .btn { width: auto; }
.ivt-txrow.is-reversed td { opacity: .55; text-decoration: line-through; }
.ivt-txrow.is-reversed td:last-child, .ivt-txrow.is-reversed td .dbtag { text-decoration: none; }
.ivt-reverse summary { cursor: pointer; color: var(--nv-ink-2); font-size: 13px; list-style: none; }
.ivt-reverse summary::-webkit-details-marker { display: none; }
.ivt-reverseform { display: flex; gap: 6px; margin-top: 6px; min-width: 240px; }
.ivt-reverseform .btn { width: auto; white-space: nowrap; }
/* ⚠ `.ivt-rowacts` sits on a <td>; `display:flex` there un-tables the cell
   and lets the browser squeeze it, pushing the buttons out of the card.
   The cell stays a cell — the buttons are inline, non-wrapping. */
.ivt-rowacts { white-space: nowrap; text-align: right; }
.ivt-rowacts form { margin: 0; display: inline-block; }
.ivt-rowacts .btn { width: auto; display: inline-flex; padding: 0 10px; margin-left: 6px; font-size: 13.5px; }
.ivt-watch td:nth-child(2) { min-width: 160px; }
.ivt-addrow td { background: rgba(18,30,56,.25); }
/* ⚠ Same rule as `.ivt-rowacts`: never `display:flex` on a <td>. The
   master tables carry inputs, so each input states a floor width and the
   securities table scrolls sideways inside `.ivt-hscroll` instead of
   squeezing its fields to two letters. */
.ivt-bondcells { white-space: nowrap; }
.ivt-bondcells .input { display: inline-block; width: 92px; margin-right: 4px; vertical-align: middle; }
.ivt-bondcells input[type=date].input { width: 150px; }
.ivt-table input.input.h34 { min-width: 120px; }
.ivt-table select.input.h34 { min-width: 100px; }
/* v31.393 — the securities master as THREE-row blocks with the columns aligned
   (owner: "some semblance of left alignment, right alignment and width for each
   column … name needs to span two"). One grid of five tracks for every row from
   768 up — the 02-Screen stacking kept on 04/05 — single column on the phone:
     row 1  ticker · name (×2) · class · save
     row 2  currency · exchange · isin (×2) · active
     row 3  bonds only → face · coupon · maturity · hint   (only while class = Bonds) */
.ivt-secrow { display: grid; grid-template-columns: 1fr; gap: 8px; align-items: center; }
.ivt-secrow .input { width: 100%; min-width: 0; }
.ivt-secrow .btn { width: auto; justify-self: end; white-space: nowrap; padding: 0 14px; }
.ivt-secrow .fl { font-size: 12.5px; color: var(--nv-ink-2); letter-spacing: .04em; text-transform: uppercase; }
.ivt-secrow .ivt-bondhint { font-size: 12px; }
@media (min-width: 768px) {
    .ivt-secrow { grid-template-columns: 112px minmax(0, 1fr) minmax(0, 1fr) 164px 156px; }
    .ivt-secrow .c2 { grid-column: span 2; }
}
.ivt-table select.input[name=userId] { min-width: 170px; }
.ivt-rates .ivt-inline { display: flex; gap: 6px; justify-content: flex-end; }
.ivt-rates .ivt-ratein { width: 120px; min-width: 96px; text-align: right; }
@media (max-width: 767.98px) { .ivt-rates .ivt-ratein { width: 96px; } .ivt-rates th:nth-child(4), .ivt-rates td:nth-child(4) { display: none; } }
.ivt-rates td .btn { width: auto; }
@media (max-width: 767.98px) { .ivt-opt { display: none; } }

/* forms, sheets */
.ivt-formgrid { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 10px; }
@media (min-width: 600px) { .ivt-formgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .ivt-formgrid .wide { grid-column: 1 / -1; } }
.ivt-formgrid .field .s { font-size: 12px; color: var(--nv-ink-2); }
/*  ⚠⚠ v31.535 — TWO FIELDS SHARING ONE CELL, AT EXACTLY HALF EACH (owner:
    *"make the Exchange textbox and the feed symbol textbox same width"*).
    `minmax(0, 1fr)` twice, never a bare `1fr`: a bare `1fr` floors at the
    content's minimum width, so the half holding the longer placeholder — or the
    longer translated label — would take more than half and "same width" would
    hold in English and nowhere else. The same reason `.acc-cp-acts` gives one
    stylesheet over. */
.ivt-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: end; }
.ivt-pair > div { min-width: 0; display: flex; flex-direction: column; }
.ivt-textarea { height: auto; min-height: 84px; padding: 10px 16px; resize: vertical; display: block; }
.ivt-field-btn .btn { height: 36px; font-size: 15px; }
.ivt-addcard { margin-top: 12px; padding: 12px 14px 14px; border: 1px solid rgba(225,166,92,.45); border-radius: 10px; background: rgba(18,30,56,.4); }
.ivt-addcard .t { font-weight: 500; color: var(--nv-gold-bright); }
.ivt-addacts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 12px; }
.ivt-addacts .s { font-size: 12px; color: var(--nv-ink-2); }
.ivt-ghostform { display: none; }
.ivt-estimate { margin-top: 14px; padding: 12px 14px; border-radius: 8px; background: rgba(18,30,56,.5); border: 1px solid rgba(104,152,232,.2); }
.ivt-estimate .t { font-size: 13.5px; color: var(--nv-ink-2); }
.ivt-estimate .n { font-size: 24px; font-weight: 200; margin-top: 2px; font-variant-numeric: tabular-nums; }
.ivt-estimate .s { font-size: 12px; color: var(--nv-ink-2); margin-top: 2px; }
.ivt-sheetacts { display: flex; gap: 10px; justify-content: flex-end; margin-top: 16px; }
.ivt-sheetacts .btn { width: auto; min-width: 150px; padding: 0 18px; white-space: nowrap; }
.ivt-sheetbg { position: fixed; inset: 0; background: rgba(5,10,22,.72); z-index: 55; /* v31.397: above the sticky head (43), BELOW the toolkit calendar (.nvcal, 60) — at 200 the date pop-up opened behind the sheet */ display: flex; align-items: flex-start; justify-content: center; overflow-y: auto; padding: 0; }
/* ⚠ `.card` carries no fill of its own (owner, tokens.css) — a dialog over a
   busy page needs one, so the sheet paints the same gradient the mail sheet
   uses; without it the table behind reads through the form. */
.ivt-sheet { width: 100%; max-width: 640px; margin: 0; border-radius: 0; min-height: 100%; background: linear-gradient(145deg, #010107 -151%, #1E1E1E 50%, #13294B 100%); box-shadow: 0 24px 60px rgba(0,0,0,.55); }
@media (min-width: 768px) { .ivt-sheetbg { padding: 40px 16px; align-items: flex-start; } .ivt-sheet { border-radius: var(--r-card); min-height: 0; } }
.ivt-sheethead { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.ivt-sheethead h2 { margin: 0; font-size: 20px; font-weight: 300; }
body.ivt-sheet-open { overflow: hidden; }
/* v31.408 (owner's screenshot): the sheet lives inside `.scroll`, which is its own
   stacking context (tokens.css: relative, z-index 2) — so the sticky page head
   (z-index 20, a sibling) painted OVER the sheet and cut its title. While a sheet is
   open the head steps under the scroll body; everything else stays as it was. */
body.ivt-sheet-open .screen-head { z-index: 1; }
.ivt-notebox { margin-top: 12px; padding: 10px 12px; border-radius: 8px; background: rgba(18,30,56,.45); border: 1px solid rgba(104,152,232,.18); font-size: 14px; }
.ivt-notebox .s { font-size: 13px; color: var(--nv-ink-2); margin-top: 3px; }
.ivt-endpoints, .ivt-contract { margin-top: 14px; }
.ivt-endpoints pre, .ivt-contract pre { font-size: 12.5px; background: rgba(5,10,22,.6); padding: 12px; border-radius: 8px; overflow-x: auto; color: #cfe0ff; margin: 6px 0 0; }

/* strategy */
.ivt-trow { display: grid; grid-template-columns: 1fr; gap: 6px; padding: 10px 0; border-top: 1px solid rgba(104,152,232,.12); align-items: center; }
@media (min-width: 600px) { .ivt-trow { grid-template-columns: 90px 1fr 1fr 80px; gap: 10px; } }
.ivt-trow .s { font-size: 12.5px; color: var(--nv-ink-2); }
.ivt-track { height: 7px; border-radius: 4px; background: rgba(104,152,232,.16); overflow: hidden; margin-top: 3px; }
.ivt-track i { display: block; height: 100%; background: #e1a65c; }
.ivt-track i.tgt { background: #4a92dd; }
.ivt-tgt { text-align: right; }
.ivt-tsum { margin-top: 10px; padding: 10px 12px; border-radius: 8px; background: rgba(18,30,56,.45); }
.ivt-tsum > div:first-child { display: flex; justify-content: space-between; font-size: 15px; }
.ivt-tsum .s { font-size: 12.5px; color: var(--nv-ink-2); margin-top: 3px; }
.ivt-tsum .s.warn { color: #e1a65c; }
.ivt-versions { margin-top: 12px; font-size: 13.5px; }
.ivt-versions .s { font-size: 12.5px; color: var(--nv-ink-2); }
.ivt-reviewadd .ivt-formgrid { margin-top: 0; }
@media (min-width: 900px) { .ivt-reviewadd .ivt-formgrid { grid-template-columns: 1fr 180px 200px; } .ivt-reviewadd .ivt-formgrid .wide { grid-column: auto; } }
.ivt-check { padding: 7px 0; border-top: 1px solid rgba(104,152,232,.12); font-size: 14.5px; }
.ivt-check::before { content: "☐  "; color: var(--nv-ink-2); }
.ivt-checknote { margin-top: 10px; }

/* performance */
.ivt-toolbar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between; padding: 12px 16px; }
.ivt-toolform { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.ivt-toolform .input { width: auto; }
.ivt-toolform select.input { min-width: 90px; }
.ivt-toolform input.input { width: 120px; }
.ivt-toolform .btn { width: auto; }
.ivt-toolform .s { font-size: 12.5px; color: var(--nv-ink-2); }
/* ⚠ `.nv-tabs` is a GRID (app.css:9122); as a flex item inside the toolbar
   it collapsed into one column of stacked chips. The period picker is an
   inline row, so it drops the grid, the rule and the bottom margin here. */
.ivt-periods.nv-tabs { display: flex; flex-wrap: wrap; gap: 6px; border-bottom: 0; padding-bottom: 0; margin-bottom: 0; }
.ivt-periods > .chip { flex: 0 0 auto; height: 30px; padding: 0 12px; font-size: 13.5px; }
.ivt-perfrows { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 16px; margin-top: 10px; }
@media (min-width: 600px) { .ivt-perfrows { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.ivt-perfrows > div { display: flex; justify-content: space-between; gap: 8px; padding: 6px 0; border-bottom: 1px solid rgba(104,152,232,.14); font-size: 14px; }
.ivt-perfrows span { color: var(--nv-ink-2); }
/* v31.408 (owner): the same block at the top of the dashboard's Portfolio value card */
.ivt-pvcaption { margin-top: 2px; }
.ivt-pvrows { margin-top: 4px; margin-bottom: 14px; }
.ivt-pvrows b.neg { color: #e79a9a; }
.ivt-allocmini { margin-top: 12px; display: flex; gap: 12px; flex-wrap: wrap; font-size: 13.5px; color: var(--nv-ink-2); }
.ivt-allocmini b { color: var(--nv-ink); font-weight: 400; }
.ivt-allocmini > b { width: 100%; }
.ivt-auditrow .r { white-space: nowrap; }

/* the Home door card */
/*  v31.403 (owner): the door IS a Financials hub tile now (`.card.fin-tile.live`
    carries the layout, the gold icon and the conic halo); only the centring in
    its taller slot and the one small line are its own. ⚠ No border-color here —
    the halo's rule sets the border transparent and an !important gold would
    paint a solid edge over it. */
.ivt-door { justify-content: center; min-height: 150px; }
.ivt-door .lb { font-weight: 500; line-height: 1.25; font-size: 17px; }
.ivt-door .s { font-size: 13.5px; color: var(--nv-ink-2); }
/* v31.391 — on 01-Screen the section note (`.more`) drops under the heading
   instead of sharing the line with it and wrapping to four narrow rows. */
@media (max-width: 767.98px) {
    .ivt-scroll h2.section { flex-wrap: wrap; }
    .ivt-scroll h2.section > .more { flex: 1 0 100%; text-align: left; font-size: 12.5px; margin-top: 2px; }
}

/* v31.393 — Settings: the rates and traders tables fit their half-width cards
   (owner: "should not have a horizontal scroll") */
.ivt-rates .ivt-ccy { width: 48px; padding-right: 4px; }
.ivt-rates .ivt-ratein { width: 96px; min-width: 80px; }
.ivt-rates td, .ivt-rates th { padding-left: 6px; padding-right: 6px; }
.ivt-rates td .btn { padding: 0 10px; }
.ivt-rates .ivt-xbtn { min-width: 34px; margin-left: 4px; }
.ivt-trtable { table-layout: auto; }
.ivt-trtable td, .ivt-trtable th { padding-left: 5px; padding-right: 5px; }
.ivt-trtable input.input.h34 { min-width: 104px; }
.ivt-trtable select.input.h34 { min-width: 92px; padding-right: 26px; }
.ivt-trtable select.input[name=userId] { min-width: 136px; }
.ivt-trtable td .btn { padding: 0 10px; }
.ivt-trtable td .ivt-xbtn { min-width: 34px; }
/* v31.394 — the masters as GRID + ONE FORM (owner). The grid rows are
   selectable; the form's three broker tracks are equal (owner: "fields in the
   columns same width"); the securities form keeps the aligned five tracks. */
.ivt-grid tbody tr.ivt-gridrow { cursor: pointer; }
.ivt-grid tbody tr.ivt-gridrow:hover td { background: rgba(104,152,232,.08); }
.ivt-grid tbody tr.ivt-gridrow.is-cur td { background: rgba(232,167,92,.14); }
.ivt-grid tbody tr.ivt-gridrow.is-off td { color: var(--nv-ink-3); }
.ivt-grid tbody tr.ivt-gridrow:focus-visible { outline: 2px solid var(--nv-gold); outline-offset: -2px; }
.ivt-editform { margin-top: 12px; padding: 12px; border-radius: 10px; background: rgba(18,30,56,.25); display: flex; flex-direction: column; gap: 8px; }
.ivt-editform.is-selected { background: rgba(232,167,92,.07); }
.ivt-editform .field { margin: 0; }
.ivt-editform .field label { font-size: 12.5px; color: var(--nv-ink-2); }
.ivt-editform .ivt-editacts, .ivt-editacts { display: flex; flex-direction: row; gap: 6px; justify-content: flex-end; align-items: flex-end; white-space: nowrap; }
.ivt-editacts .btn { width: auto; padding: 0 14px; }
.ivt-editacts .ivt-xbtn { padding: 0 8px; min-width: 34px; display: inline-flex; align-items: center; justify-content: center; }
.ivt-del { color: var(--nv-danger, #e97e84); }
.ivt-brrow { display: grid; grid-template-columns: 1fr; gap: 8px; align-items: end; }
.ivt-brrow .input { width: 100%; min-width: 0; }
@media (min-width: 768px) { .ivt-brrow { grid-template-columns: repeat(3, minmax(0, 1fr)); } .ivt-brrow .ivt-editacts { grid-column: 2 / span 2; } }
.ivt-editform .acc-ctrywrap { display: block; }
.ivt-editform .acc-ctrybtn { height: 34px; }
/* the Investments panel (the mail-folders shell) */
.ivt-panel .mf { font-size: 16px; }
.fin-tray .ivt-panel { display: flex; flex-direction: column; gap: 2px; }
/* the in-use pop-up rides the sheet styles */
.ivt-pop p { margin: 10px 0 12px; line-height: 1.5; }
.ivt-inuse { margin-bottom: 6px; }
/* v31.393 — the head carries the entity chip AND two tray buttons from 560 to
   1023; the chip clips rather than running under the buttons. */
.ivt-head > .nv-ent { overflow: hidden; flex: 0 1 auto; }
.ivt-head .fin-tray-btn, .ivt-head .mmt-btn { flex: 0 0 auto; }
/*  v31.480 (owner, 5 Nov, with the arrow drawn on the watchlist head) — the
    two tray buttons TRAVEL TOGETHER AT THE RIGHT EDGE. v31.393 pinned the ⋯
    button hard against the entity chip (`margin-left: 0`) while the ☰ kept
    the `margin-left: auto` it has app-wide, so the free space opened up
    BETWEEN them and the ⋯ sat marooned in the middle of the row. Moving the
    `auto` onto the first of the pair puts the space before them both. */
.ivt-head .fin-tray-btn { margin-left: auto; }
.ivt-head .mmt-btn { margin-left: 0; }
.ivt-del.is-armed { color: #fff; background: var(--nv-danger, #e97e84); border-color: transparent; }

/* v31.394 — the charges fold-out on the transaction sheet */
.ivt-charges { margin: 4px 0 10px; }
.ivt-charges > summary { cursor: pointer; color: var(--nv-gold); font-size: 14px; padding: 6px 0; list-style: none; }
.ivt-charges > summary::before { content: "▸ "; }
.ivt-charges[open] > summary::before { content: "▾ "; }
.ivt-charges > summary .s { color: var(--nv-ink-2); margin-left: 6px; }
.ivt-formgrid3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 768px) { .ivt-formgrid3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.ivt-sheetmsg { margin: 8px 0 10px; }
.ivt-sheetmsg { padding: 8px 12px; border-radius: 8px; background: rgba(233,126,132,.12); border: 1px solid rgba(233,126,132,.45); font-size: 14px; }
.ivt-trtable select.ivt-ccysel { min-width: 92px; }
.ivt-trtable td.ivt-cactive { text-align: center; }
.ivt-tick { width: 18px; height: 18px; accent-color: var(--nv-gold-bright, #e8b857); vertical-align: middle; cursor: pointer; }
.ivt-curchips { margin-left: auto; }
.ivt-curchips > .chip { height: 26px; padding: 0 9px; font-size: 12.5px; }

/* v31.395 — the transaction history's columns (owner): date one line, QTY
   narrow, TYPE wide, broker ellipsised; the brokers grid one line per row */
.ivt-txns { table-layout: auto; }
.ivt-txns th, .ivt-txns td { padding-left: 6px; padding-right: 6px; }
.ivt-txns { font-size: 13.5px; }
.ivt-txns .ivt-cdate { white-space: nowrap; }
.ivt-txns .ivt-ctype { min-width: 108px; white-space: nowrap; }
.ivt-txns .ivt-cqty { width: 48px; }
.ivt-txns .ivt-cbroker { max-width: 128px; }
.ivt-txns td .s { white-space: nowrap; }
.ivt-ell { display: block; max-width: 128px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 1599.98px) { .ivt-txns .ivt-cbroker, .ivt-txns .ivt-ell { max-width: 96px; } }
@media (max-width: 1365.98px) { .ivt-txns { font-size: 13px; } .ivt-txns .ivt-ctype { min-width: 0; } .ivt-txns th, .ivt-txns td { padding-left: 4px; padding-right: 4px; } .ivt-txns .ivt-cbroker, .ivt-txns .ivt-ell { max-width: 84px; } .ivt-txns .ivt-traderchip { padding: 2px 7px; } }
#ivtBrGrid { table-layout: fixed; }
#ivtBrGrid td, #ivtBrGrid th { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#ivtBrGrid th:nth-child(1), #ivtBrGrid td:nth-child(1) { width: 32%; }
#ivtBrGrid th:nth-child(2), #ivtBrGrid td:nth-child(2) { width: 24%; }
#ivtBrGrid th:nth-child(3), #ivtBrGrid td:nth-child(3) { width: 17%; }
.ivt-cashwarn { margin-top: 8px; padding: 8px 10px; border-radius: 8px; font-size: 13px; border: 1px solid rgba(225,166,92,.6); background: rgba(225,166,92,.12); color: #e1a65c; }
.ivt-cashwarn[hidden] { display: none; }
.ivt-sheetacts .btn:disabled { opacity: .45; cursor: not-allowed; filter: saturate(.4); }

/* v31.396 — the market feed: its standing on the provider card, the feed-symbol row */
.ivt-feedstate { display: grid; gap: 6px; padding: 10px 12px; border: 1px solid rgba(104,152,232,.25); border-radius: 10px; font-size: 13.5px; margin-bottom: 10px; }
.ivt-feedstate.is-ready { border-color: rgba(63,202,133,.45); }
.ivt-feedstate b { margin-right: 6px; }
.ivt-feedstate code { font-size: 12.5px; padding: 1px 6px; border-radius: 6px; background: rgba(104,152,232,.14); }
/*  ══ v31.546 — THE LAST RUN'S DETAIL, inside the provider card ═════════════
    A clean run and a failed one are the same block with a different border,
    because the reader's question is the same either way: did the last call
    work. ⚠ The list wraps rather than scrolls sideways — HR-1; a symbol and a
    sentence on one line would overflow the card at 01/02-Screen, so each
    failure is three stacked lines and the card grows downward.              */
/*  ⚠⚠ `min-width: 0` ON BOTH BOXES, AND IT IS NOT DECORATION. This block is a
    GRID ITEM of `.ivt-feedstate`, and a grid item's default `min-width: auto`
    is its content's min-content width — so the no-wrap row below made the
    whole provider card wider than 01-Screen and pushed the PAGE sideways,
    with the overflow reported against the card's other lines rather than
    against this one. Measured at 390px: 97px of page scroll, 0px inside this
    block. The guard belongs on the item, not on the thing that grew. */
/*  ══ v31.547 — THE TWO CONTROLS LINE UP, AND THE DETAIL IS BEHIND ONE ══════
    The owner asked for the new icon *"below the information icon"*. Pushing
    both to the card's right edge is the only version of that which survives a
    longer sentence or a translated one — anchored to the end of their own
    text they would sit under each other in English and nowhere near in German.
    ⚠ `gap` carries the word spacing here: whitespace between inline elements
    is DROPPED inside a flex container, so without it `Last run11 Nov` is what
    renders. */
.ivt-feedstate .ivt-feedline { display: flex; align-items: center; flex-wrap: wrap; gap: 2px 5px; min-width: 0; }
.ivt-feedstate .ivt-feedline > .nv-info-b { margin-left: auto; }

/*  the warning glyph's one rule. ⚠ The BUTTON is the house control untouched
    (HR-5) — this colours it and nothing else, and it is only ever on the page
    when the last run actually failed. */
.nv-info-b.is-warn { color: var(--nv-warn); border-color: rgba(217,161,59,.6); background: rgba(217,161,59,.12); }
.nv-info-b.is-warn:hover, .nv-info-b.is-warn[aria-expanded="true"] { border-color: var(--nv-warn); background: rgba(217,161,59,.2); }
.nv-info-b.is-warn svg { display: block; }

/*  the note itself is wider than an explanation pop, because it carries a
    table of failures rather than a sentence. ⚠ NO `overflow-y` — `_NvInfo`
    shuts on any scroll (a fixed panel cannot follow its icon), so a panel that
    scrolled inside itself would close on the first drag. It is bounded by
    showing six and counting the rest. */
.nv-info-pop.ivt-feedpop { width: 460px; max-width: min(92vw, 460px); letter-spacing: normal; padding: 10px; }
.nv-info-pop.ivt-feedpop .ivt-feeddiag { border: 0; padding: 0; margin: 0; }
/*  ⚠⚠ BELOW 768 THE WIDTH GOES BACK TO THE PARTIAL'S. `_NvInfo` pins the note
    `left: 12px; right: 12px; width: auto` there — measured when the Investments
    pop was built, because a fixed-width panel does not fit beside a 26px icon
    on a phone. A `width` set here is MORE SPECIFIC than that rule, so it wins
    silently and the note hangs 82px off the right of a 390px screen (measured,
    arm 6b). Restating `width: auto` is what hands it back. */
@media (max-width: 767.98px) { .nv-info-pop.ivt-feedpop { width: auto; max-width: none; } }

.ivt-feeddiag { border: 1px solid rgba(232,167,92,.38); border-radius: 10px; padding: 8px 10px; margin: 2px 0 10px; font-size: 13px; min-width: 0; }
.ivt-feeddiag .fd-more { margin-top: 6px; color: var(--nv-ink-2); }
.ivt-feeddiag.is-clean { border-color: rgba(63,202,133,.35); }
.ivt-feeddiag .fd-h { line-height: 1.5; }
.ivt-feeddiag .fd-h b { margin-right: 6px; }
.ivt-feeddiag .fd-ok { color: var(--nv-ink-2); }
.ivt-feeddiag .fd-list { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 6px; }
.ivt-feeddiag .fd-list li { padding: 6px 8px; border-radius: 8px; background: rgba(104,152,232,.10); min-width: 0; }
/*  ⚠ ONE LINE, AND THE NAME IS WHAT GIVES WAY. Wrapped, the stage word
    (`delayed quote`) dropped to a line of its own against the right edge and
    read as an orphan at every width under about 560px. The symbol and the
    stage are both short and both load-bearing; the security's name is the
    long part and the one already carried in full by the message bullets and
    the grid, so it truncates with a `title` - the same bargain `.ivt-nm`
    strikes two cards above. */
.ivt-feeddiag .fd-top { display: flex; align-items: baseline; gap: 8px; }
.ivt-feeddiag code { font-size: 12.5px; padding: 1px 6px; border-radius: 6px; background: rgba(104,152,232,.18); word-break: break-all; }
.ivt-feeddiag .fd-top code { flex: 0 0 auto; word-break: normal; }
.ivt-feeddiag .fd-sec { color: var(--nv-ink); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ivt-feeddiag .fd-stage { color: var(--nv-ink-2); font-size: 12px; flex: 0 0 auto; margin-left: auto; }
.ivt-feeddiag .fd-why { margin-top: 3px; line-height: 1.4; color: var(--nv-warn); }
.ivt-feeddiag .fd-from { margin-top: 2px; color: var(--nv-ink-2); }
.ivt-feeddiag .fd-foot { margin-top: 8px; color: var(--nv-ink-2); line-height: 1.4; }
.ivt-feedacts { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.ivt-feedacts .btn { width: auto; }
.ivt-feedacts .btn:disabled { opacity: .45; cursor: not-allowed; }
/*  ⚠ v31.534 — THE SELECTOR TAKES THE SECOND DOOR IN rather than a copy of
    this declaration being made for it. The Add-new-security card in New
    Transaction carries the same field and the same sentence, and a hint styled
    in two places is a hint that drifts in one of them - which is exactly how
    `--nv-grad-panel` came to exist two rounds ago. */
.ivt-feedrow .ivt-feedhint,
.ivt-feedfield .ivt-feedhint { font-size: 12px; color: var(--nv-ink-2); line-height: 1.3; }

/*  ⚠ the hint sits UNDER the input rather than beside it here: `.ivt-feedrow`
    is a three-column grid in the catalog, and this card's `.field` is a
    column, so the sentence lands on its own line with no rule of its own. */
.ivt-feedfield > .ivt-feedhint { margin-top: 4px; display: block; }

/* v31.397 — the transaction list: details chevron, document doors, the reversals tick; the sheet's type-driven fields */
.ivt-txns .ivt-cchev { width: 30px; padding-left: 2px; padding-right: 2px; }
.ivt-txns .ivt-cacts { white-space: nowrap; }
.ivt-actwrap { display: inline-flex; align-items: center; gap: 6px; }   /* never flex on a td — the wrapper flexes */
.ivt-txns .ivt-cacts .docclip-btn, .ivt-txns .ivt-cacts .docadd-btn { vertical-align: middle; margin-right: 4px; }
.ivt-txdetail > td { padding: 0 0 12px 0; }
.ivt-txcard .ivt-txstats { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
.ivt-txnote { margin-top: 8px; }
.ivt-checklbl { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; white-space: nowrap; margin-left: 10px; cursor: pointer; }
.ivt-form .field.is-off { opacity: .45; }
.ivt-form .field.is-off .input { pointer-events: none; }
.ivt-docfield { margin-top: 10px; }
.ivt-docfield .s { font-size: 12px; color: var(--nv-ink-2); }
.ivt-file { padding: 7px 10px; height: auto; }
.ivt-file::file-selector-button { background: rgba(104,152,232,.18); color: var(--nv-ink); border: 0; border-radius: 8px; padding: 5px 12px; margin-right: 10px; cursor: pointer; }

/* v31.397 — the dashboard's rotating blocks (the home flip's dots, three views where needed) */
.ivt-rota { min-width: 0; }
.ivt-rotapanes { display: grid; }
.ivt-rotapane { grid-area: 1 / 1; min-width: 0; visibility: hidden; opacity: 0; transition: opacity .45s ease; }
.ivt-rotapane.is-on { visibility: visible; opacity: 1; }
.ivt-rotadots { padding-top: 8px; }
.ivt-rotadots span { cursor: pointer; }
.ivt-rotahead { margin-bottom: 10px; }
.ivt-rotachips { margin-bottom: 10px; }
.ivt-rotachips .chip { cursor: pointer; font: inherit; }
.ivt-stack2 { display: contents; }
/* the chips the owner marked "make these the same size": an fr grid sized to max-content gives every track the widest item */
.ivt-ranges.nv-tabs, .ivt-rotachips.nv-tabs { display: grid; grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 1fr; width: max-content; max-width: 100%; gap: 6px; }
.ivt-ranges.nv-tabs > .chip, .ivt-rotachips.nv-tabs > .chip { text-align: center; justify-content: center; min-width: 0; }
/*  v31.401 (owner): "both same width … same size as the Portfolio value chip" —
    every chip row of a rotating block sizes its chips the same way (up to 220px
    each, shrinking together), so the two position chips and the three view chips
    are the same size whatever row they sit on. */
.ivt-rotachips.nv-tabs { width: 100%; grid-auto-columns: minmax(0, 200px); }
/* the index overview */
.ivt-indextable { font-size: 13px; }
.ivt-indextable th, .ivt-indextable td { padding-left: 5px; padding-right: 5px; }
@media (max-width: 1599.98px) { .ivt-indextable .ivt-ixopt { display: none; } .ivt-ixband { width: 90px; } .ivt-band { min-width: 70px; } }
.ivt-indextable .ivt-ixgroup td { padding: 8px 6px 4px; font-size: 12.5px; letter-spacing: .04em; text-transform: uppercase; color: var(--nv-gold-bright); border-bottom: 0; }
.ivt-indextable .n { font-variant-numeric: tabular-nums; }
.ivt-nowrap { white-space: nowrap; }
.ivt-ixband { width: 120px; }
.ivt-band { position: relative; display: block; height: 8px; border-radius: 4px; background: rgba(104,152,232,.22); min-width: 80px; }
.ivt-band i { position: absolute; top: -3px; width: 3px; height: 14px; margin-left: -1px; border-radius: 2px; background: var(--nv-gold-bright); }
@media (max-width: 767.98px) { .ivt-ixband { width: 70px; } .ivt-band { min-width: 60px; } }

/* v31.397 — the ✕ on a Today's-focus row (a form inside the row's link: the button does its own job, the row keeps its) */
.focus-card .focus-row { grid-template-columns: auto minmax(0, 1fr) auto; }
.focus-card .focus-row .focus-go { display: contents; color: inherit; text-decoration: none; }
.focus-card .focus-row .focus-x { grid-column: 3; grid-row: 1 / span 2; align-self: center; }
.focus-card .focus-row .focus-xbtn { width: 26px; height: 26px; border-radius: 50%; border: 1px solid rgba(104,152,232,.3); background: transparent; color: var(--nv-ink-2); font-size: 12px; line-height: 1; cursor: pointer; opacity: .6; }
.focus-card .focus-row:hover .focus-xbtn, .focus-card .focus-row .focus-xbtn:focus-visible { opacity: 1; color: var(--nv-gold-bright); }

/* v31.398 — the picker's bin (Investments documents; present only where the list carries `del`) */
.nvdocpick-row { display: flex; align-items: center; gap: 8px; }
.nvdocpick-row a.doclink { flex: 1 1 auto; min-width: 0; }
.nvdocpick-del { flex: 0 0 auto; width: 32px; height: 32px; border-radius: 8px; border: 1px solid rgba(104,152,232,.3); background: transparent; color: var(--nv-ink-2); cursor: pointer; display: inline-grid; place-content: center; }
.nvdocpick-del:hover, .nvdocpick-del.is-armed { color: #e1a65c; border-color: rgba(225,166,92,.6); }
.nvdocpick-del.is-armed { background: rgba(225,166,92,.12); }
/* the dashboard rotator: an inactive pane takes no room, so the block contracts to the view shown (owner) */
.ivt-rotapane { display: none; }
.ivt-rotapane.is-on { display: block; animation: ivtFade .35s ease; }
@keyframes ivtFade { from { opacity: 0; } to { opacity: 1; } }

/* v31.398 — the comparison legend under a feed-card / explorer chart */
.ivt-cmplegend { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; font-size: 12.5px; color: var(--nv-ink-2); margin-top: 6px; }
.ivt-cmplegend i { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; vertical-align: -1px; }
.ivt-cmplegend .ivt-bmform { margin-left: auto; }
.ivt-cmplegend .ivt-bmsel { min-width: 150px; height: 30px; font-size: 12.5px; }
.ivt-chart .ivt-zero { stroke: rgba(232,167,92,.5); }

/*  ══ v31.399 — the allocation donut (owner: "the asset allocation as a pie
    chart just above the strategy alignment row") ═══════════════════════════ */
.ivt-donutwrap { display: flex; justify-content: center; padding: 10px 0 6px; }
.ivt-donut { display: block; }
.ivt-donut-empty { stroke: var(--nv-line, rgba(232,167,92,.18)); }
.ivt-donut-c { font-size: 22px; font-weight: 500; fill: var(--nv-ink); letter-spacing: .01em; }

/*  ══ v31.406 — Reverse beside Edit on the details card; the Reversals card ═══ */
.ivt-cardacts { display: flex; gap: 6px; align-items: center; position: relative; flex: 0 0 auto; }
.ivt-reverse-card summary { font-size: inherit; color: inherit; display: inline-flex; align-items: center; }
/*  the confirm form drops BELOW the two buttons, over the card, so the head
    keeps its one line and the figures do not jump when Reverse is pressed */
.ivt-reverse-card .ivt-reverseform { position: absolute; right: 0; top: calc(100% + 6px); margin: 0; padding: 8px; min-width: 380px; background: var(--nv-bg-deep); border: 1px solid var(--acc-gold); border-radius: 10px; z-index: 5; }
.ivt-reverse-card .ivt-reverseform .input { flex: 1 1 auto; min-width: 0; }
.ivt-reverse-card .ivt-reverseform .btn { flex: 0 0 auto; overflow: visible; text-overflow: clip; }
.ivt-revcard { margin-top: 12px; }

/*  ══ v31.407 — the KPI card links, the one-row activity, the index tabs ═══ */
.ivt-kpi { position: relative; }
.ivt-kpi .ivt-kpilink { position: absolute; top: 12px; right: 12px; width: 28px; height: 28px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; color: var(--nv-ink-3); border: 1px solid rgba(104,152,232,.25); text-decoration: none; transition: color .15s, border-color .15s, background .15s; }
.ivt-kpi .ivt-kpilink:hover, .ivt-kpi .ivt-kpilink:focus-visible { color: var(--nv-gold-bright); border-color: var(--acc-gold); background: rgba(232,167,92,.08); outline: none; }
.ivt-kpi .lab { padding-right: 36px; }
.ivt-actrow { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto; gap: 12px; align-items: center; }
.ivt-actrow .ivt-actwhat, .ivt-actrow .ivt-actwhen { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ivt-actrow .ivt-amt { text-align: right; font-variant-numeric: tabular-nums; }
/* v31.408 (owner): a View on each watchlist row → the Market explorer; the three
   cells share the row with the link at the right */
.ivt-wlrow { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 12px; }
.ivt-wlrow > div:first-child { min-width: 0; }
.ivt-wlrow > div:first-child .s { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ivt-wview { white-space: nowrap; font-size: 13px; color: var(--nv-ink-2); text-decoration: none; }   /* the `.more` look, on the row */
.ivt-wview:hover { color: var(--nv-ink); }
.ivt-ixtabs.nv-tabs { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); grid-auto-flow: row; width: 100%; gap: 6px; margin-bottom: 10px; }
/*  ⚠ `display:block`, not flex — a flex item never draws an ellipsis, it clips in the middle ("v Jones Indus") */
.ivt-ixtabs.nv-tabs > .chip { display: block; text-align: center; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; font: inherit; font-size: 13.5px; padding-left: 8px; padding-right: 8px; }
@media (max-width: 767.98px) { .ivt-ixtabs.nv-tabs { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.ivt-ixhead { display: flex; justify-content: space-between; gap: 12px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 8px; }
.ivt-ixhead .s { font-size: 12.5px; color: var(--nv-ink-2); }
.ivt-ixbandwrap { display: flex; flex-direction: column; gap: 4px; min-width: 160px; }
.ivt-ixbandwrap .ivt-band { min-width: 140px; }
.ivt-ixstats { grid-template-columns: repeat(5, minmax(0, 1fr)); margin-bottom: 6px; }
.ivt-gridtotal td { text-align: right; padding-top: 12px; padding-bottom: 12px; }
.ivt-gridtotal .ivt-totlab { color: var(--nv-ink-2); font-weight: 400; }
.ivt-gridtotal .ivt-totval { font-size: 24px; font-weight: 200; margin-left: 14px; font-variant-numeric: tabular-nums; letter-spacing: .01em; color: var(--nv-ink); }
.ivt-gridtotal .ivt-totval.neg { color: #e79a9a; }
/* v31.408 (owner): the totals sit under the columns they sum — one line per settlement
   currency; the base-currency equivalent small beneath */
.ivt-gridtotal td.n { white-space: nowrap; }
.ivt-gridtotal td.n .ivt-totval { margin-left: 0; }
.ivt-gridtotal.ivt-gridtotal-more td { padding-top: 0; border-top: 0; }   /* one footer, several currencies — no rule between them */
.ivt-gridtotal .ivt-totccy { margin-left: 10px; font-size: 12px; letter-spacing: .06em; color: var(--nv-ink-3); }
.ivt-gridbase td { padding-top: 0; padding-bottom: 12px; font-size: 12.5px; color: var(--nv-ink-2); }
.ivt-gridbase b { font-weight: 500; color: var(--nv-ink); }
.ivt-gridtotal .ivt-totnarrow { display: none; }   /* the cash total stands in under Gross only where the Cash column is hidden */
@media (max-width: 767.98px) { .ivt-gridtotal td.n .ivt-totval { font-size: 18px; } .ivt-gridtotal .ivt-totnarrow { display: inline; } }
/* v31.410 (owner): the report library's pills — equal width, two to a line */
.ivt-rptacts { display: inline-flex; gap: 8px; flex: 0 0 auto; }
.ivt-rptacts .ivt-rptbtn { width: 64px; min-width: 64px; padding-left: 0; padding-right: 0; justify-content: center; text-align: center; }
.ivt-rptacts .dbtag { align-self: center; }
/* ══ v31.412 — THE SHARED FILL ON THE INVOICE WIZARD (Read document · AI Update Lines) ══
   a required field the document could not settle wears a red border and a sentence;
   the fill's answer is a small card under the documents */
.fin-missing .input, .fin-missing .acc-ctrybtn, .fin-missing .nv-acc-code, .fin-missing button.input { border-color: #e05a5a !important; box-shadow: 0 0 0 1px rgba(224,90,90,.35); }
.fin-missing-note { color: #e79a9a; font-size: 12.5px; margin: 4px 0 2px; }
.docfill { border: 1px solid rgba(104,152,232,.22); border-radius: var(--r-card); padding: 10px 12px; font-size: 13px; background: rgba(13,22,42,.35); }
.docfill-msg { font-size: 13.5px; color: var(--nv-ink); }
.docfill-msg.bad { color: #e79a9a; }
/* v31.446 (owner): the "Reading the document and preparing the lines…" notice is in the SYSTEM GREEN, in a pill, not white */
.docfill-msg.busy { color: var(--acc-green); border: 1px solid rgba(63,202,133,.55); border-radius: var(--r-pill); padding: 6px 14px; background: rgba(63,202,133,.08); }
.docfill-warn { color: #e8b857; margin-top: 4px; }
.docfill-tbl { width: 100%; border-collapse: collapse; margin-top: 8px; font-size: 12.5px; }
.docfill-tbl th, .docfill-tbl td { padding: 4px 6px; border-bottom: 1px solid rgba(104,152,232,.12); text-align: left; vertical-align: top; }
.docfill-tbl th { font-weight: 400; color: var(--nv-ink-2); font-size: 11.5px; letter-spacing: .06em; text-transform: uppercase; }
.docfill-tbl td.n, .docfill-tbl th.n { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.docfill-src { font-family: ui-monospace, Consolas, monospace; letter-spacing: .12em; color: var(--nv-gold); }
.docfill-flag { color: #e79a9a; font-size: 12px; margin-top: 2px; }
.docfill-key { font-size: 11.5px; margin-top: 8px; }
.docfill-acts { margin-top: 10px; display: flex; justify-content: flex-end; }
.docfill-acts .btn { width: auto; padding: 0 16px; height: 36px; }
/* v31.419 — the accounts the document could be: offered under the supplier field when nothing was unique */
.docfill-cands { margin: 2px 0 6px; display: flex; flex-direction: column; gap: 4px; }
.docfill-cands .docfill-diag { font-size: 11.5px; color: var(--nv-ink-2); }
.docfill-cand { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; height: 36px; padding: 0 12px; border: 1px solid rgba(104,152,232,.25); border-radius: var(--r-pill); background: rgba(13,22,42,.35); color: var(--nv-ink); font: inherit; font-size: 13px; cursor: pointer; }
.docfill-cand:hover, .docfill-cand:focus-visible { border-color: var(--nv-gold); outline: none; }
.docfill-cand .nm { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.docfill-cand .cd { font-variant-numeric: tabular-nums; color: var(--nv-ink-2); font-size: 12px; white-space: nowrap; }
.docfill-cand .ty { font-size: 11.5px; color: var(--nv-ink-2); white-space: nowrap; }
.docfill-cand.off { opacity: .6; }
/* v31.424 — the invoice offer on the email read page */
.inv-offer { border-color: rgba(232,167,92,.55); }
.inv-offer .inv-kv { display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; font-size: 13.5px; margin: 6px 0 8px; }
.inv-offer .inv-file { font-size: 13px; color: var(--nv-ink-2); margin: 4px 0 10px; }
.inv-offer .inv-btns { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.inv-offer .inv-btns form { margin: 0; min-width: 0; }
.inv-offer .inv-btn { width: 100%; height: 40px; padding: 0 6px; font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
.inv-offer .chip-done { display: inline-block; border: 1px solid var(--nv-gold); color: var(--nv-gold); border-radius: var(--r-pill); padding: 2px 10px; font-size: 12.5px; }

/*  v31.427 (owner) — Investments: the CASH row of target vs actual sits apart from the classes
    that share the invested 100%; the rationale editor is the 25-row rich surface; the pie's caption. */
.ivt-trow-cash { border-top: 2px solid rgba(104,152,232,.22); margin-top: 4px; }
.ivt-cashtgt { text-align: right; }   /* v31.428 (owner): the cash target reads like the class targets above it */
.ivt-rationale { min-height: 500px; }
/*  v31.428 (owner: "fill the mandate card") — from the two-column width the mandate card stretches to the
    right-hand stack's height and the rationale editor takes the room: the card is a column, the form grid's
    last row (the wide rationale field) is the one that grows, and the editor fills that field above the
    status note. Below 1024 the columns stack and the 25-row minimum alone applies. */
@media (min-width: 1024px) {
    .ivt-grid2 > .card.ivt-mandate { align-self: stretch; display: flex; flex-direction: column; }
    .ivt-mandate .ivt-formgrid { flex: 1 1 auto; grid-template-rows: auto auto minmax(0, 1fr); align-content: start; }
    .ivt-mandate .ivt-formgrid .field.wide { display: flex; flex-direction: column; min-height: 0; }
    .ivt-mandate .ivt-formgrid .field.wide .rt-ed { flex: 1 1 auto; }
    .ivt-mandate .ivt-notebox { flex: 0 0 auto; }
}
.ivt-piecap { text-align: center; padding-bottom: 4px; }
.ivt-allocrow-cash { border-top: 2px solid rgba(104,152,232,.22); }

/* ══ v31.437 (owner) — THE ASK SHEET on the message's invoice card: the sender is a supplier of
   another entity. Hit rows and buttons share ONE height and ONE width (owner: "make the buttons
   and fields the same size"); the Known-in chips sit in the card's own key/value grid. ══ */
.inv-offer .inv-known { margin: 2px 0 10px; align-items: center; }
.inv-offer .inv-chip { display: inline-block; border: 1px solid rgba(104,152,232,.25); border-radius: var(--r-pill); padding: 1px 9px; font-size: 12.5px; margin: 1px 4px 1px 0; }
.inv-offer .inv-chip-ok { border-color: rgba(63,199,154,.6); color: #3fc79a; }
.inv-offer .inv-chip-no { border-color: rgba(224,90,90,.6); color: #e79a9a; }
/* v31.446 (owner): the "Known in" and "Here" pills are the SAME WIDTH — the value column is sized to the widest pill and every pill fills it */
.inv-offer .inv-known { grid-template-columns: auto max-content; }
.inv-offer .inv-known .inv-chip { display: block; width: 100%; box-sizing: border-box; text-align: center; margin: 1px 0; }
.inv-else .inv-rows { display: grid; gap: 8px; }
.inv-else .inv-rows form { margin: 0; min-width: 0; }
.inv-else .inv-hit { display: grid; grid-template-columns: minmax(0,1fr) auto; grid-template-rows: 1fr 1fr; column-gap: 10px; align-items: center; height: 40px; padding: 0 12px; border: 1px solid rgba(232,167,92,.55); border-radius: var(--r-input); font-size: 13px; line-height: 1.2; min-width: 0; }
.inv-else .inv-hit > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.inv-else .inv-hit-ent { font-weight: 500; }
.inv-else .inv-hit-acc { font-size: 12px; }
.inv-else .inv-hit-on { grid-column: 2; grid-row: 1 / span 2; font-size: 12px; color: #3fc79a; }
.inv-else .inv-hit-no { color: #e79a9a; }
.inv-else .inv-hit-cur { border-color: rgba(104,152,232,.22); opacity: .85; }
.inv-else .inv-btn > span { flex: 0 0 auto; }
.inv-else .inv-btn b { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* v31.437 — the wizard's safety net under the unresolved supplier: hit rows and the two buttons
   share one height and width (owner), the rows wear the candidate row's own dress. */
.docfill-cands .docfill-else { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(104,152,232,.18); }
.docfill-cands .docfill-else-title { font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--nv-gold); }
.docfill-cands .docfill-else-row { height: 40px; cursor: default; border-color: rgba(232,167,92,.55); }
.docfill-cands .docfill-else-row:hover { border-color: rgba(232,167,92,.55); }
.docfill-cands .docfill-else-row .ty { color: #3fc79a; }
.docfill-cands .docfill-else form { margin: 0; }
.docfill-cands .docfill-else-btn { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 40px; padding: 0 12px; font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-decoration: none; gap: 2px; }
.docfill-cands .docfill-else-btn b { font-weight: 500; overflow: hidden; text-overflow: ellipsis; }

/* ═══════════════════════════════════════════════════════════════════════════
   v31.439 — THE MESSAGING MODULE (/messages) — the owner's mock-up (docs/
   messaging) in the house shell: conversation list · thread · details inside
   the mail-app content column, the Messages panel in the side column and the
   phone tray. Blue outgoing bubbles, gold accents (scope §1). Sizes are the
   tokens — no new pixel sizes under the floor. Under 1024 the list and the
   thread take turns (`.msg-work.chatopen`). The toast (`.msg-toast`) is drawn
   by nv-msgtoast.js on EVERY page and lives at the end of <body>.
   ═══════════════════════════════════════════════════════════════════════════ */
.msg-scroll { gap: 12px; }
.msg-demo { padding: 4px 2px; font-size: var(--fs-caption); }
.msg-notready p { margin: 8px 0 0; }
.msg-work { display: grid; grid-template-columns: minmax(280px, 340px) minmax(0, 1fr); gap: 14px; height: calc(100vh - 150px); min-height: 520px; }
.msg-work { position: relative; }
/*  ⚠⚠ v31.548 — ONE DECLARATION OF THE PANE FILL, because it is now needed in
    THREE places and a colour written three times is a colour that drifts in
    one of them (the lesson `InvFeed.ExchangeCode` carries about venue codes,
    one layer down). The fallback keeps any pane that renders outside
    `.msg-app` on the same colour rather than on nothing. */
.msg-app { --msg-pane-fill: rgba(13,22,42,.35);
           /*  ⚠ THE GROUND THE 04 COLUMN ACTUALLY SITS ON, MEASURED — not the body
               colour. `.app` paints the house panel gradient behind the whole work
               area and it is effectively flat across a 300px column at the right
               edge: sampled rgb(28,28,28) at four points, which is that gradient's
               middle stop. The floating pane at 03 composites over THIS so the two
               bands render the same surface. */
           --msg-pane-ground: #1e1e1e; }
.msg-list, .msg-chat, .msg-details { border: 1px solid rgba(104,152,232,.18); border-radius: var(--r-card); background: var(--msg-pane-fill, rgba(13,22,42,.35)); min-height: 0; display: flex; flex-direction: column; }
/*  the details pane: a third column where there is room, an overlay on the thread's right below that.

    ⚠⚠⚠ v31.500 (owner) — 1366, NOT 1500. He asked for his own profile card on the right *"for only
    04-Screen and 05-Screen"*, and the house ladder (app.css §note at .nt-workrow) puts 04-Screen at
    1366–1919. At 1500 the lower half of 04-Screen had no third column at all, so the card he asked
    for would have had nowhere to sit on a 1440 laptop — which is the commonest 04-Screen there is.

    ⚠⚠ THE NUMBER IS IN TWO PLACES AND THEY ARE ONE DECISION. `nv-messages.js` matchMedia's the same
    query to decide whether the pane opens by itself; if these two ever disagree the pane opens as an
    overlay over the thread on exactly one band of widths, which is the kind of fault that gets
    reported as "it flickers". quick500 arm 2 reads both and fails if they differ. */
/*  ══ v31.548 (owner) — 03-SCREEN DRAWS ON THE SAME GROUND AS 04 ════════════
    *"This 03-Screen background should be the same as 04-Screen background."*

    ⚠⚠⚠ IT HAD TO BE OPAQUE, AND THAT IS WHY IT WAS WRONG. At 03 this pane is
    an OVERLAY on the thread (`position: absolute`, z-index 6), so the
    translucent pane fill would have let the conversation show through it —
    which is why it was given `--nv-bg-deep`. But `--nv-bg-deep` is the PAGE's
    own ground, and the 04 column is the pane fill composited OVER that ground:
    measured, rgb(5,9,18) here against rgb(8,14,26) there. Two different
    colours for one surface, which is what he could see.

    ⚠⚠⚠ AND THE GROUND IS NOT THE PAGE COLOUR — MEASURE IT, DO NOT READ IT OFF
    A TOKEN. My first cut flattened the fill over `--nv-bg-deep` (the body's
    rgb(5,9,18)) and came out at rgb(8,14,27) against 04's rgb(23,26,33): still
    two colours. The ground behind that column is NOT the body. `.app` carries
    the house panel gradient, so what 04 actually composites over is about
    rgb(28,28,28) — the gradient's middle. Hiding the pane and sampling the
    pixel underneath is what settled it; the ancestor chain's background COLOURS
    are all transparent down to the body and say nothing about it.

    ⚠⚠ AND NOT THE PANEL GRADIENT EITHER — THAT WAS THE SECOND WRONG GROUND.
    `--nv-grad-panel` starts at rgb(1,1,7), so grounding the pane on it made the
    TOP of the pane darker than its bottom (sampled rgb(5,8,19) at +14px rising
    to rgb(6,10,21) at +32), while 04 is flat rgb(23,26,33) at every point. A
    gradient cannot match a flat surface; the ground has to be the flat colour
    that surface composites over. Measured after: rgb(24,27,34) at 03 against
    rgb(23,26,33) at 04 — one unit, on every channel, at every point. */
.msg-details { position: absolute; top: 0; right: 0; bottom: 0; width: min(300px, 100%); z-index: 6; box-shadow: -12px 0 32px rgba(0,0,0,.35);
               background: linear-gradient(var(--msg-pane-fill, rgba(13,22,42,.35)), var(--msg-pane-fill, rgba(13,22,42,.35))), var(--msg-pane-ground, #1e1e1e); }
@media (min-width: 1366px) {
    .msg-work:has(.msg-details:not([hidden])) { grid-template-columns: minmax(280px, 340px) minmax(0, 1fr) 300px; }
    .msg-details { position: static; width: auto; box-shadow: none; background: var(--msg-pane-fill, rgba(13,22,42,.35)); }
}
/*  the reader's own card — the same pane, so it inherits the padding, the close control and the
    headings; only the rows below the head differ. */
.msg-ownrow { padding: 2px 0 2px; color: var(--nv-ink); }
.msg-ownhint { margin: 16px 0 0; font-size: var(--fs-caption); line-height: 1.45; }
/*  ⚠ the pane is a flex column, so a chip left to itself stretches the full width and stops
    reading as a button. */
.msg-ownun { height: 30px; padding: 0 12px; align-self: flex-start; width: auto; }
.msg-search { display: flex; gap: 8px; padding: 12px 12px 8px; }
.msg-search .input { flex: 1 1 0; min-width: 0; height: 40px; }
.msg-newbtn { width: 40px; height: 40px; padding: 0; flex: 0 0 40px; font-size: var(--fs-body); }
.msg-tabs { display: flex; gap: 6px; padding: 0 12px 10px; flex-wrap: wrap; }
/*  ⚠ v31.500 (owner) — "Hide the Pinned chip from view in 03-Screen size only." 1024–1365 is where
    the list column is at its narrowest relative to four chips plus the search row, and the fourth
    chip was pushing the set onto a second line. The chip is HIDDEN, not removed: `?f=pinned` still
    works, the filter is still reachable from a link or the back button, and the row it lands on
    still shows the chip as selected on every other width. */
@media (min-width: 1024px) and (max-width: 1365.98px) {
    .msg-tabs .msg-fpin { display: none; }
}
.msg-tabs .chip { height: 32px; padding: 0 12px; }
.msg-rows { flex: 1 1 0; min-height: 0; overflow-y: auto; overflow-x: hidden; }
.msg-none { padding: 18px 14px; }
.msg-row { display: flex; gap: 11px; align-items: center; padding: 10px 12px; text-decoration: none; color: inherit; border-top: 1px solid rgba(104,152,232,.10); min-width: 0; }
.msg-row:hover { background: rgba(120,165,240,.06); }
.msg-row.sel { background: rgba(232,167,92,.12); }
.msg-rbody { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.msg-rtop, .msg-rbot { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.msg-rname { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--nv-ink); }
.msg-row.unread .msg-rname { font-weight: 400; }
.msg-rtime { flex: 0 0 auto; font-size: var(--fs-caption); color: var(--nv-ink-2); font-variant-numeric: tabular-nums; }
.msg-rprev { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-caption); color: var(--nv-ink-2); }
.msg-row.unread .msg-rprev { color: var(--nv-ink); }
.msg-count, .msg-pcount { flex: 0 0 auto; min-width: 22px; height: 22px; padding: 0 6px; border-radius: 11px; background: var(--nv-blue); color: #fff; display: inline-grid; place-content: center; font-size: var(--fs-caption); font-variant-numeric: tabular-nums; }
.msg-pcount { margin-left: auto; }
.msg-pin { color: var(--nv-gold); margin-right: 4px; }
.msg-mutedmark { margin-right: 4px; opacity: .8; }
.msg-av { flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%; display: inline-grid; place-content: center; background: linear-gradient(160deg, rgba(232,167,92,.28), rgba(232,167,92,.08)); border: 1px solid rgba(232,167,92,.45); color: var(--nv-gold-bright); font-size: var(--fs-caption); letter-spacing: .04em; }
.msg-av.grp { background: linear-gradient(160deg, rgba(74,146,221,.32), rgba(74,146,221,.08)); border-color: rgba(74,146,221,.5); color: #9cc3f0; }
.msg-av.sm { width: 30px; height: 30px; }
/* v31.441 (owner): a picture from RefUsers.DocDetails covers the circle; the initials stay underneath for a failed load */
.msg-av { position: relative; overflow: hidden; }
.msg-av img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.msg-av.has { color: transparent; }
.msg-av.big { width: 72px; height: 72px; font-size: var(--fs-h2); }
/* the thread */
.msg-chathead { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-bottom: 1px solid rgba(104,152,232,.14); min-height: 62px; }
.msg-chathead:empty { display: none; }
.msg-hname { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; }
.msg-hname .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--nv-ink); }
/*  ⚠⚠ v31.531 — THE SUBTITLE NEEDED THE SAME ELLIPSIS THE NAME HAS HAD, and
    without it the owner's 01-Screen photograph is what happens: `.msg-hname`
    is `flex: 1 1 0; min-width: 0`, so in a tight header it collapses toward
    zero — the NAME ellipsises away as it is told to, while *Direct*, having
    no overflow rule of its own, simply painted straight out of its own box
    and across the Pin button beside it.

    ⚠ The ellipsis is the floor, not the fix: a one-word subtitle clipped to
    "D…" is not worth the row it costs, so at 01-Screen it goes entirely —
    see the 767 block below. [stated] owner: *"Where the person's name is
    hidden, also hide Direct."*                                              */
.msg-hname .s { font-size: var(--fs-caption); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/*  v31.531 — [stated] *"Slightly widen the Pin button and centre its visible
    label."* The chip was sized by its content alone, so Pin and Unpin came out
    different widths and the label sat hard against the left edge. Centring is
    safe on every chip in this header; the FLOOR is not, and is given only to
    the one the owner named.

    ⚠⚠ `.msg-hbtn` is worn by Pin AND Select. Flooring the class put 84px under
    both and pushed the header 14px past its own box at 360 — measured, not
    guessed. One extra selector is cheaper than a header that overflows on the
    narrowest phone this app supports.                                        */
.msg-hbtn { height: 32px; justify-content: center; text-align: center; }
#msgPinBtn { min-width: 84px; }
/*  ⚠ and the floor yields below 380, where the row genuinely has no room for
    it — a widened button is a nicety, a header that fits is not. */
@media (max-width: 379.98px) { #msgPinBtn { min-width: 0; } }
.msg-back { display: none; }
.msg-thread { flex: 1 1 0; min-height: 0; overflow-y: auto; overflow-x: hidden; padding: 12px 14px; display: flex; flex-direction: column; gap: 6px; }
.msg-day { align-self: center; margin: 8px 0 4px; }
.msg-day span { display: inline-block; padding: 2px 10px; border-radius: var(--r-pill); border: 1px solid rgba(104,152,232,.2); font-size: var(--fs-caption); color: var(--nv-ink-2); }
.msg-sys { align-self: center; text-align: center; font-size: var(--fs-caption); padding: 2px 10px; }
.msg-b { position: relative; max-width: min(72%, 560px); align-self: flex-start; padding: 8px 12px 20px; border-radius: 14px 14px 14px 4px; background: var(--nv-surface-2); border: 1px solid rgba(104,152,232,.16); color: var(--nv-ink); overflow-wrap: anywhere; }
/*  ⚠ v31.556 (owner) — THE OUTGOING PILL, HIS VALUES VERBATIM. He sent the
    rule itself, so only the one declaration that differed was changed:
    `linear-gradient(160deg, var(--nv-blue-bright), var(--nv-blue))` became
    `linear-gradient(33deg, var(--nv-blue-bright), var(--nv-bg))`. The radius,
    the alignment, the border colour and the white text are what they were.
    ⚠⚠ The second stop is now the APP BACKGROUND (`#0b1020`), so the pill's far
    corner sits at the pane's own colour — the bright border is what keeps the
    bubble's shape readable, and it is unchanged. Measured after the change at
    the two ends of the sweep; see tools/quick556.py arms 1 and 1a. */
.msg-b.out { align-self: flex-end; border-radius: 14px 14px 4px 14px; background: linear-gradient(33deg, var(--nv-blue-bright), var(--nv-bg)); border-color: var(--nv-blue-bright); color: #fff; }
.msg-b.pend { opacity: .65; }
.msg-b.fail { border-color: var(--nv-red); cursor: pointer; }
.msg-b .msg-who { display: block; font-size: var(--fs-caption); color: var(--nv-gold); margin-bottom: 2px; }
.msg-b .msg-txt { display: block; }
.msg-b .msg-meta { position: absolute; right: 10px; bottom: 3px; font-size: var(--fs-caption); opacity: .75; white-space: nowrap; font-variant-numeric: tabular-nums; }
.msg-b.fail .msg-meta { color: #e79a9a; opacity: 1; }
.msg-tick { margin-left: 4px; }
.msg-tick.all { color: #bdf1dd; }
.msg-b .msg-img { display: block; margin: 6px 0 2px; border-radius: 8px; overflow: hidden; max-width: 320px; }
.msg-b .msg-img img { display: block; max-width: 100%; height: auto; }
.msg-file { display: inline-flex; align-items: center; gap: 6px; margin: 6px 6px 2px 0; padding: 4px 10px; border-radius: var(--r-pill); border: 1px solid rgba(255,255,255,.25); color: inherit; text-decoration: none; font-size: var(--fs-caption); max-width: 100%; }
.msg-file > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-file small { opacity: .7; }
.msg-earlier { align-self: center; margin-bottom: 6px; }
.msg-typing { padding: 0 14px 4px; font-size: var(--fs-caption); min-height: 20px; }
.msg-leftnote { padding: 10px 14px; border-top: 1px solid rgba(104,152,232,.14); }
.msg-composer { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid rgba(104,152,232,.14); position: relative; }
/* v31.445 (owner's picture): the controls stand in a column to the RIGHT of the text field — 📎 🙂 above, ➤ at the
   foot — and the field takes the full width; its floor is the column's height so the two share top and bottom edges,
   and it still grows with the text (v31.442). */
.msg-cstack { display: flex; flex-direction: column; align-items: stretch; gap: 3px; flex: 0 0 36px; width: 36px; }
.msg-cbtn { width: 36px; height: 30px; flex: 0 0 30px; }
.msg-cbtn svg { width: 22px; height: 22px; }
.msg-cbox { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
/* v31.442 (owner): one row that grows with the text — no scrollbar inside the box, the thread gives way */
.msg-cbox textarea.input { resize: none; height: auto; min-height: 102px; max-height: none; line-height: 1.35; padding-top: 9px; overflow: hidden; }
.msg-composer { flex: 0 0 auto; }
.msg-thread { flex: 1 1 0; }
.msg-send { width: 36px; height: 36px; padding: 0; flex: 0 0 36px; min-width: 0; }
.msg-pending { display: flex; flex-wrap: wrap; gap: 6px; }
.msg-pf { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; border-radius: var(--r-pill); border: 1px solid rgba(232,167,92,.45); font-size: var(--fs-caption); }
.msg-pf button { border: 0; background: none; color: inherit; cursor: pointer; font: inherit; padding: 0 2px; }
/*  ══════════════════════════════════════════════════════════════════════════════════════════════
    v31.457 (owner: "add the full range of emojis") — THE HOUSE EMOJI PICKER, `nv-emoji.js`.
    The old `.msg-emojis` was an eight-column grid of twenty-four, positioned inside the composer.

    ⚠⚠ THE PANEL IS `position: fixed` AND LIVES IN THE BODY. Anchored inside the composer it was at
    the mercy of every `overflow` and every stacking context between it and the page — which on the
    01/02-Screen layout means the chat pane's own scroller. The script places it; this only says how
    it looks and that nothing may clip it.
    ⚠ The grid auto-fills rather than fixing a column count, so the same rules serve the phone sheet
    and the desktop panel without a second breakpoint.
    ══════════════════════════════════════════════════════════════════════════════════════════════ */
.nv-emo { position: fixed; z-index: 90; width: 344px; max-width: calc(100vw - 16px); display: flex; flex-direction: column; gap: 6px; padding: 8px; border-radius: var(--r-card); border: 1px solid rgba(104,152,232,.25); background: var(--nv-bg-deep); box-shadow: 0 18px 40px rgba(0,0,0,.45); }
.nv-emo[hidden] { display: none; }
.nv-emo-top { display: flex; }
.nv-emo-q { width: 100%; height: 32px; font-size: var(--fs-caption); }
.nv-emo-tabs { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; border-bottom: 1px solid rgba(104,152,232,.18); padding-bottom: 4px; }
.nv-emo-tabs::-webkit-scrollbar { display: none; }
.nv-emo-tabs button { flex: 0 0 auto; width: 30px; height: 28px; border: 0; background: none; cursor: pointer; font-size: var(--fs-caption); border-radius: 6px; opacity: .65; }
.nv-emo-tabs button:hover { background: rgba(120,165,240,.12); opacity: 1; }
.nv-emo-tabs button.on { opacity: 1; box-shadow: inset 0 -2px 0 var(--nv-gold); }
.nv-emo-grid { height: 232px; overflow-y: auto; overscroll-behavior: contain; }
.nv-emo-rows { display: grid; grid-template-columns: repeat(auto-fill, minmax(34px, 1fr)); gap: 1px; }
/*  ⚠ the colour-emoji font is named explicitly: without it a system whose default sans has a
    monochrome glyph for a symbol (⚠ ❤ ✈ and the rest of the pre-2010 set) draws it in outline,
    so the picker and the sent message disagree about what the reader chose.

    ⚠⚠ v31.500 — 'Noto Color Emoji' MOVES TO THE FRONT HERE, AND ONLY HERE-LIKE PLACES. These two
    elements draw one emoji and never a figure, so the digit trap described in tokens.css cannot
    bite them, and the picker must show the reader the SAME drawing the bubble will. Everywhere
    else the family sits at the END of the text stack, behind Poppins. */
/*  ⚠⚠⚠ v31.503b — THE MACHINE'S OWN EMOJI FONT DRAWS FIRST, AND NOTO IS THE FALLBACK.

    The owner reported the picker empty a second time, and his screenshot settles what it is:
    the TAB ICONS are in colour and the GRID CELLS and the TONE BUTTONS are blank — with a
    scrollbar, so the cells are there and have height. The tabs take the BODY stack (Poppins …
    'Noto Color Emoji' last, so the OS font answers); the cells and the tones were the only two
    places that put 'Noto Color Emoji' FIRST. One stack paints, the other paints nothing: the
    webfont is resolving on his machine and rasterising to nothing — the known COLRv1 failure
    on older Chromium/Edge builds, where the family loads, claims the codepoints, and draws
    empty boxes. `font-display: swap` cannot help: nothing is pending, the glyphs are blank.

    ⚠⚠ A FONT CAN FAIL BY DRAWING NOTHING, AND THAT IS NOT A LOADING FAILURE. Every guard this
    file had — the fallback chain, swap, the tofu canvas — defends against a font that does not
    ARRIVE. None of them can see one that arrives and paints air.

    So the order is reversed here: the machine's own emoji font, which is demonstrably drawing
    (the tabs prove it on his own screen), and the self-hosted Noto behind it for a machine that
    has none. ⚠ The family still appears, so nothing the owner asked for in v31.500 is thrown
    away — it is the second choice rather than the first until we know why it paints blank. */
.nv-emo-c { width: 34px; height: 34px; border: 0; background: none; cursor: pointer; border-radius: 6px; line-height: 1; padding: 0;
            font-size: 21px; font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", sans-serif; }
.nv-emo-c:hover { background: rgba(120,165,240,.14); }
.nv-emo-c:focus-visible { outline: 2px solid var(--nv-gold); outline-offset: -2px; }
.nv-emo-none { margin: 18px 8px; font-size: var(--fs-caption); color: var(--nv-muted, #93a3bd); text-align: center; }
.nv-emo-tones { display: flex; gap: 2px; border-top: 1px solid rgba(104,152,232,.18); padding-top: 5px; }
.nv-emo-tones button { width: 30px; height: 26px; border: 0; background: none; cursor: pointer; border-radius: 6px; font-size: 15px; opacity: .6;
                       font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; }
.nv-emo-tones button:hover { opacity: 1; background: rgba(120,165,240,.12); }
.nv-emo-tones button.on { opacity: 1; background: rgba(120,165,240,.18); }
@media (max-width: 480px) { .nv-emo { width: calc(100vw - 16px); } .nv-emo-grid { height: 46vh; } }
/*  ⚠ v31.500 (owner: "make more prominent") — the empty thread is the biggest surface on this page
    and it was whispering across it: a 48px outline and one muted line, both in the caption colour
    used for things the reader is meant to skim past. It is the only instruction a reader with
    nothing open has. The mark is bigger and carries the gold this application uses for "act here",
    the line is heading-sized and in the primary ink, the second line says where to look, and the
    action the sentence names is now a button that does it. */
/*  v31.501 (owner, quick) — 50px of air, and the line itself in gold. */
.msg-empty { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; text-align: center; padding: 50px; }
.msg-emptymark { color: var(--acc-gold); opacity: .85; margin-bottom: 2px; }
.msg-emptyt { font-size: var(--fs-h2, 20px); color: var(--nv-gold); line-height: 1.3; max-width: 22em; }
.msg-emptys { font-size: var(--fs-caption); max-width: 24em; }
.msg-emptybtn { margin-top: 8px; height: 40px; padding: 0 18px; }
.msg-chat:has(.msg-chathead:not(:empty)) .msg-empty { display: none; }
/* details */
.msg-details { padding: 16px 14px; overflow-y: auto; gap: 6px; }
.msg-dhead { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; margin-bottom: 10px; position: relative; }
/*  ══ v31.550 (owner) — THE PHOTO STANDS BESIDE ITS DETAILS, NOT ABOVE THEM ══
    *"for all sizes place photo to the left of photo details to reduce the
    height of the section"*, with the stacked version crossed out.

    ⚠⚠ CSS ONLY, AND SCOPED BY `:has(.msg-av)`. The same `.msg-dhead` draws the
    FOLDER card (v31.530), which has a heading and no photo at all — given two
    columns unconditionally its `h2` would sit in the avatar's track and the
    card would read as indented for no reason. The selector asks for the thing
    the layout is about rather than for a flag some caller has to remember to
    set, which is the `_NvEntity` lesson one level down.
    ⚠ The avatar spans BOTH rows so the name sits on its upper half and the
    kind on its lower, which is what he drew. `align-self` on the two text rows
    pulls them together against the middle rather than spreading them.
    ⚠⚠ AND THE HEAD KEEPS ITS RIGHT-HAND CLEARANCE. `.msg-dclose` is absolutely
    positioned top-right and is OUT OF FLOW, so the name — now on the same line
    rather than below a centred photo — would otherwise run under the ✕. */
.msg-dhead:has(.msg-av) { display: grid; grid-template-columns: auto minmax(0, 1fr);
                          align-items: center; column-gap: 12px; row-gap: 0;
                          text-align: left; padding-right: 34px; }
.msg-dhead:has(.msg-av) > .msg-av { grid-column: 1; grid-row: 1 / span 2; }
.msg-dhead:has(.msg-av) > h2 { grid-column: 2; grid-row: 1; align-self: end; margin: 0; }
.msg-dhead:has(.msg-av) > .msg-dkind { grid-column: 2; grid-row: 2; align-self: start; justify-content: flex-start; }
.msg-dhead:has(.msg-av) > .muted { grid-column: 2; grid-row: 2; align-self: start; }
/*  ⚠ renaming a group replaces the h2 with a field row — it takes the whole
    width, or it is typed into a column the photo has already narrowed. */
.msg-dhead:has(.msg-av) > .msg-rename { grid-column: 1 / -1; grid-row: 3; margin-top: 6px; }
.msg-dclose { position: absolute; right: 0; top: 0; }
.msg-rename { display: flex; gap: 6px; width: 100%; }
.msg-rename .input { flex: 1 1 0; min-width: 0; height: 36px; }
.msg-rename .btn { width: auto; flex: 0 0 auto; height: 36px; padding: 0 12px; }
.msg-dhead h2 { margin: 4px 0 0; font-size: var(--fs-body); font-weight: 400; }
.msg-dh { margin: 14px 0 6px; font-size: var(--fs-caption); font-weight: 400; letter-spacing: .06em; text-transform: uppercase; color: var(--nv-gold); }
.msg-members { display: flex; flex-direction: column; gap: 6px; }
.msg-member { display: flex; align-items: center; gap: 8px; min-width: 0; }
.msg-mname { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-rm { width: 28px; height: 28px; }
.msg-dacts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.msg-dacts .chip { height: 32px; }
/*  ⚠⚠⚠ v31.543 (owner) — NINE AT A TIME, THEN IT SCROLLS. *"Make this section
    a maximum of height containing only 9 images visible at a time and the
    scroll to view the rest."*

    ⚠⚠ THE CAP IS A HEIGHT, NOT A COUNT, and that is deliberate: three rows of
    72px thumbnails plus two 6px gaps. In the details pane three fit a row, so
    the cap IS nine; on a wider pane more fit a row and more are visible, which
    is the right answer to a reader with more room rather than nine enforced
    into a column of white space.
    ⚠ `overscroll-behavior: contain` — a flick that reaches the end of this
    grid must not then drag the pane underneath it, the same rule `.nv-abovenav`
    carries for every sheet. */
/*  ⚠⚠⚠ v31.549 — `flex: 0 0 auto`, AND IT IS LOAD-BEARING SINCE THE CARD WAS
    ANCHORED. `.msg-details` is a flex COLUMN. While the card's height was its
    content's, nothing shrank; given both edges (v31.549) the card has a
    DEFINITE height, so every child became shrinkable and the media strip was
    the one that gave — measured 20px at 01-Screen against its 75px cap, a
    sliver of a row. The cap is a cap, not a target: the card scrolls, the
    strip keeps its size. */
.msg-media { display: flex; flex-wrap: wrap; gap: 6px; max-height: 228px; overflow-y: auto; overscroll-behavior: contain; flex: 0 0 auto; }
/*  ══ v31.548 (owner) — THE CAP IS A LADDER, NOT ONE NUMBER ═════════════════
    *"The media should have different max height to render page properly."*
    His values, one per band. The grid is 72px thumbnails with a 6px gap, so
    the only honest stops are 75 (one row), 150 (two) and 228 (three) — a cap
    between them cuts a row in half and reads as a rendering fault.

      01  ≤767    75px   one row · the sheet is the whole screen here and the
                         Actions block below it must survive (owner's own
                         value, found by hand before he asked)
      02  768-1023 150px  two rows · the sheet is wider, so a row holds six to
                         nine and two rows is most of a small library
      03  1024-1365 75px  one row · the pane is 300px, three to a row, and it
                         shares the height with the thread beside it
      04/05 ≥1366  228px  three rows, unchanged — the pane is its own column

    ⚠⚠ NARROWEST LAST. Each query is `max-width`, so they overlap: 01 is also
    under 1023 and under 1365. Written in any other order the wide rule wins on
    a phone and the ladder silently collapses to one value.
    ⚠ THE BAND IS 767, NOT THE 480 IN HIS PASTE. 480 would leave 481-767 — most
    of 01-Screen, every large phone — uncapped at three rows, which is the
    layout he is reporting. The house ladder is 01 ≤767. */
@media (max-width: 1365.98px) { .msg-media { max-height: 75px; } }
@media (max-width: 1023.98px) { .msg-media { max-height: 150px; } }
@media (max-width: 767.98px) { .msg-media { max-height: 75px; } }
.msg-thumb { width: 72px; height: 72px; border-radius: 6px; overflow: hidden; border: 1px solid rgba(104,152,232,.2); }
.msg-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* the picker sheet (a dialog needs its own fill — .card has none) */
.msg-modalback { position: fixed; inset: 0; z-index: 60; background: rgba(5,9,18,.72); display: grid; place-items: center; padding: 16px; }
.msg-modal { width: min(560px, 100%); max-height: min(80vh, 720px); display: flex; flex-direction: column; gap: 10px; background: var(--nv-bg-deep); border: 1px solid rgba(232,167,92,.45); padding: 16px; }
.msg-mhead { display: flex; align-items: center; }
.msg-mhead h2 { margin: 0; }
/* v31.441 (owner): six rows visible without a scrollbar — the list is as tall as its rows up to six (48px each), then scrolls */
.msg-users { flex: 0 1 auto; max-height: 288px; overflow-y: auto; display: flex; flex-direction: column; gap: 0; }
.msg-user { display: flex; align-items: center; gap: 10px; height: 48px; flex: 0 0 48px; padding: 0 8px; border-radius: 8px; cursor: pointer; }
.msg-user:hover { background: rgba(120,165,240,.08); }
.msg-uname { display: flex; flex-direction: column; min-width: 0; }
.msg-uname small { font-size: var(--fs-caption); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-mfoot { display: flex; align-items: center; gap: 10px; }
.msg-mfoot .muted { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-caption); }
.msg-mfoot .btn { width: auto; flex: 0 0 auto; min-width: 300px; }   /* v31.441 (owner): Start conversation ≥ 300px */
body.msg-sheet-open .screen-head { z-index: 1; }
/* the toast, any page */
.msg-toasts { position: fixed; right: 16px; bottom: 92px; z-index: 70; display: flex; flex-direction: column; gap: 10px; width: min(380px, calc(100vw - 32px)); }
.msg-toast { background: var(--nv-bg-deep); border: 1px solid rgba(232,167,92,.55); box-shadow: 0 12px 36px rgba(0,0,0,.45); padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
.msg-trow { display: flex; align-items: center; gap: 10px; }
.msg-tname { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; }
.msg-tname .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--nv-ink); }
.msg-tname .s { font-size: var(--fs-caption); }
.msg-tx { margin-left: 0; }
.msg-tbody { max-height: 120px; overflow-y: auto; overflow-wrap: anywhere; }
.msg-treply { display: flex; gap: 8px; }
.msg-treply .input { flex: 1 1 0; min-width: 0; height: 40px; }
.msg-treply .btn { width: auto; flex: 0 0 auto; height: 40px; padding: 0 14px; }
.msg-tacts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.msg-tacts .chip { height: 30px; }
.msg-tstate { margin-left: auto; font-size: var(--fs-caption); }
.msg-tstate.ok { color: var(--nv-good); }
.msg-tstate.bad { color: var(--nv-red); }
/* the rail / tray / home badge */
.msg-navbadge { display: inline-grid; place-content: center; min-width: 20px; height: 20px; padding: 0 5px; border-radius: 10px; background: var(--nv-blue); color: #fff; font-size: var(--fs-caption); margin-left: 6px; font-variant-numeric: tabular-nums; }
@media (max-width: 1023.98px) {
    .msg-work { display: block; height: auto; min-height: 0; }
    /* v31.441 (owner): the composer was behind the bottom nav — the panes end above it, and the fifteen-row box caps at a third of the screen here */
    /* v31.442 (owner): the height is set by nv-messages.js (`fitSmall`) to end one gap above the bottom nav */
    .msg-work { padding-bottom: 0; }
    /*  ⚠⚠⚠ v31.530 — `.msg-folderpane` BELONGS IN THIS RULE AND ITS ABSENCE WAS
        THE WHOLE OF THE "folder items not listed" FAULT ON PHONE AND TABLET.
        Below 1024 `.msg-work` is display:block with a height set by fitSmall;
        the panes take `height: 100%` from it. The folder pane was left out, so
        it was height:auto — and its rows live in `flex: 1 1 0` inside it, which
        resolves to ZERO. The head drew, the rows existed in the DOM, and the
        box under them measured 0px. ⚠ A new pane added to this module must be
        added here in the same edit. */
    .msg-list, .msg-chat, .msg-folderpane { height: 100%; min-height: 320px; }
    .msg-cbox textarea.input { max-height: 40vh; overflow: auto; }
    .msg-modal { max-height: calc(100vh - 170px); }
    .msg-modalback { align-items: start; padding: 24px 12px 116px; }
    .msg-mfoot { flex-wrap: wrap; }
    .msg-mfoot .btn { min-width: 0; width: 100%; }
    .msg-work.chatopen .msg-list { display: none; }
    .msg-work:not(.chatopen) .msg-chat { display: none; }
    /*  one pane at a time: a folder takes the screen exactly as a thread does */
    .msg-work:not(.foldopen) .msg-folderpane { display: none; }
    .msg-back { display: inline-grid; }
    /*  the details as a bottom sheet. ⚠ Its height and bottom edge now come
        from the SHARED rule at the end of this file (`.nv-abovenav`), not from
        a literal copied out of the nav's stylesheet. */
    /*  ⚠ v31.534 (owner, verbatim) — the panel gradient and a 10px inset, so the
        sheet reads as a card floating over the thread rather than a slab welded
        to three edges. `--nv-grad-panel` is the same declaration the pop-up and
        the account card read (tokens.css), so the three cannot drift apart.

        ⚠⚠ THE MARGIN IS ALSO TOLD TO `.nv-abovenav`. A margin on a fixed box
        sits OUTSIDE it: `bottom` already lifts the sheet clear of the
        navigation, but `max-height` was computed for a box with no margins, so
        20px of them would have made the sheet that much taller than the band
        it was given and pushed its top under the thread header. The shared rule
        subtracts `--nv-abovenav-gap`, which is 0 everywhere it is not set. */
    .msg-details { position: fixed; inset: auto 0 auto 0; width: auto; z-index: 45; border-radius: var(--r-card) var(--r-card) 0 0; background: var(--nv-grad-panel); box-shadow: 0 -12px 32px rgba(0,0,0,.45);
        /*  ⚠⚠ v31.550 (owner) — *"We do not need the approx 10px gap"*, over the
            strip of thread still showing between the header and the card. The
            card is FLUSH under the header now and keeps its inset on the other
            three sides, so it still reads as a card rather than a slab.
            ⚠⚠⚠ AND THE HEIGHT MATHS HAD TO BE TOLD. The shared rule reserves
            `2 * --nv-abovenav-gap` for the margins it cannot see; left at 10
            while only 10px of margin remains, it reserves 20 and the gap simply
            MOVES TO THE BOTTOM — the fault wearing a different hat. 5 × 2 = the
            10px that is actually there. */
        margin: 0 10px 10px; --nv-abovenav-gap: 5px; }
    .msg-b { max-width: 86%; }
    .msg-toasts { bottom: 76px; }
}

/* v31.447 calendar */
/*  ⚠ ITEM 6 — the search row under the calendar head. `.cal-team` already gives
    it the flex row, the padding and the centred measure at every width; this
    only lets the `.fld-go` wrapper take the row (it is `display:block` and has
    no width of its own inside a flex parent). The input keeps the 44px height
    of the team picker beside/above it. */
.cal-find > .fld-go { flex: 1 1 auto; min-width: 0; }
.cal-find .input { height: 44px; }
/*  ⚠⚠ v31.459 (owner's screenshot: *"All drop-down is too large. Can you make it 30% of
    the row"*) — THE FILTER TAKES 30% AND THE SEARCH BOX TAKES THE REST. It arrived with
    no width of its own, so as a flex child of `.cal-find` it sized to its longest option
    and then shared the row evenly with the search box — a dropdown of three words sitting
    as wide as the thing people actually type in.
    ⚠ `flex: 0 0 30%` with `min-width: 0`, not `width: 30%`: a flex child's `width` is a
    starting point that `flex-grow` then overrides, which is how the first attempt at a
    narrow control in this app came back full width.
    ⚠ BELOW 560 IT GOES FULL WIDTH ON ITS OWN LINE. 30% of a phone is about 100px, which
    cannot hold "Vrátane pripomienok" in any of the five languages. */
.cal-find .cal-findf { flex: 0 0 30%; min-width: 0; height: 44px; }
@media (max-width: 559px) {
    .cal-find { flex-wrap: wrap; }
    .cal-find .cal-findf { flex: 1 1 100%; }
}
/*  the date column of a result row holds a date over a time — two short
    lines, right-aligned so the subjects line up. ⚠ `--fs-caption`, never a
    literal small size. */
.cal-findrow .cal-findwhen { width: 92px; line-height: 1.25; text-align: left; font-size: var(--fs-caption); }
/*  ⚠ ITEM 8 — Notes RENDERED as HTML inside the detail card and the tooltip.
    `.ev-notes .v` is `white-space: pre-wrap` for plain text; markup brings its
    own line structure, so the html variant drops that (a <ul> after pre-wrap
    text would otherwise carry the source's own newlines as blank lines) and
    gives lists and paragraphs the margins a card can afford. */
.ev-notes .v.html { white-space: normal; }
.ev-notes .v.html ul, .ev-notes .v.html ol { margin: 4px 0 4px 18px; padding: 0; }
.ev-notes .v.html li { margin: 2px 0; }
.ev-notes .v.html p { margin: 0 0 6px; }
.ev-notes .v.html b, .ev-notes .v.html strong { color: var(--nv-ink); }

/*  v31.465 — the re-sync button, armed. ⚠ `.ivt-tick` was ALREADY TAKEN by the
    18×18 checkbox at the foot of this file, which is why v31.464's status line
    is `.ivt-live*`: a later rule of the same name silently squashed it to a
    square. Check for the name before adding one. */
.ivt-resync { white-space: nowrap; }
.ivt-resync.is-armed { border-color: #e1a65c; color: #e1a65c; }

/*  ══ v31.468 — THE MESSAGE SAYS WHICH IT IS, IN COLOUR ════════════════════
    Owner: a successful delete "in green", a blocker "in the warning colour".
    ⚠ The tone comes from `InvPageModel.MsgTone`, whose list is the SUCCESSES —
    an unrecognised word is shown as a warning, never as success. The older
    border-colour selectors above are left exactly as they are; these rules add
    the text colour the owner asked for and win on being later and more
    specific. */
.ivt-msg.is-ok { color: var(--nv-good, #3fca85); border-color: rgba(63, 202, 133, .55); }
.ivt-msg.is-ok b { color: var(--nv-good, #3fca85); }
.ivt-msg.is-warn { color: #e1a65c; border-color: rgba(225, 166, 92, .6); }
.ivt-msg.is-warn b { color: #e1a65c; }

/*  v31.469 — the pair delete lives inside the expanded details, so it costs a
    chevron, a click and a confirm. The note is amber because the action is the
    only irreversible one in this module. */
.ivt-unpair > summary { color: #e1a65c; }
/*  ⚠⚠ v31.503 (owner: "fix the removal message overflow") — THE PANEL IS A COLUMN, NOT A ROW.

    `.ivt-reverseform` is `display: flex` because the REVERSAL panel is a note field beside a
    button on one line. The pair-removal reuses the class and is not that shape at all: a 44ch
    paragraph laid beside a button inside a panel that is `position: absolute; right: 0` pushed
    the whole thing past the edge of the card, which is what the owner photographed.

    So this one call site lays out downwards — message, then the two ids, then the button — and
    takes a width it can actually have: 380px where there is room, and the viewport less a gutter
    where there is not, so it cannot run off the side of a narrow window either.

    ⚠⚠⚠ THE SELECTOR IS `.ivt-unpair > .ivt-reverseform`, ONE ELEMENT, NOT A DESCENT. The first cut
    read `.ivt-reverse-card .ivt-unpair .ivt-reverseform` — but `ivt-reverse-card` and `ivt-unpair`
    are two classes on the SAME `<details>`, so that selector asked for an unpair panel nested
    inside a reversal card and matched nothing at all. The CSS parsed, the page rendered, and the
    overflow the owner photographed was still there. quick503's 4a measures the rendered box
    against its card, which is why it caught a rule that was only notionally applied. */
.ivt-unpair > .ivt-reverseform {
    flex-direction: column;
    align-items: stretch;
    min-width: 0;
    width: min(380px, calc(100vw - 32px));
    max-width: calc(100vw - 32px);
}
/*  ⚠⚠⚠ v31.503b — THE OVERFLOW THAT SURVIVED THE OVERFLOW FIX, AND WHY.

    v31.503 laid the panel out as a column and the owner's next photograph showed the SAME
    sentence running off the card. The column was right; the sentence was never wrapping.

    `.ivt-txns td .s { white-space: nowrap; }` (line ~19167) is a GRID rule — it keeps the
    second line of a cell on one line, which is correct for a cell. The removal note is a
    `<div class="s ivt-unpairnote">` sitting inside that same `<td>`, in the expanded details
    row, so it INHERITED a rule written for a table cell and refused to wrap at any width.

    ⚠⚠ A width fix cannot fix a wrapping fault, and the two look identical in a photograph.
    quick503's 4a measured the FORM against its card and passed — the form was inside the card;
    it was the text inside the form that was not. The arm now measures the NOTE as well. */
.ivt-unpairnote,
.ivt-txns td .ivt-unpairnote,
.ivt-txns td .s.ivt-unpairnote,
.ivt-txns td .ivt-unpairnote * { white-space: normal; overflow-wrap: anywhere; word-break: normal; }
.ivt-unpairnote { color: #e1a65c; max-width: 100%; line-height: 1.5; margin-bottom: 8px; }
.ivt-unpairnote b { display: block; margin-bottom: 2px; }
.ivt-unpairids { font-variant-numeric: tabular-nums; font-weight: 600; margin-bottom: 4px; }
/*  the transaction number under the date — a quiet second line, tabular so a column of them
    lines up, and never the thing the eye lands on first. */
.ivt-txno { color: var(--nv-ink-3); font-variant-numeric: tabular-nums; }

/*  ══ v31.470 — THE PORTFOLIO VALUE REGISTER ═══════════════════════════════
    Owner's sample: instrument, position, last, change %, cost basis, market
    value, both money columns totalled, then the change between the totals.
    ⚠ The whole first cell is the link into Holdings, so the target is the row
    rather than the three characters of a ticker. */
.ivt-pvcard .ivt-hscroll { margin-top: 2px; }
.ivt-pvlink { display: block; text-decoration: none; color: inherit; }
.ivt-pvlink:hover .ivt-ticker { text-decoration: underline; }
.ivt-pvrow:hover { background: rgba(255, 255, 255, .025); }
.ivt-pvtable td.n, .ivt-pvcash td.n, .ivt-pvtable th.n, .ivt-pvcash th.n { font-variant-numeric: tabular-nums; }
/*  the change row reads as a conclusion, not as one more total */
.ivt-pvchange td { border-top: 1px solid var(--nv-line); font-weight: 500; }
.ivt-pvcashhead { margin-top: 18px; }
.ivt-pvcash { width: 100%; }
.ivt-pvcash .s { color: var(--nv-ink-2); font-size: 12px; }

/*  v31.471 — the totals labels moved into the fourth column, right aligned.
    ⚠ That column is `ivt-opt`, so below 768 the label would vanish with it:
    `.ivt-totnarrow` is the stand-in in the leading cell, hidden until then
    (the v31.408 idiom, scoped here because that rule only covers .ivt-gridtotal). */
/*  the reconciling line is the card's conclusion — it gets the weight */
.ivt-pvgrand td { border-top: 2px solid rgba(225, 166, 92, .6); }
.ivt-pvtie { margin-top: 6px; padding: 0 8px; color: var(--nv-ink-2); }

/*  v31.471 (owner) — one row per security: the name sits beside the ticker,
    ellipsised, with the whole thing on the link's title for hover. The table
    is fixed-width so the ellipsis has something definite to cut against, and
    the two narrow columns stop Position and Change % taking space the
    instrument needs. Headers may wrap to two lines. */
/*  ⚠⚠⚠ v31.475 — `table-layout: fixed` IS GONE AND MUST NOT COME BACK.
    Under fixed layout the totals row's `colspan="4"` cell keeps columns 2-4
    alive even when every cell in them is display:none, so their `8ch` widths
    were still reserved and the two money columns were squeezed to 35px for
    figures needing 87 — the overlapping text the owner photographed. Auto
    layout drops a hidden column properly and sizes money to its content. */
.ivt-pvtable { width: 100%; }
/*  ⚠ `.ivt-table th.n` sets nowrap at (0,2,1); a bare `.ivt-pvtable th` is (0,1,1)
    and loses, so the numeric headers never wrapped. Matched here on specificity
    and placed later in the file. */
.ivt-pvtable th, .ivt-pvtable th.n { white-space: normal; line-height: 1.25; vertical-align: bottom; }
/*  the name ellipsises against its OWN max-width now, not against a fixed
    column — which is what lets the table use auto layout. */
.ivt-pvtable .ivt-pvnarrow { width: 1%; }
.ivt-pvtable td.ivt-pvinst { max-width: 1px; }   /* auto layout: the child's max-width does the cutting */
.ivt-pvlink { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.ivt-pvlink .ivt-ticker { flex: 0 0 auto; }
.ivt-pvname { flex: 1 1 auto; min-width: 0; max-width: 22ch; overflow: hidden; text-overflow: ellipsis;
              white-space: nowrap; color: var(--nv-ink-2); font-size: 12.5px; }

/*  ══ v31.472 — THE CHANGE ROW IN THE HOUSE +/- COLOURS ════════════════════
    Owner: *"Color code the +/- with green or negative color code used in
    system."* ⚠ `.ivt-total td` paints every totals cell gold at (0,1,1) and
    `.pos`/`.neg` are (0,1,0), so the change row came out gold whichever way
    it went. Matched here at (0,2,1), and SCOPED to this card: the gold totals
    everywhere else are deliberate and are left alone. */
.ivt-pvchange td.pos { color: var(--acc-green, #3fca85); }
.ivt-pvchange td.neg { color: #e79a9a; }
/*  a long broker name is ellipsised, with the whole of it on hover */
.ivt-pvacct { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 0; }

/*  v31.473 (owner) — the selected business analysis on its own, third line of
    an enquiry row. ⚠ It wraps rather than ellipsising: the point of giving it
    a line was that it was being cut off on the date line, so cutting it here
    too would move the fault rather than fix it. */
.nv-barow { color: var(--nv-ink-3, var(--nv-ink-2)); margin-top: 1px; }
.nv-barow .nv-balab { color: var(--nv-ink-2); }
.nv-barow .nv-balab::after { content: ":"; }

/*  ══ v31.475 — THE PORTFOLIO VALUE CARD SIZES OFF ITSELF, NOT THE WINDOW ══
    Owner on a phone: *"For small screens the grids are not responsive."* He
    asked me to check 01, 02 and 03-Screen, and he was right to — 03 was
    broken too, which the screenshot could not have shown him.

    ⚠⚠⚠ THE VIEWPORT WAS THE WRONG SIGNAL. `.ivt-opt` hides below 768, but at
    1100px (03-Screen) the dashboard is two columns and this card is only
    380px wide, so all six columns stayed and nine cells clipped into each
    other — the same damage as the phone, at two and a half times the width.
    What decides whether a column fits is the CARD's width, so that is what
    is measured.

    ⚠⚠ NARROW IS THE BASE, WIDE IS THE ENHANCEMENT — the house rule at
    `accshell`: where @container is unsupported the base must be the safe one.
    So the three columns that always matter (instrument, cost basis, market
    value) are plain CSS, and Last, Position and Change % are added back as
    the card earns the room. Restored in that order: a price is worth more on
    a small card than a unit count, and a day's move is worth least.

    ⚠ `min-width` is the backstop. If even three columns cannot fit, the table
    overflows and `.ivt-hscroll` scrolls it — which is ugly but readable, and
    strictly better than the overlapping text it replaces. */
.ivt-pvcard { container-type: inline-size; container-name: pvcard; }
/*  ⚠ 200, not 260: the backstop existed to force a scroll rather than a crush,
    but the column ladder does that job properly now and a 260px floor was
    itself pushing a 320px phone 6px over. */
.ivt-pvtable { min-width: 200px; }
.ivt-pvtable .pv-c2, .ivt-pvtable .pv-c3, .ivt-pvtable .pv-c4 { display: none; }
/*  ⚠ these thresholds are the card's CONTENT width, which is ~42px less than
    the card: at 04-Screen this card is 631px wide but queries at 589. Measured,
    not guessed — a 700px threshold meant Change % never returned at any width. */
/*  ⚠⚠ v31.544 — TWO OF THESE WERE ~20px OPTIMISTIC, AND IT SHOWED AT
    03-SCREEN. Found while fixing the pill overflow on the same card; measured
    on the scroller, not the card: at a 380px container the table wanted 403
    (Last kept at the old 360 floor) and at 442 it wanted 479 (Position kept at
    the old 440 floor). Both tipped `.ivt-hscroll` into a sideways scroll and
    clipped the last money column mid-figure — `+€3,000.0(`.
    ⚠ The thresholds move, the LADDER does not: the same columns go in the same
    order for the same reason (v31.475). This is that rule's own stated intent
    — *"these thresholds are the card's CONTENT width … Measured, not guessed"*
    — applied to what the content actually measures now that a sixth column
    carries money rather than a repeat of the fifth. */
@container pvcard (min-width: 400px) { .ivt-pvtable .pv-c3 { display: table-cell; } }
@container pvcard (min-width: 470px) { .ivt-pvtable .pv-c2 { display: table-cell; } }
@container pvcard (min-width: 540px) { .ivt-pvtable .pv-c4 { display: table-cell; } }
/*  the fund name gets less room the fewer columns there are to share with */
@container pvcard (max-width: 439.98px) { .ivt-pvname { max-width: 10ch; } }
@container pvcard (min-width: 440px) and (max-width: 619.98px) { .ivt-pvname { max-width: 14ch; } }

/*  v31.475 — the totals label has two homes and exactly one is ever shown:
    column 4 once that column exists (the owner's layout), column 1 below
    that, where it is the only non-money column left. */
.ivt-pvtable tfoot .pvlab1 { display: inline; }
@container pvcard (min-width: 540px) { .ivt-pvtable tfoot .pvlab1 { display: none; } }

/*  ⚠ v31.475 — the last 30px, on a 360px phone. Below this the fund name has
    to go: the ticker identifies the row, the whole name is still on the link's
    title for a long press, and the alternative was a sideways swipe on the
    card the owner looks at most. Padding comes in with it. */
@container pvcard (max-width: 339.98px) {
    .ivt-pvname { display: none; }
    .ivt-pvtable th, .ivt-pvtable td { padding-left: 5px; padding-right: 5px; }
}

/*  ⚠⚠ v31.475 — BELOW ~290px OF CARD, COST BASIS GOES TOO. Two money columns
    at ~87px plus a ticker need about 250px and a 320px phone gives the card
    238px of content: no amount of CSS makes that fit, so the choice is which
    column to lose. Market value stays because it is what the card is for, and
    the change row keeps its percentage, which is the readable half at that
    size. Everything is one tap away on Holdings. */
/*  ⚠⚠⚠ v31.542 — `pv-c5` BECAME `pv-c6` BECAUSE THE MEANING MOVED, NOT THE
    INDEX. The owner swapped Market value into the fifth column and the
    toggling column into the sixth. v31.475's reason for this rule is about the
    COLUMN, not its position — "Market value stays because it is what the card
    is for" — so the class rides the sixth column now. Left on the fifth, the
    swap alone would have started hiding MARKET VALUE on a 320px phone: a
    layout ladder silently inverted by a change that never mentioned it, and
    nothing in either file would have said so. */
.ivt-pvtable .pv-c6 { display: none; }
@container pvcard (min-width: 290px) { .ivt-pvtable .pv-c6 { display: table-cell; } }

/*  ══ v31.542 — THE FOUR-WAY COLUMN, AND THE PILLS THAT DRIVE IT ══════════
    [stated] owner: *"Remove the label … and replace with the three pills
    'Gains', 'Units' and 'High/Low' … The toggle should toggle column 6 … The
    sixth column should default with the 'Gains' values."* Cost basis is the
    fourth, on his ruling: the swap had just put it in column 6, and three
    pills would have meant it was never once displayed there.

    ⚠⚠⚠ ONE CLASS ON THE TABLE CHOOSES, AND NOTHING USES `hidden`. Every value
    is rendered server-side — so a decimal comma stays a decimal comma in
    sk/cs/de/pl — and the sixth column simply shows whichever span the table's
    class selects. No script writes a figure, and v31.541's lesson is honoured
    by construction: there is no `hidden` attribute here for a `display` rule
    to defeat. */
.ivt-pvtable .pvv { display: none; }
.ivt-pvtable.pv-gains .pvv-gains,
.ivt-pvtable.pv-units .pvv-units,
.ivt-pvtable.pv-hilo  .pvv-hilo,
.ivt-pvtable.pv-cost  .pvv-cost { display: inline; }

/*  ⚠⚠ DELIBERATELY NOT `.chip`. The rotator above these (Portfolio Value /
    Portfolio Trend / Markets / Indexes) is `.chip` and swaps the whole PANE;
    these swap one column of the table under them. The owner's note on the
    mock-up is exactly that — "New button pills make different from the above
    buttons" — so: smaller, fully round, gold when chosen, and never the same
    shape as a tab. */
/*  ⚠⚠⚠ v31.543 (owner, HR-2d) — ONE GRID, FOUR EQUAL TRACKS, PUSHED RIGHT.
    *"They should be equal width, not sized based on label/text content … to
    the right of the row they occupy, not centred or to the left."*

    ⚠⚠ THIS IS THE HOUSE PATTERN ALREADY IN THIS FILE, NOT A NEW ONE.
    `.ivt-ranges.nv-tabs` has used `grid-auto-flow: column` + `grid-auto-
    columns: 1fr` + `width: max-content` since v31.401, for this exact
    instruction. `1fr` tracks size to the WIDEST item rather than to each
    label, and `max-content` keeps the row no wider than it needs — the one
    sizing that is both "all equal" and "only as wide as necessary".
    A flex row with `flex: 1` would NOT do it: equal grow shares the LEFTOVER
    space after each item's own content, so the longest label keeps the widest
    button. That is the uneven pair `_MailSelActions` documents at v31.30 and
    the one the owner has now reported three times. */
.ivt-pvpills { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.ivt-pvpillset {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    width: max-content;
    max-width: 100%;
    gap: 6px;
}
.ivt-pvpill {
    font: inherit; font-size: 11.5px; font-weight: 500; letter-spacing: .04em;
    line-height: 1; white-space: nowrap; cursor: pointer;
    padding: 6px 11px; border-radius: 999px;
    color: var(--nv-ink-2, #aab3c8);
    background: rgba(22,33,60,.5);
    border: 1px solid rgba(104,152,232,.30);
}
.ivt-pvpill:hover { border-color: rgba(232,167,92,.55); color: var(--nv-gold); }
.ivt-pvpill.on { border-color: var(--nv-gold); color: var(--nv-gold); background: rgba(232,167,92,.14); }
.ivt-pvpill:focus-visible { outline: 2px solid var(--nv-gold); outline-offset: 2px; }
/*  ⚠ THE CURRENCY LEADS THE ROW RATHER THAN TRAILING IT. Left last, it
    wrapped onto the pills' second line and sat immediately after *Cost basis*,
    where it reads as that pill's own label rather than as the unit the money
    columns are in. First, it qualifies the group it opens. */
.ivt-pvccy { font-size: 11.5px; letter-spacing: .04em; padding-right: 4px; order: -1; }
/*  ⚠ HR-1 — on a phone the four pills wrap onto their own line under the
    heading rather than widening the card. They are a row of controls, not a
    strip of navigation, so wrapping is right here where HR-3 forbids it for a
    menu: nothing is hidden by the wrap, every pill stays on screen. */
/*  ══ v31.544 (owner) — THE PILLS WRAP BY THE CARD'S WIDTH, NOT THE SCREEN'S ══
    *"For 03-Screen only, the pills overflow beyond card. All other screen
    sizes are fine."*

    ⚠⚠⚠ AND THAT IS WHY A MEDIA QUERY WAS THE WRONG INSTRUMENT. v31.543 wrapped
    the row at `max-width: 767.98px` — a VIEWPORT measure — but this card's
    width is not a function of the viewport: from 1024 the dashboard puts
    Portfolio value beside Asset allocation, so the card gets NARROWER as the
    screen gets wider. Measured, card content width against the row's 414px:

        360px screen → 278   (wrapped: fits)        1100 → 380   ← 113px over
        390px screen → 305   (wrapped: fits)        1200 → 442   ←  51px over
        768-1023     → 686   (plenty)               1280 → 491   ←   2px over
                                                    1365 → 516 · 1440 → 589 ok

    The one band that overflowed is the one band no viewport threshold was
    looking at. The card already declares `container-type: inline-size`
    (v31.475), so the ladder below it has been asking the right question since
    then — the pills were asking the wrong one.

    ⚠⚠ TWO THRESHOLDS, BOTH MEASURED, BECAUSE THERE ARE TWO FAILURES.
    (1) Under ~560px of card the heading cannot hold the title AND the row on
        one line — at 1200 the title had already collapsed to 79px and the row
        still hung 51px out of the card — so the row takes its own line,
        right-aligned.
    (2) Under ~420px of card the row cannot fit its own line either (414px
        needed), so it becomes a 2x2 — still equal, still right (HR-2d).
    Above 560 nothing changes: the pills share the heading line exactly as the
    owner already approved at 04/05-Screen.

    ⚠ HR-1 is why neither threshold shrinks the type instead. */
@container pvcard (max-width: 559.98px) {
    .ivt-pvcard h2.section { flex-wrap: wrap; }
    .ivt-pvcard h2.section > .ivt-pvpills { flex: 1 0 100%; width: 100%; margin-top: 6px; justify-content: flex-end; }
}
@container pvcard (max-width: 419.98px) {
    .ivt-pvcard .ivt-pvpillset { grid-auto-flow: row; grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 1023.98px) {
    .ivt-pvpill { font-size: 11px; padding: 7px 10px; }
}

/*  ⚠⚠ v31.544 (owner) — THE WHOLE ROW OPENS THE HOLDING. *"Make the entire row
    of Portfolio value clickable, not just first column."* The anchor in the
    first cell stays exactly as it is — it is the keyboard route and the thing
    a screen reader announces — and the row simply forwards a press to it. */
.ivt-pvtable tbody tr.ivt-pvrow { cursor: pointer; }
.ivt-pvtable tbody tr.ivt-pvrow:hover { background: rgba(104,152,232,.07); }

/*  ⚠⚠ v31.543 (owner) — HIGH AND LOW ARE TOLD APART, THE SEPARATOR IS NOT.
    *"Colour code the entries so that the high are in a different colour from
    the low, both not white, except the '/' separator to be in white."*
    ⚠ The two house tokens, so the pair cannot drift from the palette — and
    deliberately NOT the `.pos`/`.neg` classes used for gains beside them: a
    52-week high is not a profit and a low is not a loss, and borrowing those
    classes would say it was. Same hues, different meaning, different name. */
.ivt-pvtable .pvv-hilo .hi { color: var(--nv-good); font-weight: inherit; }
.ivt-pvtable .pvv-hilo .lo { color: var(--nv-warn); font-weight: inherit; }
.ivt-pvtable .pvv-hilo .sep { color: var(--nv-ink); font-style: normal; padding: 0 3px; }

/*  ══ v31.476 — THE CASH TABLE SPILLED OUT OF THE CARD ═════════════════════
    Owner's phone again, red box round the cash block. The register above it
    fits now; this one did not — 352px against a card 238-305px wide, pushing
    past the card's right edge at every phone size.

    ⚠⚠⚠ THE CULPRIT IS `class="n"` ON A SENTENCE. `.ivt-table td.n` sets
    `white-space: nowrap`, which is right for a figure and wrong for
    "Portfolio value · positions and cash" — 230px of unbreakable label plus a
    90px figure cannot fit in 238px, so the table grew instead. The label may
    wrap; the FIGURES still may not, which is why this targets the label cell
    only and not the row.

    ⚠ The account name ellipsises against max-width like the instrument does —
    auto table layout needs a real px value, not 0, to cut against. */
.ivt-pvcash .ivt-pvgrand td.n:first-child { white-space: normal; }
.ivt-pvcash td.ivt-pvacct { max-width: 1px; }
@container pvcard (max-width: 339.98px) {
    .ivt-pvcash th, .ivt-pvcash td { padding-left: 5px; padding-right: 5px; }
}

/*  ══ v31.477 — A FUND NAME MUST NOT MAKE A ROW EIGHT LINES TALL ═══════════
    Owner's phone, Holdings: "iShares Core MSCI World UCITS ETF USD (Acc) EUR"
    in a 79px cell with `white-space: normal` wrapped one word per line and
    took the row to 98px. The same name does the same thing on the Watchlist
    (84px) and in the Settings securities grid (105px) — one fault, three
    places, so one class.

    ⚠ These tables all sit in `.ivt-hscroll` and are MEANT to be swiped, so
    the answer is not to shrink the type or drop the column: the name is
    simply cut with an ellipsis, with the whole of it on `title` for hover and
    long-press. Swiping reaches the rest of the row as it always did.

    ⚠⚠ NOT A BARE `.s` RULE. `.s` is used all over the app for small print;
    scoping to an explicit class keeps this to the three cells that needed it. */
.ivt-nm { display: block; max-width: 26ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/*  ⚠⚠⚠ v31.491 (owner) — THE SCROLL SHADOW IS GONE. *"Remove the following
    css only as it affects the Investment grid and shows those grids with a
    non-brand style."*

    It was added to give the horizontal swipe an affordance, and it did — but a
    painted gradient on the scroll container reads as a different surface from
    every other grid in the app, which is the louder fault. The rule is removed
    outright rather than commented out: a commented block is a rule waiting to
    come back. `.ivt-hscroll` keeps its scrolling (declared above with the rest
    of the Investments grid); only the paint is gone.                          */

/*  ⚠ v31.477 — the watchlist's Rationale is free text too, and at 03/04-Screen
    it was the row's driver once the NAME had been dealt with: 28 characters in
    an 88px cell wrapped to three lines. Wider cap than a fund name, because a
    rationale is a sentence and a few words of it are worth reading. */
.ivt-nm-wide { max-width: 34ch; }

/*  ══ v31.478 — 01-SCREEN: A ROW IS A CARD, AND NOTHING SCROLLS SIDEWAYS ═══
    Owner: *"horizontal scroll not good..."* and *"Perhaps stacking rows?"*.
    So below 768 the grids stop being grids. Each row becomes a small card of
    label/value lines, the header row is hidden because every line carries its
    own label, and the swipe — and the edge shadow that advertised it — are
    gone with it.

    ⚠⚠ `.ivt-hscroll` MUST STOP SCROLLING TOO, or the card would still sit in
    a scrollable box and the page would keep the sideways drag the owner has
    now rejected twice. */
@media (max-width: 767.98px) {
    .ivt-hscroll:has(> .nv-stack),
    .ivt-hscroll:has(> form > .nv-stack) { overflow-x: visible; background-image: none; margin: 0; padding: 0; }

    .nv-stack, .nv-stack tbody, .nv-stack tr, .nv-stack td { display: block; width: auto; }
    .nv-stack thead { display: none; }
    .nv-stack { min-width: 0; }

    .nv-stack tbody tr {
        border: 1px solid var(--nv-line, rgba(104, 152, 232, .25));
        border-radius: 10px; padding: 8px 10px; margin: 0 0 8px;
        background: rgba(18, 30, 56, .28);
    }
    .nv-stack tbody tr > td {
        display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
        border: 0; padding: 4px 0; text-align: right; min-height: 0;
    }
    /*  the label is the column's own heading, written in by nv-stack.js */
    .nv-stack tbody tr > td[data-lab]::before {
        content: attr(data-lab);
        flex: 0 0 auto; text-align: left; color: var(--nv-ink-2);
        font-size: 12px; letter-spacing: .04em; text-transform: uppercase;
    }
    /*  "headline plus tap for the rest": the columns the table did not keep */
    .nv-stack tbody tr > td.nv-stack-hide { display: none; }

    /*  ⚠ a cell that is only a control (chevron, actions) gets no label and sits
        on its own line, full width, so the buttons keep their normal hit area. */
    .nv-stack tbody tr > td:not([data-lab]) { justify-content: flex-end; }
    .nv-stack .ivt-nm { max-width: none; }
    .nv-stack .ivt-ell, .nv-stack .ivt-cbroker { max-width: none; }

    /*  ⚠ the chevron takes the card's top-right corner instead of a line of its
        own: it is an affordance, not a field, and a row of empty space with a
        single arrow in it reads as a missing value. */
    .nv-stack tbody tr { position: relative; padding-right: 44px; }   /* 34 left the first value touching a 30px chevron */
    .nv-stack tbody tr > td.ivt-chevcol,
    .nv-stack tbody tr > td.ivt-cchev { position: absolute; top: 6px; right: 6px; padding: 0; }

    /*  the expanded detail row is not a record and keeps its own layout —
        it just needs to stop being a table cell inside a blocked-out table. */
    .nv-stack tr.ivt-feed, .nv-stack tr.ivt-txdetail { display: block; border: 0; padding: 0; background: none; }
    .nv-stack tr.ivt-feed > td, .nv-stack tr.ivt-txdetail > td { display: block; text-align: left; padding: 0; }

    /*  ⚠ v31.479 (owner: "Alligng footer totals to the right for both screens")
        — `tfoot` was missing from the block list above, so footer rows kept
        table layout and sized to their own content: 253px against 305px body
        cards, which is the step the owner photographed. The footer now blocks
        out like the body and carries the same card edge, so the totals line up
        with the figures above them. */
    .nv-stack tfoot, .nv-stack tfoot tr, .nv-stack tfoot td { display: block; width: auto; }
    .nv-stack tfoot tr {
        border: 1px solid var(--nv-line, rgba(104, 152, 232, .25));
        border-radius: 10px; padding: 8px 10px; margin: 0 0 8px;
        background: rgba(18, 30, 56, .28);
    }
    .nv-stack tfoot tr > td {
        display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
        border: 0; padding: 4px 0; text-align: right; min-height: 0; white-space: normal;
    }
    .nv-stack tfoot tr > td[data-lab]::before {
        content: attr(data-lab);
        flex: 0 0 auto; text-align: left; color: var(--nv-ink-2);
        font-size: 12px; letter-spacing: .04em; text-transform: uppercase;
    }
    /*  the spanning cell is the row's own heading — full width, left, and the
        figures that follow it sit right, which is the alignment asked for. */
    .nv-stack tfoot td.nv-stack-head { display: block; text-align: left; }
    .nv-stack tfoot td.nv-stack-pad { display: none; }
    /*  ⚠ the footer rows must NOT inherit the body card's chevron gutter: there
        is no chevron on a total, and 44px of reserved space would push every
        figure 44px short of the edge the body figures line up on. */
    .nv-stack tfoot tr { position: static; padding-right: 10px; }

    /*  ⚠ and the detail card's own label/value grid goes to ONE column, which is
        the defect in the owner's screenshot: at two columns the right-hand pairs
        (Settlement date, Unit price, Brokerage) ran off the edge. */
    .ivt-xstats, .ivt-feedgrid .ivt-xstats, .ivt-txcard .ivt-txstats { grid-template-columns: 1fr; }
}

/*  ══ v31.490 — BOOKINGS TO INVOICE ════════════════════════════════════════
    The queue grid of the locked booking spec. ⚠ PREFIXED `bkq-` AND NOTHING
    ELSE IS TOUCHED: the Investments grid solved the same problem under `ivt-`
    and borrowing those classes would tie a Sales screen to a module it has
    nothing to do with — the day one is restyled the other changes with it.

    ⚠⚠⚠ v31.491 (owner) — NOTHING SCROLLS SIDEWAYS, AT ANY WIDTH. *"Remove the
    horizontal scrolling from both grids … increase the item rows as necessary
    for each screen so we have no horizontal scroll that is responsive to screen
    sizes."* So the min-width is gone and the container no longer scrolls: a
    narrow screen makes a row TALLER (cells wrap) instead of hiding half of it
    off to the right, and under 768 `nv-stack` turns each row into a card. With
    six rows a page the taller row costs nothing.                              */
/*  ══ v31.492 (owner) — THE TWO TOGGLE ROWS ══════════════════════════════
    *"Make the two toggle buttons fit in half their present size, equal
    length."* They were stretching to half the card each and read as a banner
    rather than a choice. Both rows now sit at a fixed equal width — the top
    one as the mailbox's two-row icon pill (`.ai-chips`), the mode pair as
    plain chips beneath it, so the two kinds of choice look different.
    ⚠ Under 768 they share the line evenly instead, because a fixed 220px pair
    leaves a ragged gap on a phone.                                          */
/*  ⚠ THE CHILD COMBINATOR IS NOT OPTIONAL HERE. `.chips > .chip { flex: 1 1 0 }`
    lives in tokens.css, which loads after this sheet, so a plain
    `.bkq-doors .chip` ties on specificity and loses on order — the pair stayed
    541px wide and the first attempt at this looked like it had done nothing. */
.chips.bkq-doors > .chip, .chips.fd-doors > .chip { flex: 0 0 220px; min-width: 220px; }
@media (max-width: 767.98px) {
    .chips.bkq-doors > .chip, .chips.fd-doors > .chip { flex: 1 1 0; min-width: 0; }
}

/*  the row's own tick: match THIS booking and watch it go, rather than staking
    sixty-one on one press (owner, v31.492) */
.bkq-tick {
    flex: 0 0 auto; width: 34px; height: 34px; margin-left: 8px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 999px; cursor: pointer;
    border: 1px solid rgba(104, 152, 232, .45);
    background: rgba(120, 165, 240, .10);
    color: var(--nv-ink);
}
.bkq-tick:hover { border-color: var(--nv-gold-bright); color: var(--nv-gold-bright); }
/*  ⚠⚠ v31.493 (owner) — THE NOTE BELOW THE BOX, NOT BESIDE IT. `suggested` sat
    in the same flex line as the input and the tick, so it pushed the tick out
    of column on the rows that had one and overlapped the cell's bottom border.
    The pair now has its own row and the note is a block under it: the tick
    lands in the same place on every row, the box keeps its width, and the
    row's bottom line runs unbroken. */
.bkq-act { display: block; }
.bkq-inprow { display: flex; align-items: center; gap: 8px; }
.bkq-inprow .bkq-inv { flex: 1 1 auto; min-width: 0; }
.bkq-tick { margin-left: 0; }

.bkq-hscroll { overflow-x: visible; }
.nv-stack.bkq-table { min-width: 0; table-layout: auto; }
/*  ⚠⚠⚠ `table-layout: fixed`, AND THE COLUMNS ARE DECLARED. Letting the
    browser size eight columns from their content gave the first one 793px of
    1060 — a Booking.com reference is a 40-character token and auto layout
    hands it whatever it asks for, squeezing City tax to 42px. Fixed layout
    plus `overflow-wrap: anywhere` means the long token wraps inside its own
    column and every other column keeps the width it deserves. The percentages
    carry down to the 02-Screen unchanged; under 768 `nv-stack` takes over and
    none of this applies.                                                     */
.bkq-table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 14px; }
.bkq-table th:nth-child(1) { width: 22%; }
.bkq-table th:nth-child(2) { width: 11%; }
.bkq-table th:nth-child(3) { width: 14%; }
.bkq-table th:nth-child(4) { width: 6%; }
.bkq-table th:nth-child(5) { width: 11%; }
.bkq-table th:nth-child(6) { width: 15%; }
.bkq-table th:nth-child(7) { width: 11%; }
.bkq-table th:nth-child(8) { width: 10%; }
/*  ⚠⚠ MATCH MODE IS A DIFFERENT TABLE AND GETS DIFFERENT COLUMNS. The last
    column stops being a button and becomes the box the owner asked to be able
    to read a whole invoice number in, so it takes a fifth of the grid and the
    rest give it up proportionally. A single set of widths served one mode and
    overflowed the other. */
/*  ⚠ v31.495 (owner, his picture): *"Increase field size as number view is
    truncated."* 19% of the grid left about 140px for the box once the tick,
    the gap and the cell's padding were taken out, and `I-250293` at 15px needs
    more than that to sit inside its own border rather than against it. The
    column takes a quarter and the six text columns give up two points each —
    they wrap, and the box cannot. */
.bkq-table.bkq-matchmode th:nth-child(1) { width: 18%; }
.bkq-table.bkq-matchmode th:nth-child(2) { width: 9%; }
.bkq-table.bkq-matchmode th:nth-child(3) { width: 12%; }
.bkq-table.bkq-matchmode th:nth-child(4) { width: 6%; }
.bkq-table.bkq-matchmode th:nth-child(5) { width: 9%; }
.bkq-table.bkq-matchmode th:nth-child(6) { width: 12%; }
.bkq-table.bkq-matchmode th:nth-child(7) { width: 9%; }
.bkq-table.bkq-matchmode th:nth-child(8) { width: 25%; }
.bkq-table th {
    text-align: left; font-weight: 400; font-size: 11.5px; letter-spacing: .06em;
    text-transform: uppercase; color: var(--nv-ink-2, #9fb0cd);
    padding: 0 8px 8px; border-bottom: 1px solid rgba(104, 152, 232, .25);
}
.bkq-table td { padding: 9px 8px; border-bottom: 1px solid rgba(104, 152, 232, .12); vertical-align: top; }
/*  ⚠⚠ NO `white-space: nowrap` ON A NUMBER CELL. A figure has no spaces in it
    and never breaks anyway; what the nowrap actually held together was the
    SUB-LINE beside it (`+ 11.59 VAT @ 5 %`, `6 × 1.50`), and that one phrase
    set a floor under the whole table's width — the grid still overflowed its
    card at 820–1280px with the scroller taken away. The sub-lines wrap; the
    figures do not need to.                                                   */
.bkq-table td.n, .bkq-table th.n { text-align: right; font-variant-numeric: tabular-nums; }
/*  ⚠ ONLY THE FIRST COLUMN BREAKS MID-WORD. A Booking.com reference is a
    40-character token with nowhere to break, and it is the only thing on this
    grid that needs `anywhere`; applying it to every cell shreds ordinary words
    one letter to a line the moment a column is tight. */
.bkq-table td:nth-child(1) { overflow-wrap: anywhere; }
.bkq-table .mut { color: var(--nv-ink-2, #9fb0cd); font-size: 12.5px; }
.bkq-table tr.bkq-ref td { opacity: .78; }
.bkq-why { color: var(--nv-warn, #e8c45c); font-size: 13px; }
.bkq-act { white-space: nowrap; }
/*  ⚠ v31.491 (owner): *"Make the input text sizes large so we can view full
    invoice number."* I-250289 in a 170px box at 13px showed `I-25028` and the
    placeholder `no candidate` showed `no canc`. */
.bkq-inv { width: 100%; font-size: 15px; letter-spacing: .01em; }
/*  ⚠ v31.495 (owner, his picture): *"Center the Suggested label below the
    field."* Centred under THE BOX, not under the cell — the tick is 34px with
    an 8px gap beside it, so the note is made exactly that much narrower and
    its centre line then falls on the box's. Centring it on the cell would put
    it 21px right of where his picture has it.
    ⚠ `width: calc(100% - 42px)`, NOT `padding-right: 42px`. Both centre the
    text identically, and only one of them can be measured: with padding the
    element still spans the whole cell, so every rectangle a probe can read
    says the note is cell-centred. The arm that caught this was reading the
    truth about the wrong box. */
.bkq-sug {
    display: block; margin-top: 4px; font-size: 11px;
    color: var(--nv-gold-bright); letter-spacing: .04em;
    text-align: center; width: calc(100% - 42px);
}
.bkq-total td { border-bottom: 0; padding-top: 12px; }
.bkq-totlead { text-align: right; }
.bkq-totlab { color: var(--nv-ink-2, #9fb0cd); font-size: 12.5px; }
.bkq-foot { display: flex; justify-content: flex-end; gap: 10px; margin-top: 12px; }
.bkq-foot .armed { filter: saturate(1.4); }
.bkq-win { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bkq-win input[type="date"] { flex: 0 0 auto; width: 160px; }
.bkq-win .btn, .bkq-foot .btn { flex: 0 0 auto; width: auto; padding: 0 18px; }
/*  ⚠ THE ROW'S BUTTON FILLS ITS COLUMN. With `table-layout: fixed` a button
    wider than its column spills out of the card — 24px at 1500 and 50px at
    1024, which is the grid overflow the no-scroll arm was catching. */
.bkq-table .bkq-post { width: 100%; padding: 0 8px; }
.bkq-win label { font-size: 12.5px; }
.bkq-hint, .bkq-note { font-size: 13px; max-width: 80ch; }
/*  the 02/03-Screens: the grid stays a grid and the cells wrap rather than
    pushing the row off the right-hand edge */
/*  the stay is one line while there is room for it, and wraps below 1366
    rather than pushing the row off the edge */
@media (min-width: 1366px) { .bkq-stay { white-space: nowrap; } }
@media (max-width: 1365.98px) {
    .bkq-table { font-size: 13.5px; }
    .bkq-table th, .bkq-table td { padding-left: 6px; padding-right: 6px; }
}
@media (max-width: 767.98px) {
    /*  the window form takes its own line and each date box its own row — at
        390px two side by side cut the year off, and a date box that cannot
        show its year is not a date box */
    .bkq-win { width: 100%; gap: 6px; }
    .bkq-win label { flex: 1 1 100%; }
    .bkq-win input[type="date"] { flex: 1 1 100%; min-width: 0; }
    .bkq-win .btn { flex: 1 1 100%; }
    .bkq-inv { max-width: 100%; width: 100%; }
    .bkq-foot .btn { flex: 1 1 auto; }
    /*  a stacked row's Post button is the row's own action, full width under
        its figures rather than a lone control floating right */
    .nv-stack tbody tr > td.bkq-act { justify-content: stretch; }
    .nv-stack .bkq-post { width: 100%; }
}
}

/*  ══ v31.494 — THE RICH COMPOSER, THE EDIT BAR AND THE BUBBLE MARKS ══════
    The composer is `_RichEd` now, so the surface inside `.msg-cbox` is the
    editor's `.rt-ed` rather than a textarea. ⚠ The toolbar is part of the
    editor, so the box grows by its height; the controls column beside it stays
    bottom-aligned so ➤ still sits level with the foot of the field.           */
.msg-cbox .rt-toolbar { flex-wrap: wrap; }
.msg-cbox .rt-ed { max-height: 30vh; overflow-y: auto; }
.msg-composer { align-items: flex-end; }

/*  the bar that says a message is being changed rather than a new one typed */
.msg-editbar {
    display: flex; align-items: center; gap: 10px;
    margin: 0 0 6px; padding: 6px 10px;
    border-radius: 10px;
    border: 1px solid rgba(232, 167, 92, .35);
    background: rgba(232, 167, 92, .08);
}
.msg-editlab { font-size: 13px; color: var(--nv-gold-bright); }
.msg-editbar .chip { margin-left: auto; }

/*  ══ v31.528 (owner, quick) — THE CONTROLS ARE VISIBLE EVERYWHERE ═════════
    [stated], against a screenshot of the desktop thread with the pencil ringed:
    *"Icon should be displayed for all screen sizes. Currently showing for only
    01-Screen and 02-Screen."*

    ⚠⚠⚠ AND IT WAS NEVER ABOUT SCREEN SIZE. These controls were `opacity: 0`
    until the bubble was hovered, with `@media (hover: none)` revealing them on
    a touch screen — so a phone and a tablet, which have no hover, showed them
    always, and a desktop showed nothing until the pointer was on the bubble.
    The two bands he named are exactly the devices with no hover. A reader on a
    PC had no way to know the control existed, and a screenshot could not
    capture it.

    ⚠⚠ THEY ARE VISIBLE AT REST AND STILL QUIET. The original note — *"a pencil
    and a 文A shouting on every line would make a thread harder to read"* — was
    right about the problem and wrong about the remedy: invisible is not quiet,
    it is absent. They now sit at 0.6 and come to full strength on hover or
    focus, so they can be seen from across the thread and still do not compete
    with the words. Say the word if you want them at full strength.

    ⚠ The pencil is drawn on ONE bubble per thread anyway (v31.495: *"Include
    edit for ONLY last text SENT"*), so it was never the one shouting. */
.msg-b { position: relative; }
.msg-acts {
    position: absolute; top: 4px; right: 6px;
    display: flex; gap: 4px; opacity: .6; transition: opacity .12s ease;
}
.msg-b:hover .msg-acts, .msg-b:focus-within .msg-acts { opacity: 1; }
.msg-act {
    width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;
    border-radius: 6px; cursor: pointer; padding: 0;
    border: 1px solid rgba(104, 152, 232, .35);
    background: rgba(8, 12, 24, .55);
    color: var(--nv-ink-2);
}
.msg-act:hover, .msg-act.on { color: var(--nv-gold-bright); border-color: var(--nv-gold-bright); }
/*  ⚠ FULL STRENGTH ON A TOUCH SCREEN — there is no hover to bring them up with,
    so the resting 0.6 above would be the only state a phone ever showed. */
@media (hover: none) { .msg-acts { opacity: 1; } }

/*  `edited`, beside the time, in the meta line's own quiet weight */
.msg-edm { font-style: normal; opacity: .7; margin-right: 4px; }

/*  a message body is markup now: keep its lists off the bubble's edge, exactly
    as `.rt-ed` does for the editor (v31.429), and never let a pasted width
    push the bubble sideways */
.msg-txt ul, .msg-txt ol { padding-left: 24px; margin: 4px 0; }
.msg-txt blockquote { margin: 0 0 0 20px; }
.msg-txt p { margin: 0 0 4px; }
.msg-txt img, .msg-txt table { max-width: 100%; }

/*  ══════════════════════════════════════════════════════════════════════════
    USER SETTINGS (v31.504) — and the two TOOLKIT classes the house rules name.

    `docs/toolkit/HOUSE-RULES.md` holds the rules; this is their one
    implementation. HR-2 (`.nv-actcol`) and HR-2b (`.nv-cellcol`) are APP-WIDE
    and deliberately not prefixed `set-` — every module that lists rows with
    actions should reach for them rather than writing its own.
    ══════════════════════════════════════════════════════════════════════════ */

/*  ⚠⚠ HR-2 — ACTIONS LINE UP IN COLUMNS AND SHARE A WIDTH.
    [stated] owner, 7 Nov 2026: *"Keep buttons same size. I would like to see some
    columnar symmetry where buttons are arranged in columns (add this to the toolkit as I
    have had to request this over time)."* Actions sized by their own words put every
    button at a different x on every row, so a list of three reads as three unrelated
    things. ⚠ `.ac-hold` is the cell with no action, KEEPING ITS PLACE. */
.nv-actcol { display: grid; grid-auto-flow: column; align-items: center; gap: 8px; margin-left: auto; }
.nv-actcol > * { min-width: 0; }
.nv-actcol .btn, .nv-actcol .pill { width: 100%; justify-content: center; white-space: nowrap; }
.ac-2 { grid-template-columns: repeat(2, 96px); }
.ac-3 { grid-template-columns: repeat(3, 96px); }
.ac-wide { grid-template-columns: repeat(2, 128px); }
.ac-hold { visibility: hidden; }

/*  ⚠⚠⚠ HR-2b — AND THE SAME INSIDE A GRID CELL.
    [stated] owner, over a photograph of seven company codes at seven different x
    positions: *"my request for symmetry when display chips, buttons, tabs, apply to when
    they are inside grids."* The cell is a grid of its own: the label takes the room that
    is left, the chip gets a track.
    ⚠ THE CHIP TRACK IS FIXED, NOT `auto` — `auto` sizes to the widest content, which
    aligns the column by accident today and un-aligns it the first time a longer value
    arrives. ⚠⚠ And it OUTRANKS `white-space: nowrap` on the cell: the old trick of
    stopping the cell wrapping makes the column as wide as its longest row and pushes the
    table into a horizontal scroll — breaking HR-1 to achieve what this does properly. */
.nv-cellcol { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 10px; }
.nv-cellcol > :first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.nv-cellcol > .pill { justify-self: start; width: var(--chipw, 72px); justify-content: center; }

/* ── the settings panel: the house `.mail-folders` column, its own rows ──── */
.set-panel .set-ph { font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
                     color: var(--nv-ink-3); padding: 14px 10px 7px; }
.set-panel .fld { display: flex; align-items: center; gap: 10px; }
.set-panel .set-ic { width: 18px; flex: 0 0 18px; display: inline-flex; opacity: .9; }
.set-panel .set-lb { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.set-panel .set-n { margin-left: auto; font-size: 11px; color: var(--nv-ink-3); }
/*  ⚠ a section whose round has not shipped is LISTED, disabled and marked — a list that
    silently omitted them would make a half-built module look finished. */
.set-panel .set-soon { opacity: .5; cursor: default; }
.set-panel .set-soon:hover { background: none; }

/* ── the cards ──────────────────────────────────────────────────────────── */
.set-scroll { gap: 14px; }
.set-cardhead { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 14px; }
.set-cardhead h2 { font-size: 16px; font-weight: 400; margin: 0; }
.set-cardhead .muted { margin-top: 2px; max-width: 64ch; }
.set-acts { margin-left: auto; flex-shrink: 0; }
.set-grid2 { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px 16px; }
.set-grid3 { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px 16px; }
.set-narrow { max-width: 620px; }
.set-idrow { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }
.set-idrow > .set-grid2 { flex: 1 1 260px; min-width: 0; }
.set-hint { color: var(--nv-ink-3); margin-top: 6px; }
/*  ⚠ a sentence inside one 190px track of `.set-grid3` becomes a column of single words.
    It is prose about the field above it, so it spans the row it belongs to. */
.set-grid3 > .fld-wrap > .set-hint { grid-column: 1 / -1; }
.set-grid3 { align-items: start; }
.set-grid3 > .fld-wrap:has(> .set-hint) { grid-column: 1 / -1; }
.set-hint.warn { color: var(--nv-warn); }
.set-fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.set-photorow { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.set-photorow .input { max-width: 320px; }

/* the avatar — initials until there is a picture, as messaging already does */
.set-av { width: 42px; height: 42px; flex: 0 0 42px; border-radius: 50%; overflow: hidden;
          display: grid; place-items: center; border: 1px solid rgba(104,152,232,.22);
          background: linear-gradient(145deg, #24406d, #16213c); color: var(--nv-ink); font-size: 15px; }
.set-av img { width: 100%; height: 100%; object-fit: cover; }
.set-av-lg { width: 84px; height: 84px; flex: 0 0 84px; font-size: 26px; }

/*  ⚠ v31.507 — the avatar IS the upload control (owner: "Click on image to upload/update
    new image"). It is a <label> for the file input, so the click, the keyboard and the
    screen reader all get the same behaviour with no script. `overflow: hidden` on `.set-av`
    would clip the badge, so the badge is positioned inside the circle rather than over its
    edge. */
.set-avbtn { position: relative; cursor: pointer; display: inline-flex; }
.set-avbtn:hover { box-shadow: 0 0 0 2px rgba(232,167,92,.45); }
.set-avbtn:focus-within { outline: 2px solid var(--nv-blue); outline-offset: 2px; }
.set-avcam { position: absolute; left: 0; right: 0; bottom: 0; height: 24px;
             display: flex; align-items: center; justify-content: center;
             background: rgba(10,16,30,.72); color: var(--nv-gold); opacity: 0; transition: .15s; }
.set-avbtn:hover .set-avcam, .set-avbtn:focus-within .set-avcam { opacity: 1; }
/*  the badge is the only affordance on a touch screen, where there is no hover */
@media (hover: none) { .set-avcam { opacity: 1; } }

/*  the photograph row now sits INSIDE the Identity card, above the avatar row.
    ⚠ `.nv-actcol` carries `margin-left: auto` app-wide — right for a card's action bar,
    wrong here, where the button belongs beside the field it acts on rather than adrift at
    the far edge of a 1134px card. */
.set-photorow { margin-bottom: 14px; }
.set-photorow .nv-actcol { margin-left: 0; }
/*  ⚠ it WRAPS, or the gold sentence round 3 adds to this band steals the room from the
    name it is talking about — "Martina K…" beside a full-width sentence about her. */
.set-who { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.set-whotext { min-width: 0; flex: 1 1 200px; }
.set-whotext b { display: block; }

/*  ⚠⚠⚠ R2 ON THE SCREEN. The owner chose one-way hashing, which ends that account's v1
    sign-in the moment a password is saved here. It is a consequence of pressing the
    button, so it is said beside the button. */
.set-warn { display: flex; gap: 11px; padding: 12px 14px; margin-top: 14px; border-radius: var(--r-card);
            border: 1px solid rgba(217,161,59,.45); background: rgba(217,161,59,.08);
            color: var(--nv-ink-2); font-size: 13px; }
.set-warn b { display: block; color: var(--nv-warn); font-weight: 400; margin-bottom: 2px; }
.set-warnx { flex: 0 0 16px; color: var(--nv-warn); }

/*  each section saves ITSELF (R9) — v1's single Save for seven tabs either lost work or
    saved what you had not looked at. ⚠ SOLID, not translucent: a sticky bar that lets the
    card show through reads as two things overlapping (seen on the owner's phone). */
/*  ⚠⚠ v31.518 (owner) — THE CATALOG PILL. *"We use the pills with the last two digit of the
    catalog."* A capsule with a hairline and letter-spaced caps, the shape his own entity
    picker draws beside each company — deliberately NOT the house `.pill`, whose 3px left bar
    marks a STATE (done, mail) rather than a place. ⚠ It is a label: `title` on the heading
    still carries the catalog's real name for anyone who needs it. */
.pill.cat { display: inline-flex; border-left: 0; border: 1px solid rgba(104,152,232,.45);
            border-radius: 999px; padding: 1px 10px; font-size: 11px; letter-spacing: .08em;
            font-weight: 500; color: var(--nv-ink-2); background: rgba(22,33,60,.5); }

.set-savebar { position: sticky; bottom: 0; display: flex; align-items: center; gap: 12px;
               flex-wrap: wrap; margin-top: 14px; padding: 11px 14px; border-radius: var(--r-card);
               border: 1px solid rgba(104,152,232,.22); background: var(--nv-surface);
               box-shadow: 0 -10px 18px -12px rgba(5,9,18,.9); }
.set-flash.good { border-color: rgba(88,179,104,.45); color: var(--nv-good); }
.set-flash.bad { border-color: rgba(217,161,59,.45); color: var(--nv-warn); }

@media (max-width: 1023.98px) {
    /*  ⚠⚠ HR-1 — 01/02-Screen NEVER scrolls the document sideways. The action columns
        become full-width rows here, EXCEPT a Discard/Save pair, which stays side by side:
        stacking it makes the sticky bar tall enough to cover its own card. */
    .nv-actcol { grid-auto-flow: row; grid-template-columns: 1fr; width: 100%; gap: 6px; }
    .set-savebar .nv-actcol { grid-auto-flow: column; grid-template-columns: repeat(2, 1fr); }
    .set-savebar .muted { width: 100%; }
    .set-grid2, .set-grid3 { grid-template-columns: 1fr; }
    .hide-sm { display: none; }
}

/* ── USER SETTINGS round 2 — company access (v31.505) ───────────────────── */
/*  the signed-in user's photograph at the foot of the rail (v31.509, owner).
    ⚠ 03/04/05-Screen only — the rail is hidden below 1024 and the ☰ tray serves those
    widths, but the rule is written rather than inherited. */
.rl-me { display: none; }
@media (min-width: 1024px) {
    .rl-me { display: flex; justify-content: center; margin: 14px 0 18px; }
    .rl-me img { width: 96px; height: 96px; border-radius: 50%; object-fit: cover;
                 border: 2px solid rgba(232,167,92,.45); background: var(--nv-surface-2); }

    /*  ⚠ v31.512 (owner): *"When the menu panel is minimized, please make the user image
        icon smaller with padding left and right."* The minimized rail is 84px wide, so a
        96px photograph was wider than the rail that holds it — it filled the column edge
        to edge and read as a band rather than an avatar. `.mail-app` is what the house
        sets on every page that collapses the rail to icons, so this follows that class
        rather than inventing a second signal for the same state. */
    .mail-app .rl-me { margin: 10px 0 14px; padding: 0 14px; }
    .mail-app .rl-me img { width: 52px; height: 52px; border-width: 1px; }
    .rl-me:hover img { border-color: var(--nv-gold); }
    .rl-me:focus-visible img { outline: 2px solid var(--nv-blue); outline-offset: 3px; }
}

/* ── v31.513 — round 6, My applications ────────────────────────────────── */
.set-apps { display: grid; grid-template-columns: 1fr; gap: 8px; margin: 10px 0 4px; }
@media (min-width: 768px) { .set-apps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1366px) { .set-apps { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.set-apptile { display: flex; align-items: center; gap: 10px; min-width: 0;
               padding: 8px 10px; border: 1px solid rgba(104,152,232,.18); border-radius: 6px;
               background: var(--nv-surface-2); }
.set-apptile .set-coname { flex: 1 1 auto; --chipw: 92px; }
.set-apptile.off { opacity: .5; }
.set-apptile.drag { opacity: .4; border-color: var(--nv-gold); }
/*  ⚠ a tile whose change has not been saved yet says so rather than looking settled */
.set-apptile.pending { border-color: rgba(232,167,92,.6); }
.set-appgrip { cursor: grab; color: var(--nv-ink-3); flex: 0 0 auto; font-size: 15px; line-height: 1; }
.set-apptray { margin-top: 16px; padding-top: 12px; border-top: 1px solid rgba(104,152,232,.14); }

/* ── v31.512 — the accordions and the signature tabs ───────────────────── */
/*  ⚠ the marker is the house chevron, drawn by us: the UA triangle differs by platform and
    would be the only control on these screens that does. */
.set-acct > details > summary, .set-catdet > summary { list-style: none; cursor: pointer; }
.set-acct > details > summary::-webkit-details-marker,
.set-catdet > summary::-webkit-details-marker { display: none; }
.set-acctsum { display: flex; align-items: center; gap: 12px; padding: 2px 0 10px; }
.set-acctsum::after { content: ""; flex: 0 0 9px; width: 9px; height: 9px; margin-left: auto;
                      border-right: 1.6px solid var(--nv-ink-3); border-bottom: 1.6px solid var(--nv-ink-3);
                      transform: rotate(45deg) translate(-3px, -3px); transition: transform .15s; }
details[open] > .set-acctsum::after { transform: rotate(225deg) translate(-1px, -1px); }
.set-acctsum:hover::after { border-color: var(--nv-gold); }
.set-acctname { display: flex; align-items: center; gap: 12px; min-width: 0; flex-wrap: wrap; }
.set-acctname b, .set-acctname h2 { min-width: 0; margin: 0; }
/*  ⚠⚠ v31.514 — HR-2b ON THE MAIL CARDS. [stated] owner: *"The blue chips should be
    aligned in one column as shown in the attached image."* They were flowing immediately
    after the address, so the column moved with every address length and six cards drew six
    different positions. The address now owns a FIXED track and ellipsises inside it; the
    chips start where that track ends, which is the same x on every card whatever the
    address is. Scoped to `.set-acct` so the Company-access catalog headers, which share
    `.set-acctname`, keep their own single-line layout. */
.set-acct .set-acctname { display: grid; align-items: center; column-gap: 16px;
                          grid-template-columns: minmax(0, var(--acctnamew, 300px)) auto; }
.set-acct .set-acctname > .s { display: grid; justify-items: start; row-gap: 3px; }
/*  ⚠ and below 02-Screen the fixed track would push the card past the viewport (HR-1), so
    the chips move under the address — still one column, just a narrower one. */
@media (max-width: 1023px) {
    .set-acct .set-acctname { grid-template-columns: minmax(0, 1fr); row-gap: 6px; }
}
.set-catdet .set-acctname h2 { font-size: 14px; letter-spacing: .06em; color: var(--nv-ink-2); }
/*  three controls in the signature bar now, so the row widens to hold them */
.set-sigform .nv-actcol.ac-wide { grid-template-columns: 34px 128px 128px; }
.set-sigstar { align-self: center; }
.set-sigform { margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(104,152,232,.14); }

/*  ⚠⚠ TABS WITHOUT SCRIPT — two radios and `:checked ~`. They work before any JavaScript
    runs and cannot leave the card in a state no stylesheet describes. The radios carry a
    name the handler never reads. */
.nv-tabs2 { display: grid; grid-template-columns: auto auto 1fr; align-items: end; gap: 0 4px; }
.nv-tabr { position: absolute; opacity: 0; width: 0; height: 0; }
.nv-tabl { grid-row: 1; padding: 7px 14px; font-size: 13px; cursor: pointer; white-space: nowrap;
           color: var(--nv-ink-3); border-bottom: 2px solid transparent; }
.nv-tabl:hover { color: var(--nv-ink-2); }
.nv-tabp { grid-row: 2; grid-column: 1 / -1; display: none; padding-top: 12px; }
/*  ⚠ `+`, not `~` with `nth-of-type`: each radio is immediately followed by its own label,
    so the adjacent combinator says exactly that and cannot be thrown off by a third child
    arriving later. The panes still need `~` because they sit after both radios. */
.nv-tabs2 > .nv-tabr:checked + .nv-tabl { color: var(--nv-gold); border-bottom-color: var(--nv-gold); }
.nv-tabs2 > .nv-tabr:nth-of-type(1):checked ~ .nv-tabp1,
.nv-tabs2 > .nv-tabr:nth-of-type(2):checked ~ .nv-tabp2 { display: block; }
.nv-tabr:focus-visible ~ .nv-tabl { outline: 2px solid var(--nv-blue); outline-offset: 2px; }

/*  ══════════════════════════════════════════════════════════════════════════════════
    MY APPLICATIONS — v31.516. Built from the locked mock-up r4.
    ⚠⚠ EVERYTHING THE SCRIPT ADDS HANGS OFF `.js` ON THE CONTAINER, which the script
    itself puts there. Without it every module is open with all its tiles, which is the
    round-6 behaviour and a working screen.
    ══════════════════════════════════════════════════════════════════════════════════ */
.set-scope { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin: 0 0 16px; }
.set-seg { display: inline-flex; border: 1px solid rgba(104,152,232,.22); border-radius: var(--r-card);
           overflow: hidden; flex-wrap: wrap; }
.set-seg > a { padding: 7px 12px; font-size: 13px; color: var(--nv-ink-3); text-decoration: none; white-space: nowrap; }
.set-seg > a + a { border-left: 1px solid rgba(104,152,232,.22); }
.set-seg > a:hover { color: var(--nv-ink-2); }
.set-seg > a.on { background: rgba(232,167,92,.14); color: var(--nv-gold); }

/*  ⚠⚠⚠ THE HOME TILES ARE SQUARE AND GOLD, like `.app-tile` on the dashboard — owner's
    own comparison. A FIXED 124px track and not `1fr`: stretched to fill the row they come
    out as 180px slabs, and a tile should stop growing before the row runs out. */
.set-hometiles { display: grid; grid-template-columns: repeat(auto-fill, 124px); gap: 12px;
                 justify-content: start; padding: 14px; margin: 0 0 4px;
                 border: 1px dashed rgba(232,167,92,.20); border-radius: var(--r-card);
                 background: rgba(232,167,92,.045); }
.set-hometiles.over { border-color: var(--nv-gold); background: rgba(232,167,92,.09); }
.set-ht { position: relative; display: flex; flex-direction: column; align-items: center;
          justify-content: center; gap: 8px; aspect-ratio: 1; padding: 10px 8px; cursor: grab;
          border: 1px solid rgba(232,167,92,.22); border-radius: var(--r-card);
          background: rgba(232,167,92,.05); text-align: center; }
.set-ht:hover { border-color: var(--nv-gold); }
.set-ht.drag { opacity: .4; }
.set-ht > .ic { width: 52px; height: 52px; border-radius: 8px; display: flex; align-items: center;
                justify-content: center; border: 1px solid rgba(232,167,92,.45); color: var(--nv-gold); }
.set-ht > .ic svg { width: 26px; height: 26px; }
.set-ht > b { font-weight: 500; font-size: 12px; line-height: 1.25; max-width: 100%;
              display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.set-ht > .pos { position: absolute; top: 6px; left: 7px; font-size: 10.5px; color: var(--nv-gold);
                 border: 1px solid var(--nv-gold-dim); border-radius: 4px; padding: 0 5px;
                 background: var(--nv-bg-deep); font-variant-numeric: tabular-nums; }
.set-ht > .xb { position: absolute; top: 4px; right: 4px; width: 22px; height: 22px; padding: 0;
                line-height: 1; border: 1px solid transparent; background: none; border-radius: 4px;
                color: var(--nv-ink-3); cursor: pointer; }
.set-ht:hover > .xb { border-color: rgba(104,152,232,.22); }
.set-ht > .xb:hover { color: var(--nv-gold); border-color: var(--nv-gold-dim); }
.set-ht-empty { display: flex; align-items: center; justify-content: center; aspect-ratio: 1;
                border: 1px dashed rgba(104,152,232,.22); border-radius: var(--r-card);
                color: var(--nv-ink-3); font-size: 11.5px; text-align: center; padding: 8px; }

/*  ⚠ EQUAL WIDTH, his word — `1fr` tracks, so a module's name length stops deciding its
    chip's width and the row stops being ragged for no reason. */
.set-appchips { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
                gap: 8px; margin: 16px 0 14px; }
.set-chip { display: flex; align-items: center; gap: 8px; min-width: 0; text-align: left;
            border: 1px solid rgba(104,152,232,.22); border-radius: var(--r-card);
            background: none; padding: 8px 10px; font-size: 12.5px; color: var(--nv-ink-3); cursor: pointer; }
.set-chip:hover { color: var(--nv-ink-2); }
.set-chip.on { border-color: var(--nv-gold); color: var(--nv-gold); background: rgba(232,167,92,.08); }
.set-chip .n { margin-left: auto; flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.set-appq { width: 100%; margin: 0 0 16px; }

/*  ⚠⚠⚠ TWO COLUMNS FROM 1024. The owner works at ~1254 and the first cut put the
    breakpoint at 1280, so the arrangement he had asked for never engaged on his own
    screen. Below 1024 one column IS the layout (HR-1). */
.set-appcols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-items: start; }
.set-appcol { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
@media (min-width: 1024px) { .set-appcols.js { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/*  collapsed is GOLD and open is BLUE — the owner's inversion, same as the mail cards */
.set-mod { border: 1px solid rgba(232,167,92,.38); border-radius: var(--r-card);
           background: var(--nv-surface); min-width: 0; }
.set-mod.open { border-color: rgba(104,152,232,.22); }
.set-modh { display: flex; align-items: center; gap: 12px; width: 100%; min-width: 0;
            background: none; border: 0; padding: 13px 14px; text-align: left; cursor: pointer; }
/*  ⚠⚠ v31.517 (owner, ringed every collapsed card's name in red): THE NAME IS WHITE AGAIN
    WHEN THE CARD IS SHUT. `.set-modh` is a <button>, and a button does not inherit the page's
    text colour — it takes the control colour — so every module name came out dim while the
    count beside it, an ordinary <span>, stayed legible. The open card keeps its gold name. */
.set-modh h3 { font-size: 13.5px; min-width: 0; margin: 0; color: var(--nv-ink); }
.set-mod.open .set-modh h3 { color: var(--nv-gold); }
.set-modh > .s { margin-right: auto; }
.set-modh .tog { flex: 0 0 auto; font-size: 11.5px; letter-spacing: .04em; text-transform: uppercase;
                 color: var(--nv-ink-3); border: 1px solid rgba(104,152,232,.22);
                 border-radius: var(--r-card); padding: 3px 9px; }
.set-modh:hover { background: rgba(104,152,232,.05); }
.set-modh:hover .tog, .set-mod.open .set-modh .tog { color: var(--nv-gold); border-color: var(--nv-gold-dim); }
.set-modh::after { content: ""; width: 8px; height: 8px; margin-left: 6px; flex: 0 0 8px;
                   border-right: 1.5px solid var(--nv-ink-3); border-bottom: 1.5px solid var(--nv-ink-3);
                   transform: rotate(45deg) translate(-2px, -2px); transition: transform .12s; }
.set-mod.open .set-modh::after { transform: rotate(225deg) translate(-1px, -1px); }
/*  ⚠ the collapse is the SCRIPT's: without it every body is open, which is a working screen */
.set-appcols.js .set-mod:not(.open) .set-modbody { display: none; }
.set-appcols.js .set-mod:not(.open) .set-modh { cursor: pointer; }

.set-modf { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 14px 12px; }
.set-modf label { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--nv-ink-3);
                  border: 1px solid rgba(104,152,232,.22); border-radius: var(--r-card);
                  padding: 5px 10px; cursor: pointer; }
.set-modf label:hover { color: var(--nv-ink-2); }
.set-modf label.on { color: var(--nv-gold); border-color: var(--nv-gold-dim); background: rgba(232,167,92,.07); }
.set-modf input { accent-color: var(--nv-gold); margin: 0; }

.set-modtiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(236px, 1fr));
                gap: 10px; padding: 0 14px 14px; }
/*  ⚠ the APP tiles stay rectangular — owner's word. A name, a module code and where it
    opens is a line of text, not an icon. Only the HOME tiles went square. */
.set-apptile { display: grid; grid-template-columns: 34px minmax(0, 1fr) auto; align-items: center;
               gap: 10px; min-height: 60px; padding: 9px 10px; cursor: grab;
               border: 1px solid rgba(104,152,232,.22); border-radius: var(--r-card);
               background: var(--nv-surface-2); }
.set-apptile:hover { border-color: var(--nv-gold-dim); }
.set-apptile.drag { opacity: .4; }
.set-apptile.off { opacity: .55; cursor: not-allowed; border-style: dashed; }
.set-apptile.off:hover { border-color: rgba(104,152,232,.22); }
.set-apptile > .ic { width: 34px; height: 34px; border-radius: var(--r-card); display: flex;
                     align-items: center; justify-content: center; color: var(--nv-blue-bright);
                     background: rgba(104,152,232,.14); }
.set-apptile.live > .ic { color: var(--nv-gold); background: rgba(232,167,92,.14); }
.set-apptile > .ic svg { width: 19px; height: 19px; }
.set-apptile .nm { min-width: 0; }
.set-apptile .nm b { display: block; font-weight: 500; font-size: 13.5px; }
.set-apptile .nm small { display: block; color: var(--nv-ink-3); font-size: 11.5px; }

.set-modpgr { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 0 14px 14px; }
.set-modpgr button { background: none; border: 1px solid rgba(104,152,232,.22); border-radius: var(--r-card);
                     min-width: 30px; padding: 4px 8px; font-size: 12.5px; color: var(--nv-ink-3); cursor: pointer; }
.set-modpgr button:hover:not(:disabled) { color: var(--nv-gold); border-color: var(--nv-gold-dim); }
.set-modpgr button.on { color: var(--nv-gold); border-color: var(--nv-gold-dim); background: rgba(232,167,92,.08); }
.set-modpgr button:disabled { opacity: .35; cursor: default; }
.set-modpgr .of { margin-left: auto; font-size: 12px; color: var(--nv-ink-3); font-variant-numeric: tabular-nums; }

@media (max-width: 767px) {
    .set-modtiles { grid-template-columns: minmax(0, 1fr); }
    .set-hometiles { grid-template-columns: repeat(auto-fill, 106px); }
}

/*  ⚠⚠ v31.515b — COLLAPSED IS GOLD, OPEN IS BLUE. [stated] owner: *"there is too much blue,
    so make the email cards gold border. But if the card is expanded, the current blue border
    should be maintained."* So the accent is INVERTED against the usual house habit, and
    deliberately: a wall of collapsed cards carries the brand edge, and the one card you are
    working in drops back to the ordinary card border so it reads as the surface it is
    rather than as the loudest thing on the screen.

    ⚠ `:has(details[open])` again, for the same reason as the grid below — which card is open
    changes in the browser with no request. Where `:has()` is unsupported every card simply
    stays gold, which is the collapsed treatment and still correct-looking. */
.set-acct, .set-catcard { border-color: rgba(232,167,92,.38); }
.set-acct:has(details[open]), .set-catcard:has(details[open]) { border-color: rgba(104,152,232,.22); }

/*  ⚠⚠⚠ v31.515a — THE MAIL ACCOUNTS PAIR UP. [stated] owner: *"Arrange in two rows
    (except when the top has the item)."* Collapsed cards sit two to a row; the one that is
    OPEN spans both tracks, because the account being edited carries five fields, a toggle
    row, a save bar and a signature editor and none of that belongs in half a column.

    ⚠⚠ `align-items: start` IS LOAD-BEARING. Without it grid stretches both cards in a row
    to the taller one's height, so a collapsed card beside an open one would grow a tail of
    empty card — the thing the pairing is meant to remove.

    ⚠ Only from 1024: below that one column IS the layout (HR-1), and two 300px cards side
    by side would put the address and its chips in the same fight v31.514 just settled. */
@media (min-width: 1024px) {
    .set-mailgrid, .set-catgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
                                  gap: 12px; align-items: start; }
    .set-mailgrid > .set-acct, .set-catgrid > .set-catcard { margin: 0; }
    .set-mailgrid > .set-acct:has(details[open]),
    .set-catgrid > .set-catcard:has(details[open]) { grid-column: 1 / -1; }
}

/* ── v31.510 — rounds 4 and 5: mail accounts, signatures, calendars ────── */
.set-coflags2 { display: flex; flex-wrap: wrap; gap: 10px 22px; margin: 12px 0 4px; }
/*  the Postmark card is read-only by nature, and says so by being quieter */
.set-acct-ro { opacity: .78; border-style: dashed; }
.set-sigs { margin-top: 12px; padding-top: 10px; border-top: 1px solid rgba(104,152,232,.14); }
.set-sigrow { display: flex; align-items: center; gap: 12px; padding: 8px 0; flex-wrap: wrap; }
.set-sigrow .set-coname { flex: 0 1 360px; --chipw: 48px; }
/*  ⚠ v31.516 — [stated] owner: *"Email text for normal text should be the same height as
    the rich text box for the html box."* `ViewData["ReMin"]` is 150px for the editable
    area and `_RichEd` adds its toolbar above it, so the two panes only match when this
    box carries both. ⚠⚠ THE TWO NUMBERS ARE COUPLED: change `ReMin` in the mail section
    and this has to move with it, which is why they are named together here. */
.set-sigbox { width: 100%; min-height: 182px; font-family: inherit; line-height: 1.5; padding: 9px 11px; }
.set-sigedit { border-color: rgba(232,167,92,.4); }

/*  ⚠ the twelve in ONE column at the narrow end and two from 768 — never more, because
    v1's two-column layout is exactly what made its screen order differ from its string
    order, and three columns would invite the same confusion back. */
.set-cals { display: grid; grid-template-columns: 1fr; gap: 10px 24px; margin: 4px 0 14px; }
@media (min-width: 768px) { .set-cals { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.set-calrow { display: flex; align-items: center; gap: 4px; min-width: 0; }
.set-calrow .set-sw { min-width: 0; }

/* ── v31.509 — the password eye and the capped picker ─────────────────────── */
/*  ⚠⚠⚠ v31.520 — THE INFORMATION ICON THAT USED TO LIVE HERE IS GONE, and this note is
    the signpost rather than a leftover. It was a THIRD presentation of the same idea —
    an 18px blue-grey dot revealing a panel on `:hover` / `:focus-within` — and the owner
    ruled one style for the whole application. ⚠ It also could not work on a phone: a
    touch screen has no hover, so eleven explanations on this screen were unreachable on
    the device half his readers use, which no probe had ever asked about.
    **THE ONE CONTROL IS `Pages/Shared/Toolkit/_NvInfo.cshtml`** — `.nv-info-b` and
    `.nv-info-pop`, rendered by the layout, opened by a CLICK. Nothing about an
    information icon is declared in this stylesheet any more, on purpose: two homes for
    one control is how the three were arrived at. */

/*  the eye rides the house's own in-field button, so it sits where every other
    in-field control on this application sits.

    ⚠⚠⚠ v31.512 — THE GEOMETRY HANGS OFF A CLASS, NOT OFF `:has()`. `.fld-btn` is
    positioned against the WRAP, so the eye only lands inside the field while the input
    fills that wrap — and where it did not, the eye was drawn beyond the input's right edge
    and over the next field. It measured as correct in the sandbox at every width, which is
    exactly why it needs to be stated rather than inferred: `width: 100%` makes the input
    fill its wrap by instruction, and `.set-eyewrap` carries the padding without depending
    on a selector the layout can silently fall through. */
.nv-eye.on { color: var(--nv-gold); }
/*  ⚠⚠⚠ v31.514 — THE EYE IS NOW POSITIONED AGAINST THE INPUT, which is what v31.512
    claimed to have done and did not. `.fld-btn` centres itself on its containing block
    with `top: 50%`, and the containing block was `.fld-wrap` — the LABEL plus the input.
    So the eye centred on a box about 24px taller than the field and sat 12px above it,
    measured, at every width. I had written the right sentence about the mechanism and
    then left the positioned ancestor alone.

    ⚠⚠ `.fld-box` wraps the input AND the button and nothing else, so the containing
    block IS the field. It holds at any field height — which matters this round, because
    the owner's 40px moved every one of these. */
.fld-box { position: relative; display: block; }
.set-eyewrap > .fld-box > .input { width: 100%; padding-right: 40px; }
.set-eyewrap > .fld-box > .nv-eye { right: 4px; }

/*  ⚠⚠⚠ THE CAPPED DROP-DOWN. A native `<select>` popup is drawn by the operating system
    and ignores CSS, so the owner's 120px and 180px cannot be honoured by styling one. The
    list is drawn in the page instead — and the real select stays beside it, hidden, still
    the thing that posts (a hidden control submits; only a disabled one is skipped). */
.nv-pick { position: relative; }
.nv-pick-native { display: none; }
.nv-pick-b { display: flex; align-items: center; text-align: left; cursor: pointer; }
.nv-pick-t { min-width: 0; flex: 1 1 auto; }
.nv-pick-b::after { content: ""; flex: 0 0 8px; width: 8px; height: 8px; margin-left: 8px;
                    border-right: 1.5px solid var(--nv-ink-3); border-bottom: 1.5px solid var(--nv-ink-3);
                    transform: rotate(45deg) translate(-2px, -2px); }
.nv-pick-l { position: absolute; z-index: 45; top: calc(100% + 4px); left: 0; right: 0;
             overflow-y: auto; background: var(--nv-surface-2);
             border: 1px solid rgba(104,152,232,.35); border-radius: 6px;
             box-shadow: 0 10px 28px rgba(0,0,0,.5); padding: 4px; }
.nv-pick-o { padding: 7px 9px; border-radius: 4px; font-size: 13px; cursor: pointer;
             white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-pick-o:hover { background: rgba(104,152,232,.14); }
.nv-pick-o.on { background: rgba(232,167,92,.14); color: var(--nv-gold); }
.nv-pick-o.off { opacity: .45; cursor: default; }
.nv-pick-o.off:hover { background: none; }

/*  ⚠ the file input's own button overflowed its box — owner: *"the Choose pill should fit
    inside the card"*. The control is sized by the browser, so the box is told to contain
    it rather than the pill told to shrink. */
/*  ⚠⚠ v31.514 — the pill is now SIZED FROM THE BOX, not guessed at. The owner pointed at
    the compose-email control as the example and said *"perhaps the solution is to increase
    the height"* — so the box is his 40px, the padding is 5px, and the pill takes exactly
    what the content box leaves (40 − 10 − 2 borders = 28px). Nothing is clipped to fit,
    which is what `overflow: hidden` was doing before: hiding the overflow rather than
    removing it, so the pill still crossed the border and the border simply cut it. */
/*  ⚠ the 4px top is the owner's own number, sent after seeing it on his screen —
    the pill reads as sitting high against a box this shallow without it. */
.set-photorow .input[type="file"] { height: 40px; display: flex; align-items: center;
                                    padding: 4px 10px 0 5px; line-height: 1; }
.set-photorow .input[type="file"]::file-selector-button,
.set-photorow .input[type="file"]::-webkit-file-upload-button {
    height: 28px; margin: 0 10px 0 0; padding: 0 14px; border: 0; border-radius: 999px;
    background: var(--nv-gold); color: #241706; font-family: inherit; font-weight: 200;
    font-size: 13px; line-height: 28px; cursor: pointer; }

/* ── v31.508 — PEOPLE, and the band that says whose record you are in ───── */
/*  ⚠⚠⚠ THE BAND CHANGES COLOUR, it does not merely change its words. v1's screen looked
    identical whether you were editing yourself or somebody else, and that is the fault the
    `Subject` / `Me` separation exists to prevent — a reader who is not looking for a
    difference has to be shown one. */
.set-who.other { border-color: rgba(232,167,92,.55); background: rgba(232,167,92,.06); }
.set-who.other .set-av { box-shadow: 0 0 0 2px rgba(232,167,92,.5); }
.set-whoback { flex: 1 1 100%; display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
               margin-top: 10px; padding-top: 10px; border-top: 1px solid rgba(232,167,92,.28);
               font-size: 13px; color: var(--nv-gold); }
.set-whoback .btn { margin-left: auto; }

.set-pfilter { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.set-pfilter .fld-wrap { flex: 0 1 220px; min-width: 0; }
.set-pfilter .set-pactive { flex: 0 0 auto; padding-bottom: 6px; }
.set-open { white-space: nowrap; }
@media (max-width: 1023.98px) {
    /*  HR-1 — the filter becomes one column rather than a row that cannot fit */
    .set-pfilter .fld-wrap { flex: 1 1 100%; }
    .set-whoback .btn { margin-left: 0; width: 100%; }
}

.set-warn-card { display: flex; flex-direction: column; gap: 10px; }
/*  ⚠ a catalog that is NOT the sign-in one is a tidiness note, not a lockout — the same
    band in a quieter colour, because telling a reader both are urgent tells them neither is. */
.set-warn.mild { border-color: rgba(104,152,232,.35); background: rgba(104,152,232,.07); }
.set-warn.mild b, .set-warn.mild .set-warnx { color: var(--acc-blue); }

.set-catcard .set-cardhead h2 { font-size: 14px; letter-spacing: .06em; color: var(--nv-ink-2); }

/*  ══ v31.506 — THE COMPANY GRID ════════════════════════════════════════════════════
    [stated] Owner: *"Can we have this in Grid format, similar to v1"*.

    ⚠⚠⚠ BELOW 1200 THIS IS NOT A GRID, AND THAT IS THE POINT. Eleven tracks need about
    1050px; the content column is 1134px on his screen and roughly 900 at 1280, so the
    table is declared INSIDE the 1200 query and the stacked layout is the base. HR-1
    forbids the other answer — a table that keeps its columns by scrolling the page
    sideways at 01/02-Screen. One DOM serves both: the header supplies the column names
    above 1200, and below it the header is hidden and each cell shows its own `.co-l`.  */
.set-corow { padding: 12px 0; border-top: 1px solid rgba(104,152,232,.14);
             display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
.set-corow:first-of-type { border-top: 0; }
.set-cohdr { display: none; }
.co-c { display: flex; align-items: center; gap: 8px; min-width: 0; }
.co-c .input { min-width: 0; }
.co-l { font-size: 12px; color: var(--nv-ink-3); white-space: nowrap; }
/*  ⚠ tabular figures, or a column of positions and employee numbers does not line up. */
.co-num { font-variant-numeric: tabular-nums; }
.co-star { flex: 0 0 auto; }
/*  ⚠ the name takes the room it needs and no more — the chip column then sits beside the
    names rather than at the far edge of the card, which reads as a column instead of a
    code adrift. HR-2b's rule is the FIXED TRACK, not a particular distance. */
/*  ⚠⚠⚠ v31.507 — A COLLAPSED COLUMN IS HIDDEN AT EVERY WIDTH, NOT ONLY IN THE GRID.
    Hiding it only above 1500 would leave the stacked layout showing fields the grid has
    decided are empty, so the card would disagree with itself as the window moved. The
    inputs stay in the form either way — see the markup note; a hidden cell still posts its
    value, so saving a card with Team collapsed writes Team back rather than blanking it. */
.set-cotable .co-off { display: none; }
.co-allck:checked ~ .set-cotable .co-c.co-off { display: flex; }
.co-allck:checked ~ .set-cotable .set-cohdr > span.co-off { display: block; }
/*  the control that brings them back — small, and only drawn when something is collapsed */
.co-allbtn { display: inline-flex; gap: 6px; align-items: center; cursor: pointer;
             font-size: 12px; color: var(--nv-ink-3); padding: 2px 0 8px; }
.co-allbtn:hover { color: var(--nv-gold); }
.co-lb-hide { display: none; }
.co-allck:checked ~ .co-allbtn .co-lb-show { display: none; }
.co-allck:checked ~ .co-allbtn .co-lb-hide { display: inline; }
.co-allck:focus-visible ~ .co-allbtn { outline: 2px solid var(--nv-blue); outline-offset: 2px; }

.co-nm { flex: 0 1 460px; }
.co-nm > .set-coname { width: 100%; }
.set-coname { min-width: 0; --chipw: 72px; }
.set-coacts { flex: 0 0 auto; }

/*  ⚠⚠⚠ 1500, AND THE NUMBER IS MEASURED, NOT CHOSEN — TWICE OVER.
    The table's usable width is not the viewport. `.app-main` carries a 1410px cap from
    1024, so this column measures 838px at 1280, 958px at 1400 and 1052px from about
    1494 up, where the cap binds and the width stops growing.

    ⚠ The first cut asked for 1070px of tracks and overflowed at EVERY width including
    1920: Remove was clipped off the right edge and the names collapsed to three
    characters. The second cut fitted, and was still wrong — at 1366 the name cell came
    to 164px, of which the code chip takes 82, leaving 48px of company name. A column
    that fits and cannot be read is not a fix.

    So the grid started where the cap binds and the width is constant: 1052px of column,
    714px of tracks, 80px of gaps, 258px of name cell, 198px of readable name. Below
    that the stacked layout served, which is also what HR-1 requires lower down.
    quick506 arm 4c measures BOTH — the overflow and the surviving name width.

    ⚠⚠⚠ v31.516 — THE 1500 WAS MEASURED AGAINST A TRACK LIST THAT NO LONGER EXISTS.
    [stated] owner, twice in one morning: *"where page width is large, do not create
    additional rows, retain one row per item"* and *"Retain single rows for all large
    screens."* He is right, and the reason is my own: v31.507 took Team out of the
    default set and collapsed every column with nothing in it, which cut the FIXED
    tracks to 442px + 56px of gaps. Everything after that is the name, which is `1fr`
    and simply absorbs the slack — so the row has fitted comfortably for several rounds
    and nobody re-measured the breakpoint that was keeping it hidden.

    ⚠⚠ 1280 AND 1366 ARE MEASURED, NOT CHOSEN, and they DIFFER because the two tables do.
    Company name cell: 190px at 1200 (ellipsised), 270 at 1280, 356 at 1366. People's name
    cell: 110 at 1200, 144 at 1280 — still ellipsised — 230 at 1366. So Company access goes
    single-row from 1280, People from 1366, and the band between keeps People stacked. One
    number for both would either clip People or keep the catalogue stacked at a width where
    it reads perfectly well.
    ⚠ Neither ever OVERFLOWED at any of these widths — the name is `1fr` and ellipsises. A
    clipped name is the defect, not a scrollbar, which is why the arm measures the NAME. */
/*  ⚠ People carries one more text column than the catalogue does, so it needs 86px more
    before its name survives. Measured, not guessed. */
@media (min-width: 1280px) and (max-width: 1365.98px) {
    .set-cotable.set-pplt { display: block; }
}

@media (min-width: 1280px) {
    /*  ⚠⚠⚠ `display: contents` IS WHAT ALIGNS THE COLUMNS ACROSS ROWS. Every row is its
        own <form> because Save and Remove post different handlers; without this each form
        would be its own formatting context and the columns would be ragged — which is the
        complaint this round answers. The hidden inputs inside the form carry the UA's
        `display: none`, so they create no tracks. */
    /*  ⚠⚠ THE TRACK LIST COMES FROM THE CARD (`--cotracks`), because it depends on which
        columns that catalog shows — Team is off by his ruling, and Access group / Employee
        no. drop out where no row carries one. The property is set inline at every width but
        only read here, so the stacked layout below is untouched by it. */
    .set-cotable { display: grid; align-items: center; gap: 0 8px;
                   grid-template-columns: var(--cotracks, 30px minmax(150px, 1fr) repeat(4, 56px) 170px 44px 62px 144px); }
    .co-allck:checked ~ .set-cotable { grid-template-columns: var(--cotracks-all); }
    /*  ⚠ the spinner is ~18px of a 60px track, so the position it is meant to edit gets
        clipped by the control for editing it. The field is two digits; it does not need
        arrows, and `quick506` arm 4d measures every cell for exactly this. */
    .set-cotable .co-num { appearance: textfield; -moz-appearance: textfield; }
    .set-cotable .co-num::-webkit-outer-spin-button,
    .set-cotable .co-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
    .set-cotable .co-c .input { padding-inline: 8px; }
    /*  ⚠⚠ v31.512 — A SELECT'S CHEVRON IS DRAWN BY THE BROWSER INSIDE THE PADDING BOX, so
        the 8px above — which I set to win back track width in v31.506 — left the option
        text running underneath it. `scrollWidth` cannot see that: the text is not clipped,
        it is covered. Owner: *"the text should not overlap the down chevron."* */
    .set-cotable .co-c select.input { padding-right: 26px; }
    .set-cotable > .set-corow, .set-cotable > .set-cohdr { display: contents; }
    /*  ⚠ 10px and no letter-spacing: "FORECASTS" is one word and cannot wrap, so the
        header has to fit its own track or it runs into the next column's head. */
    .set-cohdr > span { font-size: 10px; letter-spacing: 0; text-transform: uppercase;
                        color: var(--nv-ink-3); padding: 0 0 8px; align-self: end;
                        overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .set-cohdr > span:nth-child(n+3):nth-child(-n+6) { text-align: center; }
    /*  the two actions share their track equally (HR-2) and shrink to fit it */
    .set-cotable .set-coacts.nv-actcol { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
    .set-cotable .set-coacts .btn { padding-inline: 4px; font-size: 12px; min-width: 0; }
    .set-cotable > .set-corow > .co-c { padding: 9px 0; border-top: 1px solid rgba(104,152,232,.14); height: 100%; }
    .set-cotable > .set-corow:first-of-type > .co-c { border-top: 0; }
    /*  the header already names the column, so the cell's own label would say it twice */
    .set-cotable .co-l { display: none; }
    /*  the four flags read as marks in a column, so the switch centres in its track */
    .co-f { justify-content: center; }
    .co-f .set-sw { gap: 0; }
    /*  ⚠ a narrower chip track here than in a card: the code is five digits and the name
        needs every pixel the cell can spare. HR-2b's rule is a FIXED track, not 72px. */
    .set-cotable .co-nm { flex: 1 1 auto; }
    .set-cotable .set-coname { --chipw: 60px; }
    .set-cotable .set-coacts { width: 100%; }
}
.set-rm { color: var(--nv-ink-2); }
.set-rm:hover { color: var(--nv-danger); border-color: rgba(217,101,101,.4); }

/*  the default star: a radio in button clothing. ⚠ It posts to its own handler, because
    setting it must clear another row and a row save must never do that. */
.set-star { width: 30px; height: 30px; flex: 0 0 30px; border-radius: 50%; font-size: 15px; line-height: 1;
            border: 1px solid rgba(104,152,232,.25); background: none; color: var(--nv-ink-3); }
.set-star:hover { border-color: rgba(232,167,92,.5); color: var(--nv-gold); }
.set-star.on { border-color: rgba(232,167,92,.65); background: rgba(232,167,92,.14); color: var(--nv-gold); }

/*  four toggles rather than four Yes/No dropdowns: four dropdowns reading "No" are four
    things to read, four switches are one shape. */
/*  ⚠ `.set-coflags` and `.set-cogrid` were the v31.505 row's two wrappers. The grid cells
    replaced them, so the rules are gone rather than left behind — dead CSS naming classes
    no element carries is how the next reader learns the wrong structure. */
.set-sw { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--nv-ink-2); cursor: pointer; }
.set-sw input { position: absolute; opacity: 0; width: 0; height: 0; }
.set-sw i { width: 36px; height: 20px; flex: 0 0 36px; border-radius: 11px; position: relative;
            background: var(--nv-surface-3); border: 1px solid rgba(104,152,232,.22); transition: .15s; }
.set-sw i::before { content: ""; position: absolute; left: 2px; top: 2px; width: 14px; height: 14px;
                    border-radius: 50%; background: var(--nv-ink-3); transition: .15s; }
.set-sw input:checked + i { background: rgba(63,202,133,.18); border-color: rgba(63,202,133,.5); }
.set-sw input:checked + i::before { transform: translateX(16px); background: var(--acc-green); }
.set-sw input:focus-visible + i { outline: 2px solid var(--nv-blue); outline-offset: 2px; }

.set-grantrow { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.set-grantrow .input { max-width: 420px; }
.set-grantrow .set-hint { width: 100%; }

@media (max-width: 1023.98px) {
    /*  ⚠ HR-1 — every cell becomes a full-width row here, so no track can push the page
        sideways. The grid above is declared from 1200 and never reaches this width. */
    .set-corow > .co-c { width: 100%; }
    .set-corow > .co-c .input { width: 100%; }
    .set-coacts { width: 100%; }
    .set-coacts.nv-actcol { grid-auto-flow: column; grid-template-columns: repeat(2, 1fr); }
}

/*  ══════════════════════════════════════════════════════════════════════════
    SAVE TO FOLDER — v31.529                                   Messages module

    Owner's locked mock-up of 10 Nov 2026, and his ruling on Archive: option A,
    the FILTER goes and the ACTION stays. Nothing here hides a message: filing
    adds a mark and the bubble keeps its place.

    ⚠⚠⚠ THE PICKER HAS THREE HOMES AND ONE BODY — the details column at
    04/05-Screen, a centred card at 03, a bottom sheet below 1024. The 1366
    boundary is the SAME one the details column already uses, and it lives in
    two files (here and nv-messages.js `wideEnough`). They are one decision: if
    they drift, the picker opens as an overlay on top of a column that is also
    showing it, and it gets reported as flicker.
    ══════════════════════════════════════════════════════════════════════════ */

/*  ── the bubble row ───────────────────────────────────────────────────────
    ⚠⚠ THE TICK IS A SIBLING OF THE BUBBLE AND THE ROW CARRIES THE ALIGNMENT.
    `.msg-b` used `align-self` inside `.msg-thread`; wrapped in a row, that
    stopped reaching it, so the row owns the side now and the bubble keeps its
    own max-width. `min-width: 0` at every level or a long unbroken word stops
    the whole row shrinking — the HR-1 trap this round has already paid for
    once in the mock-up.                                                      */
.msg-brow { display: flex; align-items: center; gap: 9px; min-width: 0; max-width: 100%; align-self: flex-start; }
.msg-brow.out { flex-direction: row-reverse; align-self: flex-end; }
.msg-brow > .msg-b { align-self: auto; min-width: 0; }

.msg-pick { flex: 0 0 22px; width: 22px; height: 22px; border-radius: 6px; border: 1.6px solid var(--nv-ink-3);
            display: grid; place-items: center; color: transparent; cursor: pointer; background: transparent;
            transition: background .12s ease, border-color .12s ease, color .12s ease; }
.msg-pick:hover { border-color: var(--nv-gold-dim); }
.msg-pick.on { background: var(--nv-gold); border-color: var(--nv-gold); color: #140c02; }
.msg-pick:focus-visible { outline: 2px solid var(--nv-gold); outline-offset: 2px; }
/*  a pending, failed, system or deleted line cannot be filed; the gap is held
    so the bubbles beside it do not jump sideways. */
.msg-pick.is-void { border: 0; cursor: default; }
.msg-b.picked { outline: 2px solid var(--nv-gold); outline-offset: 2px; }

/*  ⚠ v31.528 found this and v31.529 fixes it: the bubble controls are absolutely
    positioned top-right, so on a ONE-LINE bubble they sat on the last word. It
    was invisible while they were transparent until hover. */
.msg-b.hasacts { padding-right: 38px; }

/*  ⚠⚠ v31.530 — `.msg-fmark` HAS GONE, and with it the gold mark a filed line
    used to keep inside its bubble. [stated] owner, 10 Nov: *"Messages saved to
    a Folder must not also appear in the general conversation thread. Folders
    are intended to reduce the size of the main thread."*

    A filed line is no longer in the thread at all, so there is no bubble left
    to mark. What stands in its place is `.msg-filedgap` below — one chip per
    run of filed lines, naming the folder they went to, so the thread does not
    silently lose its shape where something was taken out of view.

    ⚠ The message itself is untouched. This is a PER-READER VIEW RULE, not a
    move: the other participant still sees every line in their own thread, and
    `sms.Message` is not written. Nothing here reaches the other end.        */
.msg-b .msg-meta { display: flex; align-items: center; }

/*  ── the selection bar — it REPLACES the composer, it does not sit above it ── */
.msg-selbar { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; padding: 10px 14px;
              border-top: 1px solid rgba(104,152,232,.18); background: rgba(16,26,48,.85); }
.msg-selbar .msg-selsp { flex: 1 1 auto; }
/*  ⚠⚠ v31.542 — THE BAR MAY STILL WRAP, AND THAT IS NOT WHAT WAS WRONG WITH
    IT. The first cut of this round forced it to one row, on the reasoning that
    a two-row bar is a bar that can be HALF covered. That reasoning was about
    the overlap, and the overlap is fixed properly now: `--nv-abovenav-foot` is
    measured as the navigation's top edge less the BAR'S TOP edge, so it
    reserves the whole bar however many rows it has.

    ⚠ Forcing one row cost the thing the bar is for. Three controls on 348px —
    a primary button and two chips — left `Save to folder (1)` ellipsised to
    `Save …`, a label shortened to make room for a constraint that was no
    longer carrying any weight. Two legible rows, fully visible, beat one row
    that cannot say what its button does.

    What the round actually guarantees is in `quick542`: the sheet stops above
    the bar's top, and the pixel at either END of the bar is the bar. */
.msg-savebtn { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.msg-savebtn[disabled] { opacity: .38; cursor: not-allowed; }
/*  ⚠ v31.542 — Delete wears the refusal the same way Save does, so "you have
    picked nothing yet" looks the same wherever the reader meets it. The red
    only appears once the button can actually act: a permanently red control
    teaches nothing, and a disabled one that looks armed is worse. */
/*  ⚠⚠⚠ v31.543 — `.is-off` LOOKS DISABLED AND IS NOT. The owner reported the
    tooltip never appearing on the disabled Delete, and he is right about why:
    a `disabled` button fires no pointer events, so there is no hover to raise
    a `title` from and no click to answer with. It wears the refusal instead —
    same dimming, same cursor, `aria-disabled` for anyone listening — while
    staying reachable, so the tooltip works and a press explains itself.
    ⚠ `cursor: not-allowed` and the opacity are the whole of what `disabled`
    was contributing here; the guard that actually refuses is in the handler. */
.msg-ddel.is-off { opacity: .38; cursor: not-allowed; }
.msg-ddel:not(.is-off) { border-color: rgba(217,101,101,.5); color: #e8a0a0; }
.msg-ddel:not(.is-off):hover { border-color: rgba(217,101,101,.8); background: rgba(217,101,101,.10); }
.msg-ddel #msgDDelCnt { font-variant-numeric: tabular-nums; font-weight: 700; }
/*  Select, now row 1 column 1 of the ladder — the same "on" treatment the
    header's copy carried, so turning picking on still looks the same. */
.msg-dsel.on { border-color: var(--nv-gold); color: var(--nv-gold); background: rgba(232,167,92,.14); }

/*  ⚠⚠ v31.543 (owner) — PIN IS A STAR BESIDE THE KIND. *"Move pin button and
    display it next to the image above."* It is the ONLY pin control now, so it
    has to carry its own state: the outline is unpinned, the filled star is
    pinned, and the title says which action a press performs. */
.msg-dkind { display: flex; align-items: center; justify-content: center; gap: 8px; }
.msg-dpin {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; flex: 0 0 auto;
    border-radius: 50%; cursor: pointer;
    color: var(--nv-ink-2); background: rgba(22,33,60,.5);
    border: 1px solid rgba(104,152,232,.30);
    -webkit-tap-highlight-color: transparent;
}
.msg-dpin:hover { border-color: rgba(232,167,92,.55); color: var(--nv-gold); }
.msg-dpin.on { border-color: var(--nv-gold); color: var(--nv-gold); background: rgba(232,167,92,.14); }
.msg-dpin:focus-visible { outline: 2px solid var(--nv-gold); outline-offset: 2px; }
.msg-savebtn #msgSaveCnt { font-variant-numeric: tabular-nums; font-weight: 700; }
.msg-selbtn.on { border-color: var(--nv-gold); color: var(--nv-gold); background: rgba(232,167,92,.14); }
.msg-selbtn svg { vertical-align: -2px; }

/*  ── the picker ──────────────────────────────────────────────────────────── */
.msg-foldback { position: fixed; inset: 0; z-index: 70; background: rgba(3,6,14,.62); display: flex;
                align-items: flex-end; justify-content: center; }
.msg-foldcard { width: min(440px, 100%); max-height: 86vh; overflow-y: auto; background: var(--nv-surface);
                border: 1px solid rgba(232,167,92,.2); border-radius: 16px 16px 0 0; padding: 14px 16px 18px;
                box-shadow: 0 -16px 46px rgba(0,0,0,.55); }
/*  from 1024 there are three panes and no room for a fourth, so it centres */
@media (min-width: 1024px) {
    .msg-foldback { align-items: center; }
    .msg-foldcard { border-radius: var(--r-card); border-color: rgba(232,167,92,.26); box-shadow: 0 20px 56px rgba(0,0,0,.6); }
}
/*  and from 1366 it fills the details column that is already there */
.msg-details.is-picker { padding: 16px 14px; }
.msg-foldhead h2 { font-size: 1.05rem; font-weight: 500; margin: 0; }
.msg-foldhead h2 .gold { color: var(--nv-gold); }
.msg-foldsub { font-size: var(--fs-caption); color: var(--nv-ink-3); margin: 2px 0 12px; }
.msg-foldsub b { color: var(--nv-gold); font-variant-numeric: tabular-nums; }
.msg-foldq { width: 100%; }
.msg-foldtree { margin-top: 10px; max-height: 236px; overflow-y: auto; border: 1px solid rgba(104,152,232,.18);
                border-radius: var(--r-card) var(--r-card) 0 0; background: var(--nv-bg-deep); }
.msg-foldrow { display: flex; align-items: center; gap: 9px; width: 100%; min-width: 0; padding: 9px 11px;
               background: transparent; border: 0; border-bottom: 1px solid rgba(104,152,232,.08);
               color: var(--nv-ink); text-align: left; cursor: pointer; font: inherit; font-size: var(--fs-body); }
.msg-foldrow:last-child { border-bottom: 0; }
.msg-foldrow:hover { background: rgba(104,152,232,.08); }
.msg-foldrow.on { background: rgba(232,167,92,.14); }
.msg-foldrow.on .fn { color: var(--nv-gold); font-weight: 600; }
.msg-foldrow .fic { flex: 0 0 17px; display: grid; place-items: center; color: var(--nv-ink-3); }
.msg-foldrow.on .fic { color: var(--nv-gold); }
.msg-foldrow .fn { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-foldrow .fq { font-size: var(--fs-caption); color: var(--nv-ink-3); font-variant-numeric: tabular-nums; }
.msg-foldrow.is-new { cursor: default; }
.msg-foldname { width: 100%; padding: 4px 8px; font-size: var(--fs-body); }
.msg-foldnone { padding: 14px 12px; margin: 0; font-size: var(--fs-caption); }
/*  ⚠ PINNED BELOW THE SCROLLING LIST. As the tree's last child it had already
    scrolled out of sight with four folders — and it is the one affordance this
    whole feature adds. */
.msg-foldnew { display: flex; align-items: center; gap: 9px; width: 100%; padding: 10px 11px;
               border: 1px solid rgba(232,167,92,.18); border-top: 0; border-radius: 0 0 var(--r-card) var(--r-card);
               background: rgba(232,167,92,.07); color: var(--nv-gold); cursor: pointer; font: inherit;
               font-size: var(--fs-body); text-align: left; }
.msg-foldnew:hover { background: rgba(232,167,92,.14); }
.msg-foldnew .fic { flex: 0 0 17px; display: grid; place-items: center; }
.msg-folderr { margin-top: 9px; font-size: var(--fs-caption); color: var(--nv-red); }
.msg-foldact { display: flex; gap: 9px; margin-top: 13px; }
.msg-foldact .btn.primary { flex: 1 1 auto; }

/*  ── the toast. ⚠ It lives in `.msg-work`, OUTSIDE the picker: inside, the
    panel takes `pointer-events: none` the moment a save closes it, and the
    Undo was painted, enabled and unclickable. ───────────────────────────── */
.msg-foldtoast { position: absolute; left: 50%; transform: translateX(-50%); bottom: 22px; z-index: 72;
                 display: flex; align-items: center; gap: 10px; max-width: min(460px, 92%);
                 padding: 11px 15px; border-radius: var(--r-card); background: #13331f;
                 border: 1px solid rgba(86,169,106,.55); color: var(--nv-ink); font-size: var(--fs-body);
                 box-shadow: 0 12px 30px rgba(0,0,0,.5); }
.msg-foldtoast svg { color: #7fd095; flex: 0 0 auto; }
.msg-foldtoast span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-foldtoast .u { margin-left: auto; flex: 0 0 auto; background: none; border: 0; color: var(--nv-gold);
                    text-decoration: underline; cursor: pointer; font: inherit; font-size: var(--fs-caption); }

/*  ── folders in the side panel ───────────────────────────────────────────── */
.msg-fhd { display: flex; align-items: center; gap: 8px; margin: 16px 0 5px; padding: 0 4px 0 10px;
           font-size: var(--fs-caption); letter-spacing: .09em; text-transform: uppercase; color: var(--nv-ink-3); }
.msg-fhd::after { content: ""; flex: 1 1 auto; height: 1px; background: rgba(232,167,92,.16); }
.msg-faddbtn { flex: 0 0 auto; width: 24px; height: 24px; border-radius: 6px; border: 1px solid rgba(232,167,92,.3);
               background: transparent; color: var(--nv-gold); cursor: pointer; font-size: 15px; line-height: 1; }
.msg-faddbtn:hover { background: rgba(232,167,92,.14); }
.mf.msg-fsub { padding-left: 22px; }

/*  ── reading a folder: it opens WHERE A THREAD OPENS ─────────────────────── */
/*  ⚠ v31.548 — the FOURTH copy of the pane fill, found by the arm that asserts
    there is only one. This pane opens where a thread opens, so it is the same
    surface as the list and the chat and must stay the same colour as them. */
.msg-folderpane { border: 1px solid rgba(104,152,232,.18); border-radius: var(--r-card);
                  background: var(--msg-pane-fill, rgba(13,22,42,.35)); min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.msg-work.foldopen .msg-chat { display: none; }
/*  ⚠⚠ v31.530 — THE HEAD WRAPS BY ROWS, NOT BY WORDS. At 03-Screen this pane is
    ~446px and the first draft kept everything on one line: the title collapsed
    to a letter, the two buttons were different widths, and their labels broke
    OUTSIDE them. Owner: *"Use ellipsis if necessary. Labels must fit into
    buttons. buttons must be same width."* So the title owns the first row and
    ellipsises; the buttons own the second, share the width equally, and are
    allowed two lines of label inside themselves. */
.msg-foldpanehead { display: flex; align-items: center; gap: 10px; min-width: 0; padding: 10px 13px;
                    border-bottom: 1px solid rgba(104,152,232,.18); background: rgba(16,26,48,.6);
                    flex-wrap: wrap; row-gap: 8px; }
.msg-foldacts { display: flex; gap: 8px; align-items: stretch; margin-left: auto; min-width: 0; }
.msg-foldacts .chip { flex: 1 1 0; min-width: 84px; max-width: 150px; white-space: normal;
                      text-align: center; line-height: 1.2; padding: 6px 10px; hyphens: none; }
@media (max-width: 1365.98px) {
    /*  below the details column the head has no room for a title row AND a
        button row side by side, so the buttons take a full row of their own. */
    .msg-foldacts { flex: 1 0 100%; margin-left: 0; }
    .msg-foldacts .chip { max-width: none; }
}
.msg-foldpanehead .fic { flex: 0 0 auto; color: var(--nv-gold); display: grid; place-items: center; }
.msg-foldtitle { flex: 1 1 auto; min-width: 0; }
.msg-foldtitle b { display: block; font-size: 1rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-foldtitle .muted { display: block; font-size: var(--fs-caption); }
.msg-foldrows { flex: 1 1 0; min-height: 0; overflow-y: auto; }
.msg-saved { display: flex; gap: 11px; min-width: 0; padding: 12px 14px; border-bottom: 1px solid rgba(104,152,232,.08); }
.msg-saved:hover { background: rgba(104,152,232,.05); }
.msg-savedb { flex: 1 1 auto; min-width: 0; }
.msg-savedb .t1 { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
/*  ⚠ the sender was ellipsised to "C…" at 03-Screen because the date took its
    width first. The name keeps at least half the row and the date wraps under
    it instead — a date on a second line still reads; a one-letter name does not. */
.msg-savedb .t1 { flex-wrap: wrap; row-gap: 2px; }
.msg-savedb .t1 b { font-size: var(--fs-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
                    flex: 1 1 auto; min-width: 55%; }
.msg-savedb .t1 .w { margin-left: auto; flex: 0 0 auto; font-size: var(--fs-caption); color: var(--nv-ink-3); white-space: nowrap; }
.msg-savedb .q { display: block; margin-top: 4px; padding-left: 10px; border-left: 2px solid var(--nv-gold-dim);
                 color: var(--nv-ink-2); overflow-wrap: anywhere; }
.msg-savedb .src { display: block; margin-top: 5px; font-size: var(--fs-caption); color: var(--nv-ink-3); }
.msg-savedb .src a { color: var(--nv-blue-bright); text-decoration: none; border-bottom: 1px dotted currentColor; }
.msg-unfile { flex: 0 0 auto; align-self: flex-start; opacity: .5; }
.msg-saved:hover .msg-unfile, .msg-saved:focus-within .msg-unfile { opacity: 1; }
@media (hover: none) { .msg-unfile { opacity: 1; } }

/*  ⚠ HR-1. Below 1024 the module is one pane at a time, so a folder takes the
    whole width exactly as a thread does — and `min-width: 0` keeps a long
    folder name or an unbroken quote from pushing the page sideways. */
@media (max-width: 1023.98px) {
    .msg-work.foldopen .msg-list { display: none; }
    .msg-folderpane { grid-column: 1 / -1; }
}
.msg-work > * { min-width: 0; }
/*  the folder view's own avatar size — initials only, see nv-messages.js */
.msg-av.msg-avsm { flex: 0 0 32px; width: 32px; height: 32px; font-size: var(--fs-caption); }

/*  ── v31.530 · what a filed line leaves behind in the thread ───────────────
    Owner: *"Items already saved from a folder should only be visible in that
    folder. they should not be shown in the general thread. The idea of folders
    is to reduce size of general conversation thread."*

    ⚠⚠ SO THE BUBBLE LEAVES THE READER'S OWN VIEW — and this chip is what stops
    the thread quietly losing its thread. One line per run of hidden messages,
    naming the folder and opening it; a reply that answers a filed line still
    has something to point at. It is a dozen pixels against the hundreds the
    messages took, which is the reduction he asked for.
    ⚠ NOTHING IS MOVED OR DELETED. This is a view rule for ONE reader: the
    message is still in the conversation, and the other participant still sees
    it exactly where it was written.                                          */
.msg-filedgap { align-self: center; display: inline-flex; align-items: center; gap: 7px; max-width: 92%;
                margin: 6px 0; padding: 3px 11px; border-radius: 999px; cursor: pointer;
                border: 1px dashed var(--nv-gold-dim); background: rgba(232,167,92,.08);
                color: var(--nv-gold); font-size: var(--fs-caption); font: inherit;
                font-size: var(--fs-caption); }
.msg-filedgap:hover { background: rgba(232,167,92,.18); }
.msg-filedgap b { font-variant-numeric: tabular-nums; }
.msg-filedgap span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

/*  ══════════════════════════════════════════════════════════════════════════
    v31.531 — SEVEN FIXES FROM PRODUCTION USE (owner, 10 Nov 2026)
    ══════════════════════════════════════════════════════════════════════════ */

/*  ── 1 · the 01-Screen conversation header ────────────────────────────────
    [stated] *"Where the person's name is hidden, also hide Direct."* At
    01-Screen the header carries Back, avatar, name, Pin, Select and ⓘ in
    under 360px, and the subtitle is the one thing in that row carrying no
    information the reader does not already have — they opened this thread,
    so they know whose it is. Dropping it gives the NAME the room instead,
    which is the half of the row that was being squeezed out.                */
@media (max-width: 767.98px) {
    .msg-chathead .msg-hname .s { display: none; }
    .msg-chathead .msg-hname { justify-content: center; }
}

/*  ── 3 · words wrap between words, never down the page ────────────────────
    [stated] *"Prevent words from wrapping vertically letter-by-letter."*

    ⚠⚠⚠ A PERCENTAGE RE-BASED ITSELF ONTO A BOX THAT DID NOT EXIST WHEN IT WAS
    WRITTEN, and that is the whole defect.

    `.msg-b` has carried `max-width: min(72%, 560px)` since round 1, when a
    bubble was a direct child of `.msg-thread` — a flex column of definite
    width, so 72% meant 72% OF THE THREAD, which is what it reads as.

    v31.529 put `.msg-brow` in between, to carry the selection tick beside the
    bubble. `.msg-brow` is `align-self: flex-start` — shrink-to-fit — so it is
    sized FROM the bubble. The percentage now resolves against it, and the
    constraint closes on itself: the bubble may be at most 72% of a row whose
    width is the bubble. Measured on the owner's own screen — row 132px,
    bubble capped at 95px — and the shorter the message, the harder it bites,
    which is exactly why `Ano` and `Ďakujem` stacked while a long URL beside
    them looked perfectly ordinary.

    ⚠⚠ MY FIRST READING OF THIS WAS WRONG and is recorded so it is not retried:
    I blamed `overflow-wrap: anywhere` (it makes a break opportunity count
    toward min-content, which is real) and added a `min-width` floor. Neither
    touched it — a standalone reproduction of bubble, row and thread would not
    stack at all, because the defect needs the circular percentage and nothing
    else. The floor was a number papering over a misdiagnosis, and it is gone.

    THE FIX: the cap belongs on the row, which IS a flex item of the thread,
    so 72% means what it has always said. The bubble then fills the room the
    row gives it.                                                            */
.msg-brow { max-width: min(72%, 560px); }
.msg-brow > .msg-b { max-width: 100%; }
/*  `break-word`, not `anywhere`: both break an unbroken URL, but `anywhere`
    also lets a flex item with `min-width: 0` — which `.msg-brow > .msg-b` is —
    shrink to one character. Not the cause above, but a loaded gun beside it. */
.msg-b, .msg-b .msg-txt { overflow-wrap: break-word; word-break: normal; }

/*  ── 4 · the Actions ladder ───────────────────────────────────────────────
    [stated] *"Row 1: two equal-width buttons. Combined width of Row 1 = width
    of the single Row 2 button. Row 3 button = same width as the Row 2
    button."*

    ⚠ That geometry IS a two-column grid, so it is built as one rather than
    measured into place with widths: Pin and Mute take a column each, and a
    chip marked `msg-dwide` spans both. Nothing here needs to know how wide
    anything is, which is why it holds at every band and in five languages —
    a hand-set width would have been correct in English only.               */
.msg-dacts { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; align-items: stretch; }
.msg-dacts .chip { height: 32px; display: inline-flex; align-items: center; justify-content: center;
                   gap: 5px; text-align: center; min-width: 0; }
.msg-dacts .chip > span, .msg-dacts .chip > svg { flex: 0 0 auto; }
.msg-dwide { grid-column: 1 / -1; }

/*  ── 5 · 6 · filing by drag ───────────────────────────────────────────────
    [stated] *"Allow individual message cards/items in the conversation thread
    to be dragged directly onto any Folder"* and *"Allow saved Folder items to
    be dragged from one Folder to another Folder; or back to the original
    conversation, equivalent to the existing X/remove-from-folder action."*

    ⚠⚠⚠ THE GRAB CURSOR IS BEHIND `(pointer: fine)` ON PURPOSE. HTML5 drag
    does not fire on touch, and v31.518's lesson is written down: a screen
    that OFFERS a gesture whose other half is not there reads to the reader
    as a fault in their own machine. On touch the two doors that do work —
    Select → Save to folder, and the row's own ✕ — are what remains, and
    nothing on screen suggests otherwise.                                   */
@media (hover: hover) and (pointer: fine) {
    .msg-b[draggable="true"], .msg-saved[draggable="true"] { cursor: grab; }
    .msg-b[draggable="true"]:active, .msg-saved[draggable="true"]:active { cursor: grabbing; }
}
.msg-drag { opacity: .45; }
/*  the target lighting up. ⚠ `outline`, not `border`: a border on a drop
    target reflows the list under the pointer the moment it is offered, and a
    row that moves as you approach it is a row you cannot hit. */
.mf.msg-dropok, .msg-row.msg-dropok {
    outline: 2px dashed var(--nv-gold); outline-offset: -2px;
    background: rgba(232,167,92,.12);
}
/*  ⚠⚠ AND THE REFUSAL IS DRAWN TOO. A saved line dropped on somebody else's
    conversation is refused — a message cannot change conversation, which is
    the rule the whole feature rests on — so the row says no while the
    pointer is still over it, rather than swallowing the drop in silence. */
.msg-row.msg-dropno, .mf.msg-dropno {
    outline: 2px dashed var(--nv-danger); outline-offset: -2px;
    background: rgba(217,101,101,.10); cursor: not-allowed;
}

/*  ══════════════════════════════════════════════════════════════════════════
    v31.532 · `.nv-abovenav` — ONE RULE FOR EVERY SHEET THAT MUST SIT ON TOP OF
    THE PAGE AND ABOVE THE FIXED BOTTOM NAVIGATION

    [stated] owner, 10 Nov 2026, over two screenshots — the Save-to-folder
    picker and the conversation Details card, both with their lower edge buried
    under Home / Apps / Calendar / Search / Profile:

      *"Any Messages overlay/sheet should calculate its usable mobile height
      excluding the fixed bottom navigation, with appropriate bottom
      padding/safe-area handling. **Do not fix these two screenshots with
      independent hard-coded heights if the same shared mobile overlay rule can
      resolve both.**"*

    ⚠⚠⚠ HE IS RIGHT THAT IT IS ONE FAULT, AND IT WAS A COPIED NUMBER. Both
    sheets anchored their bottom edge to the VIEWPORT bottom (`inset: auto 0 0
    0`, and a full-screen `inset: 0` scrim aligning its card to `flex-end`) and
    then tried to buy the nav back with padding. Measured at 390 before this
    round: each ran exactly 96px — the whole nav — into the bar.

    ⚠⚠ AND THE PADDING WAS ALREADY WRONG AT ONE BAND. `padding-bottom: 96px` is
    the 01-Screen height; from 768 the nav is 110px, so every Messages sheet
    under-cleared by 14px on a tablet. Nothing could notice, because the number
    lived in a different file from the height it was copying.

    SO THE RULE RESERVES, IT DOES NOT PAD:
      · the sheet's BOTTOM EDGE stops at the nav's top (`bottom: navclear`),
        which is why the nav can never cover it whatever the content does —
        padding only moves content, and content that outgrows it comes back;
      · its HEIGHT is the band that is actually left, in `dvh` so a phone's
        collapsing browser chrome is counted (`vh` lies there, and lies
        upward);
      · and it SCROLLS INSIDE ITSELF, so Actions at the foot of a long card
        stays reachable instead of being pushed out of the world.

    ⚠ ONE DECLARATION, NOT ONE PER BAND. `--nv-navh` is 96px, 110px and 0px at
    the three bands, declared beside each `.bottomnav` height, so the formula
    below needs no arithmetic of its own anywhere — a rule written once per
    band is a rule that drifts once per band, which is how the old literal came
    to be wrong at 768 while only the phone was ever reported.

    ⚠ It is still wrapped in one breakpoint, because below 1024 is where these
    two are SHEETS at all: from 1024 Details is the module's third grid column
    and must keep its own height, and the picker is a centred dialog that wants
    the viewport it already has.                                             */
@media (max-width: 1023.98px) {
    /*  ONE declaration block, worn by both. The only thing either sheet says
        for itself is how much room something above it needs. */
    .nv-abovenav {
        /*  the fallback first, for anything without `dvh`; the second wins
            where it is supported. ⚠ Both are needed — a lone `dvh` is no
            height at all on an older browser, and a sheet with no cap does
            not scroll, which is the whole of items 2 and 4. */
        /*  ⚠ v31.534 — `--nv-abovenav-gap` is any MARGIN the sheet carries.
            A margin on a fixed box is outside the box, so a sheet with
            `margin: 10px` occupies 20px more than its own height; without
            subtracting it the cap describes a band the sheet does not fit in.
            Zero wherever it is not set, so every other user is unaffected. */
        /*  ⚠⚠⚠⚠ v31.542 — `--nv-abovenav-foot` IS THE SECOND FIXED THING IN THE
            SAME BAND, and it is the symmetric twin of `--nv-abovenav-top`.
            The nav is not the only surface a sheet has to give way to: while
            messages are being picked the SELECTION BAR sits at the foot of the
            thread, and a sheet that reserves only the nav ends INSIDE it.
            Measured at 390 before this round: the Details sheet ran 66→558 and
            the selection bar 442→555, so the sheet covered the bar whole; a
            phone one notch taller ends between the bar's two rows instead and
            leaves Select all / Done showing beneath a clipped sheet — half a
            control under half a control, which is what the owner photographed.
            ⚠⚠ IT IS MEASURED, NEVER TYPED. `nv-messages.js` reads the bar's own
            box and writes this token; the 96/110 literal that cost v31.532 is
            exactly what a hand-written height here would repeat.
            ⚠ Zero wherever it is not set, so every other sheet is unaffected. */
        bottom: calc(var(--nv-navclear) + var(--nv-abovenav-foot, 0px));
        max-height: calc(100vh - var(--nv-navclear) - var(--nv-abovenav-foot, 0px) - var(--nv-abovenav-top, 0px) - (2 * var(--nv-abovenav-gap, 0px)));
        max-height: calc(100dvh - var(--nv-navclear) - var(--nv-abovenav-foot, 0px) - var(--nv-abovenav-top, 0px) - (2 * var(--nv-abovenav-gap, 0px)));
        overflow-y: auto;
        /*  a flick that reaches the end of this list must not then drag the
            thread underneath it */
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
    }

    /*  ══ v31.549 (owner) — ANCHORED TO THE HEADER, NOT FLOATED UP FROM THE
        BOTTOM. *"the card to be anchored to the top"*, over a screenshot with a
        band of the conversation showing between the thread header and the sheet.

        ⚠⚠⚠ THE 56px THAT USED TO SIT HERE WAS TYPED, THREE LINES UNDER THIS
        FILE'S OWN WARNING NOT TO. The header MEASURES 73px at 02-Screen and
        more at 01, where it wraps — so the sheet was handed back 17px it did
        not have and rose OVER the header; on a taller phone the same literal
        leaves the gap he photographed. `nv-messages.js` writes the token from
        the header's own box now, exactly as it does for `--nv-abovenav-foot`.

        ⚠⚠ AND `top` IS SET, which is what "anchored" means. With only `bottom`
        the sheet grows UPWARD from the navigation, so a short card hangs at the
        bottom of the band and leaves the space above it — the gap is the
        CONTENT's height, not a style, and capping the media (v31.548) made it
        bigger. Given both edges the card spans the band whatever it holds.
        ⚠ The 10px margin still insets it on all four sides (v31.534), so the
        card reads as a card and not a slab welded to the header. */
    #msgDetails.nv-abovenav { top: calc(var(--nv-abovenav-top, 0px)); }

    /*  ⚠⚠ THE PICKER WEARS IT ON THE SCRIM, NOT THE CARD. `.msg-foldback` is
        the full-screen backdrop and the card is a flex child aligned to
        `flex-end`, so shortening the SCRIM is what lifts the card. It also
        stops the backdrop greying out a navigation bar that is still live
        underneath it — the owner's *"bottom navigation remains fixed in its
        normal position"* read literally. The card then fills what it is given
        and scrolls inside that. */
    .msg-foldback.nv-abovenav > .msg-foldcard { max-height: 100%; }
}

/*  ── v31.533 · the account card takes the pop-up's gradient ───────────────
    [stated] owner, 10 Nov 2026: *"Replace the current uniform dark background
    on the user/account information card with the subtle gradient background
    already used by V2 pop-up messages, keeping the existing content and
    controls unchanged."*

    ⚠ SCOPED TO THIS CARD. The background it had came from `.card`, which every
    card in the application wears — putting a gradient there would have restyled
    the whole app on a request about one card.

    ⚠⚠ The value is `--nv-grad-panel`, the same declaration `_NvMsg`'s box
    reads, because he asked the two to MATCH and a copied colour drifts. Content
    and controls are untouched: this rule sets a background and nothing else. */
.card.acc-detail { background: var(--nv-grad-panel); }

/*  ⚠ v31.543 (owner) — the folders rail's two bulk actions are off the rail
    and in the three-dot menu now. They stay in the DOM because the menu
    delegates to them and the save sheet binds `#mfSave` by snapshot; what
    moved is where the reader presses, not where the work is done. */
.eml-railact { display: none !important; }
