* { margin: 0; padding: 0; box-sizing: border-box; }

body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f0f2f5; color: #2c3e50; }

/* The old green navbar (brand/logo/date/actions) is retired — see .navbar
   { display:none } in the app-shell section. Its styling rules are removed;
   the brand block, the date and the actions all live in the sidebar now. */

/* Inline line-icons — stroke inherits currentColor so the active pill colours them */
.ic{ width: 1.15rem; height: 1.15rem; flex-shrink: 0; fill: none; stroke: currentColor;
    stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
/* .ic-sm — an icon sitting INSIDE a run of text, where an emoji used to be
   (Session 99). Sized in em not rem so it tracks whatever text it sits in,
   and nudged onto the baseline: an svg is bottom-aligned by default, which
   leaves it visibly low against the words beside it. */
/* 1.2em, not 1em (Ali, Session 99: the + and the Jobs section icons "lookes
   little smaller" — raised globally on his call, not per-spot). Sized in em so
   it tracks whatever text it sits in and follows the text-size setting; the
   baseline nudge grows with it or the icon sits low against the words. */
.ic-sm{ width: 1.2em; height: 1.2em; vertical-align: -0.24em; stroke-width: 2; }

/* MAIN TABS — superseded by the left sidebar (#appSidebar); kept in the DOM
   (display:none, not removed) because switchTab() reads .main-tab[data-section]
   directly and Alt+key shortcuts still resolve against it. */
.main-tabs { display: none; }

/* The green navbar is retired (its markup stays, hidden, only for switchTab +
   Alt-keys). The frame is now just two columns: a full-height sidebar on the
   left, content on the right. There is no top bar at all. */
.navbar { display: none; }

/* APP SHELL — CSS grid: full-height sidebar, then the content column. Single
   row, since the topbar it used to sit above was removed (it only restated the
   sidebar's active module, for ~51px of every viewport). */
.app-shell { display: grid; grid-template-columns: auto 1fr; grid-template-rows: 1fr; align-items: stretch; }

/* Sidebar is a 3-row flex column: .nav-top (Dashboard + pin, fixed) /
   .nav-tree (the scrollable module list, flex:1 - grows to fill whatever
   space is left) / .nav-bottom (Settings, fixed). flex:1 on the middle row
   is what pins Settings to the true bottom of the sidebar instead of just
   trailing off after the last item with empty space below it. */
#appSidebar {
    width: 214px; flex-shrink: 0;
    display: flex; flex-direction: column;
    background: var(--surface); border-right: 1px solid var(--line);
    position: sticky; top: 0; height: 100vh; grid-row: 1 / 3;
}
/* No top bar. Each screen titles itself (the ledger's own header bar, the
   entry panel's header), and the sidebar's filled module pill + highlighted
   sub-item are the breadcrumb - so a global header only restated what was
   already on screen, for ~51px of every viewport. Import/Settings/Logout live
   at the sidebar bottom; the date sits under the brand block. */
.nav-top {
    display: flex; align-items: center; gap: .5rem;
    padding: .7rem .7rem .6rem .8rem; flex-shrink: 0;
    border-bottom: 1px solid var(--line-2);
}
.nav-brand { display: flex; align-items: center; gap: .55rem; flex: 1; cursor: pointer; min-width: 0; }
.nav-brand-tile {
    width: 2.1rem; height: 2.1rem; flex-shrink: 0; border-radius: 9px;
    background: linear-gradient(140deg, var(--brand), var(--brand-2));
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 2px 6px rgba(47,82,64,.35);
}
.nav-brand-txt { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.nav-brand-name { font-size: var(--fs-base); font-weight: 800; color: var(--ink); letter-spacing: -.01em;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-brand-sub { font-size: var(--fs-lbl); font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-3); }
.nav-date { font-size: var(--fs-lbl); color: var(--ink-3); font-weight: 650; padding: .5rem .9rem .6rem;
    border-bottom: 1px solid var(--line-2); flex-shrink: 0; }
.nav-bottom { flex-shrink: 0; padding: .5rem; border-top: 1px solid var(--line-2); }
/* .nav-pin-btn is GONE (2026-08-28). The width toggle moved out of the brand row
   into .nav-bottom above Import and is an ordinary .nav-item now, so it inherits
   every one of those styles instead of carrying its own. */

.nav-tree { flex: 1; overflow-y: auto; padding: .5rem .5rem; }

.nav-group { margin-bottom: 2px; position: relative; }
.nav-item {
    width: 100%; display: flex; align-items: center; gap: .7rem;
    background: none; border: none; text-align: left; cursor: pointer;
    /* --fs-base, not --fs-sm: navigation is a primary control used constantly,
       so it should not be smaller than the data it navigates to. Sub-items stay
       at --fs-sm, which is what now carries the parent/child hierarchy. */
    padding: .5rem .7rem; border-radius: 9px; font: inherit; font-size: var(--fs-base); font-weight: 600;
    color: var(--ink-2);
}
.nav-item:hover { background: var(--brand-tint); color: var(--ink); }
/* The sidebar's keyboard cursor. Same 2px accent ring the rest of the app uses,
   inset so it cannot be clipped by the narrow rail's edge. On the ACTIVE row the
   ring turns white: an accent outline on an accent-filled pill is invisible,
   which is the forest-on-forest trap in another costume. */
.nav-item:focus-visible, .nav-sub:focus-visible {
    outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 9px;
}
.nav-item.active:focus-visible, .nav-sub.active:focus-visible { outline-color: var(--brand-ink); }
/* S131 — THE ACTIVE MODULE PILL IS FOREST, NOT RUST, and this one reaches every
   module, not just Accounts: Ali flagged it in the same breath as the Accounts
   selection — "one left menu also it uses red which is wrong, red in only for
   danger." Selection now speaks with one colour on both sides of the screen.
   The shadow moves with it; a rust glow under a forest pill is the kind of
   leftover that reads as a rendering fault rather than a choice. */
.nav-item.active { background: var(--accent); color: #fff; font-weight: 700; box-shadow: 0 2px 8px rgba(158,79,46,.30); }
.nav-item.active .ic { stroke: #fff; }
/* flex:none, not flex:1 (2026-08-28). The three sidebar counts USED to live
   inside .nav-label — which #appSidebar.rail hides outright, so every count
   vanished in the narrow rail (Ali: "alert when rail cosed does not show alerts
   number, that number is very important"). They are siblings of the label now,
   so the rail can place them itself. That only works if the label stops
   claiming the whole row: the caret takes over pushing to the right edge (see
   its margin-left:auto below), which leaves wide mode pixel-identical while
   freeing the badge to sit immediately after the text as it always did. */
.nav-label { flex: none; display: flex; align-items: center; gap: .4rem; }
/* The narrow rail's word. Hidden everywhere else — .nav-label carries the full
   name in wide mode, this carries the rail's. Purchase and Accounts were briefly
   "Purch" and "Accts"; once the rail went to 5.25rem for the pill's gutter they
   both fit whole and were restored (measured against 58.5 / 62.8 / 69.1px of row
   at the three text sizes: Purchase 47.4 / 50.8 / 55.9, Accounts 48.1 / 51.5 /
   56.6). Only DASHBOARD still does not fit and stays "Dash" — it is the longest
   word and the least-used item. Every word is CENTRED rather than stretched: a
   stretched "Jobs" would drift away from the icon above it. */
.nav-short { display: none; font-size: var(--fs-lbl); font-weight: 600; line-height: 1.15;
    letter-spacing: .01em; }
/* The brand's own rail mark, so the top of the sidebar reads like the rest of it
   (icon, name beneath). Heavier and letter-spaced because it is three capitals
   carrying the brand slot, not a module label - caps optically read larger, so it
   holds that position at the same 11px as the words below. */
.nav-brand-short { display: none; font-size: var(--fs-lbl); font-weight: 800;
    letter-spacing: .06em; color: var(--ink); line-height: 1.15; }
.nav-date-short { display: none; }
/* Disclosure caret for groups that have a submenu - collapsed by default
   (all main menus start closed); toggled by clicking the parent's own row. */
.nav-group:has(.nav-children) .nav-item::after { content: '▸'; color: var(--ink-3); font-size: var(--fs-lbl); flex-shrink: 0; margin-left: auto; }
.nav-group.expanded .nav-item::after { content: '▾'; }
.nav-item.active::after { color: #fff; }
.nav-children { display: none; flex-direction: column; padding-left: 2.75rem; }
.nav-group.expanded .nav-children { display: flex; }
.nav-sub {
    background: none; border: none; text-align: left; cursor: pointer;
    padding: .4rem .6rem; font: inherit; font-size: var(--fs-sm); color: var(--ink-2);
    border-radius: var(--radius-sm);
}
/* The sidebar's screen items are LINKS since S211 (A24), so right-click "Open
   link in new tab", Ctrl+click and middle-click work. A link is inline and
   underlined by default; these two lines make it lay out exactly as the button
   it replaced. */
a.nav-item, a.nav-sub { text-decoration: none; box-sizing: border-box; }
a.nav-sub { display: block; }
/* S131 — HOVER IS QUIET, SELECTION IS ACCENT. Ali: "hover still uses red."
   It was --accent-tint, the SAME fill .nav-sub.active uses three lines up, so
   hovering an unselected row made it look selected and two rows appeared chosen
   at once. That is what he was seeing as stray red — Day Book tinted while
   Vouchers was the actual selection.
   Matches .nav-item:hover directly above, which had always used the quiet tint;
   only the sub-level had drifted onto the accent. */
.nav-sub:hover { background: var(--brand-tint); color: var(--ink); }
.nav-sub.active { background: var(--accent-tint); color: var(--accent-2); font-weight: 700; }

/* RAIL MODE — collapsed to icon marks only (54px). Dashboard+pin stack
   vertically in the top row since they can't fit side by side at 54px. */
/* RAIL WIDTH IS A TOKEN, NOT A NUMBER (2026-08-28). It was 54px in two places
   that had no idea about each other: this rule, and .ls-overlay's left:74px
   ("54px rail + 20px gap", written into its own comment). Widening the rail for
   the word under each icon would have slid the lot selector over the rail with
   nothing failing loudly. Both read the token now. In rem, not px, so the word
   keeps its clearance at every Settings > Text Size. */
/* 5.25rem, not 4.75 (2026-08-28). The 2-digit count chip had squeezed the tree's
   side padding down to .28rem, which left the selected pill 4.2px from the pane
   edge - Ali: "the selection box is also right on edge, should we decrease that
   color box little so we have a bit gap". The pill's inset is what you see, so
   the width gives way instead: 7px of content area buys a real .5rem gutter with
   the chip still inside it. */
:root { --rail-w: 5.25rem; }
#appSidebar.rail { width: var(--rail-w); }
#appSidebar.rail .nav-top { flex-direction: column; padding: .5rem 0 .45rem; }
#appSidebar.rail .nav-label,
#appSidebar.rail .nav-brand-txt,
#appSidebar.rail .nav-date-full,
#appSidebar.rail .nav-children { display: none; }
/* Ali: "date and day are important". The rail used to hide this row outright;
   it now keeps it and swaps the string instead. Centred, and the side padding
   drops from .9rem so the short form has its full width. */
/* The date keeps --fs-lbl, same as the words under the icons. It was briefly
   stepped down to .68rem to clear the pane edge, but Ali spotted that as a
   double fix - "if we are increasing side bar then date font does not need
   change right?" - and he was right: at 5.25rem the row's own padding already
   gives it the same gutter the selected pill has. One size in the rail. */
#appSidebar.rail .nav-date { padding: .3rem .4rem .34rem; text-align: center; }
#appSidebar.rail .nav-date-short { display: block; }
#appSidebar.rail .nav-brand { flex-direction: column; justify-content: center; flex: none; gap: 3px; }
#appSidebar.rail .nav-brand-short { display: block; }
/* The narrow rail is icon-over-word, not icon-only (Ali, 2026-08-28: "what if we
   add P for purchase, J for jobs" — a single letter collided twice, Sales/Stock
   and Accounts/Alerts, so it became a short word instead). Column layout, and
   the tree's side padding comes in so the count has room beside a centred icon. */
#appSidebar.rail .nav-tree,
#appSidebar.rail .nav-bottom { padding: .28rem .5rem; }
/* ROW SPACING IS NOT WHERE THE HEIGHT COMES FROM (2026-08-28). At Ali's real
   698px viewport the nine rows plus the date row and the four bottom rows
   overflowed the tree by 10px. Squeezing the ROWS to absorb it was the wrong
   trade and he caught it on sight - "first it was looking beatiful, now
   something changes" - because a row's padding is the thing you actually see:
   at .3rem the word sits almost on its icon. The rows keep their air, and the
   10px comes out of the surrounding chrome below, which nobody perceives. */
#appSidebar.rail .nav-item { flex-direction: column; justify-content: center;
    gap: 2px; padding: .42rem 0 .34rem; position: relative; }
#appSidebar.rail .nav-short { display: block; }
/* LARGE TEXT ONLY. At Settings > Text Size = Large the nine modules, the date row
   and the four bottom rows need 454px of a 402px tree at Ali's 698px viewport, so
   the ninth module - ALERTS, the one carrying the count that matters most -
   scrolled silently out of sight. Ali: "when text size set to large, it a mess".
   Width cannot fix a vertical overflow, so the rhythm tightens here and only
   here: at Large the type is bigger, so a tighter row still reads comfortably,
   which is exactly why the same spacing felt cramped at Medium and was reverted
   there. Nothing is hidden and no word shrinks. */
html[data-text-size="l"] #appSidebar.rail .nav-item { gap: 1px; padding: .28rem 0 .24rem; }
html[data-text-size="l"] #appSidebar.rail .nav-group { margin-bottom: 1px; }
html[data-text-size="l"] #appSidebar.rail .nav-tree,
html[data-text-size="l"] #appSidebar.rail .nav-bottom { padding: .18rem .5rem; }
html[data-text-size="l"] #appSidebar.rail .nav-date { padding: .22rem .4rem .26rem; }
html[data-text-size="l"] #appSidebar.rail .nav-top { padding: .35rem 0 .3rem; }
/* THE COUNT IN THE NARROW RAIL — Ali's option B, 2026-08-28. It keeps the red
   fill but sits clear of the icon instead of on its corner: the corner badge is
   what crowded the mark ("the red round takes to much space"), not the fill
   itself. Absolute, so the icon stays centred with every other icon in the
   column — centring the icon+count PAIR would have shifted this one mark left of
   the rest and bent the column out of line. */
/* Wide mode has to come out where it went in. The count used to live inside
   .nav-label, so its distance from the word was that flex gap (.4rem) plus its
   own margin-left (6px) = 12px. As a sibling it now inherits .nav-item's larger
   .7rem gap instead, which measured 16.5px — visibly looser. Trimming the margin
   puts it back on 12px exactly. */
#appSidebar .nav-item > .alert-badge { margin-left: 1.5px; }
#appSidebar.rail .nav-item > .alert-badge {
    position: absolute; left: calc(50% + .72rem); top: .3rem;
    margin: 0; min-width: .93rem; padding: .13rem .23rem;
    font-size: .6rem; border-radius: .47rem; line-height: 1;
    font-variant-numeric: tabular-nums;
}
/* No disclosure caret in the rail — there is nothing to disclose there any more
   (peek removed 2026-08-28), and the caret only made the rail marks look uneven. */
#appSidebar.rail .nav-item::after { display: none; }

/* The .peek state is GONE (2026-08-28). Clicking a rail icon used to widen the
   sidebar in place to expose that module's sub-items; it cost one click MORE than
   simply using the page's own sub-tab band, and disagreed with the keyboard where
   Enter never peeked. One rule now: the rail picks the module, the page picks the
   sub-item. Every #appSidebar.rail.peek override lived here and is removed with it. */

/* MAIN CONTAINER */
/* Top padding is 14, not 25: with the top bar gone every screen began with a
   band of empty page above its first element. Bottom stays 25 - that space is
   doing work. NOTE: .purch-modal's height calc subtracts this pair (14+25=39),
   so the two must move together. */
/* width:100% is what makes every screen START AT THE SAME PLACE.
   Without it this box is fit-content - it sized to its widest VISIBLE child and
   then centred itself via margin:0 auto, so the content's left edge moved per
   screen AND per tab. Measured at 1528: Purchase and Stock landed at L214 (wide
   tables), the Alerts Active tab at L561 (only narrow cards, so it shrank to
   621px and read as "centred"), and the Alerts History tab at L285. Ali:
   "alerts are in center of screen and history use big screen ... is it not
   better all the screen start from same positions".
   With width:100% every screen is L214/1314 at 1528 and L54/1096 at 1150, and
   it also REMOVED a pre-existing horizontal scroll on Purchase at 1150.
   Verified against the two surfaces that dock INSIDE this container: the
   Purchase entry is byte-identical (L244/1254, SAVE 47px, 5 rows, both widths);
   the read-only invoice grows 900 -> 1100px, which is simply its own declared
   max-width no longer being squeezed to its min by a fit-content parent. */
.main-container { grid-column: 2; grid-row: 1; width: 100%; min-width: 0; padding: 14px 30px calc(25px + var(--mstrip-h)); max-width: 1700px; margin: 0 auto; }
/* Uniform 20px padding, staged rollout - Purchase, Stock, and the Sales invoice
   screen so far (Ali's call, screen-by-screen same as everything else in this
   design pass). Reclaims 10px each side over the old 30px left/right and
   normalizes top/bottom (was an uneven 14/25) to match. The rest of the app
   keeps 14/30/25 until reviewed. */
.main-container:has(> #transactions.active),
.main-container:has(> #stock.active),
.main-container:has(> #accounts.active),
/* D15: the comment above already claimed Sales as part of this rollout, but
   the selector never actually listed #sales - so its LIST view (both
   Proforma and Invoice) was still on the legacy 30px sides while Purchase/
   Stock were already at 20px. That 10px-per-side gap is what read as "more
   side padding" on Sales. The #inv-screen/#pfScreen rule just below only
   covers the DETAIL overlay, not the list underneath it. */
.main-container:has(> #sales.active),
/* Same D15 gap, found again on Jobs once its list picked up the same
   .pur-panel chrome as Purchase/Stock/Sales - #jobs was never added here. */
/* Same D15 gap a THIRD time: #masters was never in this list either, so it
   sat on the legacy 30px sides while every converted screen was at 20px.
   Added as part of the Masters rebuild (spec 2026-08-03). */
.main-container:has(> #masters.active),
.main-container:has(> #jobs.active) { padding: 20px 20px calc(20px + var(--mstrip-h)); }
/* Sales invoice and Proforma run at 10px, not the 20px the other converted
   screens use (Ali, S168 - tried 20 and read it as too much here). They were
   at 3px from S82 to S168; that exception existed so the header and bottom bar
   would run near-flush and make the inner boxes read as nested, which the frame
   built this session does instead. 10px is the middle: standard-looking margin,
   without spending 40px of width on a screen that carries the most columns.
   Walked 20 -> 10 -> 5 in one sitting; 5 is where Ali stopped. The old 3px is
   two characters away if it ever wants to go back. */
.main-container:has(> #inv-screen.show),
.main-container:has(> #pfScreen.show) { padding: 5px; }
.main-section { display: none; }
.main-section.active { display: block; }
/* No rule under the screen title. It was a 3px #c0392b line left from the old
   palette, and on Alerts it stacked directly above the tab strip's own border -
   two horizontal lines a few px apart. The screens with a proper screen bar
   (Purchase, Stock) never had one. Shared by Dashboard, Masters, Alerts,
   Reports and Accounts. */
.section-title { font-size: 1.8em; color: var(--ink); margin-bottom: 20px; padding-bottom: 0; border-bottom: none;
    display: flex; align-items: center; gap: 10px; }
/* Emoji -> inline SVG, Ali's ask ("can we use color svg with same size as
   emoji?"), scoped down to just the Alerts page title bell (Session 80: he
   asked for the rest of the screen's icons - tabs, job-type labels, buttons -
   to come back out, plain text is enough there). Reuses the existing #ic-*
   sprite so the bell is literally the same shape as the sidebar's.
   COLOURED, not monochrome - .ic sets stroke:currentColor, so this overrides
   it rather than inheriting the text colour. */
.ic-title { width: 1.5em; height: 1.5em; stroke-width: 2; }
.ic-warn  { stroke: var(--warn); }

/* STATS GRID */
.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-bottom: 25px; }
.stat-card { background: white; border-radius: 12px; padding: 22px; display: flex; align-items: center; gap: 18px; box-shadow: 0 2px 10px rgba(0,0,0,0.08); transition: transform 0.2s; border-left: 5px solid transparent; }
.stat-card:hover { transform: translateY(-3px); box-shadow: 0 5px 20px rgba(0,0,0,0.12); }
.stat-card.blue { border-left-color: #3498db; }
.stat-card.green { border-left-color: #27ae60; }
.stat-card.orange { border-left-color: #e67e22; }
.stat-card.purple { border-left-color: #9b59b6; }
.stat-icon { font-size: 2.5em; }
.stat-label { font-size: 0.85em; color: #7f8c8d; margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.5px; }
.stat-value { font-size: 1.8em; font-weight: 700; color: #2c3e50; }

/* DASHBOARD GRID */
.dashboard-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; }

/* CARDS */
.card { background: white; border-radius: 12px; padding: 22px; box-shadow: 0 2px 10px rgba(0,0,0,0.08); margin-bottom: 20px; }
.card-title { font-size: 1.15em; color: #2c3e50; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid #ecf0f1; font-weight: 700; }

/* ACTION BAR */
.action-bar { margin-bottom: 18px; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }

/* BUTTONS */
.btn { padding: 10px 20px; border: none; border-radius: 8px; font-size: 0.95em; font-weight: 600; cursor: pointer; transition: all 0.25s; display: inline-flex; align-items: center; gap: 6px; }
.btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
.btn:active { transform: translateY(0); }
.btn-secondary { background: #7f8c8d; color: white; }
.btn-success { background: var(--pos-deep); color: white; }
.btn-danger { background: var(--neg); color: white; }
.btn-edit { background: transparent; color: var(--warn-deep); border: 1.5px solid var(--warn-deep); padding: 5px 12px; font-size: 0.85em; }
.btn-edit:hover { background: var(--warn-deep); color: white; }
.btn-sm { padding: 6px 14px; font-size: 0.88em; }
.btn-large { padding: 14px 30px; font-size: 1.1em; }

/* TOGGLE BUTTONS */
.toggle-group { display: flex; gap: 8px; margin-top: 5px; }
.toggle-btn { padding: 10px 20px; border: 2px solid #e0e0e0; border-radius: 8px; cursor: pointer; font-weight: 600; background: white; color: #7f8c8d; transition: all 0.25s; font-size: 0.95em; }
.toggle-btn.active { background: #c0392b; color: white; border-color: #c0392b; }

/* FILTER */
.filter-panel { background: #fafafa; border: 1px solid #ecf0f1; padding: 10px 15px !important; margin-bottom: 8px !important; }
.filter-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-bottom: 15px; }

/* FORM GROUPS */
.form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 6px; font-weight: 600; color: var(--ink-2); font-size: 0.92em; }
.form-group input, .form-group select, .form-group textarea { width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: 6px; font-size: 0.95em; font-family: inherit; transition: border-color 0.25s; background: var(--surface); color: var(--ink); }
.form-group input:focus, .form-group select:focus, .form-group textarea:focus { border-color: var(--accent); background: var(--surface); }
.readonly-field { background: var(--head) !important; cursor: not-allowed; color: var(--ink-3); }
.required { color: var(--neg); font-size: 0.9em; }
.editable-note { color: #27ae60; font-size: 0.78em; font-weight: normal; margin-left: 4px; }
.auto-note { color: #3498db; font-size: 0.78em; font-weight: normal; margin-left: 4px; }
.hint { color: #95a5a6; font-size: 0.82em; margin-top: 4px; display: block; }
.full-width { grid-column: 1 / -1; }

/* FORM GRIDS */
.form-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.form-grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }

/* TABLES */
.scrollable-table { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 0.93em; }
thead { position: sticky; top: 0; z-index: 5; }
th { background: linear-gradient(135deg, #c0392b, #922b21); color: white; padding: 11px 13px; text-align: left; font-weight: 600; white-space: nowrap; }
td { padding: 10px 13px; border-bottom: 1px solid #ecf0f1; vertical-align: middle; }
tr:hover { background: #fdf2f2; }
tr:last-child td { border-bottom: none; }
.text-right { text-align: right; }
.text-center { text-align: center; }

/* SUB TABS */
.sub-tabs { display: flex; gap: 8px; margin-bottom: 20px; flex-wrap: wrap; }
.sub-tab { background: white; border: 2px solid #e0e0e0; padding: 10px 22px; border-radius: 8px; cursor: pointer; font-weight: 600; color: #7f8c8d; transition: all 0.25s; font-size: 0.95em; }
.sub-tab:hover { border-color: #c0392b; color: #c0392b; }
.sub-tab.active { background: linear-gradient(135deg, #c0392b, #922b21); color: white; border-color: transparent; }
.master-content { display: none; }
.master-content.active { display: block; }

/* REPORTS GRID */
.reports-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }

/* BADGE */
.badge { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 0.82em; font-weight: 600; }
.badge-success { background: #d5f5e3; color: #1e8449; }
.badge-danger { background: #fadbd8; color: #922b21; }

/* CURRENT RATE BOX */
.current-rate-box { background: linear-gradient(135deg, #27ae60, #1e8449); color: white; padding: 20px 25px; border-radius: 10px; margin-bottom: 15px; font-size: 1.1em; }
.current-rate-box strong { font-size: 2em; display: block; margin-top: 5px; }

/* MODAL */
.modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.65); overflow-y: auto; }
.modal.show { display: flex; align-items: flex-start; justify-content: center; padding: 20px; }
.modal-content { background: white; border-radius: 14px; width: 100%; max-width: 550px; max-height: 90vh; overflow-y: auto; animation: slideDown 0.3s; box-shadow: 0 20px 60px rgba(0,0,0,0.3); }
.modal-xlarge { max-width: 1300px; max-height: 95vh; }
@keyframes slideDown { from { transform: translateY(-30px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
.modal-header { padding: 20px 25px; border-bottom: 2px solid #ecf0f1; display: flex; justify-content: space-between; align-items: center; background: linear-gradient(135deg, #c0392b, #922b21); color: white; border-radius: 14px 14px 0 0; }
.modal-header h2 { color: white; font-size: 1.3em; }
.close { font-size: 1.8em; font-weight: bold; color: rgba(255,255,255,0.8); cursor: pointer; line-height: 1; transition: color 0.2s; }
.close:hover { color: white; }
.modal-body { padding: 25px; }
.modal-footer { padding: 18px 25px; border-top: 2px solid #ecf0f1; display: flex; gap: 10px; justify-content: flex-end; background: #fafafa; border-radius: 0 0 14px 14px; }

/* PURCHASE FORM SECTIONS */
.sticky-section { background: #fdf2f2; border: 2px solid #e8b4b0; border-radius: 10px; padding: 20px; margin-bottom: 20px; }
.sticky-section::before { content: "Set Once - Stays for all items"; display: block; font-weight: 700; color: #c0392b; margin-bottom: 15px; font-size: 0.95em; }
.item-entry-section { background: #f0fff4; border: 2px solid #a9dfbf; border-radius: 10px; padding: 20px; margin-bottom: 20px; }
.items-table-section { margin-bottom: 20px; }
.section-subtitle { font-size: 1.1em; font-weight: 700; color: #2c3e50; margin-bottom: 15px; }
.bottom-section { background: #f8f9fa; border: 2px solid #e0e0e0; border-radius: 10px; padding: 20px; }
.empty-items { text-align: center; padding: 30px; color: #aaa; font-style: italic; border: 2px dashed #ddd; border-radius: 8px; }

/* TOTALS BOX */
.totals-box { background: white; border: 2px solid #e0e0e0; border-radius: 10px; padding: 15px 20px; margin-top: 20px; }
.total-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0f0f0; font-size: 1em; }
.total-row:last-child { border-bottom: none; }
.total-row.grand { font-size: 1.3em; background: linear-gradient(135deg, #c0392b, #922b21); margin: 5px -20px -15px; padding: 15px 20px; border-radius: 0 0 8px 8px; color: white; }
.total-row.grand strong { color: white; font-size: 1.2em; }

/* AVG WEIGHT HIGHLIGHT */
.avg-normal { color: #1e8449; font-weight: 700; }
.avg-warning { color: #e74c3c; font-weight: 700; background: #fadbd8; padding: 2px 8px; border-radius: 4px; }

/* ===== PURCHASE ENTRY 3-PART LAYOUT ===== */
.modal-xlarge { max-width: 1400px; max-height: 98vh; }
.modal-header { padding: 14px 20px; display: flex; justify-content: space-between; align-items: center; }
.modal-header-left { display: flex; align-items: center; gap: 10px; }
.modal-title-icon { font-size: 1.5em; }
.modal-header-right .esc-hint { font-size: 0.85em; opacity: 0.8; background: rgba(255,255,255,0.2); padding: 4px 10px; border-radius: 4px; }

.purchase-body { padding: 12px 15px; display: flex; flex-direction: column; gap: 10px; }

/* PART BOXES */
.part-box { border: 2px solid #ddd; border-radius: 8px; padding: 12px 15px; }
.part-label { font-size: 0.82em; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: #922b21; margin-bottom: 10px; }
.part2-hint { font-size: 0.85em; color: #7f8c8d; font-weight: normal; text-transform: none; letter-spacing: 0; }

/* PART 1 - 2 ROW LAYOUT */
.part1-box { background: #fdf8f8; border-color: #e8b4b0; }
.part1-row1 { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin-bottom: 12px; }
.part1-row2 { display: flex; gap: 20px; align-items: flex-end; border-top: 2px dashed #e8b4b0; padding-top: 12px; }
.p1-field { display: flex; flex-direction: column; }
.p1-purno { width: 80px; }
.p1-date { width: 160px; }
.p1-code { width: 150px; }
.p1-party { flex: 1; min-width: 300px; }
.p1-partycode { width: 200px; }
.p1-field label { font-size: 0.82em; font-weight: 600; color: #555; margin-bottom: 4px; }
.p1-field input { padding: 7px 10px; border: 1.5px solid #ddd; border-radius: 6px; font-size: 0.93em; height: 36px; box-sizing: border-box; }
.p1-field input:focus { border-color: #c0392b; outline: none; }
.p1-party input, .p1-party > div > input { height: 44px !important; font-size: 1.05em !important; font-weight: 600 !important; }
.p1-partycode input { height: 44px !important; font-size: 1.1em !important; font-weight: 700 !important; color: #c0392b !important; letter-spacing: 1px; }
.auto-field { background: #f0f0f0 !important; font-weight: 700 !important; color: #c0392b !important; font-size: 1.1em !important; }
.code-hint { color: #27ae60; font-size: 0.78em; font-weight: normal; }

/* PART 2 */
.part2-box { background: #f8fffe; border-color: #a9dfbf; }
.item-input-row { display: flex; gap: 8px; align-items: flex-end; margin-bottom: 10px; flex-wrap: nowrap; overflow-x: auto; }
.item-field { display: flex; flex-direction: column; flex-shrink: 0; }
.item-field label { font-size: 0.78em; font-weight: 600; color: #555; margin-bottom: 3px; white-space: nowrap; }
.item-field input, .item-field select { padding: 6px 8px; border: 1.5px solid #ddd; border-radius: 6px; font-size: 0.9em; height: 34px; width: 100%; }
.item-field input:focus { border-color: #27ae60; outline: none; }

/* ITEMS TABLE */
.items-table-wrap { max-height: 200px; overflow-y: auto; border: 1px solid #ddd; border-radius: 6px; }
.items-table { width: 100%; border-collapse: collapse; font-size: 0.88em; }
.items-table thead th { background: #27ae60; color: white; padding: 8px 10px; white-space: nowrap; position: sticky; top: 0; }
.items-table tbody td { padding: 7px 10px; border-bottom: 1px solid #f0f0f0; }
.items-table tbody tr:hover { background: #f0fff4; }
.items-table tfoot td { padding: 8px 10px; background: #f8f9fa; border-top: 2px solid #27ae60; font-size: 0.95em; }
.empty-items-cell { text-align: center; color: #aaa; font-style: italic; padding: 20px !important; }
.totals-row { background: #e8f8f0 !important; }
.avg-warn { background: #fadbd8; color: #c0392b; font-weight: 700; padding: 2px 6px; border-radius: 4px; }
.avg-ok { color: #1e8449; font-weight: 700; }

/* PART 3 */
.part3-box { background: #fffdf0; border-color: #f0c060; }
.part3-layout { display: flex; gap: 20px; align-items: flex-start; }
.part3-left { flex: 1; }
.part3-right { width: 280px; flex-shrink: 0; }

.charges-table { width: 100%; border-collapse: collapse; font-size: 0.9em; }
.charges-table tr td { padding: 6px 8px; }
.charge-label { font-weight: 600; color: #555; white-space: nowrap; width: 130px; }
.charge-input { width: 90px; padding: 6px 8px; border: 1.5px solid #ddd; border-radius: 6px; font-size: 0.9em; }
.charge-input:focus { border-color: #c0392b; outline: none; }
.charge-remarks { width: 100%; padding: 6px 8px; border: 1.5px solid #ddd; border-radius: 6px; font-size: 0.88em; }
.charge-remarks:focus { border-color: #c0392b; outline: none; }
.charge-detail { color: #666; font-size: 0.88em; }
.charge-amount { text-align: right; font-weight: 600; color: #2c3e50; white-space: nowrap; min-width: 90px; }

.subtotal-box { background: white; border: 1.5px solid #ddd; border-radius: 8px; padding: 12px 15px; margin-bottom: 12px; }
.subtotal-row { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid #f5f5f5; font-size: 0.9em; }
.subtotal-row:last-child { border-bottom: none; }
.sub-total-final { font-size: 1.15em !important; background: #c0392b; margin: 5px -15px -12px; padding: 10px 15px !important; border-radius: 0 0 6px 6px; color: white; }
.sub-total-final strong { color: white; font-size: 1.1em; }

.save-section { display: flex; flex-direction: column; gap: 8px; }
.btn-save { width: 100%; justify-content: center; font-size: 1.05em; padding: 13px; }

/* KEYBOARD SHORTCUT BADGE */
.kb { font-size: 0.72em; background: rgba(255,255,255,0.25); padding: 2px 6px; border-radius: 4px; margin-left: 4px; font-weight: normal; }

/* REPORT TABS */
.report-content { display: none; }
.report-content.active { display: block; }
.report-actions { display: flex; gap: 10px; margin-bottom: 15px; }

/* REPORT TABLE */
.report-table { width: 100%; border-collapse: collapse; font-size: 0.92em; }
.report-table th { background: linear-gradient(135deg, #c0392b, #922b21); color: white; padding: 10px 12px; text-align: left; white-space: nowrap; }
.report-table td { padding: 9px 12px; border-bottom: 1px solid #f0f0f0; }
.report-table tr:hover { background: #fdf2f2; }
.report-table .total-row td { background: #c0392b; color: white; font-weight: 700; font-size: 1em; padding: 11px 12px; }
.report-table .party-header td { background: #2c3e50; color: white; font-weight: 700; font-size: 0.95em; }
.report-table .party-total td { background: #e8f8f0; color: #1e8449; font-weight: 700; border-top: 2px solid #27ae60; }

/* RESPONSIVE */
@media (max-width: 1200px) {
    .stats-grid { grid-template-columns: repeat(2, 1fr); }
    .form-grid-4 { grid-template-columns: repeat(2, 1fr); }
    .dashboard-grid, .reports-grid { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
    .main-container { padding: 15px; }
    .stats-grid { grid-template-columns: 1fr; }
    .filter-grid, .form-grid-3, .form-grid-4 { grid-template-columns: 1fr; }
}
.kb-dark { font-size: 0.72em; background: rgba(0,0,0,0.15); padding: 2px 6px; border-radius: 4px; margin-left: 4px; font-weight: normal; }

/* PART 2 - Fixed height scrollable items table */
.part2-box { display: flex; flex-direction: column; }
.items-table-wrap {
    max-height: 220px;
    min-height: 80px;
    overflow-y: auto;
    overflow-x: auto;
    border: 1px solid #e0e0e0;
    border-radius: 6px;
    flex: 1;
}
/* Make purchase modal use flex layout so Part3 always visible */
.purchase-form-body {
    display: flex;
    flex-direction: column;
    height: calc(100vh - 120px);
    overflow: hidden;
}
.part2-box {
    flex: 1;
    min-height: 0;
    overflow: hidden;
}
.part3-box {
    flex-shrink: 0;
}

/* PURCHASE MODAL - Fixed layout so Part3 always visible */
.modal-content.purchase-modal-content {
    max-height: 95vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.purchase-form-scroll {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    display: flex;
    flex-direction: column;
    min-height: 0;
}
.part2-box {
    flex-shrink: 0;
}
.items-table-wrap {
    max-height: 200px !important;
    min-height: 60px;
    overflow-y: auto !important;
    overflow-x: auto;
}
.part3-box {
    flex-shrink: 0;
    position: relative;
    z-index: 1;
}
/* Ensure dropdown always on top */
#ddVariety > div:last-child,
#ddParty > div:last-child,
#ddEmployee > div:last-child,
#ddGodown > div:last-child {
    z-index: 999999 !important;
}

/* ── Form controls do not inherit font-family ──
   A <button>/<input> that sets no font-family of its own falls back to the UA
   default — Arial here, and monospace for input[type=date] — so this screen was
   painting SAVE/UPDATE, every ledger row button and the Return inputs in Arial
   while all its surrounding text was Inter. Verified in Chromium: the woff2
   loads and Inter really is the rendering face, so the fault was only ever
   these controls.
   Scoped to BOTH Purchase roots: the entry panel is a SIBLING of #transactions,
   not a child, since it was docked into .main-container (spec 5c-B). One rule
   rather than ten named selectors, so a control added later cannot quietly
   reintroduce the fault. */
#transactions button, #transactions input, #transactions select, #transactions textarea,
#purchaseModal button, #purchaseModal input, #purchaseModal select, #purchaseModal textarea {
    font-family: inherit;
}

/* ============================================================
   PURCHASE MODAL — PHASE 7 FINAL (matches approved demo)
   ============================================================ */
/* ── DOCKED ENTRY (spec 5c-B) ──
   Replaces .modal's fixed positioning + dark scrim. .show is still what makes
   it visible, so all 14 openModal/closeModal call sites are unchanged. */
.purch-entry { display: none; }
.purch-entry.show { display: flex; flex-direction: column; }
/* While the entry is open the module screens step aside - a swap, not an
   overlay. Same :has() idiom already used for the Stock full-width rule. */
.main-container:has(> #purchaseModal.show) > .main-section { display: none !important; }
/* Maximise: borrow the whole viewport on demand. Per-entry, never persisted.
   z-index stays BELOW the shared .modal (1000) on purpose - a maximised entry
   still has to duck behind any modal opened from inside it (Add Party etc.),
   the same as the docked entry does. It only needs to clear the ordinary
   static app chrome (sidebar, main-container), which is auto/0. */
.purch-entry.maxi { position: fixed; inset: 0; z-index: 500; }

.purch-modal {
    background: var(--bg) !important;   /* warm paper ground; zones sit on top of it */
    width: 100% !important;
    max-width: none !important;
    /* viewport minus .main-container's 20px top + 20px bottom padding on
       Purchase (no topbar) - was 14+25=39, now 20+20=40 */
    height: calc(100vh - 40px - var(--mstrip-h)) !important;
    max-height: calc(100vh - 40px - var(--mstrip-h)) !important;
    display: flex !important;
    flex-direction: column !important;
    overflow: hidden !important;
    padding: 0 !important;
    border: 1px solid var(--line);
    /* --radius, not --radius-sm: the ledger card next door uses --radius, and
       the two are the same screen seen through two sub-tabs, so their corners
       must not change roundness as you switch. */
    border-radius: var(--radius);
    /* Traps the item/party/godown/employee pickers' own escape-the-table
       z-index (9999, up to 999999 on their open dropdowns - .p2if-variety
       and friends) inside this card, so they only ever rank against each
       OTHER, never against a sibling modal like #partyModal (z-index 1000)
       appended under <body>. Without this, opening "+ Add New" from any of
       those pickers put the picker's own field in front of the modal that
       was supposed to cover it. */
    isolation: isolate;
}
.purch-entry.maxi .purch-modal {
    height: 100vh !important; max-height: 100vh !important;
    border: 0; border-radius: 0;
}

/* ── HEADER ──
   Light header (surface ground + --line rule), not the old full-width red
   gradient — same reason the green navbar went. With no global top bar, this
   header is what tells you which screen you are on. */
.purch-header {
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    color: var(--ink); padding: 9px 16px;
    display: flex; justify-content: space-between; align-items: center;
    flex-shrink: 0;
}
.purch-hint { color: var(--ink-3); font-size: 0.8em; }
.purch-title { font-weight: 800; font-size: var(--fs-h1); color: var(--ink); letter-spacing: -.01em; }
.purch-no { background: var(--accent-tint); padding: 2px 12px; border-radius: var(--radius-pill); font-size: 0.9em; font-weight: 700; color: var(--accent-2); }
/* Window controls - minimise / maximise / close. Replaces the old text
   Cancel button (.purch-btn-cancel, deleted with it). */
.wctl { display: flex; gap: 5px; }
.wb { width: 1.9rem; height: 1.9rem; border-radius: var(--radius-sm);
    border: 1px solid var(--line); background: var(--surface); color: var(--ink-2);
    cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }
.wb:hover { background: var(--brand-tint); color: var(--ink); }
/* Close (.wb-x) is Cancel Group A - discards an unsaved entry, never destroys a saved
   record - so it must never look destructive. Was red-tinted; matches .wb:hover now
   (button spec sec 5.3). */
.wb-x:hover { background: var(--brand-tint); color: var(--ink); }
/* The .park-dot that used to flag a minimised purchase entry here was removed
   2026-08-04, replaced by the minimised-document strip at the bottom of this
   file. Two indicators for one thing is noise (Ali's call), and the dot only
   ever covered Purchase - proforma and invoice had nothing at all. */
.purch-nav-btn { background: var(--surface); color: var(--ink-2); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 3px 10px; cursor: pointer; font-size: 0.82em; font-weight: 600; }
.purch-nav-btn:hover { background: var(--brand-tint); color: var(--ink); }
.purch-nav-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.purch-nav-info { font-size: 0.8em; background: var(--head); padding: 3px 10px; border-radius: var(--radius-sm); min-width: 130px; text-align: center; color: var(--ink-2); }

/* ── BODY ── */
.purch-body { padding: 8px 12px; display: flex; flex-direction: column; gap: 7px; flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0; }

/* ── PART 1 ──
   Was brand-tint (sage/green) - Ali, 2026-08-19: "green tint, change to sand",
   so part1 now matches part3's ground (--head) instead of standing apart from
   it. Two zones now, not three: sand (part1+part3) bracketing part2's white
   work area. Red/green/yellow still mean neg/pos/warn only, unrelated to this. */
.purch-part1 { background: var(--head); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 8px 12px; flex-shrink: 0; }
/* Row 1 wraps ONLY as a safety valve, and the widths below are what keep it from
   ever needing to. Its four APMC fields originally wanted 1054px in the 1000px
   row at 1150, and wrapping pushed EMPLOYEE to a second line, costing a table
   row (part1 101px -> 139px, 5 rows -> 4). GODOWN and EMPLOYEE were each ~190px
   of input holding "GTC" and "SN", which is where that overflow came from -
   narrowed below, so APMC now fits one line at both widths with room spare.
   A GST purchase adds two more fields here and may still wrap at 1150; that is
   the valve doing its job, and it is the rarer case. */
.purch-row1 { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; margin-bottom: 6px; }
/* Row 2 DOES wrap, as a safety valve - it only acts when the row genuinely
   cannot fit. A GST purchase reveals SUPPLIER INV NO + SUPPLIER INV DATE here,
   and at 1150 those two extra fields used to crush the party field to 22px and
   put the whole PAGE into horizontal scroll (which takes the sidebar with it).
   With wrap they drop to their own line and the party field stays readable. */
.purch-row2 { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; padding-top: 6px; border-top: 1px dashed var(--line); }
.purch-divider { display: none; }
/* ── PART 1 LABELS SIT BESIDE THEIR VALUE, NOT ABOVE IT ──
   Stacked label-over-input cost a whole extra line per row: Part 1 was 147px of
   a 656px panel for six fields, leaving the item table only 149px = 4 rows.
   Inline drops Part 1 to 101px and the table gets 195px = 5 rows.
   MEASURED, at 1528x695 and at the 1150 half-screen, before and after.
   The reference was the Sales invoice, which fits the same six fields plus seven
   buttons into 59px. Note what did NOT help: shrinking the label or input font.
   8.5px / 10px / 11px labels all landed within 4px of each other, because the
   height is structural (two rows + a divider), not typographic. Do not come back
   here and shrink the type — there is nothing in it.
   `.pf` is used ONLY inside `.purch-part1` (verified across index.html and every
   static/js_*.js), so changing the base rule is safe and needs no override. */
.pf { display: flex; flex-direction: row; align-items: center; gap: 7px; }
.pf label { font-size: 0.78em; font-weight: 700; color: var(--ink-3); margin-bottom: 0; white-space: nowrap; flex-shrink: 0; text-transform: uppercase; letter-spacing: 0.3px; }
.pf input, .pf select { padding: 5px 9px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 0.92em; height: 32px; font-family: inherit; background: var(--surface); color: var(--ink); outline: none; box-sizing: border-box; }
.pf input:focus, .pf select:focus { border-color: var(--accent); }
/* The adthidar name takes ALL the slack in its row (Ali: "dont shorten adthidar
   name area, we have empty space, utilise it"). Once `.pf` became a row, the
   `#ddParty` wrapper sized to its content, so the field sat at ~310px with the
   rest of the row empty beside it - the space existed, nothing claimed it.
   `flex:1` on the WRAPPER is what claims it; `min-width:0` lets it shrink below
   its content when the row is tight, and the 200px basis is the floor that,
   together with the row's flex-wrap, stops a GST purchase at 1150 from crushing
   this field to 22px. */
.pf-party { flex: 1 1 200px; min-width: 200px; }
.pf-party > div { flex: 1 1 auto; min-width: 0; }
.pf-party input, .pf-party > div > input { height: 38px !important; font-size: 1.0em !important; font-weight: 600 !important; width: 100% !important; }
#ddParty input { height: 38px !important; font-size: 1.0em !important; font-weight: 600 !important; }
.purch-partycode { width: 150px !important; height: 38px !important; font-size: 1.0em !important; font-weight: 700 !important; color: var(--accent-2) !important; letter-spacing: 1px; border: 2px solid var(--accent) !important; text-align: center; }

/* ── PART 2 ── */
/* min-height is an explicit floor, and it has to be BOTH things at once:
   - not 0, or a later sibling (the returns detail) squeezes this box to
     nothing, and since overflow is visible the item rows spill out and paint
     underneath it - which reads as "the returns panel covers the table";
   - not auto either, because auto floors the box at its own content: it then
     grows past .purch-body and pushes the TOTAL row and the returns panel out
     of the scroll viewport, which is how they went missing.
   A number lets the box shrink so the item table scrolls INSIDE itself, while
   never collapsing. Measured: it must cover the input row + table head + the
   table body's own min-height + the TOTAL foot.
   overflow MUST stay visible - that is what lets the Item and Party dropdowns
   escape the box. */
.purch-part2 { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); display: flex; flex-direction: column; flex: 1; overflow: visible; min-height: 232px; }

/* Input area */
.p2-input-area { padding: 7px 10px 6px; border-bottom: 1px solid var(--line); flex-shrink: 0; background: var(--head); border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
/* Grid tracks are copy-pasted from the table's own colgroup (below) - same 11
   columns, same order, same widths. column-gap stays 0 so a track boundary
   here is the same pixel as the column boundary two elements down; the small
   visual gap between boxes comes from each input giving up 6px of its own
   track (see `.p2if input` width) rather than from a grid gap, which would
   accumulate and drift the row out of alignment with the table by the time
   it reaches Total. The row is pulled out 10px each side (this area's own
   padding) so its 100% is measured against the same width the borderless
   tables measure their 100% against - .p2-tbl-head/body/foot carry no
   padding of their own. No. and Avg Wt/Actions stay blank: there is nothing
   to type into a row that doesn't exist yet, but the columns still have to
   claim their width so Remarks through Total land on their own column. */
.p2-input-row { display: grid; grid-template-columns: 3.9% 7.5% 12.0% 6.8% 11.0% 7.5% 6.6% 9.6% 11.1% 12.2% 11.8%; column-gap: 0; align-items: flex-end; margin: 0 -10px; width: calc(100% + 20px); }
.p2if-sno { }
.p2if { display: flex; flex-direction: column; min-width: 0; }
.p2if label { font-size: 0.82em; font-weight: 600; color: var(--ink-3); margin-bottom: 3px; white-space: nowrap; }
.p2if input { width: calc(100% - 6px); box-sizing: border-box; padding: 6px 9px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 1.05em; height: 36px; font-family: inherit; background: var(--surface); color: var(--ink); outline: none; }
.p2if input:focus { border-color: var(--accent); background: var(--surface); }
/* Read-only computed field. Same treatment as .p3-total-auto (the other
   auto-calculated money field in this modal) — a --head fill would be invisible
   against .p2-input-area's --head ground, and --ink-3 on it fails AA. Avg Wt
   here is the same static "auto" placeholder as Total, not a live calc - the
   real per-row figure (with the low/high colour warning) only exists once the
   row is in the table below; matches this row's existing behaviour for Total. */
.p2if-auto input { background: var(--accent-tint) !important; color: var(--accent-2) !important; font-weight: 700; }
.p2if-variety { position: relative; z-index: 9999; min-width: 0; }
.p2if-variety > div { width: calc(100% - 6px); }
.p2if-variety > div > input { font-size: 1.05em !important; height: 36px !important; border: 1px solid var(--line) !important; border-radius: var(--radius-sm) !important; padding: 6px 9px !important; }
.p2if-variety > div > input:focus { border-color: var(--accent) !important; background: var(--surface) !important; }
.p2if-variety > div > div { z-index: 999999 !important; }

/* Frozen header */
.p2-tbl-head { flex-shrink: 0; }
.p2-tbl-head table { width: 100%; border-collapse: collapse; table-layout: fixed; }
/* Forest header (Ali's pick, option C). Gives the item table a clear top edge
   and stops the column names competing with the numbers. The same --brand
   already carries the active sidebar item, so it reads as one system - and it
   is the old red header's job done without the old red. Dividers step to
   --brand-2 so they stay visible on the dark ground. */
.p2-tbl-head th { background: var(--brand); color: var(--brand-ink); padding: 7px 9px; font-size: 1.0em; white-space: nowrap; text-align: center; border-right: 1px solid var(--brand-2); border-bottom: 1px solid var(--brand); }
/* Header row is centred regardless of column type (Ali, S140) - only the BODY
   keeps left-for-text/right-for-numbers (.p2-tbl-body td.r, untouched). */
.p2-tbl-head th.r { text-align: center; }

/* Scrollable body */
/* No max-height: the table grows into whatever the panel gives it, and
   purchFillEmptyRows() tops it up with ruled empty rows, so spare height reads
   as more table rather than as a white void. min-height keeps the short-screen
   floor - this is the first thing that shrinks when the viewport is tight. */
.p2-tbl-body { overflow-y: auto; flex: 1; min-height: 80px; }
.p2-tbl-body table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.p2-tbl-body td { padding: 7px 9px; font-size: 1.0em; border-bottom: 1px solid var(--line-2); vertical-align: middle; }
.p2-tbl-body td.r { text-align: right; }
.p2-tbl-body tr:nth-child(even) td { background: var(--zebra); }   /* same value, now the shared token */
.p2-tbl-body tr:hover td { background: var(--brand-tint) !important; }
/* Item row keyboard cursor - the exact amber idiom Jobs' Output/Input/Hamali/
   RFS row rings already use (js_jobs.js JD_OUT_RING etc). :focus-within, not
   :focus, so the row stays lit whichever of Edit/Delete actually holds focus
   (a plain :focus would make the highlight vanish the moment focus leaves the
   <tr> itself, which never happens here - only its buttons take real focus).
   Placed after the :hover rule so a mouse-hovered, keyboard-focused row still
   shows amber, not the hover tint - both use !important, so source order is
   what decides the tie. */
.p2-tbl-body tr:focus-within { outline: none; }
.p2-tbl-body tr:focus-within td { background: #f2d585 !important; color: #241f00; }
.p2-tbl-body tr:focus-within td:first-child { box-shadow: inset 3px 0 0 #8a6d0b; }
.p2-empty-cell { text-align: center; color: var(--ink-3); padding: 22px !important; font-style: italic; font-size: 0.9em; }
/* Edit+Delete now share ONE td (the header only ever had one Actions column -
   see the colspan-mismatch fix history above). The flex+gap centering lives on
   an INNER div, not the <td> itself - display:flex directly on a real <td>
   overrides its default table-cell box, which is what stretches a cell to the
   row's full height. That silently shrank this one cell to its own content
   height (37px) inside a 50px row, leaving the row's amber keyboard-cursor
   highlight (:focus-within above) with a plain white gap around the buttons
   instead of covering the whole cell - found by comparing this cell's
   getBoundingClientRect() against its own <tr>'s. The <td> stays a plain
   table-cell (fills the row correctly); the div does the centering. */
.p2-act-cell { padding: 3px; }
.p2-act-inner { display: flex; align-items: center; justify-content: center; gap: 6px; height: 100%; }
/* Filler rows that pad the grid out to 4 lines (emitted by updateItemsTable) */
.purch-empty-row td { height: 34px; border: 1px solid var(--line-2); background: var(--surface); }
/* Persisted save-error highlight on the flagged bags/weight cell */
.pur-cell-err { background: var(--neg-tint) !important; color: var(--neg-deep) !important; font-weight: 700; }
.p2-sno { color: var(--ink-3); }
/* DEAD (no HTML/JS reference — kept for the Phase 2 final cleanup pass):
   .p2-item-name, .p2-avg-ok, .p2-avg-warn */
.p2-item-name { font-weight: 700; color: #1e8449; }
.p2-avg-ok { color: #1e8449; font-weight: 700; text-align: right !important; }
.p2-avg-warn { color: #c0392b; font-weight: 700; text-align: right !important; background: #fdf2f2; }

/* Edit mode — warn/amber is correct here: an in-progress, unsaved row.
   The live row class is .inline-edit-active (added by startInlineEdit); the
   .editing variant below is the legacy name, kept in sync. */
.p2-tbl-body tr.editing td { background: var(--warn-tint) !important; }
.p2-tbl-body tr.editing td input,
.p2-tbl-body tr.editing td select { width: 100%; padding: 3px 5px; border: 1.5px solid var(--warn); border-radius: 6px; font-size: 0.93em; font-family: inherit; background: var(--surface); color: var(--ink); box-sizing: border-box; }
/* Inline-edit fields (emitted by startInlineEdit) */
.p2-edit-inp { width: 100%; padding: 3px 5px; border: 1.5px solid var(--accent); border-radius: 6px;
    font-size: 0.9em; box-sizing: border-box; background: var(--surface); color: var(--ink); font-family: inherit; }
.p2-edit-num { text-align: right; }
.p2-edit-ctr { text-align: center; }
.p2-edit-sel { padding: 3px 4px; font-size: 0.88em; }

/* Frozen footer */
.p2-tbl-foot { flex-shrink: 0; border-top: 2px solid var(--line); }
.p2-tbl-foot table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.p2-tbl-foot td { padding: 7px 9px; font-size: 1.02em; font-weight: 700; background: var(--head); color: var(--ink); }
.p2-tbl-foot .ft-grand { color: var(--accent-2); }
.p2-tbl-foot td.r { text-align: right; }

/* ── ROW ACTION BUTTONS — one definition for the whole app ──────────────────
   Session 99. Before this, "remove this row" was drawn three different ways —
   solid red (Purchase entry), a hardcoded off-palette red (#e74c3c, the old
   invoice), and a bare ✕ painted in --line, a BORDER token used as an icon
   colour, which measured 1.34:1 against its own background where 3.0 is the
   floor. Ali, Session 99: "delete ✕ is barely visible", then the rule —
   "anything which delete or removes need a solid red and no outline red."

   TWO SHAPES, ONE HOVER RULE:
   - a bare ✕ inside a row has no word to carry its meaning, so the SHAPE must:
     solid red at rest, darker red on hover. That is this block.
   - a button with a word on it (Remove, Clear All) keeps the outline and
     FILLS solid on hover — the .btn-danger-outline design Ali approved:
     "clearall button, when we hover or go for selection it will with solid
     red, its a button design which matches the jobs page."

   EDIT IS --warn-deep, NEVER --accent. --accent is user-switchable across 5
   presets, so an accent-coloured Edit button changes colour per preset while
   the identical button in Jobs does not. Same trap already fixed once for
   --attn (foundation.css). The lot selectors had Edit in RED, sitting next to
   a red Remove — two reds side by side where one edits and one destroys.

   These selectors are the single source; the older per-screen declarations
   (.btn-rm-acc, .btn-edit-acc, .inv-del-btn) were deleted, not left dormant. */
.btn-edit-item, .btn-edit-acc {
  background: var(--surface); color: var(--warn-deep);
  border: 1.5px solid var(--warn-deep); border-radius: 4px;
  padding: 3px 7px; cursor: pointer; font-size: 0.82em; font-weight: 700;
  font-family: inherit; line-height: 1.2; margin-right: 2px;
  display: inline-flex; align-items: center; gap: 4px;
}
.btn-edit-item:hover, .btn-edit-acc:hover { background: var(--warn-deep); color: #fff; }
/* S144 — the shared 2px above sits Edit almost touching Delete, which reads as
   one control rather than two (and Delete is destructive). The job tables get
   real separation; Purchase keeps the 2px its own S142 layout was tuned to. */
.tbl-out .btn-edit-acc, .tbl-dc .btn-edit-acc { margin-right: 10px; }

.btn-remove-item, .btn-rm-acc, .inv-del-btn {
  /* border:1.5px solid transparent (not `none`) so this button claims the same
     box height as .btn-edit-item's real 1.5px border - `none` made Delete ~3px
     shorter than Edit, which is what read as "uneven" sitting next to it. */
  background: var(--neg); color: #fff; border: 1.5px solid transparent; border-radius: 4px;
  padding: 3px 7px; cursor: pointer; font-size: 0.82em; font-weight: 700;
  font-family: inherit; line-height: 1.2;
  display: inline-flex; align-items: center; gap: 4px;
}
.btn-remove-item:hover, .btn-rm-acc:hover, .inv-del-btn:hover { background: var(--neg-deep); color: #fff; }
.btn-edit-item:disabled, .btn-edit-acc:disabled,
.btn-remove-item:disabled, .btn-rm-acc:disabled { opacity: .45; cursor: not-allowed; }
/* A SECOND, stronger cue on top of the row's amber tint (above) - a colour
   change with nothing moving is easy to miss the instant focus lands here
   programmatically (editPurchase's initial focus, or landing back here after
   Save/Cancel/Escape), even though it's obvious once an arrow key visibly
   moves it. Same outline idiom the app already uses for other custom-
   focusable controls (.acc-bar:focus-visible, Jobs). */
.btn-edit-item:focus-visible, .btn-remove-item:focus-visible,
.btn-save-inline:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Edit turns into Save IN PLACE on two screens (Jobs output-lot row, and the
   job lot selector). That was done with inline styles set from JS — a
   hardcoded #27ae60, a fourth green outside the palette, and inline styles
   beat any stylesheet, so the button silently ignored every later fix. It is
   a state now, so Save is the app's real Save green everywhere. */
.btn-edit-acc.is-saving, .btn-ls-edit-injob.is-saving {
  background: var(--pos-deep); color: #fff; border-color: var(--pos-deep);
}
.btn-edit-acc.is-saving:hover, .btn-ls-edit-injob.is-saving:hover { filter: brightness(1.12); }
.btn-save-inline { background: var(--pos-deep); color: white; border: none; border-radius: 4px; padding: 3px 7px; cursor: pointer; font-size: 0.82em; margin-right: 2px; }
.btn-cancel-inline { background: var(--surface); color: var(--ink-2); border: 1px solid var(--line); border-radius: 4px; padding: 3px 7px; cursor: pointer; font-size: 0.82em; }

/* Dropdown z-index */
#ddVariety > div:last-child, #ddParty > div:last-child,
#ddEmployee > div:last-child, #ddGodown > div:last-child { z-index: 999999 !important; }

/* ── PART 3 — 4-column ── */
/* Pinned footer: a sibling of .purch-body, not a child, so it never scrolls
   away. flex-shrink:0 keeps SAVE at full size no matter how tall the form
   gets. The margin replaces the padding/gap it used to inherit from the body. */
/* GRID, not flex, since 2026-08-31: the columns are the items table's colgroup
   (see the block below), which is what puts each footer figure under its own
   heading. The old flex row and its .p3-col-div separators are gone with it. */
.purch-part3 { background: var(--head); border: 1px solid var(--line); border-radius: var(--radius-sm);
  /* NO horizontal padding: the grid has to span the same box as the items table or
     the columns drift. Measured before this — the 14px each side pulled Code in by
     7px and Total out by 7px, and Ali is reading the two against each other. The
     edge breathing room moved into the first and last cells instead. */
  padding: 10px 0; margin: 0 12px 10px; flex-shrink: 0;
  /* EIGHT columns, and they are the items table's own, merged only where the
     expense region needs the width: No+AvgWt+Remarks+Code = 30.2%, then Item, Lot,
     Bags, Rate, Weight, Total and the actions column one for one.
     The labels start at ITEM because that is where the table's own TOTAL row
     starts — Ali reads the two against each other, so "TOTAL" and "Bag charges"
     and "Round off" all begin on the same pixel. */
  display: grid;
  grid-template-columns: 30.2% 11% 7.5% 6.6% 9.6% 11.1% 12.2% 11.8%;
  align-items: center; height: 145px; }
.p3-col-div { width: 1px; background: var(--line); margin: 0 14px; flex-shrink: 0; }
/* center, not space-between: whenever one column got taller (the net-after-return
   tag, a GST row) every other column's rows were pushed apart to the full height,
   opening a gap in the middle of the bar. Centred rows stay a group and keep
   their own gap whatever the bar height is. */
.p3-col { display: flex; flex-direction: column; justify-content: center; gap: 8px; }
.p3-col-wide { flex: 1.2; }
.p3-col-mid { flex: 0 0 auto; }
.p3-col-summary { flex: 0 0 auto; justify-content: center; min-width: 130px; }
.p3-field { display: flex; align-items: center; gap: 7px; }
.p3-sign { width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.88em; flex-shrink: 0; }
/* +/- / x / auto marks: these ARE semantic (adds money, deducts money, computed) */
.p3-sign-plus { background: var(--pos-tint); color: var(--pos-deep); }
.p3-sign-minus { background: var(--neg-tint); color: var(--neg-deep); }
.p3-sign-auto { background: var(--accent-tint); color: var(--accent-2); font-size: 0.8em; }
.p3-sign-x { width: 26px; height: 26px; border-radius: 5px; background: var(--neg-tint); color: var(--neg-deep); display: flex; align-items: center; justify-content: center; font-weight: 900; font-size: 1.0em; flex-shrink: 0; border: 1px solid var(--neg); }
.p3-label { font-size: 0.8em; font-weight: 700; color: var(--ink-3); white-space: nowrap; width: 70px; }
.p3-desc { flex: 1; padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 0.88em; font-family: inherit; height: 30px; min-width: 0; background: var(--surface); color: var(--ink); }
.p3-desc:focus { border-color: var(--accent); outline: none; }
.p3-input { padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 0.95em; width: 75px; text-align: right; font-family: inherit; height: 32px; background: var(--surface); color: var(--ink); }
.p3-input:focus { border-color: var(--accent); outline: none; }
.p3-total-auto { padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 0.95em; width: 100px; text-align: right; font-family: inherit; height: 32px; background: var(--accent-tint); color: var(--accent-2); font-weight: 700; }
.p3-divider-h { height: 1px; background: var(--line); margin: 2px 0; }
.p3-summary-item { display: flex; flex-direction: column; align-items: flex-start; justify-content: center; flex: 1; }
/* Label and the net-after-return tag share ONE line. As a third stacked line the
   tag grew the whole Part 3 bar and dragged the other columns apart with it. */
.p3-summary-head { display: flex; align-items: center; gap: 5px; margin-bottom: 2px; }
.p3-summary-label { font-size: 0.72em; font-weight: 700; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.5px; }
.p3-summary-value { font-size: 1.25em; font-weight: 700; color: var(--ink); }
/* TODO-role: ".red" on the grand total is EMPHASIS, not danger — --neg would be
   wrong, and --ink is what .p3-summary-value already resolves to (the modifier
   would render as a no-op). Using --accent-2 to keep the distinction. */
.p3-summary-value.red { color: var(--accent-2); }
/* ~70% of the column height, not the full 125px — filling it made SAVE the loudest
   thing in the bar when the total is what should be. Centred, so the four rows still
   read as the subject. */
.p3-save-col { grid-column: 8; display: flex; align-items: center; justify-content: center;
               align-self: stretch; padding: 0 14px 0 10px; }
.p3-save-col .purch-btn-save { flex: 1; height: 88px; padding: 0 12px; min-width: 0; }

/* ── PURCHASE FOOTER, REBUILT 2026-08-31 (Ali) ──────────────────────────────
   The bar is a GRID whose columns are the items table's own colgroup, so every
   figure sits under the heading it came from. The split lands at the end of
   Remarks (23.4%), not on a round 30% — Ali: "we will limit + - expense till
   remarks (i am comparing table vs bottom)".

     23.4%          17.8%      7.5%   16.2%     11.1%    12.2%    11.8%
     to end Remarks Code+Item  Lot    Bags+Rate Weight   Total    actions
     the 2 expenses label      bags   damaged   good x   value    SAVE

   HEIGHT IS 145px, up from 125. Measured: the items table goes 469px -> 449px,
   which is 12.7 rows -> 12.1, so it keeps all 12 whole rows and only gives up a
   partial one that was never usable. */
.p3-exp { grid-column: 1; display: flex; flex-direction: column; justify-content: center;
          gap: 8px; padding-left: 14px; padding-right: 14px; border-right: 1px solid var(--line); align-self: stretch; }
.p3-rows { grid-column: 2 / 8; display: grid; grid-template-columns: subgrid;
           align-content: center; row-gap: 5px; }
/* Item | Lot | Bags | Rate | Weight | Total — inherited as SUBGRID, not restated as
   fractions. Restating them re-rounds the percentages independently of the bar and
   left every cell 3-4px off its column; passing the tracks straight through makes
   the two exact. */
.p3-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid;
          align-items: center; height: 24px; }
.p3-row-charge { height: 30px; }
.p3-row > * { padding: 0 9px; }
.p3-rk { font-size: 0.86em; font-weight: 700; color: var(--ink-3); white-space: nowrap;
         overflow: hidden; text-overflow: ellipsis; }
.p3-rc { display: flex; align-items: center; justify-content: flex-end; gap: 6px;
         font-size: 0.86em; font-weight: 600; color: var(--ink-3); white-space: nowrap; }
.p3-rc b { color: var(--ink); font-weight: 800; font-size: 1.12em; }
/* THE BAGS PAIR: the word pulled RIGHT to the end of the Lot column, the count pushed
   LEFT to the head of the Bags column, so the two sit together and the air falls after
   them - between the count and "Damaged", which is where it was missing. Right-aligning
   the count instead put it 9px from the word (measured: 30 ended at 868, Damaged began
   at 877) and the two read as one string. */
.p3-rbagslab { justify-content: flex-end; }
.p3-rbags    { justify-content: flex-start; }
/* THE DAMAGED CELL OVERFLOWS ITS OWN COLUMN, and being flex-end it overflows LEFTWARD,
   which is the other half of why the count and the word collided. Measured at 1150: the
   cell is 96px, its content 124px, so "Damaged" began at 630 - the exact pixel the count
   ended. Trimmed to 112px so the overflow stops short of the count instead of touching
   it. The input holds a bag count, never a money figure, so 44px is not tight. */
#p3ApmcDamaged { gap: 4px; }
#damagedBags { width: 44px; }
.p3-rx { color: var(--ink-3); font-weight: 700; }
.p3-rlab { color: var(--ink-3); font-weight: 600; }
.p3-ra { font-size: 0.92em; font-weight: 700; color: var(--ink); text-align: right;
         font-variant-numeric: tabular-nums; }
/* The rounding row is the one figure nobody typed, so it is the one that has to
   look different — same warn tone the "net" tag already uses. */
.p3-row-round .p3-ra { color: var(--warn-deep); }
/* the un-rounded figure the round-off came from: present, but never competing with
   the total two rows down */
.p3-rraw { font-size: 0.8em; color: var(--ink-3); font-variant-numeric: tabular-nums; }
/* The round-off is the one figure in the bar you can type. It stays quiet until it
   holds YOUR value, which is when it earns the accent border. */
.p3-ra-edit { display: flex; justify-content: flex-end; }
/* IT HAS TO LOOK LIKE A FIELD. The first cut was transparent until hovered, which
   made the one typable figure in the bar indistinguishable from the read-only ones —
   Ali: "it does not show a field, its just sand". Same white ground and border as
   Damaged and Bag Rate, because it is the same kind of thing. */
.p3-roundin { width: 86px; height: 26px; padding: 3px 7px; text-align: right;
              font-family: inherit; font-size: 1em; font-weight: 700;
              color: var(--warn-deep); background: #fff;
              border: 1px solid var(--line); border-radius: var(--radius-sm);
              font-variant-numeric: tabular-nums; }
.p3-roundin:hover { border-color: var(--ink-3); }
.p3-roundin:focus { outline: none; border-color: var(--accent);
                    box-shadow: 0 0 0 2px var(--accent-tint); }
.p3-roundin.is-manual { border-color: var(--accent); background: var(--accent-tint);
                        color: var(--accent-2); }
/* when the figure is his, the word beside it says so */
.p3-row-round .p3-rlab { font-style: normal; }
/* the total row's own bags / rate / weight, matching the table's TOTAL row weight */
.p3-row-total .p3-rc b { font-size: 1em; font-weight: 700; color: var(--ink); }
/* A row with nothing in it greys out rather than vanishing, so the four rows stay
   where the eye last found them (Ali signed this off on the plate). */
.p3-row-empty .p3-rk, .p3-row-empty .p3-ra { color: var(--ink-4, #a0a69c); }
.p3-row-total { height: 32px; border-top: 2px solid var(--line); }
/* flex, so the "net" tag sits BESIDE the label instead of wrapping beneath it and
   colliding with the row below. nowrap on the text alone does not stop an
   inline-block tag from taking its own line once the cell is narrow. */
.p3-row-total .p3-rk { font-size: 0.78em; font-weight: 800; letter-spacing: 0.06em;
                       text-transform: uppercase; display: flex; align-items: center;
                       gap: 6px; overflow: visible; }
.p3-row-total .p3-ra { font-size: 1.34em; font-weight: 800; color: var(--accent-2);
                       letter-spacing: -0.01em; }
.p3-inline { height: 26px; width: 54px; padding: 3px 7px; font-size: 1em; text-align: right;
             font-weight: 700; color: var(--ink); border: 1px solid var(--line);
             border-radius: var(--radius-sm); background: #fff; font-family: inherit;
             font-variant-numeric: tabular-nums; }
.p3-inline-ro { width: 92px; background: var(--accent-tint); color: var(--accent-2); }
/* The expense boxes lose their text labels: the sign badge already says which way
   each goes, and dropping the word is what lets the pair fit once the region stops
   at Remarks. Measured — the placeholders need 122px and 136px against 143px. */
.p3-exp .p3-field { gap: 8px; }
.p3-exp .p3-desc { flex: 1; min-width: 0; }
.p3-exp .p3-amt { width: 68px; text-align: right; font-variant-numeric: tabular-nums; }
/* The one genuinely semantic green left in this modal. Solid --pos-deep per the
   spec's white-on-success AA rule (--pos fails white text). */
.purch-btn-save { background: var(--pos-deep); color: white; border: none; border-radius: var(--radius-sm); padding: 0 32px; font-size: 1.1em; font-weight: 700; cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-width: 100px; }
.purch-btn-save:hover { background: var(--pos); }
/* Edit mode: the button becomes UPDATE #n. Warn, not success - it overwrites an
   existing record rather than creating one. (Was a hardcoded #e67e22 set from JS.) */
.purch-btn-save.is-update { background: var(--warn-deep); }
.purch-btn-save.is-update:hover { background: var(--warn); }

/* The .pur-card* card view, .pur-detail-table, .pur-footer-* and .pur-btn-detail
   were deleted 2026-07-26: the ledger has rendered as .pur-view-table since
   Phase 7 and every one of those 21 selectors had zero references in any JS or
   template. Deleted rather than converted - no point restyling a dead screen. */

/* ── AVG WEIGHT CELL HIGHLIGHTING ── */
/* Unified with Stock's .sc-avg-* (plan: avg-weight must look identical app-wide).
   LOW -> --low/--low-tint (orange, no border), HIGH -> --neg-deep/--neg-tint. */
/* nowrap: the flagged rows carry a warning mark after the number, and in this
   narrow column it wrapped to a second line - which made only the flagged rows
   double height and left the item list looking ragged. */
.purch-avg-ok  { text-align:right; padding:4px 6px; font-weight:700; color:var(--pos-deep); white-space:nowrap; }
.purch-avg-low { text-align:right; padding:4px 6px; font-weight:700; color:var(--low) !important; background:var(--low-tint) !important; border-radius:4px; white-space:nowrap; }
.purch-avg-high{ text-align:right; padding:4px 6px; font-weight:700; color:var(--neg-deep) !important; background:var(--neg-tint) !important; border-radius:4px; white-space:nowrap; }
.purch-avg-mark{ font-size:0.82em; opacity:0.75; }
tr.inline-edit-active td { background:var(--warn-tint) !important; }

/* ── REMOVE SPINNERS from all number inputs ── */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
input[type=number] { -moz-appearance: textfield; appearance: textfield; }

/* ── BASE FONT for purchase modal — overrides global 0.93em table rule ──
   1.067rem == 16px at the default 15px rem anchor, so this is pixel-identical
   today but now follows the Settings > Text Size control instead of ignoring it. */
.purch-modal { font-size: 1.067rem; }

/* Part 1 */
.purch-part1 .pf label { font-size: 0.78em !important; font-weight: 700 !important; }
.purch-part1 .pf input, .purch-part1 .pf select { font-size: 0.92em !important; height: 32px !important; }
#ddParty input { font-size: 1.0em !important; height: 38px !important; font-weight: 600 !important; }
.purch-partycode { font-size: 1.0em !important; height: 38px !important; }
/* ── ROW 1 FIELDS SIZED TO THEIR REAL CONTENT (Ali's ceilings, not guesses) ──
   Every one of these was rendering at the browser's default input width while
   holding a handful of characters. Ali gave the real maxima: godown and employee
   "are short name and max it will use is 4 to 5 letters", the GST invoice number
   "may use max 5 to 7 digit", and the date field "has some empty space".
   Together with dropping the word SUPPLIER from both labels, this is what gets
   the six-field GST row 1 onto ONE line - it needed 1283px in a 1202px row even
   at the shortest labels, so trimming widths was the only lever left.
   Same lesson as the ledger's GST column last session: ask for the real ceiling,
   do not size from whatever happens to be on screen. */
#purchaseDate { width: 132px !important; }
#ddGodown input, #ddEmployee input { font-size: 0.92em !important; height: 32px !important; width: 96px !important; }
#supplierInvoiceNumber { width: 84px !important; }
#supplierInvoiceDate { width: 140px !important; }

/* Part 2 input row — match demo: 1.05em inputs */
.p2if label { font-size: 0.82em !important; font-weight: 600 !important; }
.p2if input { font-size: 1.05em !important; height: 36px !important; }
.p2if-variety > div > input { font-size: 1.05em !important; height: 36px !important; }

/* Part 2 table — override the global table 0.93em rule.
   0.967rem == 14.5px at the default 15px rem anchor (was 1.067rem/16px).
   Ali's call: 16px "look like a giant" next to the 12.5px form labels above it.
   MEASURED FIRST at 16/15/14.5/14 on purchase 158 at his real 1528x695 and at the
   1150 half-screen: rows visible (7), SAVE clearance (47px) and horizontal scroll
   are IDENTICAL at every size, because row height is set by the input controls
   inside the cells, not by the text. So this is appearance only — do not "reclaim
   space" by shrinking it further, there is none to reclaim.
   Stays above the 13px reading tables (ledger/invoice/stock/jobs) on purpose: the
   entry is the roomier density, since it is the surface Ali types into for hours. */
.p2-tbl-head table { font-size: 0.967rem !important; }
.p2-tbl-body table { font-size: 0.967rem !important; }
.p2-tbl-foot table { font-size: 0.967rem !important; }
.p2-tbl-head th { font-size: 1.0em !important; padding: 7px 9px !important; }
.p2-tbl-body td { font-size: 1.0em !important; padding: 7px 9px !important; }
.p2-tbl-foot td { font-size: 1.02em !important; padding: 7px 9px !important; }

/* ── HALF-SCREEN ENTRY (<=1200px) ──
   Ali works Purchase half-screen: Sinu's handwritten slip on one half of a 22"
   panel, the entry on the other. That window is 768 CSS px at 100% zoom (1920
   panel at 125% Windows scaling), so at 67% browser zoom innerWidth is 1150 -
   confirmed by Ali, measured, not assumed.
   The entry's hard floor was 1271px, so at 1150 the Total column, the row
   edit/remove buttons and the UPDATE button all fell off the right edge. He was
   stuck at 50% zoom, where it fits but the text is ~7px and invites misreading.

   MEASURED min-content of the panel's three children, which settles it:
     .purch-header  603px
     .purch-body    726px   (part1 477, part2 710 - both fine)
     .purch-part3  1130px   <- THE FLOOR
   It is Part 3, not the header rows (they already shrink to ~1104 with ~230px
   of slack) and not the item table (table-layout:fixed means <col width> is the
   border-box width, so cell padding sits INSIDE it and trimming padding cannot
   shrink the table at all - that was a wrong theory, twice).
   Part 3 is expensive in chrome rather than content: three dividers with 14px
   margins each side = 87px, plus 28px of its own padding and 24px of margin.
   Its columns are flex-shrink:0, so it cannot give any of it back.

   Fix = trim that chrome, and allow wrapping as the safety valve. flex-wrap
   only acts when the row genuinely cannot fit, so nothing moves while it does.

   ALL of this is inside the query on purpose: Ali's full-screen view is 1536
   CSS px, above the breakpoint, so the layout he approved is untouched. The
   !important matches lines 739-741, which would otherwise win. */
@media (max-width: 1200px) {
    /* THE floor: give Part 3 its chrome back, then let it wrap if still tight. */
    .purch-part3 { flex-wrap: wrap; padding-left: 8px; padding-right: 8px; margin-left: 8px; margin-right: 8px; }
    .p3-col-div { margin-left: 6px; margin-right: 6px; }
    /* Gaps and shell padding, not field widths - an input narrower than its own
       value is worse than a tight gap when you are typing off a paper slip. */
    .purch-row1, .purch-row2 { gap: 6px; }
    /* Column-gap must stay 0 (see the base rule) - the -10px/+20px offset below
       is retuned to -6px/+12px to match .p2-input-area's narrower padding on
       this line, so the row still measures its 100% the same way the tables do. */
    .p2-input-row { margin: 0 -6px; width: calc(100% + 12px); }
    .purch-body { padding-left: 8px; padding-right: 8px; }
    .purch-part1 { padding-left: 8px; padding-right: 8px; }
    .p2-input-area { padding-left: 6px; padding-right: 6px; }
    /* Not a width fix (see above) - just buys back reading room inside each
       column now that the panel is narrow. */
    .p2-tbl-head th,
    .p2-tbl-body td,
    .p2-tbl-foot td { padding-left: 4px !important; padding-right: 4px !important; }
}

/* ── PART 3: wider amount field, narrower desc ── */
/* No min-width:0 here (removed) - that forced this column to keep shrinking
   past its own content's floor instead of wrapping to its own row under
   .purch-part3's flex-wrap:wrap, and past a point .p3-label (fixed 70px) and
   .p3-input (flex-shrink:0) don't have anywhere left to give, so the Deduct/Add
   Exp fields started painting on top of the Damaged/Bag Rate column next to
   them. Leaving min-width at its default `auto` lets the browser's normal
   flex automatic-minimum-size (content-based) protect it, so it wraps clean
   below instead of overlapping once the half-screen entry gets this narrow. */
.p3-desc { flex: 1; min-width: 60px; max-width: none; }
.p3-input { width: 100px !important; flex-shrink: 0; }
.p3-total-auto { width: 110px !important; flex-shrink: 0; }

/* ============================================================
   PHASE 2 - FIXED HEADER/FOOTER REPORT TABLES
   ============================================================ */
.report-scroll-wrap {
    border: 1px solid #ddd;
    border-radius: 6px;
    overflow-x: auto;
    overflow-y: auto;
    max-height: 72vh;
    position: relative;
}

.sticky-report {
    border-collapse: collapse;
    table-layout: auto;
    width: 100%;
}

/* Sticky header */
.sticky-report thead th {
    position: sticky;
    top: 0;
    z-index: 10;
    background: #c0392b !important;
    color: white;
    padding: 9px 12px;
    font-size: 0.88em;
    white-space: nowrap;
    border: 1px solid #a93226;
    text-align: left;
}
.sticky-report thead th.text-right { text-align: right; }

/* Body rows */
.sticky-report tbody td {
    padding: 7px 12px;
    border: 1px solid #e8e8e8;
    font-size: 0.9em;
    white-space: nowrap;
}
.sticky-report tbody td.text-right { text-align: right; }
.sticky-report tbody tr:nth-child(even) td { background: #fafafa; }
.sticky-report tbody tr:hover td { background: #fdf2f2 !important; }

/* Sticky footer - must match header/body column structure exactly */
.sticky-report tfoot tr {
    position: sticky;
    bottom: 0;
    z-index: 10;
}
.sticky-report tfoot td {
    background: #c0392b !important;
    color: white !important;
    font-weight: 700;
    padding: 9px 12px;
    border: 1px solid #a93226;
    white-space: nowrap;
    box-shadow: 0 -2px 6px rgba(0,0,0,0.15);
}
.sticky-report tfoot td.text-right { text-align: right; }
.sticky-report tfoot td strong { color: white; }

.sort-arrow {
    cursor: pointer;
    font-size: 0.85em;
    opacity: 0.7;
    margin-left: 4px;
}
.sort-arrow:hover { opacity: 1; }
.sort-arrow.asc::after { content: ' ↑'; }
.sort-arrow.desc::after { content: ' ↓'; }

/* ══════════════════════════════════════
   STOCK REPORT
══════════════════════════════════════ */
.hide { display:none !important; }
/* Layout C: Stock is a table-zone — let it use the full shell width (not the
   1700px form cap) so the wide ledger table can breathe on the 32" monitor. */
.main-container:has(> #stock.active) { max-width:none; }
/* One panel, same recipe as Purchase's .pur-panel - topbar/filter-bar/table
   are flush children (border-bottom dividers, no radius/margin of their
   own), not three separately-rounded boxes stacked with gaps between them. */
.stock-panel { padding:0; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); overflow:hidden; }
.stock-filter-bar { background:var(--head);border-bottom:1px solid var(--line);padding:10px 18px;display:flex;align-items:flex-end;gap:10px;flex-wrap:wrap; }
.stock-filter-group { display:flex; flex-direction:column; gap:3px; }
.stock-filter-group label { font-size:var(--fs-lbl); font-weight:700; color:var(--ink-3); text-transform:uppercase; letter-spacing:0.4px; }
.stock-filter-group input[type=date], .stock-filter-group input[type=text] { height:34px; padding:0 8px; border:1.5px solid var(--line); border-radius:6px; font-size:var(--fs-sm); font-family:inherit; outline:none; }
.stock-filter-group input[type=date]:focus, .stock-filter-group input[type=text]:focus { border-color:var(--accent); }
.stock-filter-group input[type=text] { width:110px; }

/* Chip multi-select */
.stock-chip-wrap { display:flex; flex-wrap:wrap; align-items:center; gap:4px; min-height:32px; border:1.5px solid var(--line); border-radius:6px; padding:3px 6px; background:var(--surface); min-width:140px; max-width:220px; }
.stock-chip-wrap:focus-within { border-color:var(--accent); }
.stock-chip { display:flex; align-items:center; gap:4px; background:var(--accent-tint); color:var(--accent-2); font-size:var(--fs-sm); font-weight:700; padding:2px 8px; border-radius:10px; white-space:nowrap; }
.stock-chip-x { cursor:pointer; opacity:0.6; font-size:var(--fs-sm); }
.stock-chip-x:hover { opacity:1; }
.stock-chip-wrap select { border:none; outline:none; font-size:var(--fs-sm); color:var(--ink-3); background:transparent; cursor:pointer; padding:1px 2px; min-width:80px; }

/* Buttons */
.stock-btn { height:32px; padding:0 14px; border:none; border-radius:var(--radius-sm); font-size:var(--fs-sm); font-weight:700; cursor:pointer; font-family:inherit; display:flex; align-items:center; gap:5px; white-space:nowrap; }
/* .stock-btn-blue/-blue:hover/-blue.active were deleted 2026-07-26 - unreferenced.
   The only blue toolbar button was the Purchase ledger's Expand All, which
   carried its colour inline in index.html; it now uses .stock-btn-acc. */
.stock-btn-acc { background:var(--accent); color:#fff; }
.stock-btn-acc:hover { background:var(--accent-2); }
.stock-btn-acc.active { background:var(--accent-2); }
.stock-btn-grey { background:var(--surface); color:var(--ink-2); border:1.5px solid var(--line); }
.stock-btn-grey:hover { background:var(--brand-tint); color:var(--ink); }
/* Pressed state for the grey toggles - the invoice list's Profit button needs to
   read as ON, the same way the invoice screen's own profit button does. Follows
   .stock-btn-acc.active's existing convention rather than inventing a new one. */
.stock-btn-grey.active { background:var(--accent); color:#fff; border-color:var(--accent); }
.stock-btn-grey.active:hover { background:var(--accent-2); color:#fff; }
/* Icon-only variant. The list's toolbar was already full - a fifth text button
   pushed it onto three rows - and the invoice screen's own profit control is an
   icon button, so this matches it rather than inventing a look. */
.stock-btn.si-ib-prof { padding:0 9px; }
.stock-btn.si-ib-prof .ic { width:17px; height:17px; }
.stock-btn-green { background:var(--pos-deep); color:white; }
.stock-btn-green:hover { filter:brightness(0.92); }
/* Solid red = deletion of a saved record, per the button/action colour spec
   (2026-07-28 sec 2). Added 2026-08-03 for the expense-master pages' Delete —
   the only Masters action that really removes a row. */
.stock-btn-red { background:var(--neg-deep); color:white; }
.stock-btn-red:hover { filter:brightness(0.92); }

/* Column toggle panel */
.stock-col-panel { position:absolute; top:calc(100% + 6px); right:0; background:var(--surface); border:1px solid var(--line); border-radius:8px; padding:10px 14px; min-width:180px; z-index:9999; box-shadow:var(--shadow-pop); display:none; }
.stock-col-panel.show { display:block; }
.stock-col-title { font-size:var(--fs-lbl); font-weight:700; color:var(--ink-3); text-transform:uppercase; letter-spacing:0.5px; margin-bottom:8px; padding-bottom:6px; border-bottom:1px solid var(--line-2); }
.stock-col-toggle { display:flex; align-items:center; gap:8px; padding:5px 0; cursor:pointer; font-size:var(--fs-sm); color:var(--ink); border-bottom:1px solid var(--line-2); }
.stock-col-toggle:last-child { border-bottom:none; }
.stock-col-toggle input[type=checkbox] { width:15px; height:15px; accent-color:var(--accent); cursor:pointer; }
.stock-col-toggle:hover { color:var(--accent-2); }

/* Table wrap */
.stock-table-wrap { background:var(--surface); display:flex; flex-direction:column; overflow:hidden; }
/* 212px = real measured chrome above+below the wrap (filter-bar+pagination+
   .main-container's own padding) at the new 20px main-container padding,
   1528x695. A prior 186px undercounted by ~24px and quietly turned into a
   real, empty page-level scroll - see .pur-view-table-wrap's comment,
   same root cause, tuned the same wrong way (local footer slack, not
   document.body.scrollHeight vs clientHeight). Root-caused Session 81. */
.stock-tbl-scroll { overflow-y:auto; overflow-x:auto; max-height:calc(100vh - 212px - var(--mstrip-h)); }
#stockTable { width:100%; border-collapse:collapse; font-variant-numeric:tabular-nums; }
/* Auto layout, not fixed - unlike the Purchase ledger, Stock isn't actually
   short on width (Ali's call), so every column can size to its own real
   content instead of a hand-picked px guess. That guess is exactly what
   just went wrong: fixed widths sized for typical rows truncated real ones
   (large Weight/Total figures, "10-Jul-26" dates) the moment the sidebar's
   expanded state ate more room. Auto still respects these as preferred
   widths, so short columns (GDN/Emp) stay compact and don't waste space,
   but nothing is hard-capped anymore. */
.sc-date { width:80px; }
.sc-gdn { width:44px; }
.sc-emp { width:44px; }
.sc-source { width:70px; }
.sc-remarks { width:80px; }
.sc-partycode { width:56px; }
.sc-item { width:60px; }
.sc-lot { width:56px; }
.sc-bags { width:50px; }
.sc-rate { width:64px; }
.sc-weight { width:70px; }
.sc-total { width:85px; }
.sc-avgwt { width:60px; }
#stockTable thead th { background:var(--brand); color:var(--brand-ink); padding:9px 12px; font-size:var(--fs-sm); font-weight:600; white-space:nowrap; text-align:left; position:sticky; top:0; z-index:10; }
#stockTable thead th.r { text-align:right; }
#stockTable thead th.sortable { cursor:pointer; user-select:none; }
#stockTable thead th.sortable:hover { background:var(--brand-2); }
#stockTable thead th.st-asc .stock-sort-icon::after { content:' ↑'; }
#stockTable thead th.st-desc .stock-sort-icon::after { content:' ↓'; }
#stockTable thead th:not(.st-asc):not(.st-desc) .stock-sort-icon::after { content:' ↕'; opacity:0.5; }
.stock-sort-icon { font-size:var(--fs-sm); margin-left:3px; }
#stockTable tbody td { padding:6px 9px; font-size:var(--fs-sm); height:var(--row-h); border-bottom:1px solid var(--line-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
#stockTable tbody td.r { text-align:right; }
#stockTable tbody tr:hover td { background:var(--brand-tint) !important; }
#stockTable tbody tr:nth-child(even) td { background:var(--zebra); }
/* BA73 row cursor - same zebra-beating fix as .pur-view-table above. */
#stockTable tbody tr.acc-cur td{ background:#f2d585 !important; color:#241f00; }
#stockTable tbody tr.acc-cur td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }
#stockTable tfoot td { background:var(--head) !important; font-weight:700; font-size:var(--fs-sm); padding:9px 12px; border-top:2px solid var(--line); position:sticky; bottom:0; z-index:10; }
#stockTable tfoot td.r { text-align:right; }
.stock-footer-label { color:var(--ink); }
.sc-lot-num { font-weight:600; color:var(--accent-2); }
.sc-item-name { font-weight:700; color:var(--ink); }
.sc-total-val { font-weight:650; color:var(--ink); }
.sc-avg-ok  { font-weight:700; color:var(--pos-deep); }
.sc-avg-low { font-weight:700; color:var(--low); background:var(--low-tint); padding:2px 6px; border-radius:4px; }
.sc-avg-high { font-weight:700; color:var(--neg-deep); background:var(--neg-tint); padding:2px 6px; border-radius:4px; }

/* ══════════════════════════════════════
   PURCHASE SUB-TABS
══════════════════════════════════════ */
/* Sub-tabs. The active tab is the accent (rust), matching the sidebar's active
   sub-item - these two are the same breadcrumb at two levels, so they should
   agree. The bar sits on the page ground, not white, so the active tab reads as
   lifting off it. */
.pur-subtabs { display:flex; gap:4px; padding:0 16px; border-bottom:1px solid var(--line); margin-bottom:14px; background:var(--bg); }
.pur-subtab { padding:8px 20px; border:none; border-radius:var(--radius-sm) var(--radius-sm) 0 0; font-size:0.9em; font-weight:700; cursor:pointer; font-family:inherit; background:var(--head); color:var(--ink-3); border-bottom:3px solid transparent; transition:background 0.15s, color 0.15s; }
.pur-subtab:hover { background:var(--brand-tint); color:var(--ink); }
.pur-subtab.active { background:var(--surface); color:var(--accent-2); border-bottom:3px solid var(--accent); }

/* ── MASTERS 2-tier nav ──────────────────────────────────────────────────────
   Tier 1 = branch, tier 2 = that branch's leaf. Was 3 tiers (Inventory >
   Purchase/Sales Side > leaves) until Ali saw it live: the middle tier "waste
   the space, and does not look good". Purchase and Sales are top-level now, so
   EVERY branch is exactly 2 rows -- nothing shifts between branches and the
   min-height the 3-tier version reserved is gone.
   Each tier borrows a treatment the app already uses rather than inventing one:
     t1 -> filled forest, same as .jobs-subtab.active
     t2 -> small accent-tint pill */
.mst-tiers { margin-bottom:14px; }
.mst-tiers .pur-subtabs { margin-bottom:0; background:transparent; }

/* MASTERS WAS THE ONLY SCREEN WITH A DIFFERENT SELECTED TAB (removed 2026-08-28).
   Its tier-1 tabs were a filled forest pill while Purchase, Jobs, Sales, Accounts
   and Alerts all showed the standard white tab with an accent underline - measured
   on all six. Ali: "for some it show with green forest as tab selected and for
   some it show white as selected, make sure we have similar selection of tabs."
   The comment above still cites `.jobs-subtab.active` as the forest precedent, but
   that is stale: Jobs uses .pur-subtab like everything else now, so the forest fill
   had no sibling left. Masters simply inherits .pur-subtab.active. */
/* The way back to the landing sits first in the tier-1 row and is deliberately
   quieter than a branch: it is a destination, not one of the four sets. */
.mst-home-tab { font-weight:600; }

/* ── MASTERS LANDING (AA47 #5) ─────────────────────────────────────────────
   Four cards on one row at the locked 1536px base viewport, wrapping to two
   below that. auto-fit rather than a fixed 4 so a fifth branch would land
   without a CSS edit. */
.mst-home { padding:4px 2px 20px; }
.mst-home-h { font-size:1.35em; font-weight:700; color:var(--ink); margin:0 0 4px; }
.mst-home-sub { color:var(--ink-3); font-size:0.9em; margin:0 0 18px; }
/* align-items:start so a card is only as tall as it needs to be. Stretched to
   a common height, the two-leaf cards left a dead band between the blurb and
   the buttons — measured on screen, and it read as something missing. */
.mst-home-grid {
    display:grid; grid-template-columns:repeat(auto-fit, minmax(250px, 1fr)); gap:14px;
    align-items:start; }
.mst-home-card {
    background:var(--surface); border-radius:var(--radius); padding:16px 16px 14px;
    box-shadow:0 2px 10px rgba(0,0,0,0.08); border-top:4px solid var(--brand);
    display:flex; flex-direction:column; }
.mst-home-card h3 { margin:0 0 4px; font-size:1.02em; font-weight:700; color:var(--ink); }
.mst-home-blurb { margin:0 0 12px; font-size:0.83em; color:var(--ink-3); line-height:1.45; }
.mst-home-leaves { display:flex; flex-direction:column; gap:6px; }
.mst-home-leaf {
    text-align:left; padding:9px 12px; border:1.5px solid var(--line); border-radius:var(--radius-sm);
    background:var(--head); color:var(--ink); font-family:inherit; font-size:0.9em; font-weight:600;
    cursor:pointer; transition:background 0.15s, border-color 0.15s, color 0.15s; }
.mst-home-leaf:hover { background:var(--brand-tint); border-color:var(--brand); color:var(--brand); }
/* A visible focus ring is not decoration here — it is the only thing that says
   where the keyboard is on a grid of otherwise identical buttons. */
/* THE KEYBOARD CURSOR IS ONE COLOUR EVERYWHERE (2026-08-28). These cards drew
   their own forest ring, so walking onto one looked nothing like walking onto a
   tab, a toolbar button or a row - Ali: "still there focus is green and not out
   standard color". They take the app's accent outline now, the same
   var(--accent) the Settings picker drives; the tint stays as the resting
   hover. */
.mst-home-leaf:focus-visible,
.mst-home-leaf:focus { outline:2px solid var(--accent); outline-offset:2px;
    border-color:var(--accent); background:var(--surface); color:var(--ink); box-shadow:none; }

.mst-t2 { border-bottom:none; padding-top:6px; padding-bottom:2px; }
.mst-t2 .pur-subtab { font-size:0.84em; padding:5px 14px; border-radius:var(--radius-pill); border-bottom:none; }
.mst-t2 .pur-subtab.active { background:var(--accent-tint); color:var(--accent-2); border-bottom:none; }

/* ── MASTERS RECORD PAGE — DOCKED, shared by every master that gets one ───────
   Same technique as #inv-screen / #pfScreen: a .main-container sibling, not a
   fixed-position overlay. 20px padding (Ali: "keeping 20px on all side"), so
   the height budget subtracts 40px = 20 top + 20 bottom.
   Selectors are on .mst-page, NOT an id, so each new master's page inherits the
   whole thing - Adthidars first, Customers second. */
.main-container:has(> .mst-page.show) { padding: 20px; }
.main-container:has(> .mst-page.show) > .main-section { display: none !important; }
.mst-page { display:none; width:100%; height:calc(100vh - 40px); background:var(--surface);
    border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow);
    flex-direction:column; overflow:hidden; }
.mst-page.show { display:flex; }
/* Maximise borrows the viewport. width/height:auto so the inset (not the
   docked height calc above) sizes the box — the .si6 .shell bug class. The
   box-shadow is a backdrop with no extra DOM: position:fixed + a non-zero
   inset would otherwise leave the sidebar visible in the gap. */
.mst-page.maxi { position:fixed; inset:20px; z-index:1200; width:auto; height:auto;
    box-shadow:var(--shadow-pop), 0 0 0 100vmax var(--bg); }

/* SETTINGS IS A TABBED SCREEN, NOT A RECORD PAGE (2026-08-28, Ali). It was the
   only tabbed page in the app wearing a title bar, and the title said
   "Settings" - which the lit sidebar pill already says: "setting is only page
   which uses name setting at top and does not use the tabs directly ... none of
   page uses closed page format, no one has X on there corner ... we also dont
   need maximise and close also".
   So it drops .mst-page's card chrome and stands up the way Purchase and Day
   Book do: the tab band on the GROUND, then the panel. Measured off Purchase -
   band 36.23 high, margin-bottom 14 (already on .pur-subtabs), panel from
   y 70.23. The six Masters RECORD pages keep their headers: there the title
   names the record you are editing.
   There is no X: Escape closes, and the sidebar navigates away. */
.mst-page.set-page { background:none; border:0; border-radius:0; box-shadow:none; overflow:visible; }
/* The Company form is the one Settings panel that is a real TWO-COLUMN form.
   It must restate `display:grid`, not just the width: `.set-tabpanel .mstp-grid`
   (further down this file) sets display:flex/column so every other Settings tab
   stacks its groups, and two classes beat .mstp-grid's one. Setting only
   max-width here left the boxes in a single 370px column with the right half of
   the panel empty - caught by SCREENSHOTTING it, not by reading the source.
   The sixth time a longer selector has quietly won in this file.
   1250, not .mstp-grid's 900: the cap the Adthidar and Customer forms use, so
   two columns of ~620 fill the panel (Ali: "can we utilise the space"). */
#setTabCompany .mstp-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
    gap:11px; align-items:start; max-width:1250px; }
@media (max-width:980px){ #setTabCompany .mstp-grid { grid-template-columns:1fr; } }
/* Hint BESIDE the box, not under it - Ali: "just mention max 5 character beside
   its feild so we dont use another row". The input is sized to its content: a
   five-character box that stretched 460px would read as a long-name field. */
.coy-short { display:flex; align-items:center; gap:10px; min-width:0; }
.coy-short input { width:96px; flex:0 0 96px; text-transform:uppercase;
    letter-spacing:.07em; font-weight:700; }
.coy-hint { font-size:0.833rem; color:var(--ink-3); white-space:nowrap; }
.set-panel { flex:1; min-height:0; display:flex; flex-direction:column;
    background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
    box-shadow:var(--shadow); overflow:hidden; }

.mstp-hdr { display:flex; align-items:center; gap:14px; padding:11px 16px;
    border-bottom:1px solid var(--line); background:var(--head); flex-shrink:0; }
.mstp-title { display:flex; align-items:center; gap:10px; font-size:1.13em; font-weight:600; color:var(--ink); }
.mstp-hdr .wctl { margin-left:auto; }

.mstp-body { flex:1; min-height:0; overflow-y:auto; padding:14px 16px; }
/* Two balanced columns, not auto-fit: four groups in one row left the fields
   cramped and the page mostly empty. Capped because this is a FORM zone, which
   the layout spec says never stretches to fill a 32" monitor (2026-07-24 §2.1). */
.mstp-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:11px;
    align-items:start; max-width:900px; }
@media (max-width:820px){ .mstp-grid { grid-template-columns:1fr; } }
/* Balanced column FLOW instead of a row grid, for forms whose sections differ
   a lot in height. A grid row is as tall as its tallest cell, so pairing a
   5-field Bank block against a 1-field Terms block wasted a whole row's worth
   of empty space and pushed the Adthidar form ~60px past the window. CSS columns
   have no row concept: sections just flow and balance, which measured 707px ->
   ~600px with no change to font size or field width.
   Scoped with its own modifier so Settings and the two expense pages - which
   also use .mstp-grid - keep the row grid they were laid out against.

   Width: these forms inherited .mstp-grid's 900px cap while the Customer page
   ran to 1250px, so two of the three master forms left a wide empty gutter on
   the right (Ali: "new adthidar and new ledger does not use full width, new
   customer uses the full width"). All three share the 1250px cap now, and the
   extra width buys a THIRD column rather than wider fields - which is what
   actually removes the scroll. */
.mstp-grid.mstp-flow { display:block; column-count:3; column-gap:11px; max-width:1250px; }
/* DESCENDANT, not child. The Ledger form wraps its route-specific section in
   #mstCoaRouteExtra so Under can repaint just that part, and a `>` selector
   skipped straight past it - so Opening rendered flush against the section
   below it with no gap at all. Selectors match the DOM tree, so display:contents
   on the wrapper does not rescue a child combinator either; the wrapper needs
   BOTH (it is out of flow for layout, still present for matching). */
.mstp-grid.mstp-flow .mstp-grp { break-inside:avoid; margin-bottom:11px; }
@media (max-width:1250px){ .mstp-grid.mstp-flow { column-count:2; } }
@media (max-width:820px){ .mstp-grid.mstp-flow { column-count:1; } }
/* Customers: all the detail groups stack in a LEFT column, the charge profile
   owns the RIGHT one (Ali: "move all info on left side and charges on right
   side"). Charges are a tall list, so a column suits them better than a
   full-width strip underneath. Wider cap than the plain form zone because the
   right column carries a table. */
.mstp-grid.mstp-2col { grid-template-columns:minmax(0,1fr) minmax(0,1.15fr); max-width:1250px; }
.mstp-col { display:flex; flex-direction:column; gap:11px; min-width:0; }
/* A NEW customer has no charges, so there is no right column and the same
   wrapper must vanish from the layout - display:contents promotes its children
   back to being grid items, giving the plain 2x2 form. */
.mstp-passthru { display:contents; }
/* The charges group tracks the left column's height and scrolls past it, so a
   customer with many charges can never push the page taller than the left side. */
.mstp-grp.mstp-charges { display:flex; flex-direction:column; max-height:100%; }
.mstp-grp.mstp-charges > div { overflow-y:auto; }
@media (max-width:980px){ .mstp-grid.mstp-2col { grid-template-columns:1fr; } }

.mstp-grp { border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface); overflow:hidden; }
.mstp-grp h4 { margin:0; padding:8px 14px; font-size:0.733rem; text-transform:uppercase; letter-spacing:.07em;
    color:var(--ink-3); font-weight:700; background:var(--head); border-bottom:1px solid var(--line-2); }
.mstp-fields { padding:11px 14px; display:flex; flex-direction:column; gap:8px; }
.mstp-f { display:grid; grid-template-columns:120px 1fr; gap:10px; align-items:center; }
.mstp-f label { font-size:0.867rem; color:var(--ink-3); }
.mstp-f input, .mstp-f select, .mstp-f textarea { font:inherit; font-size:0.933rem; width:100%;
    padding:6px 10px; border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); color:var(--ink); }
.mstp-f input:focus, .mstp-f select:focus, .mstp-f textarea:focus {
    outline:2px solid var(--accent); outline-offset:-1px; border-color:var(--accent); }
.mstp-f textarea { resize:vertical; min-height:44px; }
.mstp-f .req { color:var(--neg); }

/* BZ99 - the line under the Books pair. It states the rule while the window is
   valid, and CARRIES THE REFUSAL when a bound would orphan existing entries:
   "18 entries (13 purchases, 1 job, ...) are dated before 10-Aug-2026" is too
   long and too consequential for a toast that dismisses itself. Indented to the
   input column so it reads as a note on the fields, not a heading over them. */
.mstp-note { grid-column:1 / -1; margin-left:130px; font-size:0.8rem;
    color:var(--ink-3); line-height:1.45; }
.mstp-note.is-bad { color:var(--neg); font-weight:600; }

/* BZ99 — a date outside the books window, marked the moment it lands rather
   than at Save. Set by booksMarkField() in js_core.js, never by :invalid: a
   `required` date that is merely EMPTY is :invalid too, and colouring an
   untouched field red on arrival would be noise, not a warning.
   !important because these fields live in thirteen different form systems
   (.pf, .hin, .ji-input, .of-input, .ef-input, .td-input-edit, .form-group)
   and several set their own border in a longer selector — the trap this repo
   has now hit six times. */
input[type="date"].books-out {
    border-color:var(--neg) !important;
    color:var(--neg) !important;
    background:color-mix(in srgb, var(--neg) 5%, var(--surface)) !important;
}
/* The focused state has to be restated: every one of these form systems sets
   border-color:var(--accent) on :focus in a longer selector, so without this the
   field turns rust the moment you are IN it — which is exactly when you are
   reading it. Measured: rgb(158,79,46) instead of rgb(192,67,60). */
input[type="date"].books-out:focus {
    border-color:var(--neg) !important;
    outline-color:var(--neg) !important;
}

/* Several fields on ONE row, labels stacked above the inputs. A side-by-side
   label would eat 120px per field and leave nothing for the input once three
   share a line; stacking the label costs ~20px of height once but buys the
   full cell width for every input in the row. Used by the full-width Bank
   block, which goes from five stacked lines to two. */
.mstp-frow { display:grid; gap:10px; }
.mstp-frow .mstp-f { grid-template-columns:1fr; gap:3px; align-items:stretch; }
.mstp-frow .mstp-f label { font-size:0.78rem; }

/* A section that spans every column of .mstp-grid, sitting under the columns
   rather than inside one of them. */
.mstp-span { grid-column:1 / -1; }

/* Jobs % (S152), round 3 (Ali's review of round 2 — one card per set had a
   scrollbar on the left, a wall of white on the right). A TABLE instead: one
   row per set (Palla / DC-Stemless / DC-Final / PDR), one column per zone,
   using the app's own .tbl standard (sand header, zebra) so it reads like
   every other data table here, not a bespoke settings widget. Each cell still
   shows "from – to": "from" is either fixed (0, Low) or an ECHO of the
   previous cell's typed value (read-only — it just follows), "to" is the one
   real input; Very High has no "to", an open top band reads as "above N". */
.jp-legend { display:flex; align-items:center; gap:16px; padding:9px 14px; margin-bottom:12px;
    background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm); flex-wrap:wrap; }
.jp-legend-item { display:flex; align-items:center; gap:6px; font-size:0.85rem; color:var(--ink-3); }
.jp-swatch { width:13px; height:13px; border-radius:3px; border:1px solid var(--line); background:var(--surface); }
.jp-swatch.zone-low { background:var(--low-tint); border-color:var(--low); }
.jp-swatch.zone-elevated { background:var(--caution-tint); border-color:var(--caution-edge); }
.jp-swatch.zone-check { background:var(--neg-tint); border-color:var(--neg-deep); }
.jp-legend-note { font-size:0.8rem; color:var(--ink-3); opacity:0.8; margin-left:auto; }

/* S153 — widened for the new Buffer + Hint columns; Hint is the one column
   left width:auto so it absorbs whatever room the fixed-width columns
   don't use, rather than every column splitting the leftover evenly. No
   max-width cap (Ali: "we have empty space after table on right side") —
   fills whatever the settings panel actually gives it instead of guessing
   a number that falls short at the app's own locked viewport. */
.jp-table { width:100%; table-layout:fixed; }
.jp-table th:first-child, .jp-table td:first-child { width:150px; }
.jp-table th:nth-child(6), .jp-table td:nth-child(6) { width:80px; }
.jp-table th:last-child, .jp-table td:last-child { width:auto; }
.jp-table thead th { text-align:center; }
/* display:flex goes on THIS inner wrapper, never the <td> itself — a <td>
   with display:flex stops being display:table-cell, which pulls it out of
   the row's column layout entirely. Measured: all four zone cells collapsed
   onto one column, stacked as four short rows, because the browser no
   longer counted them as cells in the same row once they weren't
   table-cells any more. */
.jp-cell-inner { display:flex; align-items:center; justify-content:center; gap:5px; }
.jp-seg-from { font-size:0.867rem; flex:0 0 auto; min-width:1.4em; text-align:right; }
.jp-seg-dash { opacity:0.6; flex:0 0 auto; }
.jp-seg-above { font-size:0.78rem; opacity:0.85; flex:0 0 auto; }
.jp-seg-input { font:inherit; font-size:0.9rem; width:64px; padding:4px 6px;
    border:1px solid var(--line); border-radius:6px; background:var(--surface); text-align:right; }
.jp-seg-input:focus { outline:2px solid var(--accent); outline-offset:-1px; border-color:var(--accent); }
/* Each cell tinted like its own zone (Normal stays plain, same rule as
   everywhere else this colour system appears — nothing needing no attention
   should visually compete with what does). Overrides .tbl's own zebra stripe.
   Ali: "let number also show there true nature by showing its original font
   colors" — the numbers themselves (from/dash/above text AND the typed
   value) carry the zone's own deep colour too, not just a tinted background
   with plain ink text. */
.jp-table td.jp-seg-low      { background:var(--low-tint) !important; }
.jp-table td.jp-seg-elevated { background:var(--caution-tint) !important; }
.jp-table td.jp-seg-veryhigh { background:var(--neg-tint) !important; }
.jp-seg-low      .jp-seg-from, .jp-seg-low      .jp-seg-input { color:var(--low); }
.jp-seg-elevated .jp-seg-from, .jp-seg-elevated .jp-seg-input { color:var(--caution-edge); }
.jp-seg-veryhigh .jp-seg-from, .jp-seg-veryhigh .jp-seg-above { color:var(--neg-deep); }
.jp-seg-low      .jp-seg-input { border-color:var(--low); }
.jp-seg-elevated .jp-seg-input { border-color:var(--caution-edge); }
/* Normal cells and the Buffer column carry no zone tint on purpose, but that
   left their inputs' border (--line, a very pale tan) sitting on an
   equally pale background — Ali: "box has no border line where white
   matches." A visibly darker border here, not a colour change to the cell
   itself. */
.jp-seg-normal .jp-seg-input { border-color:var(--ink-3); }
/* DC — Output's Low cell reuses .jp-seg-above for "below N%" (S153) instead
   of the "0 – N" every other row's Low cell uses, since its Low boundary is
   negative — "0" would be a lie. Needs its own colour rule since the base
   .jp-seg-low rule above only reaches .jp-seg-from/.jp-seg-input, not
   .jp-seg-above (that pairing only existed on the Very High cell before). */
.jp-seg-low .jp-seg-above { color:var(--low); }

/* S153 — Buffer column: plain, no zone tint (it is not itself a zone).
   Hint column: small muted text, same voice as .jp-legend-note above the
   table, wrapping normally since it is the one column given room to grow. */
.jp-seg-buffer { text-align:center; }
.jp-buffer-input { font:inherit; font-size:0.9rem; width:56px; padding:4px 6px;
    border:1px solid var(--ink-3); border-radius:6px; background:var(--surface); text-align:right; }
.jp-buffer-input:focus { outline:2px solid var(--accent); outline-offset:-1px; border-color:var(--accent); }
.jp-hint-cell { padding-left:10px; }
.jp-hint-text { font-size:0.8rem; color:var(--ink-3); line-height:1.3; display:block; }

/* Jobs % zone colours (S152) — applied to a shortage figure wherever one is
   shown (Jobs list SHORTAGE column, Job Detail's DC Charges / Final Costing
   boxes). Reuses the app's existing 4-signal palette rather than inventing
   new colours: --low was already documented as "below-range flag", --caution
   as "careful, look at this — an unusual weight", --pos-deep/--neg-deep are
   the app's standing good/bad. Normal gets no colour at all — a figure that
   needs no attention should not visually compete with ones that do. */
/* Jobs % zone colours applied two ways: directly on a bare span (Jobs list),
   or on a wrapper around .acc-stat-val children (Job Detail's pair boxes).
   The second form needs its OWN rule reaching the children — .acc-stat-val
   sets its own color:var(--ink) (line ~1824), and an element's own rule
   always wins over an inherited one from its parent, no matter how specific
   the parent's selector is. Measured: without the descendant half, the pill
   got its tint background but the number itself stayed --ink, not the zone
   colour — looked like dark text on a tint, not the colour itself flagging it. */
/* .acc-stat-val.zone-* combined-class selectors added S153: Palla/PDR/Grinding
   apply the zone class directly on the .acc-stat-val span itself (not a
   wrapper), which ties with the base ".acc-stat-val{color:var(--ink)}" rule
   below on specificity — source order then decided it, and .acc-stat-val
   came later, so the zone colour silently lost every time on those three job
   types (DC's Stemless/Final boxes use a wrapper span so they were never
   affected). The combined-class selector outscopes the base rule regardless
   of order. */
.zone-low, .zone-low .acc-stat-val, .acc-stat-val.zone-low           { color:var(--low); }
.zone-elevated, .zone-elevated .acc-stat-val, .acc-stat-val.zone-elevated { color:var(--caution-edge); }
.zone-check, .zone-check .acc-stat-val, .acc-stat-val.zone-check       { color:var(--neg-deep); }
.zone-low      { background:var(--low-tint);      font-weight:700; border-radius:4px; padding:1px 5px; }
.zone-elevated { background:var(--caution-tint);  font-weight:700; border-radius:4px; padding:1px 5px; }
.zone-check    { background:var(--neg-tint);      font-weight:700; border-radius:4px; padding:1px 5px; }

/* The Dr/Cr pair inside a field row. .acc-mside came from the voucher modals,
   where it owns a full-width line; dropped into a 120px+1fr field cell it wrapped
   each label onto three ragged lines. Keep the pair on one line and let the row
   scroll rather than reflow. */
.mstp-f .acc-mside { flex-wrap:nowrap; gap:14px; white-space:nowrap; }
.mstp-f .acc-mside label { font-weight:500; font-size:0.86rem; }

/* A field that does not apply to the current route (today: GST on an adthidar,
   who is a farmer rep and never GST-registered). Locked in place rather than
   hidden — hiding changed the form's height as the route flipped, which is very
   visible on a docked page. Reads clearly as "not applicable here", not as an
   empty field someone forgot to fill. */
.mstp-f-locked label { opacity:0.5; }
.mstp-f-locked input, .mstp-f-locked select {
    background:var(--head); color:var(--ink-3); cursor:not-allowed; opacity:0.7; }

/* Checkboxes — first introduced by the two expense pages. The rule above sets
   width:100% on every input in a field row, which would stretch a checkbox into
   a bar, so they opt out explicitly. */
.mstp-f input[type=checkbox] { width:15px; height:15px; flex-shrink:0; padding:0; accent-color:var(--accent); }
.mstp-cbs { display:flex; flex-wrap:wrap; gap:8px 18px; align-items:center; }
.mstp-cb { display:flex; align-items:center; gap:6px; font-size:0.9rem; color:var(--ink-2); cursor:pointer; }
.mstp-cb input[type=checkbox] { accent-color:var(--accent); }
/* Grid rows centre their content; a wrapped hint under a control reads better
   top-aligned against its label. */
.mstp-f.mstp-f-top { align-items:start; }
.mstp-f.mstp-f-top > label { padding-top:6px; }
.mstp-hint { display:block; font-size:0.8rem; color:var(--ink-3); margin-top:4px; line-height:1.4; }

/* Configuration tier (permanent / default / manual) is a gradient, not a
   good-bad axis, so it takes the neutral head fill rather than one of the four
   meaning-carrying pill colours. */
.mst-pill { display:inline-block; padding:2px 9px; border-radius:var(--radius-pill);
    background:var(--head); color:var(--ink-2); font-size:0.78rem; font-weight:700;
    border:1px solid var(--line-2); }

/* Action bar — sibling of .mstp-body, never a child, so Save can't scroll away. */
.mstp-foot { display:flex; align-items:center; gap:9px; padding:11px 16px;
    border-top:1px solid var(--line); background:var(--head); flex-shrink:0; }
.mstp-dirty { display:none; font-size:0.833rem; font-weight:600; color:var(--warn-deep); }
.mst-page.is-dirty .mstp-dirty { display:inline; }

/* Charges table inside the Customers page */
.mstp-chg { width:100%; border-collapse:collapse; font-size:0.9rem; }
.mstp-chg th { background:var(--head); color:var(--ink-3); font-size:0.733rem; text-transform:uppercase;
    letter-spacing:.05em; font-weight:700; text-align:left; padding:6px 10px; border-bottom:1px solid var(--line); }
.mstp-chg td { padding:5px 10px; border-bottom:1px solid var(--line-2); }
.mstp-chg tr:last-child td { border-bottom:none; }
.mstp-chg input { font:inherit; font-size:0.9rem; width:110px; padding:4px 8px;
    border:1px solid var(--line); border-radius:6px; background:var(--surface); color:var(--ink); text-align:right; }
.mstp-chg input:focus { outline:2px solid var(--accent); outline-offset:-1px; border-color:var(--accent); }
.mstp-chg .perm { font-size:0.78rem; color:var(--ink-3); }
.mstp-chg-add { display:flex; gap:8px; align-items:center; padding:10px 14px; border-top:1px solid var(--line-2); background:var(--zebra); }
.mstp-chg-add select { flex:1; min-width:0; font:inherit; font-size:0.9rem; padding:6px 10px;
    border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface); color:var(--ink); }

/* ── The five SMALL master lists ─────────────────────────────────────────────
   Godowns (2 rows), Varieties (17), Employees (5), Transporters (2), Bag
   Charges (rate history). Far too few rows to justify a pager, so they get the
   .pur-panel frame and a plain inline search box instead of the full toolbar
   Adthidars and Customers carry. */
.mst-inline-search { min-width:260px; }
.mst-inline-search input { font:inherit; font-size:0.9rem; width:100%; padding:6px 11px;
    border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); color:var(--ink); }
.mst-inline-search input:focus { outline:2px solid var(--accent); outline-offset:-1px; border-color:var(--accent); }
/* THIS RULE NEVER FIRED. It is one class, and so is .pur-view-table-wrap, which
   these divs also carry — equal specificity, and the generic rule is ~1100 lines
   further down the file, so it won. Every small list has been capped at the
   generic 260px all along: measured 435px tall at 1528x695 with 136px of empty
   page below it, and Varieties (17 rows) scrolling for the last 227px of a table
   that had room to show all of it. Ali, 2026-08-14: "varieties show scroll bar
   and it has lots of space left at bottom, may be 100px".

   Two fixes, not one: the selector now outranks the generic rule, AND the number
   is re-measured. These lists have no filter bar, so their wrap starts at 123
   instead of 195 — N = 123 + 1 + 0 + 20 = 144. The old 179 was 251 minus a 72px
   filter bar, and 251 was itself 35px stale (see #partiesTable below). */
.pur-view-table-wrap.mst-small-wrap { max-height:calc(100vh - 144px - var(--mstrip-h)); }
#godownsTable tbody tr[data-row], #employeesTable tbody tr[data-row],
#varietiesTable tbody tr[data-row], #transportersTable tbody tr[data-row],
#bagChargesTable tbody tr[data-row] { cursor:pointer; }
.mst-past { font-size:0.82rem; color:var(--ink-3); }

/* Bag Charges: the CURRENT rate is the answer, so it sits in the toolbar
   rather than in a card above the history table. */
.mst-current-rate { display:flex; align-items:baseline; gap:8px; margin-left:16px; }
.mst-current-rate .lbl { font-size:0.733rem; text-transform:uppercase; letter-spacing:.05em;
    color:rgba(255,255,255,.75); font-weight:600; }
.mst-current-rate .val { font-size:1.2rem; font-weight:700; color:#fff; }
.mst-current-rate .per,
.mst-current-rate .from { font-size:0.82rem; color:rgba(255,255,255,.75); }

/* ── MASTERS POPUPS ─────────────────────────────────────────────────────────
   The five small masters keep .modal rather than becoming docked pages — that
   is what keeps every quick-add "+" in Purchase entry, Jobs and Sales invoice
   working (spec 2026-08-03 §4.2/§4.3). They just needed to stop looking
   pre-makeover.

   SCOPED to .mst-modal, which tags only the 10 Masters-owned popups. The base
   .modal-* styles are shared by all 17 popups in the app, including Settings,
   Import, Purchase Return, New Job, Close Job and Factory Reset — Ali's call
   was "go for master now", so those seven keep the old red header until their
   own screens come up. Restyling the shared rule instead would have been one
   edit for all 17; this is deliberately the narrower change.

   The look is a small version of the .mst-page record pages: forest header,
   grouped field box, sentence-case labels, Save right in a footer. */
.mst-modal .modal-content { background:var(--surface); border:1px solid var(--line);
    border-radius:var(--radius); box-shadow:var(--shadow-pop); }
.mst-modal .modal-header { background:var(--head); color:var(--ink);
    border-bottom:1px solid var(--line); padding:13px 18px;
    border-radius:var(--radius) var(--radius) 0 0; }
.mst-modal .modal-header h2 { color:var(--ink); font-size:1.13em; font-weight:600; margin:0; }
.mst-modal .modal-header .modal-title-icon { display:none; }   /* emoji marks */
.mst-modal .close { color:var(--ink-3); font-size:1.35em; font-weight:600; }
.mst-modal .close:hover { color:var(--ink); }
.mst-modal .modal-body { padding:16px 18px; background:var(--surface); }
.mst-modal .modal-footer { padding:12px 18px; border-top:1px solid var(--line);
    background:var(--head); border-radius:0 0 var(--radius) var(--radius); gap:9px; }

/* Fields — same vocabulary as .mstp-f on the record pages, at dialog size. */
.mst-modal .form-group { margin-bottom:11px; }
.mst-modal .form-group:last-child { margin-bottom:0; }
.mst-modal label { display:block; font-size:0.867rem; color:var(--ink-3);
    font-weight:500; margin-bottom:4px; }
.mst-modal .form-control,
.mst-modal input[type="text"], .mst-modal input[type="number"],
.mst-modal input[type="date"], .mst-modal select, .mst-modal textarea {
    font:inherit; font-size:0.933rem; width:100%; padding:7px 10px;
    border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); color:var(--ink); }
.mst-modal .form-control:focus,
.mst-modal input:focus, .mst-modal select:focus, .mst-modal textarea:focus {
    outline:2px solid var(--accent); outline-offset:-1px; border-color:var(--accent); }
.mst-modal textarea { resize:vertical; min-height:44px; }
.mst-modal .required, .mst-modal .req { color:var(--neg); }

/* Buttons — reuse the app's own action colours instead of the legacy greys. */
.mst-modal .modal-footer .btn,
.mst-modal .modal-footer button { font:inherit; font-size:0.867rem; font-weight:600;
    cursor:pointer; border-radius:var(--radius-sm); padding:7px 15px;
    border:1.5px solid transparent; }
.mst-modal .modal-footer .btn-primary { background:var(--pos-deep); color:#fff; border-color:var(--pos-deep); }
.mst-modal .modal-footer .btn-primary:hover { filter:brightness(0.93); }
.mst-modal .modal-footer .btn-secondary { background:var(--surface); color:var(--ink-2); border-color:var(--line); }
.mst-modal .modal-footer .btn-secondary:hover { background:var(--brand-tint); color:var(--ink); }

/* Import's file-drop panel. Was inline #f8f9fa / dashed #ddd - the two greys
   the makeover replaced everywhere else. */
.imp-drop { background:var(--head); border:2px dashed var(--line); border-radius:var(--radius-sm);
    padding:20px; text-align:center; margin-bottom:15px; }

/* Import's two tabs. `.sub-tab` is SHARED with the Reports screen (7 more
   buttons, index.html:2095-2101), which is deliberately out of the makeover -
   so the base rule with its pre-makeover red gradient must stay put. Scoping to
   .mst-modal converts Import's pair and leaves Reports exactly as it was. */
.mst-modal .sub-tab { background:var(--surface); border:1px solid var(--line);
    padding:6px 16px; border-radius:var(--radius-sm); font-size:0.867rem;
    font-weight:600; color:var(--ink-2); }
.mst-modal .sub-tab:hover { border-color:var(--accent); color:var(--accent-2); }
.mst-modal .sub-tab.active { background:var(--brand); border-color:var(--brand); color:var(--brand-ink); }

/* ── SETTINGS PAGE ────────────────────────────────────────────────────────
   Reuses .mst-page/.mstp-* wholesale (see the markup comment in index.html
   for why) — this block only adds what's genuinely new: tab-panel show/hide,
   and the two Data Reset danger cards. */
.set-tabpanel .mstp-grid { display:flex; flex-direction:column; gap:11px; max-width:820px; }

/* Appearance tab: show the real thing, not just its name — text-size buttons
   are set to their actual px (foundation.css html[data-text-size]). Each
   accent button is filled with its own real color (foundation.css
   html[data-accent]) rather than a neutral button with a swatch dot: --accent
   is what colors the active sidebar item, focus rings and active tabs
   app-wide, so this IS what picking it actually looks like, not a preview
   of it. The picked one gets a ring instead of turning the app's generic
   "selected" green, which would misrepresent a color that isn't itself. */
.acc-opt { font:inherit; font-size:0.867rem; font-weight:600; color:#fff;
    border:1.5px solid transparent; border-radius:var(--radius-sm);
    padding:7px 14px; cursor:pointer; }
/* Selected-state ring, shared by the accent/ground swatches and the text-size
   buttons (Ali, 2026-08-27 — the text-size buttons had only a fill before, no
   ring, so the "which is selected" signal read differently from the colours). */
.acc-opt.acc-active,
#appTextSize .btn.acc-active { box-shadow:0 0 0 2px var(--surface), 0 0 0 4px var(--ink); }

.set-danger-card { border-radius:var(--radius); padding:14px 16px; background:var(--neg-tint); }
.set-danger-clear   { border:1.5px solid var(--neg); }
.set-danger-factory  { border:1.5px solid var(--neg-deep); }
.set-danger-title { font-weight:600; margin-bottom:4px; }
.set-danger-clear .set-danger-title   { color:var(--neg); }
.set-danger-factory .set-danger-title  { color:var(--neg-deep); }
.set-danger-desc { font-size:0.85em; color:var(--ink-3); margin-bottom:12px; }
.btn-danger-deep { background:var(--neg-deep); border-color:var(--neg-deep); color:#fff; }
.btn-danger-deep:hover { filter:brightness(0.93); }

/* Row-level affordance on the list tables: the whole row opens the record. */
#partiesTable tbody tr, #customersTable tbody tr,
#expenseMasterInlineTable tbody tr, #custExpenseMasterInlineTable tbody tr { cursor:pointer; }
.mstp-inactive td { opacity:0.62; }
/* The ledger is ONE card, not four stacked full-bleed strips. The toolbar,
   filter bar, table and pagination keep their own square edges; the rounded
   corners come from this wrapper clipping them, so nothing inside needs
   per-corner radii that would fight each other as rows are added or the
   pagination hides. overflow:hidden is safe here - the panel has no
   absolutely-positioned children (the filter bar uses native selects, which
   render outside the flow). */
.pur-panel { padding:0; background:var(--surface); border:1px solid var(--line);
    border-radius:var(--radius); overflow:hidden; }
/* ══════════════════════════════════════════════════════
   PHASE 9 — JOBS MODULE CSS
   Append to end of style.css
   ══════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════
   PHASE 9 — JOBS MODULE CSS (copied from approved demos)
   ══════════════════════════════════════════════════════ */

/* JOB STATUS BADGES */
.job-badge { display:inline-block;padding:3px 10px;border-radius:12px;font-size:0.78em;font-weight:700;letter-spacing:0.5px; }
.job-badge-open     { background:var(--pos-tint);color:var(--pos-deep); }
.job-badge-closed   { background:var(--neg-tint);color:var(--neg-deep); }
.job-badge-reopened { background:var(--warn-tint);color:var(--attn); }
.job-badge-cancelled { background:var(--neg);color:#fff; }

/* JOBS LIST subtabs */
.jobs-subtab { padding:7px 20px;border:2px solid var(--line);border-radius:20px;font-size:0.9em;font-weight:700;cursor:pointer;background:var(--surface);color:var(--ink-2);font-family:inherit; }
.jobs-subtab.active { background:var(--brand);color:var(--brand-ink);border-color:var(--brand); }

/* ── JOB DETAIL VIEW (inline, not modal) ── */
/* Ali's call (Session 80 continued): plain block flow, no forced min-height/
   justify-content - the save bar is just the last box in the sequence now,
   normal minor spacing throughout. A short job leaving blank space below
   everything is accepted, not chased. */
#jobDetailView { display:none; }
#jobsListView  { display:block; }

/* Jobs list table - same shape as Sales' own .si-view-table: forest header,
   table-layout:auto inside .pur-view-table-wrap's own overflow-x:auto, so a
   long column set scrolls sideways instead of fighting for a fixed width
   (Ali: "its ok if it scroll horizontaly"). Column set differs per job type
   (see jobsColsForType() in js_jobs.js), so thead/tbody are rendered fresh on
   every type switch rather than kept as one static markup with per-type CSS. */
.jobs-view-table { width:100%;border-collapse:collapse;font-size:0.82em;table-layout:auto; }
/* Sticky on <thead> itself, not per-th - the header is two rows now (group
   labels + sub-labels, see renderJobsTableHead() in js_jobs.js), and a
   sticky <thead> keeps both rows pinned as one unit without hand-measuring
   row-1's height to offset row-2's `top`. */
.jobs-view-table thead { position:sticky;top:0;z-index:10; }
.jobs-view-table thead th { background:var(--brand);color:var(--brand-ink);padding:6px 7px;text-align:center;white-space:nowrap;font-weight:600; }
.jobs-view-table thead th.r { text-align:center; }
/* Group header row (DATE/INPUT/OUTPUT/RATE/SHORTAGE) - same forest as the
   sub-label row below it (Ali: one standard header color, not two), reads
   as "this labels the row below it" via a stripe under the word rather
   than a background shade shift. The stripe is a block spanning 80% of the
   GROUP's full cell width (not just the word's own width via text-
   decoration) - e.g. INPUT's 3-column cell gets a stripe from 10% to 90%,
   so it doesn't run edge-to-edge into the next group. */
/* SHARED WITH THE WEIGHBRIDGE LIST (S191), by adding a selector rather than
   copying the block: both are two-row grouped headers and they must look
   identical, which a second copy stops guaranteeing the first time one is
   edited. Every group on the weighbridge table clears the 100px min-width
   (narrowest is WEIGHBRIDGE at 50+104), so nothing is pushed wider than budget. */
.jobs-view-table thead th.jgh,
#wbTable thead th.jgh { letter-spacing:0.4px;font-size:0.85em;padding-bottom:7px;min-width:100px; }
.jobs-view-table thead th.jgh .jgh-stripe,
#wbTable thead th.jgh .jgh-stripe { display:block;width:80%;margin:0 auto;padding-bottom:2px;border-bottom:2px solid rgba(255,255,255,0.55); }
.jobs-view-table tbody tr { border-bottom:1px solid var(--line-2); }
.jobs-view-table tbody tr.pv-alt td { background:var(--zebra); }
.jobs-view-table tbody tr:hover { background:var(--brand-tint); }
.jobs-view-table tbody tr.pv-alt:hover td { background:var(--brand-tint); }
/* BA73 row cursor - same zebra-beating fix as .pur-view-table above. */
.jobs-view-table tbody tr.acc-cur td{ background:#f2d585 !important; color:#241f00; }
.jobs-view-table tbody tr.acc-cur td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }
/* BA73: Left/Right on a cursored row moves onto its own action button(s). */
.jobs-view-table tbody .jb-action-cur{ outline:2px solid #8a6d0b; outline-offset:1px; }
.jobs-view-table tbody td { padding:6px 7px;white-space:nowrap;color:var(--ink); }
.jobs-view-table tbody td.r { text-align:right; }
.jobs-view-table tbody td.c { text-align:center; }
/* A long job name used to force the whole table into a horizontal scroll -
   clamp it instead (ellipsis, full name on hover via the span's own title).
   More width is free to give it once the sidebar collapses to its icon rail. */
.jobs-view-table tbody td.jc-name { max-width:12ch;overflow:hidden;text-overflow:ellipsis; }
#appSidebar.rail ~ .main-container .jobs-view-table tbody td.jc-name { max-width:20ch; }
/* A job whose underlying data changed since it was last saved (same alert
   _alertsData already drives elsewhere) - folded into Status ("OPEN -
   EDITED") instead of its own badge/column, with the row picking up the
   same pale-amber highlight Sales gives a Draft/alert row so it stays
   visible scanning the whole list. */
tr.job-row-alert td { background:#fff8e1 !important; }
tr.job-row-alert:hover td { background:#fff3cd !important; }
.job-badge-edited { background:var(--warn-tint);color:var(--warn-deep); }

/* ── JOB INFO BAR ── */
/* Sticky wrap is what actually pins+covers - it owns the position:sticky and
   the solid page-background band above/below the bar, so scrolled content
   can never peek through that gap (Ali caught it live: a sliver of the box
   behind would show through a transparent gap). .job-infobar itself is just
   the visible card inside it, no longer sticky on its own. */
.job-infobar-sticky-wrap { position:sticky;top:0;z-index:100;background:var(--bg);padding:14px 0 16px; }
/* S139 — Ali, climbing the keyboard cursor Up from Save all the way back to
   Input Lots: "cursor reachs top but page does not go all the way top."
   Job Detail scrolls at the WINDOW level (no nested overflow:auto ancestor
   for accScrollCursorIntoView's own sticky-band math to find), so its plain
   scrollIntoView({block:'nearest'}) fallback has no idea this sticky bar
   (measured 106.6px) covers the top of the viewport - it stops as soon as a
   focused element is technically "in view," even landing right underneath
   this bar. scroll-margin-top is read natively by scrollIntoView, no JS
   change needed - reserves the bar's height (+10px breathing room) so a
   focused row/button/header bar never lands hidden behind it. */
/* scroll-margin-bottom for the same reason at the other edge - Ali: "save is
   last button in page and still page is litle hidden". block:'nearest' parks
   a below-the-fold element flush on the viewport's bottom pixel, which reads
   as still-cut-off; 24px gives it the same breathing room the top now has. */
#jobDetailView button,
#jobDetailView input,
#jobDetailView [tabindex] { scroll-margin-top: 116px; scroll-margin-bottom: 24px; }
.job-infobar { background:var(--surface);border:1.5px solid var(--line);border-left:5px solid var(--accent);border-radius:10px;padding:12px 18px;display:flex;align-items:center;gap:18px;flex-wrap:wrap;box-shadow:0 3px 10px rgba(0,0,0,0.06); }
.ji-field { display:flex;flex-direction:column;gap:3px; }
.ji-label { font-size:0.72em;color:var(--ink-3);font-weight:600;text-transform:uppercase; }
.ji-value { font-weight:800;color:var(--ink);font-size:1.05em; }
.ji-input { border:1px solid var(--line);border-radius:6px;padding:6px 10px;font-size:0.95em;font-family:inherit;background:var(--surface);color:var(--ink); }
.ji-divider { width:1px;height:34px;background:var(--line);flex-shrink:0; }
.ji-actions { margin-left:auto;display:flex;gap:8px;flex-shrink:0; }
/* S144 — Ali, on a closed job: "beacuse of status 'closed' not fitting in that
   space, collapse and all other came down". A closed job widens the status
   badge AND adds a Reopen Job button, which tipped .ji-actions (473px) just
   past the room left by the fields - flex-wrap sent the whole button block
   onto a second line and the bar grew from 64px to 129px. The buttons never
   wrap now; Name/Description is the field that gives up width instead, since
   it is the only free-text one and its input can scroll. */
.job-infobar { flex-wrap:nowrap; }
.job-infobar .ji-field { flex-shrink:0;min-width:0; }
.job-infobar .ji-field:has(#jiNameInput) { flex-shrink:1; }
.job-infobar #jiNameInput { min-width:90px;width:100%; }
.ji-badge-open     { background:var(--pos-tint);color:var(--pos-deep);padding:4px 14px;border-radius:12px;font-size:0.88em;font-weight:700; }
.ji-badge-closed   { background:var(--neg-tint);color:var(--neg-deep);padding:4px 14px;border-radius:12px;font-size:0.88em;font-weight:700; }
.ji-badge-reopened { background:var(--warn-tint);color:var(--attn);padding:4px 14px;border-radius:12px;font-size:0.88em;font-weight:700; }
.btn-ji-back      { background:var(--surface);color:var(--ink-2);border:1px solid var(--line);border-radius:6px;padding:7px 16px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:6px; }
.btn-ji-back:hover { background:var(--brand-tint);color:var(--ink); }
.btn-ji-save      { background:var(--pos-deep);color:white;border:none;border-radius:6px;padding:7px 16px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ji-completejob { background:transparent;color:var(--ink);border:1.5px solid var(--ink);border-radius:6px;padding:7px 16px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ji-completejob:hover { background:var(--ink);color:white; }
.btn-ji-reopenjob { background:transparent;color:var(--warn-deep);border:1.5px solid var(--warn-deep);border-radius:6px;padding:7px 16px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ji-reopenjob:hover { background:var(--warn-deep);color:white; }

/* ── ACCORDION (Option C — off-white + colored left border) ── */
.acc-section { background:var(--surface);border:1.5px solid var(--line);border-radius:10px;margin-bottom:10px;overflow:visible; }
.acc-bar { display:grid;grid-template-columns:260px 1fr auto 20px;align-items:center;padding:12px 18px;cursor:pointer;user-select:none;background:var(--surface);border-radius:10px 10px 0 0;gap:0; }
.acc-bar:hover { background:var(--brand-tint); }
/* S138 v2 — a thick left-only bar read as "fat and lopsided," not a focus
   ring. A normal all-around outline in the box's own color instead, same
   width/offset the app already uses for this exact purpose on Masters'
   own keyboard-focusable grid (.mst-home-leaf:focus). --acc-color is set
   once per box below and now drives both the left rail and this ring, so
   a future recolor of any one box only needs the one line changed. */
/* S138 v3 — a positive offset draws the ring OUTSIDE the bar, and two edges
   have zero clearance to draw into: the top, because Input Lots (the first
   box) sits flush against the sticky .job-infobar-sticky-wrap above it with
   no gap at all; the bottom, on every box, once it's open and its own table
   starts immediately below. A negative offset draws the ring INSIDE the bar
   instead, needing no external space on any edge - same technique this app
   already uses on its own input focus rings (.mstp-f input:focus etc). */
.acc-bar:focus-visible { outline:2px solid var(--acc-color);outline-offset:-2px; }
.acc-title-block { display:flex;flex-direction:column;padding-right:14px; }
/* Input Lots converted first (Ali's call, 2026-07-28) - Output/Expenses/Final
   Costing/Ready for Sale keep their pre-makeover colors until each gets its
   own reviewed turn. Section identities per the button spec's §3.
   Input and Output both moved S138: fixed charcoal/forest (--in-deep/
   --out-deep), not theme-following. */
.acc-bar-in  { --acc-color:var(--in-deep);   border-left:5px solid var(--acc-color); }
.acc-bar-out { --acc-color:var(--out-deep);border-left:5px solid var(--acc-color); }
.acc-bar-exp { --acc-color:var(--warn-deep);border-left:5px solid var(--acc-color); }
.acc-bar-dc  { --acc-color:var(--warn-deep);border-left:5px solid var(--acc-color); }
.acc-bar-sum { --acc-color:var(--sum-deep);border-left:5px solid var(--acc-color); }
.acc-bar-rfs { --acc-color:var(--brand);   border-left:5px solid var(--acc-color); }
.acc-title     { font-weight:800;font-size:0.98em;min-width:160px; }
.acc-title-in  { color:var(--in-deep); }
.acc-title-out { color:var(--out-deep); }
.acc-title-exp { color:var(--warn-deep); }
/* S146 — DC Charges became a peer box. It keeps the warn/orange identity it
   already had as a nested panel, so the eye still reads it as part of the
   expense story rather than as a new colour to learn. Same accent as Hamali on
   purpose: they are two halves of one cost, and the DC Charges line in Hamali's
   own table is the total of this box. */
.acc-title-dc  { color:var(--warn-deep); }
.acc-title-sum { color:var(--sum-deep); }
.acc-title-rfs { color:var(--brand); }
.acc-subtext   { font-size:0.78em;color:var(--ink-3);font-weight:400;margin-top:2px; }
.acc-stats-row { display:flex;align-items:center;gap:24px;padding:0 12px;flex-wrap:wrap; }
.acc-stat      { display:flex;flex-direction:column;gap:2px;align-items:center; }
.acc-stat-lbl  { color:var(--ink-3);font-size:0.72em;font-weight:600;text-transform:uppercase;letter-spacing:0.03em;white-space:nowrap; }
.acc-stat-val  { font-weight:700;color:var(--ink);font-size:1em;white-space:nowrap; }
.acc-actions   { display:flex;gap:8px;align-items:center; }
.acc-toggle    { font-size:0.85em;color:var(--ink-3);font-weight:700;min-width:20px;text-align:center;margin-left:12px;transition:transform 0.2s; }
.acc-toggle.open { transform:rotate(180deg); }
.acc-body      { display:none;border-top:1px solid var(--line); }
.acc-body.open { display:block; }

/* ── Half-screen (1150px target) — same @media(max-width:1200px) breakpoint
   Purchase's own entry panel already uses, so both screens switch to their
   narrow layout at the same window width. The fixed 260px title column left
   only ~345px for the 5-stat row at 1150px, forcing an ugly wrap (measured
   live: box height 64px -> 127px). Same philosophy as Purchase's block:
   shrink the fixed column and the gaps first, wrapping stays only as the
   last-resort safety valve, nothing above 1200px is touched. */
/* S144 — Ali: "move add lot and clear all to left side like we have in sales
   ... sales does not have clear all, or we can give clear all on right side
   above delete area". So: the +Add button moves LEFT beside the title, Clear
   All goes to the far right where it sits over the rows' own ✕ column, and
   the stat block in between becomes a grid whose tracks mirror the table
   columns underneath - ENTRIES over Item+Lot, then BAGS/RATE/WEIGHT/VALUE
   each over their own column.
   The left region is a FIXED 460px (260 title + 200 add-button) on purpose:
   the table's action column is the flexible one, so every data column is
   anchored to the table's LEFT edge and shifts with the rail. Only a
   left-anchored offset can track them - anything right-anchored drifts. */
/* S146 — #accBarDc joins this group. A bar carrying a Clear All has FIVE
   children, and the base .acc-bar grid only defines four columns, so the new
   DC bar was laying its Clear All on top of the TOTAL stat and wrapping the
   chevron onto a second line. Seen on screen, not guessed. */
#accBarIn, #accBarOut, #accBarExp, #accBarDc { grid-template-columns:260px 200px 540px 1fr auto 20px; }
#accBarIn > .acc-title-block, #accBarOut > .acc-title-block, #accBarExp > .acc-title-block, #accBarDc > .acc-title-block { grid-column:1; }
#accBarIn > .acc-actions,     #accBarOut > .acc-actions,     #accBarExp > .acc-actions,     #accBarDc > .acc-actions     { grid-column:2; }
#accBarIn > .acc-stats-row,   #accBarOut > .acc-stats-row,   #accBarExp > .acc-stats-row,   #accBarDc > .acc-stats-row   { grid-column:3; }
#accBarIn > .acc-actions-right, #accBarOut > .acc-actions-right, #accBarExp > .acc-actions-right, #accBarDc > .acc-actions-right { grid-column:5; }
#accBarIn > .acc-toggle,        #accBarOut > .acc-toggle,        #accBarExp > .acc-toggle,        #accBarDc > .acc-toggle        { grid-column:6; }
.acc-actions-right { display:flex;gap:8px;align-items:center;justify-content:flex-end; }

/* The stat tracks ARE the table's own column widths (70/110/118/132), with
   ENTRIES spanning Item+Lot (60+50). Right-aligned with the same 9px cell
   padding the numeric <td>s use, so the figures sit exactly over the numbers
   below them rather than merely inside the right column. */
#inputStatsNew, #outputStatsNew {
    display:grid; grid-template-columns:110px 70px 110px 118px 132px;
    gap:0; padding:0; flex-wrap:nowrap;
}
#inputStatsNew .acc-stat, #outputStatsNew .acc-stat { align-items:flex-end;padding-right:9px; }
#inputStatsNew .acc-stat:first-child, #outputStatsNew .acc-stat:first-child { align-items:center;padding-right:0; }

/* Hamali carries only THREE figures, so they are placed onto the same tracks
   by hand rather than filling 1-2-3: Entries, then Rate over the Rate column
   and Total over the Total column. Ali: "total is in middle and rate is on
   side, need to swap" - the markup order was Entries/Total/Rate, which put
   each figure over the wrong column; it is Entries/Rate/Total now. */
#expenseStatsNew {
    display:grid; grid-template-columns:110px 70px 110px 118px 132px;
    gap:0; padding:0; flex-wrap:nowrap;
}
#expenseStatsNew .acc-stat { align-items:flex-end;padding-right:9px; }
#expenseStatsNew .acc-stat:nth-child(1) { grid-column:1;align-items:center;padding-right:0; }
#expenseStatsNew .acc-stat:nth-child(2) { grid-column:3; }
#expenseStatsNew .acc-stat:nth-child(3) { grid-column:5; }

/* DC Charges (S152): Shortage kg/% take over the Entries/Bags tracks, then
   Avg Rate/Weight/Total land on the SAME tracks Input/Output use for those —
   same five tracks, filled in natural source order like Input/Output are
   (unlike Hamali above, this box genuinely has all five). Was plain flex,
   which drifts out of alignment the moment a label's text width differs from
   "Entries"/"Bags"/etc — measured up to 54px off before this rule existed. */
#dcStatsNew {
    display:grid; grid-template-columns:110px 70px 110px 118px 132px;
    gap:0; padding:0; flex-wrap:nowrap;
}
#dcStatsNew .acc-stat { align-items:flex-end;padding-right:9px; }
#dcStatsNew .acc-stat:first-child { align-items:center;padding-right:0; }
/* One label bridging the Shortage kg/% pair, like a colspan header — the two
   VALUES stay in their own tracks (kg under Entries, % under Bags) so they
   still read as two numbers, just under one shared caption instead of two. */
#dcStatsNew .acc-stat-pair { grid-column:1 / span 2;display:flex;flex-direction:column;align-items:center;gap:2px; }
/* Shared by every .acc-stat-pair (DC Charges above, Final Costing below) —
   ONE definition so the two boxes can't drift apart the way they did when
   this was scoped to #dcStatsNew only and #costingStatsNew silently fell
   back to block (no gap at all between the two values). */
.acc-stat-pair-vals { display:flex;gap:18px; }
/* The pair's own stacking, unscoped — BT93 nests Final Costing's pair inside an
   .acc-stat-group so a second figure can sit beside it, and a nested pair is no
   longer a direct child of #costingStatsNew, so the `>` rule below stops
   matching it. Without this it falls back to block and the kg/% line loses its
   caption alignment. The two `>` rules keep their grid-column and simply repeat
   these three declarations; they are the same values, not a competing opinion. */
.acc-stat-pair { display:flex;flex-direction:column;align-items:center;gap:2px; }

/* S144 — Final Costing bar, same idea one step further: APPROX RATE sits over
   the Avg Rate column and the FINAL RATE input over the Total column, with its
   caption parked in the Weight column beside it. The bar's own tracks mirror
   Rate(110)/Weight(118)/Total(132) so both land by position, not by guesswork.
   The shortage figures are wrapped in .acc-stat-group (js_jobs.js) because
   there are two of them on DC and one on Palla - grouping keeps Approx Rate a
   single placeable item whichever job type is open. */
/* Tracks 3-7 ARE the table's columns (Item+Lot / Bags / Rate / Weight / Total),
   so every figure lands by position: SHORTAGE starts where ENTRIES starts on
   the other bars, APPROX RATE over Avg Rate, FINAL RATE over Total. */
#accBarSum { grid-template-columns:260px 200px 110px 70px 110px 118px 132px 1fr 20px; }
#accBarSum > .acc-title-block { grid-column:1; }
#accBarSum > .acc-stats-row   { grid-column:3 / span 3;display:grid;grid-template-columns:110px 70px 110px;gap:0;padding:0; }
#accBarSum > .acc-actions     { grid-column:6 / span 2;display:block; }
#accBarSum > .acc-toggle      { grid-column:9; }
/* Ali: "keep shortage exactly below entries, center of entries meets center of
   shortage" - the group is centred ON the Entries track rather than filling it,
   so its midpoint is the track's midpoint whether DC shows two shortage figures
   or Palla shows one. It is allowed to spill past the 110px track (the slot to
   its left is empty on this bar), capped so it can never reach Approx Rate. */
#costingStatsNew > .acc-stat-group { grid-column:1;justify-self:center;max-width:248px; }
/* Ali (S154 cont'd): DC vs Ready now leads Shortage instead of following it,
   and Shortage's right edge must stay exactly where DC vs Ready's bare %
   used to end - not just "the group stays centred", which would push BOTH
   edges outward once DC vs Ready grew a label + its own kg partner. Anchor
   the right edge on the column boundary instead of centring, then pull it
   back out by the exact overflow the old centred group measured (894.27 -
   828 = 66.27px) so the edge lands on the same physical pixel as before;
   everything new grows left of it. Scoped past the base rule above (two
   classes beat one) so Palla's single-stat .acc-stat-group is untouched. */
#costingStatsNew > .acc-stat-group.acc-stat-group-anchor-r { justify-self:end;margin-right:-66.27px;max-width:none; }
#costingStatsNew > .acc-stat       { grid-column:3; }
/* DC's Final Shortage, same "Stemless Shortage" pair treatment as the DC
   Charges box above — spans the same 180px (Entries+Bags tracks), one label
   bridging both values. */
#costingStatsNew > .acc-stat-pair  { grid-column:1 / span 2;display:flex;flex-direction:column;align-items:center;gap:2px; }
.acc-stat-group { display:flex;gap:14px;align-items:center;justify-content:center; }
#costingStatsNew > .acc-stat { align-items:flex-end;padding-right:9px; }
.fc-rate-wrap { display:grid;grid-template-columns:118px 132px;align-items:center; }
.fc-rate-lbl  { font-size:0.72em;color:#aaa;font-weight:600;text-transform:uppercase;
                letter-spacing:0.3px;text-align:right;padding-right:10px;line-height:1.25; }
.fc-rate-wrap .costing-input { justify-self:end;margin-right:9px; }

@media (max-width: 1200px) {
    .acc-bar { display:flex; flex-wrap:wrap; row-gap:8px; column-gap:14px; }
    #accBarSum > .acc-stats-row { display:flex; }
    #accBarSum > .acc-actions { display:flex; }
    .fc-rate-wrap { display:flex;gap:10px; }
    .fc-rate-lbl { text-align:left;padding-right:0;white-space:nowrap; }
    #costingStatsNew > .acc-stat { align-items:center;padding-right:0; }
    .acc-title-block { flex:0 0 auto; padding-right:10px; }
    .acc-stats-row { flex:1 1 auto; gap:14px; }
    .acc-actions { margin-left:auto; }
    /* Below the alignment breakpoint the tables no longer hold their fixed
       column grid either, so the mirrored stat grid is dropped with it. */
    #inputStatsNew, #outputStatsNew, #expenseStatsNew, #rfsStatsNew { display:flex; gap:14px; }
    #inputStatsNew .acc-stat, #outputStatsNew .acc-stat, #expenseStatsNew .acc-stat,
    #rfsStatsNew > .acc-stat { align-items:center;padding-right:0;padding-left:0; }
    .acc-actions-right { margin-left:auto; }
}

/* ── ADD BUTTONS (colored border, fills solid on hover) ── */
.btn-add-in  { background:var(--surface);color:var(--in-deep);border:1.5px solid var(--in-deep);border-radius:6px;padding:6px 14px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit; }
.btn-add-in:hover  { background:var(--in-deep);color:white; }
.btn-add-out { background:white;color:var(--out-deep);border:1.5px solid var(--out-deep);border-radius:6px;padding:6px 14px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit; }
.btn-add-out:hover { background:var(--out-deep);color:white; }
.btn-add-exp { background:white;color:var(--warn-deep);border:1.5px solid var(--warn-deep);border-radius:6px;padding:6px 14px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit; }
.btn-add-exp:hover { background:var(--warn-deep);color:white; }

/* ── SHARED TABLE (Option C) ── */
.main-table { width:100%;border-collapse:collapse;font-size:var(--fs-sm);font-variant-numeric:tabular-nums; }
.main-table.tbl-in  th { background:var(--head);color:var(--ink); }
.main-table.tbl-out th { background:var(--head);color:var(--ink); }
.main-table.tbl-exp th { background:var(--head);color:var(--ink); }
.main-table.tbl-rfs th { background:var(--head);color:var(--ink); }
.main-table.tbl-dc  th { background:var(--head);color:var(--ink); }
.main-table.tbl-sum th { background:#f5f5f5;color:#555;border-bottom:2px solid #e0e0e0; }
/* S138 — headers were falling through to the legacy global th{text-align:left}
   (style.css ~290), since this rule never declared its own value for that
   property. Centered to match the Jobs list table's own header convention
   (.jobs-view-table thead th) - text columns' body values stay left/right
   as they already were, only the header LABEL centers. */
.main-table th { padding:7px 9px;font-weight:600;font-size:var(--fs-sm);white-space:nowrap;text-align:center; }
.main-table th.r { text-align:right; }
.main-table td { padding:6px 9px;border-bottom:1px solid var(--line-2);vertical-align:middle;color:var(--ink); }
.main-table td.r { text-align:right; }
.main-table td.c { text-align:center; }
.main-table tfoot td { background:var(--head);font-weight:700;font-size:var(--fs-sm);padding:7px 9px;border-top:2px solid var(--line); }
.main-table tfoot td.r { text-align:right; }
/* Flat lists only - real zebra, safe (nothing inserted between rows). Input
   Lots is excluded: its date-group/detail two-level structure already
   carries its own row hierarchy below, a flat stripe would fight it, same
   conflict as the Purchase ledger (table standard §4.2) and Chart of
   Accounts' tree. */
.main-table.tbl-out tbody tr:nth-child(even) td,
.main-table.tbl-exp tbody tr:nth-child(even) td,
.main-table.tbl-rfs tbody tr:nth-child(even) td { background:var(--zebra); }
.main-table.tbl-out tbody tr:hover td,
.main-table.tbl-exp tbody tr:hover td,
.main-table.tbl-rfs tbody tr:hover td { background:var(--brand-tint); }

/* ── INPUT LOTS two-level ── */
/* S138 — was --head (sand), same color the column header above it just
   moved onto. Two sand bands stacked with nothing between them read as
   one solid block once every group is collapsed - the exact "collapsed
   sheet turns into one slab" issue Trial Balance already hit once
   (S138 mockup review). White instead: the header is always sand, this
   row is always visible whether collapsed or open (same role as Trial
   Balance's own root level, which stayed plain for the same reason), and
   only the true leaf rows below keep any tint (the zebra stripe). */
.date-sum-row td { background:var(--surface);font-weight:700;font-size:var(--fs-sm);border-bottom:1px solid var(--line-2);cursor:pointer; }
.date-sum-row:hover td { background:var(--brand-tint); }
.lot-detail-row td { background:var(--surface); }
.lot-detail-row.alt td { background:var(--zebra); }
.lot-detail-row:hover td { background:var(--brand-tint); }
.lot-detail-row.editing td { background:var(--warn-tint); }
/* BA73: the table's own keyboard cursor - a plain browser outline on a <tr>
   renders inconsistently across cells, so this is painted the same way
   every other row cursor this session is (a td-level background, amber to
   match). :focus, not a class - real focus is what drives this cursor,
   same as the rest of the Job Detail screen's own entry-chain. */
/* :focus-within, not :focus - a leaf row's own Delete button is a second
   stop on the row (jdLotsMoveV/jdLotsKeyHandler), and the row must stay lit
   while that button holds real focus, same as Hamali/RFS below. */
#inputLotsTbody tr:focus-within{ outline:none; }
#inputLotsTbody tr:focus-within td{ background:#f2d585 !important; color:#241f00; }
#inputLotsTbody tr:focus-within td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }

/* S138 — Final Costing's own table rows, fully read-only (no inputs to
   worry about, unlike Hamali's), so a plain :focus recipe works the same
   way Input/Output's own rows already do. */
#accSum .sum-table tbody tr:focus{ outline:none; }
#accSum .sum-table tbody tr:focus td{ background:#f2d585 !important; color:#241f00; }
#accSum .sum-table tbody tr:focus td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }

/* S138 — Output Lots' own rows never carried tabindex at all, so there was
   nothing to arrow through. Same amber cursor recipe as Input Lots above,
   scoped to #outputLotsTbody the same way. */
/* :focus-within, not :focus - Edit/Delete are now two more stops on the row
   (JD_OUT_RING), and the row must stay lit while either holds real focus. */
#outputLotsTbody tr:focus-within{ outline:none; }
#outputLotsTbody tr:focus-within td{ background:#f2d585 !important; color:#241f00; }
#outputLotsTbody tr:focus-within td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }

/* S138 — Hamali's rows aren't read-only like Input/Output; every cell can
   be its own input, so the row itself is a "home" you land on (real .focus,
   same as the two above) AND you can drill into Bags/Rate within it -
   :focus-within instead of :focus, so the amber cursor stays visible on the
   whole row whether the <tr> itself or one of its inputs is what's focused. */
#expensesTbody tr:focus{ outline:none; }
#expensesTbody tr:focus-within td{ background:#f2d585 !important; color:#241f00; }
#expensesTbody tr:focus-within td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }

/* S139 — RFS's own rows, same shape as Hamali's above: the checkbox/rate
   input/Add-to-Stock button inside a row are real Tab stops, so :focus-within
   keeps the amber cursor visible whether the <tr> itself or one of those
   inner elements holds focus. */
#readyForSaleTbody tr:focus{ outline:none; }
#readyForSaleTbody tr:focus-within td{ background:#f2d585 !important; color:#241f00; }
#readyForSaleTbody tr:focus-within td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }

/* BB75 — DC Charges' own rows never got this rule when the box was split
   out of Hamali (S146), so a focused row fell through to the browser's
   plain default outline instead of the app's amber convention - reported
   live 2026-08-27 as "gets a focus with red border" instead of yellow.
   Same :focus-within shape as the four tables above (row/edit/delete are
   all real Tab stops on this table too). */
#dcChargesTbody tr:focus{ outline:none; }
#dcChargesTbody tr:focus-within td{ background:#f2d585 !important; color:#241f00; }
#dcChargesTbody tr:focus-within td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }

/* ── Input-lots row hover — SCOPED TO #inputLotsTbody ON PURPOSE ────────────
   Ali's original report: "the hover color matches near row very much". The
   shared --brand-tint hover measured 1.10 against the zebra stripe and 1.03
   against the sand date row beside it (1.00 = identical), because --brand-tint
   0.849 and --head 0.825 sit in the same luminance band by construction — so
   nudging it within that band collides again. This goes clearly BELOW the band.

   Deliberately NOT changed app-wide. --brand-tint is the hover in ~35 rules and
   Ali was explicit: "i dont want it to change any where else, we can just
   change for input lots." Scoped to the one table he actually hits.
   The .date-sum-row here is the click target for expand/collapse, so it needs
   the same treatment or the row people actually click keeps the defect.

   #a4c3ae is the lightest sage that clears 1.5 against ALL FOUR surfaces a
   hovered row can sit against — its own resting white, the zebra, the sand
   date row, and --surface. Measured, not picked by eye: 1.89 / 1.89 / 1.80 /
   1.59, where the old tint managed 1.03.

   The colour override is not decoration. Darkening the ground broke the row's
   own muted text: --ink-3 fell to 2.56:1 and --ink-2 to 3.08:1, both under the
   4.5 floor, so the date column washed out exactly when you hover it. Forcing
   full --ink puts it back to 7.71:1. Any future change to this background must
   re-check the text on it. */
#inputLotsTbody .lot-detail-row:hover td,
#inputLotsTbody .date-sum-row:hover td { background:#a4c3ae; }
/* !important is load-bearing here, not laziness: the Date and Remarks cells
   carry an INLINE style="color:#aaa" from js_jobs.js, and an inline style beats
   any stylesheet rule. Without this the two muted columns sit at ~1.6:1 on the
   hover ground - unreadable exactly when you are pointing at them. Only the
   hover state is forced; the resting muted look is left alone. */
#inputLotsTbody .lot-detail-row:hover td,
#inputLotsTbody .lot-detail-row:hover td *,
#inputLotsTbody .date-sum-row:hover td,
#inputLotsTbody .date-sum-row:hover td * { color:var(--ink) !important; }
.td-item { font-weight:700; }
.td-lot  { font-weight:600; }
.td-readonly { color:var(--ink); }
.td-input-edit { border:1px solid var(--line);border-radius:5px;padding:5px 7px;font-size:0.92em;font-family:inherit;text-align:right;width:85px; }
.td-input-edit.warn { border-color:var(--warn-deep);background:var(--warn-tint); }
/* S154, Ali: "we need little bigger size for vital info like bags rate weight."
   MEASURED, not guessed — the whole edit row sat at 0.92em (11.96px) while the
   display rows around it are 13.0px, so the moment you started editing, the
   figures you were checking became the smallest text on the screen.

   Only the fields that carry a real figure are lifted: Lot, Bags, Weight on the
   Output Lots row and Rate, Weight on DC Charges. Date/GDN/Emp/Remarks stay at
   0.92em on purpose — that contrast IS the point, the vital numbers now read
   heavier than the labels around them.

   A COMBINED-CLASS selector, never a bare `.td-input-key`: that would tie with
   `.td-input-edit` on specificity and let source order decide, which is the trap
   that ate S153's zone colours (dead-css-rule-loses-to-longer-selector).

   Costs nothing in layout: every one of these inputs carries a fixed inline
   width, so the table stays 1272px wide at any font size — measured at 1em,
   1.02em and 1.06em, no column moved. Row height grows by exactly 1px. */
.td-input-edit.td-input-key { font-size:1.06em; }

/* ── OUTPUT FORM ── */
/* S138 — was a hardcoded mint tint (#f9fff9), unrelated to any token. Sand
   (--head) is now the table's own header color and --surface is the box's
   resting background, so this entry strip gets the app's other existing
   secondary tone (--zebra) instead of a fourth invented shade. */
.out-form { background:var(--zebra);border-bottom:2px solid var(--line);padding:12px 16px; }
.out-form-row { display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap; }
.of-field { display:flex;flex-direction:column;gap:3px; }
.of-label { font-size:0.72em;color:#888;font-weight:700;text-transform:uppercase; }
/* Required-field * was riding the label's own grey - same pattern Purchase
   entry's .p2if label still has, but that screen is parked (Ali: no new
   changes there), so this is scoped to Output only. */
.of-label .req { color:var(--neg); }
.of-input { border:1.5px solid #c8e6c9;border-radius:6px;padding:7px 9px;font-size:0.92em;font-family:inherit;background:white; }
.of-input.auto { background:#f5f5f5;color:#aaa; }

/* S146 — ONE control height across the Output add-form row.
   Ali: "they dont have aligment some are off by a 1 or 2 px, not sure check
   once." Measured, and he was right - the labels sat at four different tops
   (282.2 / 284.2 / 286.2 / 291.2, a 9px spread) because the CONTROLS were four
   different heights and .out-form-row is align-items:flex-end, so every odd
   control shifts its own label:
     plain .of-input .............. 32px
     native <input type=date>, <select>  34px  (UA chrome adds to the box)
     createSearchDropdown's input . 36px  (hardcoded in js_core.js)
     the WC checkbox wrapper ...... 13px
   Pinning the height here fixes the first two and the last. The picker's own
   input is inline-styled by the component, which beats a class selector, so it
   is normalised in JS at its build site instead - same approach editOutputLot
   already takes for the in-row picker. Do NOT "simplify" this by adding
   !important; the JS half is deliberate. */
.out-form-row .of-input { height:32px; box-sizing:border-box; }
.out-form-row .of-check { height:32px; padding:0; display:flex; align-items:center; justify-content:center; cursor:pointer; }

/* The keyboard hint on the Output and Expense add-forms.
   S146 revision, Ali: "use that area, i dont want to add another row" and, of
   the Expense form, "had plenty space, there u used so less space it barely
   visible." So it now lives INSIDE the flex row rather than on a line of its
   own, pushed into the empty space past Cancel, a step larger and a shade
   darker than the first attempt. Still quieter than a label - it is a reminder
   for someone already typing, not a call to action.

   MEASURED, because the first attempt at putting it in the row wrapped it onto
   a second line - exactly the extra row he did not want. Free space to the
   right of Cancel at the locked 1528px viewport:
       Output form, no WC fields ....... 132px
       Output form, WC + WC Rate shown .. 48px   <- DC/Stemless, ticked
       Expense form .................... 402px
   "Esc closes this form" needs 114px, so it shows in full in the first and
   third cases. For the 48px case it must not wrap, so it SHRINKS and ellipses
   instead: flex:1 1 0 with min-width:0 lets it give up its width down to
   nothing, which keeps the row one line high no matter how full it gets. The
   longer wording (and the Enter half) lives in the title tooltip. */
.form-kb-hint { flex:1 1 0; min-width:0; align-self:flex-end; padding-bottom:7px; text-align:right;
                white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
                font-size:0.8em; color:#8a8a8a; letter-spacing:0.02em; }

/* S146 — Ali: "add and cancel height looks smaller than other feilds (height
   wise)." Measured: 6px padding + 1.5px border + a 0.88em line box came out
   around 30px against the 32px every input in the row now uses. These four are
   the +Add / Cancel pair on each of the two forms. */
.out-form-row .btn-of-add, .out-form-row .btn-of-cancel,
.exp-form-row .btn-ef-add, .exp-form-row .btn-ef-cancel { height:32px; box-sizing:border-box; padding:0 14px; }

/* The Expense form's own fields carry the same four-different-heights problem
   the Output row had (see above): .ef-input is 32px, but the picker that
   createSearchDropdown builds into #ddJobExpense is inline-styled to 36px.
   Normalised in JS at the build site for the same reason as #ddOutItem. */
.exp-form-row .ef-input { height:32px; box-sizing:border-box; }
/* The DC Charges form reuses .exp-form / .exp-form-row wholesale, so it
   inherits the same one-height rule and the same in-row hint. */
#dcFormRow .ef-input { height:32px; box-sizing:border-box; }
/* Was a flat solid-green fill, sized 9px/18px — a different size AND a
   different mechanic from Cancel right next to it. Now the same
   outline-fills-on-hover recipe as this box's own +Add Output button
   (.btn-add-out), sized to match Cancel (.btn-sm) exactly. */
.btn-of-add { background:var(--surface);color:var(--out-deep);border:1.5px solid var(--out-deep);border-radius:6px;padding:6px 14px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit;align-self:flex-end; }
.btn-of-add:hover { background:var(--out-deep);color:white; }
/* Was .btn.btn-secondary.btn-sm - hardcoded #7f8c8d grey, unrelated to the
   app's own dedicated Cancel treatment (charcoal outline, fills solid on
   hover - .btn-cancel-inline uses the same tokens at a smaller size).
   Scoped here rather than editing .btn-secondary itself, which is shared
   by many other modals app-wide. */
.btn-of-cancel { background:var(--surface);color:var(--ink-2);border:1.5px solid var(--ink-3);border-radius:6px;padding:6px 14px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit;align-self:flex-end; }
.btn-of-cancel:hover { background:var(--ink-2);color:white; }

/* ── EXPENSE DROPDOWN ── */
.job-exp-dropdown { position:absolute;top:100%;right:0;background:white;border:2px solid #e67e22;border-radius:8px;min-width:280px;z-index:9999;box-shadow:0 6px 20px rgba(0,0,0,0.15);overflow:hidden; }
.job-exp-dd-item  { padding:10px 14px;cursor:pointer;font-size:0.9em;color:#2c3e50;border-bottom:1px solid #f5f5f5; }
.job-exp-dd-item:hover  { background:#fef9f4; }
.job-exp-dd-master { padding:10px 14px;cursor:pointer;font-size:0.88em;color:#e67e22;font-weight:700;border-top:2px solid #f5cba7;background:#fffbf7; }
.job-exp-dd-master:hover { background:#fef4e7; }
/* Inline expense form (demo style) */
/* S138 — was a hardcoded peach tint, same fix as Output's .out-form: the
   app's other existing secondary tone (--zebra) instead of inventing a
   fifth shade now that sand (--head) is the table's own header color. */
.exp-form { background:var(--zebra);border-bottom:2px solid var(--line);padding:12px 16px; }
.exp-form-row { display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap; }
.ef-field { display:flex;flex-direction:column;gap:3px; }
.ef-label { font-size:0.72em;color:#888;font-weight:700;text-transform:uppercase; }
.ef-input { border:1.5px solid #f5cba7;border-radius:6px;padding:7px 9px;font-size:0.92em;font-family:inherit;background:white; }
/* S139 — .exp-select-wrap / .exp-dropdown / .exp-dd-item / .exp-dd-disabled /
   .exp-dd-addnew all removed with the bespoke Job Detail expense picker. It is
   a createSearchDropdown now (token-based), so these nine rules - and the
   hardcoded #f5cba7 / #fdf0e0 / #fdf9f4 / #f9f9f9 palette that made this the
   one picker deaf to the Ground/Accent setting - had no elements left to match. */
/* S138 — was a flat solid-orange fill on a hardcoded hex (#e67e22, not the
   box's own --warn-deep), and had no Cancel next to it at all. Now the same
   outline-fills-on-hover recipe as this box's own +Add Expense button
   (.btn-add-exp), on the box's real token, sized to match the new Cancel. */
.btn-ef-add { background:var(--surface);color:var(--warn-deep);border:1.5px solid var(--warn-deep);border-radius:6px;padding:6px 14px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit;align-self:flex-end; }
.btn-ef-add:hover { background:var(--warn-deep);color:white; }
.btn-ef-cancel { background:var(--surface);color:var(--ink-2);border:1.5px solid var(--ink-3);border-radius:6px;padding:6px 14px;font-size:0.88em;font-weight:700;cursor:pointer;font-family:inherit;align-self:flex-end; }
.btn-ef-cancel:hover { background:var(--ink-2);color:white; }

/* ── EXPENSE TABLE inline editing ── */
/* S138 — the wrapping <td> was already text-align:right (measured: the box
   itself sits correctly at the right edge of its column), but the NUMBER
   inside the box was still left-aligned by default, so it read as "left"
   at a glance. Purchase entry's own Bags/Rate/Weight/Total inputs already
   set this explicitly (inline style:text-align:right) - same fix here. */
.job-td-input { border:1px solid #e0e0e0;border-radius:4px;padding:4px 8px;font-size:0.88em;font-family:inherit;text-align:right; }
.job-td-input:focus { outline:none;border-color:#e67e22; }

/* ── FINAL COSTING TABLE ── */
/* S138 — was its own font scale (1.0em, 10-12px padding, hardcoded
   #f0f0f0 border) instead of the --fs-sm/6-9px/--line-2 standard every
   other job table already uses. */
.sum-table { width:100%;border-collapse:collapse;font-size:var(--fs-sm); }
.sum-table th { background:var(--head);color:var(--ink);padding:7px 9px;font-weight:600;text-align:center; }
/* S139 — this was text-align:center, and the S138 note here claimed the
   centred-header/right-data offset matched "every other table in this app".
   That was simply wrong: .main-table th.r (Input/Output/Hamali/RFS, and every
   th in those tables carries class="r") is text-align:RIGHT, sitting directly
   over its right-aligned money. This table was the only one centring them, so
   its headers read a few px left of their own figures - Ali: "header is little
   left and values are shown little right side". Right, like everywhere else;
   the 9px side padding is identical on th and td, so they line up exactly. */
.sum-table th.r { text-align:right; }
.sum-table td { padding:6px 9px;border-bottom:1px solid var(--line-2); }
/* Bags/Avg Rate/Weight kg/Total ₹ are money values - Ali's own standing rule
   (already applied to Input/Output/Hamali): money data stays right. */
.sum-table td.r { text-align:right;color:var(--ink);font-weight:600; }
/* S138 — seven different hardcoded row colors (plus three more on the DC
   variant: row-wc/row-net/row-dc-paid) down to one rule: a row's LABEL
   takes the color of the box its figure actually came from - Input,
   Output, and the two expense-category rows (Total Expenses, Stemless
   Charges) borrow their box's fixed color; Loss/Shortage stays red on the
   real --neg token, label AND numbers. Everything else (System/Approx
   Rate, Whole Chillies, Net DC Input, Approx/Final Total) is Final
   Costing's own figure, so the base rule's plain --ink black applies
   without an override. Odd row backgrounds and Final Total's extra
   border/font-size bump are gone; Approx/Final Total keep bold + one top
   rule instead, the same way grand-total rows look everywhere else. */
/* Row labels (Input/Output/Loss.../etc) read small at the table's own
   --fs-sm - Ali: "value look fine, only text i am talking about," so only
   the label column steps up a notch, the numbers next to it stay put.
   --fs-base (15px) was tried first and called too large - 14px instead,
   no token for that step so a literal value, same as the app's other
   one-off in-between sizes. */
.sum-table td:first-child { color:var(--ink);font-weight:700;font-size:14px;display:flex;align-items:center;gap:7px; }
.row-in td:first-child      { color:var(--in-deep); }
.row-out td:first-child     { color:var(--out-deep); }
.row-exp td:first-child,
.row-dc-paid td:first-child { color:var(--warn-deep); }
.row-loss td:first-child,
.row-loss td.r              { color:var(--neg-deep); }
.row-approx-total td,
.row-final-total td { font-weight:800;border-top:2px solid var(--line);padding-top:9px;padding-bottom:9px; }
/* Every other job table's own GRAND TOTAL/TOTAL row sits on sand (--head);
   Final Total is this table's equivalent row and was plain white, the one
   total row in the whole screen that didn't match. Approx Total stays
   plain - it's a sub-total, not the table's actual bottom line. */
.row-final-total td { background:var(--head); }
/* Bug 23 — Final Costing header: 4 stats evenly spaced */
#costingStatsNew { display:flex;align-items:center;gap:0;flex:1;justify-content:space-evenly; }
/* Fix Final Costing header height — compress Final Rate area */
.acc-bar-sum .acc-actions { flex-direction:column;gap:1px;align-items:flex-end; }
.costing-input { border:2px solid #27ae60;border-radius:6px;padding:6px 10px;font-size:1.05em;font-family:inherit;text-align:right;width:120px;font-weight:700;color:#27ae60;background:#f0faf4; }

/* ── READY FOR SALE ── */
/* The lock sits BESIDE the rate, never under it. The cell is 128px now (was
   110, which left 92px of content for a 90px box plus a 15.6px icon — measured,
   and the icon wrapped to a second line, taking the row from ~40px to 58px). */
.rfs-rate-cell { white-space:nowrap; }
.rfs-rate-cell .ic { vertical-align:middle;margin-left:3px; }
.rfs-rate-input { border:1.5px solid #a9dfbf;border-radius:5px;padding:5px 8px;font-size:0.92em;font-family:inherit;text-align:right;width:90px;font-weight:700; }
.rfs-rate-input.rate-default  { border-color:#a9dfbf;color:#27ae60;background:#f0faf4; }
.rfs-rate-input.rate-disc     { border-color:#f0b27a;color:#e67e22;background:#fef5ec; }
.rfs-rate-input.rate-locked   { border-color:#e0e0e0;color:#aaa;background:#f8f8f8; }
.rfs-rate-input.rate-auto     { border-color:#a9cce3;color:#2980b9;background:#eaf4fb;font-style:italic; }

/* ── RFS rate split (S163) ────────────────────────────────────────────────
   Two positions, and the hint under them says what each one does — the whole
   mechanism is invisible otherwise, which is how the old balance row went
   years without anyone knowing what the bolt column meant. */
.rfs-mode { display:flex;align-items:center;flex-wrap:wrap;gap:8px;
            padding:9px 14px 8px;border-bottom:1px solid var(--line,#e6e2da); }
.rfs-mode-lbl { font-size:0.8em;font-weight:700;letter-spacing:.03em;
                text-transform:uppercase;color:#8a8578; }
/* TICKS, not a segmented toggle: neither ticked is a real state (every lot gets
   the same rate), and a toggle cannot show that — it always claims one side. */
.rfs-mode-tick { display:inline-flex;align-items:center;gap:6px;
                border:1.5px solid #d9d4c9;background:#fff;color:#6b665c;
                border-radius:5px;padding:4px 11px 4px 8px;font-size:0.85em;
                font-weight:600;cursor:pointer;user-select:none; }
.rfs-mode-tick input { margin:0;cursor:pointer;accent-color:var(--accent,#9c4a2f); }
.rfs-mode-tick:hover:not(.off) { border-color:var(--accent,#9c4a2f);
                color:var(--accent,#9c4a2f); }
/* Ticked = the accent ground, so its own text must stay white — the hover rule
   above would otherwise paint accent on accent and the word disappears, which
   is exactly what Ali saw on the button version. */
.rfs-mode-tick.on { background:var(--accent,#9c4a2f);border-color:var(--accent,#9c4a2f);
                color:#fff; }
.rfs-mode-tick.on:hover:not(.off) { color:#fff;filter:brightness(1.12); }
.rfs-mode-tick.on input { accent-color:#fff; }
.rfs-mode-tick.off { opacity:.55;cursor:default; }
.rfs-mode-tick.off input { cursor:default; }
.rfs-mode-hint { flex:1 1 260px;min-width:0;font-size:0.8em;color:#8a8578;
                 line-height:1.5; }
/* When the split is fixed the hint stops explaining the modes and says why the
   buttons are dead — a disabled control with no reason beside it is the thing
   people file bugs about. */
.rfs-mode-hint.locked { color:#a1673f;font-weight:600; }
/* Share mode nominates no lot, so the column that nominates one comes off,
   and so does its entry in the legend under the table. */
.tbl-rfs.rfs-hide-bal .rfs-bal-col { display:none; }
#accRfs.rfs-hide-bal .rfs-bal-legend { display:none; }
.btn-add-stock    { background:#f0faf4;color:#27ae60;border:1.5px solid #a9dfbf;border-radius:6px;padding:5px 12px;font-size:0.85em;font-weight:700;cursor:pointer;font-family:inherit;white-space:nowrap; }
.btn-add-stock:hover { background:#27ae60;color:white;border-color:#27ae60; }
.btn-add-stock:disabled { background:#f5f5f5;color:#bbb;border-color:#e0e0e0;cursor:default; }
.btn-in-stock     { background:#f0faf4;color:#27ae60;border:1.5px solid #a9dfbf;border-radius:6px;padding:5px 12px;font-size:0.85em;font-weight:700;cursor:default;font-family:inherit; }
.btn-remove-stock { background:var(--surface);color:var(--neg);border:1.5px solid var(--neg);border-radius:6px;padding:5px 12px;font-size:0.85em;font-weight:700;cursor:pointer;font-family:inherit; }
.btn-remove-stock:hover:not(:disabled) { background:var(--neg);color:#fff; }
.btn-remove-stock:disabled { color:var(--ink-3);border-color:var(--line);cursor:default;opacity:.55; }

/* ── BOTTOM SAVE BAR ── */
/* Ali's call: not a floating/locked bar - just the 6th box in the sequence,
   same shape+padding+charcoal accent as the acc-bar boxes above it (Job
   Complete's own button is already charcoal, reused here as this "box"'s
   identity color, same as every other box's left-border accent). */
.job-save-bar { background:var(--surface);border:1.5px solid var(--line);border-left:5px solid var(--ink);border-radius:10px;padding:14.5px 18px;display:flex;align-items:center;justify-content:space-between; }
.job-save-bar-right { display:flex;align-items:center;gap:12px; }

/* Jobs % footer hint (S152), round 2 — INSIDE the save bar's own middle
   space (Ali: "its empty and has only 3 buttons, we can fit easily inside"),
   not a separate card above it. flex:1 claims the room between Job Complete
   and the right-hand button group; its own content stays centred in that
   space. Same zone colours as everywhere else this system appears. Never
   shown for Normal — see the HTML comment above this element. */
/* A centred one-or-two-line paragraph, NOT a flex row (Ali, 2026-08-27). As a
   flex row the sentence broke into ragged column-chunks ("Shortage / is  Very /
   High  ...") the moment it wrapped, which it does at the Large text size. Block
   + text-align:center lets it wrap like normal prose; the icon rides inline. */
.jp-footer-hint-inline { flex:1; display:block; text-align:center; line-height:1.4;
    font-size:0.87rem; font-weight:600; margin:0 16px; padding:6px 14px; border-radius:var(--radius-sm); }
.jp-footer-hint-inline .ic { vertical-align:-0.15em; margin-right:5px; }
/* zone-low/zone-check darker than the shared --low/--neg-deep tokens (Ali,
   S153) — scoped to this one usage only, since the sentence here got longer
   and the plain (non-bold) portion needed more contrast than the token gives
   the shorter badges elsewhere (Palla's shortage figure, the alert legend
   swatches) already signed off with. zone-elevated uses the shared
   --caution-edge directly — no scoped override needed once that token itself
   was deepened (foundation.css, same session, same complaint). */
.jp-footer-hint-inline.zone-low      { background:var(--low-tint);     color:#6e3410; }
.jp-footer-hint-inline.zone-elevated { background:var(--caution-tint); color:var(--caution-edge); }
.jp-footer-hint-inline.zone-check    { background:var(--neg-tint);     color:#732621; }
.jp-footer-hint-inline b { font-weight:700; }

/* ── LOT SELECTOR (from demo; docked piece 4, Session 102) ──
   Ali's own option d: "floating box that fits the space" rather than a real
   .main-container page slot like Purchase/Sales/Proforma became — the picker
   opens FROM INSIDE those screens (and others), so becoming a page-slot peer
   would collide with whichever one is already open underneath. Stays
   position:fixed (still layers on top of everything, still a child
   interaction), but no longer inset:0 — it stops 20px short of the top/right/
   bottom edges and starts after the sidebar's rail width, so the rail stays
   visible instead of being covered outright. 74px = 54px rail (#appSidebar.rail,
   forced open in js_lot_selector.js while the picker is open) + 20px gap,
   matching the app's other 20px-off-the-rail screens. Border/radius mirror
   Purchase's own docked .purch-modal treatment (1px var(--line) + var(--radius),
   flush when maximised) rather than a heavier shadow, for the same "docked
   window" look. */
.ls-overlay { position:fixed;top:20px;left:calc(var(--rail-w) + 20px);right:20px;bottom:20px;background:var(--bg);z-index:9999;display:none;flex-direction:column;
    border:1px solid var(--line);border-radius:var(--radius);overflow:hidden; }
.ls-overlay.show { display:flex; }
/* Maximise: borrow the whole viewport, same idiom as .purch-entry.maxi. Never
   persisted — always cleared on close (js_lot_selector.js), or a later open
   would silently reopen full-bleed. */
.ls-overlay.maxi { top:0;left:0;right:0;bottom:0;border:0;border-radius:0; }
.ls-topbar { background:var(--brand);color:var(--brand-ink);padding:11px 20px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0; }
.ls-title  { font-weight:800;font-size:1.1rem; }
/* gap 6px, not the original 10px — piece 5 (Session 102): at the docked width
   (1404px content box, piece 4) the filter bar's 12 items genuinely didn't fit
   on one row and .ls-actions wrapped to its own line. Measured before touching
   anything: 12 items + 11 gaps needed 1576px against 1404px available, a 172px
   overflow. This alone reclaims 44px; the rest comes from the Date Preset
   width and the shortened Search box below. */
.ls-filters { padding:10px 18px;background:var(--head);border-bottom:2px solid var(--line);display:flex;gap:6px;flex-wrap:wrap;align-items:flex-end;flex-shrink:0; }
.ls-filter { display:flex;flex-direction:column;gap:3px; }
.ls-filter label { font-size:var(--fs-lbl);color:var(--ink-3);font-weight:700;text-transform:uppercase; }
/* A visible (not hover-only) shortcut hint next to a filter label, e.g. "(F2)"
   on From Date - lighter weight than the label itself so it reads as a hint,
   not part of the field name. Not dimmed with opacity too - that combined with
   the already-muted label color made it unreadable (Ali: "F2 is very faint"). */
.fld-hint { font-weight:400; }
.ls-filter input,.ls-filter select { padding:7px 10px;border:1.5px solid var(--line);border-radius:6px;font-size:var(--fs-sm);font-family:inherit;height:36px;background:var(--surface); }
.ls-search { flex:1;min-width:240px; }
.ls-search input { width:100%;border:2px solid var(--accent);border-radius:6px;padding:7px 12px;font-size:var(--fs-sm);font-family:inherit;height:36px; }
.ls-actions { display:flex;gap:8px;align-items:flex-end;margin-left:auto; }
/* Piece 2 (Session 102): the lot selector's own search box only — narrower than
   the shared .ls-search base above, which Purchase/Jobs/Stock/Sales also use, so
   this is scoped to #lotSelectorNew rather than edited in place. Frees width for
   the Date Preset + Source filters added alongside it (Ali: "we can reduce search
   bar and give date preset beside the date"). Narrowed further in piece 5 (200px
   -> 170px) once the label shortened to just "Search" — the full "searches
   date/party/item/lot/bags" explanation moved to a title tooltip instead, since
   it no longer fit next to Godown/Employee/Party/Item/Lot/Bags/the review chip
   in the same row at the docked (piece 4) width. */
/* flex-grow 1 (was 0): Ali — "increase size of search box so we dont get that
   empty space in between." The slack piece 5's row-fit fixes freed up was
   sitting as dead space before the actions group (.ls-actions's own
   margin-left:auto only pushes into space nothing else already claimed) —
   growing the search box into it removes the gap and gives the box real extra
   typing room, both at once. max-width so it can't run away on a wider window. */
#lotSelectorNew .ls-search { flex:1 1 170px;min-width:140px;max-width:420px; }
/* Source multi-select trigger — same footprint as the other .ls-filter selects
   (34px, matches the !important override below), styled as a button since it
   opens a checkbox panel rather than a native <select>. Computed-style diffed
   against #lsDatePreset/#lsPartyNew (Ali: "source font and design does not
   match the column fonts and design") — font/size/height/padding/border/
   background were already identical; the one real, visible difference was the
   dropdown arrow. A literal "▾" character sits inline right after the text at
   whatever size/weight that glyph happens to render at, where a native
   <select>'s arrow is OS-drawn, bolder, and pinned to the box's right edge.
   Flex + a proper ic-chevron-down SVG (matching every other icon in the app)
   closes both gaps at once: same visual weight as the app's other icons, and
   flush-right like a native select's own arrow. */
.ls-source-btn { height:34px;padding:0 8px;font-size:var(--fs-sm);font-family:inherit;border:1.5px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink);cursor:pointer;text-align:left;min-width:78px;white-space:nowrap;display:flex;align-items:center;justify-content:space-between;gap:6px; }
.ls-source-btn:hover { border-color:var(--accent); }
.ls-source-arrow { width:0.85rem;height:0.85rem;color:var(--ink-3); }
/* "✓ In Job (N)" review chip — piece 2d. Own class (not alerts-type-chip) so it
   stays in the lot selector's own naming domain rather than borrowing a screen-
   specific-sounding class from elsewhere. border-radius 5px, not the pill shape
   this originally shipped with — Ali: it "use[d] different box style" from
   Select All/Add to Job sitting right next to it; those two (and Columns/
   Refresh/Clear/Maximise in the topbar) all use the same 5px rectangle. */
.ls-chip-injob { height:34px;padding:0 14px;border:1.5px solid var(--line);border-radius:5px;background:var(--surface);color:var(--ink-3);font-size:var(--fs-sm);font-weight:600;cursor:pointer;font-family:inherit;white-space:nowrap; }
.ls-chip-injob:hover { border-color:var(--accent); color:var(--ink); }
.ls-chip-injob.active { background:var(--accent);color:#fff;border-color:var(--accent); }
.btn-ls-close { background:var(--neg);color:white;border:none;border-radius:6px;padding:7px 16px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit; }
/* padding 18px -> 0, piece 5: Ali screenshotted a visible gap on both the left
   (before the No column) and right (before the scrollbar) — asked directly why
   the table can't go edge to edge. The 10px try still left a visible margin;
   0 matches how .ls-topbar/.ls-filters already reach the panel's own border,
   the table area was the one exception. Bottom kept at 0 too for the same
   reason — no inconsistent edge. */
.ls-body { flex:1;overflow-y:auto;padding:0;overscroll-behavior:contain; }
/* Thinner scrollbar, same treatment .si6 (Sales invoice) already uses — Ali's
   own comparison ("cant we have different scroll bar like invoice which use
   less space"). Scoped to the picker only; the app-wide default is untouched. */
#lotSelectorNew *::-webkit-scrollbar { width:11px;height:11px; }
#lotSelectorNew *::-webkit-scrollbar-thumb { background:#cfd6de;border-radius:7px;border:3px solid transparent;background-clip:content-box; }
#lotSelectorNew *::-webkit-scrollbar-thumb:hover { background:#b6c0cb;background-clip:content-box; }
#lotSelectorNew *::-webkit-scrollbar-track { background:transparent; }
.ls-table-wrap { display:contents; }
.ls-table { width:100%;border-collapse:collapse;font-size:var(--fs-sm);font-variant-numeric:tabular-nums; }
.ls-table th { background:var(--head);color:var(--ink-2);padding:9px 10px;text-align:left;font-weight:600;font-size:var(--fs-lbl);border-bottom:2px solid var(--line);position:sticky;top:0;z-index:1; }
.ls-table th.r { text-align:right; }
.ls-table td { padding:9px 10px;border-bottom:1px solid var(--line-2);vertical-align:middle; }
.ls-table td.r { text-align:right; }
.ls-table tr:hover td { background:var(--brand-tint); }
.ls-table tbody tr.hl td { background:#f2d585 !important; color:#241f00; }
.ls-table tbody tr.hl td:first-child { box-shadow:inset 3px 0 0 #8a6d0b; }
.ls-table tr.added-row { opacity:0.35;pointer-events:none; }
/* Two-row grouped header (Available/Add -> Bags|Weight). Compact: both rows share the
   normal single-header height, same background, no borders. Row 2's sticky `top` is set
   in JS (lsSyncHeaderSticky) to row 1's measured height, so it sits flush — no magic px. */
.ls-table thead th { padding-top:3px;padding-bottom:3px; }
.ls-table thead tr:first-child th { top:0; z-index:3; }
.ls-table thead tr:nth-child(2) th { z-index:2;font-size:var(--fs-lbl);font-weight:600;padding-top:0;padding-bottom:4px; }
.ls-table th.ls-grp-hd { text-align:center; }
.ls-table th.ls-sub-hd { text-align:right; }
/* A little breathing space between the Available group and the Add group (no border). */
.ls-table .ls-col-adbags, .ls-table th.ls-grp-add { padding-left:24px; }
/* Reserved badge: whole Available-Bags cell is the hover target (cursor:help). */
.ls-table .ls-col-avbags.has-resv { cursor:help; }
/* Column squeeze — date/party/bags are short; party name is wide (off by default). */
/* ── Column widths (Session 101) ────────────────────────────────────────────
   MEASURED at the locked 1528/1536x695 pair before being set: the visible 15
   want 1189px of 1528, leaving 339px of slack. Left to itself the table spreads
   that slack evenly and inflates every column by ~20%, which is what made the
   left edge look loose and the right edge empty. So the narrow fixed-content
   columns are pinned tight here and the slack is deliberately pushed into the
   three groups Ali actually works in — AVAILABLE, ADD, ACTION. His words:
   "lot selector main part is - available - add - action. dont cramp it."
   Re-measure before changing any of these; do not tune them by eye. */
.ls-table .ls-col-no    { width:38px;white-space:nowrap;color:var(--ink-3);text-align:center; }
/* Numbered by CSS counter, not by the renderer: rows are filtered by setting
   display:none, and a display:none element does not increment a counter — so
   the numbering re-flows automatically on search/filter/sort and can never go
   stale. A renderer-assigned index would also break lsRerenderRow(), which
   redraws ONE row in place and has no idea what position it sits at. */
#lsBodyNew { counter-reset: lsrow; }
#lsBodyNew tr { counter-increment: lsrow; }
#lsBodyNew .ls-col-no::before { content: counter(lsrow); }
.ls-table .ls-col-date  { width:74px;white-space:nowrap;text-align:center; }
.ls-table .ls-col-gdn   { width:52px;white-space:nowrap; }
.ls-table .ls-col-emp   { width:52px;white-space:nowrap; }
.ls-table .ls-col-source{ width:76px;white-space:nowrap;text-align:center; }
.ls-table .ls-col-remarks{ max-width:140px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink-2); }
.ls-table .ls-col-party { width:52px;text-align:center; }
.ls-table .ls-col-item  { width:72px;text-align:center; }
.ls-table .ls-col-lot   { width:52px;text-align:center; }
.ls-table .ls-col-bags  { width:52px;white-space:nowrap; }
.ls-table .ls-col-rate  { width:72px;white-space:nowrap; }
.ls-table .ls-col-availwt { width:92px;white-space:nowrap; }
.ls-table .ls-col-total { width:92px;white-space:nowrap; }
.ls-table .ls-col-pname { max-width:134px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
/* The working end — given the slack rather than sharing it evenly. adwt was
   78px against a 108px natural need, which is J30: 1230.00 clipped inside it. */
.ls-table .ls-col-avbags { width:86px; }
.ls-table .ls-col-avwt   { width:104px; }
.ls-table .ls-col-adbags { width:88px; }
.ls-table .ls-col-adwt   { width:116px; }
.ls-bags-input { border:1.5px solid var(--line);border-radius:5px;padding:5px 7px;font-size:var(--fs-sm);font-family:inherit;text-align:right;width:72px; }
.ls-wt-input   { border:1.5px solid var(--line);border-radius:5px;padding:5px 7px;font-size:var(--fs-sm);font-family:inherit;text-align:right;width:100px; }
/* J30 (Ali: "weight feild does not have enough space and is clipped"): the input
   carried an inline style="width:72px" that beat this rule, so 1230.00 clipped.
   The inline width is gone; 100px holds a 5-digit weight with 2 decimals and the
   thousands comma (12,345.67) inside .ls-col-adwt's measured 116px cell. */
.ls-wt-input.warn { border-color:var(--warn);background:var(--warn-tint); }
.btn-ls-add { background:var(--surface);color:var(--accent-2);border:1.5px solid var(--accent-2);border-radius:5px;padding:5px 14px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ls-add:hover { background:var(--accent);color:white;border-color:var(--accent); }
.btn-ls-add.added { background:var(--pos-deep);color:white;border-color:var(--pos-deep);cursor:default; }
.btn-ls-selall { background:var(--surface);color:var(--accent-2);border:1.5px solid var(--accent-2);border-radius:5px;padding:5px 14px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ls-selall:hover { background:var(--accent);color:white;border-color:var(--accent); }
.btn-ls-unselall { background:var(--surface);color:var(--warn-deep);border:1.5px solid var(--warn-deep);border-radius:5px;padding:5px 14px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ls-unselall:hover { background:var(--warn);color:white;border-color:var(--warn); }
.btn-ls-select { background:var(--surface);color:var(--accent-2);border:1.5px solid var(--accent-2);border-radius:5px;padding:5px 12px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ls-select:hover { background:var(--accent);color:white;border-color:var(--accent); }
.btn-ls-unselect { background:var(--surface);color:var(--neg);border:1.5px solid var(--neg);border-radius:5px;padding:5px 12px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ls-unselect:hover { background:var(--neg);color:white; }
.btn-ls-addtojob { background:var(--pos-deep);color:white;border:none;border-radius:5px;padding:5px 16px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ls-addtojob:hover { filter:brightness(0.92); }
.btn-ls-addtojob:disabled { background:var(--ink-3);cursor:not-allowed; }
.btn-ls-cancel { background:var(--surface);color:var(--neg);border:1.5px solid var(--neg);border-radius:5px;padding:5px 14px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ls-cancel:hover { background:var(--neg);color:white; }
.btn-ls-clearfilters,.btn-ls-cols,.btn-ls-maximise { background:var(--surface);color:var(--ink-2);border:1.5px solid var(--line);border-radius:5px;padding:5px 12px;font-size:var(--fs-sm);font-weight:600;cursor:pointer;font-family:inherit; }
.btn-ls-clearfilters:hover,.btn-ls-cols:hover,.btn-ls-maximise:hover { background:var(--brand-tint);color:var(--ink); }
.btn-ls-refresh { background:var(--surface);color:var(--accent-2);border:1.5px solid var(--line);border-radius:5px;padding:5px 10px;font-size:var(--fs-sm);cursor:pointer;font-family:inherit; }
.btn-ls-refresh:hover { background:var(--accent-tint); }
.ls-frac { font-size:var(--fs-lbl);color:var(--ink-3);white-space:nowrap;margin-left:4px; }
.ls-frac-sel { color:var(--pos-deep);font-weight:600; }
.ls-frac-sep { font-size:var(--fs-sm);color:var(--ink-3);margin:0 3px; }
.ls-avail-input { border:1px solid var(--line);border-radius:4px;padding:4px 5px;font-size:var(--fs-sm);font-family:inherit;text-align:right;width:58px;background:var(--head);color:var(--ink-3);cursor:default; }
.ls-row-selected td { background:var(--pos-tint) !important; }
.ls-row-injob td { background:var(--head) !important; }
.ls-row-injob:not(.ls-injob-editing) { opacity:0.65; }
.ls-injob-badge { font-size:var(--fs-sm);color:var(--pos-deep);font-weight:700;padding:3px 8px;border:1px solid var(--pos);border-radius:4px;background:var(--pos-tint); }
.ls-resv-dot { color:var(--warn-deep);font-weight:700;cursor:inherit;font-size:var(--fs-sm);line-height:1;white-space:nowrap; }
.btn-ls-edit-injob { background:var(--surface);color:var(--warn-deep);border:1.5px solid var(--warn-deep);border-radius:5px;padding:5px 10px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit; }
.btn-ls-edit-injob:hover { background:var(--warn-deep);color:#fff;border-color:var(--warn-deep); }
.btn-ls-remove-injob { background:var(--surface);color:var(--neg);border:1.5px solid var(--neg);border-radius:5px;padding:5px 10px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit;margin-left:4px; }
.btn-ls-remove-injob:hover { background:var(--neg);color:white; }
.btn-ls-remove-injob:disabled { opacity:0.4;cursor:not-allowed; }
.ls-injob-editing td { background:var(--warn-tint) !important;border-top:1.5px solid var(--warn) !important;border-bottom:1.5px solid var(--warn) !important; }
.ls-injob-editing .ls-injob-bags-edit,
.ls-injob-editing .ls-injob-wt-edit { border:2px solid var(--warn) !important;background:var(--warn-tint);box-shadow:0 0 0 2px rgba(180,117,22,0.25); }
.ls-injob-editing .btn-ls-edit-injob,
.ls-injob-editing .btn-ls-cancel-edit { opacity:1 !important; }
.btn-outline-sm { background:var(--surface);color:var(--ink-2);border:1px solid var(--line);border-radius:5px;padding:4px 10px;font-size:var(--fs-sm);font-weight:600;cursor:pointer;font-family:inherit; }
.btn-outline-sm:hover { background:var(--head); }
/* padding 11px -> 7px (top/bottom), piece 5: Ali flagged the footer as taller
   than it needed to be. The Done button now matches the other lot-selector
   buttons' shared 34px height instead of sizing itself from its own padding
   (see the rule above) — between the two, this is a real row of table height
   given back, not just cosmetic. */
.ls-footer { padding:7px 18px;background:var(--head);border-top:2px solid var(--line);display:flex;align-items:center;flex-shrink:0; }
.ls-sel-info { font-size:var(--fs-sm);color:var(--accent-2);font-weight:700; }

/* JOB TYPE BADGES */
.job-type-palla    { background:#d6eaf8;color:#1a5276; }
.job-type-dc       { background:#fde8d8;color:#784212; }
.job-type-grinding { background:#e8daef;color:#512e5f; }

/* r = right-align */
.r { text-align:right; }

/* ── VIEW TOGGLE (Input Lots: List <-> Grouped by date, S144) — neutral,
   it's neither an add nor a destructive action, just a display switch. */
/* S144 — RFS carries the same columns as Output now, but its last column holds
   real Add-to-Stock buttons (and an Add All / Remove All pair in the footer)
   rather than two icon buttons. Under auto layout that wide content outbid the
   specified widths and squeezed every column left of it, so the table missed
   Output's alignment by ~64px. Fixed layout makes the declared widths binding
   and hands the leftover to the action column; the footer pair stacks so it
   lives inside that leftover instead of forcing the table wider. */
.main-table.tbl-rfs { table-layout:fixed; }
/* Ali: "remove avg weight from rfs and give proper space to add stock, remove
   and also to add all and remove all". Avg Wt's 81px went to Stock Action, so
   an in-stock row's "✓ In Stock" + "Remove" pair and the footer's Add All /
   Remove All now sit side by side at full size instead of being squeezed. */
.tbl-rfs .btn-add-stock, .tbl-rfs .btn-in-stock, .tbl-rfs .btn-remove-stock { margin-left:6px; }
.tbl-rfs .btn-add-stock:first-child, .tbl-rfs .btn-in-stock:first-child { margin-left:0; }
/* Add All and Remove All come from two different button styles and so are 26px
   and 30px tall; without this they sit on the text baseline and read as 3px
   out of line with each other. */
#rfsFoot button { vertical-align:middle; }

/* RFS header: same idea as the other bars, but this box has no Add/Clear pair,
   so the stat block runs from the Bags track through to a trailing slot for
   IN STOCK over the Stock Action column. */
#accBarRfs { grid-template-columns:260px 200px 110px 70px 110px 118px 132px 1fr 20px; }
#accBarRfs > .acc-title-block { grid-column:1; }
#accBarRfs > .acc-stats-row   { grid-column:3 / span 6;display:grid;
    grid-template-columns:110px 70px 110px 118px 132px auto;gap:0;padding:0; }
#accBarRfs > .acc-toggle      { grid-column:9; }
#rfsStatsNew > .acc-stat { align-items:flex-end;padding-right:9px; }
#rfsStatsNew > .acc-stat:nth-child(1) { grid-column:2; }  /* Bags       */
#rfsStatsNew > .acc-stat:nth-child(2) { grid-column:3; }  /* Final Rate */
#rfsStatsNew > .acc-stat:nth-child(3) { grid-column:4; }  /* Weight     */
#rfsStatsNew > .acc-stat:nth-child(4) { grid-column:5; }  /* Total      */
/* IN STOCK is right-aligned to the stat block's own right edge (1469 - the
   toggle stops it going further), and the Stock Action buttons below are inset
   to end on that same line, so the header figure sits over its buttons. */
#rfsStatsNew > .acc-stat:nth-child(5) { grid-column:6;align-items:flex-end;padding:0; } /* In Stock */
#rfsFoot button { padding-left:8px;padding-right:8px; }

.btn-view-toggle { background:var(--surface);color:var(--ink-2);border:1.5px solid var(--line);border-radius:6px;padding:4px 10px;font-size:0.78em;font-weight:700;cursor:pointer;font-family:inherit;white-space:nowrap; }
.btn-view-toggle:hover { background:var(--accent-tint);color:var(--accent-2);border-color:var(--accent); }

/* ── DANGER OUTLINE BUTTON (destructive actions: Clear All, Remove All) ── */
.btn-danger-outline { background:var(--surface);color:var(--neg);border:1.5px solid var(--neg);border-radius:6px;padding:6px 12px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit;white-space:nowrap; }
/* S139 — the hover was unguarded and there was no disabled state at all, so a
   disabled one still lit up solid red under the cursor and looked pressable.
   Same recipe .btn-remove-stock:disabled already uses, one line up the file. */
.btn-danger-outline:hover:not(:disabled) { background:var(--neg);color:white; }
.btn-danger-outline:disabled { color:var(--ink-3);border-color:var(--line);cursor:default;opacity:.55; }

/* Source tags — every screen (Stock/Sales/Proforma) shares these classes via
   srcTag() in js_core.js. SUPERSEDES the 2026-07-25 "four distinct colours"
   lock: CM112 (Ali, 2026-09-03) - "words are enough... text already tell
   truth" - one plain colour for every source word, everywhere. The word
   (PURCHASE/APMC/GST/PALLA/DC/PDR/MERGE) carries the difference, not colour,
   so a future new job/purchase type needs a new WORD, never a new colour
   decision. Per-class rules (.src-purchase/.src-palla/.src-dc/.src-pdr/
   .src-merge) are kept only as hooks for anything non-colour that still
   wants to target one source - none currently do. */
.src-tag { font-size:var(--fs-sm);font-weight:700;white-space:nowrap;color:var(--ink); }

/* Lot selector sort */
.ls-sortable { cursor:pointer; user-select:none; }
.ls-sortable:hover { background:var(--brand-tint); }
.ls-sort-icon { font-size:var(--fs-sm); color:var(--accent-2); }
/* Persistent weight warning */
.ls-wt-warn { color:var(--warn-deep); font-size:var(--fs-sm); margin-right:3px; }
/* Edit mode cancel button */
.btn-ls-cancel-edit { background:var(--surface);color:var(--ink-2);border:1.5px solid var(--line);border-radius:5px;padding:5px 10px;font-size:var(--fs-sm);font-weight:700;cursor:pointer;font-family:inherit;margin-left:4px; }
.btn-ls-cancel-edit:hover { background:var(--head); }
/* Cursor not-allowed on disabled lot selector buttons */
.btn-ls-edit-injob:disabled,.btn-ls-remove-injob:disabled,.btn-ls-select:disabled,.btn-ls-unselect:disabled,#lsAddToJobBtn:disabled,#lsSelAllBtn:disabled { cursor:not-allowed; opacity:0.45; }
/* Uniform top bar buttons */
.btn-ls-refresh,.btn-ls-clearfilters,.btn-ls-cols,.btn-ls-cancel,.btn-ls-maximise { height:34px;padding:0 14px;font-size:var(--fs-sm);font-weight:600;border-radius:6px;cursor:pointer;font-family:inherit;white-space:nowrap; }
/* Uniform filter inputs - scoped to the lot selector's own overlay. Unscoped,
   this !important leaked into every OTHER screen sharing the .ls-filter class
   (Purchase, Jobs, Stock...) and forced their filter fields to 34px, taller
   than their own Search box - Ali: "search field look smaller ... than other
   fields", when it was really every OTHER field that was too tall. */
#lotSelectorNew .ls-filter input, #lotSelectorNew .ls-filter select { height:34px !important;padding:0 8px !important;font-size:var(--fs-sm) !important;border:1.5px solid var(--line);border-radius:6px;font-family:inherit;background:var(--surface); }
/* Uniform Select All and Add to Job height */
.btn-ls-selall,.btn-ls-unselall,.btn-ls-addtojob { height:34px; }

/* ── ALERT BADGE on nav tab ─────────────────────────────── */
.alert-badge {
    display:inline-block;
    /* --neg, the app's own danger red. Was a raw #e74c3c that appeared in no
       palette at all (2026-08-28). */
    background:var(--neg);
    color:white;
    font-size:0.72em;
    font-weight:700;
    border-radius:10px;
    padding:1px 6px;
    margin-left:6px;
    vertical-align:middle;
    min-width:18px;
    text-align:center;
}

/* ── ALERTS PANEL on dashboard ──────────────────────────── */
/* ── ALERT PANEL / CARD: the --warn family ──
   An alert IS a warning, so this whole block uses the semantic warn tokens
   instead of the legacy Flat-UI ambers (#f39c12 / #8a5c00 / #fff8e1 / #fff3cd).
   CONTRAST, measured: white on --warn #b47516 is only 3.82:1 and FAILS AA, so
   nothing solid-warn carries white text - filled elements use --warn-deep
   (5.67:1). --warn-deep on --warn-tint is 4.87:1 and --ink-2 on --warn-tint is
   5.06:1, so both are safe for text on the tinted grounds. */
.alerts-panel {
    background:var(--surface);
    border:2px solid var(--warn);
    border-radius:var(--radius);
    overflow:hidden;
    box-shadow:var(--shadow);
}
.alerts-panel-header {
    background:var(--warn-tint);
    padding:10px 18px;
    display:flex;
    align-items:center;
    gap:10px;
    border-bottom:1px solid var(--warn);
}
.alerts-panel-title { font-weight:700;font-size:1em;color:var(--warn-deep); flex:1; }
.alerts-panel-count {
    background:var(--warn-deep);
    color:#fff;
    font-size:0.8em;
    font-weight:700;
    border-radius:var(--radius-pill);
    padding:2px 10px;
}
.alerts-panel-body { padding:10px 14px;display:flex;flex-direction:column;gap:8px; }
/* A23 (S204): the dashboard panel is one row per alert group, not the cards.
   Rows are real buttons (keyboard reachable) drawn as list rows; the count
   reuses .alerts-panel-count so the header and the rows agree on the pill. */
.alerts-panel-rows { gap:4px; }
.alerts-panel-row { display:flex;align-items:center;gap:12px;width:100%;text-align:left;
    background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-sm);
    padding:7px 12px;cursor:pointer;font:inherit;font-size:0.92em;color:var(--ink); }
.alerts-panel-row:hover, .alerts-panel-row:focus-visible { background:var(--warn-tint);border-color:var(--warn);outline:none; }
.alerts-panel-row-name { flex:1;font-weight:600; }
.alerts-panel-row-n { color:var(--ink-3);font-size:0.9em; }
.alerts-panel-row.is-fault .alerts-panel-row-n { color:var(--neg); }

/* ── ALERT CARD ─────────────────────────────────────────────────────────────
   S151 rebuild. The card is a BLOCK, not a two-column flex. The right-hand button
   column worked on a one-row card and stranded the buttons in dead space beside a
   six-row one - Ali: "for single row its fine, but multirow, its look bad" - and
   it was also eating ~160px of the width the eight-column Before/Now table wants.
   Actions now sit in a bottom row (.ac-actions), which is the same place on every
   card whatever its height.

   GROUND, NOT COLOUR, separates the three levels now: white box -> sand card ->
   white table. The card's own coloured left bar is gone, because .alerts-box
   already carries one 16px to its left saying the same thing and two parallel
   bars read as a mistake rather than as a category. The card is no longer painted
   --warn-tint either: with the title, the table header and the changed cells all
   shouting at once, nothing on the card was louder than anything else.
   Literal #f7f4ec, not a token: there is no sand between --bg and --head in this
   theme (checked), and inventing one for a single surface is a token nobody else
   would use - the same call already made for .ac-tbl td.ac-tag's #faf8f3. */
.alert-card {
    background:#f7f4ec;
    border:1px solid var(--line);
    border-radius:var(--radius-sm);
    padding:12px 14px;
    /* Capped as a READING width. Once .main-container became full-width these
       stretched to the whole 1314px track, which pushed "Go to Job" about a
       thousand pixels away from the text it belongs to. Kept now that the buttons
       have moved below: eight columns still read comfortably at this width. */
    max-width:1100px;
}
.alert-tag {
    background:var(--surface);
    color:var(--ink-2);
    border:1px solid var(--line);
    border-radius:4px;
    padding:1px 7px;
    font-size:0.8em;
    font-weight:600;
}

/* COLOUR MEANS ALARM, INK MEANS IDENTITY (Ali, S151: "the header line keep it red
   color" on the lot-balance card). A title that STATES THE PROBLEM is coloured by
   its box kind; a title that merely NAMES the document it happened to - "Palla
   PL-002", "Invoice KB-001" - stays ink, because the box header directly above it
   already said the category in colour and repeating it in red is what buried the
   one figure the card exists to show. */
/* TITLES CARRY THE BOX'S COLOUR (Ali, S151: "i dont see any red color text any
   where"). S150 shipped ink titles on the two purchase-change cards, on the rule
   that colour means alarm and ink means identity - measured, and he was right that
   the result had NO red text on the Jobs box at all beyond one tinted figure. A
   fault card now reads red, a risk card amber, whatever its title says. */
.ac-title { display:flex;align-items:baseline;gap:10px;margin-bottom:9px; }
.ac-title b { font-size:0.95em;font-weight:800;color:var(--neg-deep); }
.alerts-box.risk .ac-title b { color:var(--warn-deep); }
.ac-badge {
    background:var(--head);color:var(--ink-2);border-radius:var(--radius-pill);
    padding:1px 9px;font-size:0.72em;font-weight:700;
}
/* The closing sentence and the buttons SHARE one row (Ali, S151: "button use very
   much vertical space, can we keep the bottom text fixed (and auto wrapped) and
   side use the buttons?"). An action strip of its own cost a hairline plus ~37px
   on every card to hold two buttons. min-width:0 on the text column is what lets
   the sentence WRAP rather than push the buttons out of the card. */
.ac-foot {
    display:flex;align-items:center;gap:16px;
    border-top:1px solid var(--line);margin-top:9px;padding-top:9px;
}
.ac-msg { flex:1;min-width:0;font-size:0.83em;color:var(--ink-2);line-height:1.5; }
.ac-actions { flex-shrink:0;display:flex;gap:8px; }

/* ── The BEFORE / NOW block (S150) ──────────────────────────────────────────
   Same construction as the split-lot allocator's .wa-idwrap: a grid whose two
   child rows are display:contents, so every field lands in a SHARED max-content
   column and the two lines align vertically. Both lines must emit the same cells
   in the same order or everything after the gap shifts out of alignment. */
/* ONE bordered table, not eight loose pills per line (Ali: "can we give proper box
   … give proper design"). Same construction as the split-lot allocator's .wa-tbl -
   brand header bar, hairline rows, tabular numerals - so the app has one small-table
   idiom rather than three. It sits on --surface, not the card's warn tint, so the
   figures read as data lifted out of the alert rather than more alert text. */
/* Scroll container of its own. The card clips its corners, and this same table now
   also renders in the DASHBOARD panel (.alerts-panel-body), which is a good deal
   narrower than the Alerts page - eight columns there must scroll rather than
   burst the panel. Same reasoning as .alerts-table-scroll on the History table. */
.ac-tblwrap { overflow-x:auto;margin:8px 0 9px; }
.ac-tbl { border-collapse:separate;border-spacing:0;font-size:0.82em;width:100%;
          background:var(--surface);border:1px solid var(--line);
          border-radius:var(--radius-sm);overflow:hidden; }
/* SAND GREY, not brand green and not red. Ali, S151: "green is something safe and
   does not look like alert" - correct, and he then chose sand over both. Red was
   tried in between and was worse: a solid red bar directly above the one tinted
   red cell meant the changed figure had to compete with its own header. Sand is
   chrome, which is the point - it is also exactly what .tbl thead th uses in
   foundation.css, so this stops being a third table dialect and becomes the app's
   own table standard. --line is the sand-grey already in the palette; no new token. */
.ac-tbl th { background:var(--line);color:var(--ink);
             font-weight:700;font-size:0.8em;letter-spacing:0.05em;
             text-transform:uppercase;text-align:right;padding:5px 10px;
             white-space:nowrap; }
.ac-tbl th.ac-t { text-align:left; }
.ac-tbl td { padding:5px 10px;text-align:right;white-space:nowrap;
             font-variant-numeric:tabular-nums;border-top:1px solid var(--line); }
.ac-tbl td.ac-t { text-align:left; }
.ac-tbl tbody tr:first-child td { border-top:none; }
/* The row label column - "Before" / "Now" - reads as a heading for its row, so it
   gets the header's own quiet treatment rather than looking like a ninth figure. */
/* Literal, not a token: there is no --surface-2 in this theme (checked), and
   inventing one for a single label column would be a token nobody else uses. */
.ac-tbl td.ac-tag { text-align:left;font-size:0.86em;font-weight:800;
                    text-transform:uppercase;letter-spacing:0.05em;
                    color:var(--ink-3);background:#faf8f3; }
/* BEFORE recedes, NOW is the live reading. The weight difference alone carries the
   hierarchy - no second background, which would fight the changed-cell tint. */
.ac-r-old td { color:var(--ink-3);font-weight:500; }
.ac-r-new td { color:var(--ink);font-weight:700; }
.ac-r-new td.ac-tag { color:var(--ink-2); }
/* ONLY genuinely changed figures are marked - the whole point of two rows is that
   the eye lands on the delta instead of re-reading five identical identity fields.
   On a one-row FACT table the same class marks the last cell, which is the
   alarming number (the age, the shortfall, the new rate), so the eye lands in the
   same place on every card in the screen whatever kind of alert it is. */
.ac-r-new td.ac-chg { background:var(--neg-tint,#fdeceb);color:var(--neg); }
.ac-r-old td.ac-chg { color:var(--neg); }
/* A risk box is amber end to end - its left edge, its count badge, its title, and
   so also its alarming cell. Red is reserved for the four FAULT groups. */
.alerts-box.risk .ac-r-new td.ac-chg { background:var(--warn-tint);color:var(--warn-deep); }
/* The divider between two Before/Now pairs in one sales table. Sits on the BEFORE
   row of a later pair so it reads as the top edge of that lot's block, not as an
   underline on the one above. */
.ac-tbl tbody tr.ac-pair td { border-top:2px solid var(--line); }
.ac-when { font-size:0.72em;font-weight:600;color:var(--ink-3);margin-left:auto;
           text-transform:none;letter-spacing:0;white-space:nowrap; }
/* Both status pills share one line and wrap as a pair (Ali asked which; one line
   - they are read as a single thought).
   NO BORDER (S151). They were boxed in S150, which was right when the card was one
   flat tinted block - but beside a real bordered button they read as two more
   buttons. Nothing in the body of the card is clickable now, so nothing in it
   carries a border. Scoped to .alert-card-status; no other alarm pill changes. */
.alert-card-status { display:flex;gap:7px;flex-wrap:wrap;margin-top:9px; }
.alert-card-status .alert-tag {
    background:var(--warn-tint);
    border:none;
    color:var(--warn-deep);
    border-radius:4px;
    padding:3px 9px;
    font-size:0.78em;
    font-weight:700;
}
/* --attn, not --accent: superseded by the button spec sec 8 - Go-to-Job was
   riding on the user-switchable accent by accident (chosen by elimination
   pre-spec), which ties an attention cue to a personal preset.

   S151 splits this into two RANKED variants, because the two buttons had been
   visually identical while one of them navigates and the other rewrites a closed
   job and can move an already-issued invoice's Cost Rate. .btn-alert-goto stays
   the BASE class on both - alertsKeyHandler finds a row's buttons through it, so
   dropping it in favour of the variant alone would silently kill the left/right
   arrows and Enter. */
.btn-alert-goto {
    background:transparent;
    border-radius:var(--radius-sm);
    padding:7px 14px;
    font-size:0.85em;
    font-weight:700;
    cursor:pointer;
    font-family:inherit;
    white-space:nowrap;
    border:1.5px solid var(--attn);
    color:var(--attn);
}
/* The SAFE action, and the one Enter fires - so it is the filled one. Solid by
   default rather than outline->fill on hover: it is the PRIMARY action of the card,
   and the button spec's outline default is for secondary controls (sec 6).

   --accent, NOT --attn, and this deliberately reverses the S80 note above for this
   button only. That decision ("an attention cue must not ride on a personal
   preset") was made when Go-to-Job was an OUTLINE button - a thin gold rule. S151
   made it a large solid block, and Ali immediately read a filled gold slab that
   ignores his Ground/Accent picker as a bug: "main action button (in gold color)
   does not change with accent color change". A PRIMARY ACTION is not an attention
   cue; it is the same role .btn-accent fills everywhere else in the app, and every
   accent preset is AA-verified against white text (foundation.css). The
   attention-cue reasoning still stands for anything that stays an outline. */
.btn-ac-go { background:var(--accent);color:#fff;border-color:var(--accent); }
.btn-ac-go:hover { background:var(--accent-2);border-color:var(--accent-2); }
/* The CONSEQUENTIAL action. --warn-deep is the app's Edit colour everywhere else
   (button spec: "EDIT IS --warn-deep, NEVER --accent"), and reopening and
   re-closing a job to re-cost it is an edit - so this needs no new colour either.
   Outline, so it reads as the deliberate second choice next to the filled one. */
.btn-ac-careful { background:transparent;color:var(--warn-deep);border-color:var(--warn-deep); }
.btn-ac-careful:hover { background:var(--warn-deep);color:#fff; }

/* ── LOT CHANGED BADGE in input lots table ────────────────
   NOTE: this one renders on the JOBS screen, not Alerts. Converted here because
   it is part of the same alert vocabulary and was carrying the same legacy
   ambers; Jobs' own conversion should leave it alone. */
.lot-changed-badge {
    display:inline-block;
    background:var(--warn-tint);
    color:var(--warn-deep);
    border:1px solid var(--warn);
    border-radius:4px;
    padding:1px 6px;
    font-size:0.75em;
    font-weight:700;
    margin-left:6px;
    vertical-align:middle;
}

/* ── ALERTS PAGE ─────────────────────────────────────────── */
/* Active/History tabs deliberately reuse the PURCHASE sub-tab pattern rather
   than inventing one: sand ground, rust text and a 3px rust underline when
   active. Two screens, one idea of what a tab is. */
.alerts-page-tabs { display:flex;gap:4px;margin-bottom:18px;border-bottom:1px solid var(--line); }
.alerts-page-tab {
    background:var(--head);color:var(--ink-3);border:none;
    border-radius:var(--radius-sm) var(--radius-sm) 0 0;padding:8px 20px;
    font-size:0.9em;font-weight:700;cursor:pointer;font-family:inherit;
    border-bottom:3px solid transparent;transition:background 0.15s, color 0.15s;
    display:inline-flex;align-items:center;gap:7px;   /* the icon needs air from its label */
}
.alerts-page-tab.active { background:var(--surface);color:var(--accent-2);border-bottom:3px solid var(--accent); }
.alerts-page-tab:hover:not(.active) { background:var(--brand-tint);color:var(--ink); }
.alerts-page-panel { display:block; }
/* "No history yet" / "No active alerts" is GOOD news here, so it keeps a
   positive colour - but --pos-deep, since --pos is the bright dot colour and
   fails AA as text. */
.alerts-empty {
    text-align:center;padding:40px;color:var(--pos-deep);font-size:1em;
    font-weight:600;background:var(--surface);border:1.5px solid var(--line);
    border-radius:var(--radius);
}
.alerts-type-filter { display:flex;align-items:center;gap:8px;margin-bottom:16px; }
.alerts-filter-label { font-size:var(--fs-sm);color:var(--ink-3);font-weight:600; }
.alerts-type-chip {
    padding:5px 16px;border:1.5px solid var(--line);background:var(--surface);color:var(--ink-3);
    border-radius:var(--radius-pill);font-size:var(--fs-sm);font-weight:600;cursor:pointer;
    font-family:inherit;transition:all 0.12s;
}
.alerts-type-chip:hover { border-color:var(--accent); color:var(--ink); }
.alerts-type-chip.active { background:var(--accent);color:#fff;border-color:var(--accent); }
/* Amber, deliberately not the brand green Purchase/Stock use - green reads as
   "all fine" and this screen exists to say the opposite. Its own rounded
   corners: there is no wrapping .pur-panel any more to clip them for it. */
.alerts-topbar {
    background:var(--warn-deep);padding:8px 14px;display:flex;align-items:center;
    gap:10px;border-radius:var(--radius);margin-bottom:16px;
}
.alerts-topbar .pur-view-title { color:var(--brand-ink); }
.alerts-topbar-note { color:rgba(255,255,255,0.85);font-size:0.8em;margin-left:auto; }

/* One raised box PER GROUP, not one shared sheet with lines inside it. Ali:
   "all tabs shold have there own box and no on[e] box... 5 floting box in all."
   Same visual language as the dashboard's .card/.stat-card - a coloured left
   edge for category, exactly like .stat-card already does elsewhere. */
.alerts-box {
    background:var(--surface);border-radius:var(--radius);
    box-shadow:0 2px 10px rgba(0,0,0,0.08);margin-bottom:14px;
    border-left:5px solid var(--warn-deep);overflow:hidden;
}
/* Purchase-change groups are a FAULT - something already wrong, needing a
   decision, never clearing itself - so they get the danger edge, not the
   amber one every day-count group uses. Two tones, not five: the distinction
   that matters is fault-vs-risk, not which specific risk. */
.alerts-box.fault { border-left-color:var(--neg); }
.alerts-box-head {
    display:flex;align-items:center;gap:8px;padding:14px 16px;
    font-size:0.95em;font-weight:700;color:var(--ink-2);text-transform:uppercase;
    letter-spacing:0.03em;cursor:pointer;user-select:none;
}
.alerts-box-head:hover { background:var(--warn-tint); color:var(--ink); }
.alerts-box.fault .alerts-box-head:hover { background:var(--neg-tint); }
.alerts-caret { font-size:0.85em;color:var(--ink-3);width:11px;flex-shrink:0; }
/* Age and money sit beside the count so a COLLAPSED box still says whether it is
   worth opening. Collapsing must hide the CARDS, never the reason to look. */
.alerts-section-meta {
    color:var(--ink-3);font-weight:600;text-transform:none;letter-spacing:0;
    font-size:0.85em;
}
.alerts-section-meta::before { content:'·';margin-right:8px;color:var(--line); }
.alerts-box-body { padding:2px 16px 14px; }

/* Numbered row: 1 is the oldest and leads. The number sits OUTSIDE the card so it
   does not compete with the card's own title. */
.alert-row { display:flex;align-items:flex-start;gap:10px;margin-bottom:8px; }
.alert-row:last-child { margin-bottom:0; }
.alert-num {
    flex-shrink:0;min-width:22px;margin-top:11px;text-align:right;
    font-size:0.82em;font-weight:700;color:var(--ink-3);font-variant-numeric:tabular-nums;
}
.alert-row-body { flex:1;min-width:0; }
.alert-row-body .alert-card { margin-bottom:0; }
/* BA73 row cursor: .alert-row is a bare flex wrapper with no paint of its
   own - the visible surface is .alert-card inside it, so the highlight has
   to land there, same reasoning as the "must beat the zebra" td overrides
   on the table-based list cursors (.pur-view-table / .jobs-view-table /
   #stockTable), just for a different reason (nothing to beat, nothing to
   paint through).

   NO FILL, unlike every other .acc-cur in the app - Ali, S149: "get yellow row
   focus, it need focus, but remove color because yellow above orange looks bad."
   The shared cursor's amber (#f2d585) is designed to land on a WHITE table row;
   .alert-card is already painted --warn-tint (orange), so amber-on-orange read
   as muddy rather than as a cursor. The ring alone carries it here: a 2px inset
   outline plus a thick left bar, which is a stronger "you are here" than the
   fill was and stays legible whatever tint the card underneath is using. */
/* --ink (#222a25), NOT --ink-2 (#5d675f). Ali: "green border looks odd, either
   give black or suggest." He was reading the palette correctly - this is a
   forest theme, so --ink-2 is a GREEN-tinted grey, and at 2px on an orange card
   it read as a green frame rather than as a cursor. --ink is the near-black at
   the top of the same ramp: still the theme's own ink, no new colour introduced,
   but it reads black. */
.alert-row.acc-cur .alert-card{
    box-shadow:inset 0 0 0 2px var(--ink), inset 5px 0 0 var(--ink);
}
.alert-row.acc-cur .alert-num{ font-weight:700;color:var(--ink); }
/* THE BOX HEADER IS A CURSOR STOP TOO (S150). Ali: "alert box get focus but no
   visual confirmation" - and it genuinely had none, because a COLLAPSED box has no
   .alert-row in the DOM, so the nav list came back empty and the arrows did nothing
   at all on a screen where that box was the only thing there.

   RING ONLY, no fill - the same rule the card cursor follows. A tint here would put
   straight back the yellow Ali had just had removed, and two cursor stops on one
   screen must not speak two different visual languages.

   !important on the background for the SAME reason .alert-row.acc-cur needs it:
   the global `.acc-cur{background:#ffe9a8 !important}` (~line 5141) matches this
   element too, and nothing weaker can answer it. Caught by screenshotting the
   collapsed box, not by reading this file - the rule below looked complete. */
/* THE RING MUST FOLLOW THE BOX'S CORNERS (Ali, S151: "black border is rectangular
   and does not meet roundish corners"). Measured: .alerts-box-head computes
   border-radius:0 while .alerts-box is 11px with overflow:hidden, so a square 2px
   inset ring got sliced into a wedge at each corner. An OPEN box's header is only
   its top edge, so it rounds the top two; a COLLAPSED box IS its header, so it
   rounds all four - which is why the render tags the box with `closed`. */
.alerts-box-head.acc-cur{
    box-shadow:inset 0 0 0 2px var(--ink), inset 5px 0 0 var(--ink);
    background:transparent !important;
    color:var(--ink);
    border-radius:var(--radius) var(--radius) 0 0;
}
.alerts-box.closed .alerts-box-head.acc-cur{ border-radius:var(--radius); }

/* The third cursor state (S150): which BUTTON inside the current row is selected.
   RING ONLY as of S151 - it used to also force background:var(--attn);color:#fff,
   which was correct when every button here was an outline in the same colour, and
   is wrong now that they are ranked. It did two different wrong things: on
   "Go to Job" (already filled --attn) it changed nothing at all beyond the ring,
   so the selection was doing no work; on "Recompute & Close" it flooded the amber
   Edit identity with --attn, i.e. the SELECTED state repainted the button as the
   other button. The two variants already look distinct, so selection only has to
   say WHICH ONE - and the ink ring is the same language the row and box-header
   cursors use. */
.alert-row.acc-cur .btn-alert-goto.act-cur{
    box-shadow:0 0 0 2px var(--ink);
}
/* The GLOBAL cursor rule (`.acc-cur{background:#ffe9a8 !important}`, ~line 5141)
   also matches .alert-row itself, and its !important beats anything shorter -
   so removing the fill from .alert-card above left a yellow band painted on the
   WRAPPER, wider than the card and still sitting on orange. MEASURED in the
   browser, not read off the source: the card lost its fill and the row kept one.
   This is the fifth time a rule here has been defeated by a longer/!important
   selector, so: ask the element which rules match, never trust the source by eye.
   !important is required to answer !important; nothing shorter can. */
.alert-row.acc-cur{ background:transparent !important;box-shadow:none !important; }
.alert-when { font-size:0.76em;color:var(--ink-3);margin:3px 0 0 4px; }

/* The one pill that carries the alarming fact - the age, or "Changed:" on a
   purchase-change card. Same treatment on every kind so the eye lands in the
   same place whatever the alert is. */
.alert-tag-alarm { background:var(--neg-tint);color:var(--neg-deep);border-color:var(--neg-tint); }

.alerts-subtabs { margin-bottom:16px; }
.alerts-tab-count {
    background:var(--warn-deep);color:#fff;border-radius:var(--radius-pill);
    padding:0 7px;margin-left:6px;font-size:0.78em;font-weight:700;
}
.alerts-page-panel { padding:0; }
/* --warn-deep, not --warn: white on --warn is 3.82:1 and fails AA. */
.alerts-section-count {
    background:var(--warn-deep);color:#fff;border-radius:var(--radius-pill);padding:1px 9px;
    font-size:0.82em;font-weight:700;
}
/* ── ALERTS HISTORY: the table standard ──
   docs/superpowers/specs/2026-07-27-table-standard.md. Default 13px size, since
   these tables have no width pressure (10 and 12 columns in the full content
   track, no fixed layout). nth-child zebra is safe HERE, unlike the Purchase
   ledger, because these are flat lists with nothing inserted between rows. */
.alerts-history-table { width:100%;border-collapse:collapse;font-size:var(--fs-sm);background:var(--surface);border-radius:var(--radius-sm);overflow:hidden;margin-bottom:6px;font-variant-numeric:tabular-nums; }
.alerts-history-table th { background:var(--brand);color:var(--brand-ink);padding:7px 9px;text-align:left;font-weight:600;font-size:var(--fs-sm);text-transform:none;letter-spacing:0;white-space:nowrap; }
.alerts-history-table td { padding:6px 9px;border-bottom:1px solid var(--line-2);vertical-align:middle;color:var(--ink); }
.alerts-history-table tbody tr:nth-child(even) td { background:var(--zebra); }
.alerts-history-table tbody tr:hover td { background:var(--brand-tint); }
/* Box clips its own corners (overflow:hidden) - a wide table needs ITS OWN
   scroll container, not the box's, or the rounded corners fight a scrollbar. */
.alerts-table-scroll { overflow-x:auto; }

/* History rows are already resolved, not a live risk - a third "kind", neither
   the fault red nor the risk amber every other box uses. Slate reads as
   "informational / already handled" elsewhere in this app (Final Costing). */
.alerts-box.log { border-left-color:var(--slate); }
.alerts-box.log .alerts-box-head:hover { background:var(--slate-tint); }

/* History's date+search live IN the amber bar, like Purchase's own topbar
   actions - not a separate filter row below it. Light controls so they read
   against the amber, the same idea as white text on the same bar. */
.alerts-topbar-filters { display:flex;align-items:center;gap:8px;margin-left:auto; }
.alerts-topbar-filters input[type="date"],
.alerts-topbar-filters input[type="text"] {
    height:30px;padding:0 10px;border:none;border-radius:6px;
    font-size:0.82em;font-family:inherit;background:rgba(255,255,255,0.94);color:var(--ink);
}
.alerts-topbar-filters input[type="date"] { width:132px; }
.alerts-topbar-filters input[type="text"] { width:240px; }
.alerts-topbar-filters input::placeholder { color:var(--ink-3); }
.alerts-hf-sep { color:rgba(255,255,255,0.85);font-size:0.78em; }
.alerts-hf-clear {
    background:rgba(255,255,255,0.2);color:#fff;border:none;border-radius:6px;
    width:30px;height:30px;cursor:pointer;font-size:0.9em;font-family:inherit;flex-shrink:0;
}
.alerts-hf-clear:hover { background:rgba(255,255,255,0.34); }
.alerts-history-subnote { color:var(--ink-3);font-size:0.82em;margin:-4px 0 14px; }

/* Masters — all 9 sub-tabs' lists share one recipe, same table standard as
   .alerts-history-table above. nth-child zebra is safe here too: flat lists,
   nothing inserted between rows. */
.masters-tbl { width:100%;border-collapse:collapse;font-size:var(--fs-sm);background:var(--surface);border-radius:var(--radius-sm);overflow:hidden;margin-bottom:6px;font-variant-numeric:tabular-nums; }
.masters-tbl th { background:var(--brand);color:var(--brand-ink);padding:7px 9px;text-align:left;font-weight:600;font-size:var(--fs-sm);white-space:nowrap; }
.masters-tbl td { padding:6px 9px;border-bottom:1px solid var(--line-2);vertical-align:middle;color:var(--ink); }
.masters-tbl tbody tr:nth-child(even) td { background:var(--zebra); }
.masters-tbl tbody tr:hover td { background:var(--brand-tint); }

/* ── JOB ALERT BADGE in jobs list ─────────────────────────
   Renders on the JOBS screen, like .lot-changed-badge above. Same alert
   vocabulary, same legacy ambers, so converted together - Jobs' own conversion
   should leave both alone. */
.job-alert-badge {
    display:inline-block;
    background:var(--warn-tint);
    color:var(--warn-deep);
    border:1px solid var(--warn);
    border-radius:4px;
    padding:1px 6px;
    font-size:0.75em;
    font-weight:700;
    margin-left:8px;
    vertical-align:middle;
}

/* ── STOCK REPORT TOPBAR ────────────────────────────────── */
.stock-topbar {
    background:var(--brand);
    color:var(--brand-ink);
    padding:8px 14px;
    display:flex;
    align-items:center;
    flex-wrap:wrap;
    row-gap:8px;
    margin-bottom:0;
}
.stock-topbar-title { font-weight:800;font-size:1.1rem; }

/* ── B8: Stronger focus highlight on purchase form fields ──
   The three zone hues (red part1 / green part2 / orange part3) collapse to ONE
   --accent ring — focus is a single meaning, not a per-zone colour. The 2.5px
   border + 4px glow strength is deliberate (a usability fix) and is unchanged.
   Each glow declares a flat --accent-tint first, then the true translucent
   colour-mix; browsers without colour-mix keep the flat fallback. */
.p2if input:focus {
    border-color: var(--accent) !important;
    border-width: 2.5px !important;
    background: var(--surface) !important;
    box-shadow: 0 0 0 4px var(--accent-tint);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent);
}
.p2if-variety > div > input:focus {
    border-color: var(--accent) !important;
    border-width: 2.5px !important;
    background: var(--surface) !important;
    box-shadow: 0 0 0 4px var(--accent-tint);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent);
}
.pf input:focus, .pf select:focus {
    border-color: var(--accent) !important;
    border-width: 2.5px !important;
    outline: none;
    box-shadow: 0 0 0 4px var(--accent-tint);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* ── B8 additional: focus glow on p3 fields and save button ── */
.p3-input:focus,
.p3-desc:focus {
    border-color: var(--accent) !important;
    border-width: 2.5px !important;
    outline: none;
    box-shadow: 0 0 0 4px var(--accent-tint);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent);
}
.purch-btn-save:focus {
    outline: 3px solid var(--surface);
    outline-offset: 2px;
    box-shadow: 0 0 0 5px var(--pos-deep);
}

/* ── PURCHASE VIEW/EDIT REDESIGN ─────────────────────────── */
.pur-view-topbar { background:var(--brand);padding:8px 14px;display:flex;align-items:center;gap:10px; }
.pur-view-title { color:var(--brand-ink);font-size:1em;font-weight:700;white-space:nowrap;flex-shrink:0; }
.pur-view-topbar-actions { margin-left:auto;display:flex;gap:8px;align-items:center; }
/* Purchase's own #purchViewPaginationTop rules (this comment used to sit above
   them) are gone along with the element (2026-08-19) - the top pagination bar
   duplicated the footer one with no page scroll to justify both, so its slot
   in the topbar is now #purViewTitleActive ("Purchase Ledger") instead. Stock's
   topbar pagination is untouched below; it never had the duplicate. */
#stockPaginationTop .pgn-btn { padding:2px 7px;font-size:0.78em; }
#stockPaginationTop .pgn-input { width:38px;padding:2px 4px;font-size:0.78em; }
#stockPaginationTop .pgn-info { font-size:0.78em; }
#stockPaginationTop .pgn-show { font-size:0.78em;margin-left:3px;gap:3px; }
.pur-view-filter-bar { display:flex;align-items:flex-end;gap:6px;padding:8px 14px;flex-wrap:wrap;border-bottom:1px solid var(--line);background:var(--head); }
.pur-view-filter-bar .ls-filter label { font-size:0.7em;color:var(--ink-2);font-weight:700;text-transform:uppercase;display:block;margin-bottom:3px; }
.pur-view-filter-bar .ls-filter input, .pur-view-filter-bar .ls-filter select { padding:4px 7px;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--surface);color:var(--ink);font-size:0.82em;height:28px;font-family:inherit; }
.pur-view-filter-bar .ls-search label { font-size:0.7em;color:var(--ink-2);font-weight:700;text-transform:uppercase;display:block;margin-bottom:3px; }
.pur-view-filter-bar .ls-search input { padding:4px 8px;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--surface);color:var(--ink);font-size:0.82em;height:28px;min-width:200px;font-family:inherit; }
.pur-view-filter-bar .ls-filter input:focus, .pur-view-filter-bar .ls-filter select:focus,
.pur-view-filter-bar .ls-search input:focus { border-color:var(--accent); outline:none; }

/* Chart of Accounts filter row: Group filter, Show-all-ledgers toggle,
   search, Expand All - all sit in the one .pur-view-filter-bar row. */
/* Page must not scroll here - only the table between the filter row and the
   footer suspense line does. Measured live (Playwright, 1536x860): tree top
   sits at 230.8px, the suspense footer is 37px, .main-container's own bottom
   padding is 25px - 100vh - 230.8 - 37 - 25 = 100vh - 292.8, +5px safety
   margin for the suspense line's longer "Difference in Opening Balances..."
   message wrapping. Overrides the generic .pur-view-table-wrap 260px offset,
   which was never right for this panel's shorter chrome + trailing footer. */
/* RE-MEASURED 2026-08-13, after the heading moved into the green bar: the tree
   top went 230.8 -> 195, and the 298 above was still holding the old chrome, so
   the row that was freed turned into dead space UNDER the panel instead of
   going to the table. Ali: "you got one row space but did not added, that space
   is used as emplty space below."
   Live at 1528x900: tree top 195, suspense footer 37, and .main-container's
   bottom padding is 20 on #masters — the uniform-20px rollout above, which is
   the standard. So 100vh - 252 EXACTLY, with no safety margin: the old value's
   +5 was precautionary (for the suspense line's longer "Difference in Opening
   Balances..." wrapping) and never measured, and carrying it here would leave
   25px under the panel where the standard says 20. That message does not wrap
   at this panel's 1272px anyway. Worth 45px over the old rule — the row.
   253 and not 252: the arithmetic 195+37+20 is a pixel short because the
   suspense footer sits under the tree's own 1px border-bottom. Measured, not
   reasoned — at 252 the page gained a scrollbar and the gap read 19. */
#mstCoaTree.pur-view-table-wrap { max-height:calc(100vh - 253px - var(--mstrip-h)); }
/* The heading is CENTRED on the bar, not just first in it (Ali: "i want chart
   of account in center"). Absolute rather than flex spacers: the two buttons on
   the left and the three actions on the right are different widths, so equal
   spacers would centre it between them, not on the bar. Scoped to this panel —
   .pur-view-title is shared with Purchase, whose title is left-aligned by
   design. pointer-events:none so a wide title can never swallow a button click. */
#chartOfAccountsMaster .pur-view-topbar { position:relative; }
/* SIZED UP 2026-08-13 (AA47 #7). Ali: the heading is too small now that it sits
   in the green bar. Measured before changing anything: .pur-view-title computes
   to 15px, which is exactly the body font — so the screen's own name was set at
   the size of ordinary text, and centring it on a busy bar made that obvious.
   1.15em (17.25px) is a notch above the docked record pages' own .mstp-title
   (measured 16.95px), which is the app's existing "this is the thing you are
   looking at" size — so the chart's title now reads as the biggest text on the
   screen without inventing a new scale. Scoped to this panel: Purchase's title
   uses the same class and was not complained about. */
#chartOfAccountsMaster .pur-view-topbar .pur-view-title {
  position:absolute;left:50%;transform:translateX(-50%);pointer-events:none;
  font-size:1.15em;letter-spacing:0.02em; }
/* The GROUP filter is a createSearchDropdown wrapper now (AA47 #6), so the
   width belongs to the wrapper, not to a <select>. */
#mstCoaGroupPick { min-width:210px; }
#mstCoaViewSel { min-width:140px; }
.coa-filter-bar #btnCoaShowAll, .coa-filter-bar #btnCoaExpandAll { align-self:flex-end; white-space:nowrap; }

/* ── THE KEYBOARD ROW CURSOR on the two Sales lists (2026-08-13) ──────────
   Its own class, not a reuse of :hover: the pointer and the keyboard can be on
   two different rows at the same time, and if they shared a colour there would
   be no way to tell which one Enter is about to open.

   Recoloured to the same amber every other BA73 list cursor uses (Purchase/
   Jobs/Stock/Alerts all copy Accounts' .acc-cur) - Ali: "sales get green tint
   focus why its different", after the other four had already gone in with
   the amber one. Painted on the TD, not the TR: tr.si-row-alert's own pale
   amber (#fff8e1) carries !important too, and only a same-!important rule
   that is MORE SPECIFIC (id + tag + class here, vs. a bare class there) beats
   it - the exact "must beat the zebra" fix .pur-view-table/.jobs-view-table
   already needed, just against an alert tint instead of a zebra stripe. */
#salesInvoiceListView tr.kbrow td, #proformaListView tr.kbrow td {
    background: #f2d585 !important; color:#241f00; }
#salesInvoiceListView tr.kbrow td:first-child,
#proformaListView tr.kbrow td:first-child { font-weight: 700; box-shadow: inset 3px 0 0 #8a6d0b; }
/* BA73: Left/Right on a cursored row moves onto its own action button(s). */
#salesInvoiceListView .si-action-cur, #proformaListView .si-action-cur {
    outline: 2px solid #8a6d0b; outline-offset: 1px; }

/* Sales invoice list only - .pur-view-filter-bar/.ls-search are shared with
   Purchase and Proforma (same class, different screens), so this is scoped to
   #salesInvoicePanel rather than edited at the shared rule above. Narrower
   now that the list has a Date Preset box (Ali's own call, "we can reduce
   search size" after asking for the preset dropdown to be added). */
#salesInvoicePanel .ls-search { min-width:160px; }
#salesInvoicePanel .ls-search input { min-width:0; }

/* Locked header, same technique as Stock's .stock-tbl-scroll: the table
   itself scrolls internally instead of the whole page, so the topbar/
   filter-bar/pagination around it never move. 260px = the real measured
   chrome above+below the wrap (topbar+filter-bar+pagination+.main-container's
   own padding) at the new 20px main-container padding, 1528x695. A prior
   234px was deliberately undertuned to chase a cosmetic ~2px trailing-gap
   match against Stock, measured only locally near the footer - that left
   the wrap's own box 24px taller than the real viewport, which nothing
   clips, so it became a real empty page-level scroll (root-caused Session
   81). Tune this against document.body.scrollHeight vs clientHeight, not
   local footer slack. */
.pur-view-table-wrap { overflow-x:auto;overflow-y:auto;max-height:calc(100vh - 260px - var(--mstrip-h));border-bottom:1px solid var(--line); }
/* Purchase list specifically: HEIGHT not max-height, same reasoning as
   .led-wrap (S131) - a max-height lets a short page (few rows) leave the
   panel short of the floor, which is the "extra space at bottom" Ali flagged
   here. N=249 walked off real box geometry (wrap top + pagination height +
   panel's 1px bottom border + the standard 20px frame), not derived from the
   260 above (that number is a generous scroll cap, not a floor-fit value). */
#transactions .pur-view-table-wrap { height:calc(100vh - 249px - var(--mstrip-h)); max-height:none; }
/* Jobs gets its own viewport-relative cap, matching Purchase's own
   internal-table-scroll pattern (toolbar/filters stay fixed, only the
   table body scrolls) rather than letting the whole page scroll - Ali's
   own call, for consistency with Purchase.
   HEIGHT not max-height (S137, same reasoning as Purchase's own .pur-panel
   fix and .led-wrap before it): a max-height lets a short jobs list leave
   the panel short of the floor. N=249, same as Purchase/Proforma/Sales
   Invoice - Jobs got its own footer pagination bar the same session (it had
   none before; a bare N=213 with no pagination-height term briefly shipped
   and pushed the panel 15px past the viewport bottom, caught live and fixed
   here), so its geometry is now identical to those three: wrap.top (~192.5)
   + pagination bar (35.2) + panel border (1) + 20px frame = 249. */
#jobsListView .pur-view-table-wrap { height:calc(100vh - 249px - var(--mstrip-h)); max-height:none; }
.pur-view-table { width:100%;border-collapse:collapse;font-size:0.82em;table-layout:fixed; }
/* ── LEDGER: the single table standard, at the DENSE size ──
   Standard is 13px. This table cannot have it. It is at exactly its width
   budget (1252px table in a 1252px track) and uses table-layout:fixed, so a
   bigger font does not widen columns - it CLIPS them. Measured at 1528x695:
   13px clips 6 columns including Weight, Total and Grand Total; 12.6px still
   clips 4. 12.3px clips none. So the ledger keeps 12.3px and 7/7 + 6/7 padding,
   and takes every other rule of the standard. 0.7px is not visible; what makes
   tables read as one family is the ground, weight, case, zebra and total row. */
.pur-view-table thead th { background:var(--brand);color:var(--brand-ink);padding:7px 7px;text-align:left;white-space:nowrap;font-weight:600;overflow:hidden;position:sticky;top:0;z-index:10; }
.pur-view-table thead th.r { text-align:right; }
.pur-view-table thead th.c { text-align:center; }
.pur-view-table thead th.sortable { cursor:pointer;user-select:none; }
.pur-view-table thead th.sortable:hover { background:var(--brand-2); }
/* TRYING (Ali, 2026-08-19): the vertical column dividers from the Accounts
   Ledger (.led-table), scoped to #purchViewTable ONLY - .pur-view-table itself
   is shared by every Masters list (godowns/employees/parties/...), Jobs and
   more, so a bare rule on the class would have boxed all of those too. Same
   token pairing the Ledger uses: --brand-2 on the dark header, --line-2 in the
   body. Delete these two rules to revert. */
#purchViewTable thead th + th { border-left:1px solid var(--brand-2); }
#purchViewTable tbody td + td { border-left:1px solid var(--line-2); }
/* Every header centred (Ali, 2026-08-19) - data keeps its own alignment
   (Party Name/GDN/Emp/Code stay left, the money columns stay right), only the
   label above it moves to the middle of the column. #purchViewTable's ID beats
   both the base left default and the .r/.c overrides above, so this is the
   only line needed. */
#purchViewTable thead th { text-align:center; }
/* AQ63 follow-up (S179, Ali: "faint ↕"): the app-wide sort-indicator standard
   is Stock's - a faint neutral marker on every sortable column, replaced by a
   full-strength arrow on whichever one is active. Purchase, Day Book, Masters'
   Chart of Accounts and Accounts' Outstanding share this ONE class rather than
   each carrying its own opacity rule - Sales' Proforma lot table and the lot
   selector are the other two screens with sortable columns, and get the exact
   same rule under their own class names so no fourth copy of it exists. */
.pv-sort-icon, .pfc-sort-icon, .ls-sort-icon { opacity:0.5; }
.pv-sort-icon.active, .pfc-sort-icon.active, .ls-sort-icon.active { opacity:1; }
.pv-sort-icon { margin-left:3px;font-size:0.8em; }
.pur-view-table tbody tr { border-bottom:1px solid var(--line-2);cursor:pointer; }
/* Zebra keys off .pv-alt, which loadPurchases stamps from the DATA index - NOT
   nth-child. Expansion rows are inserted between ledger rows, so a positional
   stripe would flip every row below whenever one is opened. Same reasoning as
   the sc-{col} rule: never index off DOM position. */
.pur-view-table tbody tr.pv-alt td { background:var(--zebra); }
.pur-view-table tbody tr:hover { background:var(--brand-tint); }
.pur-view-table tbody tr.pv-alt:hover td { background:var(--brand-tint); }
.pur-view-table tbody tr.pv-expanded { background:var(--accent-tint);border-bottom:none; }
/* BA73 row cursor: same "must beat the zebra" fix the Ledger/Day Book/Trial
   Balance already needed (see the note at .led-table tbody tr.acc-cur td) -
   .pv-alt paints the TD, so a plain tr-level .acc-cur is hidden underneath. */
.pur-view-table tbody tr.acc-cur td{ background:#f2d585 !important; color:#241f00; }
.pur-view-table tbody tr.acc-cur td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }
/* Left/Right on a cursored row moves onto its own action buttons
   (View/Edit/Return/Cancel) - the ring sits ON the button, not the row. */
.pur-view-table tbody .pv-action-cur{ outline:2px solid #8a6d0b; outline-offset:1px; }
.pur-view-table tbody td { padding:6px 7px;white-space:nowrap;overflow:hidden;color:var(--ink); }
.pur-view-table tbody td.r { text-align:right; }
.pur-view-table tbody td.c { text-align:center; }
/* Locked footer, same technique as Stock's #stockTable tfoot: totals stay
   pinned at the bottom of the scroll area while only the data rows scroll
   underneath. Two stacked rows here (PAGE above GRAND) instead of Stock's
   one. Originally each row was stuck independently (GRAND at bottom:0,
   PAGE at a hardcoded bottom:30.83px = GRAND's measured height) - fragile,
   since any sub-pixel drift in GRAND's real rendered height (font
   rendering differs slightly across browsers/DPI) reopens a hairline gap
   between the two rows that scrolled body content shows through. Sticking
   the whole tfoot as ONE block instead removes the magic number entirely:
   PAGE and GRAND just stack in normal row order inside it, so there is
   nothing that can drift out of sync. Root-caused Session 81. */
/* Ali: "the moment we scroll, we can see black number... very minor line".
   Could not reproduce as a LAYOUT gap - PAGE/GRAND measured perfectly flush
   (0px) through a simulated real scroll at his own DPR (1.25). That points
   at a compositing seam, not a layout one: Chromium/Edge can show a
   hairline sliver of scrolled content through a sticky element during
   scroll even when its box geometry is exactly right, because the sticky
   element and the scrolling content underneath aren't always promoted to
   the same compositing pass. transform:translateZ(0) forces this tfoot onto
   its own GPU layer, the standard fix for exactly this symptom - additive
   only, the whole-tfoot-sticky shape from Session 81 (see below) is
   unchanged. */
.pur-view-table tfoot { position:sticky; bottom:0; z-index:10; transform:translateZ(0); }
.pur-view-table tfoot td { background:var(--head);padding:6px 7px;font-weight:400;color:var(--ink);border-top:2px solid var(--line); }
/* The 2px rule belongs where the foot meets the DATA. Between two total rows it
   is a pale band across a continuous sand fill, which reads as a gap in the
   colour rather than as a divider - Ali, on the Ledger and then unprompted on
   this screen: "in current total and closing balance row, they both have a white
   line between them, purchase list page also has same problem." Measured on the
   Purchase ledger: PAGE ends at 604.7 and GRAND starts at 604.7, both cells
   rgb(239,234,223), so the line was the border itself and not a real gap.
   Shared, because every two-row footer in the app has it: Purchase, Stock,
   Sales, and now the Ledger. */
.pur-view-table tfoot tr + tr td { border-top:0; }
/* The other half of the same seam, and the half that was still visible after
   the border-top went: `.pur-view-table tbody tr` puts a 1px border-bottom on
   the ROW, and under border-collapse the foot's rows carry it too - measured
   1px on every cell of the FIRST total row on both the Ledger and the Purchase
   list. Ali, after the first fix: "footer white line is still visible", then
   "purchase list also has same issue fix that also". A total row needs no rule
   under it: the panel's own border ends the box. */
.pur-view-table tfoot tr, .pur-view-table tfoot td { border-bottom:0; }
.pur-view-table tfoot td.r { text-align:right; }
/* Column widths */
/* ── Column widths ──
   Money columns are sized to the LARGEST value they can hold, not the typical
   one. Measured at the real cell font (12.3px Inter + 14px padding, ~7px/digit):
     6 figures  Rs 1,00,000     = 76px
     7 figures  Rs 99,99,999    = 84px
     8 figures  Rs 1,00,00,000  = 95px   (1 crore - rare, must not clip)
   Ali's real ceilings (2026-07-26): Bags 5 figures, Add Exp / Deduct 4 figures,
   Weight 5 digits + 2 decimals, GST 6-7 figures, Total / Grand 7 figures with
   1 crore rare. GST was the actual bug - 70px could not hold Rs 1,00,000 at all.

   .pvc-party deliberately keeps NO width, so it stays the single elastic column.
   That is what makes the column chooser work: whatever gets hidden flows
   straight back into Party. The cost is that with every column shown it is
   squeezed (Ali's call: "if we display all then party pays the price, and its
   ok" - the Code column still carries the identity, and the full name is on
   hover). Giving it a fixed width instead makes the table rigid at 1424px
   against a 1322px track, and the frame cannot scroll that: .main-container is
   a grid item with min-width:auto, so it swells to 1486px and the whole PAGE
   scrolls, clipping Actions. Measured, tried, reverted - do not redo it. */
/* 44, not 36: holds "No." plus the enlarged caret plus a 3-digit row number
   ("Show: 100" makes 3 digits reachable). Mirrored by PUR_COLS w:44. */
.pvc-sr { width:44px; }
.pvc-pno { width:56px; }
/* 100px, not 82. DW (S199): 82px gave the text 67px and "14-Aug-26" needs 86.3,
   so this column was clipping 25 of 26 rows - and with text-overflow:clip, so it
   cut mid-character with no ellipsis to say so. The 18px comes from .pvc-party,
   the elastic column, which measured 15.5px of slack: after the change its
   longest name ("ANITHA ADTHI & COMMISSION MERCHANT") still fits. Mirrored by
   PUR_COLS w:100 in js_purchase.js - change both or neither. */
.pvc-date { width:104px; }
.pvc-party { min-width:150px; }
.pvc-code { width:52px; }
.pvc-gdn { width:44px; }
.pvc-emp { width:44px; }
.pvc-entries { width:40px; }
.pvc-bags { width:56px; }
.pvc-avgrate { width:78px; }
/* 78 -> 87px (S182): the AQ63 sort-icon follow-up (b321383) put a faint ' ↕' on
   every sortable header at rest, on top of whatever text was already there.
   "Weight kg" was already the tightest fit in this fixed-layout table, and the
   icon pushed it 7px over - measured (scrollWidth 84 vs clientWidth 77), so the
   header silently lost its sort arrow while every other column kept one. Slack
   comes from .pvc-party (min-width, not width - "if we display all then party
   pays the price, and its ok", same mechanism the column chooser already uses),
   so this costs nothing elsewhere. Re-measured at 87px: scrollWidth == clientWidth. */
.pvc-weight { width:87px; }
.pvc-total { width:95px; }
.pvc-bagchg { width:70px; }
.pvc-addexp { width:66px; }
.pvc-deduct { width:60px; }
.pvc-gst { width:84px; }
.pvc-grand { width:95px; }
.pvc-actions { width:220px; }
/* Wraps the actual button row inside td.pvc-actions (renderPurchaseRow only) -
   NOT display:flex on the td itself, which is also reused empty in the
   lot-wise expansion's head/row/foot padCells and must stay a real table-cell
   for the fixed-layout column algorithm to size it correctly. */
.pv-actions-wrap { display:flex; align-items:center; gap:5px; }

/* ── Column chooser: one rule per column, driven by a class on the table ──
   Every cell of a column (head, body, expansion, footer) carries its .pvc-*
   class, so nothing here is index-based (see the sc-{col} rule in CLAUDE.md).
   GST / Add Exp / Deduct start hidden - Ali's default, since they are "-" on
   nearly every row. */
.pur-view-table.pch-pno .pvc-pno,
.pur-view-table.pch-date .pvc-date,
.pur-view-table.pch-code .pvc-code,
.pur-view-table.pch-gdn .pvc-gdn,
.pur-view-table.pch-emp .pvc-emp,
.pur-view-table.pch-entries .pvc-entries,
.pur-view-table.pch-bags .pvc-bags,
.pur-view-table.pch-avgrate .pvc-avgrate,
.pur-view-table.pch-weight .pvc-weight,
.pur-view-table.pch-total .pvc-total,
.pur-view-table.pch-bagchg .pvc-bagchg,
.pur-view-table.pch-addexp .pvc-addexp,
.pur-view-table.pch-deduct .pvc-deduct,
.pur-view-table.pch-gst .pvc-gst,
.pur-view-table.pch-grand .pvc-grand { display:none; }

/* "+New X" is a locked forest identity app-wide (Session 79 button-color
   system) - never override that globally. This is the one place a New-X
   button sits directly on a bar that is ALSO forest (.pur-view-topbar), so
   at rest it's invisible. Scoped to #btnPfNew only: light at rest (readable
   against the bar). No hover color change - Ali tried the reverse-on-hover
   idea live and it looked bad (a dark-on-dark moment), so hover is pinned
   to the exact same colors as rest, overriding .btn-primary:hover. */
#btnPfNew { background:#fff; color:var(--brand); border:1.5px solid #fff; }
#btnPfNew:hover { background:#fff; color:var(--brand); border-color:#fff; }

/* ── PROFORMA LEDGER TABLE ──
   Own selector, deliberately not sharing .pur-view-table so nothing here can
   touch Purchase's already-verified rules. Same visual pattern (forest header,
   zebra, sticky thead/tfoot) but table-layout:auto, not fixed - 13 columns
   with no single elastic column to fight over, so hidden columns just
   collapse and .pur-view-table-wrap's own overflow-x:auto handles the rest. */
.pf-view-table { width:100%;border-collapse:collapse;font-size:0.82em;table-layout:auto; }
.pf-view-table thead th { background:var(--brand);color:var(--brand-ink);padding:7px 7px;text-align:left;white-space:nowrap;font-weight:600;position:sticky;top:0;z-index:10; }
.pf-view-table thead th.r { text-align:right; }
.pf-view-table tbody tr { border-bottom:1px solid var(--line-2); }
.pf-view-table tbody tr.pv-alt td { background:var(--zebra); }
.pf-view-table tbody tr:hover { background:var(--brand-tint); }
.pf-view-table tbody tr.pv-alt:hover td { background:var(--brand-tint); }
.pf-view-table tbody td { padding:6px 7px;white-space:nowrap;color:var(--ink); }
.pf-view-table tbody td.r { text-align:right; }
/* No./P No have no natural alignment of their own (not money, not text) -
   centered top and bottom, same as Sales Invoice's own No./G No and
   Purchase's No./P No. Neither header nor body centered before this. */
.pf-view-table thead th.c { text-align:center; }
.pf-view-table tbody td.c { text-align:center; }
.pf-view-table tfoot { position:sticky; bottom:0; z-index:10; }
.pf-view-table tfoot td { background:var(--head);padding:6px 7px;font-weight:700;color:var(--ink);border-top:2px solid var(--line); }
.pf-view-table tfoot td.r { text-align:right; }

.pfc-no { width:44px; }
.pfc-pno { width:70px; }
.pfc-date { width:82px; }
.pfc-customer { min-width:140px; }
.pfc-desc { min-width:120px; }
.pfc-remarks { min-width:100px; }
.pfc-bags { width:60px; }
.pfc-rate { width:78px; }
.pfc-weight { width:78px; }
.pfc-total { width:95px; }
.pfc-avgwt { width:70px; }
.pfc-status { width:90px; }
.pfc-action { width:110px; }
/* Cancel here is a void of a saved record, so it takes Delete's red and stays
   always-solid rather than outline->fill (button spec 2026-07-28, sec 6).
   The WORD "Cancel", never an icon - Ali's instruction, and consistent with
   the app-wide emoji strip. Replaces an inline #e74c3c the old trash button
   hardcoded. */
.pf-cancel-btn { background:var(--neg-deep); color:#fff; border:1px solid var(--neg-deep);
    margin-left:4px; }
.pf-cancel-btn:hover { background:var(--neg); border-color:var(--neg); }

/* Column chooser - all columns visible by default (Ali's call, unlike
   Purchase's GST/AddExp/Deduct-hidden default). No./Customer/Status/Action
   are the locked identity+control columns, never hideable. */
.pf-view-table.pfch-pno .pfc-pno,
.pf-view-table.pfch-date .pfc-date,
.pf-view-table.pfch-desc .pfc-desc,
.pf-view-table.pfch-remarks .pfc-remarks,
.pf-view-table.pfch-bags .pfc-bags,
.pf-view-table.pfch-rate .pfc-rate,
.pf-view-table.pfch-weight .pfc-weight,
.pf-view-table.pfch-total .pfc-total,
.pf-view-table.pfch-avgwt .pfc-avgwt { display:none; }

/* ── SALES INVOICE LEDGER TABLE ──
   Own selector, same reasoning as .pf-view-table: doesn't share rules with
   Purchase's or Proforma's already-verified tables. */
.si-view-table { width:100%;border-collapse:collapse;font-size:0.82em;table-layout:auto; }
/* Headers centred, matching the Session 87 column standard - the .r class stays
   on the cells so figures keep their right alignment, only the label moves. */
.si-view-table thead th { background:var(--brand);color:var(--brand-ink);padding:7px 7px;text-align:center;white-space:nowrap;font-weight:600;position:sticky;top:0;z-index:10; }
.si-view-table thead th.r { text-align:right; }
.si-view-table tbody tr { border-bottom:1px solid var(--line-2); }
.si-view-table tbody tr.pv-alt td { background:var(--zebra); }
.si-view-table tbody tr:hover { background:var(--brand-tint); }
.si-view-table tbody tr.pv-alt:hover td { background:var(--brand-tint); }
.si-view-table tbody td { padding:6px 7px;white-space:nowrap;color:var(--ink); }
.si-view-table tbody td.r { text-align:right; }
/* No./G No have no natural alignment of their own (not money, not text) -
   centered top and bottom like Purchase's own No./P No columns, not left by
   table default. Ali caught the header/body mismatch: header already read
   centered off the blanket rule above, body cells never did. */
.si-view-table tbody td.c { text-align:center; }
.si-view-table tfoot { position:sticky; bottom:0; z-index:10; }
.si-view-table tfoot td { background:var(--head);padding:6px 7px;font-weight:700;color:var(--ink);border-top:2px solid var(--line); }
.si-view-table tfoot td.r { text-align:right; }

.sic-no { width:44px; }
.sic-gno { width:70px; }
/* D14: min-width was 110/160px, forcing a hard floor even when the real
   content was much shorter (real invoice numbers are 6-7 chars, DEMO-1-00X
   fixtures run to 10; real customer names top out around 11 in the data
   today) - table-layout:auto already sizes every OTHER column to its
   content, so these two floors were the only source of the visible empty
   gap Ali noticed. Trimmed to fit what's actually here plus headroom for a
   longer real name/number, not removed outright - Ali's own call. */
.sic-invno { min-width:65px; }
.sic-date { width:82px; }
.sic-customer { min-width:115px; }
.sic-bags { width:60px; }
.sic-weight { width:78px; }
.sic-total { width:95px; }
.sic-balance { width:95px; }
.sic-status { width:70px; }
.sic-action { width:90px; }

/* Column chooser - all columns visible by default. Sr/Customer/Status/Action
   are the locked identity+control columns, never hideable. */
.si-view-table.sich-gno .sic-gno,
.si-view-table.sich-invno .sic-invno,
.si-view-table.sich-date .sic-date,
.si-view-table.sich-lorry .sic-lorry,
.si-view-table.sich-driver .sic-driver,
.si-view-table.sich-bags .sic-bags,
.si-view-table.sich-avgrate .sic-avgrate,
.si-view-table.sich-weight .sic-weight,
.si-view-table.sich-total .sic-total,
.si-view-table.sich-avgwt .sic-avgwt,
.si-view-table.sich-mf .sic-mf,
.si-view-table.sich-gst .sic-gst,
.si-view-table.sich-grand .sic-grand,
.si-view-table.sich-balance .sic-balance,
.si-view-table.sich-freight .sic-freight,
.si-view-table.sich-advance .sic-advance,
.si-view-table.sich-topay .sic-topay { display:none; }
/* AY is not a Columns-menu choice - profit is revealed by its own button, the
   same way the invoice screen does it, so it is hidden unless the table says so. */
.si-view-table .sic-ay { display:none; }
.si-view-table.si-profit-on .sic-ay { display:table-cell; }
/* No divider before AY - tried one and Ali removed it: "i dont need that line,
   just remove n keep it normal." The column reads fine on its own here. */
/* With every column on, the list is genuinely wider than the screen. Ali's own
   call: *"if i select all, it will show horizontal scroll."* */
.pur-view-table-wrap { overflow-x:auto; }
/* Sales was silently inheriting Purchase's exact max-height:calc(100vh-260px) via the
   shared .pur-view-table-wrap rule with zero safety margin. Measured live (S92): Sales'
   own real chrome sums to exactly 260px too (not a coincidence - it carries the same
   ~46px sticky totals footer Purchase/Stock have), but real rendered content still came
   out ~4px taller than the calc predicted - sub-pixel rounding accumulated across the
   nested layout (.app-shell/.main-container/body all measured the same 698.66px, not
   independently stretched - confirmed genuine content, not the separate app-shell-
   stretch mechanism documented in the design spec §7.5). A few px of safety margin
   absorbs that residual; Purchase/Stock apparently have enough natural slack elsewhere
   that the same rounding never surfaces as visible page scroll for them. */
/* HEIGHT not max-height (S137, same fix as Purchase's own .pur-panel and
   .led-wrap before it): re-walked as real box geometry rather than kept as
   the older 265px safety-margin figure above - wrap.top measures identical
   to Purchase's own (192.5px, same topbar+filter-bar chrome), same shared
   pagination bar below it, so N=249 matches Purchase's own walked value. */
#salesInvoicePanel .pur-view-table-wrap { height:calc(100vh - 249px - var(--mstrip-h)); max-height:none; }
/* Proforma shares the identical chrome (no Date Preset yet, but the filter
   row is the same height regardless of field count) and the same shared
   pagination bar - same N=249, walked the same way. */
#proformaListView .pur-view-table-wrap { height:calc(100vh - 249px - var(--mstrip-h)); max-height:none; }
/* RE-MEASURED 2026-08-14 at the locked 1528x695, after Ali: "in purchase master,
   where it shows adthidar list, has some bottom space, is it same 20px which is
   our standard?" It was 55px. The 251 below was measured when the wrap's top
   edge sat at 226.5px; the Masters landing rebuild moved it to 195, so the cap
   kept subtracting 35px that no chrome uses any more.

   Walked as box geometry, never as a height subtraction:
   N = wrap.top(195) + (panel.bottom - wrap.bottom)(1) + panel margin-bottom(0)
       + container padding-bottom(20) = 216.
   Every Masters list with the full toolbar + filter bar shares that 195 top, so
   they share the constant. The Chart of Accounts does NOT: it carries a
   reconciliation footer inside the panel, below the wrap, which is exactly the
   38px difference that makes its own 253 correct. Leave it alone. */
#partiesTable.pur-view-table-wrap,
#customersTable.pur-view-table-wrap,
#expenseMasterInlineTable.pur-view-table-wrap,
#custExpenseMasterInlineTable.pur-view-table-wrap { max-height:calc(100vh - 216px - var(--mstrip-h)); }

/* Same forest-on-forest problem as Proforma's #btnPfNew, same fix scoped
   only to this button - see the comment there for the full reasoning. */
#btnSiNew { background:#fff; color:var(--brand); border:1.5px solid #fff; }
#btnSiNew:hover { background:#fff; color:var(--brand); border-color:#fff; }

/* Same forest-on-forest problem again - Jobs' New Job button sits on the
   same .pur-view-topbar and was missed when the list moved onto it. */
#btnNewJob { background:#fff; color:var(--brand); border:1.5px solid #fff; }
#btnNewJob:hover { background:#fff; color:var(--brand); border-color:#fff; }

/* Same forest-on-forest problem a third time - Purchase's own New Purchase
   button (S96, moved from a tab into this same .pur-view-topbar) was missed
   again. Three button ids, one recurring trap: any "+New X" landing directly
   on .pur-view-topbar needs this override, .btn-primary's app-wide green
   default is invisible on a bar that's already that color. */
#btnPurNew { background:#fff; color:var(--brand); border:1.5px solid #fff; }
#btnPurNew:hover { background:#fff; color:var(--brand); border-color:#fff; }

/* Same trap again, found while chasing the report above - EVERY Masters
   small-list "Add X" button sits on this same .pur-view-topbar and had never
   gotten the fix at all (pre-existing since Masters was built, not new this
   session). All nine, one pass, so this doesn't come back a fourth time. */
#btnAddGodown, #btnAddBagCharge, #btnAddEmployee, #btnAddVariety, #btnAddParty,
#btnAddCustomer, #btnAddTransporter, #btnAddExpense, #btnAddCustExpense
    { background:#fff; color:var(--brand); border:1.5px solid #fff; }
#btnAddGodown:hover, #btnAddBagCharge:hover, #btnAddEmployee:hover, #btnAddVariety:hover,
#btnAddParty:hover, #btnAddCustomer:hover, #btnAddTransporter:hover, #btnAddExpense:hover,
#btnAddCustExpense:hover
    { background:#fff; color:var(--brand); border-color:#fff; }

/* Same trap a fifth time - Chart of Accounts' New Group/New Ledger buttons,
   2026-08-08. Caught before shipping this time, per the standing memory. */
#btnCoaNewGroup, #btnCoaNewLedger { background:#fff; color:var(--brand); border:1.5px solid #fff; }
#btnCoaNewGroup:hover, #btnCoaNewLedger:hover { background:#fff; color:var(--brand); border-color:#fff; }

/* Edit Customer / Edit Adthidar are .btn-edit — amber OUTLINE on transparent,
   the correct row-level Edit treatment per [[row-button-standard]] (one Edit
   definition app-wide, never invent a second). But these two sit in a
   TOOLBAR on .pur-view-topbar, not a table row, so the outline reads as
   near-invisible amber-on-forest (S103 user report). Fix is the SAME shape
   New Group/New Ledger already use here - solid white pill - just with
   --warn-deep text/border instead of --brand, so the Edit color meaning
   survives without adding a second Edit style. White #fff bg + --warn-deep
   text clears contrast; hover fills --warn-deep with white text, matching
   the outline-fills-solid-on-hover rule every other Edit button follows. */
#btnCoaEditCustomer, #btnCoaEditParty { background:#fff; color:var(--warn-deep); border:1.5px solid #fff; }
#btnCoaEditCustomer:hover, #btnCoaEditParty:hover { background:var(--warn-deep); color:#fff; border-color:var(--warn-deep); }

/* ============================================================
   READ-ONLY PURCHASE INVOICE (ledger View button)
   Same docking idiom as the entry panel: a sibling inside .main-container that
   swaps the module screens out with :has(), rather than a seventh full-viewport
   overlay on its own z-index scale.
   ============================================================ */
.pur-inv { display:none; }
.pur-inv.show { display:flex; flex-direction:column; }
.main-container:has(> #purInvScreen.show) > .main-section { display:none !important; }
.pur-inv-card { background:var(--bg); border:1px solid var(--line); border-radius:var(--radius);
    display:flex; flex-direction:column; overflow:hidden;
    /* .main-container carries margin:0 auto, so it is fit-content - it sizes to
       its widest visible child. That makes width:100% circular (card sized to
       container, container sized to card: the pair settled at 653px and squeezed
       the item table), while a flat width:1100px overflowed the 1096px content
       track at Ali's half-screen 1150.
       min-width is the one lever that behaves in both directions: it feeds the
       container's intrinsic size, so the container becomes 900 + padding and the
       card then takes 100% of it - definite at any viewport, and still under the
       narrow track. A document width, per spec layout C (form-zones capped,
       table-zones full width). */
    width:100%; min-width:900px; max-width:1100px; margin:0 auto;
    /* max-height, NOT height: a two-line purchase should not be stretched into a
       mostly-empty full-height card. Matches .purch-modal's allowance - viewport
       minus .main-container's 20px top + 20px bottom padding on Purchase. Those
       must not drift apart. */
    max-height:calc(100vh - 40px - var(--mstrip-h)); }
.pur-inv-head { display:flex; align-items:center; gap:12px; padding:10px 14px;
    background:var(--surface); border-bottom:1px solid var(--line); flex-shrink:0; }
/* Top row is the METADATA strip and is deliberately SMALLER than the party name
   below it - the party is what identifies the document, the rest is reference. */
.pur-inv-meta { display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 18px;
    font-size:var(--fs-sm); color:var(--ink); }
.pur-inv-meta-item { white-space:nowrap; font-weight:600; }
.pur-inv-meta-item em { font-style:normal; font-weight:700; color:var(--ink-3);
    font-size:0.84em; text-transform:uppercase; letter-spacing:.04em; margin-right:5px; }
.pur-inv-hint { margin-left:auto; font-size:var(--fs-sm); color:var(--ink-3); white-space:nowrap; }
.pur-inv-loading { padding:28px 16px; color:var(--ink-3); font-size:var(--fs-base); }
.pur-inv-body { padding:12px 14px; overflow-y:auto; flex:1; min-height:0;
    display:flex; flex-direction:column; gap:11px; }

.pur-inv-party { display:flex; align-items:baseline; gap:10px; }
.pur-inv-party-name { font-size:1.25rem; font-weight:700; color:var(--ink); }
.pur-inv-party-code { font-size:var(--fs-sm); font-weight:700; color:var(--accent-2);
    background:var(--accent-tint); padding:2px 9px; border-radius:var(--radius-pill); }

.pur-inv-table { width:100%; border-collapse:collapse; background:var(--surface);
    border:1px solid var(--line); border-radius:var(--radius-sm); overflow:hidden;
    font-variant-numeric:tabular-nums; }
.pur-inv-table th { background:var(--brand); color:var(--brand-ink); font-size:var(--fs-sm); font-weight:600;
    text-align:left; padding:7px 9px; white-space:nowrap; }
.pur-inv-table th.r, .pur-inv-table td.r { text-align:right; }
.pur-inv-table th.c, .pur-inv-table td.c { text-align:center; }
.pur-inv-table td { padding:7px 9px; font-size:var(--fs-base); color:var(--ink);
    border-bottom:1px solid var(--line-2); }
.pur-inv-table tbody tr:nth-child(even) td { background:var(--zebra); }   /* same value, now the shared token */
.pur-inv-table tfoot td { background:var(--head); font-weight:700; color:var(--ink);
    border-top:2px solid var(--line); padding:7px 9px; font-size:var(--fs-base); }

/* Charge rows sit in the tfoot, under the TOTAL row, so their amounts land in
   the Total column without any width matching. Quieter ground than the TOTAL row
   so the table total still reads as the primary figure. */
.pur-inv-table tfoot tr.pur-inv-chg td { background:var(--surface); border-top:0;
    font-weight:400; padding-top:5px; padding-bottom:5px; }
.pur-inv-chg-label { color:var(--ink-2); }
.pur-inv-chg-label .pur-inv-calc { color:var(--ink-3); font-size:0.88em; margin-left:7px;
    font-variant-numeric:tabular-nums; }
.pur-inv-table tfoot tr.pur-inv-chg.is-deduct td strong { color:var(--neg); }
.pur-inv-table tfoot tr.pur-inv-chg:last-child td { padding-bottom:8px; }

/* The at-a-glance row, in the tfoot so its figures sit under the columns they
   total. Deliberately larger than anything in the table so bags and final value
   can be read without hunting. */
.pur-inv-table tfoot tr.pur-inv-foot-grand td { background:var(--head);
    border-top:2px solid var(--line); padding:10px 9px; vertical-align:bottom; }
.pur-inv-foot-grand span { display:block; font-size:var(--fs-lbl); text-transform:uppercase;
    letter-spacing:.04em; font-weight:700; color:var(--ink-3); margin-bottom:1px; }
.pur-inv-foot-grand strong { display:block; font-size:1.5rem; font-weight:700;
    color:var(--ink); line-height:1.15; white-space:nowrap; }
.pur-inv-foot-grand td.is-grand strong { color:var(--accent-2); }
/* Damaged bags is context, not a headline - quieter, and left-aligned away from
   the two figures that matter. */
.pur-inv-damaged { text-align:left; }
.pur-inv-damaged strong { font-size:1.15rem !important; color:var(--ink-2) !important; }

/* ── PRINT: the purchase slip, A5 portrait ──
   A6 was considered and rejected - it exists but is postcard stock, not reliably
   available or feedable in Ali's office. A5 is the common Indian slip size, and
   portrait is comfortable now the print carries six columns rather than ten.
   `visibility`, not `display`, is what isolates the block: display:none on an
   ancestor would take the print block down with it, whereas visibility can be
   turned back on for a descendant. */
.pur-inv-print { display:none; }
.pur-inv-print-btn { margin-left:4px; }
@media print {
    @page { size: A5 portrait; margin: 9mm 8mm; }
    body * { visibility:hidden !important; }
    .pur-inv-print, .pur-inv-print * { visibility:visible !important; }
    .pur-inv-print { display:block !important; position:absolute; left:0; top:0; width:100%;
        color:#000; font-size:9.5pt; }
    /* Ancestors must not clip or scroll the block off the page. */
    #purInvScreen, .pur-inv-card, .pur-inv-body {
        overflow:visible !important; max-height:none !important; height:auto !important;
        border:0 !important; background:#fff !important; padding:0 !important; margin:0 !important; }
    .pip-head { margin-bottom:6pt; border-bottom:1pt solid #000; padding-bottom:4pt; }
    .pip-party { font-size:14pt; font-weight:700; }
    .pip-meta { margin-top:2pt; font-size:9pt; display:flex; gap:16pt; }
    .pip-meta strong { font-weight:700; }
    .pip-table { width:100%; border-collapse:collapse; font-variant-numeric:tabular-nums; }
    .pip-table th { text-align:left; font-size:8.5pt; text-transform:uppercase;
        border-bottom:0.8pt solid #000; padding:3pt 4pt; }
    .pip-table td { padding:3pt 4pt; border-bottom:0.4pt solid #bbb; }
    .pip-table .r { text-align:right; }
    .pip-total td { border-top:0.8pt solid #000; border-bottom:0; font-weight:700; padding-top:4pt; }
    .pip-chg td { border-bottom:0; padding:1.5pt 4pt; }
    .pip-chg em { font-style:normal; color:#555; font-size:8.5pt; margin-left:5pt; }
    .pip-grand td { border-top:0.8pt solid #000; font-weight:700; font-size:11pt; padding-top:5pt; }
    /* A long purchase must not split a row across the fold. */
    .pip-table tr { page-break-inside:avoid; }
    .pip-table thead { display:table-header-group; }
}

/* Expand affordance in the ledger's # column. The Detail button used to be the
   only signal that a row opens - it became View, so the caret carries it now.
   Sized up from 0.8em after Ali "had to spend a minute to find it" - at the
   ledger's 0.82em base a 0.8em glyph renders around 8px, far too small to read
   as a control. */
/* Absolutely positioned, not inline-block: text-align:center on .pv-sr centers
   the row NUMBER under the "No." header (which is centered - see .pur-view-table
   thead th.c). Left in normal flow, the caret's own width counted toward the
   centered inline content and dragged the number off-centre from the header. */
.pv-caret { position:absolute; left:6px; top:50%; color:var(--accent-2); font-size:1.35em;
    line-height:1; transform:translateY(-50%); transition:transform .12s ease; }
.pv-caret.is-open { transform:translateY(-50%) rotate(90deg); }

/* ── Column chooser control ── */
.pur-cols-wrap { position:relative; display:inline-block; }
/* .ls-cols-menu shares this rule outright (Session 101). The lot selector's
   Columns menu used to be its own one-off dropdown; it now uses the same
   markup as the other five, so it must use the same box too — cloning the
   styles would just let the two drift. Note .si6 .dd-panel does NOT apply
   here: that rule is scoped to the invoice screen, and the lot selector
   sits outside it. */
.pur-cols-menu, .ls-cols-menu { display:none; position:absolute; right:0; top:calc(100% + 6px); z-index:600;
    background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
    box-shadow:var(--shadow-pop); padding:8px; min-width:210px; max-height:70vh; overflow-y:auto;
    text-align:left; }
.pur-cols-menu.show, .ls-cols-menu.show { display:block; }
.pcm-head { font-size:var(--fs-sm); font-weight:700; color:var(--ink-3); padding:2px 6px 7px;
    border-bottom:1px solid var(--line-2); margin-bottom:5px; white-space:nowrap; }
.pcm-row { display:flex; align-items:center; gap:8px; padding:5px 6px; border-radius:var(--radius-sm);
    font-size:var(--fs-sm); color:var(--ink); cursor:pointer; }
.pcm-row:hover { background:var(--brand-tint); }
.pcm-row.hl { background:#f2d585 !important; color:#241f00; }
.pcm-row input { cursor:pointer; accent-color:var(--brand); }
.pcm-row span { flex:1; }
.pcm-row em { font-size:0.78em; color:var(--ink-3); font-style:normal; }
.pcm-locked { cursor:default; opacity:0.75; }
.pcm-locked:hover { background:transparent; }
.pcm-foot { border-top:1px solid var(--line-2); margin-top:6px; padding-top:7px; }
/* Detail expand */
/* The expanded lot-wise breakdown. The 13 .pv-detail-* rules that used to sit
   here were DEAD - the expansion renders as extra <tr>s inside the main
   .pur-view-table, not as a nested table - so the real styling was hardcoded
   inline in js_purchase.js and could never follow the accent preset. These
   classes replace those inline styles.
   The old palette's green here was decorative, not semantic: the breakdown is
   neither positive nor a success state. It reads as a quieter nested version
   of its parent table instead - brand title bar, sand column header, sage
   ground, sand total. The column header was solid brand-2 forest until S137 -
   stacked under the parent table's own forest thead it read as "2 rows with
   forest color", the exact pair rule [[forest-bar-is-a-pair]] documents
   (S124/S128): one forest bar, one sand header, values from .tb-table
   thead th rather than re-invented. */
.pvd-title td { background:var(--brand-tint); border-left:3px solid var(--brand);
    padding:4px 7px; font-size:0.88em; font-weight:700; color:var(--brand); }
.pvd-head td { background:var(--head); color:var(--ink); font-weight:600;
    padding:6px 7px; border-bottom:1.5px solid var(--line); }
.pvd-row td { background:var(--surface); border-bottom:1px solid var(--line-2); padding:6px 7px; }
.pvd-row td.pvd-pad { background:var(--brand-tint); }
.pvd-sr { color:var(--ink-3); font-size:0.82em; text-align:center; }
.pvd-rem { color:var(--ink-2); }
.pvd-item { font-weight:700; color:var(--ink); }
.pvd-lot { color:var(--accent-2); font-weight:700; }
.pvd-foot td { background:var(--head); border-top:2px solid var(--brand);
    font-weight:700; color:var(--ink); padding:6px 7px; }
.pvd-foot .pvd-grand { color:var(--accent-2); }
/* Same two-direction avg-weight flag as everywhere else (LOCKED):
   low -> --low, high -> --neg-deep. nowrap so a flagged row is not taller. */
.pvd-avg { font-weight:700; color:var(--pos-deep); white-space:nowrap; }
.pvd-avg-low { color:var(--low); background:var(--low-tint); border-radius:4px; padding:1px 5px; }
.pvd-avg-high { color:var(--neg-deep); background:var(--neg-tint); border-radius:4px; padding:1px 5px; }
/* Ledger row cells + the two summary rows. These were inline styles in
   js_purchase.js, so they overrode the table CSS and could not follow the
   accent preset. PAGE was blue and GRAND red in the old palette; the pair are
   a subtotal and a total, not two different meanings, so they now differ by
   WEIGHT and rule rather than by hue. */
.pv-empty { text-align:center; padding:30px; color:var(--ink-3); }
.pv-sr { position:relative; color:var(--ink-3); font-size:0.85em; }
.pv-pno { font-weight:700; color:var(--accent-2); }
/* No color declaration here on purpose - color:var(--accent-2) used to sit here
   but `.pur-view-table tbody td { color:var(--ink) }` (2 tags) always outranked
   it (1 class), so it never actually rendered - the column has been plain
   --ink/black all along. Ali caught this by eye: "does not use any color, its
   black." Left as dead weight-only now instead of re-adding a losing color. */
.pv-grand { font-weight:700; }
.pv-foot-page { background:var(--head); border-top:1px solid var(--line); }
/* Darker than --brand-tint (#e9efe9) on purpose - that tint sits almost the
   same lightness as the table's own --head/--bg, so the row read as "similar
   to table". */
.pv-foot-grand { background:#d5e2d7; border-top:2px solid var(--brand); }
.pv-foot-label { font-weight:400; color:var(--ink-2); }
.pv-foot-grand .pv-foot-label { color:var(--brand); }
/* Every figure in the row matches .pv-grand - the table body's own "Grand
   Total" column style, bold only. No color override: the column's real
   colour (see .pv-grand above) is plain --ink, already what
   .pur-view-table tfoot td sets - matching it means adding nothing here. */
.pv-foot-grand td.r { font-weight:700; }

/* The two small hand-rolled popups in js_purchase.js (inline-edit reminder,
   delete-row confirm). Built from inline styles on the old orange/red palette;
   the surrounding box keeps its own inline positioning, only the colours move. */
.mini-pop { position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
    background:var(--surface); border:1px solid var(--warn); border-radius:var(--radius);
    padding:20px 28px; z-index:99999; box-shadow:var(--shadow-pop);
    text-align:center; min-width:320px; }
.mini-pop.is-danger { border-color:var(--neg); padding:24px 28px; min-width:300px; }
.mini-pop-title { font-weight:800; color:var(--warn-deep); margin-bottom:8px; }
.mini-pop-title.is-danger { color:var(--neg-deep); }
.mini-pop-msg { font-size:0.9em; color:var(--ink-2); margin-bottom:16px; }
.mini-pop-btn { background:var(--warn-deep); color:#fff; border:none;
    border-radius:var(--radius-sm); padding:8px 24px; font-size:0.95em;
    font-weight:700; font-family:inherit; cursor:pointer; }
.mini-pop-btn.is-danger { background:var(--neg); }
.mini-pop-btn.is-quiet { background:var(--head); color:var(--ink-2); }
/* Same reasoning as the item-row ring buttons: the popup's own auto-focus
   (Cancel, on open) is real but easy to miss with nothing else marking it. */
.mini-pop-btn:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }

/* Action buttons */
.btn-pv { padding:4px 8px;border-radius:var(--radius-sm);border:none;cursor:pointer;font-size:0.8em;font-weight:600;white-space:nowrap; }
.btn-pv-detail { background:var(--accent);color:#fff; }
/* Edit: outline at rest, fills solid on hover - the general secondary-button
   mechanic (button spec sec 6). Only Save/Delete stay always-solid; Edit was
   missing this and sat as a fourth solid block next to Cancel. */
.btn-pv-edit { background:transparent;color:var(--warn-deep);border:1.5px solid var(--warn-deep); }
.btn-pv-edit:hover { background:var(--warn-deep);color:#fff; }
/* Cancel here is cancelPurchase() - Group B, functionally Delete - stays always
   solid on purpose (button spec sec 2/6). Explicit border so its box-size
   matches the outline buttons in the same row. */
.btn-pv-cancel { background:var(--neg);color:#fff;border:1.5px solid var(--neg); }
/* Return: routine business, not a correction - kept out of the red "void" bucket,
   its own dedicated --attn (button spec sec 5.2/8), same outline->fill mechanic. */
.btn-pv-return { background:transparent;color:var(--attn);border:1.5px solid var(--attn); }
.btn-pv-return:hover { background:var(--attn);color:#fff; }
.btn-pv-return-frozen { background:var(--head);color:var(--ink-3);cursor:not-allowed;border:1.5px solid transparent; }
/* View/Detail: "no implication, just to view" - soft outline at rest, deliberately
   not solid like Cancel or any commit action (button spec sec 2). Fills solid on
   hover the same way Edit/Return do, using its own sage-ink identity (6.55:1 with
   white text) so the row's interaction pattern is consistent across all four. */
.btn-pv-view { background:transparent;color:var(--sage-ink);border:1.5px solid var(--sage-line); }
.btn-pv-view:hover { background:var(--sage-ink);color:#fff; }
/* Cancelled purchase — dulled, view-only row */
.pv-row-cancelled { opacity:0.55;filter:grayscale(40%); }
.pv-row-cancelled:hover { background:inherit; }
.pv-badge-cancelled { background:var(--neg);color:#fff;border-radius:var(--radius-sm);padding:1px 6px;font-size:0.72em;font-weight:700;letter-spacing:0.3px;margin-left:6px; }
/* Amber, not red - a return isn't a problem the way a cancelled purchase is,
   same colour family as the Return/Done buttons on the return screen itself. */
.pv-badge-returned { background:var(--warn-deep);color:#fff;border-radius:var(--radius-sm);padding:1px 6px;font-size:0.72em;font-weight:700;letter-spacing:0.3px;margin-left:6px; }
.pr-row-input { width:70px;padding:3px 5px;border:1px solid var(--line);border-radius:4px;text-align:right;background:var(--surface);color:var(--ink); }
.pr-row-input:disabled { background:var(--head);color:var(--ink-3);border-color:var(--line-2); }
.pr-row-avail { color:var(--ink-3);font-size:0.82em; }
.pr-row-gst-blocked { color:var(--ink-3);font-style:italic;font-size:0.85em; }
/* Purchase Return — frozen header + damaged-bags box (spec §18.15.1) */
.pr-frozen-info { display:flex;flex-wrap:wrap;gap:4px 22px;background:var(--brand-tint);border:1px solid var(--line);border-radius:var(--radius-sm);padding:10px 14px;font-size:0.86em;color:var(--ink-2); }
.pr-frozen-info b { color:var(--ink); }
.pr-damaged-box { display:flex;align-items:center;justify-content:space-between;background:var(--warn-tint);border:1px solid var(--warn);border-radius:var(--radius-sm);padding:10px 14px;margin-top:12px; }
.pr-hist-row td { background:var(--head);padding:4px 8px 4px 30px;font-size:0.8em;color:var(--ink-2);border-bottom:1px solid var(--line-2); }
.pr-hist-item { display:inline-flex;align-items:center;gap:8px;margin-right:18px; }
.pr-hist-reversed { text-decoration:line-through;opacity:0.6; }
.btn-pr-reverse { background:none;border:1px solid var(--neg);color:var(--neg-deep);border-radius:4px;padding:1px 7px;font-size:0.85em;cursor:pointer; }
.pr-badge-reversed { background:var(--head);color:var(--ink-3);border-radius:4px;padding:1px 6px;font-size:0.78em;font-weight:600; }

/* Purchase Entry — Returns Summary tab (spec §18.15.2 follow-up), Edit-only, read-only */
/* flex-shrink:0 - without it this panel is a shrinkable flex child, and when
   the form is tight it gets squeezed to a couple of pixels while its content
   stays 200px tall: expanded, it simply did not appear. It must keep its size
   and let .purch-body scroll instead. */
.pur-returns-tab { margin:8px 0;border:1px solid var(--warn-tint);border-radius:var(--radius-sm);background:var(--warn-tint);overflow:hidden;flex-shrink:0; }
/* THE SAME EIGHT COLUMNS as .purch-part3 and the items table, so the returned
   figures land under Bags / Rate / Weight / Total exactly like the rows above and
   below them. Ali: "so when we have return all info are aligned in single columns".
   Column 1 is merged (No..Code) and holds the icon, the summary and Manage Returns;
   Item and Lot stay empty; the arrow keeps the actions column. */
.pur-returns-head { display:grid;
  grid-template-columns:30.2% 11% 7.5% 6.6% 9.6% 11.1% 12.2% 11.8%;
  align-items:center;padding:8px 0;cursor:pointer;font-size:0.88em;font-weight:600;
  color:var(--warn-deep);user-select:none; }
/* grid-row:1 or it auto-places onto a SECOND row beneath the figures, which is what
   split this strip in two. */
.prh-lead { grid-row:1;grid-column:1;display:flex;align-items:center;gap:10px;
            padding-left:14px;min-width:0; }
.prh-lead #purReturnsSummaryLine { white-space:nowrap;overflow:hidden;text-overflow:ellipsis; }
.prh-c { grid-row:1;display:flex;align-items:center;justify-content:flex-end;gap:5px;
         padding:0 9px;font-size:0.94em;font-weight:600;white-space:nowrap;
         font-variant-numeric:tabular-nums; }
/* BY ID, not by position. :nth-of-type counts sibling SPANS, not elements carrying
   the class, so .prh-c:nth-of-type(1) matched the lead span and every figure landed
   one column right of where it belonged. A selector that claims a DOM shape it does
   not have — the same trap as a stray `>`. Ids cannot drift. */
#prhBags   { grid-column:4; }   /* Bags   */
#prhRate   { grid-column:5; }   /* Rate   */
#prhWeight { grid-column:6; }   /* Weight */
.prh-c b { font-weight:800; }
.prh-a { grid-row:1;grid-column:7;padding:0 9px;text-align:right;font-weight:800;
         font-variant-numeric:tabular-nums; }
.pur-returns-head:hover { background:var(--warn-tint);filter:brightness(0.97); }
.pur-returns-head:focus-visible { outline:2px solid var(--accent);outline-offset:-2px; }
/* grid-column:8 explicitly — without it the arrow auto-places into the first free
   track (Item) and sits orphaned in the middle of the strip instead of in the
   actions column where every other row keeps its control. */
.pur-returns-arrow { grid-row:1;grid-column:8;justify-self:center;font-size:0.8em;
                     transition:transform 0.15s; }
.pur-returns-arrow.open { transform:rotate(90deg); }
/* THIS BUTTON SETS THE STRIP'S HEIGHT - it is the tallest thing in it - so it carries
   two rules that both exist to keep the strip at an items row's 37px.
   white-space:nowrap: its label wrapped to "Manage / Returns" the moment .prh-lead got
   tight, and a 2-line button made the strip 53px (measured at 1150). Ali: "may be some
   thing is auto wrapped so height is increasing" - it was.
   padding:1.5px, not 3px: at 3px the button was 24px and the strip 40px against 37px. */
.btn-pr-manage { grid-row:1;justify-self:start;margin-left:0;white-space:nowrap;flex:0 0 auto;background:var(--surface);color:var(--warn-deep);border:1.5px solid var(--warn-deep);border-radius:var(--radius-sm);padding:1.5px 10px;font-size:0.92em;font-weight:700;font-family:inherit;cursor:pointer; }
.btn-pr-manage:hover { background:var(--warn-deep);color:#fff; }
/* NO SIDE PADDING HERE. 14px of container inset pulls the left edge in and pushes the
   right edge out, which is exactly what held this table 14px inside the items table it
   has to line up with (measured: 127-1480 against 113-1494). Edge padding belongs in
   the first and last CELLS — the same fix the strip above uses via .prh-lead. */
.pur-returns-body { padding:4px 0 12px;border-top:1px solid var(--line); }
/* table-layout:fixed, because the items table is fixed too: only then are the colgroup
   percentages the real column widths rather than hints a browser re-fits to content. */
.pur-returns-body table { width:100%;table-layout:fixed;border-collapse:collapse;font-size:0.85em;margin-top:6px; }
.pur-returns-body th:first-child, .pur-returns-body td:first-child { padding-left:14px; }
.pur-returns-body th:last-child,  .pur-returns-body td:last-child  { padding-right:14px; }
/* The .lp-* block (LOCKED POPUP / showLockedPopup) was DELETED in Session 98
   along with the component itself - every one of its 8 call sites now uses
   appConfirm, so the app has one popup style instead of three. Its z-index of
   9999999 is why .acnf carries 10000000. See the note in js_purchase.js. */

/* Explicit background: without it the GLOBAL th rule (style.css ~203, still the
   pre-makeover red gradient) wins and paints this header maroon inside an
   otherwise sand panel. Scoped here on purpose - that global rule still dresses
   the screens Phase 2 has not converted yet, so it is not safe to change. */
.pur-returns-body th { background:var(--head);padding:5px 6px;color:var(--ink-2);font-weight:700;border-bottom:1px solid var(--line); }
.pur-returns-body td { padding:4px 6px;border-bottom:0.5px solid var(--line-2); }
/* ALIGNMENT BY CLASS, NEVER BY POSITION. Date spans two columns, so every :nth-child
   after it counts a track that is not the one it names - the same trap :nth-of-type
   sprang on the strip above, one column right of where it belonged. */
.pur-returns-body .prb-l { text-align:left; }
.pur-returns-body .prb-c { text-align:center; }
.pur-returns-body .prb-r { text-align:right;font-variant-numeric:tabular-nums; }
/* Remarks is free text in a fixed 12% track: clip it rather than let it push the row
   two lines tall. The full text is on the Return screen. */
.pur-returns-body .prb-rem { overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
/* .pur-returns-net-row was DELETED in S166 with the row itself: its four figures were
   the pinned footer's Total Value row said a second time (Ali - "return has to show its
   own data, bottom footer will show net data"). */
.p3-summary-value.net-value { color:var(--warn-deep); }
/* A compact pill on the label line. Full wording lives in its title attribute. */
.p3-summary-net-tag { font-size:0.6em;font-weight:800;color:var(--warn-deep);background:var(--warn-tint);padding:1px 6px;border-radius:var(--radius-pill);text-transform:uppercase;letter-spacing:0.3px;white-space:nowrap; }
/* Pagination */
.pur-view-pagination { display:flex;align-items:center;gap:8px;padding:5px 14px;font-size:0.82em;border-top:0.5px solid var(--line-2); }
/* The three per-screen `.pur-view-pagination button` rules that used to sit here
   were overridden by buildPaginationHTML's inline styles and still carried the
   old palette. The shared .pgn-* rules below replace them. */

/* ── PAGINATION (shared: Purchase, Stock, Jobs — see buildPaginationHTML) ──
   Pill-shaped, matching the rest of the roundish control set rather than the
   4px squares these were. One definition, so all three screens agree. */
.pgn-btn { padding:2px 8px; border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); color:var(--ink-2); cursor:pointer; font-family:inherit;
    font-size:var(--fs-sm); font-weight:650; line-height:1.4; }
.pgn-btn:hover:not(:disabled) { background:var(--brand-tint); color:var(--ink); border-color:var(--brand-tint); }
.pgn-btn.is-active { background:var(--accent); border-color:var(--accent); color:#fff; }
.pgn-btn:disabled { opacity:0.4; cursor:default; }
.pgn-input { width:52px; text-align:center; padding:2px 6px; border:1px solid var(--line);
    border-radius:var(--radius-sm); font-family:inherit; font-size:var(--fs-sm);
    background:var(--surface); color:var(--ink); }
.pgn-input:focus { border-color:var(--accent); outline:none; }
.pgn-info { color:var(--ink-2); font-size:var(--fs-sm); white-space:nowrap; }
.pgn-show { color:var(--ink-3); margin-left:6px; font-size:var(--fs-sm);
    display:inline-flex; align-items:center; gap:5px; }
/* Party code (ledger row) and item/party code (lot-wise detail). Two different
   codes, so they stay two different tints - accent for the party the purchase
   is against, sage for the per-lot code inside the breakdown. */
.pv-badge-code { background:var(--accent-tint);color:var(--accent-2);border-radius:4px;padding:1px 5px;font-size:0.85em;font-weight:700; }
.pv-item-code { background:var(--brand-tint);color:var(--brand);border-radius:4px;padding:1px 4px;font-size:0.85em;font-weight:700; }

/* ══ PROFORMA ══ — the docked window itself is #pfScreen (positioned near
   #inv-screen above) styled entirely by the shared .si6 block below (same
   class, same tokens). Only the lot table's own sort/warn behaviour, which
   predates and is independent of that repaint, stays here. */
.pfc-sortable { cursor:pointer;user-select:none;white-space:nowrap; }
.pfc-sortable:hover { background:rgba(0,0,0,0.04); }
.pfc-sort-icon { margin-left:3px;font-size:0.75em;color:#888; }
/* table-layout:fixed (from .si6 .grid) is what gives every row a consistent
   height and forces the min-width horizontal scroll at narrow widths - an
   earlier auto-layout override to make column hide/show work caused both to
   break (wrapping rows, a header that didn't reliably paint full-width, no
   scroll on a half-screen window). Fixed to Sales' own oc-p/oc-g/oc-e +
   .page.show-X technique instead (width:0 on the <col>, not display:none
   per cell), so table-layout:fixed is safe to inherit unmodified. */
.pf-lot-table { border-collapse:separate !important;border-spacing:0; }
/* Sales' .grid thead sticks at top:var(--hdh), a fixed 46px assumed to equal
   the sticky .sec-hd bar directly above it in that screen. Proforma's own
   sec-hd is a different height, so that assumption doesn't hold here and the
   sticky thead settles mid-scroll instead of right under the bar - looks like
   the header row landing between two data rows. There's only one card and a
   handful of rows, so a sticky header buys little; drop back to normal flow. */
.pf-lot-table thead th { position: static !important; }
/* Convert to Invoice sits beside Save in the same bottom bar and is just as
   real a commit action - give it the same box (Save's own padding/size/weight/
   align-self/margin), just without Save's green fill, so it doesn't look like
   an afterthought. Missing align-self:center let it inherit the totbar's
   align-items:stretch and stretch to the bar's full height instead of sitting
   as a pill like Save. */
.si6 .totbar #pfConvertBtn { align-self: center; margin: 0 14px; padding: 9px 20px; font-size: 14px; font-weight: 750; border-radius: 9px; }
/* Lot selector — in-proforma edit/remove buttons */
.btn-ls-edit-inpf { background:var(--surface);color:var(--warn-deep);border:1.5px solid var(--warn-deep);border-radius:4px;padding:3px 8px;cursor:pointer;font-size:0.8em;font-weight:700;font-family:inherit;margin-right:4px; }
.btn-ls-edit-inpf:hover:not(:disabled) { background:var(--warn-deep);color:#fff; }
.btn-ls-remove-inpf { background:var(--surface);color:var(--neg);border:1.5px solid var(--neg);border-radius:4px;padding:3px 8px;cursor:pointer;font-size:0.8em;font-weight:700;font-family:inherit; }
.btn-ls-remove-inpf:hover:not(:disabled) { background:var(--neg);color:#fff; }
.btn-ls-edit-inpf:disabled, .btn-ls-remove-inpf:disabled { opacity:0.45;cursor:default; }
.pf-inpf-editing { background:#fdecea !important; }

/* ── SALES INVOICE SCREEN ─────────────────────────── */

#salesInvoiceDetail {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  z-index: 200;
  background: #f5f6fa;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

#siTopBar {
  height: 44px;
  background: #2c3e50;
  color: white;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  flex-shrink: 0;
}

.si-back-btn {
  background: rgba(255,255,255,0.15);
  color: white;
  border: 1px solid rgba(255,255,255,0.3);
  padding: 4px 10px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 0.85em;
}

.si-topbar-btn {
  background: rgba(255,255,255,0.15);
  color: white;
  border: 1px solid rgba(255,255,255,0.3);
  padding: 4px 10px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 0.82em;
}

.si-save-btn { background: #27ae60 !important; border-color: #27ae60 !important; }
.si-pdf-btn  { background: #2980b9 !important; border-color: #2980b9 !important; }

#siBox1 {
  background: white;
  border-bottom: 1px solid #ddd;
  padding: 6px 12px;
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  align-items: center;
  flex-shrink: 0;
}

.si-header-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.si-header-field label {
  font-size: 0.72em;
  color: #888;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.si-header-field input {
  border: 1px solid #ddd;
  border-radius: 3px;
  padding: 3px 6px;
  font-size: 0.85em;
  width: 130px;
}

.si-header-field input[readonly] {
  background: #f8f9fa;
  color: #555;
}

#siSplitContainer {
  flex: 1;
  display: flex;
  overflow: hidden;
  gap: 0;
}

#siLeft {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 250px;
  width: 65%;
}

#siRight {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 200px;
  flex: 1;
}

#siHDrag {
  width: 5px;
  background: #ddd;
  cursor: col-resize;
  flex-shrink: 0;
  transition: background 0.15s;
}

#siHDrag:hover { background: #2980b9; }

.si-vdrag {
  height: 5px;
  background: #ddd;
  cursor: row-resize;
  flex-shrink: 0;
  transition: background 0.15s;
}

.si-vdrag:hover { background: #2980b9; }

.si-box {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: white;
  border: 1px solid #e0e0e0;
  min-height: 60px;
}

.si-box-header {
  padding: 5px 10px;
  font-weight: 600;
  font-size: 0.82em;
  background: #f8f9fa;
  border-bottom: 1px solid #e0e0e0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-shrink: 0;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.si-header-blue   { border-left: 3px solid #2980b9; color: #2980b9; }
.si-header-green  { border-left: 3px solid #27ae60; color: #27ae60; }
.si-header-orange { border-left: 3px solid #e67e22; color: #e67e22; }
.si-header-red    { border-left: 3px solid #c0392b; color: #c0392b; }

.si-add-btn {
  font-size: 0.78em;
  padding: 2px 8px;
  background: #e74c3c;
  color: white;
  border: none;
  border-radius: 3px;
  cursor: pointer;
}

.si-box-scroll {
  flex: 1;
  overflow: auto;
}

.si-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.83em;
}

.si-table th {
  position: sticky;
  top: 0;
  background: #f0f4f8;
  color: #2c3e50;
  padding: 5px 6px;
  text-align: left;
  border-bottom: 2px solid #ddd;
  white-space: nowrap;
  font-size: 0.9em;
}

.si-table td {
  padding: 4px 6px;
  border-bottom: 1px solid #f0f0f0;
}

.si-table tfoot td {
  padding: 5px 6px;
  border-top: 2px solid #ddd;
  background: #fafafa;
  font-weight: 600;
}

.si-table .r { text-align: right; }
.si-table td[contenteditable] { white-space:nowrap; overflow:hidden; max-height:28px; min-width:40px; }

/* Profit columns — hidden by default */
.si-profit-col { display: none; }
.si-profit-active .si-profit-col { display: table-cell; }
.si-profit-pos { color: #27ae60; font-weight: 600; }
.si-profit-neg { color: #e74c3c; font-weight: 600; }

/* Right panel fields */
.si-group {
  padding: 8px 10px;
  border-bottom: 1px solid #f0f0f0;
}

.si-group-title {
  font-size: 0.75em;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #888;
  margin-bottom: 6px;
}

.si-field-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 3px 0;
  gap: 8px;
}

.si-field-row label {
  font-size: 0.82em;
  color: #555;
  white-space: nowrap;
  min-width: 110px;
}

.si-field-row input {
  border: 1px solid #ddd;
  border-radius: 3px;
  padding: 3px 6px;
  font-size: 0.83em;
  width: 100%;
  text-align: right;
}

.si-field-row input[readonly] {
  background: #f8f9fa;
  color: #444;
  border-color: #eee;
}

.si-divider {
  height: 1px;
  background: #e0e0e0;
  margin: 6px 0;
}

.si-total-ab input { font-weight: 700; color: #2c3e50; }
.si-grand-total input { font-weight: 700; color: #c0392b; font-size: 1em !important; }
.si-balance input { font-weight: 700; color: #e67e22; }

/* Box 5 fields inside scroll area */
#siBox5 .si-box-scroll { padding: 8px 10px; }

/* Status badges for invoice list */
.si-badge {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 10px;
  font-size: 0.78em;
  font-weight: 600;
}

/* B4 (2026-08-03): PI/II are a real workflow difference, not two "origin"
   flavours of the same finished thing - the earlier comment here said the
   opposite and was wrong (see status-vocabulary memory). PI is genuinely
   unfinished: stock still held under the closed proforma, nothing in the
   books. Saved (II) = green, matching how Purchase/Stock already use green
   for "all fine"; Draft (PI) = amber, the same "needs attention, clears
   itself" colour the Alerts page uses for open risks. IX keeps the solid
   "cancelled" treatment, same as Proforma's job-badge-cancelled. */
.si-badge-II { background: var(--pos-tint); color: var(--pos-deep); }
.si-badge-PI { background: var(--warn-tint); color: var(--warn-deep); }
.si-badge-IX { background: var(--neg); color: #fff; }
/* "Underlying purchase changed, review needed" - same shape as Jobs' S92
   job-badge-edited: folds into Status ("Saved - Edited") instead of a
   separate icon next to the invoice number, overriding whatever colour the
   status itself would use (even a Saved/green invoice goes amber here). */
.si-badge-edited { background: var(--warn-tint); color: var(--warn-deep); }

/* Invoice list — purchase-changed alert highlight */
tr.si-row-alert { background: #fff8e1 !important; }
tr.si-row-alert:hover { background: #fff3cd !important; }
.btn-ack-sale { background:#e8f8f0; color:#27ae60; border:1px solid #27ae60; margin-left:4px; padding:2px 7px; font-size:0.82em; border-radius:4px; cursor:pointer; }
.btn-ack-sale:hover { background:#27ae60; color:#fff; }
.btn-alert-ack { background:#e8f8f0; color:#27ae60; border:1px solid #27ae60; padding:4px 10px; border-radius:5px; cursor:pointer; font-size:0.85em; margin-top:4px; display:block; width:100%; }
.btn-alert-ack:hover { background:#27ae60; color:#fff; }

#siFooter {
  height: 44px;
  background: white;
  border-top: 1px solid #ddd;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  flex-shrink: 0;
}

/* ── LOT TRACEABILITY REPORT ─────────────────────────────── */
.lt-result-row {
    display: flex;
    gap: 12px;
    padding: 9px 14px;
    border-bottom: 1px solid #f0f0f0;
    cursor: pointer;
    align-items: center;
    font-size: 0.9em;
}
.lt-result-row:hover { background: #f5f9ff; }
.lt-source-icon { font-size: 1.1em; flex-shrink: 0; }

.lt-lot-header {
    display: flex;
    gap: 14px;
    padding: 10px 14px;
    background: #f8f9fa;
    border: 1.5px solid #e0e0e0;
    border-radius: 6px;
    margin-bottom: 12px;
    flex-wrap: wrap;
    align-items: center;
    font-size: 0.88em;
}

.lt-badge-job {
    background: #dbeafe;
    color: #1e40af;
    padding: 2px 7px;
    border-radius: 4px;
    font-size: 0.78em;
    font-weight: 700;
}
.lt-badge-sale {
    background: #dcfce7;
    color: #166534;
    padding: 2px 7px;
    border-radius: 4px;
    font-size: 0.78em;
    font-weight: 700;
}
.lt-badge-proforma {
    background: #fef9c3;
    color: #854d0e;
    padding: 2px 7px;
    border-radius: 4px;
    font-size: 0.78em;
    font-weight: 700;
}
.lt-badge-return {
    background: #fee2e2;
    color: #991b1b;
    padding: 2px 7px;
    border-radius: 4px;
    font-size: 0.78em;
    font-weight: 700;
}

/* ═══════════════════════════════════════════════════════════
   SALES INVOICE — Phase 10 new design
   ═══════════════════════════════════════════════════════════ */

/* Invoice header */
.inv-hdr { background: linear-gradient(135deg, #8B1A1A, #a52020); padding: 8px 14px; display: flex; align-items: center; gap: 10px; flex-wrap: nowrap; }
.inv-hdr-field { display: flex; flex-direction: column; gap: 1px; flex-shrink: 0; }
.inv-hdr-label { font-size: 9px; color: #ffaaaa; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 500; }
.inv-hdr-inv-no { font-size: 18px; font-weight: 500; color: #fff; }
.inv-hdr-input { font-size: 13px; font-weight: 500; background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.25); border-radius: 6px; padding: 3px 8px; color: #fff; outline: none; }
#inv-customer option { color: #2c3e50; background: #fff; }
.inv-hdr-sep { width: 1px; height: 28px; background: rgba(255,255,255,0.2); flex-shrink: 0; }
.inv-nav-btn { border-radius: 6px; padding: 4px 10px; font-size: 12px; font-weight: 600; border: 1px solid rgba(255,255,255,0.3); background: rgba(255,255,255,0.12); color: #fff; cursor: pointer; }

/* Section groups */
.inv-sec-group { border-radius: 8px; overflow-x: auto; overflow-y: clip; border: 1.5px solid #ccc; box-shadow: 0 1px 3px rgba(0,0,0,0.06); background: #fff; margin-bottom: 8px; flex-shrink: 0; }
#inv-table-a, #inv-table-b { position: sticky; top: 0; z-index: 5; }

/* Grid table */
.inv-grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.inv-col-hdr th { background: #27ae60; color: #fff; padding: 7px 9px; text-align: right; font-size: 1.0em; font-weight: 600; white-space: nowrap; border-right: 1px solid rgba(255,255,255,0.2); border-bottom: 2px solid #1e8449; cursor: pointer; }
.inv-col-hdr th.l { text-align: left; }
.inv-col-hdr th.nosort { cursor: default; }

/* Data rows */
.inv-grid tbody td { padding: 6px 7px; text-align: right; border-bottom: 0.5px solid #c3e6cb; border-right: 1px solid #e8f5e9; font-size: 14px; background: white; white-space: nowrap; overflow: hidden; }
.inv-grid tbody td.l { text-align: left; }
.inv-grid tbody tr:hover td { background: #f0fff4; }
.inv-row-hi td { background: #fff8e1 !important; }
.inv-row-lo td { background: #fde8e8 !important; }
.inv-row-charge td { background: #fafafa !important; font-size: 13px; }
.inv-cell-hidden { width: 0 !important; padding: 0 !important; overflow: hidden !important; border: none !important; font-size: 0 !important; }

/* Footer rows */
.inv-footer-row td { padding: 7px 9px !important; font-size: 1.0em !important; font-weight: 700 !important; white-space: nowrap !important; border-right: none !important; border-bottom: none !important; height: 38px; vertical-align: middle; }
.inv-footer-a td { background: #fafcff !important; border-top: 1.5px solid #d6eaf8 !important; border-bottom: 1.5px solid #d6eaf8 !important; }
.inv-footer-a td:first-child { border-left: 4px solid #2980b9 !important; }
.inv-footer-a2 td { background: #fffaf5 !important; border-top: 1.5px solid #fdebd0 !important; border-bottom: 1.5px solid #fdebd0 !important; }
.inv-footer-a2 td:first-child { border-left: 4px solid #e67e22 !important; }
.inv-footer-ta td { background: #eafaf1 !important; border-top: 1.5px solid #a9dfbf !important; border-bottom: 1.5px solid #a9dfbf !important; font-weight: 600; }
.inv-footer-ta td:first-child { border-left: 4px solid #27ae60 !important; }
.inv-footer-b td { background: #f5fdf7 !important; border-top: 1.5px solid #d5f5e3 !important; border-bottom: 1.5px solid #d5f5e3 !important; }
.inv-footer-b td:first-child { border-left: 4px solid #27ae60 !important; }
.inv-footer-gt td { background: #f0f8f7 !important; border-top: 1.5px solid #a8d5cf !important; border-bottom: 1.5px solid #a8d5cf !important; }
.inv-footer-gt td:first-child { border-left: 4px solid #2a7f6f !important; }

/* Grand total inner rows */
.inv-gt-row td { background: #fff !important; border-bottom: 0.5px solid #f0f0f0 !important; border-right: none !important; }
.inv-gt-row td.has-data { border-right: 1px solid #f0f0f0 !important; }
.inv-gt-bal td { background: #fde8e8 !important; border-right: none !important; }
.inv-gt-bal td.has-data { border-right: 1px solid #f5c6c6 !important; }
.inv-gt-inv td { background: #fafafa !important; border-bottom: 0.5px solid #f0f0f0 !important; border-right: none !important; }

/* Transport box */
.inv-transport-box { border-radius: 8px; overflow: hidden; border: 1.5px solid #ccc; box-shadow: 0 1px 3px rgba(0,0,0,0.06); background: #fff; flex-shrink: 0; }
.inv-transport-content { display: grid; grid-template-columns: 1fr 1fr 1fr; border-bottom: 1px solid #f0f0f0; }
.inv-transport-sec { border-right: 1px solid #f0f0f0; }
.inv-transport-sec:last-child { border-right: none; }
.inv-transport-sec-hdr { padding: 7px 14px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; border-bottom: 1px solid #f0f0f0; }
.inv-transport-sec-hdr.t { background: #f0f4f8; color: #1a5276; }
.inv-transport-sec-hdr.w { background: #f5f0fb; color: #6c3483; }
.inv-transport-sec-hdr.f { background: #fdf6ec; color: #a04000; }
.inv-lv-table { width: 100%; border-collapse: collapse; }
.inv-lv-table tr { border-bottom: 0.5px solid #f5f5f5; }
.inv-lv-table tr:last-child { border-bottom: none; }
.inv-lv-table td { padding: 6px 14px; font-size: 13px; vertical-align: middle; }
.inv-lv-table td.lbl { color: #999; font-weight: 500; width: 50%; font-size: 12px; white-space: nowrap; }
.inv-lv-table td.val { color: #2c3e50; font-weight: 600; }
.inv-lv-table td.val input, .inv-lv-table td.val select { width: 100%; border: none; background: transparent; font-size: 13px; color: #2c3e50; font-weight: 600; outline: none; }
.inv-lv-table td.val input:focus { background: #fffde7; border-radius: 3px; padding: 1px 3px; }
.inv-lv-table td.val input.auto { color: #888; font-weight: 400; font-style: italic; }
.inv-lv-table td.val input.green { color: #1e8449; font-weight: 700; font-style: normal; }
.inv-lv-table td.val input.red { color: #c0392b; font-weight: 700; font-style: normal; }
.inv-lv-table td.val input.orange { color: #d35400; font-weight: 700; font-style: normal; }
.inv-lv-table td.val input.blue { color: #1a5276; font-weight: 700; font-style: normal; }
.inv-wb-inner { display: grid; grid-template-columns: 1fr 1fr; }
.inv-wb-left { border-right: 1px solid #f0f0f0; }
.inv-transport-footer { display: grid; grid-template-columns: 1fr 1fr 1fr; border-top: 1px solid #f0f0f0; background: #f8f9fa; border-left: 4px solid #555; }
.inv-transport-footer-sec { padding: 8px 14px; display: flex; align-items: center; }
.inv-transport-footer-sec:nth-child(2) { border-left: 1px solid #eee; border-right: 1px solid #eee; }
.inv-scroll-zone { overflow-y: auto; }
.inv-sect-gap { background: #f5f5f5; height: 4px; }
.inv-gap-row td { background: #f5f5f5 !important; padding: 2px 0 !important; border: none !important; }
.inv-gap-row-lg td { background: #f0f2f5 !important; padding: 5px 0 !important; border: none !important; }
.inv-arr { color: #aaa; font-size: 12px; cursor: pointer; user-select: none; padding: 0 2px; }
.inv-sec-btn { border: 1px solid currentColor; border-radius: 4px; padding: 4px 12px; font-size: 13px; cursor: pointer; font-weight: 600; background: transparent; white-space: nowrap; margin-left: 8px; vertical-align: middle; }
.inv-pv-code { background: #e8f8f0; color: #1e8449; border-radius: 3px; padding: 1px 4px; font-size: 0.85em; }
#inv-col-dd.open { display: flex !important; }
/* Docked window (spec 5c-B), same technique as Purchase's .purch-entry - a
   sibling inside .main-container, not a fixed-position overlay. .show is
   what makes it visible, kept as a class (not inline style) so the
   .main-container:has(> #inv-screen.show) swap rule below can see it. 40px
   = .main-container's own 20px top + 20px bottom padding, now that Sales
   invoice is in the same 20px-padding scope as Purchase/Stock. */
/* 6px = the container's 3px padding top + bottom. Was 40px, matching the old
   20px padding - which is why a stale ~34px band sat under the bottom bar
   after the padding came down (Ali spotted it, Session 82). */
/* Sand (#f4f2ec = foundation.css --bg), not the old private #f5f6fa blue-gray.
   This ID rule is what actually paints the ground behind the boxes - .si6's own
   --bg (below) is shadowed by it, so changing the token alone showed nothing.
   .maxi's backdrop was already #f4f2ec, so the two now agree. Same on #pfScreen. */
#inv-screen { display:none; width:100%; height:calc(100vh - 10px - var(--mstrip-h)); background: #f4f2ec; flex-direction: column; overflow: hidden; }
#inv-screen.show { display:flex; }
/* Maximise: borrow the viewport on demand, Sales-only variant (Ali's call
   2026-07-29) - a 20px gap on all sides instead of Purchase's flush
   .purch-entry.maxi inset:0. width/height:auto so the inset values (not the
   docked-mode width:100%/height:calc(...) above) determine the box size -
   otherwise the explicit height would override the inset and overflow past
   this smaller box, same bug class as .si6 .shell's height:100vh fix
   earlier this session. Per-invoice, never persisted - see invMinimize()'s
   clear-on-park comment for why. */
/* inset 3px, not 20 (Ali, Session 82): docked now sits at 3px, so a 20px
   maximise made the padding jump when you toggled. The box-shadow is a
   backdrop - position:fixed leaves whatever is behind (the sidebar, the
   screen underneath) visible in the inset band, which is the "background text
   showing through" Ali reported. A 100vmax spread paints everything outside
   the window in the app's own ground colour, with no extra DOM. */
#inv-screen.maxi { position: fixed; inset: 5px; z-index: 1200; width: auto; height: auto;
    box-shadow: 0 0 0 100vmax #f4f2ec; }
.main-container:has(> #inv-screen.show) > .main-section { display: none !important; }
#inv-body { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 8px; }

/* Proforma detail - same docked-window technique as #inv-screen directly
   above (sibling of .main-container, .show toggles visibility, .maxi
   borrows the viewport). Its own CSS is entirely the shared .si6 block below -
   nothing proforma-specific needed beyond this positioning. */
#pfScreen { display:none; width:100%; height:calc(100vh - 10px - var(--mstrip-h)); background: #f4f2ec; flex-direction: column; overflow: hidden; }
#pfScreen.show { display:flex; }
#pfScreen.maxi { position: fixed; inset: 5px; z-index: 1200; width: auto; height: auto;
    box-shadow: 0 0 0 100vmax #f4f2ec; }
.main-container:has(> #pfScreen.show) > .main-section { display: none !important; }

/* Sales Invoice v6 (si6) - scoped styles, append-only to style.css */
/* S171 - THE COLOUR TOKENS ARE INHERITED NOW, NOT REDECLARED.
   Ali, 2026-09-02, comparing the Jobs expense table against the invoice's:
   "both color look different why". Measured: this block was shadowing TEN app
   tokens with a COOL palette while the rest of the app is WARM -
   --line #e7ebf0 vs #e3ddd0, --line-2 #f0f3f6 vs #efeade, --ink #1e2733 vs
   #222a25, --ink-3 #9aa6b4 vs #6c736a, --surface #fff vs #fffefb. No single
   one is visible; together they made every hairline and every muted figure on
   this screen a different temperature from every other screen. That is the
   difference he could see side by side and could not name alone.

   TWO OF THEM WERE ALSO OVERRIDING USER SETTINGS. --accent and --brand are
   theme PICKERS; Ali's accent is Rust. This block pinned them to teal and
   crimson, so changing his theme did nothing here and the SAME `src-purchase`
   tag rendered forest green on Stock and dark red on the invoice.

   Deleted rather than restated with the warm values, so a future change to the
   ground or accent picker propagates here for free - restating would just move
   the divergence to the next theme change.
   --slate/--slate-tint are KEPT: they are the only two the app does not define.
   --radius/--shadow/--hdh/--sbw are layout, not colour, and stay local. */
/* CM112 SETTLED (Ali, 2026-09-03): source tags are plain black text now (see
   the .src-tag rule), so they no longer read --brand/--brand-2/--accent/
   --accent-2 at all - the four tokens that were pinned here specifically to
   hold the OLD per-source colours steady are deleted, per the standing
   instruction ("DELETE THESE FOUR when CM112 is settled"). Everything else
   in this screen that still uses --brand/--accent (the focus ring, a couple
   of hover fills, the badges, the settle-popup's edited-value highlight) now
   inherits the app's real theme tokens instead of the invoice's own private
   teal/crimson - Ali: "we can match header", i.e. those should agree with
   the rest of the app the way .si6 .hdr already does. */
.si6{/* --band: the expense block's ground. Ali, 2026-09-02: the expense table
       and the Totals rows carry the SAME --zebra, but five consecutive tinted
       rows read heavier than the ones and twos Totals shows between untinted
       total rows - same colour, different area. So the band is mixed HALFWAY
       between the two grounds it sits amongst, which makes a five-row slab
       weigh about what one --zebra strip does.
       Derived with color-mix from the two real tokens rather than pinned to a
       fourth hardcoded ground: it keeps following the theme, and the palette
       still has three grounds, not four. color-mix is already used in this
       stylesheet (the .fld-bad ring), so it is not a new dependency. */
  --band:color-mix(in srgb, var(--zebra) 50%, var(--surface));
  --slate:#3f5e84;--slate-tint:#eef3f9;
  --radius:11px;--shadow:0 1px 2px rgba(16,24,40,.05),0 2px 6px rgba(16,24,40,.05);
  --hdh:46px;--sbw:0px;}
.si6 *{box-sizing:border-box}
.si6 *::-webkit-scrollbar{width:11px;height:11px}
.si6 *::-webkit-scrollbar-thumb{background:#cfd6de;border-radius:7px;border:3px solid transparent;background-clip:content-box}
.si6 *::-webkit-scrollbar-thumb:hover{background:#b6c0cb;background-clip:content-box}
.si6 *::-webkit-scrollbar-track{background:transparent}
.si6{background:var(--bg);color:var(--ink);font-family:"Inter",-apple-system,Segoe UI,Roboto,system-ui,sans-serif;font-size:13px;line-height:1.35}
@media (prefers-reduced-motion:reduce){ .si6 *{transition:none!important;animation:none!important} }
.si6 button{font-family:inherit}
.si6 .tnum{font-variant-numeric:tabular-nums}
.si6 :focus-visible{outline:2px solid var(--accent);outline-offset:1px;border-radius:4px}
/* height:100% not 100vh - fills whatever height #inv-screen's own docked
   sizing gives it, instead of claiming the full viewport regardless of
   its container (that mismatch would overflow the docked box). */
/* gap:3px, NOT padding-top on .page: the gap lives in the flex layout, outside
   the scrolling content, so it survives scrolling. As padding it scrolled away
   with the content and the first box's edge ran into the header (Ali caught
   this, Session 82). */
/* ── THE FRAME (S168) ────────────────────────────────────────────────────
   Ali: "increase box size ... header box will grow till footer, footer total
   fixed at bottom ... looks like one single box having all other boxes", and
   then: "old sections need to be same width and space in between them, no
   change needed there ... add footer also inside that frame".
   So NOTHING below this block changes: the cards keep their own border,
   radius, shadow, 12px inset and 9px gaps. All that is new is one frame
   drawn AROUND them. .hdr / .scroll / .totbar are siblings in .shell, so the
   frame is painted in three pieces that meet with no gap - .shell itself only
   carries the shape and the shadow, so the shadow follows the whole frame
   instead of each piece. gap:0, or the pieces would not join up. */
.si6 .shell{display:flex;flex-direction:column;height:100%;gap:0;
  --surface:#fffefb;--line:#e3ddd0;background:var(--bg);
  border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
/* Two stacked rows now (Ali's call, matching Purchase's clean header): hdr-top
   carries the title/mode, view controls (Columns/Collapse all), and the
   window controls (minimise/maximise/close) - everything Purchase's own
   header has an equivalent of. hdr-detail keeps the identity fields (Date/
   Customer/Lorry/Driver) and the remaining action buttons. Cancel is gone
   entirely - the corner X (wired to the same invCancel()) replaces it. */
/* Light header chrome — same pattern as .purch-header (Session 81 repalette).
   Pre-CM112 this escaped .si6's private maroon/teal shadow so the header
   alone read the app's real tokens; since CM112 (2026-09-03) deleted that
   shadow from the whole screen, this override is now redundant with what
   .si6 itself inherits - left in place rather than pulled mid-session, since
   pulling it is a no-op change worth its own diff, not a drive-by. */
.si6 .hdr{position:sticky;top:0;z-index:50;
  --surface:#fffefb;--ink:#222a25;--ink-2:#5d675f;--ink-3:#6c736a;--line:#e3ddd0;--line-2:#efeade;
  --accent:#9e4f2e;--accent-2:#874327;--accent-tint:#f4e7e0;
  background:var(--bg);border:none;
  border-radius:var(--radius) var(--radius) 0 0;color:var(--ink);
  display:flex;flex-direction:column;overflow:hidden}
/* Tightened (Ali, Session 82 - "header looks huge"). Row 1's height was set by
   the 30px icon buttons, not the text, so those come down too. */
.si6 .hdr-top{display:flex;align-items:center;gap:10px;padding:5px 14px}
/* Light tint, boxed and inset like Purchase's .purch-part1 - not a flat fill.
   Matches its exact spacing: .purch-body's padding:8px 12px is the 12px side
   gutter + 8px top gap before the box; .hdr-top's own border-bottom is dropped
   since this box's own border now does that separation job (Ali's call,
   2026-07-31, after forest-green and flat-fill both read wrong). */
/* gap 18px, not 12: row 2 has ~430px of slack at full width, so the fields get
   air rather than sitting shoulder to shoulder (Ali's call, Session 82).
   Vertical is squeezed though - this row was 84px of a 136px header, so the
   tint box's own margin (8->3) and padding (9->5) both come down. */
/* flex-wrap:wrap, not nowrap. Row 2 needs ~903px for its four fields plus the
   Profit and Save buttons; below roughly a 960px window it ran out of room and
   .hdr's overflow:hidden simply CLIPPED the overflow - Save and Profit vanished
   off the right edge with no way to reach them (measured at 900 and 768px,
   Session 82). Wrapping costs a taller header at those widths and keeps every
   control reachable. It never triggers at 1150 half-screen or above, so the
   normal working sizes are unaffected. */
/* Vertical padding 8 -> 12 so the bar holds its ~64px height with a 40px field
   instead of the old 29px one. Ali: do not reduce the height, spend it on type. */
.si6 .hdr-detail{display:flex;align-items:center;gap:18px;padding:12px 14px;flex-wrap:wrap;row-gap:6px;
  margin:0;border:none;border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  border-radius:0;background:var(--head)}
/* Without this the button's own label wraps to three lines before the row
   wraps, which made the card header grow instead of the layout reflowing. */
.si6 .btn-add{white-space:nowrap}
/* F7 (S175): the "expense rates are for date X" note. Full-width own row after the
   header controls (order:99 + flex-basis:100% against .hdr-detail's wrap), amber so
   it reads as an advisory not an error. hidden = zero space. */
.si6 .hdr-detail .si6-rate-note{order:99;flex:1 0 100%;margin:0;padding:6px 10px;
  font-size:0.82rem;font-weight:600;color:var(--warn-deep,#8a5a00);
  background:var(--warn-tint,#fdf3e0);border-radius:var(--radius-sm);line-height:1.3}
.si6 .hdr-title-zone{display:flex;align-items:center;gap:10px}
/* NOT --fs-h1 (22.5px, what Purchase's title uses): Ali's explicit call that
   Sales carries far more data than Purchase, so the two titles deliberately
   do not match here. 1.07rem keys off the root anchor, so the Settings Text
   Size control still scales it. */
.si6-mode-title{font-size:1.07rem;font-weight:800;letter-spacing:-.012em;color:var(--ink)}
/* Bare numbers, no "No."/"Cust No" wording - matches Purchase's .purch-no,
   which shows a bare 10296. */
.si6-badge{font-size:0.9em;font-weight:700;color:var(--accent-2);background:var(--accent-tint);
  border:none;border-radius:var(--radius-pill,99px);padding:2px 11px}
.si6 .hdr-top-actions{display:flex;align-items:center;gap:8px}
.si6 .hdr-top-right{margin-left:auto;display:flex;align-items:center;gap:8px}

/* ── Icon buttons + prev/next, Session 82 ──────────────────────────────────
   .si6-ib is the square icon button (columns / collapse / print / excel /
   profit). Secondary controls, so they follow the button spec's outline->fill
   hover; Save stays always-solid per that spec's sec 6. */
.si6-ib{width:26px;height:26px;flex-shrink:0;display:flex;align-items:center;justify-content:center;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-sm,8px);
  color:var(--ink-2);cursor:pointer;padding:0}
.si6-ib:hover{background:var(--brand-tint);color:var(--ink)}
/* Amber: a deliberate, Ali-approved exception to one-colour-one-meaning. Profit
   is the only icon here with no visible result when pressed (it reveals hidden
   columns), so it needs to read differently from the plain grey set. */
.si6-ib-prof{background:var(--warn-tint);border-color:#e5d3ad;color:var(--warn)}
.si6-ib-prof:hover{background:#f2e2c2;color:var(--warn-deep)}
.si6-ib-prof.active{background:var(--warn);border-color:var(--warn);color:#fff}
.si6 .ic-sm{width:.85rem;height:.85rem}
.si6-vr{width:1px;height:22px;background:var(--line);flex-shrink:0;margin:0 2px}
.si6-hsep{width:1px;height:28px;background:var(--line);flex-shrink:0}
.si6-nav{display:flex;align-items:center;gap:2px;flex-shrink:0;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-sm,8px);padding:2px}
.si6-nav-b{width:24px;height:24px;display:flex;align-items:center;justify-content:center;
  background:none;border:none;border-radius:6px;color:var(--ink-2);cursor:pointer;padding:0}
.si6-nav-b:hover:not(:disabled){background:var(--brand-tint);color:var(--ink)}
.si6-nav-b:disabled{opacity:.3;cursor:default}
.si6-nav-pos{font-size:0.8em;font-weight:700;color:var(--ink-2);padding:0 9px;min-width:52px;text-align:center;white-space:nowrap}
/* THE DOCUMENT HEADER READS AS A HEADING (Ali, S170). The fields used to be
   typed at 11.96px - SMALLER than the 13.5px section titles sitting below them,
   which is why the header read as the quietest thing on the screen instead of
   the loudest. The label moved from ABOVE the field to BESIDE it, which takes
   it out of the vertical stack entirely; that reclaimed height is spent on type
   and on a taller field, and the bar itself stays the height it was (~64px) via
   the padding below. Measured free width in the row was 620px; horizontal
   labels cost ~314 of it. */
.si6 .hf{display:flex;flex-direction:row;align-items:center;gap:8px;flex-shrink:0}
/* margin-bottom:0 - the global .lbl carries margin-bottom:var(--sp-1) (3.75px)
   to separate it from the field BELOW it. Stacked that was the gap; in a row it
   is dead weight, and since align-items:center centres the MARGIN box the text
   sat 1.875px high. Measured: label mid 77.1 vs input mid 79.0, now equal. */
.si6 .hf .lbl{font-size:0.8rem;line-height:1.15;letter-spacing:.3px;text-transform:uppercase;color:var(--ink-3);font-weight:700;flex:0 0 auto;white-space:nowrap;margin-bottom:0}
/* Proforma's Description/Remarks are flex:1 with width:100% inputs. In a row
   .hf that width resolves against the container the label also sits in, so they
   need to be allowed to shrink rather than overflow by the label's width. */
.si6 .hf>.hin,.si6 .hf>.hse{min-width:0}
/* SPREAD THE ROW, DO NOT LEAVE 257px DEAD AT THE RIGHT (Ali, S170). Customer,
   Lorry and Driver grow to share the slack; Date does not - a date field gains
   nothing from being wider. flex-grow acts ON TOP of the inline width in the
   markup, so the widths stay as the starting basis and no template edit or
   server restart is needed. :has() picks the .hf by the control it wraps -
   there is no parent selector, and the wrappers carry no distinguishing class.
   Deliberately keyed to the SALES ids: Proforma has only ~28px of slack and its
   Description/Remarks already flex, so it must not join in. */
/* CUSTOMER IS THE ONLY GROWING FIELD (Ali, S170): "i like customer taking all
   available width". Lorry and Driver are pinned at a width that leaves real
   empty space after a typed value - Ali: "we dont need cramped spaces".
   Measured at 15px: a 12-char registration is 123.7px and a 13-digit mobile
   126.5px, +20px padding/border = 144/147 to merely FIT, so 175/180 leaves
   ~30px of air on the longest realistic value and ~60px on a typical one.
   flex-basis, not width - the markup carries an inline width and basis wins. */
.si6 .hdr-detail .hf:has(#si6-customer){flex:1 1 auto}
.si6 .hdr-detail .hf:has(#si6-f-lorry),
.si6 .hdr-detail .hf:has(#si6-f-driver){flex:0 0 auto}
.si6 .hdr-detail .hf:has(#si6-customer)>.hse{flex:1 1 auto}
.si6 .hdr-detail .hf:has(#si6-f-lorry)>.hin{flex:0 0 175px}
.si6 .hdr-detail .hf:has(#si6-f-driver)>.hin{flex:0 0 180px}
/* Two-line labels. Ali keeps the names - "from years we are using drivers no
   and it ok" - so the width is won by WRAPPING rather than by dropping "No":
   "LORRY NO *" is 92.2px on one line and 50.2px over two, the same 42px saved.
   Two lines are 27.6px against a 40px field, so the bar height does not move.
   Width in em, not px, so the box tracks the label font at every Text size -
   a px box would let the word overflow at Large. */
/* Lorry No / Driver No read CENTRED (Ali, S170). Both hold a fixed-shape
   code - a registration and a mobile - not a quantity, so there is no decimal
   point to line up and no left-edge scan down a column; centred, the value sits
   in the middle of the air the field now carries instead of hard against the
   left edge. Date and Customer are untouched: one is a native date control, the
   other a select. */
.si6 .hdr-detail .hf:has(#si6-f-lorry)>.hin,
.si6 .hdr-detail .hf:has(#si6-f-driver)>.hin{text-align:center}
/* text-align:center so the second line ("NO *") sits centred under the first
   ("LORRY" / "DRIVER"), which is the wider word and therefore sets the box.
   Left-aligned the two lines read ragged (Ali, S170). */
.si6 .hdr-detail .hf:has(#si6-f-lorry)>.lbl{white-space:normal;width:4.5em;text-align:center}
.si6 .hdr-detail .hf:has(#si6-f-driver)>.lbl{white-space:normal;width:5em;text-align:center}
/* The buttons kept their old size while the fields went 29 -> 40, so they read
   as undersized next to them. Scoped to .hdr-detail: the SAME .si6-ib class
   draws the title-bar icon set (columns, print, export), which stays at 26px. */
.si6 .hdr-detail .si6-ib{width:40px;height:40px}
.si6 .hdr-detail .si6-ib .ic{width:1.35rem;height:1.35rem}
.si6 .hdr-detail .hbtn{height:40px;font-size:0.9rem;padding:0 14px;display:flex;align-items:center}
.si6 .hdr-detail .hbtn.save{font-size:0.9rem;padding:0 18px;gap:8px}
.si6 .hdr-detail .hbtn.save .ic{width:1.25rem;height:1.25rem}
.si6 .hf .val{font-size:14px;font-weight:600;color:var(--ink)}
.si6 .hsep{width:1px;height:30px;background:var(--line);flex-shrink:0}
.si6 .hin,.si6 .hse{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-sm,8px);color:var(--ink);
  padding:4px 9px;font-size:1rem;font-weight:700;outline:none;height:40px;box-sizing:border-box;font-family:inherit}
/* 1rem, not the old 0.92em. em multiplies the PARENT, and si6's header parent is
   13px where Purchase's is 16px - the same declared 0.92em rendered 11.96 here
   and 14.72 there. rem also means these fields finally obey the Text size
   setting, which they never did. 15px at Medium: under the 16.05px screen title,
   above the 13.5px section titles. */
.si6 .hin:focus,.si6 .hse:focus{border-color:var(--accent)}
/* CN113 - Customer is the app's own search picker now (createSearchDropdown),
   not a native <select>. Ali: "also customer does have our standard picker".
   The widget builds its own <input> with an INLINE cssText - height:36px,
   1.5px border, 6px radius, 0.93em - which would sit 4px shorter and a shade
   rounder than Date and Lorry beside it, in a header that was measured to
   40/15/8 in S170. Rather than fight that inline style declaration by
   declaration, the WRAPPER keeps .hse and stays the box; the input inside is
   flattened to a transparent filler that fills it. Inline styles are why every
   line here needs !important - nothing else beats a style attribute.
   :focus-within, not :focus - the div never takes focus itself, its input does. */
.hse.hdd{padding:0;display:flex;align-items:center;position:relative}
.hse.hdd>input{width:100%!important;height:100%!important;border:0!important;
  background:transparent!important;border-radius:inherit!important;
  font-family:inherit!important;font-size:inherit!important;font-weight:inherit!important;
  color:inherit!important;padding:4px 9px!important;box-sizing:border-box!important;
  outline:none!important}
.hse.hdd>input::placeholder{color:var(--ink-3);font-weight:400}
.si6 .hse.hdd:focus-within{border-color:var(--accent)}
/* Locked (saved invoice / saved proforma): the wrapper greys, matching a
   disabled native select. The input is disabled AND the wrapper stops pointer
   events - the same two-part lock js_masters.js uses on its Under picker,
   because the widget exposes no disable of its own. */
/* CP115 #1 - the lock is NOT greyed. Ali, 2026-09-02: "i see customer lock,
   it fine, but it cannot be greyed out." A grey box between a white Date and a
   white Lorry No reads as broken or unavailable, when all it means is that the
   customer is settled. The LOCK is untouched and still two-part - salesCustLock
   disables the generated input and stops pointer events on the wrapper - so the
   field still cannot be typed in, tabbed to, or clicked open. The class stays:
   proforma_cancel_guard reads it, and it is the hook for any future styling. */
.hse.hdd.is-locked>input{cursor:default}
.si6 .hin::placeholder{color:var(--ink-3);font-weight:400}
/* .req deliberately paints NO border. The input standard (spec sec 2) binds
   --accent to FOCUS - "you are here". A permanent rust border on Lorry/Driver
   made them look focused at all times and left nothing to distinguish the
   field actually being typed in. The red asterisk carries required on its own
   (Ali's call, Session 82). .flash still fires on a failed save. */
.si6 .hin.flash{animation:si6-flashreq .9s}
@keyframes si6-flashreq{0%,100%{background:var(--surface)}30%{background:var(--warn-tint,#f7edd8)}}
.si6 .req-dot{color:var(--neg);font-weight:800}
.si6 .hbtn{background:var(--surface);color:var(--ink-2);border:1px solid var(--line);border-radius:var(--radius-sm,8px);
  padding:5px 10px;font-size:0.82em;font-weight:600;cursor:pointer;white-space:nowrap}
.si6 .hbtn:hover{background:var(--brand-tint,#e9efe9);color:var(--ink)}
/* Always solid, never outline - button spec sec 6: Save is the highest-frequency,
   most time-critical control, filled so the eye finds it without hunting. */
.si6 .hbtn.save{background:var(--pos-deep);border-color:var(--pos-deep);color:#fff;
  display:flex;align-items:center;gap:7px;padding:6px 15px;font-size:0.86em;font-weight:750}
.si6 .hbtn.save:hover{background:var(--pos)}
.si6 .hbtn.save u{text-underline-offset:2px}
.si6 .spacer{margin-left:auto}
.si6 .dd{position:relative}
.si6 .dd-panel{position:absolute;top:34px;right:0;background:#fff;color:var(--ink);border:1px solid var(--line);
  border-radius:9px;box-shadow:0 8px 24px rgba(16,24,40,.16);padding:8px;min-width:160px;z-index:60;display:none}
.si6 .dd-panel.open{display:block}
.si6 .dd-panel label{display:flex;align-items:center;gap:8px;padding:6px 8px;font-size:13px;cursor:pointer;border-radius:6px}
.si6 .dd-panel label:hover{background:var(--accent-tint)}
/* The Columns menu lists all 16 lot-table columns, not the old 3 - give it a
   real width and let it scroll rather than growing past the docked window.
   popoutAnchor caps the height too; this is the floor for the un-anchored case. */
.si6 .dd-panel.pcm{min-width:212px;max-height:60vh;overflow-y:auto}
.si6 .dd-panel.pcm label{padding:5px 6px;font-size:12.5px}
.si6 .dd-panel.pcm .pcm-locked{opacity:.62;cursor:default}
/* No left/right padding here - the standard 20px .main-container padding is
   the only margin; an extra inner layer would double up on top of it and
   leave the cards narrower than the docked box actually offers. */
.si6 .scroll{flex:1;overflow:auto;background:var(--bg)}
/* No max-width/margin:auto centering - .totbar lives inside .page, so a cap
   here shrank the bottom bar (and every card) below the header's full
   width. Fill whatever the docked box gives us, same as Purchase/Stock's
   tables do, instead of an arbitrary reading-width ceiling.
   No side padding either: .totbar is a child, and it must stay full width so
   the boxes' own 12px margin is what creates the nesting. No padding-top: the
   3px gap is on .shell instead, so it doesn't scroll away.
   No min-height needed any more: the bottom bar moved out of .page entirely
   and is a flex sibling of .scroll, so it sits at the bottom of the window on
   its own without the content having to stretch to push it there. */
.si6 .page{display:flex;flex-direction:column;gap:9px;padding:9px 0}
/* Real tokens, not .si6's own private cool-gray shadow (line 2704) - same
   escape as .hdr above. Border is the visible win here: most of a card's own
   interior is re-covered by its sec-hd/grid children's own hardcoded #fff
   (a separate, larger pass - see PROJECT_STATUS), but the 1px edge around
   every card is .card's own and was reading as cold gray, not warm. */
.si6 .card{--surface:#fffefb;--line:#e3ddd0;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.si6 .sec-body>:last-child{border-bottom-left-radius:8px;border-bottom-right-radius:8px;overflow:hidden}
/* ── Box identity colours (Session 82) ────────────────────────────────────
   Written as literal hex on purpose: .si6's own palette block (line 2704)
   still shadows --brand/--accent/--warn/--ink with the pre-spec maroon+teal
   set, so var() here would resolve to the old colours. Values below ARE the
   real foundation.css tokens, named in the comments. Retokenise these to
   var() once the rest of the invoice body moves off that private palette.

   The logic mirrors what the Jobs screen already settled: forest = the goods
   and the summary, orange = expenses, grey = supporting detail. */
.si6 .card.k-a{border-left:4px solid #2f5240}    /* --brand    - Part A, goods */
.si6 .card.k-c{border-left:4px solid #ab4d0c}    /* --warn-deep - same token as Jobs' Expenses box */
.si6 .card.k-b{border-left:4px solid #3c6650}    /* --brand-2  - Part B, same family as A, one shade apart */
.si6 .card.k-gt{border-left:4px solid #2f5240}   /* --brand    - matches Jobs' Final Costing */
.si6 .card.k-tr{border-left:4px solid #6c736a}   /* --ink-3    - supporting detail, not a money box */
/* Inset so the header and bottom bar run wider than the boxes - that is what
   makes them read as nested, and it lifts these left stripes off the frame
   edge, where they were running into each other and the container. */
.si6 .card,.si6 .totalA{margin-left:12px;margin-right:12px}
.si6 .sec-hd{position:sticky;top:0;z-index:30;display:flex;align-items:center;cursor:pointer;user-select:none;
  background:var(--surface);height:var(--hdh);border-radius:8px 8px 0 0}
.si6 .sec-hd:hover{background:var(--zebra)}   /* was #fbfcfd, a cool grey */
.si6 .hd-left{display:flex;align-items:center;gap:10px;padding:9px 14px;flex:1 1 auto;min-width:0}
.si6 .hd-left .ic{font-size:15px}
/* The invoice carries its own .si6 .ic-sm{.85rem} (line ~3462), which beats the
   app-wide .ic-sm on specificity — so when inline icons were raised globally,
   every section header here stayed at ~12.7px and looked tiny beside its own
   15px bold title. Ali: "svg are very small need to change that also".
   Scoped to .hd-left so the genuinely small icons elsewhere in .si6 are left
   alone, and stated in rem so it still follows the text-size setting. */
.si6 .hd-left .ic-sm{ width:1.3rem; height:1.3rem; }
.si6 .hd-left .title{font-size:13.5px;font-weight:700;white-space:nowrap}
.si6 .sec-body{border-top:1px solid var(--line-2)}
.si6 .card.collapsed .sec-body{display:none}
.si6 .card.collapsed .sec-hd{position:static;border-radius:8px}
.si6 .hd-figs{display:flex;flex-shrink:0}
.si6 .vb{box-sizing:border-box;padding:0 9px;text-align:right;white-space:nowrap;display:flex;align-items:center;
  justify-content:flex-end;font-size:13px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
.si6 .w-bags{width:56px}
.si6 .w-rate{width:78px}
/* 92px, NOT 80: this slot must equal the Weight <col> in the tables below it.
   S168 widened Weight 80 -> 92 in all four si6 colgroups and left this at 80,
   which pushed every running-total left of Weight 12px right of its column.
   If a figure column's width changes, its .w-* twin changes in the same edit. */
.si6 .w-wt{width:92px}
.si6 .w-tot{width:104px}
.si6 .w-avg{width:60px}
.si6 .w-del{width:34px}
.si6 .vb.vtot{color:var(--ink)}
.si6 .vb.vpos{color:var(--pos)}
.si6 .vb.vavg{color:var(--ink);font-weight:600}
.si6 .figs-mini{gap:18px;padding-right:16px;align-items:center}
.si6 .figs-mini span{display:flex;align-items:baseline}
.si6 .figs-mini .k{color:var(--ink-3);font-size:10px;text-transform:uppercase;letter-spacing:.4px;margin-right:5px}
.si6 .figs-mini b{color:var(--ink);font-weight:700}
.si6 .card:not(.collapsed) .figs-mini{display:none}
/* The Transport & Freight header had a long empty middle: the title claimed
   all the free width and its five figures huddled on the right edge (Ali,
   2026-09-16: "header box has so much space, use the space"). The title now
   takes only what it needs and the figures spread across what is left.
   Scoped to this card - it is the only one with a mini strip AND a button. */
.si6 .card.k-tr .hd-left{flex:0 0 auto}
.si6 .card.k-tr .figs-mini{flex:1 1 auto;justify-content:space-between;padding-left:28px;padding-right:20px}
/* "right side" is not "on the border" (Ali, same message). */
.si6 .card.k-tr .sec-hd .tp-reset{flex:none;margin:0 8px 0 auto}
.si6 .card.k-tr .sec-hd .tp-reset.is-off{opacity:.45;cursor:not-allowed}
.si6 .card.k-tr .sec-hd .pl-open{flex:none;margin:0 16px 0 0}
.si6 .gwrap{overflow:visible}
.si6 .grid{width:100%;border-collapse:collapse;table-layout:fixed;min-width:1000px}
/* This was var(--accent) = .si6's private TEAL - the actual thing that still
   read teal on Part A and Part B, since the card's left stripe was already
   forest. Each table header now wears its own box's identity colour. */
/* Sentence case, weight 600, centred - Session 87. This header was UPPERCASE/700
   with extra letter-spacing while six of the app's nine lot tables (and the written
   table standard, sec 3) use sentence case at 600; Sales, Proforma and the lot
   selector were the only holdouts. Size stays per-table, which the standard allows.

   NO VERTICAL DIVIDER between header cells. Sales and Proforma were the only two
   tables in the app that had one; the other seven separate columns by alignment and
   spacing alone. Removing it settles a real bug rather than papering over it: Ali
   reported a white line going missing between Bags and Rate with the rail closed and
   profit open, and again when maximised. Measured cause - under table-layout:fixed
   nearly every column boundary lands on a HALF pixel (Remarks is elastic, so the whole
   row shifts by .5 as the frame changes), and a 1px line split across two device pixels
   paints about half on each and vanishes. That is why only some lines went and why
   which ones changed with the frame. Ali's own call, and the right one: "we dont have
   white lines anywhere else, we can just remove it." */
.si6 .grid thead th{position:sticky;top:var(--hdh);z-index:15;background:#efeadf;color:#222a25;font-size:12.3px;font-weight:600;
  padding:7px 9px;text-align:center;white-space:nowrap;border-bottom:1px solid #e3ddd0}
/* Part B's own #3c6650 head removed here - one label bar for every table. */
/* .si6 .grid thead th.l left-align removed - all headers centre (Ali, S87). */
/* Column grid lines, the pattern Purchase's list already uses (Ali, S168 -
   #purchViewTable thead th+th / tbody td+td, style.css ~3801). Head lines take
   --line #e3ddd0, one step down from the --head bar they sit in; body lines take
   --line-2 #efeade. Literal hex: si6's private palette still shadows both with
   its cool set. Was PART A ONLY on Ali's own earlier call; reversed 2026-09-03
   - "part b we need to add grid (part a has it but we had not given it to part
   b)". Charges stays unruled - CQ116/CT119 deliberately reads as one block of
   costs, not a grid, and this session's ask was Part B specifically. */
/* inset box-shadow, NOT border-left. With box-sizing:border-box and a fixed
   <col width>, a 1px border comes out of the CONTENT box: GDN's header sat at
   scrollWidth 45 / clientWidth 44, exactly on column_label_standard_guard's
   1px tolerance, and the border took clientWidth to 43 and clipped it. A shadow
   paints the same line and consumes no layout width, so every column keeps the
   room it had. */
.si6 .card.k-a .grid thead th + th,.si6 .card.k-b .grid thead th + th{box-shadow:inset 1px 0 0 #e3ddd0}
.si6 .card.k-a .grid tbody td + td,.si6 .card.k-b .grid tbody td + td{box-shadow:inset 1px 0 0 #efeade}
/* S171 - background was a hardcoded #fff. Ali, 2026-09-02: "give our white
   color to part a" and then "actually whole invoice page has to display our
   apps white... we are restoring our standard color". PURE WHITE APPEARS
   NOWHERE ELSE IN THIS APP - the app's white is --surface #fffefb - so Part A
   and Part B were the only pure-white surfaces in the product, which is what
   made them read differently from the cream Charges table beside them. */
.si6 .grid td{padding:4px 9px;text-align:right;border-bottom:1px solid var(--line-2);font-size:13px;white-space:nowrap;color:var(--ink);background:var(--surface);height:32px}
/* Zebra + row hover, per the table standard (sec 3: zebra on, even rows
   --zebra; hover --brand-tint). The invoice had neither, so long lot lists had
   nothing to track your eye along and no feedback about which row you were on.
   .si-alt is stamped from the DATA index in si6_lotRow / si6_chargeRow, never
   :nth-child - sec 4.2, because alert rows get inserted between lot rows and a
   positional stripe would flip everything below them.
   The hover rules themselves live further down, where they already existed.
   tr.si-row-alert's amber uses !important and still wins over both. */
/* --zebra is the app's shared striping token (#faf8f2). The Jobs expense
   table - the reference screen for this one - stripes with the same token
   (.main-table.tbl-exp, style.css ~2382), so this is not a new colour, it is
   the one already in use. */
.si6 .grid tbody tr.si-alt td{background:var(--zebra)}
/* CT119 REVERTED (Ali, 2026-09-03): "restore our zebra." Charges went through
   white -> uniform band -> back to striping, in that order (see docs/OPEN_ITEMS.md
   CT119 for the two tried in between). This override is gone on purpose - not
   just this line: `.si6 .grid.charges td` further down also went, since CT119's
   own revert note warns that dropping only one of the pair leaves the other
   painting every row solid. With both gone, the generic `.si6 .grid tbody
   tr.si-alt td{background:var(--zebra)}` above applies here too, so Charges
   stripes the same way Part A/B and the Jobs expense table do. */
.si6 .grid td.l{text-align:left}
.si6 .grid td.ctr{text-align:center}
/* Was #f5fbfa - a mint barely a shade off white, which is why the row you were
   on read as having no highlight at all. --brand-tint, per the table standard
   (Ali reported it, Session 82). Must sit AFTER the .si-alt stripe above:
   identical specificity, so source order is what decides the winner. */
.si6 .grid tbody tr:hover td{background:var(--brand-tint,#e9efe9)}
/* TOTAL IS BLACK, AND AVG WEIGHT ONLY COLOURS AT THE ENDS (Ali, 2026-09-03).
   Total was --neg, the SAME red that marks a required field's asterisk - one
   colour saying both "you must fill this in" and "this is money", which
   weakened each. A line total is neither negative nor wrong; it is the figure
   the row exists to produce, and it is already bold, right-aligned and under
   a column headed Total. Ali: "total can be black."
   Avg weight was --pos on every row, so green meant "this cell is an
   average" rather than "this one is good" - which is precisely what stopped
   it carrying the light/heavy signal he wanted. Ordinary is plain ink now;
   the two ends use PURCHASE's own colours, --low (light bags) and --neg-deep
   (heavy), against the tints that pair with them, so the same figure reads
   the same way on both screens. Band and thresholds: avgWtBand(), js_core.js.
   Balance keeps its red and the AY profit column keeps its green - both were
   already saying something true, and Ali kept them deliberately. */
.si6 .grid .vtot{color:var(--ink);font-weight:700}
.si6 .grid .vavg{color:var(--ink);font-weight:600}
.si6 .vavg.avg-low{color:var(--low);background:var(--low-tint);border-radius:4px}
.si6 .vavg.avg-high{color:var(--neg-deep);background:var(--neg-tint);border-radius:4px}
/* PART A ROW CURSOR - the same amber idiom as Palla (Ali named it): the exact
   recipe from #inputLotsTbody / #outputLotsTbody in Job Detail and .p2-tbl-body
   in Purchase entry, not a look-alike. :focus-within, not :focus, because the
   real focus lands on an input inside the cell and never on the <tr>.
   MUST sit after the :hover rule above - both use !important, so source order
   decides, and a hovered+focused row has to stay amber rather than flip to the
   hover tint. color:#241f00 deliberately flattens the red Total and green Avg
   for the lit row: that is what the reference does, and dark-on-amber reads
   better than red-on-amber. Was Part A only; Part B joined 2026-09-03 - Ali:
   "part b get cursor focus but no amber row focus" - the keyboard ring
   already walked Part B's rows (CR117 built it for both at once), only this
   CSS was never extended to match. */
.si6 .card.k-a .grid tbody tr.lotrow:focus-within,.si6 .card.k-b .grid tbody tr.lotrow:focus-within{outline:none}
.si6 .card.k-a .grid tbody tr.lotrow:focus-within td,.si6 .card.k-b .grid tbody tr.lotrow:focus-within td{background:#f2d585 !important;color:#241f00}
/* THE FLATTEN STOPS AT THE FIGURES THAT MEAN SOMETHING (Ali, 2026-09-03:
   "when amber row is focused, all color changes to black, is it correct?").
   It was correct when it was written and stopped being correct today. The
   colours it flattened were decorative then - red meant "this is a total",
   green meant "this is an average" - so losing them cost nothing and
   dark-on-amber read better. Now the only red or orange left on a lot row
   says THIS BAG IS HEAVY or THIS LINE IS LOSING MONEY, and flattening them
   hid the one thing worth reading at the moment you put the cursor on the
   row to read it. Measured on a forced row: avg 60.0 went #9c332d -> black
   and a profit of -1,076.38 went red -> black, while the purely decorative
   APMC source tag kept its red throughout (its selector is more specific) -
   so the meaningful colours were being lost and the decorative one kept.
   The BACKGROUND stays amber - the flag's own tint under the row cursor
   would be noise, and these inks were picked to hold up on a light ground.
   Specificity: the flatten above is (0,5,3) and matches `td` directly, so
   these have to name the cell's classes to beat it - inheritance would not
   have been enough. */
.si6 .card.k-a .grid tbody tr.lotrow:focus-within td.vavg.avg-low,
.si6 .card.k-b .grid tbody tr.lotrow:focus-within td.vavg.avg-low{color:var(--low)}
.si6 .card.k-a .grid tbody tr.lotrow:focus-within td.vavg.avg-high,
.si6 .card.k-b .grid tbody tr.lotrow:focus-within td.vavg.avg-high{color:var(--neg-deep)}
.si6 .card.k-a .grid tbody tr.lotrow:focus-within td.pr-profit.ppos,
.si6 .card.k-b .grid tbody tr.lotrow:focus-within td.pr-profit.ppos{color:var(--pos-deep)}
.si6 .card.k-a .grid tbody tr.lotrow:focus-within td.pr-profit.pneg,
.si6 .card.k-b .grid tbody tr.lotrow:focus-within td.pr-profit.pneg{color:var(--neg-deep)}
.si6 .card.k-a .grid tbody tr.lotrow:focus-within td:first-child,.si6 .card.k-b .grid tbody tr.lotrow:focus-within td:first-child{box-shadow:inset 3px 0 0 #8a6d0b}
/* TRANSPORT & FREIGHT ROW CURSOR (Ali, 2026-09-03: "same for transpoort
   movement. they both should work with enter and arrow"). Real <tr>s again
   (plain label/value tables, not .grid), so the original td-painted recipe
   applies unchanged - only the selector is new. No :first-child accent bar:
   the first cell here is the LABEL ("Vehicle No"), and painting an accent
   stripe through the middle of every row's own text label would read as a
   typo mark, not a cursor. */
/* the rebuilt card uses the pop-up's own .pl-f:focus-within cursor */
/* CHARGES ROW CURSOR (Ali, 2026-09-03: "we also need that amber row focus
   also, right now we just have cursor focus"). Same recipe as Part A above,
   just for .card.k-c / tr.chrow - Charges had only the generic per-cell
   `.si6 .grid td:focus` outline, which marks the one cell the cursor is in,
   not the row. td:first-child here IS the name cell: a chrow's first <td>
   is the colspan=10 expense-name cell, not a numbering column like lotrow
   has, so the accent bar lands on the name exactly as it should. */
.si6 .card.k-c .grid tbody tr.chrow:focus-within{outline:none}
.si6 .card.k-c .grid tbody tr.chrow:focus-within td{background:#f2d585 !important;color:#241f00}
.si6 .card.k-c .grid tbody tr.chrow:focus-within td:first-child{box-shadow:inset 3px 0 0 #8a6d0b}
/* EXPENSE NAME, BOLD (Ali, 2026-09-03: "bold the expense names"). Scoped to
   the name cell only (td.l) - the numeric cells beside it (bags/rate/amount)
   keep their own weight, same split Part A/B already use for figures vs text. */
.si6 .chrow td.l{font-weight:700}
.si6 .muted{color:var(--ink-3)}
.si6 .src{font-size:14px}
/* #7: delete × — soft red + bolder/bigger at rest, strong red with a round chip on hover */
/* The invoice / proforma row X. Ali, Session 99: "sales invoice also has old X
   on hover it changes to red" and "proforma has old X" - it was a pale pink
   glyph (#d98a8a) that grew a pink circle on hover, a third treatment for an
   action the rest of the app draws one way. It is a TABLE CELL with the glyph
   as its content, not a button, so it could not join the shared rule in the
   first pass; the glyph now sits in a .rmx span and the cell keeps the click.  */
.si6 .del, td.del{cursor:pointer;text-align:center;padding:0}
/* .si6 .grid td (0,2,1) outranks the rule above (0,2,0), so inside the lot grid
   BOTH its text-align:center and its padding:0 were dead - the X computed
   text-align:right and sat in a 16px content box it overflowed. (0,3,1) wins. */
.si6 .grid td.del{text-align:center;padding:0}
.rmx{ display:inline-flex; align-items:center; justify-content:center;
      background:var(--neg); color:#fff; border:none; border-radius:4px;
      padding:2px 6px; font-size:0.82em; font-weight:700; line-height:1.2; }
td.del:hover .rmx{ background:var(--neg-deep); }
.si6 .cin{width:100%;border:1px solid transparent;background:transparent;font:inherit;font-size:13px;color:var(--ink);
  text-align:right;padding:3px 5px;border-radius:5px;font-variant-numeric:tabular-nums;outline:none}
.si6 .cin.l{text-align:left}
/* fixed-expense info fields (bags/rate/weight) — optional, not in the total: dull + faintly
   boxed so they read as typeable side-notes, not active calc inputs */
.si6 .cin.c-info{color:var(--ink-3);font-style:italic;border-color:var(--line-2)}
.si6 .cin.c-info::placeholder{color:var(--ink-3);opacity:.55;font-style:normal}
.si6 .cin.c-info:focus{color:var(--ink);font-style:normal}
/* fixed-expense / advance-freight amount inputs: match the computed total cells (red + bold) */
.si6 .cin.c-amt-in{color:var(--neg);font-weight:700}
/* CQ116 C - AN EDITABLE CELL MUST LOOK EDITABLE. Ali, 2026-09-02: "in invoice
   all look same, no idea if it a feild which user can edit or input."
   Measured before changing anything: every .cin renders `background:transparent`
   AND `border:1px solid transparent`, so a FILLED editable input was pixel-wise
   identical to read-only text - the only boxed cells in the whole table were the
   fixed-basis `c-info` ones, which is why that row looked like the odd one and
   everything else looked inert.
   This is the Jobs treatment, which Ali approved as the pattern: an editable
   figure sits in a visible box whether or not it holds a value, and read-only
   figures stay plain text. SCOPED TO THE CHARGES TABLE for now (.chrow) - Part A
   and Part B share the .cin class and boxing 20+ lot rows is a much louder
   change, to be looked at separately.
   c-amt-in is included: it is typed into. Hover/focus below still win, being
   later and equally specific. */
/* Ali, 2026-09-02: "input feild are also cremesh". They were --surface
   (#fffefb) sitting on the --zebra band (#faf8f2) - two warm off-whites four
   units apart, so the box read as part of the ground instead of a place to
   type. PURE #fff here is deliberate and allowed: foundation.css's rule is
   "warm paper; NEVER pure #fff on LARGE AREAS", and a 60px input is not a
   large area. It is the contrast against the tinted band that makes the
   editable cells findable, which is the whole point of boxing them. */
.si6 .chrow .cin{background:#fff;border-color:var(--line)}
.si6 .cin:hover{border-color:var(--line)}
/* was a hardcoded teal wash (#eef9f7) that no token could reach - now follows the accent picker */
.si6 .cin:focus{background:var(--accent-tint);border-color:var(--accent)}
.si6 .cin.bad{animation:si6-cellbad .8s}
/* CR117 - A READ-ONLY CELL THE CURSOR CAN LAND ON MUST SHOW IT.
   Ali chose "move to all fields, edit only a few" over skipping, so the arrows
   now stop on Date, GDN, Emp, Source, Party Name, Total and Avg Wt - cells with
   no input in them. Without a mark of their own the cursor simply vanishes for
   a keypress, which is exactly the "i dont see any focus" of S170.
   Drawn with an inset box-shadow, not a border: under box-sizing:border-box a
   real border comes out of the CONTENT box and would shift the figure sideways
   as the cursor passed over it. Dimmer than an input's focus ring on purpose -
   it says "you are here", not "type here". */
.si6 .grid td:focus{outline:2px solid var(--ink-3);outline-offset:-2px;border-radius:4px}
/* Drawn as an OUTLINE, and the first attempt as box-shadow was wrong twice over.
   (1) It lost: `.si6 .card.k-a .grid tbody td + td` (0,3,3) already paints the
   column divider with an inset shadow and outranks `.si6 .grid td:focus`
   (0,3,1), so the ring never appeared - measured, the focused cell still
   reported the divider's `inset 1px 0 0`. The NINTH longer-selector kill on
   this screen; ask the element which rules match it, never read the source.
   (2) Even had it won it would have ERASED that divider, since one element has
   one box-shadow. An outline does not compete with box-shadow at all, and
   outline-offset:-2px draws it inside the cell so nothing shifts and no
   overflow clips it.
   --ink-3 (grey), NOT --accent: an input's focus ring is the accent and means
   "type here". These cells are read-only and the cursor merely passes through
   them, so the mark says "you are here" in a quieter voice. */
@keyframes si6-cellbad{0%,100%{background:#fff}30%{background:#fde0dc;border-color:var(--neg)}}
/* Charges' own #ab4d0c head removed - same one label bar. Its box is still the
   orange one: .card.k-c's left stripe is untouched. */
/* Bags column was 50px, which clipped a 3-digit bag count to "17:" inside the
   input. Only the charges table needs this - Part A/B's own Bags column holds
   per-lot counts, which are shorter (Ali spotted the clipping, Session 82). */
.si6 .grid.charges col:nth-child(11){width:70px}
/* The cream ground is GONE. It was deliberate once - a tinted band marking
   Charges as a different kind of section, with its own stripe and hover steps -
   but it is the reason this table could never match Jobs however the tokens
   were tuned: the GROUND differed, not the ink. Ali's call, 2026-09-02: "exp
   has cream color and it has to come to our colors."
   Left as an explicit --surface rather than deleted, so it beats nothing and
   states the intent where the cream used to be. */
/* CQ116's uniform-band treatment (both the per-cell and the table-level rule
   that used to sit here) is reverted - see the CT119 note above. Charges now
   carries no background override of its own, so the generic `.si6 .grid td`
   (--surface) and `.si6 .grid tbody tr.si-alt td` (--zebra) rules do the same
   job here they do everywhere else. */
.si6 .grid.charges tbody tr:hover td{background:var(--brand-tint)}   /* the app's row hover, as every other table */
/* Was visibility:hidden - a std row's .del <td> renders NO content at all
   (si6_chargeRow: `delTd=std?'<td class="del"></td>':...`), so there was
   nothing to hide; the rule's only real effect was hiding the CELL ITSELF,
   which breaks background continuity. visibility:hidden makes an element
   paint nothing, including its background-color, so the zebra stripe / amber
   row-focus color stopped dead at this column and whatever sat behind the
   <table> showed through instead (Ali, 2026-09-03: "last column zebra does
   not extend"; also the empty-focus complaint below - the gap looked like a
   focusable void because there was no colour there to say otherwise). cursor
   still needs neutralising though, since the generic `.si6 .del, td.del`
   rule sets pointer on every .del regardless of onclick. */
.si6 .chrow.std .del{cursor:default}
/* THE INLINE EXPENSE PICKER (CQ116 G+H). Jobs' mechanism, this screen's
   scale - Ali, 2026-09-03: "for size and design use sales size, as jobs
   header box are bigger. so we just take it working form but size of
   invoice." Jobs' wrapper is 400px with a 36px input, both measured for its
   own roomy form row; si6 runs at 12.5-13px with ~30px controls, so the
   picker is sized to sit in the Charges card without making that card look
   like a different screen. It sits on --zebra so the reveal reads as a strip
   that appeared, not as a table row you could type into. */
.si6 .si6-exp-pick{display:flex;align-items:center;gap:10px;padding:7px 14px;
  background:var(--zebra);border-bottom:1px solid var(--line-2)}
.si6 .si6-exp-lbl{font-size:var(--fs-lbl,10.5px);font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-3);flex:0 0 auto}
.si6 #si6-ddExpense{width:280px;flex:0 0 auto}
/* The picker writes its own inline cssText on the input (height:36px,
   font-size:.93em), so these have to be specific enough to beat it. */
.si6 #si6-ddExpense input{height:30px;padding:5px 9px;font-size:12.5px}
.si6 .si6-exp-cancel{background:var(--surface);color:var(--ink-2);border:1.5px solid var(--line);
  border-radius:8px;padding:5px 12px;font-size:12.5px;font-weight:700;cursor:pointer;font-family:inherit}
.si6 .si6-exp-cancel:hover{background:var(--line-2)}
/* CQ116 D - was a pill reading "STD". Ali: "just use *". A bordered pill drew
   more attention than the fact deserved and sat right beside the expense name,
   which is the thing you actually read. Now a bare mark: no border, no
   background, just the asterisk lifted off the baseline.
   COLOUR DROPPED 2026-09-03 - Ali: "exp table * mark does not carry any
   color." It was --ink-3, its own grey against the expense name's black, so
   a mark that only says "standard, cannot be removed" was tinted differently
   from everything around it. It inherits now and still carries that meaning
   by simply being there. */
.si6 .chip-std{font-size:12px;font-weight:700;color:inherit;
  margin-left:5px;vertical-align:top;line-height:1;cursor:default}
/* Outline at rest, fills solid on hover - the button spec's sec 6 rule for
   secondary controls. Only Save stays permanently filled, so the eye finds it
   without hunting; three always-filled Add buttons were competing with it.
   Each variant wears its own box's identity colour. */
.si6 .btn-add{display:inline-flex;align-items:center;gap:6px;background:var(--surface);color:#2f5240;
  border:1.5px solid #2f5240;border-radius:8px;box-shadow:none;
  padding:6px 13px;font-size:12.5px;font-weight:700;cursor:pointer;transition:background .13s,color .13s}
.si6 .btn-add:hover{background:#2f5240;color:#fff}
/* THE THREE "+ Add" BUTTONS SHARE A LEFT EDGE AND A WIDTH. Each one used to
   begin wherever its own title stopped - measured x=326 / 288 / 362, widths
   131 / 122 / 131 - so nothing lined up down the card. Ali, 2026-09-03:
   "button are of different size and no aligments, i think all button should
   align and have same width right?" The title reserves the width of the
   longest of the five (Part B — Builty Rate / GST Extra, 210px) so every
   button starts at the same x, and the button takes the width of the longest
   label (+ Add / Edit Lots, 131px) with its content centred so the shorter
   one does not sit off to the left inside its own box.
   Scoped to #inv-screen ON PURPOSE: #pfScreen also carries the .si6 class,
   and Proforma's header is a different shape that was never asked about. */
#inv-screen .sec-hd .title{min-width:210px}
#inv-screen .sec-hd .btn-add{width:136px;justify-content:center}
.si6 .page.cust-lock .btn-add{opacity:.45;cursor:not-allowed}
.si6 .btn-add.ghost{background:var(--surface);color:#ab4d0c;border:1.5px solid #ab4d0c;box-shadow:none}
.si6 .btn-add.ghost:hover{background:#ab4d0c;color:#fff}
.si6 .btn-add.slate{background:var(--surface);color:#3c6650;border:1.5px solid #3c6650;box-shadow:none}
.si6 .btn-add.slate:hover{background:#3c6650;color:#fff}
.si6 .foot{display:flex;align-items:center;border-top:1px solid var(--line)}
.si6 .foot .foot-left{flex:1 1 auto;padding:9px 14px;font-weight:700;font-size:12.5px}
/* One total row for every box - --head sand, --ink black, the same as
   Purchase's GRAND row. Was a per-box tint + per-box text colour each. */
.si6 .foot.a,.si6 .foot.b,.si6 .foot.c{background:#efeadf}
.si6 .foot.a .foot-left,.si6 .foot.b .foot-left,.si6 .foot.c .foot-left{color:#222a25}
/* PART A TOTALS ARE A REAL <tfoot> ROW INSIDE THE TABLE - the Jobs model
   (templates/index.html:2047, tbl-in GRAND TOTAL). It shares the table's own
   columns, so every figure sits under its column BY CONSTRUCTION and cannot
   drift. That matters because .si6 .grid is width:100% + table-layout:fixed:
   when the colgroup asks for more than the box (1424 in 1408), the browser
   scales every column down proportionally - which is exactly what put the old
   hand-matched .foot.a flex bar of fixed .w-* slots 16px off its columns. A
   tfoot is immune to that by definition. Label colspan=10 (No.->Lot) then one
   <td> per figure - never a colspan across figure columns (CLAUDE.md). */
.si6 .grid tfoot td{background:#efeadf;color:#222a25;font-weight:700;font-size:13px;
  border-top:1px solid var(--line);border-bottom:0;height:34px;padding:4px 9px;
  font-variant-numeric:tabular-nums}
.si6 .grid tfoot td.ftot-lbl{font-size:12.5px;padding-left:14px;font-weight:700}
.si6 .grid tfoot td.vtot{color:var(--ink)}
.si6 .grid tfoot td.vavg{color:var(--ink);font-weight:600}
/* .si6 .sec-body>:last-child (~5279) sets overflow:hidden to round the bottom
   corners of whatever ends the box. That used to land on .foot.a. Moving the
   totals INTO the table made .gwrap the last child, so it inherited the
   overflow - and an overflow:hidden ancestor becomes the scrollport for a
   sticky <th>. The column header then measured its top:var(--hdh) against
   .gwrap instead of .scroll, so it sat a permanent 46px DOWN inside the table
   (rows 1-2 showing above it) and never stuck while scrolling. Measured:
   th offset from .gwrap top was 46px in Part A vs 0 in Charges/Part B, which
   still have their .foot. :last-child makes this (0,4,0) - it beats 5279
   outright rather than tying with it. The rounding it gave up is drawn on the
   tfoot cells below, which are now what ends the box. */
.si6 .sec-body>.gwrap:last-child{overflow:visible}
.si6 .sec-body>.gwrap:last-child .grid tfoot tr:last-child td:first-child{border-bottom-left-radius:8px}
.si6 .sec-body>.gwrap:last-child .grid tfoot tr:last-child td:last-child{border-bottom-right-radius:8px}
/* Was --pos green. It is the sum of Part A + Charges, not a "positive" - green
   means commit/save everywhere else, so it was borrowing a meaning. --brand-2,
   matching Part B, since both are derived-from-A rather than new information. */
/* min-height 48px so it matches the other five boxes exactly. They get their
   height from .sec-hd's fixed 46px + 1px border top and bottom; Total A has no
   collapsible header, so its height came from its own padding alone and landed
   at 41.5px - visibly tighter than its neighbours (Ali measured it, S82). */
.si6 .totalA{display:flex;align-items:center;min-height:48px;background:var(--surface);border:1px solid var(--line);border-left:4px solid #3c6650;
  border-radius:11px;box-shadow:var(--shadow)}
.si6 .totalA .foot-left{flex:1 1 auto;padding:11px 14px;font-weight:800;font-size:13px;color:var(--ink)}
.si6 .empty-b{text-align:center;color:var(--ink-3);padding:18px;font-size:13px}
.si6 .totals{padding:0}
.si6 .trow{display:flex;align-items:center;border-bottom:1px solid var(--line-2)}
.si6 .trow:last-child{border-bottom:none}
/* TOTALS & GST ROW CURSOR (Ali, 2026-09-03: "it should also get same amber
   focus and cursor focus and arrow movement"). Same amber as Part A/B/Charges
   (#f2d585/#8a6d0b), just painted straight on the .trow itself rather than
   per-<td> - these rows are flex DIVs, so one background covers the row in
   one declaration instead of needing a :first-child accent-bar trick.
   :not(.dormant) matches the ring's own row selector: the Tally row is
   excluded until its lock is designed on purpose, not by accident. */
.si6 .card.k-gt .trow:not(.dormant):focus-within{background:#f2d585 !important;color:#241f00;box-shadow:inset 3px 0 0 #8a6d0b;outline:none}
.si6 .card.k-gt .trow:not(.dormant):focus-within .hint{color:#6b5a1f}
/* THE MARKET FEE HINT IS RED (Ali, 2026-09-03). It is the one hint on this
   card that states an EXCLUSION rather than describing a sum - Part B
   carries no market fee, so anyone reading the row needs to see that the
   base deliberately leaves money out, not assume the figure is wrong.
   --neg is the app's own red; the focus rule below swaps it for the deeper
   #9c332d that Balance already uses, because the ordinary red loses its
   footing on the amber row cursor. */
.si6 .trow.mf .hint{color:var(--neg);font-style:italic;font-weight:600}
.si6 .card.k-gt .trow.mf:not(.dormant):focus-within .hint{color:#9c332d}
/* THE READ-ONLY FIGURE CURSOR (Total A, Grand Total, Balance, the footer
   bar). Ali, 2026-09-03: "ring around total figure (read only values) is
   very small making actuall figure look small." The browser's own ring hugs
   the glyphs, so a big number ended up inside a small box and read as
   shrunken. Drawn on the SLOT instead - which is already the width of its
   column - with offset so it breathes, and as an OUTLINE so it costs no
   layout width and cannot shift the figure it marks (the same reason the
   lot-cell cursor is an outline; see the CR117 note above).
   Drawn INSIDE the slot (negative offset), never outside it: the footer's
   Grand Total is the first cell in the bar and sits hard against the page
   edge, and an ancestor carries overflow:hidden, so an outward ring was
   clipped and ran off the page - Ali: "footer ring goes out of area".
   Inside costs nothing here because the slot is already a whole column
   wide, which is the point - the ring marks the CELL you are standing in,
   so the figure keeps its own size instead of being squeezed by a ring
   drawn around its digits.
   MUST BEAT `.si6 :focus-visible` (~5156), which paints si6's private teal
   at offset 1px on anything focusable in this screen. It does on
   specificity - but the first two attempts never got the chance. A leftover
   comment terminator left this paragraph outside the comment, and the CSS
   parser resynchronised by eating the rule underneath it; the figure slot
   stayed teal while the footer rules below went gold, which is what gave it
   away. The SECOND attempt spelled that terminator out in the prose here -
   which closed this comment early and did it again. Never write those two
   characters inside a CSS comment; describe them. */
/* HEIGHT comes from padding, and the negative margin gives it back, so the
   ring grows without moving anything. Measured: the Total A strip is 48px
   tall while each figure slot is 18px - one line of text, `padding:0 9px`,
   no vertical padding at all - so a ring on the slot box hugged the digits
   top and bottom however wide it was. Ali: "ring is very small, i am
   talking in term of height ... it look like big ring and a value inside
   it." 7px each way makes the box 32px inside the 48px strip; the matching
   -7px keeps the row exactly the height it was, which matters because these
   slots are laid out on the lot table's columns and any real growth here
   would push the strip out of step with them. */
.si6 .hd-figs > span:focus{
  padding-top:7px; padding-bottom:7px; margin-top:-7px; margin-bottom:-7px;
  outline:2px solid #8a6d0b; outline-offset:-1px; border-radius:6px;
  background:rgba(255,255,255,.5);
}
.si6 .totbar > .cell:focus,
.si6 .totbar > .reprint-tag:focus{
  outline:2px solid #8a6d0b; outline-offset:-2px; border-radius:5px;
  background:rgba(255,255,255,.5);
}
.si6 .reset-auto:focus{ outline:2px solid #8a6d0b; outline-offset:2px; border-radius:4px; }
/* Grand Total's own figures were 13px against a 15px label - the headline
   number on the card read smaller than the words next to it. Balance is the
   figure that settles what is actually owed, and Ali: "its very important
   value in terms of accounting ... it look small". Both now carry their
   weight; the row keeps one stop on Balance, so the ring lands on the
   number itself rather than a wide flat band. */
.si6 .trow.grand .vb{font-size:15px}
.si6 .trow.bal .trow-left{font-size:14px}
.si6 .trow.bal .vb.w-tot{font-size:16px}
.si6 .trow-left{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:9px;padding:9px 14px;font-weight:600;color:var(--ink-2)}
/* ONE LABEL COLUMN, so what follows the label lines up down the card. The
   labels were bare text nodes of whatever width the words happened to be -
   measured: Market Fees 77px, GST 27px, Round Off 63px, Grand Total 87px -
   so the % boxes landed 51px apart (x=201 against x=150) and nothing in the
   card shared a left edge. Ali, 2026-09-03: "mf % and gst % are in
   difference space can we give alinmentts to them also?" 96px is the widest
   of them (Grand Total, 87) plus room; they had to be wrapped in a span in
   the template first, because a text node has no width to set. */
.si6 .trow-left .tlab{flex:0 0 96px}
/* The percent column, reserved the same way, so the hints start together
   too. Round Off has no percent to enter, and without an empty slot holding
   its place its hint began 77px left of the other two - one row out of step
   in a card that had just been lined up. The span is empty on that row on
   purpose: it is a held column, not a missing control. */
.si6 .trow-left .tpct{flex:0 0 78px;display:inline-flex;align-items:center;gap:6px}
.si6 .trow.gst,.si6 .trow.mf,.si6 .trow.round{background:var(--zebra)}
/* No background at all (Ali, Session 82). The row is carried by weight and by
   FOREST TEXT, matching this box's own left stripe - the same "the box's colour
   is its hint" rule every other box follows. Weight 700 not 800 + letter-
   spacing: at this size 800 thickens strokes inward and closes the counters,
   which is what read as cramped. */
.si6 .trow.grand{background:transparent}
.si6 .trow.grand .trow-left{color:#2f5240;font-weight:700;font-size:15px;letter-spacing:.012em}
.si6 .trow.grand .vb{color:#2f5240;font-weight:700;letter-spacing:.012em}
.si6 .trow.tally{background:transparent}
/* No pink wash. A large pale fill is what read childish - red carried by the
   TEXT reads serious. A 2px red rule marks it as the bottom line instead, and
   the text uses the deeper --neg-deep so it holds up without a tint behind it. */
.si6 .trow.bal{background:var(--surface);border-top:2px solid #c0433c}
.si6 .trow.bal .trow-left{color:#9c332d;font-weight:700;letter-spacing:.012em}
.si6 .trow.bal .vb{color:#9c332d;font-weight:700;letter-spacing:.012em}
.si6 .trow.dormant{opacity:.42}
.si6 .trow.dormant .tin{pointer-events:none}
/* Active (non-dormant) Tally row pops with the accent tint + darker labels so it
   doesn't read as dull like the dormant state. */
/* No fill here either (Ali, Session 82) - it was var(--accent-tint), which
   inside .si6 resolved to the private TEAL and left a mint band between the
   total and the balance. The row is marked by its darker label alone. */
.si6 .trow.tally:not(.dormant){background:transparent}
.si6 .trow.tally:not(.dormant) .trow-left{color:#222a25}
.si6 .trow.tally:not(.dormant) .cap{color:#874327}
/* Sentence case, not uppercase (Ali, Session 82). Only used by the Inv Amount
   caption, so no other label is affected. */
.si6 .cap{color:var(--ink-3);font-size:11px;letter-spacing:.2px;font-weight:700}
.si6 .vb.box-in{padding:0}
.si6 .tin{width:100%;text-align:right;border:1px solid var(--line);border-radius:6px;padding:4px 9px;font-size:13px;font-weight:700;
  font-family:inherit;color:var(--ink);background:#fff;font-variant-numeric:tabular-nums;box-sizing:border-box}
.si6 .tin-sm{border:1px solid var(--line);border-radius:6px;padding:4px 8px;font-size:13px;font-family:inherit;font-variant-numeric:tabular-nums}
.si6 .gpct{width:46px;text-align:center;border:1px solid var(--line);border-radius:6px;padding:3px 4px;font-size:13px;font-weight:700;font-family:inherit;font-variant-numeric:tabular-nums}
.si6 .reset-auto{font-size:10px;color:var(--accent-2);cursor:pointer;text-decoration:underline;margin-left:8px;display:none}
.si6 .trow.gst.over .reset-auto{display:inline}
.si6 .trow.mf.over .reset-auto{display:inline}
.si6 .trow.round.over .reset-auto{display:inline}
.si6 .hint{font-size:10.5px;color:var(--ink-3);font-style:italic;margin-left:8px}
/* -- EA (S196): the Transport & Freight card is the Part Load pop-up's own
   shape. Ali, 2026-09-16: "i like the pop up style transport section can we
   use that in our full invoice ... part load has 3 horizontal blocks." The
   four <table>s it was built on are gone; the walk that rode on them is now
   geometric (si6TrMove, js_sales.js). Rows are a FIXED height - a row that
   can grow is a row that jolts (S194) - and every figure sits in a 96px
   right-aligned slot so it lines up under the box above it. */
/* Three horizontal blocks, the pop-up's own: the lorry strip, then Weight and
   Freight side by side - and the five fields the pop-up has no use for (From,
   To, Driver Name, Licence, Remarks) join the strip. Every part is the
   pop-up's (.pl-card/.pl-f/.pl-grid2/.pl-v), so there is ONE set of field
   styles for both screens, not two that drift. */
.si6 .tp{display:flex;flex-direction:column;gap:var(--sp-3);padding:var(--sp-3);background:var(--bg)}
.si6 .tp-strip{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))}
.si6 .tp-strip .pl-f:not(:nth-child(3n)){border-right:1px solid var(--line-2)}
.si6 .tp-strip .pl-f:nth-last-child(-n+3){border-bottom:none}
/* 34px, not the pop-up's 36: it keeps the rebuilt card the same height as the
   one it replaces (415px measured before, 423 after). */
/* ALI'S OWN LAYOUT (sketch, 2026-09-16), after three passes that each held all
   28 fields in the pop-up's two boxes and each read busy: "i like part loads
   arrangments but not able to reproduce similar". The pop-up has ELEVEN
   fields, which is why it looks calm - so Weight and Freight get exactly the
   pop-up's shape back (2 across), and the seven bag figures the truck screen
   never shows move to their own strip, read left to right as a sum. */
.si6 .tp .pl-f{height:34px;grid-template-columns:132px 1fr}
.si6 .tp-g4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr))}
.si6 .tp-g4 .pl-f:not(:nth-child(4n)){border-right:1px solid var(--line-2)}
.si6 .tp-g4 .pl-f:nth-last-child(-n+4){border-bottom:none}
.si6 .tp-pay .pl-f .cin{font-size:15px}
.si6 .tp-pay .pl-f .pl-lbl{font-size:11px}
.si6 .tp .pl-rem{width:100%;max-width:none;text-align:left}
/* The bags block is the PART LOAD TABLE, not a field list: a sand header row
   of centred labels and one centred figure under each, seven across. Ali,
   2026-09-16: "part load use label and feild below properly and u just have
   few feild and using 2 rows?" - and he is right that seven fit, because a
   label ABOVE its figure only needs the wider of the two (~110px), not both
   side by side (~204px). An earlier build put the label above but left-aligned
   over a right-aligned figure, so the pair never looked joined: centring both
   is what makes it read as one column. */
.si6 .tp-bags{display:grid;grid-template-columns:repeat(4,132px 1fr)}
.si6 .tp-bh{background:var(--head);border-bottom:1px solid var(--line);border-right:1px solid var(--line-2);padding:5px 6px;text-align:center;font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.si6 .tp-bh:nth-child(8){border-right:none}
.si6 .tp .tp-bv{display:flex;align-items:center;justify-content:center;gap:4px;height:36px;border-bottom:none;border-right:1px solid var(--line-2);padding:0 6px}
.si6 .tp .tp-bv:last-child{border-right:none}
.si6 .tp .tp-bv .wbout{width:auto;padding:0;text-align:center}
.si6 .tp .pl-f.span4{grid-column:1/-1}
.si6 .tp .pl-f.span2{grid-column:span 2}
.si6 .tp .pl-rem{width:100%;max-width:none;text-align:left}
.si6 .tp .pl-blank{background:transparent}
.si6 .tp .cin{height:28px;padding:0 6px;box-sizing:border-box;border:1px solid var(--line);border-radius:5px;background:#fff;font:inherit;font-size:13px;font-weight:600;color:var(--ink);outline:none}
.si6 .tp .cin:focus{background:var(--accent-tint);border-color:var(--accent)}
.si6 .tp .pl-num{width:96px;text-align:right;font-variant-numeric:tabular-nums}
.si6 .tp .pl-txt{width:100%;max-width:210px;text-align:left}
.si6 .tp .pl-sel{width:100%;max-width:240px;height:28px}
/* EC-TRANSPORTPICKER: Transport Name is a createSearchDropdown now, not a
   <select> - size its generated <input> to the same box the select used to
   fill (#ddGodown/#ddEmployee input do the same thing for their own screen). */
.si6 .tp #si6-t-name input{width:100%;max-width:240px;height:28px;border:1px solid var(--line);border-radius:5px;font:inherit;font-size:13px;padding:0 6px;box-sizing:border-box}
.si6 .tp #si6-t-name input:focus{outline:none;border-color:var(--accent);background:var(--accent-tint)}
.si6 .tp #si6-t-name input::placeholder{color:#9aa6b4;font-style:italic}
/* A read-only box is a FIGURE, not a field: no border, no fill - it reads as
   the worked-out .pl-v/.wbout beside it. They stay <input>s because the JS
   writes .value to them. */
.si6 .tp .cin[readonly]{background:transparent;border-color:transparent;font-weight:700}
.si6 .tp .cin::placeholder{color:#bcc6d2;font-style:italic;font-weight:500}
.si6 .tp .pl-notef{justify-items:start}
.si6 .tp .pl-notef .pl-lbl{display:none}
/* Three light grounds from the real palette. The middle one was #f3f0f9 with
   #6c4ea0 text - a purple that appears nowhere in the design system at all. */
/* Transport's three channel labels were mint / sand / amber, each with its own
   text colour - three more label styles on a screen that now has one. */


/* nowrap + auto width: the labels were wrapping to 2-3 lines ("WB WEIGHT (KG)"
   became three), and every wrap added a row's worth of height to the whole
   card. They fit on one line if allowed to claim what they need. */
/* display:table-cell is LOAD-BEARING, not tidying. The app-wide `.lbl`
   class (a form-label style, `display:block`) also matches these <td
   class="lbl">, and a display:block td drops out of the table layout
   altogether: measured 2026-09-03 on the focused Driver No row - the cell
   was 26px wide (its padding, nothing else), its text overflowed to
   scrollWidth 82, and 156px of the row belonged to no cell at all. That gap
   is why Ali reported "row focus in each row is broken" - both real cells
   painted amber correctly and the hole between them could not. The label
   column has been broken this whole time; the row cursor is just the first
   thing that drew a background wide enough to show it. */

/* The pop-up's boxes: white on --line, 28px tall, and a numeric one is a
   fixed 96px right-aligned - the same slot a worked-out .wbout figure uses,
   so box and figure line up in one column. */

.si6 .tp .auto{color:var(--ink-3);font-style:italic}
.si6 .tp .gp{color:var(--pos)}
.si6 .tp .rd{color:var(--neg)}
.si6 .tp .bl{color:var(--slate)}

.si6 .mirror{background:#fff7e8!important;border-radius:3px}
/* Charcoal, but the PALETTE's charcoal: --ink #222a25, which is faintly green
   and sits with the forest and sand. The bar's original #0f1825 was a
   blue-black from the pre-spec palette and read cold against everything else
   (Ali compared both, Session 82). margin-bottom 10->3 to match the 3px the
   frame now uses on every other edge. */
/* A flex sibling of .scroll now, not a sticky element inside it (Session 82).
   As a child of the scrolling content its sticky bottom:0 landed exactly on
   .scroll's bottom edge - and so did the horizontal scrollbar, which on this
   platform is an OVERLAY taking zero layout space. The bar was painted on top
   of the scrollbar, so at narrow widths the table could be scrolled sideways
   but the control to do it was unreachable (Ali reported it; measured:
   scrollbar band and totbar both ended at y=777).
   Out here it sits below the scrollport, so the scrollbar is always grabbable,
   and it no longer widens with .page when si6_syncWidth() grows it for the
   profit columns - the footer holds still while the content scrolls under. */
.si6 .totbar{flex-shrink:0;z-index:40;display:flex;align-items:stretch;
  --surface:#fffefb;--line:#e3ddd0;--ink:#222a25;--ink-2:#5d675f;--ink-3:#6c736a;
  margin:0;background:var(--head);color:var(--ink);
  border:none;border-top:1px solid var(--line);border-radius:0 0 10px 10px}
.si6 .totbar .cell{padding:9px 18px;display:flex;flex-direction:column;gap:2px;border-right:1px solid var(--line);justify-content:center}
.si6 .totbar .cell .k{font-size:8.5px;letter-spacing:.7px;text-transform:uppercase;color:var(--ink-3);font-weight:700}
.si6 .totbar .cell .v{font-size:16px;font-weight:800;color:var(--ink)}
.si6 .totbar .grand .v{color:var(--ink)}
.si6 .totbar .bal .v{color:#c0433c}    /* --neg */
.si6 .totbar .pay .v{color:#ab4d0c}    /* --warn-deep, same token Jobs' Expenses uses */
.si6 .totbar .inv .v{color:var(--ink)}
.si6 .totbar .frt .v{color:var(--ink)}
.si6 .totbar .adv .v{color:var(--ink)}
.si6 .totbar .bal.dim .v{color:var(--ink-3)}
.si6 .totbar .fill{flex:1;border:none}
/* Matches the header's Save - same green, same icon, same underlined S - just
   bigger, since it's the end-of-screen commit (Ali, Session 82). */
.si6 .totbar .save-lg{align-self:center;margin:0 14px;background:var(--pos-deep);border:none;color:#fff;
  font-weight:750;font-size:14px;padding:9px 20px;border-radius:9px;cursor:pointer;
  display:flex;align-items:center;gap:8px;font-family:inherit}
.si6 .totbar .save-lg:hover{background:var(--pos)}
.si6 .totbar .save-lg u{text-underline-offset:2px}
.si6 .note{font-size:11px;color:var(--ink-3);text-align:center;padding:2px 0 10px}
.si6 .picker-backdrop{position:fixed;inset:0;background:rgba(15,24,37,.28);z-index:80;display:none;align-items:center;justify-content:center}
.si6 .picker-backdrop.open{display:flex}
.si6 .picker{background:#fff;border-radius:12px;box-shadow:0 18px 50px rgba(16,24,40,.3);width:380px;max-width:92vw;overflow:hidden}
.si6 .picker h4{margin:0;padding:13px 16px;font-size:14px;border-bottom:1px solid var(--line);background:var(--warn-tint);color:var(--warn)}
.si6 .picker .opt{display:flex;align-items:center;gap:10px;padding:11px 16px;border-bottom:1px solid var(--line-2);cursor:pointer;font-size:13px}
.si6 .picker .opt:hover{background:var(--accent-tint)}
.si6 .picker .opt .meta{margin-left:auto;font-size:11px;color:var(--ink-3)}
.si6 .picker .opt.disabled{opacity:.4;cursor:not-allowed;background:#fafbfc}
.si6 .picker .opt.disabled .meta::after{content:" - already added";color:var(--neg)}
.si6 .picker .padd{padding:11px 16px;display:flex;gap:8px;align-items:center;background:#fafbfc}
.si6 .picker .padd input{flex:1;border:1px solid var(--line);border-radius:7px;padding:7px 9px;font-family:inherit;font-size:13px}
.si6 .picker .pclose{padding:10px 16px;text-align:right}
.si6 .picker .pclose button{border:1px solid var(--line);background:#fff;border-radius:7px;padding:6px 14px;font-weight:600;cursor:pointer}
/* scm-pop footer: "Apply All Lots" solid green (same --pos-deep as Save/Apply
   All), "Later" solid red - same filled-not-outline pattern as .pf-cancel-btn. */
.si6 .scm-pop .pclose .primary{background:var(--pos-deep);border-color:var(--pos-deep);color:#fff}
.si6 .scm-pop .pclose .primary:hover{background:var(--pos);border-color:var(--pos)}
.si6 .scm-pop .pclose button:not(.primary){background:var(--neg-deep);border-color:var(--neg-deep);color:#fff}
.si6 .scm-pop .pclose button:not(.primary):hover{background:var(--neg);border-color:var(--neg)}
/* Optional lot-table columns. The width lives HERE, never as an inline style on
   the <col> - an inline width beats the .show-X rule and the column would simply
   never collapse. Date/Source/Remarks joined Party/GDN/Emp in S88 when the
   Columns menu grew to Ali's six. */
.si6 .oc-p,.si6 .oc-g,.si6 .oc-e,.si6 .oc-d,.si6 .oc-s,.si6 .oc-r{width:0;min-width:0}
.si6 .oh-p,.si6 .oh-g,.si6 .oh-e,.si6 .oc-pc,.si6 .oc-gc,.si6 .oc-ec,
.si6 .oh-d,.si6 .oh-s,.si6 .oh-r,.si6 .oc-dc,.si6 .oc-sc,.si6 .oc-rc{padding-left:0;padding-right:0;overflow:hidden;white-space:nowrap}
/* PARTY NAME IS THE ONE ELASTIC COLUMN (Ali, S170). It was a hard 240px and
   Remarks was elastic - so the surplus pooled in a rarely-used column while the
   longest values in the table sat clipped. With exactly one auto column every
   behaviour falls out of the cascade with no per-column handler: hide ANY column
   and Party Name takes the width; turn Profit on and Party Name pays for it.
   min-width is the real decision - at the floor the table stops shrinking and
   starts overflowing its box, which is what makes the section-header bar drift.
   Party Name is the safest column to squeeze because Code sits two cells away -
   but Code is NOT a substitute: 104 codes are shared by more than one party
   (ST is 17 parties), so the name is the only thing that identifies the row.
   Floor is 175px (Ali, S170) - it does not bind in any normal frame, where the
   column settles at 214-482; it is the reserve that keeps a narrow window from
   tipping the table into overflow. */
.si6 .page.show-p .oc-p{width:auto;min-width:175px}
.si6 .page.show-g .oc-g{width:48px}
.si6 .page.show-e .oc-e{width:44px}
.si6 .page.show-d .oc-d{width:70px}
/* 82 -> 66: sized for "Purchase" (71.7px) until APMC/GST replaced it. The
   widest label in the set is now MERGE at 46.2px, +18px cell padding = 64.2,
   and the "Source" header needs ~63. Party Name absorbs the 16px. */
.si6 .page.show-s .oc-s{width:66px}
/* Remarks is the ELASTIC column - no width, so it absorbs whatever the fixed
   layout has left over. That is not cosmetic: the grid is width:100% under
   table-layout:fixed, so if the declared column widths sum to LESS than the
   rendered width the surplus is redistributed across EVERY column, each one
   grows, and the running-total bars - fixed-width flex slots that are not part
   of the table - all drift. Measured: hiding Remarks alone drifted Bags by 56px,
   hiding all six optional columns by 354px.
   So there must always be exactly one elastic column, and it must sit LEFT of
   Bags or the whole figure block shifts. Remarks does it while it is shown;
   ITEM takes over when it isn't, which is why Item's width lives here too
   rather than inline on the <col>. Item is a locked column, so this fallback
   can never itself be hidden. */
/* Remarks was the elastic column; it is now a fixed 90px. Rarely used, and the
   input scrolls horizontally, so a long remark is windowed, never lost. */
.si6 .page.show-r .oc-r{width:90px}
.si6 .oc-item{width:84px}
/* Item is the FALLBACK elastic column, used only when Party Name is hidden -
   without it a table with Party Name off would have no auto column at all.
   Was :not(.show-r), from when hiding Remarks handed Item the elasticity and
   ballooned it to 326px to hold "DP". */
.si6 .page:not(.show-p) .oc-item{width:auto;min-width:84px}
.si6 .page.show-p .oh-p,.si6 .page.show-p .oc-pc{padding-left:9px;padding-right:9px}
.si6 .page.show-g .oh-g,.si6 .page.show-g .oc-gc{padding-left:9px;padding-right:9px}
.si6 .page.show-e .oh-e,.si6 .page.show-e .oc-ec{padding-left:9px;padding-right:9px}
.si6 .page.show-d .oh-d,.si6 .page.show-d .oc-dc{padding-left:9px;padding-right:9px}
.si6 .page.show-s .oh-s,.si6 .page.show-s .oc-sc{padding-left:9px;padding-right:9px}
.si6 .page.show-r .oh-r,.si6 .page.show-r .oc-rc{padding-left:9px;padding-right:9px}
/* The Remarks cell holds an input on Sales - zeroing the CELL's padding is not
   enough, the input keeps its own box and would still paint a sliver. */
.si6 .oc-rc .cin{width:100%;min-width:0}
/* Centre the short-code columns: GDN, Emp, Party, Item, Lot (headers + values) */
.si6 .grid td.oc-gc,.si6 .grid td.oc-ec{text-align:center}
/* Source reads left, like Party Name (Ali, S169). The cell is rendered with
   class "ctr oc-sc", so this has to BEAT .si6 .grid td.ctr - identical
   specificity (0,3,1), which means source order is the only thing deciding it.
   Keep this rule BELOW that one (style.css ~5380) or it dies silently.
   The header stays centred: all si6 headers centre (Ali, S87). */
.si6 .grid td.oc-sc{text-align:left}
/* A clipped Party Name gets an ellipsis, not a hard cut (Ali, S170). The rule
   that gives these cells overflow:hidden sets no text-overflow at all, so it
   computed `clip` - verified in the browser, not read off the source. Needs
   (0,3,1) to sit above .si6 .grid td. Matters more now that Party Name is the
   elastic column and is deliberately allowed to squeeze down to 175px. */
.si6 .grid td.oc-pc{text-overflow:ellipsis}
.si6 .grid th.oh-g,.si6 .grid th.oh-e,.si6 .grid th.ctr{text-align:center}
.si6 .cin.f-party,.si6 .cin.f-item,.si6 .cin.f-lot{text-align:center}
/* .si6 used to duplicate #inv-screen's fixed-overlay positioning here
   (position:fixed;inset:0;z-index:1000, and it won the specificity fight
   against #inv-screen's own rule) - removed now that #inv-screen is a
   docked window, not a full-viewport overlay. */
/* margin-left/right deliberately NOT reset here - the 12px inset above is what
   nests the boxes inside the full-width header and bottom bar (Session 82). */
.si6 .card{padding:0;margin-top:0;margin-bottom:0}
.si6 thead{position:static}
.si6 .hse option{color:#1f2d3d;background:#fff}
/* No invert. The glyph was flipped to white for a dark header this screen no
   longer has, so on the white date field it was white on white - invisible
   (Ali, S168: "date does not have calender symbol"). #si6-tally-date used to
   carry its own filter:none for exactly this reason; that override is now the
   rule, so it is gone rather than left restating the default. */
.si6 input[type=date]::-webkit-calendar-picker-indicator{opacity:.85;cursor:pointer}

/* si6 transport/freight (3e): empty-field affordance + hint */
.si6 .tp .pl-sel.empty{color:#9aa6b4;font-style:italic}
.si6 .tp .wb-hint{font-size:10.5px;color:var(--ink-3);font-style:italic}

/* si6 weighbridge (3e-c): output values + shortage colour logic */
.si6 .tp .wbout{display:inline-block;width:96px;box-sizing:border-box;padding:0 6px;text-align:right;font-size:13px;font-weight:700;font-variant-numeric:tabular-nums}
.si6 .tp .wbout.muted{color:var(--ink-3);font-weight:600}
.si6 .tp .wbout.pos{color:var(--pos)}
.si6 .tp .wbout.wb-g{color:var(--pos)}
.si6 .tp .wbout.wb-o{color:var(--warn)}
.si6 .tp .wbout.wb-r{color:var(--neg)}

/* si6 transport/freight (3e-a fixes): taller rows + add-transport modal */

.si6 #si6-tp-modal .tp-form{display:flex;flex-direction:column;gap:11px;margin:8px 0 16px}
.si6 #si6-tp-modal .tp-form label{display:flex;flex-direction:column;gap:4px;font-size:11.5px;font-weight:700;color:var(--ink-2)}
.si6 #si6-tp-modal .tp-form input{padding:8px 10px;border:1px solid var(--line);border-radius:7px;font-size:13px;font-family:inherit;color:var(--ink);outline:none}
.si6 #si6-tp-modal .tp-form input:focus{border-color:var(--slate)}
.si6 #si6-tp-modal .pclose{display:flex;gap:8px;justify-content:flex-end}
.si6 #si6-tp-modal .pclose .primary{background:var(--slate);color:#fff;border:none}

/* ── S193 PART LOAD pop-up (js_sales.js si6_plBuild) ───────────────────────
   One truck's weighbridge and freight shared across its invoices. Built from
   the app's own parts, per Ali: "use our standard feild color" and "yellow
   cursor focus" - so the editable boxes are the Charges table's (#fff on
   --line, .si6 .chrow .cin) and the row cursor is the app's amber
   (#f2d585/#8a6d0b) as a LITERAL, never var(--accent-tint): that token is
   themeable and is also the .cin:focus fill, so a cursor built on it would
   drift with the accent picker and read as a focused field (S191's mistake).
   A hand-changed figure carries NO tint of its own for the same reason -
   the hint beside Final Freight says it in words. */
.si6 .pl-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:7px 14px}
.si6 .pl-foot .pl-open{flex:none}
.si6 .picker.pl{width:1200px;max-width:96vw;max-height:94vh;display:flex;flex-direction:column;background:var(--surface);border-radius:10px}
.si6 .pl-top{display:flex;align-items:center;gap:12px;padding:10px 16px;border-bottom:1px solid var(--line)}
.si6 .picker.pl .pl-top h4{margin:0;padding:0;border:none;background:none;font-size:15px;font-weight:600;color:var(--ink)}
.si6 .pl-sub{color:var(--ink-3);font-size:12px}
.si6 .pl-x{margin-left:auto;border:none;background:none;font-size:20px;line-height:1;color:var(--ink-3);cursor:pointer;padding:2px 8px;border-radius:4px}
.si6 .pl-x:hover{background:var(--line-2)}
/* DX (S194) - Ali: "popup is very edge to edge, no spacing on all side borders".
   The body now sits inside the same side padding the weighbridge day page uses
   (--sp-5), and each group is its own bordered card instead of edge-to-edge bands. */
.si6 .pl-body{display:flex;flex-direction:column;gap:var(--sp-3);padding:var(--sp-3) var(--sp-5);flex:1 1 auto;min-height:0;overflow:auto;background:var(--bg)}
.si6 .pl-card{border:1px solid var(--line);border-radius:8px;background:var(--surface);overflow:hidden;flex:none}
.si6 .pl-cardhd{background:var(--head);padding:6px 14px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-2);border-bottom:1px solid var(--line)}
.si6 .pl-cardhd.c{text-align:center}
/* The table's own title is the one heading ABOVE two sand header rows - white and bold so
   three sand bands do not run together (Ali, 2026-09-14 review). */
.si6 .pl-tcard .pl-cardhd{background:var(--surface);color:var(--ink);font-size:12px;font-weight:800}
.si6 .pl-strip{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))}
.si6 .pl-strip .pl-f{border-bottom:none;border-right:1px solid var(--line-2)}
.si6 .pl-strip .pl-f:last-child{border-right:none}
.si6 .pl-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-3);flex:none}
.si6 .pl-grid2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}
/* One label + its figure. A pair shares a line; .span takes the whole line. */
/* A FIXED height, not min-height: a row that can grow is a row that jolts (S194 measured the
   Chargeable note wrapping and moving both boxes 3px on every keystroke through WB < MG). */
.si6 .pl-f{display:grid;grid-template-columns:118px 1fr;align-items:center;height:36px;padding:0 14px;border-bottom:1px solid var(--line-2);white-space:nowrap;overflow:hidden}
/* A worked-out figure: the input box's width, size and right alignment (96px, 13px, 1px border +
   5px padding), so it lines up with the box above and its kg falls in the same place. */
.si6 .pl-v{display:inline-block;width:96px;box-sizing:border-box;padding:0 6px;text-align:right;font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--ink)}
.si6 .pl-mid{display:inline-block;width:34px;text-align:center}
/* Chargeable came from the Min Guarantee: a small tag on the same line (the words did not fit
   in half a box and were clipped). Empty = no tag. */
.si6 .pl-mg:empty{display:none}
.si6 .pl-mg{font-size:10px;font-weight:700;letter-spacing:.04em;color:var(--warn);background:var(--warn-tint);border-radius:4px;padding:1px 5px}
.si6 .pl-grid2 .pl-f:nth-child(odd):not(.span){border-right:1px solid var(--line-2)}
.si6 .pl-f.span{grid-column:1/-1}
.si6 .pl-grid2 .pl-f:last-child{border-bottom:none}
.si6 .pl-f:focus-within{background:#f2d585;color:#241f00}
.si6 .pl-lbl{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-2)}
.si6 .pl-val{font-size:13.5px;font-variant-numeric:tabular-nums}
.si6 .pl-val.big{font-size:15px;font-weight:600}
.si6 .pl-veh{font-size:17px;font-weight:700;letter-spacing:.04em}
.si6 .pl-inl{display:flex;align-items:center;gap:6px;white-space:nowrap}
.si6 .pl-w{color:var(--ink-3);font-size:12px}
.si6 .pl-v.pl-gap{color:var(--neg-deep)}
.si6 .pl-v.pl-gap.zero{color:var(--ink)}
.si6 .pl-v.wb-g{color:var(--pos)}
.si6 .pl-v.wb-o{color:var(--warn)}
.si6 .pl-v.wb-r{color:var(--neg)}
.si6 .pl-hint{font-size:11.5px;color:var(--ink-3)}
.si6 #si6-pl-modal .cin{background:#fff;border-color:var(--line);height:28px}
.si6 #si6-pl-modal .cin:focus{background:var(--accent-tint);border-color:var(--accent)}
.si6 #si6-pl-modal .cin:disabled{background:transparent;border-color:transparent;color:var(--ink-3)}
.si6 #si6-pl-modal .pl-num{width:96px}
.si6 #si6-pl-modal .pl-sm{width:72px}
.si6 #si6-pl-modal .pl-xs{width:54px}
.si6 #si6-pl-modal .pl-txt{width:180px}
.si6 #si6-pl-modal .pl-fin{font-weight:700}
.si6 .pl-sel{height:28px;width:180px;border:1px solid var(--line);border-radius:5px;background:#fff;font:inherit;font-size:13px;color:var(--ink);padding:0 6px}
.si6 .pl-sel:focus{outline:none;border-color:var(--accent)}
/* EC-TRANSPORTPICKER: Part Load's own transport field, same conversion. */
.si6 #si6-pl-trn input{height:28px;width:180px;border:1px solid var(--line);border-radius:5px;font:inherit;font-size:13px;padding:0 6px;box-sizing:border-box}
.si6 #si6-pl-trn input:focus{outline:none;border-color:var(--accent);background:var(--accent-tint)}
.si6 #si6-pl-trn input::placeholder{color:#9aa6b4;font-style:italic}
.si6 .pl-tw{overflow:auto;max-height:none}
.si6 .pl-tcard{flex:1 1 auto;min-height:170px;display:flex;flex-direction:column}
.si6 .pl-tcard .pl-tw{flex:1 1 auto;min-height:0}
.si6 .pl-tbl{border-collapse:collapse;width:100%;min-width:1120px;font-variant-numeric:tabular-nums}
/* DX - every label centred BOTH ways (Ali: "single words are lower aligned and 2 words are right aligned"). Two header rows: the group, then its columns; the whole thead sticks. */
.si6 .pl-tbl thead{position:sticky;top:0;z-index:1}
.si6 .pl-tbl thead th{background:var(--head);font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-2);padding:6px 8px;border-bottom:1px solid var(--line);border-right:1px solid var(--line-2);text-align:center;white-space:nowrap;vertical-align:middle;line-height:1.25}
.si6 .pl-tbl thead th:last-child{border-right:none}
.si6 .pl-tbl th.l,.si6 .pl-tbl td.l{text-align:left}
.si6 .pl-tbl th.c,.si6 .pl-tbl td.c{text-align:center}
/* Every worked-out figure centred under its centred header (Ali, 2026-09-14); the input boxes keep
   their own right alignment, and the Final Freight column stays right. */
.si6 .pl-tbl tbody td{padding:4px 8px;height:38px;border-bottom:1px solid var(--line-2);text-align:center;white-space:nowrap;color:var(--ink)}
.si6 .pl-tbl tbody td:last-child,.si6 .pl-tbl tfoot td:last-child{text-align:right}
.si6 .pl-tbl tbody tr:nth-child(even) td{background:var(--zebra)}
.si6 .pl-tbl tbody tr.off td{color:var(--ink-3)}
.si6 .pl-tbl tbody tr.pl-row:focus-within td{background:#f2d585 !important;color:#241f00}
.si6 .pl-tbl tbody tr.pl-row:focus-within td:first-child{box-shadow:inset 3px 0 0 #8a6d0b}
.si6 .pl-tbl td.pl-inv{font-weight:700}
.si6 .pl-tag{display:block;font-size:10.5px;font-weight:500;color:var(--ink-3)}
.si6 .pl-chg{display:block;font-size:10.5px;font-weight:600;color:var(--neg-deep)}
.si6 .pl-tbl tfoot{position:sticky;bottom:0}
.si6 .pl-tbl tfoot td{padding:7px 8px;border-top:2px solid var(--line);background:var(--head);font-weight:700;text-align:center;white-space:nowrap}
/* Difference sits directly under the Final Freight total (Ali: "so user can relate it"). */
.si6 .pl-tbl tfoot tr.pl-diffrow td{border-top:1px solid var(--line-2);background:var(--surface)}
.si6 .pl-tbl tfoot td.pl-difflbl{color:var(--ink-2);font-size:11px;letter-spacing:.05em;text-transform:uppercase}
.si6 .pl-tbl input[type=checkbox]{width:15px;height:15px;cursor:pointer;accent-color:var(--pos-deep)}
/* Cancel and Reset on the LEFT, Save alone on the RIGHT - the app standard
   (weighbridge day page, S192; Job Detail's footer). Ali: "move cancel to left
   side, and save as right side. it our app standard format." */
.si6 .pl-sum{display:flex;align-items:center;gap:var(--sp-3);padding:10px var(--sp-5);border-top:1px solid var(--line);background:var(--head);flex:none}
.si6 .pl-note{font-size:12.5px;color:var(--ink-2)}
.si6 .pl-note.warn{color:var(--neg-deep);font-weight:600}
.si6 .pl-diff.ok{color:var(--pos-deep)}
.si6 .pl-diff.no{color:var(--neg-deep)}
.si6 .pl-btn{border:1px solid var(--line);background:#fff;border-radius:7px;padding:7px 16px;font:inherit;font-weight:600;cursor:pointer;color:var(--ink)}
.si6 .pl-btn.primary{background:var(--pos-deep);border-color:var(--pos-deep);color:#fff;margin-left:auto}
.si6 .pl-btn:focus-visible,.si6 .pl-x:focus-visible{outline:2px solid #8a6d0b;outline-offset:2px}

/* ── MERGE view — compact so input + output fit on one page ───────────────── */
#mergeDetailView .job-infobar{padding:8px 16px;margin-bottom:8px}
#mergeDetailView .acc-section{margin-bottom:8px}
#mergeDetailView .acc-bar{padding:7px 14px}
#mergeDetailView .acc-body{padding-top:0}
#mergeDetailView .main-table th{padding:6px 8px;font-size:0.8em}
#mergeDetailView .main-table td{padding:4px 8px;font-size:0.85em}
#mergeDetailView .main-table .date-sum-row td{padding:5px 8px}
#mergeDetailView .of-input{padding:3px 6px;font-size:0.85em;height:auto}
#mergeDetailView .job-save-bar{padding:8px 16px;margin-top:8px}
#mergeDetailView .acc-stat{font-size:0.92em}
#mgInputTable tbody tr:nth-child(even) td,
#mgOutputTable tbody tr:nth-child(even) td { background:var(--zebra); }
#mgInputTable tbody tr:hover td,
#mgOutputTable tbody tr:hover td { background:var(--brand-tint); }

/* ── Profit (Sales-P) — inline columns (Cost Rate / Cost Wt / Profit), toggled ── */
#si6-profit-btn.active{background:#1f9d57;color:#fff;border-color:#1f9d57}
.si6 .oc-pr-rate,.si6 .oc-pr-wt,.si6 .oc-pr-profit{width:0}
.si6 .oh-pr,.si6 .oc-prc{padding-left:0;padding-right:0;overflow:hidden;white-space:nowrap;text-align:right;font-variant-numeric:tabular-nums}
.si6 .page.show-pr .oc-pr-rate{width:84px}
.si6 .page.show-pr .oc-pr-wt{width:84px}
.si6 .page.show-pr .oc-pr-profit{width:90px}
.si6 .page.show-pr .oh-pr,.si6 .page.show-pr .oc-prc{padding-left:9px;padding-right:9px}
.si6 .pr-profit{font-weight:700}
/* AY / CR / CW and Avg Wt read CENTRED (Ali, S170). Both need (0,3,1) to beat
   .si6 .grid td{text-align:right} (0,2,1) - the same specificity trap that left
   td.del right-aligned above. Scoped to .grid so the header bar and Total A slots,
   which are flex rows outside the table, are untouched. The tfoot cells are inside
   the table and pick this up automatically, so each column stays self-consistent
   head -> body -> total. */
.si6 .grid td.oc-prc{text-align:center}
.si6 .grid td.vavg{text-align:center}
.si6 .w-pr-rate,.si6 .w-pr-wt,.si6 .w-pr-profit{width:0;flex-shrink:0;overflow:hidden;box-sizing:border-box;white-space:nowrap;display:flex;align-items:center;justify-content:flex-end;font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--ink)}
.si6 .page.show-pr .w-pr-rate{width:84px;padding:0 9px}
.si6 .page.show-pr .w-pr-wt{width:84px;padding:0 9px}
.si6 .page.show-pr .w-pr-profit{width:90px;padding:0 9px}
.si6 .pr-profit.ppos,.si6 .w-pr-profit.ppos{color:var(--pos)}
.si6 .pr-profit.pneg,.si6 .w-pr-profit.pneg{color:var(--neg)}

/* ── Purchase-change review popup (opens with an invoice that has pending lot edits) ── */
/* Flex column so h4 + footer stay pinned and only .scm-body scrolls - same
   shape as the split-lot allocator (S100). .picker's own overflow:hidden
   clips the rounded corners; max-height is vh-led since the app's LOCKED
   real viewport (695-698px) is what actually governs the size, not a
   fixed px ceiling - see design-base-viewport. */
/* Flex, so the warning icon centres against the text instead of sitting on
   its baseline - the h4 is an inline svg + a text node (js_alerts.js:1771),
   which is exactly the arrangement that leaves an icon looking dropped. */
.si6 .scm-pop>h4{background:var(--head);color:var(--ink);
  display:flex;align-items:center;gap:6px}
.si6 .scm-pop{max-width:1100px;width:94vw;max-height:min(97vh,860px);display:flex;flex-direction:column;overflow:hidden}
.si6 .scm-pop>h4{flex:0 0 auto}
.si6 .scm-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:10px 18px 14px}
.si6 .scm-pop>.pclose{flex:0 0 auto}
.si6 .scm-row{border:1px solid var(--line);border-radius:9px;padding:10px 12px;margin-bottom:12px;background:#fff;transition:opacity .15s,background .15s}
.si6 .scm-row.info{background:#fffaf2;border-color:#f0c060}
.si6 .scm-row.done{opacity:.6;background:var(--pos-tint);border-color:#bfe3cb}
.si6 .scm-head{display:flex;align-items:center;gap:10px;margin-bottom:8px}
.si6 .scm-id{font-size:14px;color:var(--ink);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.si6 .scm-id .scm-sep{color:var(--ink-3)}
.si6 .scm-id .scm-date{font-weight:600;font-variant-numeric:tabular-nums}
.si6 .scm-id .scm-pcode,.si6 .scm-id .scm-item{font-weight:700}
/* I29 follow-up (this session): was var(--accent), which inside .si6 resolves
   to this screen's own private teal (#0f766e) - not the app's real save/positive
   green. --pos-deep is what Save and every other confirming button actually use. */
.si6 .scm-applyall{margin-left:auto;flex-shrink:0;border:1px solid var(--pos-deep);background:var(--pos-deep);color:#fff;border-radius:7px;padding:7px 16px;font-weight:700;font-size:13px;cursor:pointer}
.si6 .scm-applyall:hover{background:var(--pos)}
.si6 .scm-applyall.scm-ack{border-color:#c98a2a;background:#fff;color:#a8741f}
.si6 .scm-applyall.scm-ack.on{background:#a8741f;color:#fff}
.si6 .scm-donebadge{display:none;font-size:12px;font-weight:700;color:var(--pos);margin-top:5px}
.si6 .scm-row.done .scm-donebadge{display:block}
/* Bled flush to .scm-row's own edges (-12px = the row's own left/right padding)
   so the header actually touches the card border its corner-radius already
   assumed it would. table-layout left auto (not fixed) on purpose - the wider
   popup (900->1100) gives auto-layout enough room without hardcoding column
   widths that could clip a money figure. */
.si6 .scm-tbl{width:calc(100% + 24px);margin:0 -12px;border-collapse:collapse;font-size:13.5px}
/* I29 victim 1: this had no background of its own, so it fell through to
   the app-wide pre-makeover red-gradient th - forest matches every other
   table header in the app (same fix shape as the split-lot allocator).
   Hardcoded hex, NOT var(--brand): matches the sand/forest-ink pairing every
   other .si6 table header uses (same escape .si6 .grid thead th uses for the
   same reason) - not, as of CM112, working around a shadowed --brand any
   more, since that pin is gone. */
.si6 .scm-tbl th{background:#efeadf;text-align:right;color:#222a25;font-weight:700;padding:6px 10px;font-size:11.5px;text-transform:uppercase;letter-spacing:.3px;vertical-align:top}
.si6 .scm-tbl th:first-child{text-align:left;border-radius:6px 0 0 0}
.si6 .scm-tbl th:last-child{border-radius:0 6px 0 0}
.si6 .scm-tbl th .scm-sub{display:block;margin-top:2px;font-size:9.5px;font-weight:500;text-transform:none;letter-spacing:0;opacity:.78;white-space:nowrap}
.si6 .scm-tbl td{height:42px;padding:4px 10px;border-top:1px solid var(--line-2);text-align:right;vertical-align:middle;font-variant-numeric:tabular-nums}
.si6 .scm-tbl td.scm-k{text-align:left;font-weight:700;font-size:14px}
.si6 .scm-tbl tr.na td{color:var(--ink-3)}
.si6 .scm-orig{color:var(--ink-2)}
.si6 .scm-edit{color:#ea580c;font-weight:800}
.si6 .scm-ls{color:#0891b2;font-weight:700}
.si6 .scm-cur{color:var(--ink-2)}
.si6 .scm-dash{color:var(--ink-3)}
.si6 .scm-in{width:104px;padding:6px 9px;border:1px solid var(--line);border-radius:6px;font:inherit;font-size:14px;text-align:right;font-variant-numeric:tabular-nums}
/* New Invoice Value = the figure that gets billed → make it pop (only the live, editable ones) */
.si6 .scm-newv input:not(:disabled){border:1.5px solid var(--accent);background:var(--accent-tint);color:var(--ink);font-weight:700}
.si6 .scm-in:disabled{background:#f3f5f7;color:var(--ink-3);border-color:var(--line-2)}
.si6 .scm-actc{white-space:nowrap;text-align:center}
/* split lot: weight/bags row frozen (not re-billable on this invoice) */
.si6 .scm-fr.frozen td{color:var(--ink-3)}
.si6 .scm-fr.frozen .scm-edit{color:var(--ink-3);font-weight:600}
.si6 .scm-split-note{display:inline-block;padding:3px 10px;border:1px dashed var(--line);border-radius:6px;color:var(--ink-3);font-size:11.5px;font-weight:600;text-transform:uppercase;letter-spacing:.3px}
/* split-lot balance picker (Phase 2, fired from the Purchase save) */
.wb-back{position:fixed;inset:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;z-index:9999}
/* .wb-pop is a flex column so the banner (h3) and footer stay pinned while
   only .wb-body scrolls - measured (S100) to fit 2 full lot-cards before a
   3rd engages the scroll. Width/height bumped from 560px/auto to fit the
   card layout without cramping. max-height is vh-led, not the 760px ceiling
   - the app's LOCKED real viewport is 695-698px tall (design-base-viewport),
   so 760px never binds in practice; 95vh is what actually decides the size,
   and header/footer padding is trimmed to leave more of that to the cards. */
.wb-pop{background:var(--surface);border-radius:12px;width:min(800px,94vw);max-width:800px;
  max-height:min(97vh,760px);overflow:hidden;box-shadow:0 18px 50px rgba(0,0,0,.3);
  padding:0;display:flex;flex-direction:column}
.wb-pop>h3{flex:0 0 auto;margin:0;padding:9px 20px;background:var(--warn-tint);color:var(--neg);
  font-weight:700;font-size:1em;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--line)}
.wb-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:10px 18px}
.wb-lot{margin-bottom:16px}
.wb-h{font-size:.92em;color:#444;margin-bottom:10px;line-height:1.45}
.wb-opt{display:flex;align-items:center;gap:10px;padding:9px 12px;border:1.5px solid #e0e0e0;border-radius:8px;margin-bottom:7px;cursor:pointer;transition:all .12s}
.wb-opt:hover{border-color:#f39c12;background:#fffaf0}
.wb-opt input{margin:0}
.wb-lbl{font-weight:600;color:#2c3e50}
.wb-q{margin-left:auto;color:#7f8c8d;font-size:.88em;font-variant-numeric:tabular-nums}
.wb-foot{flex:0 0 auto;display:flex;align-items:center;gap:10px;padding:9px 20px;
  background:var(--head);border-top:1px solid var(--line);margin-top:0}
.wa-progress{font-size:.85em;color:var(--ink-3);margin-right:auto}
.wb-foot button{padding:9px 20px;border-radius:8px;font-weight:600;cursor:pointer;border:1.5px solid}
.wb-cancel{background:var(--surface);border-color:var(--line);color:var(--ink-2)}
.wb-ok{background:var(--pos-deep);border-color:var(--pos-deep);color:#fff}
.wb-ok:disabled{background:var(--line);border-color:var(--line);color:var(--ink-3);cursor:not-allowed}
/* Split-lot weight allocator */
/* Each lot is its own bordered card (S100) - with 2+ lots in one popup it
   wasn't visually clear which allocation table belonged to which lot. */
/* Padding/gaps kept tight on purpose: the 2-line card header (Purchased /
   Edited) costs ~20px per card, and at the LOCKED 695px viewport the whole
   popup only gets 97vh = 674px. Measured at 1528x695 AND 1536x695 — 2 cards
   must fit with no scroll, the 3rd engages it. Re-measure after any change
   here; do NOT reason about it from the numbers, that is how S100 shipped a
   popup that fit one card. */
.wa-lot{margin-bottom:0;border:1.5px solid var(--line);border-radius:10px;padding:9px 14px 10px;
  background:var(--surface);box-shadow:0 1px 2px rgba(16,24,40,.05);transition:border-color .15s,background .15s}
.wa-lot+.wa-lot{margin-top:10px}
.wa-lot.wa-applied{border-color:var(--pos-deep);background:var(--pos-tint)}
/* Two identity lines now, so the Apply button aligns to the TOP of the block
   rather than centring against a two-line stack. */
.wa-lot-hd{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:8px}
/* ONE grid shared by both lines: .wa-idl is display:contents, so every field
   from both rows becomes a direct grid item and each column sizes to the wider
   of the two. That is what makes "Bags 70" sit exactly above "Bags 80" — a
   per-row flex box cannot align across rows, which is why the first cut didn't.
   8 columns = tag + party + variety + lot + bags + rate + weight + delta. */
.wa-idwrap{flex:1 1 auto;min-width:0;display:grid;
  grid-template-columns:repeat(7,max-content) minmax(0,1fr);
  gap:3px 13px;align-items:baseline;font-size:.86em;line-height:1.35}
.wa-idl{display:contents}
.wa-f{color:var(--ink-2);white-space:nowrap}
.wa-f b{color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums}
/* Every field that actually changed goes red — label and value together, so a
   changed field reads as one red unit rather than a grey label with a red number. */
.wa-idl-new .wa-f.wa-chg,
.wa-idl-new .wa-f.wa-chg b{color:var(--neg-deep)}
.wa-idtag{font-weight:800;text-transform:uppercase;letter-spacing:.4px;font-size:.82em;
  padding:1px 7px;border-radius:9px;white-space:nowrap;text-align:center}
.wa-idl-old .wa-idtag{background:var(--head);color:var(--ink-2)}
/* Edited row is red-tinted to match the red it now flags changes in. */
.wa-idl-new .wa-idtag{background:var(--neg-tint);color:var(--neg-deep)}
.wa-delta{color:var(--neg-deep);font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap}
.wa-card-apply{border:1.5px solid var(--pos-deep);background:var(--surface);color:var(--pos-deep);
  border-radius:7px;padding:5px 14px;font-weight:700;font-size:.85em;cursor:pointer;white-space:nowrap}
.wa-card-apply:hover{background:var(--pos-tint)}
/* Once applied the button's job changes from "confirm" to "edit", and the
   row-button-standard (G26) is explicit: Edit is amber everywhere and never
   green/--accent. Outline treatment matches .btn-edit exactly. */
.wa-lot.wa-applied .wa-card-apply{border-color:var(--warn-deep);color:var(--warn-deep);background:var(--surface)}
.wa-lot.wa-applied .wa-card-apply:hover{background:var(--warn-tint)}
.wa-h{font-size:.92em;color:#444;margin-bottom:0;line-height:1.45;flex:1 1 auto}
.wa-tbl{width:100%;border-collapse:collapse;font-size:.9em}
.wa-tbl th{background:var(--brand);color:var(--brand-ink);text-align:right;font-weight:600;font-size:.85em;padding:6px 8px}
.wa-tbl th:first-child{text-align:left;border-radius:6px 0 0 0}
.wa-tbl th:last-child{border-radius:0 6px 0 0}
.wa-tbl td{padding:4px 8px;border-bottom:1px solid #f2f2f2;text-align:right;font-variant-numeric:tabular-nums}
.wa-tbl td.wa-lbl{text-align:left;font-weight:600;color:#2c3e50}
.wa-r.stock td.wa-lbl{color:#2980b9}
.wa-r.locked{background:#fbfbf3}
.wa-lk{font-size:.85em}
.wa-hint{font-size:.82em;color:var(--ink-2);padding:0 2px 10px;line-height:1.45}
/* The BALANCE row (S149) - the piece that took whatever was left, so it is the
   one the correction lands on while every measured row stays put. Tinted the
   same green the app already uses for "this is the live/active one", and the
   tag reads as a label rather than a button so nobody tries to click it. */
.wa-r.bal td.wa-lbl{color:var(--pos-deep,#1e7a4d)}
.wa-bal{font-size:.72em;font-weight:700;text-transform:uppercase;letter-spacing:.04em;
        background:var(--pos-tint,#e6f5ec);color:var(--pos-deep,#1e7a4d);
        border-radius:4px;padding:1px 5px;margin-left:4px;vertical-align:1px}
.wa-in{width:84px;text-align:right;padding:5px 7px;border:1.5px solid #d8d8d8;border-radius:6px;font-variant-numeric:tabular-nums}
.wa-r.locked .wa-in{border-color:#f0c14b;background:#fffaf0;font-weight:700}
.wa-x,.wa-auto{border:1px solid #ddd;background:#fff;border-radius:6px;padding:2px 7px;cursor:pointer;margin-left:3px;color:#777}
.wa-x:hover{border-color:#c0392b;color:#c0392b}
.wa-auto:hover{border-color:#2980b9;color:#2980b9}
/* Applied lot: inputs/row-buttons are genuinely disabled (not just tinted) -
   this rule must win over .wa-r.locked's amber, so it comes after it. */
.wa-in:disabled{background:var(--pos-tint);border-color:var(--pos-tint);color:var(--ink-2);font-weight:600;cursor:not-allowed}
.wa-x:disabled,.wa-auto:disabled{opacity:.35;cursor:not-allowed}
.wa-x:disabled:hover,.wa-auto:disabled:hover{border-color:#ddd;color:#777}
/* A reservation that lost bags shows the drop inline (10 -> 9). Bags are the one
   exact quantity - a bag is a bag - so losing one is real, not drift, and it must
   never happen silently. Red matches the changed-field colour in the card header. */
.wa-bgold{color:var(--ink-3);text-decoration:line-through}
.wa-bgarrow{color:var(--ink-3)}
.wa-bgnew{color:var(--neg-deep);font-weight:700}
.wa-rem{text-align:right;font-size:.9em;color:#555;margin-top:4px}
.wa-remv{font-weight:700;font-variant-numeric:tabular-nums}
.wa-remv.ok{color:#27ae60}
.wa-remv.bad{color:#c0392b}
.si6 .scm-ap,.si6 .scm-ig{border-radius:6px;padding:6px 15px;font-weight:700;font-size:12.5px;cursor:pointer;border:1px solid;background:#fff;margin:0 3px}
.si6 .scm-ap{border-color:var(--pos);color:var(--pos)}
.si6 .scm-ap.on{background:var(--pos);color:#fff}
.si6 .scm-ig{border-color:var(--neg);color:var(--neg)}
.si6 .scm-ig.on{background:var(--neg);color:#fff}
.si6 .scm-ap:disabled,.si6 .scm-ig:disabled{opacity:.35;cursor:default;border-color:var(--line);color:var(--ink-3);background:#fff}
/* Once Apply or Ignore is chosen, BOTH buttons are replaced by this ONE -
   the row-button-standard (G26): Edit is amber everywhere, never green/red.
   Same shape as the split-lot allocator's card-level Apply/Edit. Named
   .scm-editbtn, NOT .scm-edit - that class already means the "Purchase
   Edited" value cell (orange text), a collision that would have silently
   repainted every changed-value cell as an amber button. */
.si6 .scm-editbtn{border-radius:6px;padding:6px 15px;font-weight:700;font-size:12.5px;cursor:pointer;
  border:1px solid var(--warn-deep);color:var(--warn-deep);background:#fff;margin:0 3px}
.si6 .scm-editbtn:hover{background:var(--warn-tint)}
.si6 tr.scm-applied{background:var(--pos-tint)}
.si6 tr.scm-ignored{background:var(--neg-tint)}
.si6 .scm-note{font-size:13px;color:var(--ink-2);margin-bottom:4px}
.si6 .scm-shared{font-size:12.5px;color:#a04000}
.si6 .scm-pop .pclose{display:flex;gap:8px;justify-content:flex-end;padding:10px 16px;border-top:1px solid var(--line)}
/* Stage 2b: billed-vs-consumed deviation arrow (red ↑/↓), absolute so it adds no column width */
.si6 .dvcell{position:relative}
.si6 .dv-arrow{position:absolute;left:1px;top:50%;transform:translateY(-50%);color:var(--neg);font-weight:800;font-size:13px;line-height:1;pointer-events:none;display:none;z-index:1}
/* highlight lots whose billed values were just updated from a purchase change */
.si6 .lotrow.lot-edited td{background:#fff7e6 !important}
.si6 .lotrow.lot-edited td.l:first-child{box-shadow:inset 3px 0 0 var(--warn)}
/* alerts clubbed by invoice — separate each changed lot inside one card */
.alert-lot-line + .alert-lot-line{border-top:1px dashed #e3c98a;margin-top:7px;padding-top:7px}
.alert-lotcount{display:inline-block;background:#c0392b;color:#fff;border-radius:10px;padding:1px 9px;font-size:0.72em;font-weight:700;margin-left:6px;vertical-align:middle}
/* "Inv Edited — reprint" tag in the bottom totbar (shown when invoice was re-billed) */
.si6 .reprint-tag{align-self:center;margin-right:12px;background:#fff3e0;color:#a04000;border:1px solid #f0c060;border-radius:14px;padding:5px 13px;font-size:11.5px;font-weight:700;cursor:pointer;white-space:nowrap;align-items:center;gap:5px}
.si6 .reprint-tag:hover{background:#ffe4c2}

/* Proforma vs Sales invoice both being .si6 gave them literally identical
   colour, no visual cue which screen is open. Proforma gets the app's own
   already-AA-verified "teal" accent preset (foundation.css data-accent="teal")
   in place of Sales' forest/rust, scoped to #pfScreen only - Sales is
   untouched. Same technique as .si6 .hdr's own rust override, just teal and
   also reaching the card stripe/table header/foot bar/Add Lots button, which
   are hardcoded forest hex rather than accent-token-driven. */
#pfScreen .hdr{--accent:#0f766e;--accent-2:#0c5f58;--accent-tint:#e3f1ef}
#pfScreen .card.k-a{border-left-color:#0f766e}
#pfScreen .grid thead th{background:#0f766e}
#pfScreen .foot.a{background:#e3f1ef}
/* Proforma has far fewer columns than Sales' Part A (no profit columns, no
   Part B/Charges), so it has real horizontal room Sales doesn't - Party Name
   gets it (higher specificity than the shared .si6 rule below, Sales
   untouched). */
/* Proforma follows Sales: Party Name is its one elastic column too. This is not
   just consistency - every OTHER si6 column rule is shared, so once Remarks went
   fixed and Item became elastic-only-when-Party-Name-is-hidden, this single id
   rule left Proforma as the one table with no auto column at all. */
#pfScreen .page.show-p .oc-p{width:auto;min-width:175px}
/* .gwrap's overflow:hidden (.si6 .sec-body>:last-child, for rounding the
   table's bottom corners) has higher specificity than .gwrap's own base
   overflow:visible, and was clipping Proforma's table outright at narrow
   widths instead of letting it scroll - no scrollbar ever appeared, the
   extra columns were just cut off. Give it its own horizontal scrollbar
   directly (ID beats the :last-child selector's specificity). */
#pfScreen .gwrap{overflow-x:auto;overflow-y:hidden}
#pfScreen .foot.a .foot-left{color:#0c5f58}
#pfScreen .btn-add{color:#0c5f58}
#pfScreen .btn-add:hover{background:#0f766e}

/* ══ ACCOUNTS TAB (Phase 11) ══════════════════════════════════════════════ */
.acc-strip{display:flex;align-items:center;justify-content:space-between;margin:0 2px 10px;flex-wrap:wrap;gap:8px}
.acc-strip h3{margin:0;color:#2c3e50;font-size:1.05em}
.acc-strip .acc-meta{color:#8a98a5;font-size:0.82em}
/* 6px, not 12. At 12px on a ~24px chip both ends are fully round, and Ali named
   it as off the app's language: "balanced used very round button and we dont use
   such rounder button we have little less round". He is right by measurement —
   6px is the most-used radius in this stylesheet (57 rules) and the key chip
   next to it sits at 4. Changed on the component rather than per screen: the
   same badge prints on the Trial Balance and the Balance Sheet, and one of them
   staying round would be the actual inconsistency. */
.acc-badge{display:inline-block;padding:3px 12px;border-radius:6px;font-size:0.78em;font-weight:700}
.acc-badge.ok{background:#eafaf1;color:#1e8449;border:1px solid #b7e4c7}
.acc-badge.bad{background:#fde8e8;color:#c0392b;border:1px solid #f5b7b1}
/* Tally-style T table (Dr | Cr) */
.acc-t{width:100%;border-collapse:collapse;font-size:0.9em;table-layout:fixed}
.acc-t th{background:#2c3e50;color:#fff;padding:8px 12px;text-align:left;font-size:0.88em;letter-spacing:0.3px}
.acc-t th.amt,.acc-t td.amt{text-align:right;width:17%;font-variant-numeric:tabular-nums;white-space:nowrap}
.acc-t td{padding:7px 12px;border-bottom:1px solid #f0f2f5;vertical-align:top}
.acc-t td.dv{border-left:2px solid #e3e7ec}
.acc-t tr.acc-total td{background:#f5f6fa;border-top:2px solid #cfd6dd;border-bottom:2px double #cfd6dd;font-weight:700}
.acc-t td.muted{color:#9aa7b2}
.acc-t td.gp-pos{color:#1e8449;font-weight:700}
.acc-t td.gp-neg{color:#c0392b;font-weight:700}
/* Trial balance */
.acc-tb{width:100%;border-collapse:collapse;font-size:0.9em}
.acc-tb th{background:#2c3e50;color:#fff;padding:8px 12px;text-align:left;font-size:0.88em}
.acc-tb th.amt,.acc-tb td.amt{text-align:right;width:150px;font-variant-numeric:tabular-nums}
.acc-tb td{padding:6px 12px;border-bottom:1px solid #f0f2f5}
.acc-tb tr.acc-grp td{background:#eef1f5;color:#5d6d7e;font-weight:700;font-size:0.82em;letter-spacing:0.6px;text-transform:uppercase;padding:5px 12px}
.acc-tb tr.acc-child td:first-child{padding-left:28px;color:#5d6d7e}
.acc-tb tr.acc-total td{background:#2c3e50;color:#fff;font-weight:700;padding:9px 12px}
.acc-tb tbody tr:hover{background:#f8fafc}
/* Ledger */
.acc-led-head{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;background:linear-gradient(135deg,#2c3e50,#34495e);color:#fff;border-radius:10px;padding:14px 18px;margin-bottom:12px}
.acc-led-head .who{font-size:1.1em;font-weight:700}
.acc-led-head .period{color:#aeb9c4;font-size:0.8em;margin-top:2px}
.acc-led-head .bal{text-align:right}
.acc-led-head .bal .lbl{color:#aeb9c4;font-size:0.75em}
.acc-led-head .bal .val{font-size:1.35em;font-weight:800;font-variant-numeric:tabular-nums}
.acc-led-head .bal .val.dr{color:#7bed9f}
.acc-led-head .bal .val.cr{color:#ffb8a8}
.acc-led{width:100%;border-collapse:collapse;font-size:0.88em}
.acc-led th{background:#2c3e50;color:#fff;padding:8px 10px;text-align:left;font-size:0.85em;position:sticky;top:0}
.acc-led th.amt,.acc-led td.amt{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.acc-led td{padding:6px 10px;border-bottom:1px solid #f0f2f5}
.acc-led tbody tr:hover{background:#f8fafc}
.acc-led tr.acc-total td{background:#f5f6fa;border-top:2px solid #cfd6dd;font-weight:700}
.acc-led .vch{color:#9aa7b2;font-size:0.85em;white-space:nowrap}
/* Drill-down navigation */
.acc-cur{background:#ffe9a8 !important;box-shadow:inset 0 0 0 2px #f0a500}
td.acc-cur + td{background:#ffe9a8 !important}
[data-nav]{cursor:pointer}
.acc-crumb{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:0 2px 10px;font-size:0.9em;color:#5d6d7e}
.acc-crumb .cr-item{background:#eef1f5;border-radius:6px;padding:4px 12px;font-weight:600}
.acc-crumb .cr-item.here{background:#2c3e50;color:#fff}
.acc-crumb .cr-sep{color:#b8c2cc}
.acc-crumb .cr-hint{margin-left:auto;color:#9aa7b2;font-size:0.85em}
/* Tally-style grouped P&L (6 columns: name|inner|outer per side) */
.acc-pl{width:100%;border-collapse:collapse;font-size:0.9em;table-layout:fixed}
.acc-pl th{background:#2c3e50;color:#fff;padding:8px 12px;text-align:left;font-size:0.88em}
.acc-pl col.c-name{width:23%}.acc-pl col.c-in{width:13%}.acc-pl col.c-out{width:14%}
.acc-pl td{padding:5px 12px;border-bottom:1px solid #f4f6f8;vertical-align:top;
  font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.acc-pl td.amt{text-align:right}
.acc-pl td.dv{border-left:2px solid #e3e7ec}
.acc-pl tr.grp td{font-weight:700;color:#2c3e50}
.acc-pl tr.child td{font-style:italic;color:#5d6d7e}
.acc-pl tr.child td.nm{padding-left:30px}
.acc-pl td.gp-pos{color:#1e8449;font-weight:700}
.acc-pl td.gp-neg{color:#c0392b;font-weight:700}
.acc-pl tr.sect td{border-top:2px solid #cfd6dd;border-bottom:2px double #cfd6dd;
  font-weight:700;background:#f5f6fa}
.acc-pl .tw{color:#9aa7b2;font-size:0.82em;font-weight:400}

/* ══ Accounts shell — retoned onto real Forest+Sand tokens, 2026-08-08 (Piece 2 of the
   Accounts makeover — see docs/superpowers/specs/2026-08-08-accounts-nav-shell-design.md).
   Middle = whichever voucher form/report is selected. Right = a narrow keyboard-operable
   accordion of real buttons (.acc-tleaf), NOT a <select> and not tree text — see §4 of the
   spec for why. No more private navy palette; this is a normal docked screen now. ══ */
#accShell{display:flex;gap:16px;align-items:flex-start}
#accCenter{flex:1;min-width:0}
/* S131 — #accSide IS THE FLEX CHILD NOW; the cards inside it are not.
   The right column stopped being "one card at a time" when the tree came back on
   every screen (see the block comment in index.html): a screen's own buttons and
   the tree are BOTH visible, stacked. They stay separate cards with the shell's
   own 16px between them rather than becoming one long card, because each already
   carries its own border, padding and sticky behaviour and merging them would
   mean re-deriving all three.
   THE TRAP THIS INTRODUCES, and the reason the rules below had to move: every
   `align-items:stretch` in this file was written to stretch the DIRECT children
   of #accShell. Once this div is the child, stretch reaches IT and stops — the
   cards inside size to their own content again, which is exactly the "ends
   42.7px above the table" fault recorded at .acc-daybook-open below, in a new
   coat. So the stretch rules now name #accSide, and #accSide passes it on. */
/* THE max-height IS LOAD-BEARING, and `align-self:stretch` is not a substitute
   for it — that was the bug Ali caught on this very build: "instead of floting
   menu getting scroll, it give the scroll to page." Stretch sizes a flex item to
   the LINE, and the line grows to its tallest item, so a column taller than the
   panel simply made the whole row taller and pushed the page past the viewport.
   Measured before the cap, at his 1528x698: the Ledger's column ended at 855
   against a 698 floor. A cap is what turns "the page is too long" into "the tree
   scrolls", which is the behaviour the tree's own overflow-y has always wanted.
   40 = the column's own top (20) + the same 20px frame the whole app uses. */
/* ONE CARD, NOT TWO. Built as two stacked cards first and Ali stopped it on
   sight — "why we seperated action and gave its own floting menu? ... i though we
   will keep it in same menu." He is right and the reason is that two cards with
   a gap between them read as two THINGS, when a screen's buttons and the tree
   under them are one column of controls with a rule across it. The card lives
   HERE now; the blocks inside are plain, and the only separator is the divider
   on the tree. */
/* HEIGHT, NOT max-height — the menu is the same size whatever the page is doing.
   Ali: "when there is noting on page floting menu reduces its height, and it look
   bad, can u fix that, keep it heigh as standard." With a cap only, the column
   grew to the taller of itself and the panel, so an empty Day Book (332px of
   panel) pulled the menu down to 534 and it ended halfway up the screen. A menu
   that changes size with the data it is not showing reads as broken.
   This is the same call the LEDGER's own wrap made and for the same reason
   (see the "HEIGHT, not max-height" note there): a control surface is furniture,
   and furniture does not resize because a table is empty. The cap survives inside
   it as the viewport bound, since the value is the viewport. */
#accSide{width:172px;flex-shrink:0;display:flex;flex-direction:column;
  align-self:stretch;height:calc(100vh - 40px - var(--mstrip-h));
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);padding:10px 7px;overflow:hidden}
/* The tree is the one that grows into whatever the buttons above it leave, so a
   short column is not half-empty and a tall one scrolls the tree rather than the
   whole page. min-height:0 is what lets it actually shrink inside a flex column —
   without it the default min-height:auto refuses and the card overflows. */
#accSide #accTree{flex:1 1 auto;min-height:0;width:auto;position:static;
  max-height:none;background:none;border:0;border-radius:0;padding:0}
/* The rule between the screen's own buttons and the tree — the divider in his
   sketch. Only drawn when something sits ABOVE it: on Import/Export the tree is
   the whole menu and a line across the top of it would separate it from nothing.
   :not(:first-child) says exactly that, and says it structurally rather than by
   listing which screens have buttons. */
#accSide #accTree:not(:first-child){border-top:1px solid var(--line);
  margin-top:8px;padding-top:8px}
/* width:auto is not tidying — every one of these blocks was written as a 172px
   CARD, the same 172 as the column. Inside a card that now owns the padding, the
   content box is 158, so a 172px child overhangs its own border by 14 and the
   buttons run out past the rounded edge. They have to be told to fill the parent
   instead of restating the column's width. */
/* DESCENDANT, NOT DIRECT CHILD. This was `#accSide > nav` until the sections and
   the tree moved inside #accSideScroll, at which point it stopped matching any of
   them — and the three navs that carry their own padding (#ledSide, #outstSide,
   and anything else with a private rule) silently got it back, putting their first
   row 12px below everyone else's. That is precisely the row-shifting this pass
   exists to remove, reintroduced by the fix for the scrollbar.
   The lesson worth keeping: a `>` combinator is a claim about the DOM shape, and
   wrapping anything for layout reasons invalidates every such claim above it.
   `position:static` is in the reset for the same class of reason. #ledSide and
   #outstSide were written as sticky cards with `top:12px`, which meant nothing
   while they were the whole column — and inside a scroller it offset exactly those
   two navs by 12px, so View sat one third of a row lower on the Ledger and
   Outstanding than on the other seven screens. A reset that neutralises paint but
   not POSITION leaves the subtlest half of the old rule alive. */
#accSide nav:not(#accTree){flex:0 0 auto;width:auto;background:none;border:0;
  border-radius:0;padding:0;max-height:none;overflow:visible;position:static}
/* A `direct` row (Day Book, Import / Export) sits at branch level but IS the
   destination, so it takes the leaf's box and the header's flush alignment —
   the leaves below a header are indented under it, and this one has no header to
   be indented under. */
#accSide .acc-tdirect{width:100%;margin:0}
/* THE FOCUS RING IS DRAWN INSIDE THE ROW, not around it. Ali: "border is not
   fixed yet, left side part is missing."
   The shared rule is `outline:2px solid var(--accent); outline-offset:1px`, which
   paints one pixel OUTSIDE the element. Every row in this menu starts exactly at
   the scroller's left content edge — measured, row left 1344 against scroller left
   1344 — and a scroller clips at that edge, so the left third of the ring was
   simply cut off. Only the left, which is why it read as a broken border rather
   than a missing one.
   -2px puts the whole ring inside the row's own box, where nothing can clip it,
   and it costs no layout: an outline never occupied space to begin with. Fixing it
   here rather than by padding the scroller keeps Print/Export and the rows below
   them on the same left edge, which is the alignment this menu was just rebuilt
   for. */
#accSide .acc-tsec-h:focus-visible,
#accSide .acc-sec-h:focus-visible,
#accSide .acc-tleaf:focus-visible,
#accSide .acc-openall:focus-visible{ outline-offset:-2px; }
/* Print/Export close their block with the same dashed rule the tree carries, so
   the menu reads as three bands: the two standard actions, the collapsible
   sections and branches, then the note. */
#accSide #accSidePE{flex:0 0 auto;padding-bottom:8px;margin-bottom:2px;
  border-bottom:1px dashed var(--line)}
/* OPEN ALL / CLOSE ALL wears the branch-header type, not a button's — it belongs
   to the rows it commands, and looking like them is what keeps it from being read
   as a screen action next to Print and Export. It is a <button> for the keyboard
   and the semantics; only the paint is borrowed. */
#accSide .acc-openall{display:flex;align-items:center;gap:6px;width:100%;
  border:0;background:none;text-align:left;font-family:inherit}
#accSide .acc-openall .ic{width:13px;height:13px;flex-shrink:0;
  stroke:var(--ink-3);stroke-width:2;fill:none}
#accSide .acc-openall:hover .ic{stroke:var(--ink)}
/* The sections' own headers take the tree's caret behaviour, so View and Period
   open exactly the way Books and Vouchers do — which is the whole point of making
   them parents rather than headings. */
#accSide .acc-sec-h{display:flex;align-items:center;gap:6px}
#accSide .acc-tsec[data-sec] .acc-tsec-body{display:none;padding:1px 1px 6px}
#accSide .acc-tsec[data-sec] .acc-tsec-body.show{display:block}
/* A LOCKED SECTION IS FURNITURE. Ali: "if i am on day book and press ledger on
   right menu, suddendly view and period come and my mouse become misalined. and
   just messes brain for a second ... can we keep view and periord permanent, if
   screen can use them then use else its just locked on that screen."
   So View and Period exist on all nine screens and hold their row whether or not
   the screen can use them. Greyed and caret-less: the missing caret is what says
   "this one does not open", since a grey row with a caret would just look broken.
   Not hidden-but-spaced either - an invisible gap reads as a rendering fault,
   where a labelled grey row teaches "this screen has no views" once and then
   stops being noticed. Bill by bill is the precedent he set for exactly this. */
#accSide .acc-sec-h.locked{color:var(--ink-3);opacity:.5;cursor:default;
  padding-left:19px}
#accSide .acc-sec-h.locked:hover{background:none;color:var(--ink-3)}
/* ONE scroller for everything collapsible, so the bar never starts midway. The
   sections and the tree are inside it together; Print/Export, Open all and the
   note stay pinned outside. min-height:0 is what lets it shrink in the column. */
/* THE SELECTED ROW'S BORDER MUST NOT MEET THE SCROLLBAR. Ali: "when selected the
   border does not fit." A row runs the full width of the scroller, and the
   scrollbar is laid over that width — so the active row's right border and its
   focus outline were being clipped by it, on exactly the rows you had just picked.
   6px of gutter is what the scrollbar occupies here; `scrollbar-gutter:stable`
   reserves it whether or not the bar is showing, so rows do not shift sideways the
   moment a branch opens and the content gets tall enough to scroll. */
#accSide #accSideScroll{flex:1 1 auto;min-height:0;overflow-y:auto;
  padding-right:6px;scrollbar-gutter:stable}
#accSide #accSideScroll #accTree{flex:none;overflow:visible;max-height:none}
/* The single note closes the card: below the tree, separated by the same dashed
   rule the tree's own hint used to carry, and never between the buttons and the
   branches. `margin-top:auto` is what pins it to the FOOT rather than letting it
   float up under a short tree — #accSide is a flex column, so the leftover space
   collects above it. */
#accSide .acc-side-note{flex:0 0 auto;margin-top:auto;padding-top:8px;
  border-top:1px dashed var(--line);font-size:0.63em;line-height:1.5;
  color:var(--ink-3)}
@media (max-width:1100px){ #accSide{ width:150px } }
/* Sized to the button LABELS, not a reserved column — Ali's correction after the first pass
   reserved too much width: "we will need middle space to display reports also." */
#accTree{width:172px;flex-shrink:0;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);padding:10px 7px;position:sticky;top:12px;
  max-height:calc(100vh - 100px);overflow-y:auto}
/* THE PER-SCREEN max-height OVERRIDES ARE GONE (S131), and the history is worth
   keeping because it is a whole class of bug that this change ENDS rather than
   fixes one more time.
   The 100px above is written against a top the "Accounts" h1 produces. Every
   screen that HIDES that h1 lifts the tree to top 20, where 100px caps it 60px
   early - a scrollbar over clear space, which is how Ali reported it twice:
   "right side menu has scroll despite having space at bottom" on the Day Book,
   and "it does not match the height of voucher screen" on the voucher. Three
   separate overrides had accumulated (.acc-daybook-open, .acc-list-open /
   .acc-ledger-open, and .acc-voucher-open added earlier the same day) and a
   fourth screen hiding the h1 would have needed a fourth.
   #accSide now owns the height and the tree takes flex:1 inside it, so the tree
   is sized by its CONTAINER instead of by a constant guessing where it starts.
   There is nothing left to restate per screen. */
/* Ali: "match the bootom of right side menu with the table, right now they are
   not aligned." #accShell is align-items:flex-start, so the tree is sized by its
   own content and ended 42.7px above the table's box at 708. Stretching it makes
   the flex line drive both, so the two bottoms track each other instead of one
   being pinned to the viewport and the other to its content. Day Book only -
   every other Accounts screen is still on the old chrome and gets reviewed on
   its own turn. */
#accounts.acc-daybook-open #accShell{ align-items:stretch; }
/* S131 — AND NOW THE VOUCHER SCREEN'S TURN, which the note above was waiting
   for: "every other Accounts screen is still on the old chrome and gets reviewed
   on its own turn." He raised it on his own screenshot — "it does not match the
   height of voucher screen."
   Raising the cap alone was not enough and the two lines are not the same fix:
   the cap stops it being CLIPPED (598 -> 658), stretch stops it being SHORT. With
   the cap alone the tree sized to its own 643px of content and still ended 15px
   above the form at his 1528x698. Stretched, the flex line drives both, so the
   form and the column share one bottom at 678 whatever the branch state. */
#accounts.acc-voucher-open #accShell{ align-items:stretch; }
.acc-tsec{margin-bottom:2px}
.acc-tsec-h{display:flex;align-items:center;gap:6px;font-weight:800;font-size:0.72em;
  text-transform:uppercase;letter-spacing:0.03em;color:var(--ink-3);cursor:pointer;
  padding:6px 5px;border-radius:var(--radius-sm)}
.acc-tsec-h:hover{background:var(--brand-tint);color:var(--ink)}
.acc-tsec-h:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.acc-tcar{display:inline-block;transition:transform .12s;font-size:0.62em;color:var(--ink-3)}
.acc-tsec-h.open .acc-tcar{transform:rotate(90deg)}
.acc-tsec-body{display:none;padding:1px 1px 6px}
.acc-tsec-body.show{display:block}
.acc-tgrp-lbl{font-size:0.65em;font-weight:700;color:var(--ink-3);text-transform:uppercase;
  letter-spacing:0.02em;padding:6px 5px 3px}
.acc-tleaf{display:flex;align-items:center;justify-content:space-between;gap:6px;
  width:100%;text-align:left;font:inherit;font-size:0.79em;font-weight:600;
  padding:6px 8px;margin-bottom:3px;border-radius:var(--radius-sm);
  border:1px solid var(--line);background:var(--bg);color:var(--ink-2);cursor:pointer}
.acc-tleaf:hover{background:var(--brand-tint);border-color:var(--brand-tint);color:var(--ink)}
/* S131 — SELECTION FOLLOWS THE ACCENT SETTING. "why use green, i asked for
   accent colors" — corrected from a first pass that made this forest, on a
   misreading of "trial balance uses green color in selection, i like it style":
   what he liked was the STYLE (tint fill, matching border, inset bar), not the
   hue, and the hue was the part that should never have been hard-coded.
   THE ACTUAL FAULT WAS THE OTHER WAY ROUND. --accent is a user SETTING (Settings
   > Accent colour: Rust / Terracotta / Ochre / Bronze / Teal) and his is Rust
   #9e4f2e, which is what he is reading as red. The Trial Balance was the one
   screen ignoring that setting — it was pinned to --brand — so the fix is to make
   IT follow the accent too, not to pin everything else to forest.
   Which means the red he does not want is one click away in Settings, and no
   stylesheet edit can answer it as well as that picker can. */
.acc-tleaf.active{background:var(--accent-tint);border-color:var(--accent);
  color:var(--accent-2);font-weight:800;box-shadow:inset 0 -2px 0 var(--accent)}
.acc-tleaf:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.acc-tkey{font-size:0.62em;font-weight:700;color:var(--ink-3);background:var(--surface);
  border:1px solid var(--line);border-radius:4px;padding:0 4px;
  font-family:ui-monospace,monospace;flex-shrink:0}
.acc-tleaf.active .acc-tkey{color:var(--accent-2);border-color:var(--accent-tint)}
.acc-thint{font-size:0.63em;line-height:1.5;color:var(--ink-3);padding:8px 5px 2px;
  border-top:1px dashed var(--line);margin-top:4px}
.acc-parked{padding:16px}
.acc-parked h3{margin:0 0 8px}
.acc-parked p{color:var(--ink-2);max-width:56ch;margin:0}
@media (max-width:1100px){ #accTree{width:150px} }

/* ── Masters — Chart of Accounts / Create Ledger list ──
   Retoned off the standalone --acc-navy palette onto the app's forest+sand
   tokens (2026-07-28, ahead of this panel's move into the Masters tab).
   The Accounts tab itself retoned too, later (Piece 2, 2026-08-08) — there is
   no more --acc-navy anywhere in the app.
   No zebra on .coa-row: real conflict, not an oversight — the tree already
   carries three background/weight tiers (nature row / group row / ledger
   row) and an alternating stripe on top would fight that hierarchy instead
   of reading as a flat list, the case §4.2 of the table standard warns
   about for non-flat row sets. */
.coa-head{display:flex;justify-content:space-between;align-items:flex-end;
  gap:16px;flex-wrap:wrap;margin-bottom:12px}
/* .coa-title is gone — the Chart of Accounts heading sits inside the green bar
   now (2026-08-13), on the app's own .pur-view-title, so it costs no row. */
.coa-asrow{display:flex;align-items:center;gap:8px}
.coa-asrow label{font-weight:600;color:var(--ink-2);font-size:0.88em}
.coa-asrow input{padding:6px 9px;border:1px solid var(--line);border-radius:var(--radius-sm);font-family:inherit;background:var(--surface);color:var(--ink);outline:none}
.coa-asrow input:focus{border-color:var(--accent)}
.coa-new{white-space:nowrap}
.coa-suspense{margin-top:0;padding:8px 12px;font-size:0.92em;min-height:18px;border-top:1px solid var(--line);background:var(--head)}
#mstCoaSuspense .coa-sus-ok{color:var(--pos-deep);font-weight:700}
#mstCoaSuspense .coa-sus-bad{color:var(--neg);font-weight:700}
.coa-c-name a{color:inherit;text-decoration:none}
.coa-c-name a:hover{text-decoration:underline}
.coa-card{padding:0;overflow:hidden}
.coa-tbl{width:100%;border-collapse:collapse;font-size:13px}
.coa-tbl thead th{background:var(--brand);color:var(--brand-ink);font-weight:600;
  text-align:left;padding:9px 12px;position:sticky;top:0;z-index:1}
.coa-th-short{width:150px}
/* The right-alignment here LOST. `.coa-tbl thead th` above is one class plus two
   elements and beats a bare class, so Opening and Balance sat hard left over
   numbers sitting hard right — Ali, 2026-08-14: "opening balance is not aligned
   to header". Same shape as .mst-small-wrap losing to .pur-view-table-wrap.
   Written to out-rank its own table's rule rather than reaching for !important. */
.coa-tbl thead th.coa-th-num{width:150px;text-align:right}
.coa-th-num{width:150px;text-align:right}
.coa-th-act{width:56px}
/* The .sortable cursor/hover pair is scoped to #stockTable and .pur-view-table,
   and the chart is neither — so it needs its own, or a clickable heading looks
   like plain text. */
.coa-tbl thead th.sortable{cursor:pointer;user-select:none}
.coa-tbl thead th.sortable:hover{background:var(--brand-2)}
.coa-tbl td{padding:6px 12px;border-bottom:1px solid var(--line);vertical-align:middle;color:var(--ink);font-weight:400}
.coa-nat td{background:var(--head);color:var(--ink);font-weight:700;font-size:0.85em;
  letter-spacing:.4px;border-top:2px solid var(--line)}
.coa-row.grp .coa-c-name{font-weight:700;color:var(--brand)}
.coa-row.led .coa-c-name{color:var(--ink-2)}
.coa-row:hover{background:var(--brand-tint)}
.coa-c-name{word-break:break-word}
.coa-chevron{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;
  margin-right:4px;flex-shrink:0;vertical-align:middle;transition:transform .12s;color:var(--ink-3)}
.coa-chevron svg{width:11px;height:11px}
.coa-chevron.coll{transform:rotate(-90deg)}
/* Keyboard row focus (Up/Down) - separate from hover so both are visible at
   once (mouse hovering one row while keyboard focus sits on another). Left
   border instead of a background swap so it never fights the Dr/Cr colors. */
.coa-row.focused{background:var(--accent-tint);box-shadow:inset 3px 0 0 var(--accent)}
#mstCoaTree:focus{outline:none}
.coa-c-short{color:var(--ink-3);font-size:0.9em}
.coa-c-num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.coa-c-act{text-align:right}
.coa-user{color:var(--warn-deep);font-weight:700}
/* How many rows a group opens onto (2026-08-13). Quiet on purpose: it is a
   structural cue sitting in a column of account NAMES, so it must read as a
   note on the row rather than compete with the name or with the Dr/Cr figures
   further right. Not tabular-nums - it is a count of rows, not money. */
.coa-count{display:inline-block;margin-left:6px;padding:0 6px;border-radius:9px;
  background:var(--line);color:var(--ink-3);font-size:0.78em;font-weight:600;
  vertical-align:middle;line-height:1.6}
.coa-dr{color:var(--pos-deep);font-weight:700}
.coa-cr{color:var(--neg);font-weight:700}
.coa-zero{color:var(--ink-3)}
.coa-openbtn{background:none;border:1px dashed transparent;border-radius:5px;padding:2px 6px;
  cursor:pointer;font:inherit;color:inherit;font-variant-numeric:tabular-nums}
.coa-openbtn:hover{border-color:var(--line);background:var(--head)}
.coa-setopen{color:var(--ink-3);font-style:italic;font-size:0.9em}
.coa-mini{background:none;border:none;color:var(--warn-deep);cursor:pointer;font:inherit;
  font-size:0.85em;padding:2px 4px;font-weight:600}
.coa-addp{background:none;border:none;color:var(--brand);cursor:pointer;font:inherit;
  font-size:0.85em;padding:2px 4px;font-style:italic}
.coa-mini:hover,.coa-addp:hover{text-decoration:underline}
.acc-mopt{color:var(--ink-3);font-weight:400;font-size:0.9em}

/* ── Accounts shared modal — RETONED 2026-08-09 (S108) ────────────────────────
   This is the "+ Add New Ledger" quick-add reached from the voucher pickers.
   It was the last pre-makeover surface in Accounts: every colour in it was a
   raw hex (#fff card, #2c3e50 heading, #ddd borders, #5d6d7e labels) from the
   2026-07 palette, so opening it from the rebuilt voucher screen dropped you
   into the old app. Ali: "you have linked add new ledger or bank to old system,
   it opens old menu."

   It stays a small dialog rather than becoming a docked page — the popup
   spec's rule is that quick-adds reached from inside another form stay
   .modal, because docking would navigate away from a half-typed voucher.
   The LOOK now matches .mst-modal: sand header band, token fields, sand
   footer, Save in --pos-deep.

   The markup is a flat card (h3, .acc-mmsg, .acc-mfield*, #accMDynamic,
   .acc-mactions) with no body wrapper, so the header and footer bleed to the
   edges and the middle children carry their own side padding. */
.acc-modal{position:fixed;inset:0;background:rgba(20,28,38,0.45);z-index:9000;
  display:flex;align-items:center;justify-content:center}
.acc-modal-card{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);box-shadow:var(--shadow-pop);padding:0;
  width:430px;max-width:92vw;max-height:88vh;overflow-y:auto;color:var(--ink)}
.acc-modal-card h3{margin:0;padding:13px 18px;font-size:1.13em;font-weight:600;
  color:var(--ink);background:var(--head);border-bottom:1px solid var(--line);
  border-radius:var(--radius) var(--radius) 0 0}
/* Side padding for the middle children (there is no body wrapper to give it to). */
.acc-modal-card > .acc-mmsg,
.acc-modal-card > .acc-mfield,
.acc-modal-card > .acc-mside,
.acc-modal-card > .acc-mhint,
.acc-modal-card > #accMDynamic{padding-left:18px;padding-right:18px}
.acc-modal-card > .acc-mfield:first-of-type{padding-top:16px}
.acc-mfield{margin-bottom:11px;display:flex;flex-direction:column;gap:4px}
.acc-mfield label{font-weight:500;color:var(--ink-3);font-size:0.867rem}
.acc-mfield input,.acc-mfield select{font:inherit;font-size:0.933rem;width:100%;
  padding:7px 10px;border:1px solid var(--line);border-radius:var(--radius-sm);
  background:var(--surface);color:var(--ink)}
.acc-mfield input:focus,.acc-mfield select:focus{outline:2px solid var(--accent);
  outline-offset:-1px;border-color:var(--accent)}
.acc-mside{display:flex;gap:16px;align-items:center;font-size:0.92em;color:var(--ink-2)}
.acc-mside label{display:flex;align-items:center;gap:5px;cursor:pointer;font-weight:600}
.acc-mside input[type=radio]{accent-color:var(--accent)}
.acc-mhint{color:var(--ink-3);font-size:0.82em;margin:-6px 0 12px}
.acc-mmsg{color:var(--neg-deep);font-size:0.86em;font-weight:600;min-height:18px;
  padding-top:10px}
.acc-mmsg:empty{min-height:0;padding-top:0}
/* Footer band, matching .mst-modal: Save green (--pos-deep, the one commit
   colour app-wide), Cancel charcoal outline that fills on hover. */
.acc-mactions{display:flex;justify-content:flex-end;gap:9px;margin-top:14px;
  padding:12px 18px;border-top:1px solid var(--line);background:var(--head);
  border-radius:0 0 var(--radius) var(--radius)}
.acc-mactions .btn{font:inherit;font-size:0.867rem;font-weight:600;cursor:pointer;
  border-radius:var(--radius-sm);padding:7px 15px;border:1.5px solid transparent}
.acc-mactions .btn-primary{background:var(--pos-deep);color:#fff;border-color:var(--pos-deep)}
.acc-mactions .btn-primary:hover{filter:brightness(0.93)}
.acc-mactions .btn:not(.btn-primary){background:var(--surface);color:var(--ink);
  border-color:var(--ink)}
.acc-mactions .btn:not(.btn-primary):hover{background:var(--ink);color:#fff}
.mstp-readonly{padding:9px 11px;background:var(--head);border-radius:8px;font-size:0.95em;color:var(--ink-2)}

/* ══ MINIMISED-DOCUMENT STRIP ═══════════════════════════════════════════
   A purchase entry, a proforma and a sales invoice can each be minimised.
   Before this there was no sign of it on screen, and for proforma/invoice no
   way back at all except starting to open another one and choosing "Resume"
   from the guard popup. Purchase alone had a 6px sidebar dot (now removed,
   see .park-dot's note above).

   --mstrip-h is 0px unless the strip is actually showing, so every calc()
   that references it below is a no-op in normal use. It MUST take its space
   rather than overlay: measured at Ali's locked 1528x695, only 16-22px of
   clear space exists under the panel on Purchase (16), Stock (21), Invoice
   list (21) and Masters (22) - all four sit on the 20px padding rollout - so
   a 34px overlay would bury the bottom row of the table. Costs ~1 row (rows
   are ~33px) for as long as something is minimised, and not a pixel
   otherwise. */
:root { --mstrip-h: 0px; }
body.has-mstrip { --mstrip-h: 34px; }

.mstrip { position:fixed; left:0; right:0; bottom:0; z-index:1500;
    display:flex; align-items:center; gap:10px;
    height:var(--mstrip-h); padding:0 14px 0 16px;
    background:var(--surface); border-top:1px solid var(--line);
    box-shadow:0 -6px 18px rgba(16,24,40,.07); }
.mstrip-lbl { font-size:0.74rem; font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; color:var(--ink-3); flex-shrink:0; }
.mstrip-items { display:flex; align-items:center; gap:8px;
    overflow-x:auto; overflow-y:hidden; }

/* Accent-tinted, so it re-tints with the accent preset in Settings rather
   than pinning a hex that would drift from the rest of the chrome. */
.mchip { display:inline-flex; align-items:center; gap:6px; flex-shrink:0;
    height:24px; padding:0 5px 0 10px;
    background:var(--accent-tint); border:1px solid var(--accent);
    border-radius:var(--radius-pill); cursor:pointer;
    font-family:inherit; font-size:0.82rem; font-weight:600;
    color:var(--accent-2); transition:background .12s, color .12s; }
.mchip:hover { background:var(--accent); color:#fff; }
.mchip-dot { width:6px; height:6px; border-radius:50%;
    background:currentColor; flex-shrink:0; }
.mchip-kind { opacity:.72; }
/* The x is a discard, so it gets --neg on hover: the pill and the x do two
   different things and must not look like one target. */
.mchip-x { display:inline-flex; align-items:center; justify-content:center;
    width:17px; height:17px; border-radius:50%; flex-shrink:0;
    font-size:0.88rem; line-height:1; color:var(--ink-3); }
.mchip:hover .mchip-x { color:#fff; }
.mchip-x:hover { background:var(--neg); color:#fff; }

/* ══ IN-APP FEEDBACK: toast / confirm / field marking ═══════════════════
   The three replacements for native alert()/confirm(). Spec:
   docs/superpowers/specs/2026-08-04-native-dialog-replacement-design.md
   Components only - the call-site rollout is its own pass. */

/* ── 1. Side message ───────────────────────────────────────────────────
   OFF THE SAVE BUTTON, on the right side between the middle and the bottom
   (Ali, 2026-08-18: "between middle n bottom right side").
   It used to sit 18px off the bottom, which is where EVERY screen puts its
   primary action. Re-measured with his own six-toast stack up on New Purchase
   at 1528x698: the stack covered the Save button by 11,465px2 — the whole
   button, 100% of it, against the 1,995px2 recorded when the click-through fix
   below was written.
   Click-through was NOT the problem. elementFromPoint at Save's centre returned
   `purch-btn-save`, and sampling the entire button face swallowed nothing, so
   the rule below still works exactly as intended. Save was COVERED, not
   BLOCKED — and you cannot aim at a button you cannot see, which is why he was
   still clearing toasts before saving. Moving the stack is the fix; the
   click-through rule stays because it costs nothing and keeps whatever the
   stack now covers usable.
   Still above the minimised strip, hence --mstrip-h. */
.atoast-box { position:fixed; right:20px; bottom:calc(30% + var(--mstrip-h));
    z-index:1000000; display:flex; flex-direction:column-reverse; gap:8px;
    align-items:flex-end; pointer-events:none; max-width:min(460px, calc(100vw - 40px)); }
/* S139 — JOB DETAIL moves the stack to the LEFT. Same reasoning as the 2026-08-18
   move above, one screen further on: bottom-right here is the Ready For Sale
   table's "Stock Action" column, and Add to Stock is both the slowest action on
   the screen (a POST + full job reload) and the one the keyboard cursor now homes
   to - so its own confirmation toast landed straight on top of the button you were
   watching. Ali: "toast land exactly there. for jobs need to change toast position."
   Left side is safe on this screen: every table's left edge is No./Date/row-label,
   nothing clickable.
   2026-09-05, full toast audit (every screen driven, worst-case 4-stack measured
   against every visible button): the SAME "left edge is plain data" reasoning
   applies to Purchase's own list and the Jobs list (both put their Actions
   column on the right too), so this is now a shared class, not Job-Detail-only.
   The left offset is ALSO fixed here: plain left:20px put the box on top of the
   Accounts/Reports/Masters/Alerts sidebar nav buttons on every screen using it -
   real, found live, not theoretical - so it now starts past the sidebar rail.
   Job Detail's OWN remaining problem - an expanded job's row actions and
   Add/Clear buttons can land in this same vertical band, and Job Complete does
   on a short job - is NOT fixed by this: no static position dodges it, since
   the accordion's content genuinely varies. Recorded, not solved. */
body.job-detail-open .atoast-box, body.toast-safe-left .atoast-box {
    right:auto; left:calc(var(--rail-w) + 20px); align-items:flex-start; }
/* CLICK-THROUGH BODY, clickable x. Only the x takes pointer events; everything
   else passes straight through to whatever is underneath. Kept, and re-verified
   2026-08-18 — see the measurement above. */
.atoast { pointer-events:none; display:flex; align-items:flex-start; gap:10px;
    padding:10px 8px 10px 14px; border-radius:var(--radius-sm);
    background:var(--surface); border:1px solid var(--line);
    border-left:4px solid var(--ink-3);
    box-shadow:var(--shadow-pop); font-size:0.9rem; color:var(--ink);
    animation:atoast-in .16s ease-out; }
@keyframes atoast-in { from{opacity:0; transform:translateY(6px)} to{opacity:1; transform:none} }
/* pre-line, not nowrap: a great many of the messages being converted were
   written for a native dialog and still carry \n between their lines. */
.atoast-msg { white-space:pre-line; line-height:1.35; }
.atoast-x { pointer-events:auto; flex-shrink:0; width:20px; height:20px; padding:0; border:none;
    background:none; cursor:pointer; border-radius:50%; line-height:1;
    font-size:1rem; font-family:inherit; color:var(--ink-3); }
.atoast-x:hover { background:var(--line-2); color:var(--ink); }
/* "x3" when the same check fires repeatedly. The repeats no longer stack, so
   this is the only thing left saying it happened more than once — and three
   failed attempts reads differently from one. */
.atoast-n { flex-shrink:0; align-self:center; font-size:0.78rem; font-weight:700;
    color:var(--ink-3); font-variant-numeric:tabular-nums; }
/* Colour carries MEANING only, per the palette rule - the border-left is the
   whole signal, the surface stays neutral so the text keeps its contrast. */
.atoast-error   { border-left-color:var(--neg); }
.atoast-success { border-left-color:var(--pos); }
.atoast-info    { border-left-color:var(--accent); }

/* ── 2. Yes/no box ─────────────────────────────────────────────────────
   Reuses .modal/.mst-modal chrome wholesale so it lands inside the makeover
   rather than beside it; only the message block is its own. */
/* The yes/no box must sit above EVERY screen it can be opened from, and some of
   those carry very high stacking values of their own: Purchase's Variety picker
   is z-index 9999 with its dropdown at 999999, the lot selector overlay is 9999,
   the locked popup 9999999. Shared `.modal` is only z-index 1000, which was
   enough while this box was a native confirm (the browser drew that outside the
   page entirely) and is NOT enough now. Found live, Session 98: the Variety
   field painted straight through the open box, over its own message.
   1e7 clears the highest thing in the app (9999999). */
.acnf { z-index:10000000; }
/* Vertically centred. It sat at top:20px because the shared `.modal.show` uses
   align-items:flex-start - fine for a tall form, wrong for a 207px question box
   that left 553px of empty screen under it (measured at 1528x780). Ali, Session
   98: "can we bring it little down or in center, right now its high on screen".

   `margin:auto` on the CHILD, not `align-items:center` on the parent. They look
   identical until the box is taller than the window: `align-items:center`
   overflows the flex container in BOTH directions, so the top of the box goes
   above the scroll origin and simply cannot be reached. margin:auto centres
   when there is room and degrades to normal flow when there is not. Only ever
   an issue at small window heights or large text, which is exactly where a
   cut-off dialog would be worst. */
.acnf.show .modal-content { margin:auto; }
.acnf .modal-content { max-height:none; }
.acnf .acnf-msg { margin:0; font-size:0.95rem; line-height:1.45; color:var(--ink-2);
    white-space:pre-line; }
.acnf .modal-footer { display:flex; justify-content:flex-end; gap:8px; }
/* CZ125 date prompt (S175): the date input was a small native control marooned in
   a wide box. Give it room and match the app's field height. */
.mst-rate-date-inp { width:100%; height:38px; margin-top:12px; padding:6px 12px;
    font-size:var(--fs-md, 0.95rem); border:1.5px solid var(--line); border-radius:var(--radius-sm); }
/* Arrow-key movement between the buttons is useless if you cannot SEE which one
   is focused, and neither .btn nor the shared modal chrome carries a focus ring
   (the two :focus-visible rules in the app are scoped to .si6 and .fnd, neither
   of which is an ancestor here). :focus, not :focus-visible - the ring must show
   for arrow/Tab movement, which is exactly the case :focus-visible suppresses
   after a mouse click. */
.acnf .modal-footer .btn:focus { outline:3px solid var(--ink); outline-offset:2px; }

/* The facts block (opts.facts) - the offending FIGURES, large and red, each
   beside the normal figure it is judged against. Ali, Session 98: one box
   carrying every suspect number, "whats unusual get some red color and little
   bigger size", instead of two plain boxes in a row.
   --neg on --neg-tint measures 4.94:1, so the big red numbers pass AA as text
   (unlike the row-hover/icon pair logged as G26). */
.acnf .acnf-facts { margin:12px 0 0; display:flex; flex-direction:column; gap:8px; }
/* GRID, not flex-wrap. With flex the rows sized themselves independently, so a
   longer value ("80.00 kg/bag") pushed its comparison onto a second line while
   the row above kept both on one - two rows of the same kind visibly disagreeing
   about their own shape, and one 28px taller than the other. Measured live.
   Three fixed columns make every row identical whatever the values are. */
.acnf .acnf-fact { display:grid; grid-template-columns:auto 1fr auto; align-items:baseline;
    column-gap:10px; padding:8px 12px; border-radius:var(--radius-sm);
    background:var(--neg-tint); border:1px solid var(--neg); }
.acnf .acnf-fact-lab { font-size:var(--fs-lbl); font-weight:700; color:var(--ink-2);
    text-transform:uppercase; letter-spacing:.4px; white-space:nowrap; }
.acnf .acnf-fact-val { font-size:1.45rem; font-weight:800; color:var(--neg-deep);
    font-variant-numeric:tabular-nums; line-height:1.15; white-space:nowrap; }
/* The comparison figure stays deliberately quiet - it is the reference, not the
   problem, and making both loud would leave nothing standing out. */
.acnf .acnf-fact-cmp { font-size:0.86rem; color:var(--ink-2); text-align:right;
    font-variant-numeric:tabular-nums; white-space:nowrap; }
.acnf .acnf-note { margin:12px 0 0; font-size:0.9rem; color:var(--ink-2); line-height:1.45; }

/* opts.tone - replaces showLockedPopup's big centred icon. That component put a
   2em glyph above the title; here the weight goes into a tinted header strip
   plus a small round mark, which says the same thing without a second layout.
   Two tones only: 'warn' (something to look at) and 'danger' (something will be
   lost). No tone = the ordinary sand header, which is what every box that is
   simply asking a question gets. */
.acnf .modal-header.acnf-head-warn   { background:var(--warn-tint); border-bottom-color:var(--warn); }
.acnf .modal-header.acnf-head-danger { background:var(--neg-tint);  border-bottom-color:var(--neg); }
.acnf .acnf-mark { flex:0 0 auto; order:-1; width:21px; height:21px; margin-right:9px;
    border-radius:50%; display:inline-flex; align-items:center; justify-content:center;
    font-size:0.82rem; font-weight:800; color:#fff; line-height:1; }
.acnf .acnf-head-warn   .acnf-mark { background:var(--warn-deep); }
.acnf .acnf-head-danger .acnf-mark { background:var(--neg-deep); }
/* The header is `justify-content:space-between` for the title/close pair; with a
   mark in front, the title must sit against it rather than being pushed apart. */
.acnf .modal-header.acnf-head-warn,
.acnf .modal-header.acnf-head-danger { justify-content:flex-start; }

/* ── 3. Red-field marking ──────────────────────────────────────────────
   Replaces the ~75 "X is required" popups. The field outlines, its label
   turns red, and the words go to the corner message - never under the field,
   because Purchase's item row is 62px-wide boxes with no room for a sentence. */
.fld-bad, input.fld-bad, select.fld-bad, textarea.fld-bad {
    border-color:var(--neg) !important;
    background:var(--neg-tint) !important;
    box-shadow:0 0 0 2px color-mix(in srgb, var(--neg) 18%, transparent) !important; }
.fld-bad:focus, input.fld-bad:focus, select.fld-bad:focus, textarea.fld-bad:focus {
    border-color:var(--neg) !important;
    box-shadow:0 0 0 3px color-mix(in srgb, var(--neg) 28%, transparent) !important; }
.fld-bad-lab { color:var(--neg-deep) !important; font-weight:700; }
/* The search dropdowns (party/variety/godown/employee) are a wrapper div with
   a generated input inside, so the mark has to reach the real control. */
.fld-bad > input { border-color:var(--neg) !important; background:var(--neg-tint) !important; }

/* ══ VOUCHER ENTRY — redesigned Session 108 ═══════════════════════════════════
   Spec: docs/superpowers/specs/2026-08-09-accounts-voucher-ui-redesign-design.md
   Visual source of truth while building: static/voucher-demo.html

   Replaces the rejected S107 form wholesale. That one used 820px of an 1086px
   space, scrolled the page 57px, put the row's remove button on its own line,
   used raw browser <select>s, and let the bills panel push Save off screen.

   The shape here is the app's own document shape — the one Purchase Entry, Jobs
   detail and Sales Invoice already share: header bar / tinted identity band /
   entry strip / body / pinned foot. Nothing new was invented for Accounts.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The doc fills the middle column between the two navs and never scrolls the
   page: the LIST absorbs all slack and scrolls internally instead.
   MEASURED at 1528x695, not guessed: #accounts is on the 20px-uniform padding
   rollout and the screen's own h1 is hidden on voucher leaves (.acc-vmode
   above), so the whole budget is the viewport minus 20px top + 20px bottom.
   Re-derive with the §7.4 recipe in the makeover spec if the shell changes. */
/* Voucher leaves hide the screen's own "Accounts" h1 — the voucher titles
   itself, so the h1 was a duplicate costing ~59px of the height budget. */
/* NAME COLLISION BUG, fixed 2026-08-09: this flag was also called .acc-vmode,
   which is the Payment-mode wrapper class a few rules below with
   display:flex on it. Same specificity as .main-section{display:none} but
   later in the file, so once you had visited a voucher, #accounts stayed
   display:flex FOREVER and rendered underneath Jobs, Purchase, everything.
   Distinct name now. */
/* S131 — THE HEADING IS GONE ON EVERY ACCOUNTS SCREEN, not eight of nine.
   Three rules had accumulated hiding it per screen (.acc-voucher-open here,
   .acc-daybook-open below, .acc-ledger-open / .acc-list-open further down) and
   Import / Export was the one holdout — which is precisely the screen where the
   right column then overshot the viewport by 59px, because #accSide's height is
   a constant written against a top that only exists when the h1 is not there.
   That is the SAME class of bug the per-screen max-height overrides were, and
   removing the last exception is what actually ends it: one top, one constant,
   nothing left to restate. The individual rules stay below - harmless now, and
   each carries its own reasoning. */
#accounts > .section-title{display:none}
#accounts.acc-voucher-open > .section-title{display:none}
/* The Day Book titles itself the same way the voucher does, and the tree's
   highlighted leaf is already the breadcrumb - so the screen-level "Accounts"
   h1 is a duplicate that costs 39px of the table's height. Ali chose the Day
   Book only for now; the statement screens keep theirs until each is redone
   in its own turn ("we are redesigning ui-ux and so will clear all screen one
   by one"). */
#accounts.acc-daybook-open > .section-title{display:none}
.acc-vpanel{display:flex;min-height:0}
.acc-vdoc{flex:1;min-width:0;display:flex;flex-direction:column;
  height:calc(100vh - 40px - var(--mstrip-h));
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow);overflow:hidden}

/* ── header bar ───────────────────────────────────────────────────────────── */
.acc-vhead{display:flex;align-items:center;gap:12px;padding:9px 14px;
  border-bottom:1px solid var(--line);background:var(--surface);flex-shrink:0}
/* Voucher type tints the TITLE ONLY. Ali cut the vertical stripe: "color coded
   voucher header is fine, but i dont think that vertical line is not good."
   In/out semantics, the same ones Stock uses — not a new palette. */
.acc-vtitle{font-size:1.12rem;font-weight:700;color:var(--ink)}
.acc-vtitle.t-out{color:var(--neg-deep)}
.acc-vtitle.t-in{color:var(--pos-deep)}
.acc-vno{font-size:0.76rem;font-weight:700;background:var(--head);color:var(--ink-2);
  border:1px solid var(--line);border-radius:var(--radius-pill);padding:2px 10px}
.acc-vnav{display:flex;align-items:center;gap:2px}
.acc-vnavb{font:inherit;font-size:0.78rem;cursor:pointer;background:var(--surface);
  border:1px solid var(--line);color:var(--ink-2);padding:2px 8px;border-radius:6px}
.acc-vnavb:hover:not(:disabled){background:var(--brand-tint);color:var(--ink)}
.acc-vnavb:disabled{opacity:.4;cursor:default}
.acc-vpos{font-size:0.74rem;color:var(--ink-3);padding:0 6px;white-space:nowrap}
.acc-vhint{margin-left:auto;font-size:0.71rem;color:var(--ink-3);white-space:nowrap}
.acc-vhint kbd{font-family:ui-monospace,monospace;font-size:0.95em;background:var(--head);
  border:1px solid var(--line);border-radius:4px;padding:0 4px;color:var(--ink-2)}
.acc-vx{font:inherit;font-size:1.1rem;line-height:1;cursor:pointer;background:var(--surface);
  border:1px solid var(--line);color:var(--ink-2);width:28px;height:28px;border-radius:6px}
.acc-vx:hover{background:var(--brand-tint);color:var(--ink)}

/* ── identity band ────────────────────────────────────────────────────────── */
.acc-vid{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap;padding:9px 14px;
  background:var(--head);border-bottom:1px solid var(--line);flex-shrink:0}
.acc-vf{display:flex;flex-direction:column;gap:3px;position:relative}
/* `.acc-vf > label` is a DIRECT-child selector, and Paid from's label now sits
   one level deeper inside .acc-vbank-lbls (it shares a row with Balance). Without
   the second selector it silently dropped to browser default — larger, mixed
   case, sitting beside DATE and BALANCE in small caps. */
.acc-vf > label,.acc-vbank-lbls > label{font-size:var(--fs-lbl);font-weight:700;
  letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3)}
/* Input standard: one --line border, 6px radius, accent border + ring on focus. */
.acc-vf input,.acc-vf select{font:inherit;font-size:0.9rem;padding:6px 9px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface);
  color:var(--ink);height:34px}
.acc-vf input:focus,.acc-vf select:focus{outline:2px solid var(--accent);
  outline-offset:-1px;border-color:var(--accent)}
/* R5-7 — a control that does not apply right now GREYS; it never vanishes.
   Ali: "if its not related then it gets greyed out." The Amount box in By-bill
   is the first instance: it keeps its size and its place on the money column,
   so the page holds one shape whatever mode it is in. The label greys with it,
   or a live label sits over a dead field. */
.acc-vf.is-off > label{color:var(--ink-3);opacity:.55}
.acc-vf input:disabled{background:var(--head);border-color:var(--line-2);
  color:var(--ink-3);cursor:not-allowed}

/* Money account and its balance: SAME tone (Ali cut the white-field-beside-a-
   sand-block version), but as two separate boxes with a gap, because welded
   together they read as one broken control — "give little empty space after
   paid from so it can feel like 2 different feilds".

   NO overflow:hidden here. It was clipping the combobox panel dead, which is
   why typing in Paid from appeared to do nothing at all — the classic
   dropdown-clipped-by-panel-overflow trap, hit again. */
/* gap 12, not 8 — it is the same gap the entry strip puts before its Amount box,
   which is what lands the balance chip exactly over the Amount below it. */
.acc-vbank{display:flex;align-items:stretch;gap:12px}
.acc-vbank .box{display:flex;align-items:center;border:1px solid var(--line);
  border-radius:6px;background:var(--surface);height:34px;position:relative}
.acc-vbank .box:focus-within{outline:2px solid var(--accent);outline-offset:-1px;
  border-color:var(--accent)}
.acc-vbank .box.name{flex:1;min-width:0}
.acc-vbank input{font:inherit;font-size:0.9rem;padding:6px 9px;border:none;outline:none;
  background:transparent;color:var(--ink);width:100%}
/* The balance chip takes the MONEY COLUMN's width, so it sits directly over the
   Amount box, the line amounts and the foot Total — Ali: "balance beside paid
   from has a small field, i think that field should match with the amount below,
   so all look aligned."
   The figure now has the box to ITSELF (the "BAL" caption moved out to a label
   above it), right-aligned on the same edge as every other figure. It has to
   hold the widest thing this business can produce, with a minus in front — and
   that ceiling MOVED on 2026-08-17: it was -Rs9,99,99,999.99 in 124px, and Ali
   passed a Rs100 crore receipt. Now -Rs1,00,00,00,000.00, which needs 133.5 of
   the 156 the column carries (see --acc-vamt-w on .acc-vdoc for what set that
   number and what paid for it). Measured, not guessed. */
.acc-vbank .bal{display:flex;align-items:center;justify-content:flex-end;
  padding:0 7px;width:var(--acc-vamt-w);flex-shrink:0;
  font-size:0.78rem;font-weight:600;white-space:nowrap;font-variant-numeric:tabular-nums}
/* Two labels on one row, each over its own box: Paid from takes the slack,
   Balance takes exactly the money column so the word sits over the figure. */
.acc-vbank-lbls{display:flex;gap:12px;align-items:baseline}
.acc-vbank-lbls > label{flex:1;min-width:0}
/* min-width, not width, plus nowrap (2026-08-12). The label now carries the side
   and a plain word — "Balance (Dr — in hand)" / "Balance (Cr — overdrawn)" — and
   at a fixed 124px that wrapped to two lines and doubled this row's height from
   16 to 32, shoving the form down. It still ENDS on the money column's right
   edge, which is what puts it over the figure; it is simply allowed to grow
   leftwards into the slack "Paid from" was already sharing. Measured after: one
   line, right edge still 1115, same as the box below it. */
.acc-vbank-lbls .bal-lbl{min-width:var(--acc-vamt-w);flex-shrink:0;text-align:right;
  white-space:nowrap;
  padding-right:7px;box-sizing:content-box;
  font-size:var(--fs-lbl);font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-3)}
/* Paid from takes the slack that used to sit dead between the balance and the
   mode switch. NO max-width: the cap stopped this block 66px short, which left
   Paid from ending on 966 while Paid to below it ended on 971 — Ali: "paid from
   and paid to does not end at same place ... we can match it". Filling the row
   puts the block's right edge on the money column (1107), so the balance chip
   lands on the Amount box and the name box ends on 971, dead level with Paid
   to. Two of his complaints are the same measurement. */
.acc-vf.acc-vf-bank{flex:1;min-width:340px}
/* Purchase/Sales voucher: the party box eats the row's spare width, which puts
   Bill ref and Bill date against the right edge instead of leaving a gap there,
   and stops the dropdown wrapping long adthidar names over three lines (Ali,
   2026-08-14). Same shape as .acc-vf-bank above. The inner combo has to be told
   100% - it is generated by createSearchDropdown and does not fill on its own. */
.acc-vf.acc-vf-party{flex:1;min-width:340px}
.acc-vf.acc-vf-party .acc-vcombo{width:100%}
/* The party's balance shares the label row with the word "Party" and sits at the
   right edge, over its own box — same arrangement as .acc-vbank-lbls, which is
   there so the figure costs the NAME box no width. This one is not width-capped:
   it carries Dr/Cr plus a plain word ("(they owe us)"), and the row it sits on is
   as wide as the party box, which now takes the whole spare row. */
.acc-vparty-lbls{display:flex;gap:12px;align-items:baseline}
.acc-vparty-lbls > label{flex:1;min-width:0;font-size:var(--fs-lbl);font-weight:700;
  letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3)}
.acc-vparty-lbls .bal-lbl{flex-shrink:0;text-align:right;white-space:nowrap;
  padding-right:7px;font-size:var(--fs-lbl);font-weight:700;letter-spacing:.05em}
/* Z46 #7 - the result band, directly above the footer so it is the last thing
   read before Save. Quiet by design: it states a fact about what is about to
   happen, it is not a control and must not compete with the Save button. */
.acc-vbill{padding:7px 14px;border-top:1px solid var(--line-2);background:var(--head);
  font-size:0.9rem;color:var(--ink);display:flex;flex-wrap:wrap;gap:5px;align-items:baseline}
.acc-vbill .k{color:var(--ink-3)}
.acc-vbill .sep{color:var(--line-1)}
.acc-vbill b{font-weight:700}
.acc-dr{color:var(--pos-deep)}
.acc-cr{color:var(--neg-deep)}

/* Payment mode. --radius-sm, the value buttons already use — not a pill.
   Only rendered for Payment and Receipt. */
.acc-vmode{margin-left:auto;display:flex;flex-direction:column;gap:3px}
.acc-vmode > label{font-size:var(--fs-lbl);font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-3)}
.acc-vseg{display:flex;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius-sm);padding:2px;height:34px}
.acc-vseg button{font:inherit;font-size:0.8rem;font-weight:600;cursor:pointer;border:none;
  background:transparent;color:var(--ink-2);padding:0 13px;border-radius:6px;
  white-space:nowrap;display:flex;align-items:center;gap:5px}
.acc-vseg button.on{background:var(--accent);color:#fff}
/* Journal's Dr/Cr side switch reuses the mode segment wholesale — same control,
   same F3 habit, so there is one pattern to learn. It does NOT take margin-left
   auto: unlike Payment's mode switch it sits between the ledger picker and the
   Amount box, in the reading order of the sentence being built ("this ledger, on
   this side, for this much"). */
.acc-vside{margin-left:0;flex-shrink:0;justify-content:flex-end}
.acc-vside .acc-vseg button{white-space:nowrap}
.acc-vseg button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

/* ── entry strip ──────────────────────────────────────────────────────────── */
/* Ali: "paid from area has some background color but paid to does not, shall we
   give some?" Yes — and a LIGHTER tint than the band above, not the same one.
   Two reasons. The entry strip and the identity band are different jobs (facts
   about the voucher / the line being typed), and white left the strip melting
   into the list of committed lines below it, separated by a hairline only.
   And the same tint would have been actively wrong: the greyed Amount box IS
   --head, so on a --head strip it vanishes completely — shot and confirmed
   (_shots/bg-B-same-tint.png shows the "0" with no box at all), which is exactly
   the disappearing box R5-7(a) was built to stop. */
.acc-vstrip{display:flex;align-items:flex-end;gap:12px;padding:10px 14px;
  border-bottom:1px solid var(--line);background:var(--acc-vstrip-bg);flex-shrink:0}
.acc-vstrip .grow{flex:1;min-width:0}
.acc-vstrip .grow input{width:100%}
/* Amount sits hard against the right edge — Ali removed the hint text that
   used to sit beside it. */
/* The entry row's Amount belongs to the SAME money column as the line amounts
   and the foot Total -- Ali: "amount on top row, amount in table and amount in
   footer will have aligment, all 3 amount will be in single column so it look
   arranged." It used to be 170px wide and flush to the document's right edge,
   overhanging the other two by the whole width of the action column.
   Same width as the others now, and held off the edge by exactly the action
   column plus the row gap (12 flex gap + 10 margin on .acc-vl-act), which is
   what puts the three right edges on one line. The space this leaves is the
   entry row's action area -- deliberately empty (D2), not a slot wanting a
   button. */
.acc-vstrip .amt{margin-left:auto;margin-right:calc(var(--acc-vact-w) + 22px)}
.acc-vstrip .amt input{width:var(--acc-vamt-w);text-align:right;font-variant-numeric:tabular-nums;
  padding-right:7px}
/* Journal (Debit + Credit) and Contra join the same grid — R5-7's fixed page
   applies across the six types, not just to Payment. Only the LAST box carries
   the action-column offset, or Debit and Credit would be pushed apart; the width
   matches .acc-vl-amt.jrnl's 130px columns so the strip sits over the table. */
.acc-vstrip .num input{width:130px;text-align:right;font-variant-numeric:tabular-nums}
.acc-vstrip .num:last-child{margin-right:calc(var(--acc-vact-w) + 22px)}
/* The strip's own flex gap is 12px, the line row's is 10px. Left alone, Journal's
   Debit box sat 2px left of the Debit column it heads — small, but it is exactly
   the kind of jog that makes a column read as crooked. */
.acc-vstrip .num + .num{margin-left:-2px}

/* The generated combobox from createSearchDropdown lives inside .acc-vf, whose
   position:relative anchors its absolutely-positioned panel. */
.acc-vf .acc-vcombo{width:100%}

/* ── particulars ──────────────────────────────────────────────────────────── */
/* DELIBERATE EXCEPTION to the table standard's forest column header, recorded in
   §3.1 of the spec: this is a document body (1-3 hierarchical rows, party ->
   its references), not a list of records. The allocation popup's bill list IS a
   real table and takes the full standard. */
/* The column header is a HEADER, not a caption floating over the first row —
   Ali: "also add header line No Particular and amount". On white with one
   hairline it read as loose text; it now carries the band tint and a rule top
   and bottom, so No. / Particulars / Amount sit in their own strip between the
   entry area above and the entries below. */
/* Ruled top and bottom, but NOT filled with --head: that is the row hover colour,
   so hovering the first entry made it merge into the heading above it — Ali:
   "when we hove on first entry header line and first entry color matches". The
   two rules are what make it read as a header line; the fill was doing no work
   and was colliding with a state. */
.acc-vlisth{display:flex;padding:6px 14px;border-bottom:1px solid var(--line);
  border-top:1px solid var(--line);
  background:var(--surface);flex-shrink:0;font-size:var(--fs-lbl);font-weight:700;
  letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3)}
/* The money column's true edge is the DIGITS, not the box around them: a line
   figure sits inside 7px of padding and a 1px border, so its last digit lands on
   1099 while the box ends on 1107. The heading was right-aligned to the box and
   therefore hung 8px past every figure it heads — Ali: "amount text is not
   centered to amount". Same 8px goes on the foot Total, and the entry box's own
   padding is trimmed to match, so heading, typed figure, line figures and Total
   all end on ONE vertical line. */
.acc-vlisth-r{margin-left:auto;width:var(--acc-vamt-w);flex-shrink:0;text-align:right;
  padding-right:8px;box-sizing:content-box}
/* V7/V16 — a leading No. on every committed line. "No." first is this app's
   house convention (Purchase ledger, Purchase entry items, Masters, Jobs), and
   it lets a line be named out loud once a voucher pays five adthidars. Only
   the PARTY lines are numbered; the reference sub-rows deliberately are not
   (1 / 1.1 / 1.2 is noise, and the indent already carries the hierarchy).
   Fixed width so the names below stay on one optical left edge. */
/* +10px: the row puts a flex gap between its No. and the party name, the header
   has no gap of its own, so PARTICULARS started 10px left of every name under
   it — Ali: "particular dont start same as ledger below". */
.acc-vlisth-no{width:26px;flex-shrink:0;margin-right:10px}
/* one source of truth for the action strip's width */
/* one source of truth for the action strip AND the money column */
/* THE MONEY COLUMN HOLDS A HUNDRED CRORE, 124 -> 156 (2026-08-17). Ali, on his
   own Rs100 crore receipt no. 7: the figure runs outside its box. Measured on
   that voucher rather than estimated, and the WORST offender was not the box he
   was looking at — it was the saved line amount, the largest type in this column
   at 14.25px/600, which needed 152.5px of a 124px box and clipped. The Amount
   typing box (13.5px) and the balance chip (11.7px, allowing a leading minus)
   both needed 133.5. One variable moves all five boxes together, which is the
   whole reason it is a variable.
   156 is not a new number: the Ledger's Debit and Credit columns took exactly it
   in S129 for exactly this figure, and the spare here (3.5px) matches the spare
   there (3.7px). Two screens showing the same money now fail at the same point.
   WHAT PAYS FOR IT: the bank-name box, the only flexible item on that row —
   331.7 -> 299.7 at his 1150 half-screen, still 127px clear of the 340px minimum
   on the field around it. Verified at 1528 and 1150: no page scroll, no scroll
   inside the voucher, every box holding Rs1,00,00,00,000.00 on one line.
   The comment at .acc-vbank .bal still describing "-Rs9,99,99,999.99 inside
   124px" is the assumption that expired — ten crore was the old ceiling. */
.acc-vdoc{--acc-vact-w:176px;--acc-vamt-w:156px;
  /* Ali: the entry strip "should differ from top box, so user aware both are
     different area" — and, of the three shot side by side, "for paid to use cool
     tint". A COOL grey-green against the warm --head band above it: the
     difference is a difference in kind, not one more shade of the same beige,
     which is what made the warm version read as muddy. --zebra was tried first
     and measured at five shades off white, far too faint to say anything at all. */
  --acc-vstrip-bg:#f2f4f2}
/* --acc-vact-w is sized by the WIDEST thing that ever sits in the action column,
   and that is the foot: Save (74) + 8 gap + Delete (83) = 165. At the old 132 the
   pair overflowed its slot by 25px and painted straight over the Total -- Ali,
   on a saved voucher: "save and delete does not have enough space". Every row's
   actions share this width, so Edit/Remove line up under Save/Delete. */
.acc-vl-no{width:26px;flex-shrink:0;color:var(--ink-3);font-size:0.82rem;
  font-variant-numeric:tabular-nums}
.acc-vlines{flex:1;min-height:0;overflow-y:auto;padding:4px 0}
.acc-vline{padding:7px 14px 8px;border-bottom:1px solid var(--line-2)}
/* Zebra, because an ENTRY is now three or more rows tall (party, its
   references, its note) and a hairline is not enough to show where one ends
   and the next begins. Ali: "multiple entries does not get zebra pattern, as
   here one entry is 3 or more lines." The band groups the whole block.
   HOVER MUST NOT BE --zebra: it used to be, and with --zebra now the resting
   stripe, hovering an even row would have changed nothing at all. */
.acc-vline:nth-child(even){background:var(--zebra)}
/* Stays --head. It only ever LOOKED wrong for the one commit where the column
   heading was also filled with --head and the hovered first entry merged into
   it; the heading is white again, so the collision is gone and the hover Ali
   likes is untouched. Ali: "dont change hover tint it was good." (The older note
   below still holds: it must not be --zebra, the resting stripe.) */
.acc-vline:hover,.acc-vline:nth-child(even):hover{background:var(--head)}
.acc-vl-top{display:flex;align-items:baseline;gap:10px}
.acc-vl-name{font-weight:700;font-size:0.95rem}
/* CONTRA — the destination's own balance, under its name in Particulars, exactly
   where Tally prints "Cur Bal: 35,61,176.45 Dr". Both sides of a contra are cash
   or bank and the From balance already sits in the header band, so this is the
   other half of the pair the screen exists to compare.

   A BLOCK, so it drops to its own line under the name instead of pushing the
   amount column left — .acc-vl-top is a baseline flex row and an inline sibling
   here would shift every figure on the row. Regular weight and small: the account
   NAME is what the eye scans for (same reasoning that keeps names bold and
   figures 600), and this is reference, not the subject. */
.acc-vl-curbal{display:block;font-weight:400;font-size:0.78rem;
  margin-top:2px;font-variant-numeric:tabular-nums}
/* JOURNAL — Tally's By / To in front of the account name, the side in a word.
   Fixed width so the names below each other start on one edge whichever word it
   is; italic and grey because it is a marker, not part of the name. */
/* By / To — the side in a word, AND the control that changes it. Fixed width so
   the names below each other start on one edge whichever word it is; italic and
   grey because it is a marker, not part of the name. Styled as a bare button:
   the hover is what says it can be clicked, since a permanent box on every row
   would compete with the amount boxes it sits opposite. */
.acc-vl-byto{width:24px;flex-shrink:0;font-style:italic;font-size:0.82rem;
  font-weight:600;color:var(--ink-3);font-family:inherit;background:none;
  border:1px solid transparent;border-radius:4px;padding:0;cursor:pointer;
  text-align:left}
.acc-vl-byto:hover{color:var(--accent-2);border-color:var(--line);background:var(--surface)}
.acc-vl-byto:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
/* The account name, when it can be re-picked. Same restraint: underline on hover
   only, so a list of lines does not read as a page of links. */
/* SWITCHED-OFF account tag — one style, used on the voucher line, in the Day
   Book, the Trial Balance tree, the party list and the ledger heading, so the
   word means the same thing wherever it is met (2026-08-13).

   Masters has an "Inactive" pill already (.badge-danger: pale pink, dark red
   text) and the WORD is deliberately the same — but that pill lives in a status
   COLUMN, where being quiet is correct. Here it sits inside a line of names and
   money and has to be found at a glance. Ali: "make sure its highlighthed may be
   red and little brighter so some good visual hint". Hence solid red on white
   text. It is not a button and never clickable — smaller than any .btn, no
   pointer cursor — so it cannot be mistaken for the red Delete (row-button
   standard: one solid red for delete). */
.acc-inact{display:inline-block;vertical-align:1px;margin-left:7px;padding:1px 7px;
  border-radius:9px;background:#e02020;color:#fff;font-size:0.68rem;font-weight:700;
  letter-spacing:0.03em;text-transform:uppercase;white-space:nowrap;cursor:default}
/* Never inherit the name's underline-on-hover — the tag is not part of the link. */
.acc-vl-name.ed:hover .acc-inact{text-decoration:none}
.acc-vl-name.ed{cursor:pointer}
.acc-vl-name.ed:hover{color:var(--accent-2);text-decoration:underline}
.acc-vl-nameedit{display:inline-block;min-width:260px;vertical-align:middle}
/* Reverse Dr/Cr sits in the heading row's action slot, above the Edit/Remove it
   acts on. Right-aligned to that column, and normal-case so it does not read as
   another column heading. */
.acc-vlisth-act .acc-vrev{float:right;text-transform:none;letter-spacing:0;
  font-weight:600}
/* The side a Journal line does NOT use. Holds the column open so the figures
   opposite stay in one straight line, and draws nothing at all — no box, no dash.
   Ali: "a party cannot have 2 amount dr and cr in same line". An empty cell says
   that; a dash in a box says "two amounts, one of them nil". */
.acc-vl-amt.jrnl.is-empty{background:none;border:1px solid transparent;
  cursor:default;pointer-events:none}
/* FIXED width, right-aligned. Shrink-to-fit gave every row a differently
   sized box — Ali: "amount in table has background color as per size, if
   figure is small then it look small and if figure is big then different, it
   does not look similar." Sized for the widest realistic figure,
   Rs99,99,999.99, so the box never resizes as you type either. */
/* 600, not 700. Ali: "bold make figure looks cramped" — and he is right about
   Inter in particular: at 14px its bold digits tighten noticeably, and these
   figures are already tabular-nums in their own column, so weight was doing no
   work that position and alignment were not already doing. Party names KEEP
   their bold: they are what the eye scans for. */
.acc-vl-amt{margin-left:auto;font-weight:600;font-size:0.95rem;font-variant-numeric:tabular-nums;
  width:var(--acc-vamt-w);flex-shrink:0;text-align:right}
.acc-vl-bal{font-size:0.76rem;color:var(--ink-3)}
/* Click-to-edit amount. V6 — Ali: "amount need some thing like feild so
   user know if i click it will be autofocused and i can edit it." A dashed
   underline on hover was invisible until you happened to mouse over it;
   this is now a resting field affordance (the same --head/--line tint the
   app already uses for readonly-field), visible before you touch it.
   Applies to the Journal Dr/Cr cells too — they share this class. */
/* Individual margin properties, NOT the shorthand — `margin:` sets all four
   sides including margin-left, which silently overrode .acc-vl-amt's own
   margin-left:auto (the rule that pushes this box to the right edge, flush
   before Remove) and dragged the box back next to the name. Ali: "you moved
   amount position from right to left." */
.acc-vl-amt.ed{cursor:pointer;background:var(--head);border:1px solid var(--line);
  border-radius:6px;padding:1px 7px;margin-top:-1px;margin-bottom:-1px;
  transition:border-color .15s,background .15s}
.acc-vl-amt.ed:hover{border-color:var(--accent);background:var(--surface);color:var(--accent-2)}
/* ONE rim, not two. Ali: "edit amount when pressed box look 2 tooned, border and
   background does not match." It carried a 1px accent BORDER and, on focus, a 2px
   accent OUTLINE inset by 1px — two concentric edges in two shades around a fill
   that changed colour at the same moment. Now it is the app's ordinary input
   treatment: a --line border, and the accent appears only as the focus ring.
   Width 100%: the editor used to be var(--acc-vamt-w) INSIDE a span that still
   carried 7px of side padding, so it overhung the money column by 8px and the
   figure jumped right the instant you clicked it. The padding goes with
   .editing below; this fills exactly what is left. */
.acc-vl-amtin{font:inherit;font-size:0.95rem;font-weight:600;width:100%;padding:1px 6px;
  text-align:right;border:1px solid var(--line);border-radius:6px;background:var(--surface);
  color:var(--ink);font-variant-numeric:tabular-nums}
.acc-vl-amtin:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
/* While editing, the box IS the input — the span stops drawing a second one
   around it. Keeps the editor on the money column and kills the doubled rim. */
.acc-vl-amt.editing{padding:0;border:none;background:transparent}
/* The F3 hint on the Payment-mode LABEL (never on the Direct button, where it
   read as "press F3 for Direct"). */
.acc-vmode > label .acc-vkey{font-size:0.62rem;font-weight:700;color:var(--ink-3);
  background:var(--surface);border:1px solid var(--line);border-radius:4px;padding:0 4px;
  font-family:ui-monospace,monospace;margin-left:4px;text-transform:none;letter-spacing:0}
/* The note belongs to the LINE, so it sits below the whole reference block,
   indented to the same 36px. A permanent field rather than a "+ note" link —
   Ali's call. It stops short of the action strip so it never runs under the
   buttons. */
/* Runs the FULL width of the entry, finishing exactly where Remove does. It used
   to stop at 1130 — 23px past the money column and 175px short of the action
   column, the only element on the screen answering to neither edge. Ali chose
   the long version over trimming it back: "or we extend till remove ends? so it
   seperate entries." It does: a full-width band under each party closes that
   entry off, which the zebra alone was not doing once an entry grew to three or
   four rows. The note row sits BELOW the action buttons, never beside them, so
   nothing collides. */
.acc-vl-noterow{display:flex;margin:5px 0 0 36px;padding-right:0}
.acc-vl-noteip{font:inherit;font-size:0.8rem;flex:1;min-width:0;color:var(--ink);
  padding:3px 8px;border:1px solid var(--line);border-radius:6px;background:var(--surface)}
.acc-vl-noteip::placeholder{color:var(--ink-3);font-style:italic}
.acc-vl-noteip:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
/* No left rule, and the reference reads as ONE fact: label, number, amount,
   grouped together. Ali: "i need ref number and its payment on same side ...
   ref and its number on left side and amount on right side does not look
   good." A happy consequence: the Amount column now holds ONLY the line
   totals — the figures that actually sum to the foot Total — with the
   reference breakdown as an indented detail beneath.
   36px indent = .acc-vl-no (26px) + the row's 10px flex gap, so references
   start exactly under the party name. */
.acc-vl-refs{margin:5px 0 0 36px}
.acc-vl-ref{display:flex;gap:10px;font-size:0.83rem;color:var(--ink-2);padding:1px 0;
  cursor:pointer;border-radius:4px}
.acc-vl-ref:hover{background:var(--brand-tint)}
/* R6-4b — the reference's own note, read back onto the voucher that WROTE it.
   It was stored, reloaded into memory and then never drawn, so Ali's comment was
   invisible on his own receipt. Italic and quiet: it is a remark, not a figure,
   and it must not compete with the amount beside it. It takes the slack after
   the amount and ellipses rather than wrapping, so a long comment cannot push a
   one-line reference row into two and disturb the rows below. The whole row
   opens the bills window, which is where the full text can be read and edited. */
.acc-vl-refnote{font-style:italic;color:var(--ink-3);font-size:0.8rem;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.acc-vl-ref .t{font-weight:700;color:var(--ink-3);min-width:78px;font-size:0.76rem;
  letter-spacing:.03em;text-transform:uppercase}
/* min-width, not a fixed width: short refs (4281) keep their amounts in a tidy
   column, and a long one (SUPPLIER-OWN-123) simply pushes its own amount right
   instead of being clipped. */
.acc-vl-ref .num{font-variant-numeric:tabular-nums;color:var(--ink-2);min-width:62px}
.acc-vl-ref .a{font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}
/* Bills shows only on bill-allocated lines, so without a FIXED width Remove
   would slide left and right from row to row. One width for the strip, and the
   list header reserves the same, so "Amount" sits over the figures and not
   over the buttons. Defined once, here — a second .acc-vl-act rule elsewhere
   would silently win by source order. */
.acc-vl-act{display:flex;gap:5px;margin-left:12px;
  width:var(--acc-vact-w);flex-shrink:0;justify-content:flex-end}
.acc-vlisth-act{width:calc(var(--acc-vact-w) + 22px);flex-shrink:0}
/* Row action buttons — the S99 standard: Edit amber and NEVER --accent, every
   remove/delete one solid red. See the row-button-standard memory. */
.acc-vmini{font:inherit;font-size:0.72rem;font-weight:700;cursor:pointer;padding:2px 8px;
  border-radius:5px;background:var(--surface)}
.acc-vmini.ed{border:1px solid var(--warn-deep);color:var(--warn-deep)}
.acc-vmini.ed:hover{background:var(--warn-deep);color:#fff}
.acc-vmini.rm{border:1px solid var(--neg);color:var(--neg)}
.acc-vmini.rm:hover{background:var(--neg);color:#fff}
/* P-7 — these buttons are now a keyboard path through the lines (Tab in, then
   Up/Down), so the focus ring has to be unmistakable at 0.72rem, and the row
   under the cursor has to read as the row you are on. Same tint as hover: the
   cursor being there and the mouse being there mean the same thing. */
.acc-vmini:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.acc-vline:focus-within,.acc-vline:nth-child(even):focus-within{background:var(--head)}
.acc-vempty{padding:26px 14px;text-align:center;color:var(--ink-3);font-size:0.85rem;font-style:italic}
.acc-vctx{margin:2px 14px 0;padding:7px 11px;background:var(--brand-tint);
  border-radius:var(--radius-sm);font-size:0.8rem;color:var(--ink-2)}
.acc-vctx b{color:var(--ink)}

/* Journal shows two money columns instead of one. */
/* Two headings over Journal's two money columns. The 10px is the row's own flex
   gap between its Debit and Credit cells — the header carries it as a margin
   because it has no gap of its own. */
.acc-vlisth.jrnl .acc-vlisth-r{min-width:130px;text-align:right;margin-right:10px}
/* padding-right:8px — the digit-edge pull-in that .acc-vlisth-r has had since
   "amount text is not centered to amount". Journal's SECOND heading never got it,
   so CREDIT alone hung 8px past the figures it heads: measured 1107 against a
   column of digits ending at 1099, while DEBIT sat correctly on 959. Pre-existing,
   and invisible until the footer totals arrived underneath to compare it against.

   BORDER-box, so the 8px comes OUT of the existing 130px instead of being added
   to it. With content-box the cell grew, which shoved the DEBIT heading — the
   flex sibling before it, held right by margin-left:auto — 8px off ITS column.
   Fixing one heading by moving the other is not a fix. */
.acc-vlisth.jrnl .acc-vlisth-r2{min-width:130px;text-align:right;flex-shrink:0;
  padding-right:8px;box-sizing:border-box}
.acc-vl-amt.jrnl{min-width:130px;text-align:right;margin-left:0}
.acc-vl-jspacer{margin-left:auto}

/* ── foot — a FLEX SIBLING of the scrolling list, never a child. This is what
   makes Save impossible to push off screen. ───────────────────────────────── */
.acc-vfoot{display:flex;align-items:center;gap:14px;padding:10px 14px;
  border-top:1px solid var(--line);background:var(--head);flex-shrink:0}
.acc-vnarr{flex:1;min-width:0;display:flex;align-items:center;gap:9px}
.acc-vnarr label{font-size:var(--fs-lbl);font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-3)}
.acc-vnarr input{flex:1;min-width:0;font:inherit;font-size:0.88rem;padding:6px 9px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface);
  color:var(--ink);height:34px}
.acc-vnarr input:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
/* Total lands on the SAME right edge as the Amount heading and every line
   amount, and Save takes the action strip below Bills/Remove — so the screen
   reads as two straight columns: money, then actions.
   The wrapper is the strip MINUS .acc-vfoot's 14px gap, which is what puts
   Total's right edge exactly on the money column. */
.acc-vfoot-act{width:calc(var(--acc-vact-w) + 8px);flex-shrink:0;
  display:flex;gap:8px;justify-content:flex-end}
/* DELETE'S OWN SLOT, at the far left of the same bar (2026-08-13). Ali: "both
   delete and save are besides each other, i dont think we have kep them
   toghther". They were 8px apart with Delete LAST, so the destructive button sat
   where the eye lands for the primary one. This is the Masters record-page
   footer arrangement — the decision the app already made everywhere else.

   The width is FIXED and the button inside is only hidden, never removed from
   the flow, so the narration box does not jump sideways as you step between a
   new voucher (no Delete) and a saved one. Sized to the button's own width at
   its 8px/20px padding; if the label ever changes, re-measure it. */
.acc-vfoot-del{width:96px;flex-shrink:0;display:flex}
.acc-vfoot-del .acc-vbtn{padding-left:16px;padding-right:16px}
/* THE 8px IS LOAD-BEARING — do not "fix" it. The money column is where the
   DIGITS end, not where the boxes do: a line figure sits inside 7px of padding
   and a 1px border, so its last numeral lands 8px inside its box, and the heading
   and the Total are pulled in by exactly that so their text lands on the same
   edge. Ali's own report ("amount text is not centered to amount"), and
   voucher_keyboard_guard check 25 measures TEXT rects for this reason.

   Removed here on 2026-08-12 while aligning Journal, on a measurement of element
   boxes — the wrong edge — and check 25 went red within the minute. Recorded so
   the next person measuring box rects sees why they disagree with the screen. */
.acc-vtot{display:flex;align-items:baseline;gap:14px;white-space:nowrap;margin-left:auto;margin-right:8px}
.acc-vtot .grp{display:flex;flex-direction:column;align-items:flex-end}
/* JOURNAL — the two totals sit in the SAME two columns as the lines and the entry
   strip: 130px cells, 10px apart (.acc-vl-amt.jrnl / .acc-vlisth.jrnl above use
   exactly those numbers). With the badge moved to the left in the markup, the
   right-hand cell's edge is the wrapper's edge, and the 8px above then carries
   BOTH figures onto the digit edge of their columns — 959 and 1099 at 1528 wide,
   measured, against line digits ending on the same two. */
.acc-vtot.jrnl{gap:10px}
.acc-vtot.jrnl .grp.jm{min-width:130px}
/* The badge keeps breathing room from the figures it used to crowd. */
.acc-vtot.jrnl .acc-vtot-badge{margin-right:4px}
.acc-vtot .k{font-size:0.68rem;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3)}
.acc-vtot .v{font-size:1.28rem;font-weight:700;line-height:1.15;font-variant-numeric:tabular-nums}
.acc-vtot .v.sm{font-size:1.05rem}
.acc-vbal-ok{color:var(--pos-deep);font-weight:700;font-size:0.9rem}
.acc-vbal-bad{color:var(--neg-deep);font-weight:700;font-size:0.9rem}

/* Buttons — the app's action colours (button-action-color-system spec). Save is
   always solid green and never outline: it is the highest-frequency control. */
.acc-vbtn{font:inherit;font-size:0.88rem;font-weight:700;cursor:pointer;padding:8px 20px;
  border-radius:var(--radius-sm);border:1.5px solid transparent}
.acc-vsave{background:var(--pos-deep);color:#fff;border-color:var(--pos-deep)}
.acc-vsave:hover{filter:brightness(.93)}
/* D1 — the save confirm is gone, so the FOCUS RING is what now stands between
   Narration and the database: Enter on Narration lands here, and it has to be
   obvious that the next Enter saves. A ring both inside and outside, since the
   button is solid green and a single accent outline on it is easy to miss. */
.acc-vbtn:focus-visible{outline:3px solid var(--accent);outline-offset:2px;
  box-shadow:inset 0 0 0 2px #fff}
.acc-vdelbtn{background:var(--neg);color:#fff;border-color:var(--neg)}
.acc-vdelbtn:hover{filter:brightness(.93)}
/* Cancel Group A — charcoal outline, fills on hover (button spec §7). */
.acc-vghost{background:var(--surface);color:var(--ink);border-color:var(--ink);
  padding:6px 15px;font-size:0.82rem}
.acc-vghost:hover{background:var(--ink);color:#fff}

/* ══ BILL-WISE ALLOCATION POPUP ═══════════════════════════════════════════════
   Its own coherent piece, not bolted onto a party row (which is what Ali
   rejected). Opens over the voucher, Esc returns. ═══════════════════════════ */
.acc-vscrim{position:fixed;inset:0;background:rgba(20,28,38,.42);z-index:1400;
  display:flex;align-items:center;justify-content:center}
.acc-vpop{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  /* 960 -> 1090 (2026-08-12, Ali: "if you want to increase width of pop up i am
     ok"). The Source column takes 196px that the Reference cell used to lend to
     the tag; without the extra width the reference number and its read-only note
     would have been left ~63px. The min() is what keeps this safe on a narrow
     window — at the 1150px half-screen size this project designs against, 94vw
     wins and the popup shrinks rather than overflowing. Do not swap it for a
     bare px width. */
  box-shadow:var(--shadow-pop);width:min(1090px,94vw);max-height:88vh;
  display:flex;flex-direction:column;overflow:hidden}
.acc-vpop-h{display:flex;align-items:center;gap:12px;padding:11px 16px;background:var(--head);
  border-bottom:1px solid var(--line);flex-shrink:0}
.acc-vpop-t{font-size:1rem;font-weight:700}
.acc-vpop-sub{font-size:0.8rem;color:var(--ink-3)}
.acc-vpop-rt{margin-left:auto;text-align:right}
.acc-vpop-rt .k{font-size:0.66rem;font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-3)}
.acc-vpop-rt .v{font-size:0.95rem;font-weight:700;font-variant-numeric:tabular-nums}
.acc-vpop-bar{display:flex;align-items:center;gap:10px;padding:8px 16px;
  border-bottom:1px solid var(--line-2);background:var(--surface);flex-shrink:0;
  font-size:0.82rem;color:var(--ink-2)}
.acc-vpop-bar input[type=date]{font:inherit;font-size:0.85rem;padding:4px 8px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink)}
.acc-vpop-bar input[type=date]:focus{outline:2px solid var(--accent);outline-offset:-1px}
.acc-vpop-hint{margin-left:auto;color:var(--ink-3)}
.acc-vlnk{font:inherit;font-size:0.8rem;font-weight:700;cursor:pointer;background:var(--surface);
  border:1px solid var(--brand);color:var(--brand);padding:4px 11px;border-radius:6px}
.acc-vlnk:hover{background:var(--brand);color:var(--brand-ink)}
/* Long bill lists scroll HERE, inside the popup body — the popup is a fixed
   overlay so nothing upstream can clip it (the trap recorded in the
   dropdown-clipped-by-panel-overflow memory). */
.acc-vpop-body{flex:1;min-height:0;overflow-y:auto}

/* The bill list IS a real table and takes the full table standard: forest
   header, white sentence-case labels, zebra, tabular numerals. */
.acc-vatable{width:100%;border-collapse:collapse;font-size:0.86rem}
.acc-vatable thead th{background:var(--brand);color:var(--brand-ink);font-weight:600;text-align:left;
  padding:7px 10px;position:sticky;top:0;z-index:2;white-space:nowrap}
.acc-vatable thead th.r{text-align:right}
.acc-vatable td{padding:5px 10px;border-bottom:1px solid var(--line-2);vertical-align:middle}
.acc-vatable td.r{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.acc-vatable tbody tr:nth-child(even){background:var(--zebra)}
.acc-vatable tbody tr:hover{background:var(--brand-tint)}
.acc-vatable tbody tr.picked{background:var(--pos-tint)}
.acc-vatable tbody tr.picked:hover{background:#dcefe3}
.acc-vatable input[type=checkbox]{width:15px;height:15px;accent-color:var(--pos-deep);cursor:pointer}
.acc-vanone{color:var(--ink-3);text-align:center;padding:16px;font-style:italic}
.acc-vapay{font:inherit;font-size:0.85rem;width:118px;padding:4px 8px;text-align:right;
  border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink);
  font-variant-numeric:tabular-nums}
.acc-vapay:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
.acc-vapay:disabled{background:var(--head);color:var(--ink-3)}
.acc-vanew{padding:7px 10px;border-bottom:1px solid var(--line-2);background:var(--surface)}
.acc-vanew-row{display:flex;gap:9px;align-items:center;margin-bottom:5px}
.acc-vanew-row .lbl{font-size:0.72rem;font-weight:700;text-transform:uppercase;
  color:var(--ink-3);min-width:78px}
.acc-vanref{font:inherit;font-size:0.85rem;max-width:220px;flex:1;padding:4px 8px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink)}
.acc-vanref:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
.acc-vaddref{font:inherit;font-size:0.8rem;font-weight:700;cursor:pointer;background:none;
  border:none;color:var(--brand);padding:2px 0}
.acc-vaddref:hover{text-decoration:underline}
/* Per-party remarks. One voucher can pay several adthidars, so "cheque 465"
   belongs to a line, not to the voucher. */
.acc-vpop-rem{display:flex;align-items:center;gap:10px;padding:8px 16px;
  border-top:1px solid var(--line-2);background:var(--surface);flex-shrink:0}
.acc-vpop-rem label{font-size:var(--fs-lbl);font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-3);white-space:nowrap}
.acc-vpop-rem input{flex:1;min-width:0;font:inherit;font-size:0.88rem;padding:6px 9px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink)}
.acc-vpop-rem input:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
/* Total row treatment: sand ground, weight 700, 2px top rule — table standard. */
.acc-vpop-f{display:flex;align-items:center;gap:14px;padding:9px 16px;background:var(--head);
  border-top:2px solid var(--line);flex-shrink:0}
.acc-vpop-cnt{font-size:0.82rem;color:var(--ink-2)}
.acc-vpop-oa{margin-left:auto;text-align:right}
.acc-vpop-tt{text-align:right;padding-left:8px}
.acc-vpop-oa .k,.acc-vpop-tt .k{font-size:0.66rem;font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-3)}
.acc-vpop-oa .v{font-size:1.05rem;font-weight:800;font-variant-numeric:tabular-nums}
.acc-vpop-tt .v{font-size:1.18rem;font-weight:800;font-variant-numeric:tabular-nums}
.acc-vaover{color:var(--neg-deep)}

/* R6-3 — New Ref rows now live INSIDE the bill table so they share its column
   edges. Ali: "amount exactly below the amount of other bills (same alignment)
   and last remove below New Balance". */
.acc-vatable tr.newref .acc-vanew-lbl{
    font-weight:700; color:var(--ink-2, #6b6459); margin-right:8px; white-space:nowrap;
}
.acc-vatable tr.newref .acc-vanref{
    width:150px; padding:3px 7px; border:1px solid var(--line, #e3e0d8);
    border-radius:5px; font:inherit; font-size:.92em;
}
.acc-vatable tr.newref .acc-vmini.rm{ float:none; }

/* R6-5 — the origin sits beside the bare reference number. Quieter than the
   number itself: the number is what you are picking, the origin is what tells
   two identical numbers apart. Same weight/colour language as the "New Ref"
   method label above so the Reference column reads as one vocabulary. */
.acc-vaorigin{
    margin-left:8px; font-size:.8em; font-weight:600; color:var(--ink-3);
    text-transform:uppercase; letter-spacing:.03em; white-space:nowrap;
}

/* The Source column (2026-08-12). Ali: "for Tag give a column beside reference
   number so it alays use that exact space" — the tag used to sit inside the
   Reference cell, so its left edge moved with the length of the number (measured
   on his own screen: two tags one row apart started 43px apart).

   The width is not a round number, it is the widest pair the column can ever
   hold, measured rather than guessed: the longest origin label is 'Purchase
   (Acc)' at 109px, plus the offset chip at 57px and its 7px gap = 173, plus the
   cell's own 10px padding a side = 193. Rounded to 196 for a little air. If a
   LONGER origin label is ever added to ACC_ORIGIN_BY_VTYPE, re-measure and raise
   this — otherwise the column silently starts stretching again and the alignment
   this exists for is gone. The margin that separated the tag from the number is
   dropped: in its own cell it starts at the cell edge. */
.acc-vatable th.acc-vasrc, .acc-vatable td.acc-vasrc{
    width:196px; min-width:196px; white-space:nowrap;
}
.acc-vatable td.acc-vasrc .acc-vaorigin{margin-left:0}

/* R6-4 — the per-reference note, on the line under the reference name, where
   Ali drew it. It stays INSIDE the row's Reference cell: a sub-row would break
   the zebra banding and shift the positions accVSoftNB writes New balance into. */
.acc-vatable tr.newref .acc-vanewline{display:flex;align-items:center;margin-bottom:3px}
.acc-vanote{font:inherit;font-size:.8rem;width:100%;max-width:270px;padding:2px 7px;
  border:1px solid var(--line);border-radius:5px;background:var(--surface);color:var(--ink-2)}
.acc-vanote::placeholder{color:var(--ink-3);font-style:italic}
.acc-vanote:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
.acc-vanote:disabled{background:var(--head);color:var(--ink-3)}
/* Read-back of a note written on an EARLIER voucher — text, not a field: this
   reference belongs to that voucher and is not editable from here. */
.acc-vanote-ro{font-size:.8rem;color:var(--ink-3);font-style:italic;margin-top:2px;
  max-width:270px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* R6-7 — a reference sitting on the SAME side as this voucher. Ticking it
   OFFSETS it and hands the amount back to the budget, which is the opposite of
   what every other row does, so it must not look like every other row. */
.acc-vaoff{margin-left:7px;font-size:.7rem;font-weight:700;text-transform:uppercase;
  letter-spacing:.04em;padding:1px 6px;border-radius:9px;white-space:nowrap;
  color:var(--brand);background:var(--brand-tint);border:1px solid var(--line-2)}

/* ══ DAY BOOK ══════════════════════════════════════════════════════════════
   The Accounts landing screen. Standard list chrome (.pur-view-topbar /
   .pur-view-filter-bar / .pur-view-table), so only the column widths, the
   two footer rows and the "auto" mark are new.
   Spec: docs/superpowers/specs/2026-08-11-accounts-day-book-design.md */

/* FOREST-ON-FOREST: .btn-primary sits on the dark .pur-view-topbar and is
   invisible without this. Hit three times before it was written down — the
   override ships in the SAME commit as the button, never "later". */
#dbkNewVoucher { background:var(--surface); color:var(--brand); border:1px solid var(--surface);
  font-weight:700; }
#dbkNewVoucher:hover { background:#fff; color:var(--brand); }

/* THE SCREEN'S NAME, in the slot the top pager used to hold (2026-08-17). It
   reuses `.fa-head` — the statements' own title — so weight, size and colour are
   decided in one place; this only says WHERE it sits.

   AND IT IS CENTRED ON THE BAR, NOT IN THE GAP, WHICH IS THE STATEMENTS' RULE
   RATHER THAN THEIR CODE. Copying their `flex:1 1 auto; justify-content:center`
   centres the title between the two side blocks — which lands on the bar's true
   centre only when those blocks are the same width. On the P&L they nearly are
   (dates 319.1 against tools 328.6) so its title measures 4.7px off centre and
   reads as centred. Here they are 149.1 (+ New Voucher) against 285.8 (three
   action buttons), and the identical rule put the title 68.4px left of centre.
   Same code, different screen, different answer: matching the family meant
   taking it out of the flow so the uneven sides stop deciding where it lands.
   Measured after: 0.0px.

   Scoped to #dbkTopbar, never to `.pur-view-topbar` — Purchase, Stock, Jobs and
   Masters all wear that class and none of them wants a positioned middle child.
   The max-width is the no-collision guard: the two side blocks plus the row's
   gaps come to ~460px, so the title can never reach a button even as the panel
   narrows (406px of room left at his 1150 half-screen). `.fa-head b` already
   ellipsises, so a longer name truncates rather than overlapping. */
#dbkTopbar{ position:relative; }
#dbkTopbar .dbk-head{ position:absolute; left:50%; transform:translateX(-50%);
    max-width:calc(100% - 460px); justify-content:center; }

/* The 20px used to be padding on #dbkTable, which cut the table to 1066 inside
   an 1086 panel - fine while there was no box, fatal once there is one: the
   green bar ran to the border and the table stopped 20px short of it. Ali only
   ever asked for air AFTER the last column ("i didnot ask to reduce table , i
   just wanted some space after last columns"), and the figures are right-
   aligned, so that air is padding-right on the Cr cells, not padding on the
   table. Table now fills the box edge to edge; the gap after the last figure is
   unchanged at ~27px.
   The three hand-rolled 6px radii that used to live here (wrap + th:first/last-
   child) are GONE. They were standing in for the missing .pur-panel. A 6px arc
   inside an 11px clipped corner leaves a visible notch - keeping them would put
   the fault straight back. */
#accPanel-daybook #dbkTable { padding:0; }

/* The table body's own scroll cap. The shared .pur-view-table-wrap 260px is
   PURCHASE's chrome, not a constant, and it left this screen ~67px of dead
   space under the box - Ali: "bottom i can see lots of space, we just need 20 px
   all the sides."
   258 is walked off real box geometry, never `panel height - wrap height`
   (that subtraction folds chrome-above and chrome-below into one number):
       wrap.top 191.5  +  (panel.bottom - wrap.bottom) 46.2  +  panel margin 0
       +  .main-container padding-bottom 20               =  257.7
   Measured at BOTH 1528x900 and 1528x708, identical nWrap at each, which is what
   proves the calc() scales instead of being one lucky number. The panel now ends
   exactly 20px above the viewport floor at both heights.
   Most of the 67px was simply the bottom pagination strip Purchase has and this
   screen did not - adding it (#dbkPagination) spent the budget that was already
   being reserved for it. Re-measure this if the toolbar, filter row or that
   strip ever change height. */
#accPanel-daybook .pur-view-table-wrap {
  max-height:calc(100vh - 258px - var(--mstrip-h)); }

/* buildPaginationHTML returns '' at zero rows, but the STRIP is still in the
   DOM carrying .pur-view-pagination's own border-top + 8px/14px padding - a
   17px dead band with a rule across it under "No contra entries on this date."
   Contra and Journal have no entries at all, so that is the normal state of two
   of the seven tabs, not an edge case. Scoped to this id: the same :empty rule
   left unscoped would change Purchase, Proforma and Sales too. */
#dbkPagination:empty { display:none; }

/* Particulars is capped at the width of "DIDI KUMARA SWAMY ADITHI & COMMISSION
   MERCHANT" (Ali picked that name as the limit). Anything longer ends in an
   ellipsis so it is obvious the name is cropped; the full name is on hover. */
.dbk-table { table-layout:fixed; }
.dbk-table .dbk-sr   { width:44px; }
.dbk-table .dbk-no   { width:104px; }
.dbk-table .dbk-date { width:96px; }
.dbk-table .dbk-type { width:112px; }
/* Particulars takes whatever the fixed columns LEAVE, so under table-layout
   :fixed its own `width` is only a hint - it measured 394 when set to 352, and
   410 after a first attempt at rebalancing. The arithmetic has to be done from
   the other end: table 1084 - (44+104+96+112+178+198) = 352, the width of
   "DIDI KUMARA SWAMY ADITHI & COMMISSION MERCHANT", the name Ali picked as the
   limit. Anything longer crops with an ellipsis and keeps its full name in
   title=.
   1084, not the 1066 first built: the table fills its box now instead of
   stopping 20px short of the border. And 1084, not the panel's own 1086 - the
   .pur-panel border eats 1px on EACH side, measured. The 18px freed went to Cr,
   which spends it again as padding-right, so Particulars keeps EXACTLY the crop
   width Ali chose. Re-measure this line if the panel border ever changes. */
.dbk-table .dbk-part { width:auto;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Only on a row whose account is switched off — the name gives up the width,
   the tag keeps it. Baseline alignment so the name still sits on the same line
   as every untagged row above and below it. */
.dbk-table .dbk-partwrap { display:flex; align-items:baseline; min-width:0; }
.dbk-table .dbk-partwrap .nm { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.dbk-table .dbk-partwrap .acc-inact { flex:0 0 auto; }
.dbk-table .dbk-dr   { width:178px; }
.dbk-table .dbk-cr   { width:198px; }
/* The air after the last column, now INSIDE the cell - the only place a
   full-width table can still have it. 7px is the shared .pur-view-table
   padding; this makes it 20. */
.dbk-table th.dbk-cr, .dbk-table td.dbk-cr { padding-right:20px; }

/* Ali: "No , Voucher No , date and type does not sit in center( i am talking
   about table values)". Headers centred too; the money columns are the one
   exception - a centred header over right-aligned figures reads as MISALIGNED,
   so those two sit right, over their own digits. */
.dbk-table thead th { text-align:center; }
.dbk-table thead th.dbk-dr, .dbk-table thead th.dbk-cr { text-align:right; }
.dbk-table td.dbk-sr, .dbk-table td.dbk-no,
.dbk-table td.dbk-date, .dbk-table td.dbk-type { text-align:center; }
.dbk-table td.r { text-align:right; }
/* The sort arrow only exists on the sorted column, so without a reserved slot
   a centred header shifts sideways the moment it gains one. */
.dbk-table thead th .pv-sort-icon { display:inline-block; min-width:11px; }

/* The .dbk-hint line ("Dr (Out) = money out ... Cr (In) = money in") is GONE -
   Ali: "remove the page total and hint also". The words live in the column
   headers, which is where they get read; a second copy under the filter row was
   also the one thing sitting between the filter row and the table, which the
   Purchase list has nothing in. */

/* KEYBOARD CURSOR vs MOUSE HOVER - different colour families on purpose, so
   the two can never read as the same thing. M1 mustard, chosen by Ali from the
   nine-option comparison; the earlier tints were all too washed out next to
   the saturated fill his Tally uses. Dark text on it, as Tally does.
   Arrow keys drive this screen, so the cursor must beat hover on a shared row. */
.dbk-table tbody tr:hover td { background:var(--brand-tint); }
.dbk-table tbody tr.acc-cur td { background:#f2d585 !important; color:#241f00;
  box-shadow:none; }
.dbk-table tbody tr.acc-cur td:first-child { box-shadow:inset 3px 0 0 #8a6d0b; }

/* ONE footer row now, and NO colour of its own - it takes the shared
   `.pur-view-table tfoot td` beige every other list screen's TOTAL row wears.
   Ali, with the Sales list's own bottom attached as the reference: "it should
   have same color as other tables has".
   It shipped brand-tint green, which was right when there were two rows and the
   green marked the grand one apart from the page total above it. With the page
   total gone (his call) the green marked nothing and made this the only TOTAL in
   the app that looked different.
   The `.dbk-tot-page` rule that used to sit beside this went out with that row.
   Keeping the class on the <tr> even with no rule left: the export and the
   probes select on it. */
.dbk-table tfoot .dbk-part { white-space:nowrap; }

/* THE MENU BUG, measured against the Purchase Columns menu: mine came out
   10.5px UPPERCASE 700 against its 13px sentence-case 400, because .ls-filter
   styles its LABELS that way and the menu sits inside one. The table standard
   is explicit (SS3): "sentence case - Party Name, never PARTY NAME", naming
   uppercase as what broke the Sales invoice. The same wrapper's
   `height:34px !important` was also stretching each checkbox to 34px tall,
   which collapsed .pcm-row's flex - one tick per line with a gap between. */
#dbkTypeMenu .pcm-row { display:flex !important; align-items:center; gap:8px;
  padding:5px 6px; }
#dbkTypeMenu .pcm-row span { font-size:var(--fs-sm) !important; font-weight:400 !important;
  text-transform:none !important; letter-spacing:normal !important; color:var(--ink); }
#dbkTypeMenu .pcm-row input[type=checkbox] {
  height:13px !important; width:13px !important; padding:0 !important;
  margin:0; flex:0 0 auto; }
/* "all types is ver y smal" - a real width, taken from Search's excess. */
#dbkTypeBtn { min-width:150px; text-align:left; }

/* Voucher-type tabs. Same .pur-subtabs vocabulary Purchase and Sales use -
   Ali: "we have tab design in all the pages. we could use that." */
#accPanel-daybook .pur-subtabs { padding:0; margin-bottom:10px; background:transparent; }

/* Shown only when today was empty and the server fell back to the last day
   that was worked - the date boxes move with it, so this explains why. It sits
   ABOVE the box, because what it explains is the date row inside it. */
.dbk-note { padding:0 2px 7px; font-size:0.82em; color:var(--ink-2); }
/* Empty tabs are the normal case, not an edge one - Contra and Journal have no
   entries at all yet. The box must still read as a box on them, so the message
   fills the same ground the table would and keeps the bottom corners honest. */
.dbk-empty { padding:26px 14px; color:var(--ink-3); font-size:0.9em;
  background:var(--surface); border-top:1px solid var(--line); }

/* ── OPENING BALANCE EDITOR (AE51) ─────────────────────────────────────────
   Same panel chrome as the Chart of Accounts; only the table is typeable. */
.mst-ob-hint { font-size:0.78rem; color:var(--ink-3); white-space:nowrap; align-self:flex-end;
    padding-bottom:6px; }
/* The opening date IS the books beginning date (S180), so it sits in the topbar
   beside the title rather than in the filter bar — it is what the screen records,
   not something it filters by. */
/* ON THE BRAND BAR, SO IT TAKES THE BRAND INK. This first shipped with
   var(--ink-3) — the FAINT ink, which is designed against a light ground and is
   nearly invisible on .pur-view-topbar's dark green (Ali: "i cannot read the
   text, its very dull"). --brand-ink is what .pur-view-title already uses here. */
.mst-ob-date { display:inline-flex; align-items:center; gap:6px; margin-left:14px;
    font-size:0.72rem; letter-spacing:0.04em; font-weight:600;
    color:var(--brand-ink); white-space:nowrap; }
/* A small control, so a literal #fff is allowed — the ban is on AREAS. It has to
   read as a typeable box against the green, which a themed surface does not.
   THE INK MUST BE SET EXPLICITLY. This first said var(--ink-1), which is not a
   token this app has (foundation.css defines --ink, --ink-2, --ink-3). An
   undefined custom property makes the declaration invalid, so the input INHERITED
   its colour from the label above — --brand-ink, i.e. #fff — and rendered white
   text in a white box: the date was there and simply could not be seen. */
.mst-ob-date input { font-size:0.82rem; padding:3px 6px; border:1px solid var(--line);
    border-radius:4px; background:#fff; color:var(--ink); }
/* The year-end the date produces, shown before Save so the one-year rule is never
   a surprise found afterwards. Secondary text on the brand bar is the same
   softened white .alerts-topbar-note uses — dimmer than the label, still legible. */
.mst-ob-window { margin-left:10px; font-size:0.78rem; color:rgba(255,255,255,0.85);
    white-space:nowrap; }
/* The action bar sits UNDER the table, inside the panel — which is what frames
   the last row, the thing the Masters lists were missing when a row got cut. */
.mst-ob-foot { display:flex; align-items:center; gap:10px; padding:8px 12px;
    border-top:1px solid var(--line); background:var(--head); }
.mst-ob-foot .coa-suspense { border-top:0; background:transparent; padding:0; margin:0; }
/* The dirty tag is normally shown by a .mst-page ancestor; this is a panel, so
   it is driven directly from JS instead. */
.mst-ob-foot .mstp-dirty { display:none; }

.mst-ob-tbl td { padding:3px 7px; }
/* The bar marks the START of the row, so it goes on the first cell only. On
   every cell it drew a vertical line down each column boundary -- Ali: "there is
   vertical color line, i dont know where it came from." */
.mst-ob-row.cur > td { background:var(--head); }
.mst-ob-row.cur > td:first-child { box-shadow:inset 3px 0 0 var(--accent); }
/* A row he has actually changed, so a walk down 600 rows can be reviewed before
   Save without remembering which ones were touched. */
.mst-ob-row.edited > td { background:#fdf6e6; }
.mst-ob-row.cur.edited > td { background:#f7edd8; }

.mst-ob-amt, .mst-ob-side { font:inherit; font-size:0.9rem; width:100%; padding:3px 7px;
    border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); color:var(--ink); }
.mst-ob-amt { text-align:right; font-variant-numeric:tabular-nums; }
/* Dr / Cr, not DR / CR: the chart and every report write it that way. It is a
   two-item LIST, not a free box (his call) -- read-only, opened by focus, and
   the caret is hidden because nothing here is typed into directly. */
.mst-ob-side { text-align:center; width:56px; cursor:pointer; caret-color:transparent; }
/* Anchored to the body via popoutAnchor, or the table's own overflow would
   clip it exactly the way the group list was being clipped by the card. */
#mstObSideMenu { position:absolute; display:none; z-index:99999; min-width:72px;
    background:var(--surface); border:1px solid var(--line); border-radius:8px;
    box-shadow:var(--shadow-pop); overflow:hidden; }
#mstObSideMenu .opt { padding:6px 14px; font-size:0.9rem; cursor:pointer; }
#mstObSideMenu .opt.on { background:var(--accent); color:#fff; }
#mstObSideMenu .opt.hide { display:none; }
.mst-ob-amt:focus, .mst-ob-side:focus { outline:2px solid var(--accent); outline-offset:-1px;
    border-color:var(--accent); }
/* The table itself takes the keys while the highlight is on a row, so it needs a
   focus ring of its own or there is no telling it is listening. */
#mstObTable:focus { outline:2px solid var(--accent); outline-offset:-2px; }

/* ══ THE LEDGER ═══════════════════════════════════════════════════════════════
   Spec: docs/superpowers/specs/2026-08-15-accounts-ledger-design.md
   ONE header line is the whole chrome. Ali: "amount of space we are wasting at
   top, i can barely read 10 line which can be few days data in ledger." 217px
   above the first row became 79 -- the header line and the column header.
   Everything below borrows the shared list-screen classes (.pur-panel,
   .pur-view-filter-bar, .pur-view-table, .pur-view-table-wrap); the rules here
   are only what this screen adds or overrides. */

/* No page heading -- a list screen carries none, same as the Day Book and the
   Purchase list it is modelled on. */
#accounts.acc-ledger-open > .section-title,
#accounts.acc-list-open > .section-title{ display:none }

/* HEIGHT. The app has NO fixed height above .main-container -- .app-shell is
   grid-template-rows:1fr with align-items:stretch -- so a flex:1 chain has
   nothing to divide and the PAGE would scroll instead of the rows. Use the
   app's own mechanism: cap the wrap at the viewport minus this screen's chrome.
   N is walked off real box geometry, never `panel height - wrap height`
   (which conflates chrome above with chrome below). Measured at 1528x698. */
#accounts.acc-ledger-open #accShell,
#accounts.acc-list-open #accShell{ align-items:stretch; }
/* S131 — the #accTree halves of this rule are gone (see the note where the base
   #accTree rule is defined); #accSide owns the column's height now and the tree
   takes flex:1 inside it. #ledSide keeps its cap: it is a fixed list of buttons
   that must never itself scroll — if it ever outgrows the column, the tree below
   it is what gives way, not the controls. */
#accounts.acc-ledger-open #ledSide{ max-height:calc(100vh - 40px - var(--mstrip-h)); }
.led-panel{ display:flex; flex-direction:column; }
/* min-height:0 on every flex ancestor, or a long table refuses to let its own
   scroller shrink and pushes the panel past the frame. */
.led-panel, .led-panel > *{ min-height:0; }
/* HEIGHT, not max-height. Every other list screen caps its wrap and lets a short
   list leave the box short -- Purchase with 3 rows ends 269px off the floor. A
   LEDGER cannot do that: its totals are the bottom line of a statement, and
   Tally pins Current Total / Closing Balance to the bottom of the SCREEN with
   the empty space above them. A capped wrap would park them right under the
   last row with 400px of nothing underneath, which is what Ali means by "no
   more extra space at bottom".
   N = 119.6, walked off real geometry at 1528x698 and identical at 900:
     98.6 above the wrap (20 frame + 78.6 header line)
   +  1.0 the panel's own bottom border
   + 20.0 frame below the panel
   Never `panel height - wrap height`, which conflates the two ends. */
/* max-height:none is REQUIRED, not tidiness: .pur-view-table-wrap carries
   `max-height: calc(100vh - 260px)` -- PURCHASE's chrome, not a constant -- and
   a max-height beats a larger height. Measured: the wrap rendered 438px (698 -
   260) while its own `height` said 578, and the panel stopped 140px off the
   floor with the totals stranded in the middle of the screen. */
/* The sand ground goes on the WRAP, not just on the totals. The wrap was
   transparent, so any sub-pixel the table did not cover showed the panel's own
   --surface white through the 11px corner radius — Ali: "some times on ledger,
   i see the white corner." Painting the scroller the same colour as the row
   that ends it means there is nothing white left to leak, whatever the
   fractional arithmetic does at 125% scaling.
   `line-height:0` is what closes the TOP gap: a table is inline-level, so the
   wrap reserved a text line's worth of leading above it and 2-3px of that
   ground showed as a pale sliver across the top of the header — measured at
   wrap top 98.64 against header top 101.64. It reads as a light corner where
   the panel's own radius curves through it, which is where Ali saw it: "that
   white corner is on top left side". The table restores a normal line-height
   for its own cells. */
.led-wrap{ height:calc(100vh - 120px - var(--mstrip-h)); max-height:none;
    border-bottom:0; background:var(--head); line-height:0; }
.led-wrap > table{ line-height:var(--lh); }
/* ...and the rows keep the surface they are supposed to have, since the wrap is
   no longer white behind them. */
.led-table tbody{ background:var(--surface); }
/* NO focus ring on the wrap. It is only focusable so the arrow keys have an
   owner, and an accent outline around the whole table read as an error state --
   Ali: "table when we have mustard highlight has a orange border full table we
   dont need that." The mustard cursor row IS the visible focus: it says where
   the keys will act, which is the only thing a ring here could have said. */
.led-wrap:focus, .led-wrap:focus-visible{ outline:none; }

/* ── the one header line ── */
.led-bar{ align-items:flex-end; gap:10px; padding:10px 14px; flex-wrap:nowrap; }
/* At the app's real INPUT size, not the compressed filter-row size. Ali: "need
   to increase size of that row as its main row and now it s little small." */
.led-bar .ls-filter input, .led-bar .ls-search input{ height:34px; font-size:0.9em;
    padding:5px 9px; }
.led-bar .ls-filter label, .led-bar .ls-search label{ font-size:0.72em;
    margin-bottom:4px; letter-spacing:.02em; }
.led-bar .ls-filter input[type=date]{ width:132px; }
/* The picker takes every pixel the row does not need -- it is the field holding
   51-character party names; the dates and search are not. */
.led-pick{ flex:1; min-width:0; position:relative; }
.led-pick #ledPickMount input{ height:34px !important; font-size:0.9em !important;
    font-weight:700; }
/* The account's kind INSIDE the box, right-aligned. Ali: "you are showing its
   sub ledger or group outside, if we want show lets show inside only why spend
   space outside." The input's padding-right is set in JS from this chip's
   measured width, so a long name never runs under it. */
.led-kind{ position:absolute; right:9px; bottom:8px; pointer-events:none;
    background:var(--brand-tint); color:var(--brand); border-radius:99px;
    padding:2px 9px; font-size:0.68rem; font-weight:700; white-space:nowrap; }
.led-kind.inact{ background:var(--neg-tint); color:var(--neg-deep); }
/* 184px, down 30% from 262. Ali: "ledger and search gets similar width, its not
   correct, ledger should get more width and search should reduced by 30% and
   that space give to ledger." The picker is flex:1, so every pixel taken off
   search lands there without either number being restated. */
/* flex:none is REQUIRED, not tidiness: `.pur-view-filter-bar .ls-search` is
   `flex:1 0 0%`, so a bare width was ignored and search grew to match the
   picker exactly (measured 381 = 381). */
.led-search{ width:184px; flex:none; position:relative; }
.led-search input{ width:100%; padding-right:28px !important; }
/* Clear sits INSIDE the box -- no Clear Filters button taking a slot of its own.
   Ali: "we can give clear filter inside search, one single X to clear search or
   similar to save space." */
.led-clr{ position:absolute; right:5px; bottom:6px; width:22px; height:22px;
    border:0; background:none; border-radius:5px; cursor:pointer; display:none;
    place-items:center; color:var(--ink-3); font-size:0.8rem; line-height:1; }
.led-clr.on{ display:grid; }
.led-clr:hover{ background:var(--line); color:var(--ink); }
/* Rides in the SEARCH label's row, right-aligned. `float` rather than a flex
   label, because .pur-view-filter-bar .ls-search label is `display:block` and
   changing that would reach every other screen using the shared bar. */
.led-count{ float:right; font-size:0.7rem; color:var(--ink-3); font-weight:600;
    text-transform:none; letter-spacing:0; white-space:nowrap; }
.led-count b{ color:var(--accent-2); }
.led-pdc{ color:var(--warn-deep); }

/* ── the table ── */
.led-table td, .led-table th{ vertical-align:top; }
.led-narr{ display:block; font-size:0.9em; color:var(--ink-3); white-space:nowrap;
    overflow:hidden; text-overflow:ellipsis; }
.led-cur td .led-narr{ color:#5a4d17; }
.led-side{ font-size:0.85em; font-weight:800; margin-left:3px; }
.led-side.dr{ color:var(--pos-deep); } .led-side.cr{ color:var(--neg-deep); }
.led-word{ font-weight:600; color:var(--ink-2); font-size:0.92em; }
.led-mth{ font-weight:700; }
.led-nil td{ color:var(--ink-3); }
/* Answers Enter on an empty month, on the row the cursor is already sitting on.
   Cleared by the next move, so there is nothing to dismiss. */
.led-none{ margin-left:10px; font-size:0.85em; font-weight:600; color:var(--ink-3);
    font-style:italic; }
.led-table tbody tr.acc-cur .led-none{ color:#5a4d17; }
.led-empty{ padding:22px 14px; color:var(--ink-3); font-size:0.85rem; }
.led-empty.err{ color:var(--neg-deep); }
/* OPENING BALANCE lives in the THEAD, so it stays on screen like Tally's does --
   as a tbody row it scrolls away. Its `top` is set in JS from the column
   header's MEASURED height; a hardcoded px goes stale the moment the font or
   the Text Size setting moves. */
/* AN OPAQUE BACKGROUND ON THE HEAD ITSELF, so nothing can ever be seen between
   its cells. Ali: "when all is scroll down, look at top rows, date-particular and
   op bal row is bleeding, there is a gap and it display from back side."
   NOT REPRODUCED IN THE DRIVER — probed at five scroll offsets at his own 1.25
   scaling and the seam measured 0.0000 every time, so this is a defensive fix
   against the only mechanism that fits the evidence, not a confirmed one. The
   head is two rows of cells under `border-collapse`, where the merged border is
   rasterised in its own pass; at a fractional device pixel (32.75 CSS px is
   40.9375 device px on his screen) a hairline can fall between the two rows, and
   the thead had NO background of its own, so what showed through was the tbody
   scrolling behind. Painting the thead means any such hairline shows sand
   instead of a moving row. It sits below both rows' cells (z-index 5 against
   their 9 and 10), so nothing that currently paints is changed.
   "It is not always" fits: it depends where the panel lands on the pixel grid. */
.led-table thead{ background:var(--head); }
.led-table thead tr.led-op td{ background:var(--surface); padding:6px 7px;
    font-weight:700; color:var(--ink-2); white-space:nowrap; position:sticky;
    z-index:9; border-bottom:1px solid var(--line); }
.led-table thead tr.led-op td.r{ text-align:right;
    font-variant-numeric:tabular-nums; }
/* Two footer rows, as Tally has them: the period's movement, and the standing
   balance. Different figures, both wanted -- unlike the Day Book, where the two
   were duplicates on his data and one was removed. */
/* THE TOTALS ARE THE BOTTOM LINE OF A STATEMENT, so they read a step up from
   the entries rather than the same size in bold. Ali: "footer total look little
   small need to increase font size a little", and "make sure bold does not make
   it cramped, it happend in other totals."
   The cramping is real and it is not the weight itself: at 12.3px the digits are
   small enough that Inter's bold sidebearings close the gaps between figures. So
   give the row its own size AND a little positive letter-spacing, which is what
   buys the air back. Padding up too, or a taller line sits in the same box.
   MEASURED, because `em` here compounds: .pur-view-table is 0.82em, so an
   0.95em foot resolved to 11.7px -- SMALLER than the 12.3px body it was meant
   to sit above. `rem` is absolute against the root, so 0.9rem = 13.5px and says
   what it means.

   S129 — PADDING AND ALIGNMENT MATCHED TO THE OTHER FOUR, FONT DELIBERATELY NOT.
   Ali asked for all four ("match these 2 to others, in all height padding font
   text sits"), then took the font back when it was measured: 0.98rem here pushed
   the Debit box to 164px inside a 156px cell and every figure wrapped again — the
   hundred-crore fault from earlier this session, reopened by a font change. His
   call: "ok then leave font same ... its not big difference."
   So 0.9rem stays and the foot lands ~2px under the statements' 46.3. Worth
   knowing WHY 0.9rem is now odd rather than principled: it was chosen to sit a
   step ABOVE a 12.3px body, and this session raised that body to 14.7, so the
   foot is now slightly smaller than the rows it totals — the exact inversion the
   paragraph above was written to prevent, arrived at from the other direction. A
   number justified against another number goes stale when that other one moves.
   The honest fix is wider money columns, which costs the Particulars column; he
   has seen the trade and chosen the smaller font.
   vertical-align:middle is the third thing he spotted ("total - we owe is not
   center aligned"): without it these cells inherit `vertical-align:top` from
   `.led-table td, .led-table th`, a rule meant for BODY rows, where a wrapped
   name and its narration must start on one line. The same inheritance is what
   put the column header at the top of its cell. The boxed figures make the row
   taller than the label's own line, so top-alignment left "Total — we owe"
   riding above the figures it belongs to.
   The other four screens override all of this lower down and are untouched. */
.led-table tfoot td{ font-size:0.9rem; padding:7px; vertical-align:middle;
    letter-spacing:0.01em; }
.led-table tfoot td.r{ letter-spacing:0.015em; }

/* THE FOOTER FIGURES SIT IN A FIELD, the P&L's own treatment and the same values.
   Ali, looking at his three footers side by side: "ledger and group does not have
   feild like P&L at footer" — and then what it is FOR: "for me box was just
   footer showing clear figures and does not get lost in footer." So it is a
   legibility fix, which is why EVERY figure gets one rather than only the closing
   balance: a box on one of three leaves the other two as lost as they were.
   NO min-width, and that is inherited experience rather than taste — the P&L's
   note records that equal-sized boxes pushed the table past its wrap and produced
   a horizontal scrollbar, which then ate the padding keeping the box off the
   edge. The box hugs its number and can never outgrow its column.
   Scoped to the two panels, so Outstanding and the Trial Balance keep the boxes
   they already produce on their own first row and nothing else in the app that
   wears .led-table is reached. */
/* white-space:nowrap is the load-bearing part, not the padding. Ali posted a
   100-crore receipt — "we have never received more than 1 cr value receipt, but
   its business and it can have 100 carore dr or cr" — and the Balance box broke
   in half: at 152px of usable cell the figure fitted but its Dr tag did not, so
   the tag wrapped and the box grew to 49px tall inside a 48px foot. The BODY row
   handled the same figure fine, which is the tell — a body cell spends 14px on
   padding where a boxed one spends 38. The box now refuses to wrap and the
   columns below were widened to match; padding trimmed 11 -> 8 to buy 6px of
   that back without taking it from the Particulars column. */
.led-1r .led-table tfoot td.r > span,
.gs-1r .led-table tfoot td.r > span{
    display:inline-block; padding:4px 8px; white-space:nowrap;
    background:var(--surface); border:1px solid var(--line);
    border-radius:6px; text-align:right; }

/* TRYING IT: FAINT COLUMN RULES, LEDGER ONLY. Ali: "just wanted to check what if
   we give vertical lines, a very light faint vertical line, so we can have box
   type view, can we just apply to ledger and check?" — an experiment on one
   screen, so it is scoped to .led-1r and comes out in one deletion.
   --line-2 (#efeade), not --line (#e3ddd0): the faint token, and on the body's
   white and zebra rows it reads as a rule rather than a border. The column header
   takes the STRONGER --line because --line-2 is within a shade of the sand band
   itself (#efeadf) and would be invisible there — a grid that stops at the header
   looks like a mistake rather than a choice.
   NOT on the foot. Its figures now sit in their own boxes, and column rules
   through a row of boxes is two grids arguing. Not on the Opening Balance band
   either: it spans, so a rule would land mid-sentence.

   ROLLED OUT ACROSS ACCOUNTS, his call once he had seen it on the Ledger: "we
   wil apply to account part now, we will leave TB as also its pairs dont have PL
   and BS so." The Trial Balance is excluded BY FAMILY, not by measurement — it
   is read against the P&L and the Balance Sheet, which he ruled out ("i dont
   need it in P&L and BS, they are good"), and one ruled sheet among three plain
   ones is the split he objects to. It measures against the rules anyway: three
   columns, one of them a 664px Particulars, so there is nothing to scan across.
   The hooks below ARE the opt-in list — adding a screen means adding its
   hook here, never copying the block.

   THE DAY BOOK WAS ADDED AND THEN TAKEN BACK OFF, 2026-08-17, and the reason is
   worth keeping because THE MEASUREMENT DID NOT SETTLE IT. He asked ("should we
   apply vertical lines to day book list?") and S129's survey had left that row of
   its table blank. Measured, the screen looks like a strong candidate on SHAPE —
   7 columns, six short tokens against one 28-char Particulars, uniform rows, no
   trailing button column — and it was recommended on that basis.
   That was the wrong basis. SHAPE IS WHAT THIS SCREEN HAS IN COMMON WITH THE
   LEDGER, so it could never tell the two apart. A second pass looking for variety
   found them level too: the Ledger's own Date column runs 25 identical values and
   its Vch Type 20, against the Day Book's 14 and 14. The only real asymmetry is
   small — on a single-type tab the Day Book's other money column is 100% blank
   (25 of 25), so the table's right edge becomes a column of empty boxes, where
   the Ledger always ends on a Balance column that is never blank.
   He compared it against a screenshot of the old and preferred the old. With the
   numbers level the tie goes to the screen that was already working: the Ledger
   ASKED for this ("can we just apply to ledger and check?"), the Day Book did
   not, and it is the screen he lands on rather than one he goes to and reads.
   IF IT IS EVER REVISITED, two things from the exercise are worth reusing. The
   middle option is Outstanding's — rule the MONEY columns only and leave
   No./Voucher No/Date/Type plain. And the header would need
   rgba(255,255,255,.08), NEVER --line: this header is FOREST, where --line
   measures 154 away against the 13.3 it sits at on every sand header — twelve
   times the contrast, a header grid shouting over the faint body one. */
.led-1r .led-table tbody td + td,
.gs-1r .led-table tbody td + td{ border-left:1px solid var(--line-2); }
.led-1r .led-table thead th + th,
.gs-1r .led-table thead th + th{ border-left:1px solid var(--line); }

/* OUTSTANDING RULES ONLY ITS MONEY COLUMNS, and that is forced by its rows, not
   chosen. Its GROUP rows carry three cells (`colspan=3` over Particulars, Type
   and Code, then Dr and Cr) while its party rows carry five — so `td + td` draws
   four rules on a party row and two on a group row, and in Group wise, where
   EVERY row is a group row, the header's Type and Code rules hang over a body
   that has none. Measured: 3 cells spanning 3,1,1 against 5 cells spanning
   1,1,1,1,1.
   The Dr and Cr boundaries are the only two that exist in every row of both
   arrangements, and they land on the same x in both — so these are continuous
   whichever way the screen is switched, which matters because he switches it
   from the right-hand column. The alternative is to rule all four and accept
   dashed verticals wherever a group sits; that is his call, not a fix. */
.os-panel .led-table tbody td.r{ border-left:1px solid var(--line-2); }
.os-panel .led-table thead th.r{ border-left:1px solid var(--line); }
/* EXCEPT IN LEDGER WISE, WHERE ALL FIVE CELLS EXIST ON EVERY ROW — so this view
   takes the full grid. Ali, looking at exactly that view: "trying to understand
   - we cannot give border here in outstanding?" We can; the restriction above is
   the GROUPED view's, and applying it to both was over-cautious. Type and Code
   are ruled here and not there because here they are real cells on every row and
   there they are the middle of a spanning group name. */
.os-panel .os-flat tbody td + td{ border-left:1px solid var(--line-2); }
.os-panel .os-flat thead th + th{ border-left:1px solid var(--line); }

/* RED ONLY WHERE RED MEANS SOMETHING — a bank overdrawn or cash short, his own
   ruling ("when bank is overdrawan or cash is short red, elde only dr and cr get
   color"). Every other balance keeps plain ink with the colour on its small Dr/Cr
   tag, which is where a DIRECTION belongs: on a ledger Dr and Cr are not good and
   bad, and painting each Cr red would have fired on most adthidars most of the
   time while quietly giving red a second meaning next to the P&L's red minus.
   Text only, no tint and no red border: the box is doing legibility, this is
   doing meaning, and stacking both makes an ordinary overdraft look like an
   error dialog. The condition lives in ledAlarmBal, beside the words it must
   agree with. */
.led-1r .led-table tfoot td.r > span.led-alarm{ color:var(--neg-deep); }

/* NO rule between the two total rows. `.pur-view-table tfoot td` carries
   `border-top:2px solid var(--line)` -- right where the foot meets the DATA, and
   wrong between two sand rows, where it draws a pale band that reads as a gap in
   the fill. Ali: "in current total and closing balance row, they both have a
   white line between them, purchase list page also has same problem."
   Measured on the Purchase list: rows flush at 572.9 + 31.8 = 604.7, both cells
   rgb(239,234,223) -- so the line IS the border, not a gap. Killing it on the
   SECOND row only keeps the strong rule under the last entry, which is what
   separates the totals from the ledger.

   S129 — THIS RULE NOW MATCHES NOTHING, and that is recorded rather than fixed
   by deleting it. Every screen wearing .led-table went to a one-row foot: the
   Trial Balance and Outstanding first ("footer grand total use 2 rows not
   needed ... we can get extra 1 row"), then the Ledger and Group Summary. Kept
   because it is still the CORRECT treatment the day a second foot row comes
   back, and because deleting it would lose the measurement above — but do not
   read it as evidence that anything currently has two. */
.led-table tfoot tr + tr td{ border-top:0; }
/* AND the row's own border-bottom, which is the line he could still see after
   the border-top was killed. `.pur-view-table tbody tr` sets
   `border-bottom:1px solid var(--line-2)` on the TR, and under
   border-collapse the foot's rows inherit the same treatment -- measured 1px on
   every cell of the FIRST total row, which is exactly where the pale line sat.
   Killing top and bottom is what actually clears it; killing only the top left
   the other half of the same seam. */
.led-table tfoot tr, .led-table tfoot td{ border-bottom:0; }
/* THE CURSOR MUST BEAT THE ZEBRA. The shared `.acc-cur` paints the TR, but
   `.pur-view-table tbody tr.pv-alt td` paints the TD -- and a cell's own
   background sits in front of its row's. So the mustard showed on odd rows and
   VANISHED on every second one. Ali: "every 2 second line musturd color
   highlight does not show in table." Paint the TD, exactly as the Day Book's
   own `.dbk-table tbody tr.acc-cur td` does, and carry the dark text and the
   left bar with it. */
.led-table tbody tr.acc-cur td{ background:#f2d585 !important; color:#241f00;
    box-shadow:none; }
.led-table tbody tr.acc-cur td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }
.led-table tbody tr.acc-cur td .led-narr{ color:#5a4d17; }
.led-table tbody tr.acc-cur td .led-side{ color:#241f00; }

/* The totals belong at the BOTTOM OF THE SCREEN, not under the last row with
   400px of nothing below them -- that is what Ali means by "no more extra space
   at bottom", and it is how Tally prints Current Total / Closing Balance.
   position:sticky alone cannot do it: a table shorter than its scroller has
   nothing to stick to, so the foot just sits where the rows end.
   So the table always fills the scroller, and one empty FILLER ROW at the end
   of the tbody swallows the slack. It is a real <tr> in the real table, so the
   colgroup still drives every column and nothing about the sticky bands, the
   zebra or the cursor changes. The filler is not [data-nav], so the keyboard
   walks straight past it. */
.led-table tr.led-fill td{ border:0; padding:0; }
/* THE HOVER TINT LIVES ON THE TR, not the td. Clearing only the cell left the
   row's own .pur-view-table tbody tr:hover green showing straight through the
   transparent cell — the tint Ali saw over dead space below the last line
   ("if empty screen just show white and no hover tint needed"). Out-ranked with
   a third class rather than !important. */
.led-table tr.led-fill:hover td{ background:transparent; }
.led-table tbody tr.led-fill:hover{ background:transparent; }

/* ── the screen's own right column (Tally's button bar) ── */
#ledSide{ width:172px; flex-shrink:0; background:var(--surface);
    border:1px solid var(--line); border-radius:var(--radius); padding:10px 7px;
    position:sticky; top:12px; overflow-y:auto; }
#ledSide .led-sec{ cursor:default; padding:8px 5px 5px; }
#ledSide .led-sec:first-child{ padding-top:2px; }
#ledSide .acc-tleaf[disabled]{ opacity:.45; cursor:not-allowed; }
#ledSide .acc-tleaf[disabled]:hover{ background:var(--surface); color:var(--ink-2);
    border-color:var(--line); }
.led-side-note{ margin-top:10px; padding:9px 6px 2px; border-top:1px dashed var(--line);
    font-size:0.68rem; color:var(--ink-3); line-height:1.45; }
/* ~330px tall inside a 172px column that is overflow:auto, so it is anchored
   to the BODY by popoutAnchor (which sets position/top/left inline). Without
   it the menu opened, sat inside the viewport, and was clipped by its own
   container - same fault the party dropdowns had; same fix. */
#ledContactMenu{ right:6px; top:auto; }
.led-contact{ min-width:230px; max-width:300px; padding:6px 0 8px; }
.led-contact .lc-row{ display:flex; gap:10px; padding:5px 12px; font-size:0.8rem;
    align-items:baseline; }
.led-contact .lc-l{ color:var(--ink-3); font-weight:700; font-size:0.72rem;
    text-transform:uppercase; letter-spacing:.03em; width:74px; flex-shrink:0; }
.led-contact .lc-v{ color:var(--ink); flex:1; min-width:0; word-break:break-word; }
@media (max-width:1100px){ #ledSide{ width:150px } }

/* ══ OUTSTANDING + GROUP SUMMARY (S124) ═══════════════════════════════════════
   Both wear the Ledger's chrome — .led-panel / .led-bar / .led-wrap / .led-table
   are reused as-is, deliberately, so the three screens cannot drift apart. What
   follows is only what these two ADD. */

/* The view switch sits in the header line, so it has to stand exactly as tall as
   the inputs beside it or the row's baseline breaks. 34px is .led-bar's own input
   height, not a guess. */
.os-seg{ display:flex; gap:0; height:34px; }
/* EQUAL WIDTHS. Ali: "at present they are different width, give them same
   width" — with padding-only sizing, "All" came out a third of "Cr (we owe)"
   and the three read as unrelated controls rather than one switch.
   `flex:1 1 0` divides the track evenly regardless of the text inside, and the
   min-width sets the track from the widest label rather than the narrowest. */
.os-vw{ flex:1 1 0; min-width:132px; height:34px; padding:0 12px;
    display:inline-flex; align-items:center; justify-content:center; gap:5px;
    font-size:0.82em; font-weight:700;
    background:var(--surface); color:var(--ink-2); border:1px solid var(--line);
    border-right-width:0; cursor:pointer; white-space:nowrap; }
/* The plain-word hint rides behind the Dr/Cr, lighter and not italic-shouting —
   the two letters are the label, the words are the reminder. */
.os-vw i{ font-style:normal; font-weight:600; opacity:.72; font-size:0.94em; }
.os-vw.active i{ opacity:.85; }
.os-table thead th i{ font-style:normal; font-weight:600; opacity:.75;
    font-size:0.92em; }
.os-vw:first-child{ border-radius:var(--radius-sm) 0 0 var(--radius-sm); }
.os-vw:last-child{ border-radius:0 var(--radius-sm) var(--radius-sm) 0;
    border-right-width:1px; }
.os-vw:hover{ background:var(--brand-tint); color:var(--ink); }
/* NOT a solid fill. Ali: "right now all is selected and shows green which look
   bad" — a filled block that size reads as the loudest thing on a screen whose
   subject is the figures, and it fought the header band directly above it. The
   app's own quieter idiom for "this one is chosen" is the sidebar's .nav-sub:
   a tint with the brand colour carrying the weight. Same here, plus a heavier
   border so the choice is still unmistakable at a glance. */
.os-vw.active{ background:var(--brand-tint); color:var(--brand);
    border-color:var(--brand); box-shadow:inset 0 -2px 0 var(--brand); }
.os-vw.active + .os-vw{ border-left-color:var(--brand); }
.os-vw.active i{ color:var(--brand); }
/* The lone Expand/Collapse button is not part of a segment, so it carries its
   own rounded ends and does not stretch to a shared track. */
.os-lone{ flex:none; min-width:0; border-right-width:1px;
    border-radius:var(--radius-sm); }
/* Takes the row's spare width — Ali: "hald screen ( header part ) is empty."
   Nothing in this bar was flexible, so every control packed against the left and
   the right half sat unused. Search is the field that earns it: it matches party
   names up to 51 characters. Group Summary keeps its own fixed search, because
   there the PICKER is the flexible field (same slot the Ledger uses). */
.os-search{ flex:1; min-width:200px; }
#accPanel-grpsum .os-search{ flex:none; width:230px; }

/* ── Outstanding ── */
/* The group line is a HEADING with figures on it, so it reads a step heavier
   than the parties under it and carries the section's own tint. It IS navigable
   now — ArrowRight/Enter/double-click opens it, per Ali's "when we open it show
   ledgers and enter we open ledgers" — so the cursor stopping there does
   something, which is the condition his "showing but does not move" rule sets. */
.os-table tbody tr.os-grp td{ background:var(--brand-tint); font-weight:800;
    color:var(--ink); border-bottom:1px solid var(--line); padding-top:7px;
    padding-bottom:7px; }
/* The cursor still has to win over the group's own tint — same fight the zebra
   put up on the Ledger, same answer: paint the TD. */
.os-table tbody tr.os-grp.acc-cur td{ background:#f2d585; color:#241f00; }
.os-gcnt{ margin-left:8px; font-weight:700; font-size:0.76em; color:var(--ink-3);
    background:var(--surface); border-radius:99px; padding:1px 7px; }
.os-table tbody tr.acc-cur td .os-gcnt{ color:#5a4d17; }
.os-table tbody tr.os-grp td.r{ font-variant-numeric:tabular-nums; }
.os-kind, .os-code{ color:var(--ink-3); font-size:0.94em; }
/* The net line spans both amount columns — it is ONE figure with a side, not a
   debit and a credit, and printing it under either column would say the wrong
   thing about which side it falls on. */
.os-table tfoot td[colspan="2"]{ text-align:right; }

/* ── Group Summary ── */
/* Indent is inline (padding-left, computed from depth) because the tree is
   arbitrarily deep — GST sits four levels down. The caret is a fixed-width slot
   so names stay in one column whether or not a row has children. */
.gs-car{ display:inline-block; width:14px; color:var(--ink-3); font-size:0.9em; }
.gs-table tbody tr.gs-grp td{ font-weight:800; color:var(--ink); }
.gs-table tbody tr.gs-grp td.r{ font-variant-numeric:tabular-nums; }
/* How many rows a sub-group is about to add, so you know before you open it —
   the Chart of Accounts shows the same count for the same reason. */
.gs-count{ margin-left:9px; font-weight:700; font-size:0.76em; color:var(--ink-3);
    background:var(--brand-tint); border-radius:99px; padding:1px 7px; }
/* Money sitting on a GROUP with no member row behind it. On a party control
   head that means a posting made with no party attached — worth seeing, since
   otherwise the group's total exceeds the rows under it with no explanation. */
.gs-own{ margin-left:9px; font-weight:600; font-size:0.78em; color:var(--ink-3);
    font-style:italic; }
.gs-table tbody tr.acc-cur td .gs-own,
.gs-table tbody tr.acc-cur td .gs-car{ color:#5a4d17; }
.gs-table tfoot td .acc-badge{ font-size:0.78rem; }

/* ── Trial Balance (S124) ────────────────────────────────────────────────────
   Same bar as every other list screen — a forest one was mocked up and turned
   down ("did not like 2 rows with forst color"). The only thing this screen
   adds is its TITLE, centred in the row and vertically across BOTH of its lines
   (label row + control row). Ali: "trial balance will be in center from top
   also, wll use both row place." flex:1 on the title is what centres it against
   the real remaining width rather than against the whole bar. */
/* ONE width for both sides of the title. Ali: "if left 2 and right 2 total width
   are same then it will look asthically good" — From+To on the left, Expand+
   Search on the right, each pair filling the same track, which is also what
   makes the centred title actually land in the middle. */
.tb-bar{ position:relative; justify-content:space-between; --tb-side-w:322px; }
.tb-side{ width:var(--tb-side-w); flex-shrink:0; display:flex; gap:10px;
    align-items:flex-end; }
.tb-side > *{ flex:1; min-width:0; }
.tb-side input{ width:100%; }
/* The date input has a UA-drawn calendar button and its own intrinsic width, so
   it ignores the flex track unless min-width is released. */
.led-bar .tb-side .ls-filter input[type=date]{ width:100%; min-width:0; }
/* The Expand/Collapse control matches an input's height and weight — it is a
   control in the same row, not a link. */
/* inline-flex, not a plain button: it carries the app's expand icon beside its
   word now, and a text-flow button would sit the two on different baselines. */
.tb-exp{ height:34px; width:100%; font:inherit; font-size:0.82em; font-weight:700;
    background:var(--surface); color:var(--ink-2); border:1px solid var(--line);
    border-radius:var(--radius-sm); cursor:pointer; white-space:nowrap;
    display:inline-flex; align-items:center; justify-content:center; gap:6px; }
.tb-exp:hover{ background:var(--brand-tint); color:var(--ink); }
.tb-exp:disabled{ opacity:.45; cursor:not-allowed; }
.tb-exp:disabled:hover{ background:var(--surface); color:var(--ink-2); }
.tb-expwrap{ flex:0 0 116px; }
/* ABSOLUTELY centred, not centred in the leftovers — flex centring put it 12px
   off the bar's real middle, and a report title that is NEARLY centred reads as
   a mistake rather than a choice. */
.tb-title{ position:absolute; left:50%; top:0; bottom:0; transform:translateX(-50%);
    display:flex; align-items:center; text-align:center; pointer-events:none; }
.tb-title b{ font-size:1.32rem; font-weight:800; letter-spacing:-.01em;
    color:var(--ink); line-height:1.1; white-space:nowrap; }
/* S131 — the four screens that got a column of their own share this block with
   the Trial Balance rather than copying it four more times. Written as one
   selector list on purpose: the whole point of the spec is that every Accounts
   screen's column looks identical, and four separate rules are four places for
   them to drift. #ledSide keeps its own copy above only because it carries extra
   state (the disabled Bill by bill, the contact popover anchor).
   `position:sticky` is dropped here — inside #accSide these are flex items with
   a stretched parent, and sticky on a flex item that is already pinned by its
   container does nothing but confuse the next reader. */
#tbSide, #faSide, #gsSide, #dbkSide, #vchSide, #ieSide{ width:172px; flex-shrink:0;
    background:var(--surface); border:1px solid var(--line);
    border-radius:var(--radius); padding:10px 7px; overflow-y:auto; }
#tbSide .led-sec, #faSide .led-sec, #gsSide .led-sec{ cursor:default; padding:8px 5px 5px; }
#tbSide .led-sec:first-child, #faSide .led-sec:first-child,
#gsSide .led-sec:first-child{ padding-top:2px; }
#tbSide .acc-tleaf[disabled], #faSide .acc-tleaf[disabled]{ opacity:.45; cursor:not-allowed; }
#tbSide .acc-tleaf[disabled]:hover, #faSide .acc-tleaf[disabled]:hover{
    background:var(--surface); color:var(--ink-2); border-color:var(--line); }
/* The chosen view, in the app's quiet way — the same treatment Outstanding's
   view switch ended up with after the solid fill was rejected. */
/* S131 — REPOINTED FROM --brand TO --accent. This was the ONE selected state in
   Accounts pinned to the forest brand, which made it the only one that ignored
   Settings > Accent colour. It read as "the nice green one" next to four rust
   siblings, but the difference was a bug, not a style: change the accent and
   every other selection in the app moved while this one did not. */
#tbSide .tb-vw.active{ background:var(--accent-tint); color:var(--accent-2);
    border-color:var(--accent); font-weight:800; box-shadow:inset 0 -2px 0 var(--accent); }
/* The badge sits INSIDE the Grand Total cell, so it must not inherit the foot's
   size and weight — it is a note on the figure, not part of it. */
/* ══ PROFIT & LOSS + BALANCE SHEET (S125) ═══════════════════════════════════
   Both wear the Ledger's chrome — .led-panel / .led-bar / .led-wrap /
   .led-table — and the Trial Balance's header layout (.tb-bar / .tb-side /
   .tb-title). What follows is only what these two ADD. */

/* ══ ONE ROW OF CHROME (S126) ═══════════════════════════════════════════════
   Ali drove the two-row version and called the filter band out: "one part which
   looks out of place or use more space then needed." Then he specified the shape
   himself — "so we have grenn header with whitish color, only one row header, not
   2 row (as it is righ now) and thne we have partiuclar line with grey color and
   then out table."

   The sand band is GONE. Dates, title, Expand all and Search share the app's own
   forest bar (.pur-view-topbar). 118px of chrome above the first figure became
   87px, and every pixel of it went to the rows.

   EQUAL SIDE TRACKS are what let the title be centred AND stay in flow. Ali's own
   rule, from the Trial Balance: "if left 2 and right 2 total width are same then
   it will look asthically good". The retired .tb-title centred itself absolutely,
   which is fine for a fixed word and wrong here — a three-level drill path would
   have run straight over the date boxes with nothing to stop it. In flow with
   equal sides it lands on the real middle and ellipsises instead of colliding. */
/* 22px, not 12: the gap IS the space either side of the title, and with the dates
   now filling their track a 12px one put a 19px bold heading almost against a
   white box. It comes out of the date fields' growth, not out of the layout. */
/* 52, not the 46 the five screens were built at. Ali, looking at all five side by
   side once their boxes finally matched: "shall we increase height a little bit
   for all?" 28px boxes in a 46px row leave 9px of forest above and below; at 52
   they get 12, which is the same air the sand band used to give them and the
   reason the old two-line header read as roomier than this one.
   ON .fa-topbar, NEVER on .pur-view-topbar — that class is worn by Purchase,
   Stock and Masters too, and this is an Accounts decision. Every screen wearing
   it re-walks its wrap constant to 94 below; they are one number in two places
   and a change here that skips them pushes the last row off the bottom. */
.fa-topbar{ display:flex; align-items:center; gap:22px; min-height:52px;
    /* THE WHITE CORNER, and he has reported it more than once: "this corner
       shows white, it is not always, in past also i mentioned it."
       The panel is an 11px rounded box with a 1px border and overflow:hidden,
       and this bar is a SQUARE rectangle relying entirely on the parent's clip
       to round its top corners. Clip and fill are rasterised separately, so at a
       fractional device pixel — his 1.25 scaling — a sliver of the panel's OWN
       near-white background peeks between the clipped green and the border. The
       "not always" is the giveaway: it depends on where the panel lands on the
       pixel grid, which moves with the window.
       Giving the green its own radius means the SHAPE is round, not just the
       clip, so nothing can show through the corner however it rounds. 10, not
       11: the panel's radius less its 1px border is the curve this bar actually
       sits inside. */
    border-top-left-radius:10px; border-top-right-radius:10px; }
/* THE GROUPS TAKE THEIR NATURAL WIDTH AND THE TITLE CENTRES IN WHAT IS LEFT.
   Equal GROWING tracks (the previous rule) forced a choice between two faults:
   let the controls grow to fill their track and the date boxes end up mostly
   empty white — "empty space on date looks bad" — or leave them at their natural
   size and the leftover becomes dead space inside one group, which is the 14px
   that made the title look off-centre.
   Sizing the groups to their contents removes both. The middle takes the whole
   remaining span and centres its text in it, so the air either side is equal BY
   CONSTRUCTION rather than by arithmetic — and it stays equal when the button
   relabels itself Collapse all, which is wider. That self-correction is the real
   reason for this shape; any fixed-width version wobbles on every toggle.
   NO min-width:0 on the sides, still load-bearing: a flex item's automatic
   minimum is its content's, and that is what stops a three-level drill path
   pushing a track under its own controls (it drew the title over the Expand all
   button and clipped the dates mid-word). The middle is the only thing that
   shrinks. */
.fa-dates, .fa-tools{ flex:0 0 auto; display:flex; align-items:center; gap:8px; }
.fa-tools{ justify-content:flex-end; }
.fa-dates > *, .fa-tools > *{ flex:0 0 auto; }
.fa-head{ flex:1 1 auto; min-width:0; overflow:hidden; display:flex;
    align-items:baseline; gap:7px; justify-content:center; }
/* THE NAME IS NEVER TRUNCATED, THE PATH IS. Which screen you are standing on
   outranks how you got there, so the crumb gives up its width first. */
.fa-head b{ flex:0 0 auto; font-size:19px; font-weight:800; letter-spacing:-.014em;
    color:#fff; white-space:nowrap; }

/* THE DRILL PATH runs before the name on the same line, so the bar reads as one
   breadcrumb and a three-level drill costs it no height. */
.fa-crumb{ flex:0 1 auto; min-width:0; font-size:0.82rem; font-weight:600;
    color:rgba(255,255,255,.62); white-space:nowrap; overflow:hidden;
    text-overflow:ellipsis; }
.fa-crumb:empty{ display:none; }

/* THE DATES, in the white Purchase already puts on this bar (#btnPfNew's
   treatment: white fill AND white border, so the control reads as a solid chip
   rather than an outlined hole punched in the forest). */
.fa-dates label{ font-size:var(--fs-lbl); font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; color:rgba(255,255,255,.66); white-space:nowrap; }
/* THE DATES FILL THEIR TRACK. The two side tracks are already exactly equal and
   the title is already dead centre — measured, both 427px, both centres at 777 —
   but the LEFT track's controls only filled 328 of it, so ~110px of bare forest
   sat between the last date box and the title while the right side began 12px
   after it. Geometrically centred, visually pushed right, and Ali read it as the
   fault it looks like: "P&L does not look in center, either we increase width of
   date to match right side".
   Growing rather than a hardcoded width, so the two sides stay balanced at any
   window size. min-width, never width:auto or min-width:0 — a date input has a
   UA-drawn calendar button and a real intrinsic width, and letting it shrink
   clipped the field mid-word ("16-08-202"). */
.fa-dates input[type=date]{ flex:0 0 auto; width:122px; height:28px;
    padding:0 8px; font:inherit; font-size:0.82em; color:var(--ink);
    background:#fff; border:1px solid #fff; border-radius:var(--radius-sm);
    font-family:inherit; }
.fa-dates input[type=date]:focus{ outline:2px solid var(--accent-tint);
    outline-offset:1px; }

/* EXPAND ALL IN THE ACCENT. Ali: "detailed can have accent color, like right now
   we have rust color". It is the only action on the bar, so it is the only thing
   allowed to be coloured — and on forest the rust is legible where a .btn-primary
   would vanish (the forest-on-forest trap this app has already paid for twice). */
.fa-exp{ width:auto; height:28px; padding:0 9px 0 11px; font-size:0.8em;
    background:var(--accent); color:#fff; border-color:var(--accent); }
.fa-exp:hover:not(:disabled){ background:var(--accent-2); color:#fff;
    border-color:var(--accent-2); }
/* DISABLED IS A WHITE FIELD WITH GREY INK, not a ghost of the bar. Ali, S127,
   looking at Outstanding's flat list: "in ledger wise, expand get green color,
   change it to white and greyed out." A translucent white over forest IS green —
   it reads as a button in some third state rather than one that is switched off,
   and it was the only control on a bar of white fields not wearing one.
   Changed on .fa-exp itself, so the Trial Balance's own Ledger wise gets the
   same treatment: this is one control in one state, not a per-screen look. */
.fa-exp:disabled{ background:#fff; border-color:#fff;
    color:var(--ink-3); opacity:1; }
.fa-exp:disabled:hover{ background:#fff; color:var(--ink-3); }
/* The key rides ON the button, the same .acc-tkey chip the sidebar uses for
   F5..F9 — a shortcut nobody can see is a shortcut nobody presses. */
.fa-key{ background:rgba(255,255,255,.22); color:#fff; border-color:transparent;
    margin-left:2px; }
/* The chip has to follow the button. A translucent-white chip on a white button
   is an invisible chip — the shortcut would silently stop being advertised on
   exactly the screens where the control is off. */
.fa-exp:disabled .fa-key{ background:var(--head); color:var(--ink-3); }

/* SEARCH IS A BOX HOLDING THREE THINGS — field, count, clear — in a flex row,
   not an input with a count floated on top of it. Ali asked for the count "inside
   search bat on right side"; a flex row is what keeps the clear button from
   covering it the moment you type. */
/* IT GROWS AS WELL AS SHRINKS, and that is what makes the title read centred.
   The two tracks are equal, but .fa-tools right-aligns its contents — so when
   they were 14px narrower than the track, that 14px became dead space on the
   LEFT of the group, i.e. between the title and the button. Measured: 22px after
   the dates, 35.8px before Expand all. Ali saw it: "space after date ends and
   space before expand start is not same, it looks bad."
   He suggested shrinking the dates to match. Filling is the better half of the
   same idea — it equalises the gaps without opening dead space on both sides,
   and it is exactly what the date fields already do on their side.
   It is also still the one control allowed to give up width, since it holds a
   placeholder rather than a value; floored so it never becomes too small to
   type in. */
/* min-width:0 IS REQUIRED, not tidiness: .ls-search — shared with Purchase,
   Jobs, Stock and Sales — carries min-width:240px, and a min-width beats a
   smaller flex-basis. The box computed 240px while its basis said 175 and the
   declaration looked like it was simply being ignored. */
.fa-search{ display:flex; align-items:center; gap:7px; flex:0 0 175px;
    min-width:0; height:28px; padding:0 8px; background:#fff;
    border:1px solid #fff; border-radius:var(--radius-sm); position:relative; }
.fa-search input{ flex:1; min-width:0; height:26px;
    border:0 !important; background:transparent; padding:0 !important;
    font-size:0.82em !important; color:var(--ink); }
.fa-search input:focus{ outline:none; }
.fa-search .fa-count{ float:none; margin:0; white-space:nowrap; flex:0 0 auto;
    font-size:0.68rem; color:var(--ink-3); padding-left:7px;
    border-left:1px solid var(--line); }
/* Static, not absolute: it is a third item in the row now, not an overlay. */
.fa-search .led-clr{ position:static; width:18px; height:18px; flex:0 0 auto;
    bottom:auto; right:auto; }
.fa-lbl-off{ position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }

/* THE COLUMN LINE IS SAND, NOT FOREST — his pick, and it is the same #efeadf the
   Total band already wears, so the table opens and closes on one colour instead
   of two greys. He asked the right question about it: "we cannot have 2 shades in
   one columns and if we change here then we need to change it every where also
   righe?" Yes — which is why this is scoped to .fa-table and states a rule rather
   than editing the shared one: FOREST HEADER ON LISTS YOU SCAN, SAND ON THE TWO
   STATEMENTS YOU READ. Purchase, Stock, Jobs, Sales, Masters, the Ledger, the
   Trial Balance, Outstanding and the Day Book are untouched.
   The 1.5px rule under it is what stops the header merging into the first row now
   that it is no longer a dark band. */
/* SMALLER THAN THE FIGURES IT LABELS. On forest the header was a dark band and
   its size did not compete; in sand at the table's own 0.98rem it read as another
   row of content. A column name is not data — it is said once and then never
   read again, so it steps back. The FOOTER keeps the table size: that one IS a
   figure, and the bottom line of a statement is the last thing that should
   shrink (Ali: "reduce font size of particulars keep bottom font same"). */
.fa-table thead th{ background:var(--head); color:var(--ink);
    font-size:0.84rem; letter-spacing:.01em;
    border-bottom:1.5px solid var(--line); }

/* THE TOTAL ROW SITS HIGH, and the badge is why. "Balanced" is a 24.5px
   inline-block hung off the BASELINE (vertical-align:1px), so it stretches the
   line box upward only — the cell centres that lopsided box and the figures ride
   high inside it. Measured: 11px above the text, 16.5px below. Ali: "footer total
   is looks little uper side and not centered from all side."
   Centring the badge on the text's own midline instead makes the line box
   symmetric, so the row's own centring finally lands where it looks like it
   should. It fixes BOTH cells, because one table row shares one height. */
.fa-table tfoot .tb-bal .acc-badge{ vertical-align:middle; }
/* THE TOTAL MATCHES THE ROWS IT SUMS. It was inheriting .led-table's 0.9rem
   against this table's own 0.98rem — measured 13.5px under 14.7px body rows, so
   the bottom line of the statement was the SMALLEST figure on it. Ali: "font size
   of footer looks smaller than table." Equal size, with the weight it already
   carries doing the emphasis; making it bigger than the rows would shout. */
.fa-table tfoot td{ vertical-align:middle; font-size:0.98rem; padding:7px; }

/* THE TOTAL SITS IN A FIELD. Ali: "every back ground has white lable feild, the
   amount which is very important sits in plain black color with out any feild,
   what if we give a filed and then put numbr inside it" — with the search box on
   its sand bar sent as the COLOUR idea only, not the label-above-it layout.
   He is pointing at a real gap: on a sand band, plain text is the quietest thing
   on the row, and this row is the answer the whole screen exists to give.
   White on the sand makes it the brightest.
   The border is --line, one pixel, and the radius is the same 6 the badge just
   took — deliberately NOT the search box's own 8px input styling, because a
   figure you cannot type into should not wear an input's shape.
   min-width so both sides print the same size box whatever the two numbers are;
   the td padding drops 9 -> 7 so the band gains height once, not twice.
   THE FOOTER ONLY. It went onto the Trading band too for one round ("every
   grey") and he took it straight back off after seeing it: "i dont need it here."
   He is right — Trading is a running subtotal you pass on the way down, and a
   field around it made a waypoint look like a conclusion. The field is what marks
   the LAST line, so only the last line gets it.
   NO min-width. Making both boxes the same size was my idea, not his, and it
   cost a HORIZONTAL SCROLLBAR: 168px plus the cell's padding is wider than the
   16% column it sits in, so the column forced the table past the wrap and the
   overflow ate the very padding that keeps the field off the edge — which is why
   the box touched it. The box hugs its number now and can never outgrow its
   column, at any window width. The sand he wants to see beside it is the cell's
   OWN padding, the same 7px every other column has, rather than a special number
   for this one place. */
.fa-table tfoot td.r > span{
    display:inline-block; padding:4px 11px;
    background:var(--surface); border:1px solid var(--line);
    border-radius:6px; text-align:right; }
/* The centre rule was drawn in white-on-forest for the old dark header; on sand
   it takes the same --line as every other band, from one rule as before. */
.fa-table thead th:nth-child(4){ border-left-color:var(--line); }

/* THE WRAP'S HEIGHT IS A MEASURED CONSTANT and knows nothing about this chrome.
   .led-wrap caps at 100vh - 120px, walked off the LEDGER's geometry. Re-walked
   for one row: 20 frame + 46 bar = 66 above the wrap, + 1 panel border + 20 frame
   below = 87 — and then MEASURED, which put the panel 1px past the floor, so 88.
   Scoped to .fa-panel so the Ledger, Trial Balance, Outstanding and Group Summary
   keep the constant that is right for them. */
/* 144 = 98 + 46, and BOTH halves were measured rather than carried over.
   THE 4 IS A PRE-EXISTING BUG THIS PASS FOUND, not something the strip caused:
   with the old 94 these three panels ended at 682 against a 678 frame — four
   pixels past it, on every one of them, since before the strip existed. Four
   pixels is invisible as a gap and very visible as a consequence: the PAGE grows
   a scrollbar, and Ali caught it as "it gives a joult or moves the complete page
   when it suddendly gets the scoll". The other list screens use 94 and land on
   678 exactly, so the constant was simply wrong here and nothing had made it
   matter until the column stopped absorbing the overflow.
   The 46 is the S131 reports strip (.pur-subtabs): 36px tall with a 10px gap,
   sitting above this box. Measured at his 1528x698 before and after — the table
   went 604px to 558px and 17 rows to 16, which is the cost quoted to him when he
   chose tabs over a reports index page. If that strip is ever removed, this goes
   back to 98 in the same edit or the table underflows by exactly 46. */
.fa-panel .led-wrap{ height:calc(100vh - 144px - var(--mstrip-h)); }

/* DEAD SPACE BELOW THE LAST LINE IS WHITE, and takes no hover (Ali: "if empty
   screen just show white and no hover tint needed"). The filler cell is
   transparent by default, so what showed through was .led-wrap's sand — measured
   rgb(239,234,223), not the white it looked like in a screenshot. Painted on the
   CELL in both states, because the :hover rule next door clears it back to
   transparent and would have handed the sand straight back.
   Scoped to .fa-panel: the Ledger, Trial Balance, Outstanding and Group Summary
   share .led-fill and are not in this pass. */
.fa-panel .led-table tr.led-fill td,
.fa-panel .led-table tr.led-fill:hover td{ background:var(--surface); }
/* SECOND .fa-head BLOCK, AND IT IS THE ONE THAT FIRES. A duplicate of the pair
   ~170 lines above; same specificity, so this one wins on order and the 19px
   set up there has been dead since it was written — the bar rendered 1.12rem
   (16.8px at this root) while the notes recorded 19. Sizes live HERE until the
   two blocks are merged; changing the other one changes nothing.

   1.12rem IS THE DECIDED SIZE, not an accident that was never noticed. Shown at
   a true 19px and rejected on sight — Ali: "16.8 is good, 19 looks loud". Do not
   "correct" this to match the upper block or the S126 notes. */
.fa-head{ display:flex; align-items:baseline; gap:6px; min-width:0; }
.fa-head b{ font-size:1.12rem; font-weight:800; letter-spacing:-.01em;
    color:#fff; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* THE DRILL PATH runs BEFORE the title on the same line, so the bar reads as one
   breadcrumb — "Profit & Loss > Current Liabilities > Sundry Creditors" — and a
   three-level drill costs the bar no extra height. Light enough to sit behind
   the current screen's name without competing with it. */
.fa-crumb{ font-size:0.82rem; font-weight:600; color:rgba(255,255,255,.62);
    letter-spacing:.01em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
    flex:0 1 auto; min-width:0; }
.fa-crumb:empty{ display:none; }
/* Row 2 has no centre block any more; the two sides simply sit at the ends. */
.fa-bar .tb-title{ display:none; }

/* ── ROW 2, ONE LINE TALL (idea B, his pick, 2026-08-16) ────────────────────
   "one part which looks out of place or use more space then needed."
   The band was 78px because every label sat ABOVE its control, so it had to be
   two lines tall — and with the title gone to the forest bar, the middle third
   held nothing at all. Labels now sit BESIDE their boxes and the band is 42px.
   Nothing is hidden and nothing costs an extra click; the row simply stops
   being two lines. 36px back, which is one more line of statement.
   The fixed 322px side tracks go with it: they existed to centre the title
   between two equal halves, and there is no title on this row to centre. */
.fa-bar{ align-items:center; padding:6px 14px; }
.fa-bar .tb-side{ width:auto; flex:0 0 auto; align-items:center; gap:12px; }
.fa-bar .tb-side > *{ flex:0 0 auto; }
.fa-bar .ls-filter{ flex-direction:row; align-items:center; gap:7px; }
.fa-bar .ls-filter label{ margin-bottom:0; white-space:nowrap; }
.fa-bar .ls-filter input[type=date]{ width:auto; min-width:0; }
.fa-bar .tb-expwrap{ flex:0 0 auto; }
.fa-bar .tb-exp{ width:auto; height:28px; padding:0 13px; }
/* Present for a screen reader, gone from the page — the control says it. */
.fa-bar .fa-lbl-off{ position:absolute; width:1px; height:1px; padding:0;
    margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
/* The clear button was anchored to the BOTTOM of a wrapper that used to be
   label-plus-input. With the label gone the wrapper is the input, so it centres
   on that instead — bottom:6px would have hung it off the top edge. */
.fa-bar .led-clr{ top:50%; bottom:auto; transform:translateY(-50%); }
/* The count is its own item at the end of the row now, not a float inside a
   label that no longer prints. */
.fa-bar .fa-count{ float:none; margin:0; white-space:nowrap; }
/* .led-search is 184px wide but .ls-search input carries min-width:200px, so the
   box had always overflowed its own wrapper by 16px. Invisible while the count
   sat in the LABEL ABOVE it; the moment the count moved beside the box, the
   input covered it and "11 lines" rendered as ".1 lines". The wrapper takes the
   width the input actually wants, and the input stops insisting. */
.fa-bar .led-search{ width:200px; }
.fa-bar .led-search input{ min-width:0; width:100%; }

/* ONE CENTRE LINE, in the app's own hairline. Set by COLUMN POSITION rather
   than a class per cell, so the header, the rows, the block subtotal and the
   footer all take it from one rule and cannot drift apart as the screen grows.
   It must run through EVERY band: broken at the block boundary, it read as
   separate boxes (Ali: "i see 4 boxes").
   The drill screens render two cells per row and so have no 4th child — right,
   because a drill is ONE list, not two sides. */
.fa-table tr > *:nth-child(4){ border-left:1px solid var(--line); }
/* A sand hairline is invisible on the forest header, so the same line is drawn
   in that band's own terms. */
.fa-table thead th:nth-child(4){ border-left-color:rgba(255,255,255,.34); }

/* A GROUP line. td.grp, never tr.grp td — the class sits on the CELL, because
   the two halves of one <tr> are different lines and only one of them may be a
   group. Weight 600, not 800: at 800 every group shouted (Ali: "bold name not
   lokimng good") and the hierarchy is already carried by the child rows being
   indented, italic and lighter. */
/* A STATEMENT IS READ, NOT SCANNED — bigger than the list screens (Ali: "in table
   i want to increase font size"). Set on the table so header, rows, subtotal band
   and footer all move together and the hierarchy keeps its proportions. */
.fa-table{ font-size:0.98rem; }
.fa-table td.fa-grp{ font-weight:600; color:var(--ink); }
/* NO ITALICS ANYWHERE ON THIS TABLE (Ali: "i dont like italic fonts make it
   normal"), AND FULL INK ON THE CHILD ROWS TOO.
   Those two go together. A child row used to carry three signals — indent,
   italic, lighter ink — and dropping the italic left the grey doing work it was
   never meant to do alone: "inside the table, fonts color specially grey look
   little dull". It was --ink-2 on a figure someone is trying to read.
   The app had already answered this: the Trial Balance renders EVERY nesting
   level at full --ink and lets the indent carry the level, measured. So a child
   here keeps the indent and the parent's weight 600 against its own 400, and
   stops being harder to read than the line above it. */
.fa-table td.fa-kid{ padding-left:26px; color:var(--ink); }
.fa-table td.fa-kid.r{ padding-left:8px; }

/* THE BLOCK SUBTOTAL IS A SAND BAND, not a ruled line — the same treatment the
   Purchase footer, the Stock footer and the Ledger's Current Total already
   wear. It closes the trading block and separates it from the one below, so no
   blank spacer row is needed, and it leaves no horizontal rule for the centre
   line to cross. */
.fa-table tr.fa-sub td{ background:var(--head); font-weight:700;
    border-top:1px solid var(--line); border-bottom:0; }

/* The result lines — Gross Profit c/o, Nett Loss and the sheet's own P&L line.
   Ali: "profit and loss can get colored fonts." Not a new palette: the app's
   own semantic pair, one meaning each everywhere. Only these lines carry it, so
   the colour still means something where it appears. */
.fa-table td.fa-res{ font-weight:700; }
.fa-table td.fa-res.fa-loss{ color:var(--neg-deep); }
.fa-table td.fa-res.fa-gain{ color:var(--pos-deep); }
/* A figure running against its side's natural direction: a plain minus, in red. */
.fa-table .fa-neg{ color:var(--neg-deep); }
/* "(auto, at cost)" beside Closing Stock. The app's existing .tw is scoped to
   .acc-pl — the RETIRED screen — so it does not reach this table at all. */
.fa-table .tw{ font-weight:400; font-size:0.9em; color:var(--ink-3); font-style:normal; }

/* THE CURSOR PAINTS THE TD, NOT THE TR — a cell's own background sits in front
   of its row's, so a row-level rule loses to the zebra on every second line
   (the Ledger records the same fix).
   And it lights ONE SIDE ONLY. The shared cursor puts .acc-cur on the element
   carrying [data-nav], which here is the NAME cell of one half; its two amount
   cells are picked up as adjacent siblings. A two-sided sheet is two
   independent columns, so a highlight spanning the whole row would claim the
   other side's line is selected too. */
.fa-table tbody td.acc-cur,
.fa-table tbody td.acc-cur + td,
.fa-table tbody td.acc-cur + td + td{ background:#f2d585 !important; color:#241f00 !important; }
.fa-table tbody td.acc-cur{ box-shadow:inset 3px 0 0 #8a6d0b; }
.fa-table tbody td.acc-cur .fa-neg,
.fa-table tbody td.acc-cur + td .fa-neg,
.fa-table tbody td.acc-cur + td + td .fa-neg,
.fa-table tbody td.acc-cur .tw,
.fa-table tbody td.acc-cur .gs-count{ color:#5a4d17; }
.fa-table tr.fa-open td{ cursor:pointer; }
.fa-table tr.fa-open:hover td:not(.acc-cur){ background:var(--brand-tint); }

/* ══ THE VERTICAL FORM (View > Vertical) ═════════════════════════════════════
   Three columns instead of six, so the centre rule keyed off nth-child(4) never
   appears and no rule here has to undo it. */
/* A SECTION HEADING — "Trading Account:", "Income Statement:", "Sources of
   Funds:". It names a block and carries no figure, so it takes no cursor and no
   zebra either. Letter-spaced like Tally's: that, not the weight, is what stops
   it reading as just another bold row among bold group rows.
   NO EXTRA PADDING, and the letter-spacing is why it can do without. A 12px
   padding-top here read fine and cost 6px a row against a table that had 5px of
   slack — the collapsed statement scrolled by half a row, which is worse than a
   heading sitting tight. The one row height of the six-screen family holds. */
.fa-table tr.fa-sec td{ font-weight:700; letter-spacing:.04em; color:var(--ink); }
/* THE THIRD NAME LEVEL. Tally nests names three deep but keeps every figure in
   one of two columns, so this indents the NAME only — it is never put on a
   money cell, which is why it needs no .r reset the way .fa-kid does. */
.fa-table td.fa-kid2{ padding-left:44px; }
/* THE TWO NAMELESS SUBTOTALS. The rule sits over the FIGURE alone: drawn full
   width it would cut the statement in half, which is the same complaint that
   turned the T-form's block rule into a sand band ("i dont like these + shaped
   line"). Nothing in Particulars is Tally's own and his explicit call.
   SHORTER THAN A LINE OF THE STATEMENT, and it does not break the one-row-height
   rule because it is not a line: it has no name, takes no cursor, and is not
   counted in "N lines". It is a rule with a figure on it, and giving it a full
   34.3px row bought nothing but the scrollbar this form could not afford. */
.fa-table tr.fa-vsub td{ padding-top:2px; padding-bottom:2px; }
.fa-table tr.fa-vsub td.r, .fa-table tr.fa-vtot td.r{
    border-top:1px solid var(--ink-3); }

.tb-bal{ margin-left:12px; font-weight:400; letter-spacing:0; }
.tb-bal .acc-badge{ font-size:0.76rem; vertical-align:1px; }
#accounts.acc-list-open #tbSide{ max-height:calc(100vh - 40px - var(--mstrip-h)); }
@media (max-width:1100px){ #tbSide{ width:150px } }

/* ══ TRIAL BALANCE — THE P&L'S ONE ROW OF CHROME (S127) ══════════════════════
   The bar itself is .fa-topbar UNCHANGED, so the two statements share one
   definition of what that row looks like and cannot drift apart. Everything
   below is only what THIS screen adds, all of it behind .tb-panel / .tb-table /
   .tb-topbar, which exist nowhere else. Nothing above this block is edited: a
   change he asks for on the Trial Balance must not be able to reach the P&L,
   whose review is still open.

   The old .tb-bar / .tb-side / .tb-title layout is left in place, unreferenced —
   retired, not deleted, the same way accLoadTBLegacy was. */

/* SAND HEADER, FOREST BAR — one of each, which is what "did not like 2 rows with
   forst color" (S124) was asking for. The Trial Balance is a statement you READ,
   not a list you scan: it ends in a Grand Total with a balanced check, exactly as
   the P&L ends in Total. Same rule, same treatment, values taken from
   .fa-table thead th rather than re-invented. */
.tb-table thead th{ background:var(--head); color:var(--ink);
    font-size:0.84rem; letter-spacing:.01em;
    border-bottom:1.5px solid var(--line); }

/* ══ THREE KINDS OF LINE, THREE BACKGROUNDS (S127) ═══════════════════════════
   Ali: "there is no difference in group sub group ledger ... no way to know its
   sub group value or ledger or group value", then, after being shown weight and
   two colour schemes: "main group has to have white backgroup, all sand looks
   bad. sub group can have sand", and on the ledgers, L1 — the app's own resting
   stripe rather than a new hue.

     .tb-root   --surface   top group: a total, and the ENTIRE screen collapsed
     .tb-sub    --line      sub-group
     .tb-sub2   --head      sub-sub-group, and anything deeper
     .tb-led    --zebra     a ledger — the stripe this table used to wear
     .tb-led2   --surface   the alternate ledger, so a long run stays readable

   FOUR EVEN STEPS, AND THE GAP HAD TO BE OPENED TO GET THEM. Ali: "another sub
   level also remains sand. see duties and gst both has sand." Both sub-levels
   wearing --head said nothing about which was inside which. The obvious fix —
   splitting the range between --head and --zebra — was built and shown, and it
   is at the edge of what the eye resolves: eleven points across two boundaries.
   Dropping the FIRST sub-level onto --line instead (the border colour, still a
   token, no new value) spaces all four about twelve apart:
     #fffefb -> #e3ddd0 -> #efeadf -> #faf8f2
   Deeper reads lighter, running down to the detail, with the top group white by
   his own call. His chart is three group levels deep and no more.

   NOT ONE INVENTED COLOUR. All four are existing tokens.

   WHY THE TOP GROUP GETS THE PLAINEST TREATMENT, which looks backwards: any mark
   that means "this row is a group" says nothing when EVERY row on screen is one,
   and that is the collapsed sheet — six roots and nothing else. Bold figures
   were tried there and he rejected them on exactly that ("when closed all figues
   in bold looks bad"), and colouring the groups turns the collapsed sheet into
   one solid sand slab. Leaving the roots white keeps collapsed identical to
   today and spends the colour only when there is something to tell apart.

   A GREEN TINT WAS REJECTED FOR A MECHANICAL REASON, not taste: --brand-tint is
   the HOVER colour, so a hovered group would have turned the exact shade that
   means "ledger".

   THE HOVER RULE HAS TO COME BACK, and it caps how specific the rules above may
   be. Hover paints the TR (.pur-view-table tbody tr:hover) and a cell's own
   background sits in FRONT of its row's, so painting cells by kind kills the
   hover tint on every line — the same fault the cursor and the old zebra each
   hit here. It is repainted on the CELL below, at equal specificity and after,
   so it wins on order. That is also why a row carries ONE kind class and never
   two: `.tb-led.tb-z1` would out-rank this and leave half the table dead to the
   mouse. The cursor keeps its !important and still beats both. */
/* OUTSTANDING RIDES THE SAME RAMP (S127), because it nests the same four levels
   — a root, a sub-group, the sub-group that holds the parties, then the parties.
   ONE rule per level carrying both screens' selectors, rather than a second copy
   of the values under os-*: two definitions of one ramp is two things to keep in
   step, and he will be looking at these two screens side by side. Each screen
   keeps its OWN class names, so either can still be overridden alone. */
.tb-table tbody tr.tb-root td,
.os-table tbody tr.os-root td{ background:var(--surface); }
.tb-table tbody tr.tb-sub  td,
.os-table tbody tr.os-sub  td{ background:var(--line); }
.tb-table tbody tr.tb-sub2 td,
.os-table tbody tr.os-sub2 td{ background:var(--head); }
.tb-table tbody tr.tb-led  td,
.os-table tbody tr.os-led  td{ background:var(--zebra, #faf8f2); }
.tb-table tbody tr.tb-led2 td,
.os-table tbody tr.os-led2 td{ background:var(--surface); }
.tb-table tbody tr:hover td,
.os-table tbody tr:hover td{ background:var(--brand-tint); }

/* THE TYPE MATCHES THE P&L'S. Ali, S127: "we have increases font size and change
   the font color a bit, check that also n apply it here." Checked rather than
   assumed, and the two halves of that turned out to be in different states:

   SIZE was the real gap — 12.3px here against the statement's 14.7px, measured.
   0.98rem is the P&L's own value, not a new one.

   COLOUR was already done. Every row of this table already prints full --ink; the
   dull grey his P&L note was about ("inside the table, fonts color specially grey
   look little dull") never applied to these rows. The one grey left is the CARET,
   --ink-3 on all 19 rows, and the P&L has no caret to copy from because it drills
   instead of unfolding. Moved one notch to --ink-2 — "a bit", his word. Full --ink
   would put the caret level with the name it points at.

   WEIGHT comes with the size: the statement sets its group lines to 600 and this
   table left every row at 400, so the hierarchy was carried by indent alone. The
   name cell only — the figures stay 400 there too, or every group row shouts. */
.tb-table{ font-size:0.98rem; }
.tb-table tr.gs-grp > td:first-child{ font-weight:600; }
.tb-table .gs-car{ color:var(--ink-2); }

/* THE GRAND TOTAL SITS IN A FIELD, as the P&L's Total does — "what if we give a
   filed and then put numbr inside it". Two figures here instead of one, so two
   fields; the wrapping <span> is produced by tbHTML(), because a rule can only
   paint a box that exists.
   vertical-align:middle for the same reason the P&L needs it: the Balanced badge
   is an inline-block hung off the baseline, which pushes the line box up and
   leaves the figures riding high in a top-aligned cell (.led-table sets top).

   AND IT MUST NOT COST TWO ROWS. Ali drove it and called it: "footer grand total
   uses 2 row space check it once." Measured 48.56 against a 30.83 body row —
   1.58 rows — where the P&L he approved runs 46.31 against 34.31, or 1.35. Two
   separate causes, both inherited rather than chosen:
     - .led-table's 9px footer padding, where the P&L's own footer uses 7;
     - 0.9rem (13.5px) type over 12.3px rows, so the bottom line of the statement
       was the BIGGEST figure on it. That is his own P&L ruling read backwards —
       "the total matches the rows it sums ... making it bigger than the rows
       would shout" — which there meant growing the footer and here means not
       letting it outgrow them. The weight still does the emphasis.
   Together: 48.56 -> 42.8, and 1.58 rows -> 1.39, the P&L's own proportion. The
   white field's 10px (8 padding + 2 border) is the floor; below that the figure
   has no box left.
   `inherit`, not a repeat of the number: it takes .tb-table's own size, so the
   total keeps matching the rows through any later change to the table's type
   rather than silently drifting out of step the way it had. */
.tb-table tfoot td{ vertical-align:middle; padding:7px; font-size:inherit; }
.tb-table tfoot td.r > span{
    display:inline-block; padding:4px 11px;
    background:var(--surface); border:1px solid var(--line);
    border-radius:6px; text-align:right; }

/* DEAD SPACE BELOW THE LAST LINE IS WHITE and takes no hover — Ali: "if empty
   screen just show white and no hover tint needed". Painted on the CELL in both
   states, because the :hover rule clears it back to transparent and would hand
   .led-wrap's sand straight back. */
.tb-panel .led-table tr.led-fill td,
.tb-panel .led-table tr.led-fill:hover td{ background:var(--surface); }

/* ══ OUTSTANDING — THE SAME CHROME (S127) ═══════════════════════════════════
   Sand column header against the forest bar, statement type, and the totals in
   a field. Values are the Trial Balance's, taken from it rather than re-picked.

   NO TITLE ON THIS ONE, and it is a measurement not a preference: this screen's
   controls need 1026px of a 1084px bar at his 1528 — a date, a THREE-button
   segmented control carrying "(they owe us)" / "(we owe)", Expand all and
   Search. At his 1150 half-screen they need 877 of 866 and are already over
   TODAY. The only pieces big enough to cut are the Dr/Cr hints, and those are
   his explicit instruction ("dont skip dr and cr, use them at front and in
   bracket give hint"). A title that ellipsises to nothing is worse than none;
   the left menu already says which screen this is. */
/* THE BAR. No title, so no centre track to protect — the controls simply run
   left to right and Search takes the spare width, which is what he asked for
   when the old band left the right half empty ("hald screen ( header part ) is
   empty"). Everything sits on forest, so the labels and the date box take the
   white-on-forest treatment .fa-dates already defines rather than a second set
   of values. */
.os-topbar{ gap:12px; }
/* .os-field is this screen's OWN wrapper, and the rename is the whole fix.
   These rules asked for height:28px from the day the bar was built and never
   once fired: the lot selector's "uniform filter inputs" rule near the top of
   this file sets height/padding/font-size !important on any input inside
   .ls-filter, and an !important cannot be out-ranked by a longer selector. So
   the boxes stood 34 tall in a 46px bar — overflowing its 8px padding, leaving
   6px of forest above and below against the statements' 9 — which is exactly
   the "one looks cramped" Ali reported side by side.
   Narrowing the lot selector's rule at source was the other option and is the
   wrong one: .pur-view-filter-bar .ls-filter input already asks for 28 and
   loses the same way, so every Purchase/Stock/Jobs/Group Summary filter bar
   would drop 34->28 in one edit, on screens he has not asked about. */
.os-topbar .os-field{ display:flex; flex-direction:row; align-items:center;
    gap:7px; flex:0 0 auto; margin:0; height:28px; }
.os-topbar .os-field label{ margin-bottom:0; white-space:nowrap;
    font-size:var(--fs-lbl); font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; color:rgba(255,255,255,.66); }
/* 132, not the statements' 122: those carry TWO dates and can afford the tighter
   box, and at 122 this one clipped its own calendar button against the year. */
.os-topbar .os-field input[type=date]{ width:132px; min-width:0; height:28px;
    padding:0 8px; font:inherit; font-size:0.82em; color:var(--ink);
    background:#fff; border:1px solid #fff; border-radius:var(--radius-sm);
    font-family:inherit; }
.os-topbar .os-field input[type=date]:focus{ outline:2px solid var(--accent-tint);
    outline-offset:1px; }
/* THE MENU IS A WHITE FIELD, the same object as the date box beside it and the
   search box opposite. That is the whole answer to "3 green button looks ugly":
   everything else on this bar is a white field and the segmented control was the
   only thing that was not.
   Sized like a date box so the left block reads as two matching fields — which
   is exactly the slot the second date occupies on the statements.
   The 28px here was dead until the wrapper above became .os-field: the select
   sat inside .ls-filter and took the lot selector's height:34px !important with
   the date box. ONE rule sizes this control — a second one added alongside is
   how the first became invisible in the first place. */
.os-topbar .os-menu{ width:148px; height:28px; padding:0 8px; font:inherit;
    font-size:0.82em; color:var(--ink); background:#fff; border:1px solid #fff;
    border-radius:var(--radius-sm); cursor:pointer; }
.os-topbar .os-menu:focus{ outline:2px solid var(--accent-tint); outline-offset:1px; }
/* THIS SCREEN'S OWN BUTTON COLUMN, and the arrangement lives there now rather
   than in a second menu on the bar — his instruction: "i wanted a menu button on
   right side like we have in trial balance ... so remove menu from headr."
   Same width and same box as #tbSide: the two reports carry the same choice and
   must not look like two different controls. */
#outstSide{ width:172px; flex-shrink:0; background:var(--surface);
    border:1px solid var(--line); border-radius:var(--radius); padding:10px 7px;
    position:sticky; top:12px; overflow-y:auto; }
#accounts.acc-list-open #outstSide{ max-height:calc(100vh - 40px - var(--mstrip-h)); }
@media (max-width:1100px){ #outstSide{ width:150px } }
.os-topbar .os-viewwrap{ flex:0 0 auto; }
/* SEARCH IS THE ONLY THING THAT GROWS. .fa-search fixes its width at 175px for
   the statements, whose bars have a centre title to leave room for; this one has
   no title, so the field takes what is left instead of leaving a forest gap. */
.os-topbar .os-search{ flex:1 1 auto; min-width:150px; }

/* THE HALF-SCREEN NEEDS NO SPECIAL CASE ANY MORE. The three-button control was
   396px and forced one: at his 1150 the bar wanted 967px of 850, and the only
   thing big enough to cut was the bracket hint, which is his. The menu is 148 —
   the same slot the statements' second date fills — so the row fits at both
   widths with the hints intact and the title in the middle. The media query that
   collapsed the brackets below 1250, and the .os-seg-on-forest rules it went
   with, are gone rather than left to rot: nothing renders .os-seg on this bar. */

.os-table thead th{ background:var(--head); color:var(--ink);
    font-size:0.84rem; letter-spacing:.01em;
    border-bottom:1.5px solid var(--line); }
/* The Dr/Cr hint is the one thing on that header row that must NOT take the
   header's weight — it is a gloss on the two letters, not a second heading. */
.os-table thead th i{ font-weight:400; font-style:normal; color:var(--ink-2); }
/* THE SORT HOVER HAD TO BE RESTATED FOR A SAND HEADER. The shared rule paints
   --brand-2, a dark green drawn for the forest header every other list wears;
   on sand it turns the cell you are pointing at into a black box. Same
   specificity as the shared rule, declared later, so it wins on order. */
.os-table thead th.sortable:hover{ background:var(--line); color:var(--ink); }
.os-table thead th.sortable .pv-sort-icon{ color:var(--accent); }
.os-table{ font-size:0.98rem; }
.os-table tr.os-grp > td:first-child{ font-weight:600; }
.os-table .gs-car{ color:var(--ink-2); }
.os-table tfoot td{ vertical-align:middle; padding:7px; font-size:inherit; }
/* FIRST ROW ONLY. The foot has two lines and only the Total's figures take a
   field. The Net line's cell also holds a span — ledBalCell's Dr/Cr word — so an
   unqualified `tfoot td.r > span` boxed that badge as if it were a figure. */
.os-table tfoot tr:first-child td.r > span{
    display:inline-block; padding:4px 11px;
    background:var(--surface); border:1px solid var(--line);
    border-radius:6px; text-align:right; }
.os-panel .led-table tr.led-fill td,
.os-panel .led-table tr.led-fill:hover td{ background:var(--surface); }

/* THIS SCREEN'S OWN CONSTANT. It lands on the same 88 as the two statements, and
   that is a RESULT rather than a reason to share their selector: 20 frame + 46
   bar + 1 panel border + 20 frame is the same sum here because the bar was
   trimmed to 46 above. Walked, then measured — panel bottom 678 against a 698
   floor, the 20px frame exactly. Left on --mstrip-h alone it stopped 32px short
   of the floor, still wearing the Ledger's two-line 120. */
.os-panel .led-wrap{ height:calc(100vh - 94px - var(--mstrip-h)); }

/* THE WRAP'S HEIGHT IS THIS SCREEN'S OWN MEASURED CONSTANT — deliberately NOT
   .fa-panel's 88, which was walked off the P&L's geometry. Borrowing that
   selector would couple two heights that only happen to agree today.
   Walked here: 20 frame + 46 bar = 66 above the wrap, + 1 panel border + 20
   frame below = 87. Measured at 88 on his 1528x698: wrap bottom 677, panel
   bottom 678, floor 698 — the 20px frame exactly, so the walked 87 would have
   run 1px past it, the same 1px the P&L's own constant absorbs. */
/* +46 for the S131 reports strip, exactly as .fa-panel above — see the note
   there. The two statements and the Trial Balance carry the same strip, so they
   must carry the same constant or the three drift apart when you tab between
   them, which is the one thing the shared chrome exists to prevent. */
.tb-panel .led-wrap{ height:calc(100vh - 144px - var(--mstrip-h)); }

/* ══ THE LEDGER'S HEADER (S128) ═══════════════════════════════════════════════
   The fifth screen onto the P&L's one forest row. Ali's own second option:
   "ledger does not have expand, so we have space from, to date till search, and
   in that we can fit ledger picker."

   THE PICKER TAKES THE TITLE'S SLOT, and that is the whole idea. The other four
   screens name a REPORT in the middle; this one names an ACCOUNT, and the box
   already holds that name — so the middle block is a control here, with the
   field's own label saying which screen you are standing on. It is also the only
   arrangement that fits: .fa-dates and .fa-tools are flex:0 0 auto, so exactly
   ONE item on the bar may grow, and on this screen that has to be the field
   holding 51-character party names.

   Everything screen-specific hangs off .led-topbar / .led-1r, never off the
   shared .fa-* classes.

   S129 — GROUP SUMMARY NOW SHARES THIS BAR, by his instruction ("need to match
   with ledger"), so the rules below name BOTH hooks rather than being copied
   into a second block. That is deliberate: these two screens are read as a pair
   and a copy is two places for them to drift apart, which is the whole reason
   the pair was asked for. Anything either screen needs DIFFERENTLY still goes on
   its own hook — .led-topbar / .led-1r for the Ledger, .gs-topbar / .gs-1r for
   Group Summary — so "make them match" has not become "they can never differ". */
/* 12px between the blocks, not the shared 22. That 22 was bought for the
   statements, where "the gap IS the space either side of the title" — this bar
   has no title, so it is spacing nothing, and Outstanding (the other title-less
   bar) already sits at 12. Measured, it is not cosmetic: the longest account
   name needs 483px of field and 22px gaps left it 462. */
.led-topbar, .gs-topbar{ gap:12px; }
.led-topbar .led-pick, .gs-topbar .led-pick{ display:flex; align-items:center;
    gap:8px; flex:1 1 auto; min-width:0; position:relative; }
/* The label sits INLINE before the field, exactly as FROM and TO do — on a
   one-line bar there is no row above it to stand in. */
.led-topbar .led-pick > label, .gs-topbar .led-pick > label{ margin:0;
    flex:0 0 auto; font-size:var(--fs-lbl); font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; color:rgba(255,255,255,.66); white-space:nowrap; }
.led-topbar #ledPickMount, .gs-topbar #gsPickMount{ flex:1 1 auto; min-width:0; }
/* A WHITE FIELD, like everything else on a forest bar — his ruling from S127,
   the answer to "3 green button looks ugly". White fill AND white border, so the
   control reads as a solid chip rather than a hole punched in the forest.
   !important is not decoration here: createSearchDropdown writes border, height,
   padding and font-size into the element's STYLE ATTRIBUTE, and nothing but
   !important outranks that.
   padding-right is deliberately NOT declared — ledSyncPicker (and gsSyncPicker,
   which does the same thing with the parent-path chip) sets it inline from the
   chip's measured width, and an !important here would win and let a long name
   run under the chip. */
.led-topbar #ledPickMount input, .gs-topbar #gsPickMount input{ height:28px !important;
    padding-top:0 !important; padding-bottom:0 !important; padding-left:9px !important;
    font-size:0.82em !important; font-weight:700; color:var(--ink);
    background:#fff !important; border:1px solid #fff !important;
    border-radius:var(--radius-sm) !important; }
.led-topbar #ledPickMount input:focus,
.gs-topbar #gsPickMount input:focus{ outline:2px solid var(--accent-tint);
    outline-offset:1px; }
/* The chip centres on a 28px box. The shared rule pins it to bottom:8px, which
   was measured against the old 34px field in a two-line band. */
.led-topbar .led-kind, .gs-topbar .led-kind{ top:50%; bottom:auto;
    transform:translateY(-50%); right:7px; }
/* WITH NOTHING PICKED THERE IS NO KIND, and an empty chip still paints its own
   padding — a small grey pill sitting in an empty field, which on the old sand
   band was nearly invisible and on a white one is not. On Group Summary the same
   chip carries the PARENT PATH, which is empty for a root group as well as for
   no selection — so this screen reaches the empty case in normal use, not just
   on arrival. */
.led-topbar .led-kind:empty, .gs-topbar .led-kind:empty{ display:none; }
/* SEARCH DOES NOT GROW HERE, and it is narrower than the statements' 175. Every
   pixel it takes comes off the picker, and the two fields are not equals on this
   screen: search filters rows you are already looking at, the picker holds
   51-character party names. Measured — at 175 the longest name in the book was
   clipped by 21px. */
.led-topbar .led-searchbox, .gs-topbar .gs-searchbox{ flex:0 0 160px; }

/* SAND COLUMN HEADER, FOREST BAR — one of each. Ali caught this the moment he
   looked: "you did not change particulars header color." The forest bar arrived
   and the forest column header stayed, which is TWO forest rows stacked — the
   exact thing "did not like 2 rows with forst color" (S124) rejected, and the
   reason the Trial Balance and Outstanding both repainted their header when they
   took this bar. Values taken from .tb-table thead th rather than re-invented,
   and S129 brought Group Summary onto the same bar, so it takes the same sand
   header in the same edit — the pair is what matters, not either half, and a
   screen that took the forest bar without repainting its column header is
   exactly the fault he caught here. */
.led-1r .led-table thead th, .gs-1r .led-table thead th{ background:var(--head);
    color:var(--ink); font-size:0.84rem; letter-spacing:.01em;
    border-bottom:1.5px solid var(--line); }

/* TRYING IT: THE COLUMN HEADER CENTRED BOTH WAYS. Ali: "date particular vch -
   that header, shall we keep it center (all side up-down left-right)?"
   The up-down half is a real fault rather than a preference — `.led-table td,
   .led-table th` sets vertical-align:top for the BODY (where a wrapped party
   name and its narration must start on the same line), and the header inherited
   it, so every heading sat pinned to the top of its cell with the padding all
   below it.
   Left-right is the part to LOOK at rather than reason about: it overrides
   th.r's right-alignment, so Debit, Credit and Balance no longer sit over the
   edge their figures line up to. That is the trade, and it is his to judge.
   It was Ledger-only while he judged it ("may be after they come in center we
   can tell that"), then ROLLED OUT WITH THE COLUMN RULES — "also apply center
   aligment as per ledgers" — to the same three screens and no others. */
.led-1r .led-table thead th,
.gs-1r .led-table thead th,
.os-panel .led-table thead th{ text-align:center; vertical-align:middle; }

/* AND THE THREE SHORT COLUMNS IN THE BODY WITH THEM. Ali: "also in table m date,
   vch type, vch no can hve center?" — the three that hold a short fixed-format
   value, where centring reads as a column of tokens rather than ragged text.
   The other four are deliberately NOT centred and the reason is different for
   each pair: Particulars is a NAME of unpredictable length, which only scans as
   a list when every row starts at the same left edge; Debit, Credit and Balance
   are MONEY, which is read by lining up the last digit, so centring them would
   put the rupees and paise of different magnitudes over each other.
   Named by class, matched to each column's own header — never nth-child, which
   goes quietly wrong the first time a column moves. */
.led-1r .led-table .led-date,
.led-1r .led-table .led-vtype,
.led-1r .led-table .led-vno,
/* Outstanding's equivalents: Type and Code are the only two columns on that
   screen holding a short fixed value. Its other three are a name and two money
   columns, which keep their own alignment for the reasons above — and Group
   Summary has NO column of this kind at all (Particulars plus four money
   columns), so the body centring simply does not apply there. Same rule, applied
   where the column type matches rather than everywhere it would fit. */
.os-panel .led-table .os-kind,
.os-panel .led-table .os-code{ text-align:center; }

/* THE FAMILY'S ROW HEIGHT, and this is the one Ali reported by eye: "ledger look
   less height n tight n p&l look comfortable". Measured, he was reading the TEXT.
   The other four statement screens each set 0.98rem on their own table when they
   were converted (.fa-table, .tb-table, .os-table); the Ledger kept the list
   default of 0.82rem, so its cells ran 12.3px against their 14.7 and its rows
   came out 30.8 against their 34.3 — same padding, same column header, shorter
   row. Same value as the other three rather than a fourth number, and scoped to
   .led-1r — and S129 added .gs-1r when Group Summary joined the family, so all
   six screens now run one row height. */
.led-1r .led-table, .gs-1r .led-table{ font-size:0.98rem; }

/* A CUT NAME MUST LOOK CUT. This column already clipped at his 1150 half-screen,
   where the six fixed columns leave Particulars 134px — the bigger text above
   only made it show more often. With text-overflow:clip a party came out as
   "ABHIRAM TRADER" and "RENUKUNTALA BA", which on a money screen does not read
   as a truncation, it reads as a DIFFERENT PARTY. Ellipsis is what Stock, the
   Day Book and the lot selector all already do; the cell also carries a title
   attribute, so the full name is one hover away. Matched by class, not
   nth-child, so re-ordering the columns cannot silently point it elsewhere. */
.led-1r .led-table .led-part{ text-overflow:ellipsis; }

/* THIS SCREEN'S OWN CONSTANT, walked then measured like the other three: 20
   frame + 52 bar = 72 above the wrap, + 1 panel border + 20 frame below = 93,
   and 94 measured. Was 88 while the bar was 46; the bar and this number move
   together, which is why all five screens carry the same value. Group Summary
   joined them in S129 — it wore the base .led-wrap's 120 while its band was two
   lines tall, and that 120 is now dead for every screen but is left in place
   rather than deleted, the same way the legacy loaders were retired. */
.led-1r .led-wrap, .gs-1r .led-wrap{ height:calc(100vh - 94px - var(--mstrip-h)); }

/* ══ VERIFY DATA (S182) ═══════════════════════════════════════════════════
   Two axes, two colours, and they are the app's existing semantics rather than
   anything new: --neg is "wrong, stop", --caution is "careful, look at this".
   An `info` finding - open jobs, open proformas, drafts - is unfinished work,
   not a fault, so it must NEVER wear red. Seventeen open jobs on a Tuesday is
   a business. Red on that and the screen cries wolf and gets ignored.
   Every token below was grepped in foundation.css before it was typed: an
   undefined custom property does not fail, it silently INHERITS. */
/* Verify is a Settings TAB now, so the panel supplies the frame - only the
   run row and the summary need laying out. */
.vf-head { display:flex; align-items:center; gap:.75rem; flex-wrap:wrap;
    margin-bottom:.75rem; }
/* Message takes the room, Run sits hard right (Ali). min-width:0 so a long
   summary wraps inside the flex item instead of pushing the button off. */
.vf-head .vf-summary { flex:1 1 auto; min-width:0; }
.vf-head #verifyRunBtn { flex:0 0 auto; margin-left:auto; }

.vf-summary { font-size:var(--fs-base); font-weight:650; color:var(--ink-2); }
.vf-summary-bad   { color:var(--neg-deep); }
.vf-summary-clean { color:var(--pos-deep); }
/* The checks take ~1.4ms, so without a held working state and a flash on the
   result the button reads as dead - the answer is on screen before the eye
   leaves the pointer (Ali: "it just finished before blink of eye"). */
.vf-working { color:var(--ink-3); }
.vf-flash { animation:vfFlash 900ms ease-out 1; }
@keyframes vfFlash {
    0%   { background:var(--caution-tint); box-shadow:0 0 0 6px var(--caution-tint); }
    100% { background:transparent;         box-shadow:0 0 0 6px transparent; }
}
@media (prefers-reduced-motion:reduce) { .vf-flash { animation:none; } }

#verifyResults { max-height:calc(100vh - 22rem); overflow:auto; }
.vf-card { border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); margin-bottom:.5rem; }
.vf-card > summary { display:flex; align-items:center; gap:.6rem; cursor:pointer;
    padding:.55rem .7rem; font-size:var(--fs-base); color:var(--ink); }
.vf-card[open] > summary { border-bottom:1px solid var(--line-2); }
.vf-title { font-weight:650; }

/* The count is the fill carrying a dark mark, per --caution's own rule. */
.vf-count { min-width:2.1rem; text-align:center; padding:.12rem .4rem;
    border-radius:var(--radius-pill); font-weight:800; font-size:var(--fs-sm); }
.vf-error .vf-count { background:var(--neg-tint);     color:var(--neg-deep); }
.vf-info  .vf-count { background:var(--caution-tint); color:var(--caution-edge); }
.vf-error { border-left:3px solid var(--neg); }
.vf-info  { border-left:3px solid var(--caution); }

.vf-blocks { margin-left:auto; font-size:var(--fs-lbl); font-weight:700;
    text-transform:uppercase; letter-spacing:.06em; color:var(--ink-3); }

.vf-what, .vf-fix { margin:.5rem .7rem; font-size:var(--fs-sm); color:var(--ink-2);
    line-height:1.5; max-width:78ch; }
.vf-fix { color:var(--ink); }

.vf-rows { margin:.3rem .7rem .7rem; padding:0; list-style:none;
    max-height:19rem; overflow:auto; }
.vf-rows li { margin:0 0 .18rem; }
.vf-more { font-size:var(--fs-sm); color:var(--ink-3); padding:.25rem .4rem; }
/* A real <button>: in the tab order, fires on Enter and Space, no key handling
   of our own and so nothing that can trap Shift+Tab's reverse walk. */
.vf-row { display:block; width:100%; text-align:left; font:inherit;
    font-size:var(--fs-sm); color:var(--ink-2); background:none;
    border:1px solid transparent; border-radius:var(--radius-sm);
    padding:.28rem .4rem; cursor:pointer; }
.vf-row:hover { background:var(--brand-tint); color:var(--ink); }
.vf-row:focus-visible { outline:2px solid var(--accent-2); outline-offset:1px; }

/* ── STOCK VALUATION (AR64) ────────────────────────────────────────────────────
   The manual override behind the P&L's Closing Stock and the sheet's
   Stock-in-Hand. Private `sv-` prefix on real tokens, the same shape js_verify's
   `vf-` set uses — NOT .sub-tab, which is pre-makeover crimson (#c0392b) and
   would drop the old palette into a token-themed screen.

   THE DELTA STRIP IS THE ONE LOUD THING HERE and everything else stays quiet.
   A manual valuation moves reported profit, so what the figure BECOMES has to be
   visible before Save is pressed rather than discovered afterwards on the P&L. */
.sv-bar{ display:flex; align-items:center; gap:18px; margin-bottom:14px; flex-wrap:wrap; }
.sv-tabs{ display:flex; gap:6px; }
.sv-tab{ background:var(--surface); border:1px solid var(--line); color:var(--ink-2);
    padding:7px 16px; border-radius:var(--radius-sm); cursor:pointer; font-weight:600;
    font-size:var(--fs-sm); font-family:inherit; }
.sv-tab:hover{ border-color:var(--accent); color:var(--accent); }
.sv-tab.active{ background:var(--accent); border-color:var(--accent); color:#fff; }
.sv-tab:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.sv-ason{ display:flex; align-items:center; gap:8px; font-size:var(--fs-sm);
    font-weight:600; color:var(--ink-2); margin-left:auto; }
#svModal .sv-ason input{ width:auto; height:30px; padding:0 8px; border:1px solid var(--line);
    border-radius:var(--radius-sm); background:var(--surface); color:var(--ink);
    font-family:inherit; font-size:var(--fs-sm); }

/* Cost -> valued, and the distance between them. */
.sv-delta{ display:flex; align-items:baseline; gap:14px; flex-wrap:wrap;
    padding:12px 16px; margin-bottom:12px; border:1px solid var(--line);
    border-radius:var(--radius-sm); background:var(--head); }
.sv-d-part{ display:flex; align-items:baseline; gap:8px; }
.sv-d-lbl{ font-size:var(--fs-lbl); font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; color:var(--ink-3); }
.sv-d-num{ font-size:1.25rem; font-weight:700; color:var(--ink-2);
    font-variant-numeric:tabular-nums; }
.sv-d-now{ color:var(--ink); }
.sv-d-arrow{ color:var(--ink-3); font-size:1.1rem; }
.sv-d-diff{ font-weight:700; font-variant-numeric:tabular-nums; font-size:var(--fs-sm);
    padding:2px 9px; border-radius:999px; }
.sv-d-diff.dn{ background:var(--neg-tint); color:var(--neg-deep); }
.sv-d-diff.up{ background:var(--pos-tint); color:var(--pos-deep); }
.sv-d-cnt{ margin-left:auto; font-size:var(--fs-sm); color:var(--ink-3); }

.sv-wrap{ max-height:46vh; overflow:auto; border:1px solid var(--line);
    border-radius:var(--radius-sm); }
/* border-collapse:SEPARATE, not collapse. With `collapse`, Chrome does not
   honour position:sticky on a table section, so the total row sat at the natural
   end of an 1130px table inside a 520px frame - and every search that shortened
   the table moved it, which is the "it just moves and jolt the screen" Ali saw.
   The row is what he reads the answer off; it must not move.
   border-spacing:0 keeps the collapsed look. */
.sv-table{ width:100%; border-collapse:separate; border-spacing:0;
    font-size:var(--fs-sm); }
.sv-table thead th{ position:sticky; top:0; z-index:2; background:var(--head);
    color:var(--ink-2); font-size:var(--fs-lbl); font-weight:700; letter-spacing:.04em;
    text-transform:uppercase; text-align:left; padding:9px 10px;
    border-bottom:1px solid var(--line); white-space:nowrap; }
.sv-table th.sv-n, .sv-table td.sv-n{ text-align:right; }
.sv-table td{ padding:5px 10px; border-bottom:1px solid var(--line-2); color:var(--ink);
    font-variant-numeric:tabular-nums; }
.sv-table tbody tr:nth-child(even) td{ background:var(--zebra); }
.sv-lot{ font-weight:600; white-space:nowrap; }
/* A job-output lot is priced off its own rate, not a purchase rate. Saying so
   costs one small word and saves opening the lot to find out. */
.sv-src{ margin-left:6px; font-size:var(--fs-lbl); font-weight:600; color:var(--ink-3);
    border:1px solid var(--line); border-radius:3px; padding:0 4px; }
.sv-cost{ color:var(--ink-3); }
/* #svModal, not .sv-in alone: `.mst-modal input[type="number"]` sets width:100%
   at (0,2,1) and beat a bare `.sv-in input` at (0,1,1) — the number box rendered
   297px against the 92px written here. Measured in the browser, not guessed. */
#svModal .sv-in input{ width:92px; height:27px; text-align:right; padding:0 7px;
    border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); color:var(--ink); font-family:inherit;
    font-size:var(--fs-sm); font-variant-numeric:tabular-nums; }
/* EACH BOX IS SIZED TO WHAT GOES IN IT, and the bags box is the reason. Three
   money-width boxes plus a fourth column took the table to 1545px inside a
   1442px frame and cut the Remark column in half — measured, not eyeballed.
   A bag count is four digits at the very most; a rate is 12345.00 and a weight
   12345.60, so neither needs the 92px a rupee total does. */
#svModal .sv-in input[data-f="bags"]{ width:46px; }
#svModal .sv-in input[data-f="rate"]{ width:74px; }
#svModal .sv-in input[data-f="wt"]{ width:84px; }
/* The Valued group carries four columns now, so it gets the tighter padding the
   identity codes already use. */
#svModal .sv-table td.sv-g-b, #svModal .sv-table th.sv-g-b{
    padding-left:5px; padding-right:5px; }
.sv-in input::placeholder{ color:var(--ink-3); }
.sv-in input:focus{ outline:2px solid var(--accent); outline-offset:0; border-color:var(--accent); }
/* The Opening tab (S214): typed identity on a row added this session, the x that removes a
   lot, the lock dot on a lot a job or sale has drawn from, the blank Actual of an added lot. */
#svModal .sv-in select{ font-family:inherit; font-size:var(--fs-sm); padding:3px 4px; border:1px solid var(--line); border-radius:6px; background:var(--surface); color:var(--ink); width:auto; min-width:58px; }
#svModal .sv-in input[data-f="party_code"], #svModal .sv-in input[data-f="lot_number"]{ width:72px; text-transform:uppercase; }
.sv-rem-x{ white-space:nowrap; }
.sv-rem-x input{ width:calc(100% - 22px); }
.sv-x{ border:0; background:none; color:var(--ink-3); font-size:1.05em; line-height:1; padding:0 4px; cursor:pointer; vertical-align:middle; }
.sv-x:hover{ color:var(--neg-deep); }
.sv-lockdot{ color:var(--warn-deep); font-size:.7em; vertical-align:middle; }
.sv-row.sv-moved td{ background:var(--warn-tint); }
.sv-row.sv-new td{ background:var(--accent-tint); }
.sv-row.sv-bad-row td{ outline:2px solid var(--neg-deep); }
.sv-added{ text-align:center !important; color:var(--ink-3); font-size:var(--fs-lbl); letter-spacing:.03em; text-transform:uppercase; }
.sv-split{ display:block; margin-top:4px; color:var(--ink); }
.sv-head .sv-add{ white-space:nowrap; }
#svModal .sv-in input:disabled{ background:var(--head); color:var(--ink-3); cursor:not-allowed; }
/* min-width 104, not 112: the Remark box is the LAST column, so it is the one a
   7px overflow cuts in half. It still holds a readable phrase and grows with any
   slack the table has. */
#svModal .sv-rem input{ width:100%; min-width:104px; height:27px; padding:0 8px;
    border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); color:var(--ink); font-family:inherit; font-size:var(--fs-sm); }
.sv-val{ font-weight:600; }
/* ONLY A ROW THE USER ACTUALLY TYPED IN LOOKS TOUCHED. Every other lot is
   written at cost by the server, so marking them all would be a lie about what
   was done — and the count in the delta strip would stop meaning anything. */
.sv-row.is-set td{ background:var(--accent-tint) !important; }
.sv-row.is-set .sv-val{ color:var(--accent-2); }
.sv-table tfoot td{ padding:9px 10px; border-top:2px solid var(--line);
    background:var(--head); font-weight:700; color:var(--ink);
    font-variant-numeric:tabular-nums; position:sticky; bottom:0; }
.sv-empty{ padding:22px 10px !important; text-align:center; color:var(--ink-3); }
.sv-bad{ color:var(--neg-deep); }

.sv-gen{ display:block; margin-top:14px; font-size:var(--fs-sm); font-weight:600;
    color:var(--ink-2); }
.sv-gen input{ display:block; width:100%; margin-top:5px; height:32px; padding:0 10px;
    border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); color:var(--ink); font-family:inherit; font-size:var(--fs-sm);
    font-weight:400; }
.sv-gen input:focus{ outline:2px solid var(--accent); outline-offset:0; border-color:var(--accent); }
.sv-hint{ margin-top:6px; font-size:var(--fs-lbl); color:var(--ink-3); font-weight:400; }
@media (prefers-reduced-motion:reduce){ .sv-tab{ transition:none !important; } }
/* The lot's own identity, under its number: item, then the date it came in.
   Added after driving the screen - lot numbers repeat across purchases and five
   rows read an identical "3". */
.sv-item{ margin-left:6px; font-weight:400; color:var(--ink-3); }
.sv-when{ color:var(--ink-3); white-space:nowrap; }

/* ── AR64 two-group table: Actual | Valued | Difference ───────────────────────
   The lot selector's own shape - identity columns left to right, then the group
   you work in. There it is Available / Add; here it is Actual / Valued, because
   the job is editing rather than adding. */
.sv-table thead tr.sv-grp th{ text-align:center; font-size:var(--fs-lbl);
    letter-spacing:.06em; padding:6px 10px 4px; border-bottom:1px solid var(--line); }
.sv-g-a{ background:var(--zebra) !important; }
.sv-g-b{ background:var(--head) !important; }
.sv-g-d{ background:var(--head) !important; }
.sv-table td.sv-g-a{ color:var(--ink-2); }
/* The group tints must survive the zebra stripe, which is why they carry
   !important - a striped row would otherwise repaint the group columns and the
   two halves would stop reading as two halves. */
.sv-table tbody tr:nth-child(even) td.sv-g-a,
.sv-table tbody tr:nth-child(even) td.sv-g-b,
.sv-table tbody tr:nth-child(even) td.sv-g-d{ filter:brightness(0.985); }
.sv-diff{ font-weight:700; }
.sv-diff.dn{ color:var(--neg-deep); }
.sv-diff.up{ color:var(--pos-deep); }
#svFootDiff.dn{ color:var(--neg-deep); }
#svFootDiff.up{ color:var(--pos-deep); }
.sv-party{ max-width:64px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sv-src-c{ font-size:var(--fs-lbl); font-weight:700; color:var(--ink-3); letter-spacing:.03em; white-space:nowrap; } /* OP-STK ⇣ wrapped to two lines */
#svModal .sv-search{ min-width:240px; margin-left:auto; }
#svModal .sv-search input{ width:100%; height:30px; }
.sv-ason{ margin-left:0 !important; }

/* 16 columns need more than modal-xlarge's 1300px. Scoped to this modal so the
   other 17 popups keep their own width. */
#svModal .modal-content{ max-width:min(1640px, 97vw); }

/* Measured, not guessed: 16 columns ran 45px past the wrap at rest. The party
   name is the one column already ellipsising, so it gives up the width rather
   than every figure column losing a few px each. */
#svModal .sv-table td, #svModal .sv-table th{ padding-left:8px; padding-right:8px; }

/* ── AR64 layout pass 2 (Ali, 2026-09-06) ─────────────────────────────────────
   "i am barely able to see 7 lots, so much space is not used." The tab row, the
   search, the date and a whole cost/valued band sat ABOVE the table in four
   stacked strips while the header row carried a title and nothing else. Controls
   moved into the header; the band is gone and its two figures now live in the
   total row, which was already on screen. */
.sv-head{ display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
.sv-head .modal-header-left{ flex:0 0 auto; }
.sv-head .sv-tabs{ flex:0 0 auto; }
#svModal .sv-head .sv-search{ flex:1 1 200px; min-width:180px; margin-left:0; }
.sv-head .sv-ason{ flex:0 0 auto; margin-left:0 !important; }
.sv-head .close{ flex:0 0 auto; margin-left:4px; }
/* The list gets the height the strips gave up. */
/* A FIXED HEIGHT, NOT A MAX. Ali, 2026-09-06: "i belive we have not given any
   fixed height to table on every search when it is there or not it just moves and
   jolt the screen." With max-height the wrap shrank to fit whatever the filter
   left, so every keystroke in the search box resized the table and moved the
   total row, the remarks line and the buttons under the cursor. A fixed height
   means the list scrolls inside a frame that never moves. */
.sv-wrap{ height:min(58vh, 520px); max-height:none; }
/* THE TOTAL ROW IS THE SUMMARY. "we already have total at bottom we can highlight
   the cost and final value there only." */
.sv-foot-lbl{ text-align:left; }
.sv-foot-cnt{ margin-left:12px; font-weight:400; color:var(--ink-3);
    font-size:var(--fs-lbl); }
.sv-table tfoot td.sv-big{ font-size:1.06rem; }
/* The hint took the label's line rather than adding one. */
.sv-gen{ margin-top:10px; }
#svModal .sv-gen input{ margin-top:0; }
.sv-hint{ display:block; }
/* PINK IS OUT. Ali: "our P&L and BS are green sand and stock is sand ... also
   pink color value look odd." --accent-tint is the app's warm clay and read as
   pink against the statements' sand; the two groups are now separated by a rule
   and a sand ground, not by a second hue. */
.sv-table td.sv-g-b, .sv-table th.sv-g-b{ border-left:1px solid var(--line); }
.sv-table td.sv-g-d, .sv-table th.sv-g-d{ border-left:1px solid var(--line); }
.sv-row.is-set td{ background:var(--warn-tint) !important; }
.sv-row.is-set .sv-val{ color:var(--warn-deep); }

/* GDN and EMP are codes, never long. Giving them the identity block's default
   padding cost 80px the table did not have once they were added; measured, then
   taken from the party name (which already ellipsises) and these two. */
#svModal .sv-table td.sv-code, #svModal .sv-table th.sv-code{ padding-left:5px; padding-right:5px; }
#svModal .sv-table td.sv-when, #svModal .sv-table td.sv-src-c{ padding-right:5px; }

/* THE NINE IDENTITY COLUMNS ARE CENTRED, header and cell alike - No, Date, GDN,
   EMP, Source, Party Name, Code, Item, Lot. Left in the header, left in a saved
   row and right inside a NEW row's typed box was three alignments in one column
   (Ali, S215: "all field label and field are not properly aligned"). The th
   carries the same class as its td, so one selector reaches both; the figure
   columns keep sv-n (right). */
#svModal .sv-table th.sv-no, #svModal .sv-table td.sv-no,
#svModal .sv-table th.sv-when, #svModal .sv-table td.sv-when,
#svModal .sv-table th.sv-code, #svModal .sv-table td.sv-code,
#svModal .sv-table th.sv-src-c, #svModal .sv-table td.sv-src-c,
#svModal .sv-table th.sv-party, #svModal .sv-table td.sv-party,
#svModal .sv-table th.sv-item-c, #svModal .sv-table td.sv-item-c,
#svModal .sv-table th.sv-lot, #svModal .sv-table td.sv-lot{ text-align:center; }
#svModal .sv-in input[data-f="party_code"], #svModal .sv-in input[data-f="lot_number"],
#svModal .sv-in select{ text-align:center; text-align-last:center; }

/* The sticky had to go on the SECTION, not only its cells. thead worked with th
   alone; tfoot did not - it stayed at the natural end of an 1130px table inside a
   520px frame, so every search that shortened the list moved the one row Ali
   reads the answer off. Measured before and after, not assumed. */
#svModal .sv-table tfoot{ position:sticky; bottom:0; z-index:3; }
#svModal .sv-table tfoot tr{ position:sticky; bottom:0; }
#svModal .sv-table tfoot td{ z-index:3; }

/* ROW FOCUS. Ali: "i need row focus ... we can have row focus and use keyboard".
   A focused row is a place you are standing, so it is marked on the row itself
   rather than by an outline that a 18-column row would draw round the whole
   screen edge. */
/* AMBER, NOT THE ACCENT. Ali, 2026-09-06: "has some red tint we havew amber."
   --accent is this app's warm clay and reads red on a row-wide fill, which on a
   money screen looks like a warning about the row rather than a cursor position.
   --caution is the amber the app already owns.
   :FOCUS-WITHIN, not :focus - the cursor now lands in the row's Rate box rather
   than on the row itself, so the highlight has to follow the box. Bare :focus is
   kept alongside it for the one case that still lands on the row: Escape. */
.sv-row:focus, .sv-row:focus-within{ outline:none; }
.sv-row:focus > td, .sv-row:focus-within > td{
    background:var(--caution-tint) !important;
    box-shadow:inset 0 -1px 0 var(--caution-edge), inset 0 1px 0 var(--caution-edge); }
.sv-row:focus > td:first-child, .sv-row:focus-within > td:first-child{
    box-shadow:inset 3px 0 0 var(--caution-edge),
    inset 0 -1px 0 var(--caution-edge), inset 0 1px 0 var(--caution-edge); }
/* The filler takes the slack so the sticky total never rises to meet a short list. */
.sv-table tbody tr.sv-fill td{ height:100%; border:none; background:transparent !important; padding:0; }

/* overflow-y:SCROLL, not auto. The last 11px of jolt was the scrollbar itself:
   with 27 rows the vertical bar took 11px of width, which pushed the 1442px table
   over the edge and added a horizontal bar, costing 11px of height; filtered to
   three rows both vanished and the total row dropped by exactly that. Reserving
   the gutter makes the frame the same size whatever the filter finds.
   Measured 574 / 585 before, 574 / 574 after. */
#svModal .sv-wrap{ overflow-y:scroll; }

/* A FIXED HEIGHT ON THE TOTAL ROW. Its bottom edge was already steady once the
   filler row landed, but the row itself rendered 42px while the list scrolled and
   31px when the filter left it short - so its top still moved 11px. Measured, not
   eyeballed: footTop 574 vs 585 against an identical bottom of 616. */
#svModal .sv-table tfoot td{ height:42px; }

/* The filler's height is set in JS, not by height:100%. A percentage height on
   the table resolved against the wrap's BORDER box while the rows live in its
   CONTENT box, so a filtered list came out 11px too tall and pushed the total row
   down by exactly that. Measuring the gap and setting it is exact; a percentage
   was close and wrong. */

/* THE HEADER MUST NOT RESIZE WHEN THE COUNT APPEARS. Ali: "the moment i press an
   alphabet the header size increases" - in the search box. Measured: 61px -> 72px
   the instant "25 of 27" rendered. The input was width:100% of the search box, so
   the 43px count had nowhere to sit and pushed the row into a wrap.
   Two halves: the input SHRINKS to make room, and the count's slot is reserved
   whether or not it holds anything - so the layout is identical empty or full and
   nothing moves while you type. */
#svModal .sv-head .sv-search{ position:relative; }
#svModal .sv-head .sv-search input{ width:100%; padding-right:64px; }
/* The count sits INSIDE the box, on the right - the same place the P&L's own
   search puts it, and the reason it is absolutely positioned is that an in-flow
   count is what pushed the header into a second line. Out of flow, it cannot
   change the layout whether it is empty or reads "25 of 27". */
#svModal .sv-head #svCount{ position:absolute; right:10px; top:50%;
    transform:translateY(-50%); float:none; pointer-events:none;
    white-space:nowrap; }

/* The box's own ring goes amber too. A clay ring sitting inside an amber row was
   the same red Ali flagged, just smaller - and the two markers should read as one
   cursor, not two colours arguing. */
#svModal .sv-in input:focus{ outline-color:var(--caution-edge);
    border-color:var(--caution-edge); }

/* ══ THE DOOR — login and the company chooser ══════════════════════════════
   ONE CARD, TWO PANELS. Signing in does not dismiss this card; its contents
   change from the password to the list of books. The door, then the shelf
   behind it. That is honest about the flow - you are not "logging into the
   app", you are opening a particular book - and it is why the card keeps its
   size and position across the swap instead of one overlay replacing another.

   A ONE-COMPANY USER NEVER SEES THE SECOND PANEL (A6), exactly as a one-user
   install never types a username: one of anything is SHOWN, not asked.

   IT FOLLOWS THE USER'S OWN THEME. The Ground/Accent attributes are stamped on
   <html> by the inline script in <head>, before this stylesheet loads, so every
   value here is a token and a charcoal-ground user gets a charcoal door. The
   screen this replaced was a hardcoded crimson gradient (#922b21/#c0392b) that
   belonged to no token in the system and had never been through the design
   pass - Ali: "we have not updated login page also". */
/* THE TOPMOST THING IN THE APP, above the locked-record popup (9999999) and the
   999999 dropdowns. Nothing behind this card is usable - with no company chosen
   every request 500s - so it cannot be allowed to lose a stacking contest. It
   drew UNDER the "Set Up Backup" gate the first time it was measured: both were
   99999 and the backup modal is later in the DOM, so equal z-index handed it
   the front. */
.lg-scrim{ position:fixed; inset:0; z-index:10000000;
    display:flex; align-items:center; justify-content:center; padding:var(--sp-4);
    background:linear-gradient(150deg, var(--brand) 0%, var(--brand-2) 100%); }

/* The card is the app's card: warm paper, never pure white. */
.lg-card{ width:22.5rem; max-width:100%; background:var(--surface);
    border-radius:var(--radius); box-shadow:var(--shadow-pop);
    padding:var(--sp-5) var(--sp-5) var(--sp-4); }

/* The same mark, in the same tile, as the sidebar brand block - so the door and
   the app read as one product rather than two designs meeting at login. */
.lg-mark{ width:2.6rem; height:2.6rem; border-radius:9px; margin:0 auto var(--sp-3);
    display:flex; align-items:center; justify-content:center;
    background:var(--brand); color:var(--brand-ink); }
.lg-mark .ic{ width:1.5rem; height:1.5rem; }
.lg-title{ font-size:var(--fs-h1); font-weight:700; color:var(--ink);
    text-align:center; line-height:1.2; }
.lg-sub{ font-size:var(--fs-sm); color:var(--ink-3); text-align:center;
    margin-top:var(--sp-1); }

.lg-panel{ margin-top:var(--sp-5); }
/* One crossfade, and only when the panel actually changes. No entrance
   animation on load: the card is the first thing on screen and has nothing to
   arrive from. */
.lg-panel.is-entering{ animation:lgFade .18s ease-out; }
@keyframes lgFade{ from{ opacity:0; transform:translateY(4px); } to{ opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce){ .lg-panel.is-entering{ animation:none; } }

.lg-f{ margin-bottom:var(--sp-3); }
.lg-f label{ display:block; font-size:var(--fs-sm); font-weight:600;
    color:var(--ink-2); margin-bottom:var(--sp-1); }
.lg-f input{ width:100%; padding:.6rem .7rem; font-family:inherit;
    font-size:var(--fs-base); color:var(--ink); background:var(--surface);
    border:1.5px solid var(--line); border-radius:var(--radius-sm); }
.lg-f input:focus{ outline:none; border-color:var(--accent);
    box-shadow:0 0 0 3px var(--accent-tint); }
/* The password is the only field most people fill, so it carries the tracking
   that makes a masked string countable. The username does not - a name is read,
   not counted. */
.lg-f input[type=password]{ letter-spacing:.14em; }

/* Signed in as: the single-user answer to "there is no user name in login".
   The name is stated rather than typed, because typing the only username there
   is is friction with nothing on the other side of it. The field returns the
   moment there is a second user. */
.lg-who{ display:flex; align-items:baseline; gap:.4rem; margin-bottom:var(--sp-3);
    padding-bottom:var(--sp-2); border-bottom:1px solid var(--line-2); }
.lg-who-name{ font-size:var(--fs-base); font-weight:600; color:var(--ink); }
.lg-who-role{ font-size:var(--fs-sm); color:var(--ink-3); }

.lg-go{ width:100%; padding:.65rem; font-family:inherit; font-size:var(--fs-base);
    font-weight:700; color:#fff; background:var(--accent); border:none;
    border-radius:var(--radius-sm); cursor:pointer; }
.lg-go:hover{ background:var(--accent-2); }
.lg-go:focus-visible{ outline:2px solid var(--accent-2); outline-offset:2px; }

/* Errors state what happened and stay put; the row is always in the layout so
   the card does not jump when one appears. */
.lg-err{ min-height:1.2rem; margin:var(--sp-2) 0 0; font-size:var(--fs-sm);
    font-weight:600; color:var(--neg-deep); text-align:center; }

/* IDLE LOGOUT (group 4). The card that comes up over a LIVE screen must win
   against anything that screen had open - the app's own confirm box (.acnf) is
   also 10000000 and can sit later in the DOM - so it takes one step more. */
.lg-scrim.lg-scrim-idle{ z-index:10000002; }

/* The 5-minute warning: a card at the top centre, above every popup, never
   taking the focus - whoever comes back mid-sentence keeps typing where they
   were, and that key is what dismisses it. */
.idle-warn{ position:fixed; top:var(--sp-4); left:50%; transform:translateX(-50%);
    z-index:10000001; width:22rem; max-width:calc(100% - 2rem);
    background:var(--surface); border:1px solid var(--line);
    border-top:3px solid var(--warn-deep); border-radius:var(--radius);
    box-shadow:var(--shadow-pop); padding:var(--sp-4); text-align:center; }
.idle-warn-t{ font-size:var(--fs-base); font-weight:700; color:var(--ink); }
.idle-warn-t b{ font-variant-numeric:tabular-nums; }
.idle-warn-s{ font-size:var(--fs-sm); color:var(--ink-3); margin:var(--sp-1) 0 var(--sp-3); }

/* ── The shelf ──────────────────────────────────────────────────────────────
   Hairline-separated rows, not a stack of shadowed cards: these are lines in a
   list of books, and the card they sit in is already the object. */
.co-list{ margin:0 calc(-1 * var(--sp-5)); border-top:1px solid var(--line-2); }
.co-row{ display:flex; align-items:center; gap:var(--sp-3); width:100%;
    padding:.7rem var(--sp-5); text-align:left; cursor:pointer;
    background:none; border:none; border-bottom:1px solid var(--line-2);
    font-family:inherit; }
.co-row:hover{ background:var(--brand-tint); }
.co-row:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }
.co-main{ flex:1; min-width:0; }
.co-name{ font-size:var(--fs-base); font-weight:600; color:var(--ink);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* The books period is what actually tells two companies apart once a split has
   given them the same name, so it sits directly under the name rather than
   with the number. */
.co-period{ font-size:var(--fs-sm); color:var(--ink-2); margin-top:1px; }
/* The number is the identity and the folder name on disk - findable, never the
   thing you read first. */
.co-no{ font-size:var(--fs-sm); color:var(--ink-3); flex-shrink:0; }
.co-lock{ display:inline-flex; align-items:center; gap:.25rem; flex-shrink:0;
    padding:.1rem .45rem; border-radius:var(--radius-pill);
    font-size:var(--fs-sm); font-weight:600;
    color:var(--warn-deep); background:var(--warn-tint); }
.co-lock .ic{ width:.85rem; height:.85rem; }
.co-empty{ font-size:var(--fs-sm); color:var(--ink-3); text-align:center;
    padding:var(--sp-4) 0; }

/* A stated value in a Settings field row, where an <input> would sit. Same
   metrics as the inputs beside it so the labels line up. */
.set-who-v{ padding:.35rem 0; font-size:var(--fs-base); color:var(--ink); font-weight:600; }
/* Section headings inside the clubbed Data tab. .mstp-grp brings its own h4;
   these two sections are not field groups, so they carry their own. */
.set-sec-h{ margin:var(--sp-5) 0 var(--sp-3); font-size:var(--fs-base);
    font-weight:700; color:var(--ink); }
.set-sec-h-danger{ color:var(--neg-deep); }

/* ── Settings > Data: four sections, ONE column edge ───────────────────────
   Clubbing Backup, Import, Verify and Data Reset onto one page put four
   different layout systems under one heading: measured at 820, 621, 1375 and
   1375px wide, all starting at the same left edge, so the page read as three
   unrelated pages stacked. .mstp-grid's two columns are right for a form with
   many short field groups; here there are four SECTIONS, and a section that
   stops short of the one above it looks unfinished rather than airy.
   ONE RIGHT EDGE, ACROSS THE WHOLE PANEL. A single narrow column lined the
   sections up but left half the screen empty (Ali, on the first build: "left
   side is full and right side screen space seems empty we can utilise it").
   So the pairs go side by side - Backup beside Import, Clear beside Factory -
   and Verify keeps the full width, because its findings list is the one thing
   here that is a table. .mstp-grid's own 900px cap is what held it in; the cap
   is right for a master form and wrong for a page of sections.

   IT MUST RESTATE display:grid. Inside a Settings tab .mstp-grid is a FLEX
   COLUMN (.set-tabpanel .mstp-grid, ~line 1978), so setting only the columns
   and the width leaves the flex intact and the grid-template-columns inert -
   which measured as four sections at 1375px stacked on top of each other. The
   same trap is already written up above #setTabCompany's rule; it was in the
   file before this and worth reading twice. */
#setTabData .mstp-grid{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
    gap:11px; max-width:none; align-items:start; }
/* The first section carries the tab's own top edge, so it needs no heading gap. */
#setTabData > .mstp-grid:first-child{ margin-top:0; }

/* ── Settings > Alerts: four boxes, 2x2 ─────────────────────────────────────
   Ali: "we can make it 4 boxes side by side, top left, top right, bottom left
   and bottom right... put weighbridge at top right beside proforma." Same
   restate-display:grid trap as #setTabData just above - .set-tabpanel
   .mstp-grid is a flex column, so this must repeat the whole rule, not just
   the columns. DOM order decides position in a 2-col grid, which is why the
   Weighbridge box is inserted as the SECOND child in the template (right
   after Proforma & conversion) rather than placed with CSS. */
/* STRETCHED, not align-items:start - Ali: "match jobs alerts boxes, both are
   of different heights." The two Job boxes' description paragraphs are
   different lengths, and align-items:start (right for #setTabData/#setTabSplit,
   whose sections are independent) left each box sized to only its own content.
   The CSS Grid default (stretch) makes every row's pair match its taller box -
   both rows, not just the one Ali named. */
#setTabAlerts .mstp-grid{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
    gap:11px; max-width:none; }
#setTabAlerts .mstp-grp{ display:flex; flex-direction:column; }
#setTabAlerts .mstp-grp .mstp-fields{ flex:1; }
@media (max-width:980px){ #setTabAlerts .mstp-grid{ grid-template-columns:1fr; } }

/* ── Settings > Defaults: four boxes, 2x2, all one size ─────────────────────
   Ali, S211: "we have 4 boxes on one side, we can use side by side ... make
   all box of same size." Same restate-display:grid trap as the two above;
   grid-auto-rows:1fr makes BOTH rows as tall as the tallest box, so the four
   match, not just each row's pair. */
#setTabDefaults .mstp-grid{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
    grid-auto-rows:1fr; align-items:stretch; gap:11px; max-width:none; }
#setTabDefaults .mstp-grp{ display:flex; flex-direction:column; }
#setTabDefaults .mstp-grp .mstp-fields{ flex:1; }
@media (max-width:980px){ #setTabDefaults .mstp-grid{ grid-template-columns:1fr; grid-auto-rows:auto; } }

/* The last row's rule would draw a line across the bottom of the card, just
   inside its rounded corner. */
.co-row:last-child{ border-bottom:none; }

/* ── Settings > Split ──────────────────────────────────────────────────────
   The most consequential screen in the product, so it reads top to bottom in
   the order the decision is actually made: what the new book will be, what
   carries, what is in the way, and only then the button. */
#setTabSplit{ max-width:none; }
#setTabSplit .mstp-grid{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
    gap:11px; max-width:none; align-items:start; }
.sp-lede{ max-width:62ch; margin-bottom:var(--sp-4); font-size:var(--fs-sm);
    color:var(--ink-2); line-height:1.5; }

.sp-note{ font-size:var(--fs-sm); color:var(--ink-3); line-height:1.45; }

.sp-preview{ font-size:var(--fs-base); }
.sp-pv-row{ display:flex; align-items:baseline; gap:var(--sp-3);
    padding:.3rem 0; border-bottom:1px solid var(--line-2); }
.sp-pv-row:last-child{ border-bottom:none; }
.sp-pv-l{ flex:1; color:var(--ink-2); }
.sp-pv-v{ font-weight:600; color:var(--ink); text-align:right; }

/* A BLOCKING ITEM IS NOT AN ERROR. Seventeen open jobs on a Tuesday is a
   business, not a fault - it only blocks a CLOSE. So these are amber, the
   app's caution colour, never the red it uses for something wrong. */
.sp-block{ padding:.6rem .8rem; margin-bottom:8px; border-radius:var(--radius-sm);
    background:var(--warn-tint); border:1px solid var(--caution-tint); }
.sp-block-head{ display:flex; align-items:baseline; gap:.5rem; }
.sp-block-title{ font-weight:700; color:var(--warn-deep); }
.sp-block-count{ font-weight:700; color:var(--warn-deep);
    background:var(--surface); border-radius:var(--radius-pill); padding:0 .5rem;
    font-size:var(--fs-sm); }
.sp-block-what{ margin-top:.2rem; font-size:var(--fs-sm); color:var(--ink-2); }
.sp-block-fix{ margin-top:.15rem; font-size:var(--fs-sm); color:var(--ink-3); }
.sp-ok{ padding:.6rem .8rem; border-radius:var(--radius-sm);
    background:var(--pos-tint); color:var(--pos-deep); font-weight:600; }
.sp-warn{ margin-top:.5rem; padding:.45rem .6rem; border-radius:var(--radius-sm);
    background:var(--warn-tint); color:var(--warn-deep); font-size:var(--fs-sm); }

/* Step 1 of the split (S211): the Run Verify button and its one-line answer. */
.sp-verify-bar{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap; margin-bottom:var(--sp-2); }
.sp-go{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
    margin-top:var(--sp-5); padding-top:var(--sp-4); border-top:1px solid var(--line); }
.sp-go input{ width:14rem; padding:.5rem .6rem; font-family:inherit;
    font-size:var(--fs-base); color:var(--ink); background:var(--surface);
    border:1.5px solid var(--line); border-radius:var(--radius-sm); }
.sp-go input:focus{ outline:none; border-color:var(--accent);
    box-shadow:0 0 0 3px var(--accent-tint); }
.sp-go-note{ flex:1 1 100%; font-size:var(--fs-sm); color:var(--ink-3); }

/* ── SETTINGS > COMPANY: the hub's back link (S188) ────────────────────────
   The Company tab is a landing with pages behind it (Tally's K: Company menu),
   so each page needs a way back. .btn-ji-back is the app's existing back
   control - borrowed whole rather than restyled; this rule only gives it the
   breathing room a page-top control needs, since every other use of it sits
   inside a toolbar that already spaces it. */
.coy-back { margin:0 0 var(--sp-4); }

/* ── All companies (Settings > Company > All companies) ─────────────────────
   The SAME hairline-row shape as the login shelf (.co-*), deliberately: they
   are the same books and should read as the same list. What differs is what a
   row DOES - the shelf's row is one button that leaves, this one is a line with
   named buttons on it - so the classes are separate rather than shared. See
   coyRenderList()'s header for why they must not be one renderer.

   The number leads here, unlike the shelf. On the shelf you are picking a book
   to walk into and the name is the thing; here you are looking at the set, and
   the number is the sort order, the folder on disk and the thing Ali says out
   loud ("100002"). */
.coy-list{ border-top:1px solid var(--line-2); }
.coy-row{ display:flex; align-items:center; gap:var(--sp-3);
    padding:.6rem .2rem; border-bottom:1px solid var(--line-2);
    border-left:3px solid transparent; }
/* THE BOOK YOU ARE IN HAS TO BE UNMISTAKABLE. Ali, S189: "when u show company
   there is no indiction tht we are running these company." It had the tint and
   a pale badge, and both lost to the Locked pill beside them - the row that
   matters least shouted and the row you are standing in whispered. Three
   signals now, not one: the accent bar, the tint, and a SOLID badge. */
.coy-row.is-current{ background:var(--brand-tint); border-left-color:var(--pos-deep); }
.coy-r-no{ font-size:var(--fs-sm); color:var(--ink-3); font-weight:600;
    flex-shrink:0; min-width:3.6rem; }
.coy-r-main{ flex:1; min-width:0; }
.coy-r-name{ font-size:var(--fs-base); font-weight:600; color:var(--ink);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.coy-r-period{ font-size:var(--fs-sm); color:var(--ink-2); margin-top:1px; }
/* Locked keeps its own column so the buttons stay in one vertical line down the
   list whether or not a row carries the pill. */
.coy-r-state{ display:flex; align-items:center; gap:.35rem; flex-shrink:0;
    min-width:5.5rem; justify-content:flex-end; }
.coy-r-lock{ display:inline-flex; align-items:center; gap:.25rem;
    padding:.1rem .45rem; border-radius:var(--radius-pill);
    font-size:var(--fs-sm); font-weight:600;
    color:var(--warn-deep); background:var(--warn-tint); }
.coy-r-lock .ic{ width:.85rem; height:.85rem; }
/* SOLID, not the pale tint the Locked pill uses. It lost to Locked at a glance
   when both were tints, and this is the one fact on the page you must never
   have to hunt for: which book am I about to act on.

   It lives in the Load button's own grid cell and fills it, so the column reads
   Load / Loaded / Load straight down with nothing shifting. Same box as the
   button, deliberately NOT a button - it does nothing, it says what is. */
.coy-r-here{ display:flex; align-items:center; justify-content:center;
    border-radius:var(--radius-pill); font-size:var(--fs-sm); font-weight:700;
    white-space:nowrap; color:#fff; background:var(--pos-deep); }
/* TWO FIXED SLOTS, NOT A FLEX ROW. The book you are in has no Open button -
   pressing it would reload the page to arrive where you already stand - and
   with flex its lone Lock slid right, landing under the OTHER rows' Open. One
   press away from the wrong button on a control that locks a year. The slots
   are fixed-width for the same reason: Lock and Unlock are different lengths,
   so without it every row's Open would shift as its neighbour toggled. */
.coy-r-acts{ display:grid; grid-template-columns:5.4rem 5.4rem; gap:.4rem;
    flex-shrink:0; }
.coy-r-btn{ width:100%; }
.coy-empty{ font-size:var(--fs-sm); color:var(--ink-3); padding:var(--sp-4) 0; }

/* ── PURCHASE WEIGHBRIDGE (CJ109 + BF79, S189) ──────────────────────────────
   NO TABLE STYLE OF ITS OWN. The list is a .pur-view-table inside a
   .pur-view-table-wrap under a .pur-view-topbar and .pur-view-filter-bar -- the
   app's shared chrome, the same classes Masters, Jobs and the Purchase ledger
   use. The first build invented a parallel look and Ali named it exactly: "we
   have green and sand, but weigh bridge does not have it." Everything below is
   column WIDTHS and the two things this screen genuinely adds: the band colours
   and the day page. */
/* MEASURED, not guessed. The purchase list runs 1158px of fixed columns and
   fits, because Party is elastic and yields width before the table can outgrow
   its container; Remarks is this table's equivalent. These twelve total 880px,
   so there is real headroom -- which is why Bags, Purchased and Weighbridge stay
   spelled out instead of becoming Pur Wt / WB Wt. With six-figure weights the
   HEADER sets each width, not the number, so the two-row grouped header earns
   its space twice: it explains the columns AND lets each one be narrower. */
/* HEADERS CENTRED, LIKE THE JOBS LIST (Ali, S191: "heeader lines are left
   aligned we need center"). The weighbridge table borrows .pur-view-table, whose
   headers follow their column's body alignment - left for text, right for
   numbers. That is right for a flat header row and wrong for a GROUPED one: a
   group label sits over two or three columns and belongs over the middle of
   them, not jammed against one edge. .jobs-view-table already decided this and
   centres even its .r headers (see its note); this is the same decision, scoped
   to this table so the Purchase list Ali asked me to leave alone is untouched.
   BODY cells keep their own alignment - those rules are on td, not thead.
   ONE LINE, not three: #wbTable's ID already beats the base left default AND
   the .r/.c overrides, exactly as the note on #purchViewTable's own centring
   rule explains. Grid lines were considered alongside this and Ali said leave
   them - the dividers stay a Purchase-list-only thing. */
#wbTable thead th { text-align:center; }

/* EVERY COLUMN CENTRED, BODY AND FOOT TOO (Ali, S191: "need all columns center
   aligned also there headers too"). This table is a reconciliation grid - twelve
   short figures read across, not a ledger scanned down a money column - so
   centring keeps each value under its own header instead of drifting to the
   column edges.
   IT ALSO FIXES A REAL BUG. .pur-view-table's alignment rules are scoped to
   tbody (`tbody td.r`, `tbody td.c`), so the GRAND row in TFOOT matched none of
   them and fell back to the left default: its Slips figure sat a column-width
   away from the Slips values above it. Ali spotted it in a screenshot after I
   had measured the cells' BOUNDS, found them identical, and wrongly concluded
   the alignment was fine - the boxes lined up, the text inside them did not.
   Measure what is actually in question.
   Two exceptions, both prose rather than figures: Remarks is free text in the
   elastic column, and the GRAND label is a caption, not a value. */
#wbTable tbody td,
#wbTable tfoot td { text-align:center; }
#wbTable tbody td.wbc-rem,
#wbTable tfoot td.wbc-rem,
#wbTable tfoot td.pv-foot-label { text-align:left; }

/* SIZED TO THE REAL RANGE OF EACH FIGURE, from Ali (S191): "slips will not be
   more than 2 figure, like 99 also will be to much" and "max bag is 4000". So
   Slips holds 2 digits and Bags 4 - neither needs the room a 6-figure weight
   does - and the weights get the 6 figures he asked for. Everything freed goes
   to Remarks, which is the auto column: "we can give some space to remarks".
   These land on the <col> elements wbRenderHead() emits - under
   table-layout:fixed with a GROUPED header there is nothing else for them to
   attach to.
   NOT SQUEEZED TO THE MINIMUM, though (Ali: "dont tighten so much", "use the
   width"). Sizing every column to its longest possible value left Remarks with
   574px of a 1407px table and the figures crammed against each other - correct
   arithmetic, mean to read. The real ranges above set the ORDER of the widths
   (Slips narrower than a 6-figure weight, Bags narrower still) while the table's
   own width sets their scale. Thirteen fixed columns total ~1066px, leaving
   Remarks ~341px - room for a real note without starving the grid.
   .wbc-loads (S191) matches .wbc-slips: same 2-figure range (a day's load
   count, DCM or tractor, is never a big number either). */
.wbc-no{ width:50px } .wbc-date{ width:92px } .wbc-bags{ width:72px }
.wbc-pur{ width:104px } .wbc-loads{ width:54px } .wbc-slips{ width:54px }
.wbc-gross{ width:104px }
.wbc-rate{ width:74px } .wbc-std{ width:94px } .wbc-extra{ width:80px }
.wbc-net{ width:104px } .wbc-short{ width:98px } .wbc-avg{ width:86px }
.wbc-rem{ width:auto }

/* THE KEYBOARD'S ROW. Arrow keys move it, Enter opens it, and it is a heavier
   mark than :hover on purpose -- the mouse follows the pointer, the keyboard
   needs to be findable after looking away. */
/* THE APP'S AMBER, NOT THE THEME ACCENT (S191). Ali: "cursor focus is red tint,
   purchase has different amber or yellow focus." This was var(--accent-tint) /
   var(--accent) -- rust #9e4f2e in the current theme -- while Purchase, Stock
   and Jobs all use .acc-cur's amber. Worse, --accent is the THEMEABLE token, so
   the weighbridge cursor changed colour with the ground+accent picker and no
   other list did. Same mistake the keyboard made last session: it built its own
   instead of using the app's. Values copied from the .acc-cur rule that
   #stockTable and .jobs-view-table already share, so all four now match. */
.wb-row.is-cursor > td{ background:#f2d585 !important; color:#241f00; }
.wb-row.is-cursor > td:first-child{ box-shadow:inset 3px 0 0 #8a6d0b; }
/* A DAY WITH NO SLIPS GETS A RED GROUND (Ali, S191: "give red background for
   that row, so red shortage is shortage and red background is data not
   entered"). That split is the whole point: RED TEXT in the Shortage columns
   means the figures are bad, RED GROUND means there are no figures at all.
   SAND WAS TRIED FIRST AND WAS WRONG - Ali: "sand does make you believe it's the
   end of total and not something is pending or missing". It was the GRAND row's
   own colour family, so a missing day read as a subtotal. A quiet colour was the
   wrong instinct here: this row is the one thing on the screen asking to be
   acted on.
   Painted on the TD, never the TR - .pv-alt paints the td, so a tr-level
   background hides underneath the zebra (the same trap noted on
   .pur-view-table's own zebra rule). The ID beats .pv-alt without !important,
   which leaves .is-cursor's !important free to sit on top, so the keyboard
   cursor still shows on a pending row. Hover is restated for the same reason:
   the ID would otherwise swallow it and the row would stop responding. */
#wbTable tbody tr.wb-none > td{ background:var(--neg-tint); }
#wbTable tbody tr.wb-none:hover > td{ background:#f7dad6; }

/* The left bar is for scanning a month without reading every figure. `not
   entered` gets NO bar: it is a state, not a band, and must never read green. */
.wb-row{ border-left:3px solid transparent }
.wb-row.wb-green{ border-left-color:var(--pos) }
.wb-row.wb-orange{ border-left-color:var(--warn) }
.wb-row.wb-red{ border-left-color:var(--neg) }
.wb-notdone{ color:var(--ink-3); font-style:italic; }
.wbc-rem{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    color:var(--ink-2); }

/* The SAME three tokens sales paints its weighbridge result with
   (.si6 .tp .wbout.wb-g/.wb-o/.wb-r), so both screens read alike even though
   their thresholds differ on purpose. */
.wbout{ font-weight:700; font-variant-numeric:tabular-nums }
.wbout.wb-g{ color:var(--pos) } .wbout.wb-o{ color:var(--warn) }
.wbout.wb-r{ color:var(--neg) }
/* PARTIAL (S191): fewer loads weighed than were said to arrive, so a shortage
   figure would just be measuring the missing vehicles, not a real problem.
   Deliberately NOT a band colour - ink-3 + italic says "not judged yet",
   never "judged and fine" (green) or "judged and wrong" (orange/red). Same
   voice as .wb-notdone, which makes the same distinction for a day that has
   nothing entered at all. */
.wbout.wb-pending{ color:var(--ink-3); font-style:italic; font-weight:600; }

/* THE SLIPS BOX'S OWN PROGRESS LINE ("3 of 5", "5 of 5 — complete", "6 of 5 —
   more than expected") - where "+ Add slip" used to sit, now that Loads is the
   only thing that grows or shrinks the rows. Blank when Loads has never been
   answered (an old day, same as today). */
.wb-slips-status{ font-size:var(--fs-sm); color:var(--ink-3); font-weight:600; }

/* OVERCOUNT (S191): more slips entered than the day's own Loads number said to
   expect - a vehicle count anomaly, not a bad weight, so it marks the SLIPS
   figure specifically rather than the whole row. --warn, not --neg: this is
   "check this," not "this is definitely wrong" the way a red shortage band is -
   an uncounted extra vehicle is a real, ordinary possibility, not a fault. */
#wbTable tbody td.wbc-slips.wb-over{ color:var(--warn-deep); font-weight:700; }

/* How much is still to do, next to the filters that change it. Not an alert --
   an empty screen is an invitation to act, and this names the job. */
/* SITS ON THE INPUTS' OWN LINE (Ali, S191: "does not sit properly ... text
   floting without any meaning"). The filter bar is align-items:flex-end, so
   every control lines up along its bottom edge while this was align-self:center
   - floating half a label's height above them with no box around it. flex-end
   plus the inputs' own 28px height puts it on the same line as the search box,
   and .pill/.pill-neg (added in JS) give it the ground it was missing. */
.wb-hint{ margin-left:auto; align-self:flex-end; font-size:var(--fs-sm);
    color:var(--ink-3); height:28px; white-space:nowrap; }
/* .pill-neg lives in foundation.css, which loads FIRST, so at equal specificity
   .wb-hint's grey won on source order and the badge came out grey-on-pink. Two
   classes beat one; this is the whole fix. */
.wb-hint.pill-neg{ color:var(--neg-deep); }
/* ROOM FOR IT. Search is flex:1 with a 240px floor app-wide, which left the
   badge squeezed against the right edge on a narrow window. Scoped to this
   screen so Purchase/Jobs/Stock keep the shared figure. */
#wbListPage .pur-view-filter-bar .ls-search{ min-width:180px; }
#wbListPage .pur-view-filter-bar .ls-search input{ min-width:0; }
.wb-wrap{ max-height:calc(100vh - 300px - var(--mstrip-h)); }

/* ── the day ──
   Two boxes side by side: what the bridge said, and what it is measured
   against. They are the two halves of the reconciliation, and putting the
   arithmetic beside the slips rather than under them means a correction and its
   effect are visible at once. */
.wb-day-body{ padding:var(--sp-5); }
.wb-grid{ display:grid; grid-template-columns:minmax(340px, 1fr) minmax(360px, 1fr);
    gap:var(--sp-5); align-items:start; }
@media (max-width:1100px){ .wb-grid{ grid-template-columns:1fr } }
.wb-box{ border:1px solid var(--line); border-radius:var(--radius);
    background:var(--surface); overflow:hidden; }
.wb-box-hd{ display:flex; align-items:center; justify-content:space-between;
    gap:var(--sp-3); padding:7px 12px; background:var(--head);
    border-bottom:1px solid var(--line); font-weight:700;
    font-size:var(--fs-sm); color:var(--ink-2); }

.wb-slips{ width:100%; border-collapse:collapse; font-size:var(--fs-sm); }
/* THE BACKGROUND IS STATED, NOT INHERITED. The bare `th` rule at style.css:378
   paints every unclaimed header with a hard-coded red gradient, and a rule that
   sets only colour and padding loses the background to it -- the slips header
   came out rust against this card's own head bar. */
.wb-slips th{ background:none; color:var(--ink-3); font-weight:600;
    font-size:var(--fs-lbl); padding:6px 10px; text-align:left;
    border-bottom:1px solid var(--line-2); }
.wb-slips th.r, .wb-slips td.r{ text-align:right }
.wb-slips td{ padding:4px 10px; border-bottom:1px solid var(--line-2); }
/* SAND, matching .wb-line-gross on the right - Total weighbridge is the same
   figure in both boxes, so both get the same anchor-value ground. */
.wb-slips tfoot td{ border-bottom:none; border-top:2px solid var(--line);
    font-weight:700; font-variant-numeric:tabular-nums; padding:7px 10px;
    background:var(--head); }
.wb-tot-l{ color:var(--ink-2); font-weight:600; }
.wb-sr-h{ width:38px } .wb-sr{ width:38px; color:var(--ink-3);
    font-variant-numeric:tabular-nums; }
.wb-in-veh{ width:100%; font-family:inherit; font-size:var(--fs-sm);
    padding:4px 7px; border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); }
.wb-in-wt{ width:118px; font-family:inherit; font-size:var(--fs-sm);
    padding:4px 7px; border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); text-align:right;
    font-variant-numeric:tabular-nums; }
.wb-in-veh:focus, .wb-in-wt:focus, .wb-in-sm:focus, #wbExtraRem:focus{
    border-color:var(--accent); outline:none; }
.wb-x{ border:none; background:none; cursor:pointer; color:var(--ink-3);
    font-size:1.15em; line-height:1; padding:2px 6px; border-radius:4px; }
.wb-x:hover{ color:var(--neg); background:var(--neg-tint) }
.wb-x:focus-visible{ outline:2px solid var(--accent); outline-offset:1px }

.wb-lines{ padding:2px 0 }
.wb-line{ display:flex; align-items:center; justify-content:space-between;
    gap:var(--sp-3); padding:7px 12px; border-bottom:1px solid var(--line-2);
    font-size:var(--fs-sm); }
.wb-line b{ font-variant-numeric:tabular-nums; white-space:nowrap; }
.wb-line-rem{ padding-top:2px; padding-bottom:8px; border-bottom:none; }
.wb-line-rem input{ width:100%; font-family:inherit; font-size:var(--fs-sm);
    padding:4px 7px; border:1px solid var(--line);
    border-radius:var(--radius-sm); background:var(--surface); }
.wb-line-net{ border-top:2px solid var(--line); font-weight:700; }
/* SAND, same as .wb-line-pur below - Total weighbridge and Purchased are the
   two ANCHOR figures being reconciled here, so they share the one ground. */
.wb-line-gross{ background:var(--head) }
.wb-line-pur{ background:var(--head) }
.wb-line-res{ font-weight:600 }
.wb-line-res:last-child{ border-bottom:none }
.wb-in-sm{ width:76px; font-family:inherit; font-size:var(--fs-sm);
    padding:3px 6px; border:1px solid var(--line);
    border-radius:var(--radius-sm); background:var(--surface);
    text-align:right; font-variant-numeric:tabular-nums; }

/* FOREST ON FOREST. Ali: "+ weigbridge is in grenn color same as heade color."
   Measured, not eyeballed: the button's computed background was rgb(47,82,64)
   and the topbar's was rgb(47,82,64) -- the same value, so the button had no
   edge at all. The ledger's own "+ New Purchase" carries the same
   `btn btn-primary` classes and is saved by an id-scoped rule (#btnPurNew,
   style.css:4145) that flips it to white on the green. This is that rule, for
   this button. A primary button dropped on a brand-coloured bar needs the
   inversion every time; the classes alone do not know they are on green. */
#btnWbAdd{ background:#fff; color:var(--brand); border:1.5px solid #fff; }
#btnWbAdd:hover{ background:#fff; color:var(--brand); border-color:#fff; }
/* Back sits on the same green, so it needs the same treatment -- outlined
   rather than filled, since it is the quieter of the two. */
#btnWbBack{ background:transparent; color:#fff; border:1.5px solid rgba(255,255,255,.55); }
#btnWbBack:hover{ background:rgba(255,255,255,.12); color:#fff;
    border-color:rgba(255,255,255,.85); }

.wb-day-sub{ font-size:var(--fs-sm); color:var(--brand-ink); opacity:.8 }
/* SAVE SITS ALONE ON THE RIGHT. Ali: "save is on left side should be on
   right side" (S189), then "we have save and remove this on same side...
   move it to left side, and only keep save to right side" (S192) - Delete
   had followed Save to the same edge. Delete now shares the LEFT with the
   note (order 0, ahead of it); Save keeps the auto margin, so it is the only
   thing pushed to the end of the bar. */
.wb-day-bar{ display:flex; align-items:center; gap:var(--sp-3);
    padding:10px var(--sp-5); background:var(--head);
    border-top:1px solid var(--line); }
.wb-day-bar #wbDelBtn{ order:0 }
.wb-day-bar #wbSaveBtn{ margin-left:auto; order:2 }
.wb-note{ font-size:var(--fs-sm); color:var(--ink-2); order:1 }
.wb-note-bad{ color:var(--neg-deep); font-weight:600 }

/* The date of the day being entered, and the state of the book, above the two
   boxes -- both are facts you need BEFORE typing, not after. */
.wb-day-datebar{ display:flex; align-items:center; gap:var(--sp-3);
    padding:9px var(--sp-5); background:var(--head);
    border-bottom:1px solid var(--line); flex-wrap:wrap; }
.wb-day-datebar label{ font-size:var(--fs-lbl); color:var(--ink-2);
    font-weight:700; text-transform:uppercase; }
/* MATCHES THE PURCHASE ENTRY'S OWN DATE FIELD (.pf input), not merely the
   same variable name - Ali: "date font size is not matching pur standard
   dates... load field and font looks smaller." #wbLoads had no sizing of
   its own at all (a bare browser number input), which is why it read even
   smaller than the date beside it. */
#wbDayDate, #wbLoads{ font-family:inherit; font-size:0.92em; height:32px;
    padding:5px 9px; border:1px solid var(--line); border-radius:var(--radius-sm);
    background:var(--surface); box-sizing:border-box; }
#wbDayDate:focus, #wbLoads:focus{ border-color:var(--accent); outline:none }
/* Loads is a count someone reads back, not free text - centred, like every
   other counted figure in this file (fmtI() cells are right-aligned for the
   same reason: a number reads by its digits, not its start). */
#wbLoads{ text-align:center; }
/* BREATHING ROOM AFTER THE DATE. Ali: the two fields ran together. A wider
   gap here only, not on .wb-day-datebar's own gap - that would also space
   out the label from ITS OWN input. */
.wb-loads-lbl{ margin-left:var(--sp-4); }
.wb-day-note{ font-size:var(--fs-sm); color:var(--ink-3) }
.wb-day-note-warn{ color:var(--warn-deep); font-weight:600 }
.wb-locked{ margin-left:auto; font-size:var(--fs-sm); font-weight:700;
    color:var(--neg-deep); background:var(--neg-tint);
    padding:3px 10px; border-radius:var(--radius-pill); }
#wbSaveBtn:disabled{ opacity:.5; cursor:not-allowed }

/* ── OPENING STOCK (S213) ─────────────────────────────────────────────────
   Stock > Opening Stock. Borrows the weighbridge day's table (.wb-slips) and
   bar (.wb-day-bar); only what differs is here. */
