/* ==========================================================================
   Alpha Northline Global — business platform interface
   A restrained enterprise system: neutral surfaces, one accent per workspace,
   dense but breathable tables, and no decoration that costs legibility.
   ========================================================================== */

:root{
  /* neutrals */
  --n-0:#FFFFFF; --n-25:#FBFCFD; --n-50:#F6F8FA; --n-100:#EDF1F5; --n-150:#E3E9EF;
  --n-200:#D6DEE7; --n-300:#B6C2CE; --n-400:#8A98A8; --n-500:#66788C;
  --n-600:#4A5A6B; --n-700:#334455; --n-800:#22303F; --n-900:#15202B;

  /* accent is set per workspace inline; this is the fallback */
  --accent:#00A896;
  --accent-soft:color-mix(in srgb, var(--accent) 12%, white);
  --accent-line:color-mix(in srgb, var(--accent) 34%, white);

  --ok:#1E9E6A; --ok-soft:#EAF7F1;
  --warn:#B7791F; --warn-soft:#FEF6E7;
  --danger:#C93B3B; --danger-soft:#FDF0F0;
  --info:#2B6CB0; --info-soft:#EDF4FC;

  /* ------------------------------------------------------------ geometry
     An accounting console is read, not admired. Radii came down from 10/14px
     to 4/6px and the resting shadow went away entirely: a 14px radius with a
     drop shadow reads as a floating card, and a screen of floating cards is
     the look this interface was asked to stop having. Elevation is now spent
     only where something genuinely sits ABOVE the page — a menu, a drawer,
     a drag — and --shadow-2 is what spends it.
     ---------------------------------------------------------------------- */
  --radius:4px; --radius-lg:6px;
  --shadow-1:none;
  --shadow-2:0 10px 28px rgba(21,32,43,.14), 0 2px 6px rgba(21,32,43,.06);
  --sidebar-w:236px; --sidebar-w-collapsed:60px;

  /* The one command bar, and the context line under it. Declared as tokens
     because the sticky offset of a table header is measured from them. */
  --cmd-h:44px; --ctx-h:30px;

  /* ---------------------------------------------------------------- spacing
     One scale, six steps. Every gap, pad and margin in this sheet is one of
     these six values — nothing is "13px because it looked right there". A
     mixed set of 9/11/14/15/18/22px is what made two pages built a month apart
     read as two different products, and it is why the same card was 4px taller
     on one screen than on another. Steps: 4 8 12 16 24 32.
     ------------------------------------------------------------------------ */
  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px; --s-6:24px; --s-8:32px;

  /* Surface padding, derived from the scale. Named by role rather than by
     size so a density change is one edit here, not 172 edits in templates. */
  --pad-head:6px var(--s-3);          /* card headers, card footers          */
  --pad-body:var(--s-3);              /* card bodies                         */
  --gap-grid:var(--s-2);              /* between cards in a grid             */
  --gap-stack:var(--s-2);             /* between stacked blocks on a page    */
  --font:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--n-50);color:var(--n-800);
     font:14px/1.55 var(--font);-webkit-font-smoothing:antialiased}
h1,h2,h3,h4{margin:0;font-weight:650;letter-spacing:-.01em;color:var(--n-900)}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}

.skip-link{position:absolute;left:-9999px;top:0;background:var(--n-900);color:#fff;padding:10px 16px;z-index:200}
.skip-link:focus{left:8px;top:8px}

.muted{color:var(--n-500)}
.mono{font-family:var(--mono);font-size:.86em}

/* ---------------------------------------------------------------- layout */
.app{display:flex;min-height:100vh}
.shell{flex:1;min-width:0;display:flex;flex-direction:column}

/* ---------------------------------------------------------------- sidebar */
/* THE z-index HERE IS LOAD BEARING. MEASURED 6 Oct 2026.

   `position:sticky` creates a stacking context whether or not a z-index is
   given. With none, the whole sidebar sat at the root's level 0, and the
   flyout panels on the context controls inside it — which correctly ask for
   z-index 90 — were resolved INSIDE that context and could never rise above
   anything in .shell, which comes later in the document. The workspace menu
   opened over a project page and the module tab row printed straight through
   it; elementFromPoint at the middle of the open panel returned the tab, not
   the panel. It looked like a transparency bug and was a stacking one.

   100 sits above the command bar (60) and every panel the bar itself opens
   (90, 95), which never overlap the sidebar horizontally, and below the
   off-canvas state (120), the mobile bars (110, 130), modals (200) and a card
   being dragged (300). */
.sidebar{width:var(--sidebar-w);flex:0 0 var(--sidebar-w);background:var(--n-900);
  color:var(--n-200);display:flex;flex-direction:column;position:sticky;top:0;height:100vh;
  z-index:100;transition:width .18s ease,flex-basis .18s ease}
.app.is-collapsed .sidebar{width:var(--sidebar-w-collapsed);flex-basis:var(--sidebar-w-collapsed)}
.app.is-collapsed .brand__text,.app.is-collapsed .nav__link span,
.app.is-collapsed .nav__heading,.app.is-collapsed .sidebar__foot .muted{display:none}
.app.is-collapsed .nav__link{justify-content:center}

.sidebar__brand{display:flex;align-items:center;gap:10px;padding:16px 14px;border-bottom:1px solid rgba(255,255,255,.07)}
.brand__mark{width:34px;height:34px;flex:0 0 34px;border-radius:9px;display:grid;place-items:center;
  background:var(--accent);color:#fff;font-weight:800;font-size:12px;letter-spacing:.04em}
.brand__text{display:flex;flex-direction:column;line-height:1.2;min-width:0}
.brand__text strong{color:#fff;font-size:13.5px}
.brand__text small{color:var(--n-400);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase}
.sidebar__toggle{margin-left:auto;background:none;border:0;color:var(--n-400);cursor:pointer;padding:4px;border-radius:6px}
.sidebar__toggle:hover{color:#fff;background:rgba(255,255,255,.07)}
.app.is-collapsed .sidebar__toggle svg{transform:rotate(180deg)}
/* Collapsed, the brand row is 68px wide less 28px of padding — the 34px mark
   and the toggle cannot both fit on one line, so the toggle was pushed out of
   the sidebar and clipped, leaving no way to expand again. Stack them. */
.app.is-collapsed .sidebar__brand{flex-direction:column;gap:8px;padding:14px 8px 12px}
.app.is-collapsed .sidebar__toggle{margin:0;width:32px;height:32px;display:grid;
  place-items:center;background:rgba(255,255,255,.09);color:#fff}
.app.is-collapsed .sidebar__toggle:hover{background:rgba(255,255,255,.18)}
/* Always reachable by keyboard, collapsed or not. */
.sidebar__toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ---------------------------------------------- the sidebar context block
   Workspace, period and project scope, moved out of the command bar on
   6 Oct 2026. The controls are the same markup — still .cmdctl inside
   details.cmddrop, so the menu machinery in app.js needs no change — but the
   command bar's version is a white chip built for a white bar, and dropped on
   to the near-black sidebar it read as three paste-ups. `.sidectl` restates
   the surface for this background and nothing else.

   THE PANELS FLY OUT SIDEWAYS, not down. A .cmddrop__panel asks for 260px and
   the sidebar is 230px: anchored under the control it would be cut off at the
   edge, or it would force the sidebar wider for the one second a menu is open.
   Hung off the right edge it opens over the page, which is what a menu should
   do, and it works identically when the sidebar is collapsed to 68px. */
.sidectx{padding:10px 8px;border-bottom:1px solid rgba(255,255,255,.07);
  display:grid;gap:4px}

/* TWO LINES, AND THE CARET PINNED.

   First attempt let the three parts wrap freely. A company name is longer than
   a 212px control, so it took the second line and pushed the caret alone on to
   a third: a 70px control for one dropdown. The label and the value now have a
   line each by construction, and the caret is lifted out of the flow against
   the right edge, so every control in this block is the same height whatever
   is in it. */
.sidectx .sidectl{position:relative;display:flex;width:100%;height:auto;min-height:32px;
  padding:6px 22px 6px 9px;gap:2px 7px;flex-wrap:wrap;align-items:center;max-width:none;
  background:rgba(255,255,255,.05);border-color:rgba(255,255,255,.09);color:var(--n-300);
  white-space:normal;text-align:left}
.sidectx .sidectl:hover{background:rgba(255,255,255,.10);border-color:rgba(255,255,255,.18);
  color:#fff}
.sidectx .cmdctl__k{color:var(--n-500);flex:0 0 auto;line-height:1.3}
.sidectx .cmdctl__v{color:#fff;flex:1 1 100%;text-align:left;line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* Only the LAST one. The scope control opens with a list glyph that also
   carries this class and belongs inline beside its label. */
.sidectx .sidectl > .cmdctl__chev:last-child{position:absolute;right:7px;top:50%;
  transform:translateY(-50%);color:var(--n-500)}
.sidectx .sidectl > .cmdctl__chev:first-child{flex:0 0 auto;color:var(--n-500)}
.sidectx .cmdctl__dot{flex:0 0 7px}
.sidectx .sidedrop{display:block;width:100%}
.sidectx .sidedrop > summary{width:100%}
/* The warn state keeps its meaning but has to be legible on black: the amber
   fill the bar used disappears into the sidebar at this size. */
.sidectx .sidectl.is-warn{background:rgba(231,201,143,.14);border-color:rgba(231,201,143,.35);
  color:#F0D9A8}
.sidectx .sidectl.is-warn .cmdctl__v{color:#FFD88A}
.sidectx .sidectl.is-on{background:var(--accent);border-color:var(--accent);color:#fff}
.sidectx .sidectl.is-on .cmdctl__k{color:rgba(255,255,255,.6)}
.sidectx .sidectl.is-on .cmdctl__v{color:#fff}
.sidectx .sidectl:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

.sidectx .cmddrop__panel{left:calc(100% + 6px);right:auto;top:0;
  max-width:min(340px, calc(100vw - var(--sidebar-w) - 24px))}

/* Collapsed, there is no room for a label, so each control becomes its own
   initial. The value is still on the title attribute and still one press away
   in the panel, which opens at exactly the same place. */
.app.is-collapsed .sidectx{padding:8px 6px}
.app.is-collapsed .sidectx .cmdctl__k,
.app.is-collapsed .sidectx .cmdctl__v,
.app.is-collapsed .sidectx .cmdctl__chev{display:none}
.app.is-collapsed .sidectx .sidectl{justify-content:center;padding:7px 0;min-height:30px}
.app.is-collapsed .sidectx .sidectl::after{content:attr(data-short);font-size:10px;
  font-weight:800;letter-spacing:.06em;color:var(--n-300)}
.app.is-collapsed .sidectx .sidectl.is-on::after{color:#fff}
.app.is-collapsed .sidectx .cmddrop__panel{
  max-width:min(340px, calc(100vw - var(--sidebar-w-collapsed) - 24px))}

/* ON A PHONE THE PANEL DROPS, IT DOES NOT FLY OUT.

   Below 860px the sidebar is an off-canvas drawer 300px wide on a 390px
   screen, and `nav-open` lays a scrim over everything behind it. A panel hung
   off the drawer's right edge there is 136px of usable width sitting on top of
   that scrim: measured, elementFromPoint returned the scrim, not the panel.
   Inside the drawer, under its control, full width, with its own scroll so a
   long company list cannot run off the bottom of the screen. */
@media (max-width:860px){
  .sidectx .cmddrop__panel{position:absolute;left:0;right:0;top:calc(100% + 4px);
    max-width:none;min-width:0;max-height:52vh;overflow-y:auto}
}

.sidebar__nav{flex:1;overflow-y:auto;padding:10px 8px 20px;scrollbar-width:thin}
.sidebar__nav::-webkit-scrollbar{width:6px}
.sidebar__nav::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:99px}
.nav{list-style:none;margin:0;padding:0}
.nav__heading{color:var(--n-500);font-size:10px;font-weight:800;letter-spacing:.14em;
  text-transform:uppercase;padding:16px 12px 6px}
.nav__link{display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:8px;
  color:var(--n-300);font-size:13.2px;font-weight:500;white-space:nowrap;transition:background .12s,color .12s}
.nav__link:hover{background:rgba(255,255,255,.06);color:#fff}
.nav__link.is-active{background:rgba(255,255,255,.10);color:#fff;font-weight:600;
  box-shadow:inset 3px 0 0 var(--accent)}
.nav__link svg{flex:0 0 17px;opacity:.85}
.nav__link .dot{width:8px;height:8px;border-radius:99px;flex:0 0 8px}
.nav--projects .nav__link{padding-left:14px;font-size:12.8px}

.sidebar__foot{padding:12px 14px;border-top:1px solid rgba(255,255,255,.07);
  display:flex;align-items:center;gap:8px;font-size:11px;color:var(--n-500)}
.phase-chip{background:rgba(255,255,255,.10);color:#fff;border-radius:99px;padding:2px 9px;font-weight:700;font-size:10px}



/* ---------------------------------------------------------------- main */
.main{flex:1;padding:var(--s-4) var(--s-6) var(--s-8);max-width:1560px;width:100%}
.foot{display:flex;gap:var(--s-3);justify-content:space-between;flex-wrap:wrap;
  padding:var(--s-3) var(--s-6);border-top:1px solid var(--n-150);color:var(--n-500);font-size:11.5px;background:var(--n-0)}

/* The page header. One shape everywhere: title, one line of context, actions
   pushed right. It is the first thing on the page and the shortest thing on
   the page — every pixel it takes is a pixel the work does not get. */
.page-head{display:flex;align-items:flex-start;gap:var(--s-4);flex-wrap:wrap;margin-bottom:var(--s-3)}
.page-head h1{font-size:20px;line-height:1.25}
.page-head p{margin:2px 0 0;color:var(--n-500);font-size:12.5px;line-height:1.5;max-width:80ch}
.page-head__actions{margin-left:auto;display:flex;gap:var(--s-2);flex-wrap:wrap;align-items:center}

/* ---------------------------------------------------------------- cards */
.card{background:var(--n-0);border:1px solid var(--n-150);border-radius:var(--radius-lg);box-shadow:var(--shadow-1)}
.card__head{display:flex;align-items:center;gap:var(--s-3);padding:var(--pad-head);border-bottom:1px solid var(--n-100);min-height:46px}
.card__head h2{font-size:14px}
.card__head .muted{font-size:12px}
.card__body{padding:var(--pad-body)}
.card__foot{padding:var(--s-2) var(--s-4);border-top:1px solid var(--n-100);background:var(--n-25);
  border-radius:0 0 var(--radius-lg) var(--radius-lg)}
/* A card that holds nothing but a table has no business adding its own padding
   around it — the table already has cell padding of its own. */
.card__body--flush{padding:0}

.grid{display:grid;gap:var(--gap-grid)}
.grid--4{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.grid--3{grid-template-columns:repeat(auto-fit,minmax(290px,1fr))}
.grid--2{grid-template-columns:repeat(auto-fit,minmax(340px,1fr))}

/* stat tiles */
.stat{background:var(--n-0);border:1px solid var(--n-150);border-radius:var(--radius-lg);padding:var(--s-3) var(--s-4);box-shadow:var(--shadow-1)}
.stat__label{font-size:10px;font-weight:800;letter-spacing:.13em;text-transform:uppercase;color:var(--n-400)}
.stat__value{font-size:23px;line-height:1.15;font-weight:700;letter-spacing:-.02em;margin:var(--s-1) 0 1px;color:var(--n-900)}
.stat__sub{font-size:11.5px;color:var(--n-500);line-height:1.45}
.stat--pending .stat__value{color:var(--n-300);font-size:17px;font-weight:600}

/* ---------------------------------------------------------------- tables */
.table-wrap{overflow-x:auto;border:1px solid var(--n-150);border-radius:var(--radius-lg);background:var(--n-0)}

/* --------------------------------------------------------------- data tables
   Row geometry is declared once, as four custom properties, and every variant
   changes those four numbers rather than re-stating padding. `height` on a
   table row is a MINIMUM, not a cap, so a two-line cell still grows — this
   pins the floor so a table of one-line rows is the same height on every
   screen in the application instead of drifting with whatever font-size the
   page happened to set.

   Standard   header 40px   row 46px
   Compact    header 32px   row 36px   (tables inside a card, dense registers)
   ------------------------------------------------------------------------- */
table.data{--th-h:40px; --row-h:46px; --cell-x:var(--s-3); --cell-y:var(--s-2);
  width:100%;border-collapse:collapse;font-size:13px}
table.data th{background:var(--n-50);text-align:left;font-size:10px;letter-spacing:.11em;
  text-transform:uppercase;color:var(--n-500);font-weight:800;white-space:nowrap;
  height:var(--th-h);padding:0 var(--cell-x);border-bottom:1px solid var(--n-150)}
table.data tbody tr{height:var(--row-h)}
table.data td{padding:var(--cell-y) var(--cell-x);border-top:1px solid var(--n-100);vertical-align:middle}
/* Cells that stack a heading over sub-text read wrong when centred against a
   neighbour that is one line — those opt back in. */
table.data td.top,table.data tr.top td{vertical-align:top}
table.data tbody tr:hover{background:var(--n-25)}
table.data td.num,table.data th.num{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--mono)}
.empty{padding:var(--s-6) var(--s-4);text-align:center;color:var(--n-500)}
.empty strong{display:block;color:var(--n-700);font-size:14px;margin-bottom:var(--s-1)}
.empty span{display:block;max-width:64ch;margin:0 auto;font-size:12.5px;line-height:1.5}
/* An empty state inside a table cell must not inherit the row's minimum. */
table.data td.empty{height:auto}

/* ---------------------------------------------------------------- forms */
.field{margin-bottom:var(--s-3)}
.field > label{display:block;font-size:11px;font-weight:800;letter-spacing:.09em;
  text-transform:uppercase;color:var(--n-500);margin-bottom:var(--s-1)}
.field .hint{font-size:11.5px;color:var(--n-500);margin-top:var(--s-1);line-height:1.45}
/*
 | Matched by what an input ISN'T, not by listing what it is.
 |
 | The old selector list matched on `type`, so any <input> written without a
 | type attribute — which is most of the search boxes in this application —
 | fell through it entirely and rendered at the browser default: a short,
 | unstyled box sitting lower than the selects beside it. Excluding the handful
 | of control types that must keep their native appearance is the version that
 | cannot be defeated by a forgotten attribute.
 |
 | `.cell-in` opts out entirely. A spreadsheet cell is 22px tall with no
 | border of its own, and this rule's eight :not() clauses give it a
 | specificity no sane selector can outrank. Excluding a class that nothing
 | else uses changes no existing screen and settles it once.
*/
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=reset]):not([type=image]):not([type=range]):not([type=hidden]):not(.cell-in):not(.cmdsearch__input),
select,textarea{
  width:100%;padding:9px 11px;border:1px solid var(--n-200);border-radius:9px;background:var(--n-0);
  font:inherit;color:var(--n-900);transition:border-color .12s,box-shadow .12s;
  /* `font: inherit` brings the body's 1.55 line-height with it, which makes a
     text box ~4px taller than a select next to it. 18px of content plus 9+9
     padding plus 1+1 border is 38px — exactly what a select computes. */
  line-height:18px;box-sizing:border-box}
textarea{line-height:1.55}
/* Chromium gives a date/time field a taller intrinsic content box for its
   calendar and spin controls, which left it 2px prouder than every text field
   and select on the same row. Pinning the height settles the row. */
input[type=date],input[type=datetime-local],input[type=month],input[type=week],input[type=time]{height:38px}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent)}
input[disabled],select[disabled]{background:var(--n-50);color:var(--n-500)}
textarea{min-height:96px;resize:vertical}
.field--error input,.field--error select,.field--error textarea{border-color:var(--danger)}
.field__err{color:var(--danger);font-size:11.5px;margin-top:5px}
.checkline{display:flex;align-items:flex-start;gap:var(--s-2);font-size:13px;margin:var(--s-1) 0}
.checkline input{width:16px;height:16px;margin-top:2px;accent-color:var(--accent)}
fieldset{border:1px solid var(--n-150);border-radius:var(--radius);padding:var(--s-3) var(--s-4);margin:0 0 var(--s-3)}
legend{font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--n-500);padding:0 6px}

/* ---------------------------------------------------------------- buttons */
.btn{display:inline-flex;align-items:center;gap:7px;padding:9px 15px;border-radius:9px;border:1px solid var(--n-200);
  background:var(--n-0);color:var(--n-800);font-size:13px;font-weight:600;cursor:pointer;
  transition:background .12s,border-color .12s,box-shadow .12s}
.btn:hover{background:var(--n-50);border-color:var(--n-300)}
.btn--primary{background:var(--accent);border-color:var(--accent);color:#fff}
.btn--primary:hover{filter:brightness(.94);background:var(--accent);border-color:var(--accent)}
.btn--danger{background:var(--danger);border-color:var(--danger);color:#fff}
.btn--danger:hover{filter:brightness(.94);background:var(--danger)}
.btn--ghost{border-color:transparent;background:transparent}
.btn--ghost:hover{background:var(--n-100)}
.btn--sm{padding:6px 11px;font-size:12px}
.btn[disabled]{opacity:.5;cursor:not-allowed}

/* ---------------------------------------------------------------- chips */
.chip{display:inline-flex;align-items:center;gap:6px;padding:2px 9px;border-radius:99px;
  font-size:10.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase}
.chip--ok{background:var(--ok-soft);color:var(--ok)}
.chip--warn{background:var(--warn-soft);color:var(--warn)}
.chip--danger{background:var(--danger-soft);color:var(--danger)}
.chip--info{background:var(--info-soft);color:var(--info)}
.chip--muted{background:var(--n-100);color:var(--n-500)}

/* ---------------------------------------------------------------- alerts */
.alert{border-radius:var(--radius);padding:var(--s-2) var(--s-3);margin-bottom:var(--s-3);font-size:12.5px;line-height:1.5;border:1px solid}
.alert--ok{background:var(--ok-soft);border-color:#BFE6D5;color:#14653F}
.alert--err{background:var(--danger-soft);border-color:#F2CFCF;color:#8C2020}
.alert--warn{background:var(--warn-soft);border-color:#F0DFB4;color:#7A5310}
.alert--info{background:var(--info-soft);border-color:#CBE0F5;color:#1B4F86}
.alert ul{margin:6px 0 0;padding-left:20px}

/* ---------------------------------------------------------------- misc */
.pill-tabs{display:flex;gap:4px;background:var(--n-100);padding:3px;border-radius:10px;overflow-x:auto}
.pill-tabs a{padding:6px 13px;border-radius:8px;font-size:12.5px;font-weight:600;color:var(--n-600);white-space:nowrap}
.pill-tabs a.is-active{background:var(--n-0);color:var(--n-900);box-shadow:var(--shadow-1)}

.pagination{display:flex;gap:5px;list-style:none;padding:0;margin:16px 0 0;flex-wrap:wrap;font-size:13px}
.pagination li span,.pagination li a{display:grid;place-items:center;min-width:33px;height:33px;padding:0 9px;
  border:1px solid var(--n-200);border-radius:8px;background:var(--n-0);color:var(--n-700)}
.pagination li a:hover{background:var(--n-50)}
.pagination .active span{background:var(--accent);border-color:var(--accent);color:#fff}
.pagination .disabled span{opacity:.45}

.filters{display:flex;gap:var(--s-2);flex-wrap:wrap;align-items:flex-end;margin-bottom:var(--s-3)}
.filters .field{margin:0;min-width:150px}

.kv{display:grid;grid-template-columns:auto 1fr;gap:7px 18px;font-size:13px}
.kv dt{color:var(--n-500);font-size:11.5px;white-space:nowrap}
.kv dd{margin:0;color:var(--n-800)}

.progress{height:6px;background:var(--n-100);border-radius:99px;overflow:hidden}
.progress i{display:block;height:100%;border-radius:99px;background:var(--accent)}

.diff{font-family:var(--mono);font-size:11.5px;background:var(--n-50);border:1px solid var(--n-150);
  border-radius:8px;padding:9px 11px;white-space:pre-wrap;word-break:break-word;max-height:230px;overflow:auto}

.avatar-lg{width:52px;height:52px;border-radius:13px;display:grid;place-items:center;
  color:#fff;font-weight:750;font-size:17px}

/* ==========================================================================
   AUTH
   --------------------------------------------------------------------------
   Two panels: the sign-in form on the left, the group's own visual on the
   right. The form is first in the DOM as well as on the left, so the first
   thing a keyboard or a screen reader reaches is the email field rather than a
   paragraph of positioning.

   The panels are `minmax(0,1fr)`, not `1.05fr .95fr` — an `fr` track has an
   implicit minimum of its content's minimum, and the right panel holds an
   image, so at narrow widths the old ratio pushed the form off the screen.
   ========================================================================== */
.auth{min-height:100vh;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}

/* ------------------------------------------------------ the form panel */
.auth__main{background:var(--n-0);display:flex;flex-direction:column;
  padding:var(--s-6) var(--s-8);min-width:0}
/* The real wordmark, top left.
   Sized by height only — the width follows from the file's own 2.2:1 ratio, so
   the mark can be re-exported at any resolution without a number here needing
   to change. `width:auto` is explicit because the <img> carries width and
   height attributes: those are there to reserve the space before the image
   arrives (no layout shift on a cold cache, which is every first sign-in), and
   without the override the attribute would win over the height rule. */
.auth__brand{display:inline-flex;align-items:center;margin-bottom:var(--s-8);
  align-self:flex-start;border-radius:8px}
.auth__brand img{display:block;height:42px;width:auto}
.auth__brand:focus-visible{outline:2px solid var(--accent);outline-offset:6px}

/* The form sits in the optical centre of the panel, which is not its
   arithmetic centre: `margin:auto` on a flex column with a footer below
   balances the remaining space above and below the form. */
/* A grid so the form is centred, and `> *` so a screen with two stacked forms
   — the forced password change offers "Sign out instead" — keeps them the same
   width instead of shrinking each to its own content. */
/* `padding-bottom` rather than a smaller brand margin: it lifts the form to the
   optical centre — a little above the arithmetic one — which is where the eye
   expects a short form in a tall panel. */
.auth__body{flex:1;display:grid;align-content:center;justify-items:center;
  gap:var(--s-3);min-width:0;padding-bottom:7vh}
.auth__body > *{width:100%;max-width:394px}
.auth__form{width:100%;max-width:394px}
.auth__form h2{font-size:26px;letter-spacing:-.02em;margin-bottom:var(--s-1)}
.auth__form .field > label{font-size:11px}
.auth__form input:not([type=checkbox]){height:46px}
.auth__submit{width:100%;justify-content:center;height:50px;font-size:14.5px}
/* The remember-me line: checkbox left, the way out right, and they stack rather
   than crush on a narrow phone. */
.auth__row{display:flex;justify-content:space-between;align-items:center;
  gap:var(--s-3);flex-wrap:wrap;margin-top:var(--s-1)}

/* --------------------------------------------------------- the footer */
.auth__foot{display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap;
  margin-top:var(--s-8);padding-top:var(--s-4);border-top:1px solid var(--n-100);
  font-size:11.5px;color:var(--n-500)}
.auth__foot nav{margin-left:auto;display:flex;gap:var(--s-4);flex-wrap:wrap}
.auth__foot a{color:var(--n-600);font-weight:600}
.auth__foot a:hover{color:var(--accent);text-decoration:underline}

/* ---------------------------------------------------- the visual panel */
/*
 | The background design.
 |
 | Three layers, all CSS, no image request: a diagonal navy-to-teal base, two
 | soft radial blooms placed off-centre so the panel has a light source, and a
 | dot grid that fades out towards the bottom right. The grid is what stops a
 | large flat gradient reading as an unfinished div — it gives the eye a
 | texture to land on without competing with the artwork in front of it.
*/
.auth__aside{position:relative;overflow:hidden;isolation:isolate;
  grid-column:2;color:var(--n-200);
  display:flex;flex-direction:column;justify-content:center;
  padding:var(--s-8) clamp(32px, 5vw, 72px);
  background:
    radial-gradient(120% 90% at 12% 4%, rgba(0,168,150,.30), transparent 58%),
    radial-gradient(90% 80% at 96% 88%, rgba(0,168,150,.16), transparent 60%),
    linear-gradient(148deg, #123642 0%, #0E2732 42%, #0B1A24 100%);
}
/* the dot grid */
.auth__aside::before{content:"";position:absolute;inset:0;z-index:-1;
  background-image:radial-gradient(rgba(127,227,214,.20) 1px, transparent 1px);
  background-size:26px 26px;
  -webkit-mask-image:linear-gradient(155deg, #000 8%, rgba(0,0,0,.35) 52%, transparent 88%);
          mask-image:linear-gradient(155deg, #000 8%, rgba(0,0,0,.35) 52%, transparent 88%)}
/* one oversized ring, bled off the top-right corner, for depth */
.auth__aside::after{content:"";position:absolute;z-index:-1;
  width:640px;height:640px;right:-260px;top:-240px;border-radius:50%;
  border:1px solid rgba(127,227,214,.16);
  box-shadow:0 0 0 90px rgba(127,227,214,.05)}

.auth__aside h1{color:#fff;font-size:clamp(30px, 3.3vw, 44px);line-height:1.1;
  letter-spacing:-.03em;max-width:19ch;font-weight:700}
.auth__aside p{color:#A9CFCA;font-size:15.5px;line-height:1.55;max-width:44ch;margin:var(--s-4) 0 0}
/* The globe.
   The image itself has a hard circular edge — the atmosphere is here rather
   than baked in, because a soft alpha ramp is what makes a photographic PNG
   large, and a radial-gradient costs nothing and scales with the element. */
.auth__orb{position:relative;margin:var(--s-6) auto 0;width:100%;max-width:500px;
  aspect-ratio:1;display:grid;place-items:center}
.auth__orb::before{content:"";position:absolute;inset:-14%;border-radius:50%;
  background:radial-gradient(circle at 42% 38%,
    rgba(111,230,213,.26) 0%, rgba(0,168,150,.14) 42%, transparent 68%)}
.auth__visual{position:relative;display:block;width:100%;height:auto;border-radius:50%;
  filter:drop-shadow(0 32px 70px rgba(0,0,0,.55))}

.auth__note{margin-top:var(--s-6);font-size:11.5px;color:#7FA8A3;max-width:44ch}

/* The one-line summary of what this is, shown ONLY when the visual panel is
   not there to say it.

   Declared here, above the media query that reveals it — not after it. A flat
   `display:none` written below `@media(max-width:980px){display:block}` has
   equal specificity and wins at every width, which is how this ended up
   invisible on the phone it exists for. */
.auth__lead{display:none;margin:0 0 var(--s-4);font-size:13px;line-height:1.5;color:var(--n-500)}

/* ------------------------------------------------------- auth responsive
   1024  the visual panel narrows before the form does — the form has a fixed
         comfortable width and the artwork can give ground
    980  one column. The visual panel goes, but not silently: its headline
         moves above the form so a phone still gets the one sentence that says
         what this is
    480  the panel padding drops to the phone step and the footer stacks */
@media (max-width:1180px){
  .auth{grid-template-columns:minmax(0,480px) minmax(0,1fr)}
  .auth__main{padding:var(--s-6)}
}
@media (max-width:980px){
  .auth{grid-template-columns:1fr}
  .auth__aside{display:none}
  .auth__main{padding:var(--s-6) var(--s-4)}
  /* On one column the form no longer needs to be optically centred in a tall
     panel — it starts under the brand, where the thumb is. */
  /* align-content, not place-items: the base rule sets align-content and
     justify-items separately, and `place-items` sets align-ITEMS — a different
     property — so this override moved nothing. */
  .auth__body{align-content:start;padding-top:var(--s-4);padding-bottom:var(--s-4)}
  .auth__lead{display:block}
}
@media (max-width:480px){
  .auth__main{padding:var(--s-4) var(--s-3) var(--s-6)}
  .auth__brand{margin-bottom:var(--s-6)}
  .auth__brand img{height:34px}
  .auth__form h2{font-size:23px}
  .auth__foot{gap:var(--s-2);font-size:11px}
  .auth__foot nav{margin-left:0;width:100%;gap:var(--s-3)}
}


/* ---------------------------------------------------------------- responsive */
@media (max-width:1180px){
  .cmdsearch{flex-basis:200px}
}
@media (max-width:1024px){
  .workspace__label{display:none}
  .usermenu__meta{display:none}
  /* Scoped to the bar on 6 Oct 2026. Dropping the label is a way of buying
     width in a single 44px row; in the sidebar there is width to spare and the
     label is the half that carries the meaning. Unscoped, the period control
     in the drawer read "open" with nothing to say open WHAT. */
  .cmdbar .cmdctl__k{display:none}
}
@media (max-width:860px){
  .sidebar{position:fixed;left:0;top:0;z-index:120;transform:translateX(-100%);transition:transform .2s ease}
  .app.nav-open .sidebar{transform:translateX(0);box-shadow:var(--shadow-2)}
  .topbar__menu,.cmdbar__menu{display:grid}
  .main{padding:var(--s-3) var(--s-3) var(--s-8)}
  .workspace__name{display:none}
  .workspace__opt{padding:7px 10px}
  .page-head__actions{margin-left:0;width:100%}
}

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

@media print{
  .sidebar,.topbar,.cmdbar,.ctxbar,.bottomnav,.foot,.page-head__actions,.filters{display:none!important}
  body{background:#fff}
  .card,.table-wrap{box-shadow:none;border-color:#ccc}
}

/* ================================================================ phases 2-5
   Components introduced by the accounting, inventory, marketing, channel,
   document and website modules. Appended rather than interleaved so the
   Phase 1 sheet above stays diffable.
   ------------------------------------------------------------------------ */

/* --- stat strip ------------------------------------------------------- */
.stat-row{display:grid;gap:var(--s-3);margin-bottom:var(--s-3);
  grid-template-columns:repeat(auto-fit,minmax(178px,1fr))}
.stat--accent{border-color:var(--accent-line);background:linear-gradient(180deg,var(--accent-soft),var(--n-0) 62%)}
.stat--accent .stat__value{color:var(--accent)}
.stat--ok{border-color:#BFE6D5}      .stat--ok .stat__value{color:var(--ok)}
.stat--warn{border-color:#F0DFB4}    .stat--warn .stat__value{color:var(--warn)}
.stat--danger{border-color:#F2CFCF}  .stat--danger .stat__value{color:var(--danger)}

/* --- section heading between tables ----------------------------------- */
.section-title{font-size:12px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  color:var(--n-500);margin:var(--s-6) 0 var(--s-2)}
.section-title:first-child{margin-top:0}

/* --- pill (same visual language as .chip, used by the newer modules) --- */
.pill{display:inline-flex;align-items:center;gap:5px;padding:2px 9px;border-radius:99px;
  font-size:10.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
  background:var(--n-100);color:var(--n-500);white-space:nowrap}
.pill+.pill{margin-left:5px}
.pill--ok{background:var(--ok-soft);color:var(--ok)}
.pill--warn{background:var(--warn-soft);color:var(--warn)}
.pill--danger{background:var(--danger-soft);color:var(--danger)}
.pill--info{background:var(--info-soft);color:var(--info)}
.pill--muted{background:var(--n-100);color:var(--n-500)}

/* --- inline proportion bar -------------------------------------------- */
.bar{position:relative;height:7px;border-radius:99px;background:var(--n-100);overflow:hidden;min-width:64px}
.bar__fill{position:absolute;inset:0 auto 0 0;background:var(--accent);border-radius:99px}
.bar--danger .bar__fill{background:var(--danger)}

/* --- semantic value colouring inside tables --------------------------- */
.is-danger{color:var(--danger)}
.is-ok{color:var(--ok)}
table.data tr.row--muted td{opacity:.62}
.row-actions{white-space:nowrap;text-align:right}
.row-actions a,.row-actions .linkbtn{color:var(--accent);font-weight:600;font-size:12.5px}
.row-actions a+a,.row-actions a+form,.row-actions form+form{margin-left:10px}
.linkbtn{background:none;border:0;padding:0;cursor:pointer;color:var(--accent);
  font-weight:600;font-size:12.5px}
.linkbtn:hover{text-decoration:underline}

/* --- compact table variant for in-card tables -------------------------
   Redeclares the four geometry properties and nothing else. There used to be
   two competing blocks for this modifier — one at 7px/10px here and a second,
   later one at 7px/14px in the dashboard section — so the horizontal padding of
   every compact table depended on which rule the cascade reached last. One
   block now, and the difference from the standard table is four numbers. */
table.data--compact{--th-h:32px; --row-h:36px; --cell-x:var(--s-3); --cell-y:6px; font-size:12.5px}

/* --- card extras ------------------------------------------------------ */
.card__title{font-size:14px;font-weight:650;padding:var(--s-3) var(--s-4) 0;color:var(--n-900)}
.card__note{font-size:12.5px;color:var(--n-500);line-height:1.55;margin:var(--s-2) var(--s-4) var(--s-3)}
.card__body>.card__note:last-child{margin-left:0;margin-right:0}
.note-card{background:var(--n-0);border:1px solid var(--n-150);border-radius:var(--radius-lg);
  padding:var(--s-3) var(--s-4);margin-top:var(--s-3);box-shadow:var(--shadow-1)}
.note-card h3{font-size:13px;margin-bottom:var(--s-2)}
.bullets{margin:0;padding-left:18px;font-size:12.5px;color:var(--n-600);line-height:1.6}
.bullets li+li{margin-top:var(--s-1)}

/* --- segmented control (report side switches) ------------------------- */
.segmented{display:inline-flex;background:var(--n-100);border-radius:99px;padding:3px;gap:2px}
.segmented__opt{padding:5px 13px;border-radius:99px;font-size:12.5px;font-weight:600;color:var(--n-600)}
.segmented__opt.is-active{background:var(--n-0);color:var(--n-900);box-shadow:var(--shadow-1)}

/* --- marketplace connection rows -------------------------------------- */
.conn{border:1px solid var(--n-150);border-radius:var(--radius);padding:12px 14px;margin-bottom:10px}
.conn__head{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
.conn__meta{font-size:12px;margin-top:5px}
.conn__body{margin-top:10px;border-top:1px solid var(--n-100);padding-top:10px}
.conn__body>summary{cursor:pointer;font-size:12.5px;font-weight:600;color:var(--accent)}
.conn__body>summary::marker{color:var(--n-400)}

/* --- website content blocks ------------------------------------------- */
.block{border:1px solid var(--n-150);border-radius:var(--radius);padding:12px 14px;margin-bottom:12px;
  background:var(--n-25)}
.block__head{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:10px}
.block__head select{flex:1 1 190px}
.block__fields{display:grid;gap:10px}
.block__fields textarea{min-height:78px}

/* A hint is normally inside .field, but forms that group several controls use
   it standalone too. */
.hint{font-size:11.5px;color:var(--n-500);margin-top:5px}
.hint.is-danger{color:var(--danger)}

/* ================================================================ projects
   Country tabs and project cards. The card has to read as a physical object
   you can pick up — that is what makes drag-and-drop discoverable at all.
   ------------------------------------------------------------------------ */
.ptabs__bar{display:flex;gap:6px;flex-wrap:wrap;border-bottom:1px solid var(--n-150);
  margin-bottom:18px}
.ptabs__tab{display:inline-flex;align-items:center;gap:8px;background:none;border:0;
  border-bottom:2px solid transparent;padding:10px 14px;cursor:pointer;font-size:13.5px;
  font-weight:600;color:var(--n-500);border-radius:8px 8px 0 0}
.ptabs__tab:hover{color:var(--n-800);background:var(--n-50)}
.ptabs__tab.is-active{color:var(--n-900);border-bottom-color:var(--tab-accent,var(--accent))}
.ptabs__dot{width:9px;height:9px;border-radius:99px;background:var(--tab-accent,var(--accent));flex:0 0 9px}
.ptabs__count{background:var(--n-100);color:var(--n-600);border-radius:99px;
  padding:1px 8px;font-size:11px;font-weight:800;font-variant-numeric:tabular-nums}
.ptabs__tab.is-active .ptabs__count{background:var(--n-900);color:var(--n-0)}
.ptabs__tab.is-drop-target{background:var(--accent-soft);border-bottom-color:var(--accent);
  color:var(--n-900);outline:2px dashed var(--accent-line);outline-offset:-2px}
.ptabs__panel{display:none}
.ptabs__panel.is-active{display:block}

/* --- the grid, and its empty state as a drop zone --------------------- */
.pgrid{align-items:start;min-height:150px;border-radius:var(--radius-lg);
  transition:background .12s ease, box-shadow .12s ease}
.pgrid.is-drop-into{background:var(--accent-soft);
  box-shadow:inset 0 0 0 2px var(--accent-line)}
.pgrid--empty{grid-template-columns:1fr}
.pcard-empty{grid-column:1/-1;border:2px dashed var(--n-200);border-radius:var(--radius-lg);
  padding:38px 20px;text-align:center;color:var(--n-500);background:var(--n-25)}
.pcard-empty strong{display:block;color:var(--n-700);font-size:15px;margin-bottom:6px}
.pcard-empty span{font-size:13px}

/* --- the card --------------------------------------------------------- */
.pcard{position:relative;display:flex;flex-direction:column;background:var(--n-0);
  border:1px solid var(--n-200);border-radius:var(--radius-lg);
  box-shadow:0 1px 2px rgba(21,32,43,.05), 0 4px 10px rgba(21,32,43,.05);
  overflow:hidden;transition:box-shadow .14s ease, transform .14s ease, border-color .14s ease}
/* A 4px accent rail along the top: it names the brand at a glance and gives the
   card a defined edge no matter what the background is. */
.pcard::before{content:"";position:absolute;inset:0 0 auto 0;height:4px;
  background:var(--card-accent,var(--accent))}
.pcard:hover{transform:translateY(-2px);border-color:var(--n-300);
  box-shadow:0 2px 4px rgba(21,32,43,.06), 0 10px 22px rgba(21,32,43,.10)}
.pcard.is-archived{opacity:.55}
.pcard.is-dragging{opacity:.45;transform:rotate(-1.2deg) scale(.98);box-shadow:none;
  border-style:dashed}
.is-dragging-active .pcard{cursor:grabbing}

.pcard__body{display:block;padding:18px 16px 4px;flex:1}
.pcard__head{display:flex;align-items:center;gap:12px;margin-bottom:12px}
.pcard__name{display:block;font-size:15.5px;line-height:1.3;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.pcard__code{font-size:11px;letter-spacing:.06em;text-transform:uppercase}
.pcard__chips{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px}
.pcard__kv{margin:0;padding-top:12px;border-top:1px solid var(--n-100)}
.pcard__kv dt{font-size:11px}
.pcard__kv dd{font-size:12.5px}

/* Grip and edit affordances sit in the card's top rail, out of the way of the
   title, and only become prominent on hover. */
.pcard__grip{position:absolute;top:9px;right:34px;color:var(--n-300);cursor:grab;
  padding:5px;line-height:0;fill:currentColor;z-index:2;border-radius:6px}
.pcard__grip:active{cursor:grabbing}
.pcard:hover .pcard__grip{color:var(--n-500);background:var(--n-50)}
.pcard__edit{position:absolute;top:9px;right:8px;color:var(--n-300);padding:5px;
  line-height:0;z-index:2;border-radius:6px}
.pcard:hover .pcard__edit{color:var(--n-500);background:var(--n-50)}
.pcard__edit:hover{color:var(--accent)}

/* Drag feedback. A refusal explains a rule, so it stays on screen much longer
   than a confirmation and is styled to be read, not glanced at. */
.drag-toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:60;
  max-width:min(680px,calc(100vw - 32px));background:var(--n-900);color:var(--n-0);
  padding:12px 18px;border-radius:var(--radius);box-shadow:var(--shadow-2);
  font-size:13px;line-height:1.55}
.drag-toast--ok{background:var(--ok)}
.drag-toast--err{background:var(--danger)}

/* Company placement panel on the project edit screen. */
.placement{display:grid;gap:10px}
.placement__row{display:flex;align-items:center;justify-content:space-between;gap:12px;
  border:1px solid var(--n-150);border-radius:var(--radius);padding:11px 13px}
.placement__row.is-owner{border-color:var(--accent-line);background:var(--accent-soft)}
.placement__meta{font-size:12px;color:var(--n-500);margin-top:3px}
.grantrow{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;
  border-bottom:1px solid var(--n-100);padding:7px 0}
.grantrow__edit{flex:0 0 auto;margin:0}

/* Cash-flow: a two-sided bar so money in and money out read at a glance. */
.flowbar{display:flex;align-items:center;height:12px;width:100%}
.flowbar__in{background:var(--ok);height:8px;border-radius:4px 0 0 4px;margin-left:auto}
.flowbar__mid{width:1px;height:12px;background:var(--n-300);flex:0 0 1px}
.flowbar__out{background:var(--danger);height:8px;border-radius:0 4px 4px 0;margin-right:auto}
table.data tr.row--danger td{background:var(--danger-soft)}

/* ==========================================================================
   Grouped sidebar
   A flat list of twenty-six links is an inventory, not a menu. Groups collapse,
   the group containing the current page opens itself, and the whole thing stays
   legible when the sidebar is collapsed to icons.
   ========================================================================== */
.navgroup{margin:1px 0}
.navgroup__btn{display:flex;align-items:center;gap:10px;width:100%;padding:8px 12px;
  border:0;background:none;border-radius:8px;cursor:pointer;text-align:left;
  color:var(--n-300);font:inherit;font-size:13.2px;font-weight:600;letter-spacing:.005em}
.navgroup__btn:hover{background:rgba(255,255,255,.06);color:#fff}
.navgroup__btn svg{flex:0 0 17px;opacity:.85}
.navgroup__btn span{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.navgroup__chev{flex:0 0 14px;opacity:.55;transition:transform .16s ease}
.navgroup.is-open>.navgroup__btn{color:#fff}
.navgroup.is-open .navgroup__chev{transform:rotate(180deg)}
.navgroup .nav--sub{display:none;margin:2px 0 6px;padding-left:26px;
  border-left:1px solid rgba(255,255,255,.09);margin-left:19px}
.navgroup.is-open .nav--sub{display:block}
.nav--sub .nav__link{padding:6px 10px;font-size:12.7px;color:var(--n-400)}
.nav--sub .nav__link:hover{color:#fff}
.nav--sub .nav__link.is-active{background:rgba(255,255,255,.10);color:#fff;font-weight:600}
/* Collapsed to icons: labels and sub-lists go, the group icon stays clickable. */
.app.is-collapsed .navgroup__btn span,.app.is-collapsed .navgroup__chev{display:none}
.app.is-collapsed .navgroup__btn{justify-content:center;padding:9px 0}
.app.is-collapsed .navgroup .nav--sub{display:none}

/* ==========================================================================
   Dashboard grid
   Twelve columns, cards that declare their own span, and a drag handle on the
   header. Reordering is persisted per user, so two people can arrange the same
   dashboard differently without arguing about it.
   ========================================================================== */
.page-head--tight{margin-bottom:14px}
.dash{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--gap-grid);align-items:stretch}
/* Cards in the same row share a height. Ragged rows are what made the old
   dashboard read as a pile of boxes rather than a grid. */
.dash__card{height:100%}
.dash__card{grid-column:span 4;background:var(--n-0);border:1px solid var(--n-150);
  border-radius:var(--radius-lg,14px);box-shadow:var(--shadow-1);display:flex;flex-direction:column;
  min-width:0;overflow:hidden}
.dash__card--4{grid-column:span 4}
.dash__card--6{grid-column:span 6}
.dash__card--8{grid-column:span 8}
.dash__card--12{grid-column:span 12}
.dash__head{display:flex;align-items:center;gap:var(--s-2);padding:var(--s-2) var(--s-3);
  border-bottom:1px solid var(--n-100);background:var(--n-25)}
.dash__head h2{font-size:13.5px;font-weight:650;flex:1;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dash__grip{display:none;color:var(--n-400);cursor:grab;flex:0 0 13px}
.dash__grip svg{fill:currentColor}
.dash__hide{display:none;border:0;background:none;color:var(--n-400);cursor:pointer;
  font-size:17px;line-height:1;padding:0 2px;border-radius:5px}
.dash__hide:hover{color:var(--danger);background:var(--danger-soft)}
.dash__body{padding:var(--s-3);display:flex;flex-direction:column;gap:var(--s-3);flex:1;min-width:0}
/* An empty card centres its message instead of leaving it stranded at the top. */
.dash__body>.empty{margin:auto 0;padding:18px 6px}
.dash__foot{margin-top:auto;padding-top:10px;border-top:1px solid var(--n-100);
  font-size:11.5px;color:var(--n-500);display:flex;gap:10px;flex-wrap:wrap;line-height:1.5}
.dash__foot a{font-weight:600}
.dash__warn{margin:0;font-size:12px;color:var(--danger);font-weight:600}
.dash__split{display:grid;grid-template-columns:minmax(0,1fr) 190px;gap:16px;align-items:start}
.dash__chart{min-width:0}
.dash__side{display:grid;gap:10px;align-content:start}

/* Arrange mode: only then do the grip and the hide button appear, so the normal
   view is not cluttered by controls nobody is using. */
.dash.is-arranging .dash__grip,.dash.is-arranging .dash__hide{display:inline-flex;align-items:center}
.dash.is-arranging .dash__head{cursor:grab;background:var(--accent-soft)}
.dash.is-arranging .dash__card{outline:1px dashed var(--accent-line)}
.dash__card.is-dragging{opacity:.45}
.dash__card.is-drop{outline:2px solid var(--accent);outline-offset:2px}
.dash-hint{background:var(--accent-soft);border:1px solid var(--accent-line);color:var(--n-700);
  border-radius:var(--radius);padding:10px 13px;font-size:12.5px;margin-bottom:12px}
.dash-add{position:relative}
.dash-add>summary{list-style:none;cursor:pointer}
.dash-add>summary::-webkit-details-marker{display:none}
.dash-add__menu{position:absolute;right:0;top:calc(100% + 6px);z-index:40;min-width:250px;
  background:var(--n-0);border:1px solid var(--n-150);border-radius:12px;
  box-shadow:var(--shadow-2,0 10px 30px rgba(20,32,44,.14));padding:6px;display:grid;gap:2px}
.dash-add__item{display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;border:0;background:none;padding:8px 10px;border-radius:8px;cursor:pointer;
  font:inherit;font-size:13px;text-align:left;color:var(--n-700)}
.dash-add__item:hover{background:var(--n-100)}
.dash-add__item small{color:var(--n-400);font-size:11px}

/* Numbers on cards. */
.kpi{display:grid;gap:2px}
.kpi__label{font-size:11.5px;color:var(--n-500);font-weight:600;display:flex;align-items:center;gap:6px}
.kpi__value{font-size:25px;font-weight:680;letter-spacing:-.02em;color:var(--n-900);
  font-variant-numeric:tabular-nums;line-height:1.15}
.kpi__value small{font-size:12px;font-weight:600;color:var(--n-400);margin-left:6px}
.kpi__note{font-size:11.5px;color:var(--n-500)}
.kpi--sm .kpi__value{font-size:17px}
.kpi-row{display:flex;gap:22px;flex-wrap:wrap}
.kpi-split{display:flex;gap:16px;flex-wrap:wrap;font-size:12px;color:var(--n-600)}
.kpi-split i,.swatch,.kpi__label i.dot{width:9px;height:9px;border-radius:3px;display:inline-block;
  margin-right:6px;flex:0 0 9px}
.kpi__label i.dot{border-radius:99px;margin-right:0}

/* Compact tables inside cards. */
table.mini{width:100%;border-collapse:collapse;font-size:12.5px}
table.mini td{padding:5px 0;border-bottom:1px solid var(--n-100);vertical-align:top}
table.mini tr:last-child td{border-bottom:0}
table.mini td.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
table.mini tr.mini__rule td{border-top:1px solid var(--n-200);padding-top:8px}
table.mini--rows td{padding:8px 0}
.mini__label{font-size:12.5px;margin-bottom:5px}
.bar{height:6px;background:var(--n-100);border-radius:99px;overflow:hidden}
.bar span{display:block;height:100%;background:var(--accent);border-radius:99px}
.period-strip{display:flex;gap:5px;flex-wrap:wrap}
.table-wrap--flush{border:0;border-radius:0;margin:0 calc(var(--s-3) * -1)}
/* (the second, conflicting .data--compact padding block used to live here —
    the modifier is now declared once, with the other table geometry) */

.feed{list-style:none;margin:0;padding:0;display:grid;gap:8px;font-size:12.3px}
.feed li{display:grid;grid-template-columns:74px 1fr auto;gap:9px;align-items:baseline}
.feed__when{color:var(--n-400);font-size:11px;white-space:nowrap}
.feed__what{color:var(--n-700);min-width:0}
.feed__who{color:var(--n-400);font-size:11px;white-space:nowrap}

/* ==========================================================================
   Server-rendered SVG charts
   ========================================================================== */
.c-svg{display:block;width:100%;height:auto;overflow:visible}
.c-split{display:block;width:100%;height:10px;border-radius:99px;overflow:hidden}
.c-grid{stroke:var(--n-100);stroke-width:1}
.c-zero{stroke:var(--n-300);stroke-width:1.4}
.c-tick{fill:var(--n-400);font-size:12px;font-family:inherit}
.c-empty{fill:var(--n-400);font-size:14px;font-family:inherit}
.c-centre{fill:var(--n-900);font-size:18px;font-weight:700;font-family:inherit}
.c-centre-sub{fill:var(--n-400);font-size:10px;font-family:inherit}
.c-legend{display:flex;gap:16px;flex-wrap:wrap;font-size:11.5px;color:var(--n-600);margin-top:2px}
.c-legend__item{display:flex;align-items:center;gap:6px}
.c-legend__item i{width:9px;height:9px;border-radius:3px;display:inline-block}
.donut-wrap{display:flex;justify-content:center;padding:2px 0}
.donut-wrap .c-svg{max-width:190px}
.is-warn{color:var(--warn)}

/* The breakpoint is set by the narrowest a card may become before its numbers
   stop being readable — about 300px — not by a device size. At 1280 with the
   sidebar open a third of the row is still ~320px, so three across holds. */
@media (max-width:1120px){
  .dash__card--8{grid-column:span 12}
  .dash__card--6{grid-column:span 6}
  .dash__card--4{grid-column:span 6}
}
@media (max-width:900px){
  .dash{grid-template-columns:repeat(6,1fr)}
  .dash__card,.dash__card--4,.dash__card--6,.dash__card--8,.dash__card--12{grid-column:span 6}
  .dash__split{grid-template-columns:1fr}
  .dash__side{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:600px){
  .kpi__value{font-size:21px}
  .kpi-row{gap:14px}
  .feed li{grid-template-columns:1fr;gap:2px}
  .dash__side{grid-template-columns:1fr}
}


/* --------------------------------------------------------------------------
   Narrow-screen corrections found by rendering the real page at 390px.
   -------------------------------------------------------------------------- */
@media (max-width:760px){
  .page-head--tight h1{font-size:15px}
  .console{grid-template-columns:1fr}
  .sumstrip{grid-template-columns:repeat(auto-fit,minmax(122px,1fr))}
}

/* ==========================================================================
   Sheet forms
   A dense, spreadsheet-like entry surface. The old product form spread twenty
   fields down two tall columns, which meant scrolling past most of the page to
   reach the field you wanted. This puts a whole section on one screen: a
   twelve-column grid, one-line labels, and units printed inside the box rather
   than spelled out beside it.
   ========================================================================== */
.sheet{background:var(--n-0);border:1px solid var(--n-150);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-1);margin-bottom:12px;overflow:hidden}
.sheet__head{display:flex;align-items:baseline;gap:12px;padding:9px 14px;
  background:var(--n-25);border-bottom:1px solid var(--n-100)}
.sheet__head h2{font-size:12.5px;font-weight:700;letter-spacing:.02em;
  text-transform:uppercase;color:var(--n-700);white-space:nowrap}
.sheet__note{font-size:11.5px;color:var(--n-500);line-height:1.45}
.sheet__grid{display:grid;grid-template-columns:repeat(12,1fr);gap:0;
  border-top:1px solid transparent}
.sheet--actions{display:flex;align-items:center;gap:10px;padding:12px 14px;flex-wrap:wrap}

/* Each cell is a bordered box in the grid, so the whole block reads as a sheet
   rather than as a stack of floating inputs. */
.cell{grid-column:span 2;display:flex;flex-direction:column;gap:3px;
  padding:8px 12px;border-right:1px solid var(--n-100);border-bottom:1px solid var(--n-100);
  min-width:0}
.cell--1{grid-column:span 1}  .cell--2{grid-column:span 2}
.cell--3{grid-column:span 3}  .cell--4{grid-column:span 4}
.cell--6{grid-column:span 6}  .cell--8{grid-column:span 8}
.cell--12{grid-column:span 12;border-right:0}
.cell>span{font-size:10.5px;font-weight:700;letter-spacing:.045em;text-transform:uppercase;
  color:var(--n-500);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cell>span b{color:var(--danger);font-weight:700}
.cell>small{font-size:10.5px;color:var(--n-400);line-height:1.4}
.cell input,.cell select,.cell textarea{width:100%;border:0;background:none;padding:2px 0;
  font:inherit;font-size:13.2px;color:var(--n-900);border-bottom:1px solid transparent;
  border-radius:0;min-width:0}
.cell input:focus,.cell select:focus,.cell textarea:focus{outline:0;
  border-bottom-color:var(--accent);box-shadow:none}
.cell textarea{resize:vertical;line-height:1.5}
.cell select{cursor:pointer}
.cell:focus-within{background:var(--accent-soft)}

/* A unit belongs inside the field, not in the label. */
.withunit{display:flex;align-items:baseline;gap:5px;min-width:0}
.withunit input{flex:1;min-width:0}
.withunit i{font-style:normal;font-size:11px;color:var(--n-400);font-weight:600;white-space:nowrap}

.switches{flex-direction:row;flex-wrap:wrap;gap:16px;align-items:center;border-right:0}
.switches label{display:flex;align-items:center;gap:6px;font-size:12.8px;
  color:var(--n-700);white-space:nowrap;cursor:pointer;text-transform:none;letter-spacing:0}
.switches input{width:auto}

.marginbox{justify-content:center;background:var(--n-25)}
.marginbox strong{font-size:17px;font-variant-numeric:tabular-nums;color:var(--n-900)}
.marginbox strong.is-thin{color:var(--warn)}
.marginbox strong.is-loss{color:var(--danger)}
/* A total row in a compact table, and a negative figure anywhere in one. */
.data--compact tr.is-total td{border-top:1px solid var(--n-300);background:var(--n-50)}
/* Statement tables: a section heading, a subtotal and the closing total. */
.data tr.is-section td{background:var(--n-100);font-size:11px;text-transform:uppercase;
  letter-spacing:.09em;color:var(--n-600);padding-top:12px}
.data tr.is-total td{border-top:1px solid var(--n-300);background:var(--n-50)}
.data tr.is-grand td{border-top:2px solid var(--n-700);border-bottom:3px double var(--n-700);
  background:var(--n-50);font-size:14px}
.data td.is-loss,.data td.is-loss strong{color:var(--danger)}

@media (max-width:1120px){
  .cell--2{grid-column:span 3}
  .cell--3{grid-column:span 4}
  .cell--4{grid-column:span 6}
}
@media (max-width:820px){
  .cell,.cell--1,.cell--2,.cell--3,.cell--4,.cell--6,.cell--8{grid-column:span 6}
  .sheet__head{flex-direction:column;gap:2px}
}
@media (max-width:520px){
  .cell,.cell--1,.cell--2,.cell--3,.cell--4,.cell--6,.cell--8,.cell--12{grid-column:span 12;border-right:0}
}

/* ==========================================================================
   Brand identity and logo
   ========================================================================== */
.logo-preview{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.logo-preview img{max-height:64px;max-width:200px;border:1px solid var(--n-150);
  border-radius:8px;padding:6px;background:var(--n-0)}
.logo-preview .empty-logo{width:120px;height:64px;border:1px dashed var(--n-200);
  border-radius:8px;display:flex;align-items:center;justify-content:center;
  color:var(--n-400);font-size:11px;text-align:center;padding:6px}
.letterhead-preview{border:1px solid var(--n-150);border-radius:var(--radius);
  padding:16px 18px;background:var(--n-0)}
.letterhead-preview .lh-name{font-size:17px;font-weight:700}
.letterhead-preview .lh-meta{font-size:12px;color:var(--n-500);line-height:1.6;margin-top:4px}
.letterhead-preview .lh-rule{height:3px;border-radius:2px;margin:12px 0 10px}
.sendbox{display:grid;gap:10px}
.mailstate{display:flex;align-items:center;gap:8px;font-size:12.5px}
.pcard__edit--brand{right:38px}

/* A deactivated row is visible but obviously not live. */
tr.is-trashed td{opacity:.62;background:var(--n-25)}
tr.is-trashed td strong{text-decoration:line-through}


/* =====================================================================
   Mobile platform layer: bottom navigation, capture, review, card lists.
   Desktop is untouched — everything here activates under 860px except the
   components that are useful at any width (capture buttons, review grid).
   ===================================================================== */

/* ---- generic stacked list (used by queue, capture, review log) ---- */
.stack-list{display:grid;gap:0;border-top:1px solid var(--n-150)}
.stack-item{display:flex;justify-content:space-between;align-items:center;gap:12px;
  padding:12px 14px;border-bottom:1px solid var(--n-150);color:inherit;text-decoration:none}
a.stack-item:active{background:var(--n-50)}

/* ---- confidence chips on the review form ---- */
.conf{display:inline-block;margin-left:6px;padding:1px 7px;border-radius:99px;
  font-size:10.5px;font-weight:700;vertical-align:1px}
.conf--ok{background:#DCF3EA;color:#146C43}
.conf--warn{background:#FBF0D3;color:#7A5310}
.conf--danger{background:#F9E1E0;color:#8C2020}

/* ---- capture screen ---- */
.cap-actions{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px}
.cap-btn{display:grid;gap:2px;justify-items:center;text-align:center;padding:18px 10px;
  border:1.5px dashed var(--n-300);border-radius:14px;cursor:pointer;min-height:44px}
.cap-btn:active{background:var(--n-50)}
.cap-btn--camera{border-color:var(--accent);border-style:solid}
.cap-btn__icon{font-size:26px}
.cap-btn small{color:var(--n-500);font-size:11.5px}
.cap-drop{margin-top:10px;padding:18px;border:1.5px dashed var(--n-300);border-radius:12px;
  text-align:center;color:var(--n-500);font-size:13px}
.cap-drop.is-over{border-color:var(--accent);background:var(--n-50)}
.cap-list{display:grid;gap:8px;margin-top:12px}
.cap-item{display:flex;gap:10px;align-items:center;border:1px solid var(--n-150);
  border-radius:12px;padding:8px 10px}
.cap-item img{width:52px;height:52px;object-fit:cover;border-radius:8px;background:var(--n-100)}
.cap-item__meta{flex:1;min-width:0;font-size:12.5px}
.cap-item__meta strong{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cap-item__tools{display:flex;gap:4px}
.cap-item__tools button{min-width:34px;min-height:34px;border:1px solid var(--n-200);
  border-radius:8px;background:#fff;cursor:pointer;font-size:14px}
.cap-item.is-done{border-color:#146C43;background:#F4FBF8}
.cap-item.is-error{border-color:#B3261E;background:#FDF6F6}

/* ---- review screen ---- */
.doc-preview{background:var(--n-100);display:grid;place-items:center;min-height:220px}
.doc-preview img{max-width:100%;max-height:70vh;object-fit:contain}
.doc-preview iframe{width:100%;height:70vh;border:0}
.doc-text{margin:0;padding:14px;font-size:11.5px;line-height:1.5;white-space:pre-wrap;
  word-break:break-word;max-height:320px;overflow:auto;background:var(--n-25)}
details.card>summary{list-style:none}
details.card>summary::-webkit-details-marker{display:none}

/* ---- bottom navigation (mobile only) ---- */
.bottomnav{display:none}
@media (max-width:860px){
  .bottomnav{position:fixed;bottom:0;left:0;right:0;z-index:110;
    display:grid;grid-auto-flow:column;grid-auto-columns:1fr;align-items:end;
    background:#fff;border-top:1px solid var(--n-150);
    padding:6px max(8px, env(safe-area-inset-left)) max(6px, env(safe-area-inset-bottom));
    box-shadow:0 -4px 18px rgba(21,32,43,.07)}
  .bottomnav a,.bottomnav button{display:grid;justify-items:center;gap:2px;padding:6px 2px;
    min-height:48px;color:var(--n-500);text-decoration:none;background:none;border:0;
    font:inherit;font-size:10.5px;cursor:pointer}
  .bottomnav a.is-active{color:var(--accent)}
  .bottomnav svg circle{fill:currentColor}
  .bottomnav a.bottomnav__capture{position:relative;top:-16px;width:54px;height:54px;margin:0 auto;
    border-radius:50%;background:var(--accent);color:#fff!important;display:grid;place-items:center;
    box-shadow:0 6px 16px rgba(0,0,0,.22);border:4px solid #fff}
  /* room so content never hides behind the bar */
  .main{padding-bottom:calc(76px + env(safe-area-inset-bottom))}
  .foot{display:none}
}

/* The hidden attribute must beat any class display value, or "hidden until
   JS decides" quietly becomes "always visible". */
[hidden]{display:none!important}

/* ---- install banner ---- */
.pwa-banner{position:fixed;left:12px;right:12px;bottom:calc(84px + env(safe-area-inset-bottom));
  z-index:130;display:flex;gap:12px;align-items:center;justify-content:space-between;
  background:#15202B;color:#fff;border-radius:14px;padding:12px 14px;
  box-shadow:0 10px 30px rgba(0,0,0,.3)}
.pwa-banner__text{font-size:12.5px;line-height:1.4}
.pwa-banner__text strong{display:block;font-size:13.5px}
.pwa-banner__text span{color:#B9C4CE}
.pwa-banner__actions{display:flex;gap:6px;flex-shrink:0}
@media (min-width:861px){.pwa-banner{max-width:420px;left:auto;bottom:18px;right:18px}}

/* ---- responsive table/card switches ---- */
.show-mobile{display:none}
@media (max-width:700px){
  .hide-mobile{display:none!important}
  .show-mobile{display:grid}
}

/* ---- general mobile ergonomics ---- */
@media (max-width:860px){
  /* touch targets: every control at least 44px tall */
  .btn{min-height:42px}
  /*
   | Not a checkbox and not a radio.
   |
   | An unqualified `input{min-height:44px}` stretched every tick box in the
   | application to 44px tall while it stayed 16px wide, so on a phone the box
   | sat about fifteen pixels below the label it belongs to — on the sign-in
   | page's "Keep me signed in", on every permission row, on every filter. The
   | touch target is not lost: these controls are always inside a
   | <label class="checkline">, and the label is given the height instead, so
   | the whole line is tappable rather than a 16px square.
  */
  input:not([type=checkbox]):not([type=radio]),select,textarea{min-height:44px;font-size:16px} /* 16px stops iOS zoom-on-focus */
  .checkline{min-height:44px;align-items:center}
  .checkline input{margin-top:0}
  textarea{min-height:80px}
  .filters{display:grid;grid-template-columns:1fr 1fr;gap:8px}
  .filters .field{margin:0}
  .filters button{grid-column:1/-1}
  .page-head{flex-direction:column;align-items:flex-start;gap:10px}
  .page-head__actions{display:flex;flex-wrap:wrap;gap:8px}
  .grid--2,.grid--3{grid-template-columns:1fr}
  /* forms: the 12-column sheet collapses to a single column */
  .sheet__grid{grid-template-columns:1fr!important}
  .sheet__grid .cell,.sheet__grid [class*="cell--"]{grid-column:auto!important}
  /* financial tables that keep table form scroll sideways without breaking the page */
  .table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
  .table-wrap table{min-width:560px}
  .review-grid{display:grid;grid-template-columns:1fr}
  .pill-tabs{overflow-x:auto;flex-wrap:nowrap;-webkit-overflow-scrolling:touch}
}

/* installed-app mode: breathing room for the iOS status bar */
@media (display-mode: standalone){
  .cmdbar{height:auto;padding-top:max(6px, env(safe-area-inset-top));padding-bottom:6px}
}

/* Mobile drawer: scrim + scroll. Without these the open sidebar looked like a
   page that had simply jumped, and long nav lists could not be reached. */
@media (max-width:860px){
  .sidebar{overflow-y:auto;max-height:100dvh;width:min(84vw,300px)}
  .app.nav-open::after{content:"";position:fixed;inset:0;z-index:119;
    background:rgba(21,32,43,.45);backdrop-filter:blur(1px)}
}

/* ---- crop & perspective editor ---- */
.cap-editor{position:fixed;inset:0;z-index:200;background:rgba(21,32,43,.72);
  display:grid;place-items:center;padding:12px}
.cap-editor__panel{background:#fff;border-radius:16px;width:min(680px,100%);
  max-height:94dvh;overflow:auto;display:grid;gap:10px;padding:14px}
.cap-editor__head{display:grid;gap:2px}
.cap-editor__head .muted{font-size:12px}
.cap-editor__stage{background:var(--n-100);border-radius:10px;display:grid;
  place-items:center;padding:6px;overflow:hidden}
#capEditorCanvas{max-width:100%;touch-action:none;cursor:crosshair;border-radius:6px}
.cap-editor__tools{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.cap-editor__actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.cap-item__badge{width:52px;height:52px;border-radius:8px;background:var(--n-100);
  display:grid;place-items:center;font-size:11px;font-weight:800;color:var(--n-500);flex:0 0 52px}
.cap-item__state{display:block;font-size:11.5px;margin-top:2px}

/* Users table: one line per workspace, its name then the roles held in it. */
.wsrow{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap;margin:2px 0}
.wsrow__co{font-weight:600;font-size:12px;min-width:150px}
@media (max-width:860px){.wsrow__co{min-width:0;width:100%}}

/* Per-user recovery menu on the users list. */
/* .recover-menu removed: users/index.blade.php now uses the shared
   <x-row-menu>, and nothing else referenced it. */
details > summary::-webkit-details-marker{display:none}


/* ---- collapsible dashboard cards ---------------------------------------- */
.dash__collapse{display:inline-flex;align-items:center;justify-content:center;
  border:0;background:none;color:var(--n-400);cursor:pointer;padding:2px;border-radius:6px;
  flex:0 0 auto;transition:transform .2s ease,color .15s ease}
.dash__collapse:hover{color:var(--n-700);background:var(--n-100)}
.dash__collapse:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.dash__card.is-collapsed .dash__collapse{transform:rotate(-90deg)}
.dash__card.is-collapsed .dash__body{display:none}
.dash__card.is-collapsed{height:auto;align-self:start}
.dash__card.is-collapsed .dash__head{border-bottom:0}

/* ---- drag: lifted card, placeholder, and the grid while it happens ------- */
.dash__card{transition:box-shadow .18s ease,border-color .18s ease}
.dash__card.is-dragging{position:absolute;z-index:300;margin:0;pointer-events:none;
  box-shadow:0 18px 44px rgba(21,32,43,.22);border-color:var(--accent);
  transform-origin:center;will-change:transform;opacity:.97}
.dash__placeholder{border:2px dashed var(--accent-line);border-radius:12px;
  background:var(--accent-soft);opacity:.6}
body.dash-dragging{cursor:grabbing;user-select:none}
body.dash-dragging *{cursor:grabbing!important}
.dash.is-arranging .dash__head{cursor:grab;touch-action:none}
.dash.is-arranging .dash__card{outline:1px dashed var(--accent-line);outline-offset:1px}
.dash.is-arranging .dash__card:hover{border-color:var(--accent)}
@media (prefers-reduced-motion:reduce){
  .dash__card{transition:none!important}
}
.dash-hint kbd{display:inline-block;padding:1px 5px;border:1px solid var(--n-300);
  border-bottom-width:2px;border-radius:4px;font:inherit;font-size:11px;background:#fff}
.dash.is-arranging .dash__card:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.dash__card:focus{outline:none}

/* ================================================================ record actions
   Edit / Delete / Revise / History, wherever an accounting record is shown.
   ------------------------------------------------------------------------ */
.rec-actions{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.rec-actions--compact{gap:4px;justify-content:flex-end}
.rec-actions__form{display:inline}
/* An action the record's state forbids is shown and explained rather than
   removed: a missing button reads as a bug, a disabled one teaches the rule. */
.rec-actions .is-disabled{opacity:.45;cursor:not-allowed;pointer-events:auto}

.rec-menu{position:relative;display:inline-block}
.rec-menu > summary{list-style:none;cursor:pointer}
.rec-menu > summary::-webkit-details-marker{display:none}
.rec-menu__panel{position:absolute;right:0;top:calc(100% + 6px);z-index:60;width:330px;
  background:var(--n-0);border:1px solid var(--n-200);border-radius:12px;
  box-shadow:0 18px 40px rgba(16,24,40,.16);padding:13px 14px;text-align:left}
.rec-menu__panel p{margin:0 0 10px;font-size:12px;line-height:1.5}
.rec-menu__panel .field{margin-bottom:10px}
.rec-menu__panel textarea{min-height:66px;font-size:13px}
@media (max-width:700px){
  .rec-menu__panel{position:fixed;left:12px;right:12px;top:auto;bottom:12px;width:auto}
}

.field--reason input{background:#FFFBEB;border-color:#F5D98A}
.field--reason input:focus{border-color:var(--accent)}

/* Previous value / new value, side by side in the revision history. */
.valdiff{width:100%;margin-top:8px;border-collapse:collapse;font-size:11.5px}
.valdiff th{text-align:left;font-weight:700;color:var(--n-500);padding:3px 8px 3px 0;
  font-size:10.5px;letter-spacing:.06em;text-transform:uppercase}
.valdiff td{padding:3px 8px 3px 0;vertical-align:top;border-top:1px solid var(--n-100);
  word-break:break-word;max-width:280px}
.valdiff__old{color:#9A3412;text-decoration:line-through;text-decoration-color:rgba(154,52,18,.35)}
.valdiff__new{color:#166534}

/* ================================================================ chart of accounts
   A foldable hierarchy, and filter controls that line up.
   ------------------------------------------------------------------------ */

/* The search box had no `type` attribute, so `input[type=text]` never matched
   it and it rendered at the browser default height beside two fully styled
   selects. Matching type=search here fixes the alignment for good, and the
   wider basis makes an account name a comfortable thing to type. */
/* Sizing comes from the shared rule above; this is only what a search field
   needs on top of it — no native chrome, and a clear button we can style. */
input[type=search]{-webkit-appearance:none;appearance:none}
input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;
  width:15px;height:15px;cursor:pointer;opacity:.5;
  background:currentColor;
  -webkit-mask:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M5 5l10 10M15 5L5 15' stroke='black' stroke-width='2.4' stroke-linecap='round'/></svg>") center/contain no-repeat;
          mask:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M5 5l10 10M15 5L5 15' stroke='black' stroke-width='2.4' stroke-linecap='round'/></svg>") center/contain no-repeat}
input[type=search]::-webkit-search-cancel-button:hover{opacity:.85}

.filters--accounts{align-items:flex-end}
.filters__tools{display:flex;gap:6px;margin-left:auto;padding-bottom:1px}
@media (max-width:760px){
  .filters__tools{margin-left:0}
}

.acct-count{margin:-6px 0 14px;font-size:12.5px}

.acct-group__head{display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none}
.acct-group__head h2{margin:0}
.acct-group__head > .muted{margin-left:auto;font-size:11.5px}

.acct-toggle{display:grid;place-items:center;width:22px;height:22px;flex:0 0 22px;
  border:1px solid var(--n-200);border-radius:7px;background:var(--n-0);color:var(--n-600);
  cursor:pointer;padding:0;transition:transform .14s ease,background .12s,border-color .12s}
.acct-toggle:hover{background:var(--n-50);border-color:var(--n-300);color:var(--n-900)}
.acct-toggle[aria-expanded="true"]{transform:rotate(90deg)}
.acct-toggle--row{width:18px;height:18px;flex-basis:18px;border-radius:6px;vertical-align:middle;
  margin-right:6px}
.acct-toggle--leaf{width:18px;height:18px;flex-basis:18px;border:0;background:transparent;
  display:inline-block;margin-right:6px;vertical-align:middle}

/* One indent step per level of the chart. */
.acct-indent{display:inline-block;width:calc(var(--d, 0) * 20px)}

.acct-table td{vertical-align:middle}
.acct-table tr[data-acct-depth="0"] td:first-child strong{font-weight:750}
.acct-table tr[data-acct-depth]:not([data-acct-depth="0"]) td:first-child strong{font-weight:600;color:var(--n-700)}
.acct-table tr[hidden]{display:none}

.acct-group.is-collapsed .acct-group__head{border-bottom:0}
.acct-group__none{padding:16px;font-size:12.5px;color:var(--n-500);border-top:1px solid var(--n-100)}

/* ---------------------------------------------------------------- filter bars
   One shape for every list filter in the dashboard: search first and wide,
   the narrow selects beside it, tools pushed to the end of the row. */
/* The search field is always the widest control in its row and never drops
   below a comfortable minimum. It cannot be an identical pixel width on every
   page — a row with five filters has less to give than a row with one — so the
   guarantee is a floor, a ceiling and "always the largest", not a fixed number. */
.filters .field--search{flex:1 1 420px;min-width:320px;max-width:640px}
.filters button[type=submit]{align-self:flex-end}
.filter-note{margin:-8px 0 14px;font-size:12.5px}
@media (max-width:760px){
  .filters .field--search{flex-basis:100%;max-width:none}
}



/* ------------------------------------------------------- project breakdown strip
   What "all projects" and "several projects" mean in practice: each one on its
   own line, and a combined total that adds up to the company. */
.pbreak{margin:0 0 16px}
.pbreak__head{display:flex;align-items:center;gap:10px}
.pbreak__head h2{margin:0;font-size:13.5px}
.pbreak table{width:100%;border-collapse:collapse;font-size:12.5px}
.pbreak th{text-align:left;font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--n-500);font-weight:800;padding:6px 12px;border-bottom:1px solid var(--n-150)}
.pbreak td{padding:7px 12px;border-bottom:1px solid var(--n-100)}
.pbreak tr:last-child td{border-bottom:0}
.pbreak .num{text-align:right;font-variant-numeric:tabular-nums}
.pbreak__total td{font-weight:750;background:var(--n-50);border-top:1px solid var(--n-200)}
.pbreak__dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:7px}
.pbreak__pos{color:var(--ok)} .pbreak__neg{color:var(--danger)}

/* ------------------------------------------------------------- foldable panels */
.panel-tools{display:flex;gap:6px;justify-content:flex-end;margin:0 0 10px}
.card__head--foldable{cursor:pointer;user-select:none}
.panel-toggle{display:grid;place-items:center;width:22px;height:22px;flex:0 0 22px;
  border:1px solid var(--n-200);border-radius:7px;background:var(--n-0);color:var(--n-600);
  cursor:pointer;padding:0;margin-right:9px;transition:transform .14s ease,background .12s,border-color .12s}
.panel-toggle:hover{background:var(--n-50);border-color:var(--n-300);color:var(--n-900)}
.panel-toggle[aria-expanded="true"]{transform:rotate(90deg)}
/* The inline Filters button is a pill, not a chevron: it must not inherit the
   90-degree rotation the panel chevrons use, and the attribute selector above
   out-specifies a plain class, so it is matched on the attribute too. */
.panel-toggle--inline,
.panel-toggle--inline[aria-expanded="true"],
.panel-toggle--inline[aria-expanded="false"]{
  width:auto;height:auto;flex:none;display:inline-flex;align-items:center;gap:7px;
  padding:4px 11px;border-radius:99px;font-size:11.5px;font-weight:700;letter-spacing:.03em;
  color:var(--n-600);transform:none;margin-right:0}
.panel-toggle--inline[aria-expanded="true"] svg{transform:rotate(90deg)}
.panel-toggle--inline svg{transition:transform .14s ease}
.card.is-folded{padding-bottom:0}
.card.is-folded .card__head{border-bottom:0}
.filters-fold{margin:0 0 9px;display:flex}

/* -------------------------------------------------------------- project bar
   Breadcrumb, identity and module tabs, in one band that follows the project
   into every module opened from it. */
.projbar{background:var(--n-0);border-bottom:1px solid var(--n-150);padding:10px 26px 0}
.crumbs{display:flex;align-items:center;gap:7px;font-size:11.5px;color:var(--n-500);flex-wrap:wrap}
.crumbs a{color:var(--n-500)}
.crumbs a:hover{color:var(--n-900)}
.crumbs__project{font-weight:700;color:var(--n-700)!important}
.crumbs__current{color:var(--n-800);font-weight:600}

.projbar__head{display:flex;align-items:center;gap:13px;margin:9px 0 11px}
.projbar__avatar{width:40px;height:40px;flex:0 0 40px;border-radius:11px;display:grid;place-items:center;
  color:#fff;font-weight:750;font-size:14px;letter-spacing:.02em}
.projbar__id{min-width:0}
.projbar__id h2{margin:0;font-size:19px;line-height:1.2;letter-spacing:-.015em}
.projbar__id p{margin:3px 0 0;display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px}
.projbar__actions{margin-left:auto;display:flex;gap:7px;flex-wrap:wrap;align-items:center}

.projtabs{display:flex;align-items:flex-end;gap:4px;position:relative}
.projtabs__scroll{display:flex;gap:2px;flex:1 1 auto;min-width:0;overflow:visible}
.projtabs__scroll.is-scrolling{overflow-x:auto;scrollbar-width:thin;padding-bottom:2px}
.projtab{display:inline-block;padding:9px 14px;font-size:12.5px;font-weight:600;color:var(--n-600);
  white-space:nowrap;border-bottom:2px solid transparent;border-radius:8px 8px 0 0;
  transition:color .12s,background .12s,border-color .12s}
.projtab:hover{color:var(--n-900);background:var(--n-50)}
.projtab.is-active{color:var(--accent);border-bottom-color:var(--accent);background:var(--accent-soft);font-weight:750}
.projtabs__more{position:relative}
.projtabs__more > summary{list-style:none;cursor:pointer}
.projtabs__more > summary::-webkit-details-marker{display:none}
.projtab--more{border:1px solid var(--n-200);border-bottom-color:var(--n-200);border-radius:8px;padding:7px 13px}
.projtabs__menu{position:absolute;right:0;top:calc(100% + 5px);z-index:60;min-width:210px;
  background:var(--n-0);border:1px solid var(--n-200);border-radius:11px;
  box-shadow:0 16px 36px rgba(16,24,40,.15);padding:5px;display:grid}
.projtabs__menuitem{padding:8px 11px;border-radius:8px;font-size:12.5px;color:var(--n-700)}
.projtabs__menuitem:hover{background:var(--n-50);color:var(--n-900)}

@media (max-width:860px){
  .projbar{padding:10px 14px 0}
  .projbar__actions{width:100%;margin-left:0}
  .projtabs__scroll{overflow-x:auto}
}


/* ==================================================== read-and-fill panel ===
   The document reader embedded in the entry forms. Deliberately quiet: it sits
   below the fields it fills rather than above them, because the form is the
   subject of the page and the reader is an optional shortcut into it. */
.dr__zone{display:grid;justify-items:center;gap:3px;text-align:center;cursor:pointer;
  padding:22px 14px;border:1.5px dashed var(--n-300);border-radius:12px;background:var(--n-25);
  transition:border-color .12s,background .12s}
.dr__zone:hover{border-color:var(--accent)}
.dr__zone.is-over{border-color:var(--accent);background:var(--accent-soft)}
.dr__zone-icon{font-size:24px}
.dr__zone small{color:var(--n-500);font-size:11.5px}
.dr__privacy{margin:10px 0 0;font-size:11.5px;line-height:1.5;color:var(--n-500)}
.dr__status{margin:10px 0 0;font-size:12.5px;line-height:1.5;color:var(--n-600);min-height:1.2em}
.dr__status--ok{color:var(--ok);font-weight:600}
.dr__status--warn{color:var(--warn)}
.dr__status--err{color:var(--danger)}
.dr__result:not(:empty){margin-top:12px;padding-top:12px;border-top:1px solid var(--n-150);
  display:grid;gap:10px}
.dr__doctype{font-size:13px}
.dr__rule{margin:0;font-size:12px;color:var(--n-600);background:var(--n-50);
  border-left:3px solid var(--accent);padding:7px 10px;border-radius:0 8px 8px 0}
.dr__group{display:grid;gap:4px}
.dr__group-title{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--n-500);font-weight:700}
.dr__group ul{margin:0;padding-left:17px;font-size:12.5px;color:var(--n-700);display:grid;gap:2px}
.dr__conf{font-size:10.5px;padding:1px 6px;border-radius:99px;font-weight:600;vertical-align:1px}
.dr__conf--clear{background:#E6F6F1;color:#0B7A63}
.dr__conf--probable{background:#FFF3DC;color:#8A5A00}
.dr__conf--uncertain{background:#FDE8E8;color:#9B1C1C}
.dr__totals{margin:0;display:grid;grid-template-columns:auto 1fr;gap:2px 14px;font-size:12.5px}
.dr__totals dt{color:var(--n-600)}
.dr__totals dd{margin:0;font-variant-numeric:tabular-nums;font-weight:600}
.dr__none,.dr__warn,.dr__fine{margin:0;font-size:12px;line-height:1.5}
.dr__none{color:var(--n-600)}
.dr__warn{color:#8A5A00;background:#FFF8EC;border:1px solid #F5E2BE;padding:8px 10px;border-radius:9px}
.dr__fine{color:var(--n-500)}

/* A field the reader filled. The marker fades on the next edit so it never
   becomes permanent decoration on a form somebody has since taken over. */
.dr-filled{background:var(--accent-soft) !important;border-color:var(--accent) !important}
.dr-filled:focus{background:var(--n-0) !important}

/* ============================================ document library intake panel ===
   Sits inside the existing Upload card between the file input and the fields it
   fills. No new page, no new tab, no layout change — it appears when a file is
   chosen and collapses when it is cleared. */
.vi{margin:12px 0 14px;padding:12px 13px;border:1px solid var(--n-200);border-radius:12px;
  background:var(--n-25);display:grid;gap:10px}
.vi__steps{list-style:none;margin:0;padding:0;display:grid;gap:5px}
.vi__step{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--n-600)}
.vi__dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;background:var(--n-300)}
.vi__step--busy .vi__dot{background:var(--accent);animation:vi-pulse 1s ease-in-out infinite}
.vi__step--ok .vi__dot{background:var(--ok)}
.vi__step--warn .vi__dot{background:var(--warn)}
.vi__step--fail .vi__dot{background:var(--danger)}
.vi__step--skip{opacity:.6}
.vi__step--ok,.vi__step--warn{color:var(--n-700)}
@keyframes vi-pulse{0%,100%{opacity:1}50%{opacity:.35}}

.vi__body:not(:empty){display:grid;gap:9px;padding-top:9px;border-top:1px solid var(--n-200)}
.vi__class{margin:0;font-size:12.5px;line-height:1.5}
.vi__alts{margin:0;font-size:12px;color:var(--n-600);background:#FFF8EC;border:1px solid #F5E2BE;
  padding:7px 9px;border-radius:8px}
.vi__conf{font-size:10.5px;padding:1px 6px;border-radius:99px;font-weight:600;vertical-align:1px}
.vi__conf--clear{background:#E6F6F1;color:#0B7A63}
.vi__conf--probable{background:#FFF3DC;color:#8A5A00}
.vi__conf--uncertain{background:#FDE8E8;color:#9B1C1C}
.vi__group{display:grid;gap:4px}
.vi__group-title{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--n-500);font-weight:700}
.vi__group ul{margin:0;padding-left:17px;font-size:12.5px;color:var(--n-700);display:grid;gap:4px}
.vi__vendor{margin:0;font-size:12.5px;line-height:1.5}
.vi__dupe{font-size:12px;line-height:1.5;padding:8px 10px;border-radius:9px}
.vi__dupe--high{background:#FDE8E8;border:1px solid #F3C0C0;color:#8A1C1C}
.vi__dupe--medium{background:#FFF8EC;border:1px solid #F5E2BE;color:#8A5A00}
.vi__details{font-size:12.5px}
.vi__details summary{cursor:pointer;color:var(--n-600);font-weight:600}
.vi__fields{margin:8px 0 0;display:grid;grid-template-columns:auto 1fr;gap:4px 14px}
.vi__fields dt{color:var(--n-600)}
.vi__fields dd{margin:0}
.vi__ev{color:var(--n-400);font-size:11px;font-style:italic}
.vi__note,.vi__fail,.vi__fine{margin:0;font-size:12px;line-height:1.5}
.vi__note{color:var(--n-700)}
.vi__fail{color:var(--danger);font-weight:600}
.vi__fine{color:var(--n-500)}

/* A field the reader filled, on the library form. */
.vi-filled{background:var(--accent-soft) !important;border-color:var(--accent) !important}
.vi-filled:focus{background:var(--n-0) !important}

/* ======================================== Vendors & Suppliers module nav ===
   One horizontal strip under the page heading, shared by every screen in the
   module and reused as the tab bar on a vendor profile. Scrolls sideways rather
   than wrapping, because a profile has fourteen tabs and a wrapped bar pushes
   the record off the first screenful. */
.vsub{display:flex;gap:4px;overflow-x:auto;margin:0 0 14px;padding-bottom:2px;
  border-bottom:1px solid var(--n-200);scrollbar-width:thin}
.vsub__link{flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;
  padding:8px 12px;font-size:12.5px;font-weight:600;color:var(--n-600);
  border-radius:9px 9px 0 0;border-bottom:2px solid transparent;white-space:nowrap}
.vsub__link:hover{color:var(--n-900);background:var(--n-50)}
.vsub__link.is-active{color:var(--accent);border-bottom-color:var(--accent);background:var(--accent-soft)}
.vsub__count{display:inline-grid;place-items:center;min-width:18px;height:18px;padding:0 5px;
  border-radius:99px;background:var(--n-200);color:var(--n-700);font-size:10.5px;font-weight:700}
.vsub__link.is-active .vsub__count{background:var(--accent);color:#fff}
.vsub--tabs{margin-top:4px}

/* Registration completeness, on the Incomplete list. */
.meter{height:6px;border-radius:99px;background:var(--n-200);overflow:hidden}
.meter__fill{display:block;height:100%;border-radius:99px}
.meter__fill--ok{background:var(--ok)}
.meter__fill--warn{background:var(--warn)}
.meter__fill--danger{background:var(--danger)}

/* ================================================ user permission grids ===
   One pattern serves both the per-workspace module grid and the project grid.
   Horizontally scrollable with a sticky heading row, because a permission table
   is wide by nature and losing the column names while scrolling is how the
   wrong box gets ticked. */
.pgrid{margin-top:10px;border:1px solid var(--n-200);border-radius:11px;background:var(--n-0);overflow:hidden}
.pgrid__bar{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:8px 10px;
  background:var(--n-25);border-bottom:1px solid var(--n-200)}
.pgrid__search{flex:1 1 150px;min-width:120px;height:30px;padding:0 9px;font-size:12.5px;
  border:1px solid var(--n-300);border-radius:8px}
.pgrid__copy{height:30px;font-size:12px;border:1px solid var(--n-300);border-radius:8px;padding:0 6px;max-width:190px}
.pgrid__scroll{max-height:380px;overflow:auto}
.pgrid.is-collapsed .pgrid__scroll{max-height:132px}
.pgrid__table{width:100%;border-collapse:separate;border-spacing:0;font-size:12.5px}
.pgrid__table thead th{position:sticky;top:0;z-index:2;background:var(--n-50);
  border-bottom:1px solid var(--n-200);padding:7px 8px;text-align:center;font-size:11px;
  text-transform:uppercase;letter-spacing:.04em;color:var(--n-600)}
.pgrid__table thead th.pgrid__mod{text-align:left;left:0;z-index:3}
.pgrid__table td{padding:5px 8px;border-bottom:1px solid var(--n-100);vertical-align:middle}
.pgrid__table tr:hover td{background:var(--n-25)}
.pgrid__mod{text-align:left;min-width:190px;position:sticky;left:0;background:var(--n-0)}
.pgrid__table tr:hover .pgrid__mod{background:var(--n-25)}
.pgrid__act{text-align:center;width:66px}
.pgrid__colbtn,.pgrid__rowbtn{background:none;border:0;padding:0;font:inherit;color:inherit;
  cursor:pointer;text-align:inherit;font-weight:600}
.pgrid__colbtn:hover,.pgrid__rowbtn:hover{color:var(--accent);text-decoration:underline}
.pgrid__cell{display:inline-grid;place-items:center;width:26px;height:26px;border-radius:7px;cursor:pointer}
.pgrid__cell input{margin:0;cursor:pointer}
.pgrid__cell.is-default{background:var(--n-100)}
.pgrid__cell.is-override{background:var(--accent-soft);box-shadow:inset 0 0 0 1.5px var(--accent)}
.pgrid__na{color:var(--n-300)}
.pgrid__sens{display:inline-grid;place-items:center;width:15px;height:15px;border-radius:50%;
  background:#FDE8E8;color:#9B1C1C;font-size:10px;font-weight:800;margin-left:5px}
.pgrid__meta td{background:var(--n-25)}
.pgrid__legend{margin:0;padding:8px 10px;border-top:1px solid var(--n-200);background:var(--n-25);
  font-size:11.5px;color:var(--n-600);line-height:1.5}
.pgrid__key{display:inline-block;width:11px;height:11px;border-radius:3px;vertical-align:-1px}
.pgrid__key--default{background:var(--n-100);box-shadow:inset 0 0 0 1px var(--n-300)}
.pgrid__key--override{background:var(--accent-soft);box-shadow:inset 0 0 0 1.5px var(--accent)}

@media (max-width:860px){
  .pgrid__scroll{max-height:300px}
  .pgrid__mod{min-width:150px}
  .pgrid__act{width:54px}
}

/* ================================ user form: account, workspaces, projects ===
   Stacked, not columned. The account block is full width and on its own; the
   workspace cards sit side by side beneath it; project access is full width
   underneath them. The previous two-column arrangement squeezed a fourteen-
   column permission table into half a screen, and a permission table that has
   to be squinted at is one that gets skimmed. */
.wshead{margin:18px 0 10px}
.wshead h2{font-size:15px;margin:0 0 4px}
.wshead p{margin:0;max-width:88ch;font-size:12.5px;line-height:1.55;color:var(--n-600)}

/* auto-fit: two workspaces sit side by side on a desktop, three still fit on a
   wide screen, and they stack the moment a card would drop below its minimum
   rather than being crushed. */
.wsgrid{display:grid;gap:14px;align-items:start;
  grid-template-columns:repeat(auto-fit,minmax(430px,1fr))}
.ws-card{margin:0}
.ws-card > .card__head h2{font-size:13.5px}

/* A collapsed workspace keeps its inputs in the form — they still post — so
   folding one is a display preference and never a change to what is saved. */
.ws-card.is-collapsed .card__body{display:none}

@media (max-width:900px){
  .wsgrid{grid-template-columns:1fr}
}

/* ================================================== the page breadcrumb ===
   A band, not a line of small grey text. A breadcrumb nobody notices answers
   nobody's question, and "where am I" is the question every deep link, search
   result and emailed URL creates.

   NOT sticky, deliberately. The top bar is already sticky at top:0 with z-index
   60, and the project scope strip sits under it at z-index 18 — a third sticky
   band at the same offset either fights the top bar for the same pixels or
   slides underneath it and vanishes. Pinning this properly needs a real
   measurement of the top bar's height, which has no fixed value in CSS, and a
   breadcrumb that lands two pixels out of line is worse than one that scrolls
   away honestly. Prominence here comes from the band, the weight and the size,
   not from following the reader down the page. */
.crumbs__sep{display:inline-grid;place-items:center;color:var(--n-300);flex:0 0 auto}
.crumbs__link{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;border-radius:8px;
  color:var(--n-600);font-weight:600;white-space:nowrap}
.crumbs__link:hover{background:var(--n-50);color:var(--n-900)}
.crumbs__link--plain{color:var(--n-600)}
.crumbs__link--plain:hover{background:none}
.crumbs__link--home svg{opacity:.75}

/* The current page. The largest, darkest thing in the bar, because it is the
   answer to the question the bar exists to ask. */
.crumbs__current{padding:5px 8px;color:var(--n-900);font-weight:700;font-size:13.5px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:46ch}

/* A crumb that knows its neighbours. <details> so it works with the keyboard
   and a screen reader and needs no script — this directory's CSP would refuse
   an inline handler anyway. */
.crumbs__drop{position:relative}
.crumbs__drop > summary{list-style:none;cursor:pointer}
.crumbs__drop > summary::-webkit-details-marker{display:none}
.crumbs__link--menu svg{color:var(--n-400);transition:transform .12s}
.crumbs__drop[open] > summary{background:var(--n-100);color:var(--n-900)}
.crumbs__drop[open] .crumbs__link--menu svg{transform:rotate(180deg)}
.crumbs__menu{position:absolute;top:calc(100% + 5px);left:0;z-index:70;min-width:230px;
  background:var(--n-0);border:1px solid var(--n-200);border-radius:11px;padding:5px;
  box-shadow:0 18px 40px rgba(16,24,40,.16);display:grid;gap:1px;max-height:340px;overflow:auto}
.crumbs__menuitem{display:block;padding:8px 11px;border-radius:8px;font-size:12.5px;
  font-weight:500;color:var(--n-700);white-space:nowrap}
.crumbs__menuitem:hover{background:var(--n-50);color:var(--n-900)}
.crumbs__menuitem.is-current{background:var(--accent-soft);color:var(--accent);font-weight:700}

@media (max-width:860px){
  /* On a phone the middle of the trail is the least useful part: the home link
     and the page you are on are what get tapped. The rest scrolls. */
  .ctxbar .crumbs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none}
  .ctxbar .crumbs::-webkit-scrollbar{display:none}
  .crumbs__current{max-width:none}
}




/* The document trash. A blocked row is one that cannot be destroyed because a
   posted accounting record depends on it — marked, with the reason on the row,
   so the answer is on screen before somebody presses Empty. */
.trash__why{margin-top:5px;font-size:11.5px;line-height:1.5;color:#8A5A00;
  background:#FFF8EC;border:1px solid #F5E2BE;border-radius:8px;padding:6px 9px;max-width:70ch}
tr.is-blocked td{background:#FFFDF7}
.linkbtn--danger{color:var(--danger)}
.nav__count{margin-left:auto;display:inline-grid;place-items:center;min-width:18px;height:18px;
  padding:0 5px;border-radius:99px;background:rgba(255,255,255,.14);font-size:10.5px;font-weight:700}

/* ============================================= rearranging the sidebar ===
   Off until switched on. The grips and the drop feedback only exist in
   arranging mode, so the ordinary sidebar carries no furniture nobody uses. */
.navarrange{padding:10px 14px 4px;display:grid;gap:6px;justify-items:start}
.navarrange__btn{background:none;border:1px solid rgba(255,255,255,.16);border-radius:8px;
  padding:5px 10px;font:inherit;font-size:11.5px;font-weight:600;color:rgba(255,255,255,.7);cursor:pointer}
.navarrange__btn:hover{background:rgba(255,255,255,.08);color:#fff}
.navarrange__btn[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:#fff}
.navarrange__btn--reset{border-color:rgba(255,255,255,.10);color:rgba(255,255,255,.55)}
.navarrange__hint{font-size:10.5px;line-height:1.5;color:rgba(255,255,255,.45);max-width:210px}

.navgroup__grip,.nav__grip{display:inline-block;width:12px;flex:0 0 auto;
  color:rgba(255,255,255,.35);font-size:12px;line-height:1;cursor:grab;letter-spacing:-1px}
.sidebar__nav.is-arranging .navgroup{cursor:grab}
.sidebar__nav.is-arranging .navgroup__btn,
.sidebar__nav.is-arranging .nav--sub .nav__link{outline:1px dashed rgba(255,255,255,.14);
  outline-offset:-2px;border-radius:8px}
.sidebar__nav.is-arranging .nav--sub li{cursor:grab}
.sidebar__nav .is-dragging{opacity:.45}
.sidebar__nav.is-arranging .navgroup__chev{opacity:.25}

/* A collapsed sidebar has no room to drag anything: the labels are hidden and
   every row is an icon, so a reorder would be blind. The control goes with them. */
.app.is-collapsed .navarrange{display:none}


/* ==========================================================================
   SHARED STANDARDS LAYER
   ==========================================================================
   Everything below is deliberately generic. It exists so that a new screen is
   assembled from parts that already exist rather than styled from scratch, and
   so that a density decision is made once here instead of 175 times in Blade.

   Rules for anything added to this block:
     * spacing comes from the six-step scale (--s-1 … --s-8), never a raw px
     * a component earns its place by being used on more than one screen
     * nothing here may depend on a page-specific id or class
     * no !important, and no selector deeper than two compound parts
   Reference: docs/UI_UX_STANDARDS.md

   CONTENTS
     1  Tokens ................ derived spacing values
     2  Layout ................ page shell, stacks, minimum sizes
     3  Headers ............... page header, chrome band density
     4  Toolbars .............. view-wide controls, filter rows
     5  Tables ................ registers, row actions, geometry
     6  Forms ................. inputs inside components
     7  Tabs .................. (no additions — see the base sheet)
     8  Collapse controls ..... panels, filter fold, help
     9  Menus ................. row menus, disclosure panels
    10  Utilities ............. the closed list that retires inline styles
    11  Responsive ............ every media query in this layer, in one place
    12  Accessibility ......... focus, labels, screen-reader-only text

   Every @media block in this layer lives in section 11. Nothing outside that
   section is width-dependent, so the cascade order inside a section is the only
   order that matters.
   ========================================================================== */


/* ==========================================================================
   1  TOKENS
   --------------------------------------------------------------------------
   The six-step scale itself is declared in :root at the top of this file. What
   is here is the small set of role-named values derived from it, so a density
   decision is one edit rather than a search across the sheet. They are already
   consumed above; this block only documents where they come from.

     --pad-head    card and panel headers
     --pad-body    card bodies
     --gap-grid    between cards in a grid
     --gap-stack   between stacked blocks on a page
   ========================================================================== */


/* ==========================================================================
   2  LAYOUT
   ========================================================================== */

/* The page column.
   `.main` is a flex item, and a flex item's implicit minimum size is its
   CONTENT's minimum, not zero. So one table wider than the screen — the
   fourteen-column permission grid — stretched <main> itself, and every
   overflow:hidden below it was measuring against a container that had already
   grown. `.shell` was given min-width:0 for exactly this reason; `.main` was
   missed. Found by hiding elements one at a time at 360px and watching the
   document's scroll width, not by reading the stylesheet. */
.main{min-width:0}

/* The stack: the default vertical rhythm between blocks on a page.
   `display:grid` rather than margins on children, because margin collapse
   between a card, a table and an alert is exactly how three pages end up with
   three different gaps. */
.stack{display:grid;gap:var(--gap-stack);align-content:start}
.stack--tight{gap:var(--s-2)}
.stack--loose{gap:var(--s-4)}

/* A grid or flex child's implicit minimum is its content's minimum, which is
   what lets one long string stretch a whole row. */
.grid>*,.stack>*,.wsgrid>*{min-width:0}
.ws-card,.ws-card>.card__body{min-width:0}

/* Long unbreakable strings.
   A hash, a storage key, an API fingerprint or a file path has no spaces in it,
   so it sets the minimum width of everything it sits inside. On Settings at
   360px one <p class="mono"> holding a key made its card 420px wide and pushed
   the whole document 72px sideways. Monospace strings in a block context break
   anywhere; the ones in a table cell are already handled by the cell. */
p.mono,div.mono,dd.mono,li.mono,.card__body .mono{overflow-wrap:anywhere;word-break:break-word}

/* The permission grid scrolls inside its own box. It has to be told that its
   box may be narrower than its contents, or the card grows to the table's width
   and takes the page with it. */
.pgrid{max-width:100%}
.pgrid__scroll{overflow:auto;overflow-x:auto;max-width:100%}


/* ==========================================================================
   3  HEADERS: the command bar
   --------------------------------------------------------------------------
   FOUR BANDS BECAME TWO.

   The chrome above the first line of work used to be a stack: the top bar, the
   workspace strip, the project scope bar and the breadcrumb. Even after the
   first density pass that was 215px at 1440px on the home screen and 252px on
   a register. That is a quarter of a laptop screen, on every page, spent before the
   work starts, and three of the four bands were saying the same three facts in
   three different typefaces.

   They are now:

     .cmdbar   ONE persistent command bar: workspace, period, project scope,
               search, notifications, user. Sticky, 44px, dark-on-light.
     .ctxbar   ONE 30px context line: breadcrumb on the left, the facts that
               change on the right. Not sticky, because it is orientation, not a
               control, and pinning two bands at the same offset is how the
               scope bar used to disappear under the top bar.

   Nothing was deleted. Every question the four bands answered (who am I,
   which company, which projects, where am I, can I post today) is still
   answered, once each, in one of these two rows.
   ========================================================================== */

.cmdbar{position:sticky;top:0;z-index:60;display:flex;align-items:center;gap:var(--s-2);
  height:var(--cmd-h);padding:0 var(--s-4);background:var(--n-0);
  border-bottom:1px solid var(--n-200)}
.cmdbar__menu{display:grid;flex:0 0 auto}
.cmdbar__spacer{flex:1 1 auto;min-width:var(--s-2)}
.cmdbar__right{display:flex;align-items:center;gap:var(--s-1);flex:0 0 auto}
.cmdbar__sep{width:1px;height:20px;background:var(--n-200);flex:0 0 1px}

/* A control in the bar. One height, one border, one radius, so the bar reads as
   a single instrument panel rather than a row of unrelated widgets. */
.cmdctl{position:relative;display:inline-flex;align-items:center;gap:6px;height:28px;
  padding:0 8px;border:1px solid var(--n-200);border-radius:var(--radius);
  background:var(--n-0);color:var(--n-700);font:inherit;font-size:12px;font-weight:600;
  white-space:nowrap;cursor:pointer;max-width:240px;min-width:0;flex:0 1 auto}
.cmdctl:hover{background:var(--n-50);border-color:var(--n-300);color:var(--n-900)}
.cmdctl__k{font-size:9.5px;font-weight:800;letter-spacing:.11em;text-transform:uppercase;
  color:var(--n-400)}
.cmdctl__v{overflow:hidden;text-overflow:ellipsis;font-weight:650;color:var(--n-900);min-width:0}
.cmdctl__dot{width:7px;height:7px;border-radius:50%;flex:0 0 7px;background:var(--accent)}
.cmdctl__chev{flex:0 0 10px;opacity:.55}
.cmdctl.is-warn{border-color:#E7C98F;background:#FFF8EC;color:#7A5310}
.cmdctl.is-warn .cmdctl__v{color:#7A5310}
.cmdctl.is-on{border-color:var(--n-900);background:var(--n-900);color:#fff}
.cmdctl.is-on .cmdctl__k{color:rgba(255,255,255,.55)}
.cmdctl.is-on .cmdctl__v{color:#fff}

/* A disclosure in the bar. <details> so it works with the keyboard, closes on
   Escape and needs no inline handler, which the CSP on this directory refuses. */
.cmddrop{position:relative;min-width:0;display:inline-flex}
.cmddrop > summary{list-style:none;min-width:0}
.cmddrop > summary::-webkit-details-marker{display:none}
/* A CLOSED disclosure must take no space at all.
   Chromium 141 renders a closed <details> through ::details-content with
   content-visibility:hidden, and an absolutely positioned panel inside it
   STILL contributes to the document's scroll width. Measured, not assumed: at
   390px every screen gained 109px of horizontal scroll from two menus nobody
   had opened. display:none on the closed state settles it in every engine. */
.cmddrop:not([open]) > .cmddrop__panel,
.crumbs__drop:not([open]) > .crumbs__menu{display:none}
.cmddrop__panel{position:absolute;left:0;top:calc(100% + 5px);z-index:90;min-width:260px;
  max-width:min(340px, calc(100vw - 24px));background:var(--n-0);border:1px solid var(--n-200);
  border-radius:var(--radius-lg);box-shadow:var(--shadow-2);padding:var(--s-2);display:grid;gap:2px}
.cmddrop__panel--right{left:auto;right:0}
.cmddrop__head{display:flex;align-items:center;gap:var(--s-2);padding:2px var(--s-2) var(--s-2);
  font-size:9.5px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--n-400)}
.cmddrop__foot{display:flex;gap:var(--s-2);align-items:center;
  border-top:1px solid var(--n-150);margin-top:var(--s-1);padding-top:var(--s-2)}
.cmddrop__opt{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border:0;
  border-radius:var(--radius);background:none;font:inherit;font-size:12.5px;color:var(--n-700);
  text-align:left;cursor:pointer}
.cmddrop__opt:hover{background:var(--n-50);color:var(--n-900)}
.cmddrop__opt.is-active{background:var(--n-100);color:var(--n-900);font-weight:650}
.cmddrop__opt small{margin-left:auto;font-size:10px;font-weight:800;letter-spacing:.05em;
  color:var(--n-400)}
.cmddrop__scroll{max-height:290px;overflow-y:auto;display:grid;gap:1px}

/* Global search, and not a record search. This application has no cross-module
   search endpoint and inventing one would be new business logic. It is a
   command palette over the screens the signed-in person may actually open,
   plus a hand-off into the module's OWN q= filter, which every register
   already has. It never claims to have searched anything it did not. */
.cmdsearch{position:relative;flex:0 1 300px;min-width:150px}
/* The glyph and the shortcut hint sit INSIDE the box, so this control keeps its
   own padding, and the global input rule is excluded from it by name rather
   than out-specified. That rule is `input` plus nine :not() clauses, which is a
   specificity of (0,9,1); no sane class selector beats it, and the first
   attempt to try printed the magnifier on top of the placeholder. Adding a
   tenth exclusion is the version that cannot be lost to the cascade later. */
/* `width:100%` IS THE POINT OF THIS RULE, not a tidy-up. Measured 6 Oct 2026.

   Everything else here was set and the width was not, so the <input> fell back
   to its intrinsic size — about 217px for the default 20-character `size` at
   this font — inside a container that is `flex:0 1 300px`. The field stopped
   83px short of its own box, and the "/" hint, which is pinned to the right of
   the CONTAINER so the input's padding can reserve room for it, floated 63px
   clear of the field it belongs to: a small boxed slash sitting alone in the
   middle of the bar with nothing around it. */
.cmdsearch input.cmdsearch__input{width:100%;height:28px;padding:0 26px 0 28px;font-size:12.5px;
  border-radius:var(--radius);border:1px solid var(--n-200);background:var(--n-50);line-height:26px}
.cmdsearch input.cmdsearch__input:focus{background:var(--n-0)}
.cmdsearch__icon{position:absolute;left:8px;top:50%;transform:translateY(-50%);
  width:14px;height:14px;color:var(--n-400);pointer-events:none}
.cmdsearch__kbd{position:absolute;right:7px;top:50%;transform:translateY(-50%);
  font-size:9.5px;font-weight:700;color:var(--n-400);border:1px solid var(--n-200);
  border-radius:3px;padding:1px 4px;pointer-events:none;background:var(--n-0)}
.cmdsearch__results{position:absolute;left:0;right:0;top:calc(100% + 5px);z-index:95;
  background:var(--n-0);border:1px solid var(--n-200);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-2);padding:var(--s-1);max-height:340px;overflow-y:auto;
  min-width:320px}
.cmdsearch__group{font-size:9.5px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  color:var(--n-400);padding:var(--s-2) var(--s-2) var(--s-1)}
.cmdsearch__hit{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:var(--radius);
  font-size:12.5px;color:var(--n-700);cursor:pointer}
.cmdsearch__hit small{margin-left:auto;font-size:10.5px;color:var(--n-400)}
.cmdsearch__hit.is-sel,.cmdsearch__hit:hover{background:var(--n-100);color:var(--n-900)}
.cmdsearch__none{padding:var(--s-3);font-size:12px;color:var(--n-500)}

/* THE RIGHT HAND END OF THE BAR: notifications, avatar, user menu.

   THESE RULES WERE MISSING, NOT WRONG. Measured on the live stylesheet,
   6 Oct 2026. Only the fine tuning existed here — a width on .icon-btn, a
   font size on .avatar, a border radius and a top offset on .usermenu__panel
   — and the component block those values were tuning had never been written.
   .usermenu itself had no rule at all.

   With no `position:relative` on the wrapper and no `position:absolute` on the
   panel, the open menu was laid out in the flow of a 44px flex row: its three
   items sat side by side on one line, it had no background, so the context
   line underneath read straight through it, and the sign out <button> fell
   back to the browser's own grey chrome. The screenshot of it looks like a
   tooltip stuck over a menu. It is neither; it is three controls with no
   component.

   Written in the same idiom as .cmddrop__panel above, because this is the same
   object: a disclosure hung off a control in the command bar. The one
   difference is that this panel is toggled by the `hidden` attribute from
   app.js rather than by <details>, so `display` has to be stated for both
   states — a bare `display:grid` on a class beats the user agent's
   `[hidden]{display:none}` and the menu would never close again. */

.usermenu{position:relative;flex:0 0 auto}

/* The name is a control, not a paragraph. */
.usermenu__btn{display:inline-flex;align-items:center;gap:6px;max-width:220px;
  padding:2px 5px;border:1px solid transparent;border-radius:var(--radius);
  background:none;font:inherit;color:var(--n-700);cursor:pointer;text-align:left}
.usermenu__btn:hover{background:var(--n-50);border-color:var(--n-200)}
.usermenu__btn[aria-expanded="true"]{background:var(--n-100);border-color:var(--n-300)}
.usermenu__btn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

.usermenu__meta{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.usermenu__meta strong{font-size:12px;font-weight:650;color:var(--n-900);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.usermenu__meta small{font-size:10px;font-weight:600;letter-spacing:.04em;color:var(--n-500);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Right aligned: it is the last control on the bar, and a panel hung from its
   left edge runs off the screen on every laptop. */
.usermenu__panel{position:absolute;right:0;left:auto;top:calc(100% + 5px);z-index:90;
  min-width:210px;max-width:min(280px, calc(100vw - 24px));
  background:var(--n-0);border:1px solid var(--n-200);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-2);padding:var(--s-1);display:grid;gap:2px}
.usermenu__panel[hidden]{display:none}
.usermenu__panel form{margin:0;display:contents}
.usermenu__panel > a,
.usermenu__panel button{display:flex;align-items:center;width:100%;padding:7px 9px;
  border:0;border-radius:var(--radius);background:none;font:inherit;font-size:12.5px;
  font-weight:600;color:var(--n-700);text-align:left;text-decoration:none;cursor:pointer;
  white-space:nowrap}
.usermenu__panel > a:hover,
.usermenu__panel button:hover{background:var(--n-50);color:var(--n-900)}
.usermenu__panel > a:focus-visible,
.usermenu__panel button:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
/* Signing out ends the session. It is separated and named in the colour this
   system reserves for the irreversible. */
.usermenu__panel button{color:var(--danger);margin-top:var(--s-1);
  border-top:1px solid var(--n-150);border-top-left-radius:0;border-top-right-radius:0;
  padding-top:9px}
.usermenu__panel button:hover{background:var(--danger-soft,#FEECEC);color:var(--danger)}

/* The round controls in the bar: the bell, and the mobile navigation button. */
.icon-btn{position:relative;display:grid;place-items:center;width:28px;height:28px;
  flex:0 0 28px;border:1px solid transparent;border-radius:var(--radius);
  background:none;color:var(--n-600);cursor:pointer;text-decoration:none}
.icon-btn:hover{background:var(--n-50);border-color:var(--n-200);color:var(--n-900)}
.icon-btn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.icon-btn svg{width:18px;height:18px}
/* The unread count rides on the bell rather than beside it, so the bar's
   geometry does not change the moment a notification arrives. */
.icon-btn .badge{position:absolute;top:-3px;right:-3px;min-width:15px;height:15px;padding:0 4px;
  display:grid;place-items:center;border-radius:999px;background:var(--danger);color:#fff;
  font-size:9px;font-weight:800;line-height:1;border:1.5px solid var(--n-0)}

.avatar{display:grid;place-items:center;width:24px;height:24px;flex:0 0 24px;
  border-radius:var(--radius);background:var(--n-900);color:var(--n-0);
  font-size:10px;font-weight:800;letter-spacing:.02em;text-transform:uppercase}

/* The cogwheel that replaced the initials on the account button. Same 24px
   footprint, so the bar's geometry did not move when it changed; it turns on
   hover and while the menu is open, which is the only animation on this bar
   and is there because a cog that never turns reads as a logo. */
.usermenu__cog{display:grid;place-items:center;width:24px;height:24px;flex:0 0 24px;
  border-radius:var(--radius);background:var(--n-100);color:var(--n-700);
  transition:transform .25s ease,background .12s,color .12s}
.usermenu__btn:hover .usermenu__cog{background:var(--n-900);color:#fff;transform:rotate(45deg)}
.usermenu__btn[aria-expanded="true"] .usermenu__cog{background:var(--n-900);color:#fff;
  transform:rotate(45deg)}

/* SIGN OUT, ON THE BAR.

   It was the third item inside a menu that gave no sign it held it. On a
   console people share, the way out should not be something you have to
   remember where to find. Danger coloured, outlined rather than filled: it is
   the most consequential control on the bar and the least frequently wanted,
   and a filled red button at the top right of every screen reads as an alarm.

   Below 1024px the word goes and the door stays. That is the same breakpoint
   at which the name and role beside it are hidden, so the bar loses its words
   in one step rather than in three. */
.signout{margin:0;flex:0 0 auto}
.signout__btn{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;
  border:1px solid var(--n-200);border-radius:var(--radius);background:var(--n-0);
  font:inherit;font-size:12px;font-weight:650;color:var(--n-600);cursor:pointer;
  white-space:nowrap}
.signout__btn svg{flex:0 0 16px}
.signout__btn:hover{background:var(--danger-soft,#FEECEC);border-color:var(--danger);
  color:var(--danger)}
.signout__btn:focus-visible{outline:2px solid var(--danger);outline-offset:1px}
@media (max-width:1024px){
  .signout__btn{padding:0 7px}
  .signout__btn span{display:none}
}

/* .cmdbar__menu is left visible at every width ON PURPOSE. It looks like a
   mobile-only hamburger and it is not: app.js gives it two jobs, opening the
   off-canvas sidebar below 860px and collapsing the sidebar above it. Hiding
   it on a wide screen would take the collapse control off both consoles. */

/* Restated for the same reason: the responsive block that hides the name and
   role sits above this component, so at equal specificity the display:flex
   added here would otherwise bring it back on a phone. */
@media (max-width:1024px){ .usermenu__meta{display:none} }

/* ------------------------------------------------------------ context line */
.ctxbar{display:flex;align-items:center;gap:var(--s-3);min-height:var(--ctx-h);
  padding:0 var(--s-4);background:var(--n-25);border-bottom:1px solid var(--n-150);
  font-size:11.5px;color:var(--n-600)}
.ctxbar__spacer{flex:1 1 auto}
.ctxbar__facts{display:flex;align-items:center;gap:var(--s-3);white-space:nowrap}
.ctxfact{display:inline-flex;align-items:center;gap:5px}
.ctxfact__k{font-size:9.5px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--n-400)}
.ctxfact__v{font-weight:650;color:var(--n-800)}
.ctxfact--warn .ctxfact__v{color:#8A5A00}
.ctxfact__clear{background:none;border:0;padding:0;font:inherit;font-size:10.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.04em;color:var(--accent);cursor:pointer}
.ctxfact__clear:hover{text-decoration:underline}

/* The breadcrumb, inside the context line rather than in a band of its own. */
.ctxbar .crumbs{display:flex;align-items:center;gap:5px;min-width:0;overflow:hidden}
.ctxbar .crumbs__link,.ctxbar .crumbs__current{font-size:11.5px;white-space:nowrap}
.ctxbar .crumbs__current{font-weight:650;color:var(--n-800)}
.ctxbar .crumbs__sep{color:var(--n-300);display:inline-flex}
.ctxbar .crumbs__drop{position:relative}
.ctxbar .crumbs__drop > summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:3px}
.ctxbar .crumbs__drop > summary::-webkit-details-marker{display:none}
.ctxbar .crumbs__menu{position:absolute;left:0;top:calc(100% + 5px);z-index:70;min-width:210px;
  background:var(--n-0);border:1px solid var(--n-200);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-2);padding:var(--s-1);display:grid;gap:1px}
.ctxbar .crumbs__menuitem{display:block;padding:6px 8px;border-radius:var(--radius);
  font-size:12.5px;color:var(--n-700);white-space:nowrap}
.ctxbar .crumbs__menuitem:hover{background:var(--n-50);color:var(--n-900)}
.ctxbar .crumbs__menuitem.is-current{background:var(--n-100);color:var(--n-900);font-weight:650}

/* ------------------------------------------------------------- page header */
.page-head{gap:var(--s-3);margin-bottom:var(--s-2);align-items:center}
.page-head h1{font-size:16px;line-height:1.3;letter-spacing:-.005em}
.page-head p{font-size:11.5px;margin-top:1px;max-width:100ch}
.page-head__actions{gap:var(--s-1)}
.page-head--tight{margin-bottom:var(--s-2)}

/* ------------------------------------------------------------ the stat row
   A row of four tiles, each in its own bordered, rounded, shadowed box, was
   the single most repeated "AI dashboard" tell in this interface: it appeared
   above forty registers. The numbers were never the problem. The forty boxes
   were.

   The tiles now form ONE instrument strip, with one border, one radius and
   hairline dividers between the figures, and it is done with :has() so every page
   already written as `.grid.grid--4 > .stat` becomes a strip with no template
   edit at all. A browser without :has() still gets flat, low tiles rather than
   the old floating cards, which is a worse strip but not a broken page. */
.stat{padding:5px var(--s-3);border-radius:0;box-shadow:none;background:var(--n-0)}
.stat__label{font-size:9.5px;letter-spacing:.11em;color:var(--n-500)}
.stat__value{font-size:17px;line-height:1.2;margin:1px 0 0;letter-spacing:-.015em}
.stat__sub{font-size:10.5px;line-height:1.35}
.stat--pending .stat__value{font-size:13px}

.grid--4:has(> .stat),.stat-row:has(> .stat){gap:0;border:1px solid var(--n-200);
  border-radius:var(--radius);background:var(--n-0);overflow:hidden;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.grid--4:has(> .stat) > .stat,.stat-row:has(> .stat) > .stat{
  border:0;border-left:1px solid var(--n-150);border-radius:0;padding:6px var(--s-3)}
.grid--4:has(> .stat) > .stat:first-child,.stat-row:has(> .stat) > .stat:first-child{border-left:0}
/* The tone modifiers coloured a whole tile's border. Inside one strip that
   would draw a box back around a figure, so the tone moves to the figure. */
.grid--4:has(> .stat) > .stat--accent,.stat-row:has(> .stat) > .stat--accent{background:none}
.grid--4:has(> .stat) > .stat--ok,.grid--4:has(> .stat) > .stat--warn,
.grid--4:has(> .stat) > .stat--danger,.stat-row:has(> .stat) > .stat--ok,
.stat-row:has(> .stat) > .stat--warn,.stat-row:has(> .stat) > .stat--danger{background:none}
.stat--ok .stat__value{color:var(--ok)}
.stat--warn .stat__value{color:var(--warn)}
.stat--danger .stat__value{color:var(--danger)}


/* ==========================================================================
   4  TOOLBARS
   --------------------------------------------------------------------------
   One row, above the content it acts on, for controls that are about the whole
   view: expand/collapse, density, export, bulk actions. A page has AT MOST ONE.
   If a control belongs to a single card it goes in that card's header, not
   here — that is the rule that stops a second Expand All appearing.
   ========================================================================== */
.toolbar{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap;margin:0 0 var(--s-3)}
.toolbar__spacer{flex:1 1 auto}
.toolbar__label{font-size:11px;font-weight:800;letter-spacing:.09em;
  text-transform:uppercase;color:var(--n-500)}
/* Tools that sit at the end of a filter row are the same component. */
.filters .toolbar{margin:0}

/* The page-level panel toolbar.
   When a page has a .page-head, app.js moves this into its actions area and the
   band disappears entirely; this is the fallback for pages with no header. */
.panel-tools{margin:0 0 var(--s-2)}
.page-head__actions .panel-tools{margin:0;gap:var(--s-2)}


/* ==========================================================================
   5  TABLES
   --------------------------------------------------------------------------
   Row geometry lives on `table.data` as four custom properties, declared in the
   base sheet above. This section is the register's furniture: the action column
   and the flush wrapper.
   ========================================================================== */

/* A table-wrap that is already inside a card supplies no second border. */
.table-wrap--bare{border:0;border-radius:0;background:none}

/* Row actions.
   NOT display:flex. `.row-actions` is applied directly to a <td>, and a table
   cell turned into a flex container stops being a table-cell for the purposes
   of column width and vertical alignment. Spacing comes from the
   adjacent-sibling margins in the base sheet; this only removes the need for
   `style="display:inline"` on the CSRF form wrappers, which had been written
   out by hand in seventeen templates. */
.row-actions form{display:inline}
.row-actions > *{vertical-align:middle}

/* Row actions do not wrap.
   `.rec-actions` is a flex row and inherited `flex-wrap:wrap` from the version
   of it used on a record's own page, where wrapping is right. In a table cell it
   is not: on the user register the actions column is squeezed by a long email
   column, and "Manage" plus the row-menu button — 98px of content — wrapped onto
   two lines, making every row 37px taller than its neighbours. A register with
   more controls than fit is the row menu's problem to solve, not the wrap's. */
.rec-actions--compact{flex-wrap:nowrap}

/* A destructive action that must stay in the row — a payment line inside a
   document, where there is nothing else to put in a menu — is named in red
   rather than painted as a red slab. Same information, one row's worth of alarm
   instead of twenty. */
.rec-actions--compact .btn--danger{background:var(--n-0);border-color:var(--n-200);color:var(--danger)}
.rec-actions--compact .btn--danger:hover{background:var(--danger-soft);border-color:#E9B7B7;filter:none}


/* ==========================================================================
   6  FORMS
   ========================================================================== */

/* A number typed into a line-item editor lines up with the numbers printed
   below it. `.num` already does this for a table cell; the inputs inside those
   cells were each carrying style="text-align:right" by hand. */
input.num{text-align:right;font-variant-numeric:tabular-nums}


/* ==========================================================================
   7  TABS
   --------------------------------------------------------------------------
   No additions. Four tab patterns already exist in the base sheet and each has
   a distinct job: .pill-tabs (settings sections), .vsub (the vendor module
   strip), .ptabs (country panels on Projects), .projtab (a project's modules).
   They were audited for consistency rather than merged: merging them would
   change navigation structure, which is out of scope.
   ========================================================================== */


/* ==========================================================================
   8  COLLAPSE CONTROLS
   ========================================================================== */

/* Collapsible help text — the replacement for the long explanatory alert.

   An explanation of how a module works is worth having and worth keeping. It is
   not worth 120 words of coloured box above the operational content on every
   visit, every day, forever. As a <details> it costs one line until somebody
   wants it, and it needs no script — which this directory's content-security
   policy requires anyway.

   Reserve .alert for things that are TRUE RIGHT NOW and might not be tomorrow:
   a save confirmation, a validation failure, an expiring licence, a closed
   period. If the sentence would read identically on an empty database, it is
   help, not an alert. */
.help{border:1px solid var(--n-150);border-radius:var(--radius);
  background:var(--n-25);margin:0 0 var(--s-3);font-size:12.5px;line-height:1.55}
.help > summary{display:flex;align-items:center;gap:var(--s-2);cursor:pointer;
  list-style:none;padding:var(--s-2) var(--s-3);color:var(--n-600);font-weight:600;
  border-radius:var(--radius)}
.help > summary::-webkit-details-marker{display:none}
.help > summary:hover{color:var(--n-900);background:var(--n-50)}
.help > summary::before{content:"";flex:0 0 13px;height:13px;
  background:currentColor;transition:transform .14s ease;
  -webkit-mask:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M6 4l7 6-7 6' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/></svg>") center/contain no-repeat;
          mask:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M6 4l7 6-7 6' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/></svg>") center/contain no-repeat}
.help[open] > summary::before{transform:rotate(90deg)}
.help[open] > summary{border-bottom:1px solid var(--n-150);border-radius:var(--radius) var(--radius) 0 0}
.help__body{padding:var(--s-3);color:var(--n-600)}
.help__body > :first-child{margin-top:0}
.help__body > :last-child{margin-bottom:0}
.help__body p{margin:0 0 var(--s-2);max-width:92ch}
.help__body ul{margin:0;padding-left:18px}
.help__body li+li{margin-top:var(--s-1)}
.help__body strong{color:var(--n-800)}
/* Cautionary help keeps the amber tone without keeping the amber slab. */
.help--warn{border-color:#EEDCB4;background:#FFFCF4}
.help--warn > summary{color:#7A5310}
.help--warn > summary:hover{background:#FDF4E3}

/* The filter fold bar.
   app.js inserts a "Filters" pill for every filter row. On a page with a header
   it goes into the header's actions; otherwise it keeps this bar, which used to
   take a 37px band of its own to hold one 24px control. */
.filters-fold{margin:0 0 var(--s-1)}
.panel-toggle--inline,
.panel-toggle--inline[aria-expanded="true"],
.panel-toggle--inline[aria-expanded="false"]{padding:3px var(--s-2)}


/* ==========================================================================
   9  MENUS
   --------------------------------------------------------------------------
   A register with six actions per row spent a third of its width on links
   nobody clicks, and a permanent red Delete on every row is an accident waiting
   for a mis-click. The primary action stays visible; the rest live behind one
   control. <details>, so it works with the keyboard and needs no script under
   the CSP.
   ========================================================================== */
.rowmenu{position:relative;display:inline-block;vertical-align:middle}
.rowmenu > summary{list-style:none;cursor:pointer;display:inline-grid;place-items:center;
  width:26px;height:26px;border-radius:8px;color:var(--n-500);font-weight:700;
  letter-spacing:.5px;line-height:1}
.rowmenu > summary::-webkit-details-marker{display:none}
.rowmenu > summary:hover{background:var(--n-100);color:var(--n-900)}
.rowmenu[open] > summary{background:var(--n-100);color:var(--n-900)}
.rowmenu__menu{position:absolute;right:0;top:calc(100% + var(--s-1));z-index:50;
  min-width:190px;background:var(--n-0);border:1px solid var(--n-200);
  border-radius:11px;padding:var(--s-1);display:grid;gap:1px;text-align:left;
  box-shadow:0 16px 36px rgba(16,24,40,.15)}
.rowmenu__item,.rowmenu__menu a,.rowmenu__menu .linkbtn{display:block;width:100%;
  padding:var(--s-2) var(--s-2);border-radius:8px;font-size:12.5px;font-weight:500;
  color:var(--n-700);text-align:left;white-space:nowrap}
.rowmenu__menu a:hover,.rowmenu__menu .linkbtn:hover{background:var(--n-50);color:var(--n-900);text-decoration:none}
.rowmenu__menu form{display:block;margin:0}
.rowmenu__sep{height:1px;background:var(--n-150);margin:var(--s-1) 0}
/* Destructive actions are named in red, not painted as a red slab. */
.rowmenu__menu .is-destructive{color:var(--danger)}
.rowmenu__menu .is-destructive:hover{background:var(--danger-soft);color:var(--danger)}
/* An action that exists but is not available on this row stays listed, greyed,
   with the reason in its title — removing it entirely just raises the question
   of where it went. */
.rowmenu__menu .is-disabled{opacity:.45;cursor:not-allowed;color:var(--n-500)}
.rowmenu__menu .is-disabled:hover{background:none}

/* A form inside a row menu.
   Restoring a backup needs the word RESTORE typed before the button does
   anything, and resetting a password offers three ways to do it. In the row
   either one wrapped the cell onto three lines; in the menu there is width to
   lay them out. */
.rowmenu__form{display:grid;gap:var(--s-2);padding:var(--s-2);min-width:230px}
.rowmenu__label{font-size:11px;font-weight:800;letter-spacing:.06em;
  text-transform:uppercase;color:var(--n-500)}
.rowmenu__form input{font-size:12.5px}
.rowmenu__form .btn{width:100%;justify-content:flex-start}
.rowmenu__form p{white-space:normal;text-align:left}

/* A register's table lives inside .table-wrap, which scrolls sideways — and a
   box with overflow-x:auto computes overflow-y to auto as well, so an
   absolutely positioned menu inside it would be clipped at the bottom edge of
   the table. While a menu is open the wrapper stops clipping. No script: the
   open state is in the DOM as an attribute, and :has() can see it. */
.table-wrap:has(.rowmenu[open]),.table-wrap:has(.rec-menu[open]){overflow:visible}


/* ==========================================================================
   10  UTILITIES
   --------------------------------------------------------------------------
   A short, closed list. These exist to retire repeated inline `style`
   attributes, not to become a utility framework — anything used on one screen
   belongs in that screen, and anything used on five belongs in a component
   above. Do not add to this list without deleting an inline style in the same
   change.
   ========================================================================== */
.u-m-0{margin:0}
.u-mt-0{margin-top:0}
.u-mb-0{margin-bottom:0}
.u-mt-1{margin-top:var(--s-1)}
.u-mb-1{margin-bottom:var(--s-1)}
.u-mt-2{margin-top:var(--s-2)}
.u-mb-2{margin-bottom:var(--s-2)}
.u-mt-3{margin-top:var(--s-3)}
.u-mb-3{margin-bottom:var(--s-3)}
.u-mt-4{margin-top:var(--s-4)}
.u-mb-4{margin-bottom:var(--s-4)}
.u-ml-auto{margin-left:auto}
.u-right{text-align:right}
.u-nowrap{white-space:nowrap}
.u-pre{white-space:pre-wrap}
.u-inline{display:inline}
/* The three small type steps this application actually uses. They are not
   round numbers because the sheet predates the scale; they are named by value
   so converting an inline style can never shift the type by half a pixel. */
.u-11{font-size:11px}
.u-12{font-size:12px}
.u-xs{font-size:11.5px}
.u-sm{font-size:12.5px}
/* Column widths on a <th>. A table that declares its own geometry keeps the
   numeric columns from jumping about as the data changes underneath them. */
.w-70{width:70px}.w-80{width:80px}.w-100{width:100px}.w-110{width:110px}
.w-120{width:120px}.w-130{width:130px}.w-150{width:150px}.w-180{width:180px}
.w-220{width:220px}


/* ==========================================================================
   11  RESPONSIVE
   --------------------------------------------------------------------------
   Every media query in this layer, in one place and in descending width order,
   so what happens at a given width can be read top to bottom.

   Verified at 1440, 1024, 768, 390 and 360 CSS pixels.
     1440  full layout
     1024  the workspace label and the user's job title leave the top bar
      900  workspace cards go one-up; the permission grid may be narrower
      860  the sidebar becomes a drawer; page padding drops a step
      768  filter rows go full width
      760  two-column definition lists stack
      600  filter-row tools take a line of their own
      400  single column; stat tiles two-up; tables scroll rather than squeeze
   ========================================================================== */

@media (max-width:900px){
  /* A grid item's implicit min-width is its content's, and the permission table
     inside these cards is wider than a phone. Without this the card refuses to
     shrink and takes the page with it. */
  .wsgrid{grid-template-columns:1fr}
  .wsgrid > *{min-width:0}
  .ws-card{overflow:hidden}
  .ws-card>.card__body{overflow:hidden}
}

@media (max-width:640px){
  /* Two controls, an alert count and an avatar have to share 390px with the
     drawer button. The labels go first, the workspace dot keeps its colour, the
     scope control keeps its word, and nothing is allowed to set a minimum the
     bar cannot meet. */
  .cmdbar .cmdctl{max-width:104px}
  .cmdbar .cmdctl__k{display:none}
  /* Scoped to the bar on 6 Oct 2026. It pins the panel to the viewport under
     the 44px bar, which is right for a drop that hangs off that bar and wrong
     for everything else. The workspace and scope menus now live in the sidebar
     drawer, and this rule was dragging them out of it: `position:fixed` plus a
     `top` of `calc(100% + 4px)` resolves 100% against the VIEWPORT, so the
     panel opened at y=804 on an 800px screen. Measured, not guessed. */
  .cmdbar .cmddrop__panel{position:fixed;left:var(--s-3);right:var(--s-3);
    top:calc(var(--cmd-h) + 4px);min-width:0;max-width:none}
}
@media (max-width:860px){
  .cmdbar{padding:0 var(--s-3);gap:var(--s-1)}
  .ctxbar{padding:0 var(--s-3);overflow-x:auto;scrollbar-width:none}
  .ctxbar::-webkit-scrollbar{display:none}
  /* On a phone the search box and the period chip give way first: both have a
     home elsewhere (the module's own filter, the Financial Periods screen) and
     neither is worth a second line of chrome on a 390px viewport. */
  .cmdsearch,.cmdbar .cmdctl--period{display:none}
}

@media (max-width:768px){
  .filters{gap:var(--s-2)}
  .filters .field{min-width:0;flex:1 1 160px}
  .page-head h1{font-size:19px}
  .toolbar{margin-bottom:var(--s-2)}
}

@media (max-width:760px){
  /* A two-column definition list with a nowrap term and a long value has no
     width to give. Below this it stacks. */
  .kv{grid-template-columns:1fr;gap:2px var(--s-3)}
  .kv dt{margin-top:var(--s-2)}
  .kv dt:first-child{margin-top:0}
}

@media (max-width:600px){
  /* At phone width the tools at the end of a filter row have no space left to
     share, so they take a line of their own and lay out across it rather than
     being squeezed into a ragged column. */
  .filters__tools{flex:1 1 100%;margin-left:0}
  .filters__tools .btn{flex:1 1 auto;justify-content:center}
}

@media (max-width:400px){
  .main{padding:var(--s-3) var(--s-3) var(--s-6)}
  .grid,.grid--2,.grid--3,.grid--4{grid-template-columns:1fr}
  .stat-row{grid-template-columns:1fr 1fr;gap:var(--s-2)}
  .card__head{padding:var(--s-2) var(--s-3);min-height:42px}
  .card__body{padding:var(--s-3)}
  .page-head{gap:var(--s-2)}
  .page-head__actions{width:100%}
  .page-head__actions .btn{flex:1 1 auto;justify-content:center}
  table.data{--cell-x:var(--s-2)}
  .help__body{padding:var(--s-2)}
  /* 360px is the narrowest phone this is expected to meet. Nothing may be
     clipped there — the table scrolls inside its wrapper instead. */
  .table-wrap{-webkit-overflow-scrolling:touch}
}


/* ==========================================================================
   12  ACCESSIBILITY
   ========================================================================== */

/* The screen-reader-only label.
   `left:0;top:0` is not decoration. Without an offset the box is laid out at its
   static position — which, for the labels inside the permission grid, is 399px
   from the left edge on a 360px phone, because the grid scrolls sideways. Being
   out of flow it escapes the scroll container's clipping and became 40px of
   horizontal scroll on the whole document. It is 1px square and clipped to
   nothing, so pinning it costs nothing visually and it is still read aloud in
   the right place. */
.visually-hidden{position:absolute;left:0;top:0;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}


/* ==========================================================================
   13  SURFACES: the card stops being the default container
   --------------------------------------------------------------------------
   The brief for this pass named the problem exactly: large rounded cards,
   repeated white panels, too many borders, and dashboard cards where a table
   would have been more useful. A card is now reserved for four jobs:

     · a summary group whose figures belong together
     · an alert or a review panel that must be visually separable
     · a small cluster of metrics
     · a panel a person can fold away

   Everything else is a flat section: a heading, a hairline, and the content.
   Two rules enforce the rest: no shadow at rest, and no card inside a card.
   ========================================================================== */
.card{border-radius:var(--radius);box-shadow:none;border-color:var(--n-200)}
.card__head{padding:var(--pad-head);min-height:34px;gap:var(--s-2)}
.card__head h2{font-size:12.5px;font-weight:700}
.card__head .muted{font-size:11px}
.card__body{padding:var(--pad-body)}
.card__foot{padding:6px var(--s-3);border-radius:0 0 var(--radius) var(--radius)}
.card__title{font-size:12.5px;padding:var(--s-2) var(--s-3) 0}
.card__note{font-size:11.5px;margin:6px var(--s-3) var(--s-2)}
.note-card{border-radius:var(--radius);box-shadow:none;padding:var(--s-2) var(--s-3);
  margin-top:var(--s-2)}

/* No card inside a card. A nested panel keeps its separation from a hairline
   and its own head, never from a second border and a second radius. */
.card .card,.card .note-card,.card .table-wrap{border-left:0;border-right:0;border-radius:0;
  box-shadow:none}
.card .card{border-top:1px solid var(--n-150);border-bottom:0}
.card__body > .table-wrap:only-child{border:0}

/* The flat section: the default container from here on.
   A heading rule and the content under it. No box, no padding, no radius. The
   separation comes from the rule and the vertical rhythm. */
.section{display:block;margin:0 0 var(--s-4)}
.section__head{display:flex;align-items:center;gap:var(--s-2);
  padding:0 0 5px;margin:0 0 var(--s-2);border-bottom:1px solid var(--n-200)}
.section__head h2,.section__head h3{font-size:11px;font-weight:800;letter-spacing:.12em;
  text-transform:uppercase;color:var(--n-500);margin:0}
.section__meta{font-size:11px;color:var(--n-500)}
.section__tools{margin-left:auto;display:flex;align-items:center;gap:var(--s-1)}
.section__body{display:block;min-width:0}
/* A section rendered flush holds the table directly, with no .table-wrap of its
   own. Without this the table sets the section's minimum width and takes the
   grid column, and then the document, sideways with it. Found at 1024px on
   Home: the activity table was 411px inside a column that had 350. */
.section__body--flush{overflow-x:auto}
.section--boxed{border:1px solid var(--n-200);border-radius:var(--radius);background:var(--n-0)}
.section--boxed > .section__head{padding:6px var(--s-3);margin:0}
.section--boxed > .section__body{padding:var(--s-3)}
.section--boxed > .section__body--flush{padding:0}

/* A foldable section. <details>, so it costs no script and keeps working with
   the keyboard under this directory's content-security policy. */
details.section > summary{list-style:none;cursor:pointer}
details.section > summary::-webkit-details-marker{display:none}
details.section > summary .section__head{margin-bottom:0}
details.section[open] > summary .section__head{margin-bottom:var(--s-2)}
/* margin-left:auto, not a gap: the chevron belongs at the right-hand end of
   the heading rule, where it is in the same place on every section,
   instead of trailing whatever length of meta text that section has. */
details.section > summary .section__head::after{content:"";width:11px;height:11px;
  margin-left:auto;background:currentColor;color:var(--n-400);flex:0 0 11px;
  transition:transform .14s ease;
  -webkit-mask:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M6 4l7 6-7 6' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/></svg>") center/contain no-repeat;
          mask:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path d='M6 4l7 6-7 6' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/></svg>") center/contain no-repeat}
details.section[open] > summary .section__head::after{transform:rotate(90deg)}

/* A boxed section that folds keeps the heading's padding. The boxed rules
   target a DIRECT child, and in the folding form the heading is one level
   deeper, inside the summary, so without these the title sat hard against the
   border while every other boxed section had 6px of breathing room. */
.section--boxed > summary > .section__head{padding:6px var(--s-3);margin:0;border-bottom:0}
details.section--boxed[open] > summary > .section__head{border-bottom:1px solid var(--n-200)}
details.section--boxed > summary{border-radius:var(--radius)}
details.section--boxed[open] > summary{border-radius:var(--radius) var(--radius) 0 0}
details.section > summary:hover .section__head h2{color:var(--n-700)}
details.section > summary:focus-visible{outline:2px solid var(--brand-500,#2563eb);outline-offset:-2px}
/* The tools of a folding section ride at the top of the body, not in the
   summary, so pressing one does not also fold the section. */
.section__tools--stacked{margin:0 0 var(--s-2);justify-content:flex-end}
.section--boxed > .section__body--flush > .section__tools--stacked{padding:var(--s-2) var(--s-3) 0;margin:0}

.grid{gap:var(--gap-grid)}
.grid--4{grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}
.grid--3{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.grid--2{grid-template-columns:repeat(auto-fit,minmax(320px,1fr))}


/* ==========================================================================
   14  TABLES: the primary interface
   --------------------------------------------------------------------------
   Registers are where this application is actually used, so the table is the
   component that gets the budget. Geometry is still the four custom properties
   declared on `table.data`; what changes is the numbers and the furniture:

     standard   header 32px   row 44px    (was 40 / 46)
     compact    header 28px   row 34px    (was 32 / 36)

   44px is the floor asked for and it is a floor, not a cap: `height` on a row
   is a minimum, so a two-line cell still grows. Numbers stay right-aligned and
   tabular, names stay left, dates get a fixed column, and the status column is
   one width everywhere so twelve registers scan as one product.
   ========================================================================== */
table.data{--th-h:32px; --row-h:44px; --cell-x:10px; --cell-y:6px; font-size:12.5px}
table.data th{font-size:9.5px;letter-spacing:.1em;color:var(--n-500);
  border-bottom:1px solid var(--n-200);background:var(--n-50)}
table.data td{border-top:1px solid var(--n-100)}
table.data tbody tr:hover{background:var(--n-25)}
table.data--compact{--th-h:28px; --row-h:34px; --cell-x:10px; --cell-y:4px; font-size:12px}
.table-wrap{border-radius:var(--radius);border-color:var(--n-200)}

/* Sticky header. Pinned under the command bar, using the same token the bar's
   height comes from. A hard-coded offset here is what makes a header float
   over the wrong row the day the bar changes.
   Only on a wrapper that opts in, because a header pinned inside a short table
   inside a card looks like a bug rather than a feature. */
.table-wrap--sticky{overflow:auto;max-height:none}
.table-wrap--sticky table.data thead th{position:sticky;top:0;z-index:3}
.main .table-wrap--sticky{overflow:visible}
.main .table-wrap--sticky table.data thead th{top:calc(var(--cmd-h) - 1px)}

/* Column standards. A date is the same width on every register, and so is a
   status, so the eye finds them in the same place from screen to screen. */
table.data th.col-date,table.data td.col-date{width:96px;white-space:nowrap;color:var(--n-500)}
table.data th.col-status,table.data td.col-status{width:108px}
table.data th.col-num,table.data td.col-num{width:120px;text-align:right;
  font-variant-numeric:tabular-nums;font-family:var(--mono)}
table.data th.col-ref,table.data td.col-ref{width:130px;font-family:var(--mono);font-size:11.5px}
table.data th.col-act,table.data td.col-act{width:56px;text-align:right}

/* A cell whose whole content is a status badge never wraps: a status column
   that is one line on eleven rows and two on the twelfth makes a register look
   ragged for no information at all. Applied by what the cell CONTAINS rather
   than by a class, so the eighty-seven registers already written get it. */
table.data td:has(> .chip),table.data td:has(> .st),table.data td:has(> .pill){white-space:nowrap}

/* A totals row belongs to the table, not to a paragraph under it. */
table.data tfoot td,table.data tr.is-total td{background:var(--n-50);font-weight:700;
  border-top:1px solid var(--n-200)}

/* Bulk selection. The checkbox column is fixed and the header checkbox is the
   existing data-check-all control, so no new behaviour is introduced. */
table.data th.col-check,table.data td.col-check{width:34px;padding-right:0}
table.data td.col-check input,table.data th.col-check input{width:15px;height:15px;
  accent-color:var(--accent);margin:0}
table.data tbody tr.is-picked{background:var(--accent-soft)}
.bulkbar{display:flex;align-items:center;gap:var(--s-2);padding:6px var(--s-3);
  border:1px solid var(--n-200);border-bottom:0;border-radius:var(--radius) var(--radius) 0 0;
  background:var(--n-50);font-size:12px}
.bulkbar__count{font-weight:700;color:var(--n-900)}

/* A register's own sub-heading between two tables. */
.section-title{font-size:10.5px;letter-spacing:.12em;margin:var(--s-4) 0 var(--s-2)}


/* ==========================================================================
   15  STATUS
   --------------------------------------------------------------------------
   Eight states, one shape, one size, everywhere: Draft, Review, Approved,
   Posted, Paid, Overdue, Failed, Archived. A square-cornered badge rather than
   a pill, because a table of pills is the look this pass was asked to remove,
   and small enough that a column of them does not compete with the figures.

   The existing .chip and .pill classes are retuned to the same geometry rather
   than replaced, because 170 templates already use them and a second badge
   vocabulary would guarantee two looks forever.
   ========================================================================== */
.chip,.pill{border-radius:3px;padding:2px 6px;font-size:9.5px;letter-spacing:.06em;
  font-weight:800;line-height:1.5}
.pill+.pill{margin-left:4px}
.st{display:inline-flex;align-items:center;gap:5px;border-radius:3px;padding:2px 6px;
  font-size:9.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  white-space:nowrap;border:1px solid transparent}
.st::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;flex:0 0 5px}
.st--draft{background:var(--n-100);color:var(--n-500)}
.st--review{background:var(--warn-soft);color:var(--warn)}
.st--approved{background:var(--info-soft);color:var(--info)}
.st--posted{background:var(--ok-soft);color:var(--ok)}
.st--paid{background:var(--ok-soft);color:var(--ok)}
.st--overdue{background:var(--danger-soft);color:var(--danger)}
.st--failed{background:var(--danger-soft);color:var(--danger);border-color:#F2CFCF}
.st--archived{background:var(--n-50);color:var(--n-400)}
.st--neutral{background:var(--n-100);color:var(--n-600)}


/* ==========================================================================
   16  TOOLBARS AND FILTERS
   --------------------------------------------------------------------------
   A filter row is a toolbar, not a form laid out like a settings page. One
   bordered strip directly above the register it filters, controls at 30px,
   labels above them at 9.5px, and the tools at the end of the row.
   ========================================================================== */
.filters{gap:var(--s-2);align-items:flex-end;margin-bottom:var(--s-2);
  padding:var(--s-2) var(--s-3);border:1px solid var(--n-200);border-radius:var(--radius);
  background:var(--n-0)}
/* Every control shares the row and shrinks rather than one of them setting a
   width nobody asked for. A <select>'s intrinsic width is its LONGEST OPTION,
   which is how a Category filter with "Professional fees and subscriptions" in
   it grew to 200px and pushed the Filter button onto a second line. */
.filters .field{margin:0;flex:1 1 120px;min-width:100px;max-width:190px}
.filters .field > label{font-size:9.5px;letter-spacing:.1em;margin-bottom:2px}
.filters input,.filters select{height:30px;padding:0 8px;font-size:12.5px;line-height:normal;
  min-width:0;text-overflow:ellipsis}
.filters input[type=date]{height:30px}
.filters .btn{height:30px;padding:0 12px}
.filters .field--search{flex:2 1 240px;min-width:180px;max-width:460px}
.filters__tools{margin-left:auto;display:flex;align-items:center;gap:var(--s-1)}
.toolbar{margin-bottom:var(--s-2);gap:var(--s-1)}
.toolbar__label{font-size:9.5px;letter-spacing:.1em}

/* Buttons. One primary per page; everything else is quiet. */
.btn{padding:6px 11px;border-radius:var(--radius);font-size:12.5px;font-weight:600;
  box-shadow:none}
.btn--sm{padding:4px 9px;font-size:11.5px}
.btn--danger{background:var(--n-0);border-color:var(--n-200);color:var(--danger)}
.btn--danger:hover{background:var(--danger-soft);border-color:#E9B7B7;filter:none}
/* A destructive action that really is the point of its own screen, such as
   Empty the bin or Delete this account, opts back in. Nothing in a list may. */
.btn--danger.btn--solid{background:var(--danger);border-color:var(--danger);color:#fff}
.pill-tabs{border-radius:var(--radius);padding:2px;gap:2px}
.pill-tabs a{border-radius:3px;padding:5px 11px;font-size:12px}
.pill-tabs a.is-active{box-shadow:none;border:1px solid var(--n-200)}
.segmented{border-radius:var(--radius);padding:2px}
.segmented__opt{border-radius:3px;padding:4px 11px;font-size:12px}
.segmented__opt.is-active{box-shadow:none}
.pagination{margin-top:var(--s-2);font-size:12px}
.pagination li span,.pagination li a{min-width:28px;height:28px;border-radius:var(--radius)}


/* ==========================================================================
   17  EMPTY STATES
   --------------------------------------------------------------------------
   An empty section says one line and gets out of the way. A 300px box with a
   heading, a paragraph and a button, repeated down a dashboard, is how a
   system with nothing in it yet looks broken rather than new.
   ========================================================================== */
.empty{padding:var(--s-3);text-align:left;color:var(--n-500);font-size:12px;line-height:1.5}
.empty strong{display:inline;font-size:12.5px;color:var(--n-700);margin:0}
.empty span{display:inline;max-width:none;font-size:12px}
.empty strong + span::before{content:". "}
.empty .btn{margin-left:var(--s-2)}
.empty .u-mt-2{display:inline;margin:0}
/* Inside a table cell it is one quiet line across the register. */
table.data td.empty{height:auto;padding:var(--s-3);text-align:left}
/* A dashboard card with nothing in it is one line, not a blank chart. */
.dash__body > .empty{padding:var(--s-2) 0}


/* ==========================================================================
   18  FORMS
   --------------------------------------------------------------------------
   Operational forms are entered by people who type the same eight fields forty
   times a day. One field per full-width row makes that a scrolling exercise.
   Two and three column grids, short field spacing, and long text full width.
   ========================================================================== */
.field{margin-bottom:var(--s-2)}
.field > label{font-size:10px;letter-spacing:.08em;margin-bottom:2px}
.field .hint{font-size:11px;margin-top:2px}
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=reset]):not([type=image]):not([type=range]):not([type=hidden]):not(.cell-in):not(.cmdsearch__input),
select,textarea{padding:6px 9px;border-radius:var(--radius);line-height:18px}
input[type=date],input[type=datetime-local],input[type=month],input[type=week],input[type=time]{height:32px}
textarea{min-height:70px}
fieldset{border-radius:var(--radius);padding:var(--s-2) var(--s-3);margin-bottom:var(--s-2)}
legend{font-size:10px;letter-spacing:.09em}
.checkline{font-size:12.5px}

/* The form grid. Columns collapse to one at 760px, and anything that needs the
   full width says so rather than being guessed at. */
.form-grid{display:grid;gap:var(--s-2) var(--s-3);align-items:start}
.form-grid--2{grid-template-columns:repeat(2,minmax(0,1fr))}
.form-grid--3{grid-template-columns:repeat(3,minmax(0,1fr))}
.form-grid--4{grid-template-columns:repeat(4,minmax(0,1fr))}
.form-grid > .is-full{grid-column:1/-1}
.form-grid > .is-span-2{grid-column:span 2}

/* A named group of fields. The heading is a rule, not a boxed panel, so a form
   of five groups is five rules rather than five nested cards. */
.form-section{margin:0 0 var(--s-4)}
.form-section > h3,.form-section__head{font-size:10.5px;font-weight:800;letter-spacing:.12em;
  text-transform:uppercase;color:var(--n-500);margin:0 0 var(--s-2);padding-bottom:4px;
  border-bottom:1px solid var(--n-150)}
.form-section__note{font-size:11.5px;color:var(--n-500);margin:-2px 0 var(--s-2);max-width:92ch}
details.form-section > summary{list-style:none;cursor:pointer}
details.form-section > summary::-webkit-details-marker{display:none}
.form-actions{display:flex;align-items:center;gap:var(--s-2);padding-top:var(--s-3);
  margin-top:var(--s-3);border-top:1px solid var(--n-150)}
.form-actions__spacer{flex:1 1 auto}


/* ==========================================================================
   19  THE HOME CONSOLE
   --------------------------------------------------------------------------
   Home is the operations desk, not a gallery. Three things above the fold:
   where the money stands, what needs a person today, and what just happened.

     .sumstrip    one strip, eight figures, hairline dividers, no boxes
     .attn        the work queue, as a table
     .dash        the widget grid, which STAYS. The arrangement, the hiding
                  and the collapse are a saved preference belonging to each
                  user, and taking that away would be a feature removal, not a
                  redesign. It is simply no longer the first thing on the page.
   ========================================================================== */
.sumstrip{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));
  border:1px solid var(--n-200);border-radius:var(--radius);background:var(--n-0);
  overflow:hidden;margin:0 0 var(--s-3)}
.sumstrip__cell{padding:7px var(--s-3);border-left:1px solid var(--n-150);min-width:0}
.sumstrip__cell:first-child{border-left:0}
.sumstrip__k{display:block;font-size:9.5px;font-weight:800;letter-spacing:.11em;
  text-transform:uppercase;color:var(--n-500);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.sumstrip__v{display:block;font-size:17px;line-height:1.2;font-weight:700;letter-spacing:-.015em;
  color:var(--n-900);font-variant-numeric:tabular-nums;margin-top:1px}
.sumstrip__v small{font-size:9.5px;font-weight:800;color:var(--n-400);margin-left:3px;
  letter-spacing:.05em}
.sumstrip__s{display:block;font-size:10.5px;line-height:1.35;color:var(--n-500);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sumstrip__v.is-danger{color:var(--danger)}
.sumstrip__v.is-ok{color:var(--ok)}
.sumstrip__v.is-warn{color:var(--warn)}
.sumstrip__cell.is-alert{background:#FFFBF5}
.sumstrip__trend{font-size:10px;font-weight:800;letter-spacing:.03em}
.sumstrip__trend.is-up{color:var(--ok)} .sumstrip__trend.is-down{color:var(--danger)}
a.sumstrip__cell{color:inherit;display:block}
a.sumstrip__cell:hover{background:var(--n-25)}

/* Needs attention. A queue, so a table, and every row is a link into the screen
   that clears it. The count column is the same width as a status column so the
   two registers on this page line up with each other. */
.attn table.data{--row-h:38px}
.attn td.attn__what{font-weight:600;color:var(--n-800)}
.attn td.attn__why{color:var(--n-500)}
.attn tr.is-critical td.attn__what{color:var(--danger)}
.attn tr.is-warning td.attn__what{color:#8A5A00}
.attn__n{display:inline-grid;place-items:center;min-width:22px;height:19px;padding:0 6px;
  border-radius:3px;background:var(--n-100);color:var(--n-700);font-size:11px;font-weight:800;
  font-variant-numeric:tabular-nums}
.attn tr.is-critical .attn__n{background:var(--danger-soft);color:var(--danger)}
.attn tr.is-warning .attn__n{background:var(--warn-soft);color:var(--warn)}

/* The two-column operational area: the queue beside what just happened. */
.console{display:grid;gap:var(--gap-grid);grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);
  align-items:start;margin:0 0 var(--s-4)}
.console > *{min-width:0}
@media (max-width:1080px){ .console{grid-template-columns:1fr} }

/* The widget grid, retuned. Same behaviour, less furniture. */
/* A card is as tall as what is in it.
   The grid stretched every card to the tallest in its row and `.dash__body >
   .empty` then centred the one line of text inside the space that made, so a
   card saying "no expense recorded" was 300px tall with the sentence floating
   in the middle of it. That is the blank-area problem this pass exists to fix,
   and it is two declarations. Ragged bottoms in a row of cards are the correct
   trade: the alternative is white space pretending to be content. */
.dash{gap:var(--gap-grid);align-items:start}
.dash__card{height:auto;border-radius:var(--radius);box-shadow:none;border-color:var(--n-200)}
.dash__body > .empty{margin:0}
.dash__head{padding:5px var(--s-3);min-height:32px}
.dash__head h2{font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;
  color:var(--n-500)}
.dash__body{padding:var(--s-3)}
.dash__foot{padding-top:var(--s-2);margin-top:var(--s-2);font-size:11px}
.kpi__label{font-size:9.5px;letter-spacing:.11em}
.kpi__value{font-size:19px;letter-spacing:-.015em}
.kpi__note{font-size:10.5px}
table.mini{font-size:12px}
table.mini td{padding:4px 0}


/* ==========================================================================
   20  THE SIDEBAR
   --------------------------------------------------------------------------
   Dark, as it was, and grouped as the brief asks: five sections over the
   existing groups, so the same twenty-six screens are found by asking "am I
   selling, buying, or keeping the books" rather than by reading a list.

   The section heading is derived from the RENDERED order, not printed from a
   fixed table, so a person who has dragged Accountant to the top still gets a
   truthful heading above it instead of a label that has stopped describing
   what follows.
   ========================================================================== */
.sidebar__brand{padding:9px 12px}
.brand__mark{width:28px;height:28px;flex-basis:28px;border-radius:var(--radius);font-size:11px}
.brand__text strong{font-size:12.5px}
.brand__text small{font-size:9.5px}
.sidebar__nav{padding:4px 6px 16px}
.nav__link{padding:5px 10px;border-radius:var(--radius);font-size:12.4px;gap:8px}
.nav__link.is-active{box-shadow:inset 2px 0 0 var(--accent)}
.nav--projects .nav__link{padding-left:12px;font-size:12px}
.nav__heading{font-size:9px;letter-spacing:.16em;padding:12px 10px 4px;color:var(--n-500)}
.navsection{font-size:9px;font-weight:800;letter-spacing:.16em;text-transform:uppercase;
  color:rgba(255,255,255,.30);padding:12px 10px 3px;margin-top:2px;
  border-top:1px solid rgba(255,255,255,.06)}
.navsection:first-child{border-top:0;margin-top:0;padding-top:6px}
.app.is-collapsed .navsection{font-size:0;padding:8px 0 6px;text-align:center}
.app.is-collapsed .navsection::after{content:"";display:block;height:1px;
  background:rgba(255,255,255,.10);margin:0 12px}
.navgroup__btn{padding:5px 10px;font-size:12.4px;border-radius:var(--radius)}
.sidebar__foot{padding:8px 12px;font-size:10px}
.navarrange{padding:8px 10px 2px}
.navarrange__btn{border-radius:var(--radius);padding:4px 9px;font-size:11px}


/* ==========================================================================
   21  RESPONSIVE: for sections 13 to 20 only
   --------------------------------------------------------------------------
   This layer sits after section 11, so a width rule written up there can no
   longer reach a component declared down here. Everything width-dependent in
   the console layer therefore lives in this one block, in descending order.
   ========================================================================== */

@media (max-width:1080px){
  /* The queue and the activity list stop sharing a row before either becomes
     too narrow to read a reference in. */
  .console{grid-template-columns:1fr}
}

@media (max-width:900px){
  .form-grid--3,.form-grid--4{grid-template-columns:repeat(2,minmax(0,1fr))}
}

@media (max-width:768px){
  /* A filter row becomes a two-column grid on a phone (declared in section 11),
     and a grid track cannot shrink below its content's minimum. The 130px floor
     that keeps a select readable on a laptop is what pushed the ledger's filter
     row 53px past the right edge of a 390px screen. Below this width the floor
     goes and the fields share what there is. */
  .filters{padding:var(--s-2)}
  .filters .field{min-width:0}
  .filters > *{min-width:0;max-width:100%}
  .filters .field--search{min-width:0;flex-basis:100%;max-width:none}
  .form-grid,.form-grid--2,.form-grid--3,.form-grid--4{grid-template-columns:1fr}
  /* A header pinned under the command bar competes with the phone's own URL bar
     and with the horizontal scroll of the table it belongs to. */
  .main .table-wrap--sticky{overflow:auto}
  .main .table-wrap--sticky table.data thead th{position:static}
}

@media (max-width:560px){
  /* The standing facts (base currency, tax system, country) do not change
     while somebody works, and they are on the Company Profile screen. On a
     390px line they were pushing the breadcrumb into "Home > Purc". The one
     fact that IS volatile, the project filter in force, stays: it is the
     reason a register can look empty. */
  .ctxbar .ctxfact:not(.ctxfact--warn){display:none}
  .sumstrip{grid-template-columns:repeat(2,minmax(0,1fr))}
  .sumstrip__cell{border-left:0;border-top:1px solid var(--n-150)}
  .sumstrip__cell:nth-child(even){border-left:1px solid var(--n-150)}
  .sumstrip__cell:first-child,.sumstrip__cell:nth-child(2){border-top:0}
  .page-head{align-items:flex-start}
}


/* ------------------------------------------------------- the project bar
   A project page keeps its own bar. It is the one screen where the identity,
   the stage and the module tabs ARE the context, and the generic breadcrumb
   line cannot carry them. It is retuned to the same density as the rest of the
   chrome rather than left as the one band that is still 120px tall. */
.projbar{padding:6px var(--s-4) 0;border-bottom:1px solid var(--n-200);background:var(--n-25)}
.projbar .crumbs{font-size:11.5px;gap:4px}
.projbar__head{gap:var(--s-2);margin:5px 0 6px}
.projbar__avatar{width:28px;height:28px;flex-basis:28px;border-radius:var(--radius);font-size:11px}
.projbar__id h2{font-size:14px}
.projbar__id p{margin:1px 0 0;font-size:11px;gap:6px}
.projbar__actions{gap:var(--s-1)}
.projtab{padding:6px 11px;font-size:12px;border-radius:var(--radius) var(--radius) 0 0}
.projtab--more{border-radius:var(--radius);padding:5px 11px}
.projtabs__menu{border-radius:var(--radius-lg);box-shadow:var(--shadow-2)}
.projtabs__menuitem{padding:6px 9px;border-radius:var(--radius);font-size:12.5px}

/* ------------------------------------------------- the project breakdown
   Shown whenever more than one project is in view. It is a table already; it
   just no longer needs to be a card with a 14px radius and a shadow. */
.pbreak{margin:0 0 var(--s-3);border-radius:var(--radius);box-shadow:none}
.pbreak__head{padding:5px var(--s-3);min-height:30px}
.pbreak__head h2{font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;
  color:var(--n-500)}
.pbreak th{font-size:9.5px;padding:5px 10px}
.pbreak td{padding:5px 10px}
.pbreak table{font-size:12px}


/* ==========================================================================
   MARKETPLACE SHEET (a component block, not part of the shared standards layer)

   A spreadsheet, not a form that resembles one. The grid is already full of
   empty rows when it opens, cells are square and small, every side has a rule,
   and the row numbers sit in a grey gutter the way a spreadsheet's do. The
   point of the borrowed look is muscle memory: somebody who lives in Excel
   should be able to click, type, tab, and paste without being taught anything.

   Geometry is four numbers, in one place, so the whole sheet retunes together.
   ========================================================================== */
.mp{
  --cell-h:22px;          /* Excel's default row height at 100% */
  --cell-x:6px;
  --cell-font:12px;
  --gut-w:34px;
  --rule:var(--n-200);
  display:flex;flex-direction:column;border:1px solid var(--n-200);border-radius:8px;
  background:var(--n-0);overflow:hidden;box-shadow:var(--shadow-1)
}

/* ------------------------------------------------------------- totals band */
.mp__band{display:flex;flex-wrap:wrap;background:var(--brand-700,#0B5F45);color:#fff}
.mp__band-cell{padding:5px 12px;display:flex;flex-direction:column;gap:0;
  border-right:1px solid rgba(255,255,255,.16);min-width:92px}
.mp__band-cell:last-child{border-right:0}
.mp__band-cell--lead{background:rgba(0,0,0,.18);min-width:auto}
.mp__band-k{font-size:9px;text-transform:uppercase;letter-spacing:.08em;opacity:.74;
  font-weight:600;white-space:nowrap;line-height:1.5}
.mp__band-v{font-size:13.5px;font-weight:700;font-variant-numeric:tabular-nums;
  white-space:nowrap;line-height:1.35}
.mp__band-cell--lead .mp__band-v{font-size:12px;font-weight:600}

/* ------------------------------------------------------------ rates drawer */
.mp__rates{border-bottom:1px solid var(--n-150);background:var(--n-0)}
.mp__rates>summary{display:flex;align-items:center;gap:9px;padding:6px 12px;cursor:pointer;
  list-style:none;user-select:none}
.mp__rates>summary::-webkit-details-marker{display:none}
.mp__rates-title{font-weight:700;font-size:11.5px}
.mp__rates-sub{color:var(--n-500);font-size:11px}
.mp__chev{margin-left:auto;color:var(--n-500);transition:transform .15s}
.mp__rates[open] .mp__chev{transform:rotate(180deg)}
.mp__rates-body{border-top:1px dashed var(--n-150);padding:4px 12px 11px}
.mp__rates-form{display:flex;gap:16px;flex-wrap:wrap;align-items:flex-end}
.mp__rate{display:flex;flex-direction:column;gap:2px}
.mp__rate label{font-size:9px;text-transform:uppercase;letter-spacing:.07em;
  color:var(--n-500);font-weight:600}
.mp__rate input{width:96px;padding:3px 7px;border:1px solid var(--n-200);border-radius:4px;
  background:var(--n-0);font-size:12px;text-align:right;font-variant-numeric:tabular-nums}
.mp__rate-acts{display:flex;gap:6px;margin-left:auto;flex-wrap:wrap}

/* -------------------------------------------------------------- the ribbon */
.mp__bar{display:flex;align-items:center;gap:7px;flex-wrap:wrap;padding:7px 12px;
  border-bottom:1px solid var(--n-150);background:var(--n-50)}
.mp__gap{flex:1}
.mp__note{color:var(--n-600);font-size:11px}
.mp__upload{display:inline-flex}
.mp__upload label{margin:0}
.mp__cellref{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;
  color:var(--n-600);min-width:104px;border:1px solid var(--n-200);border-radius:4px;
  padding:3px 8px;background:var(--n-0)}

/* How many rows the sheet shows. Typed rows are never dropped by it: the sheet
   is always at least as long as the last line with something on it. */
.mp__rowsctl{display:inline-flex;align-items:center;gap:0;
  border:1px solid var(--n-200);border-radius:5px;background:var(--n-0);overflow:hidden}
.mp__rowsctl input{width:56px;border:0;border-left:1px solid var(--n-200);
  border-right:1px solid var(--n-200);border-radius:0;background:transparent;
  padding:3px 6px;font-size:11.5px;text-align:center;font-variant-numeric:tabular-nums;
  height:24px;line-height:24px;min-height:0;box-shadow:none;
  -moz-appearance:textfield;appearance:textfield}
.mp__rowsctl input::-webkit-outer-spin-button,
.mp__rowsctl input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.mp__step{appearance:none;border:0;background:transparent;color:var(--n-700);cursor:pointer;
  width:24px;height:24px;font-size:14px;line-height:1;font-weight:700;padding:0}
.mp__step:hover{background:var(--n-100);color:var(--n-900)}
.mp__rowsctl-label{font-size:10px;text-transform:uppercase;letter-spacing:.07em;
  color:var(--n-500);padding:0 7px;font-weight:600}

/* ---------------------------------------------------------------- the grid */
.mp__wrap{overflow:auto;max-height:calc(100vh - 300px);min-height:420px;background:var(--n-0)}
/* Fixed layout, or the browser sizes columns to the inputs' intrinsic width
   and every explicit width is ignored. The table's own width is set in script
   to the sum of the columns. */
table.mp__grid{border-collapse:separate;border-spacing:0;table-layout:fixed;min-width:100%}
.mp__grid th,.mp__grid td{
  border-right:1px solid var(--rule);border-bottom:1px solid var(--rule);
  padding:0;height:var(--cell-h);white-space:nowrap;background:var(--n-0);
  font-size:var(--cell-font);
}

/* Column headers: a spreadsheet's grey band, centred, not shouting. */
.mp__grid thead th{position:sticky;top:0;z-index:5;background:var(--n-100);color:var(--n-700);
  font-size:10.5px;font-weight:600;text-align:center;padding:0 var(--cell-x);
  height:24px;border-bottom:1px solid var(--n-300,#c8d2da);letter-spacing:.01em}
.mp__grid thead th.is-calc{background:var(--n-150);color:var(--n-600)}

.mp__grid th.is-stick,.mp__grid td.is-stick{position:sticky;z-index:4}
.mp__grid thead th.is-stick{z-index:6}
.mp__grid td.is-stick{background:var(--n-0)}
.mp__grid .is-edge{border-right:1px solid var(--n-300,#c8d2da)}
.mp__grid .is-edge::after{content:"";position:absolute;top:0;right:-1px;bottom:0;width:1px;
  box-shadow:2px 0 5px rgba(16,32,42,.10)}

/* Row numbers: the spreadsheet's row header. */
.mp__gut{width:var(--gut-w);min-width:var(--gut-w);text-align:center;color:var(--n-600);
  font-size:10.5px;background:var(--n-100)!important;position:relative;
  border-right:1px solid var(--n-300,#c8d2da);font-variant-numeric:tabular-nums}
.mp__gut button{appearance:none;border:0;background:transparent;color:var(--n-500);cursor:pointer;
  font-size:13px;line-height:1;padding:0 4px;border-radius:3px;display:none}
.mp__grid tbody tr:hover .mp__gut{background:var(--n-150)!important;color:var(--n-800)}
.mp__grid tbody tr:hover .mp__gut .mp__n{display:none}
.mp__grid tbody tr:hover .mp__gut button{display:inline-block}
.mp__gut button:hover{color:var(--danger);background:var(--danger-soft)}
.mp__grid tbody tr.is-current .mp__gut{background:var(--n-200)!important;color:var(--n-900);font-weight:700}

/* Cells. A white cell is yours; a grey one is worked out. */
/* The base input in this stylesheet is a 38px form control with 9px of padding
   and a focus ring. None of that belongs in a 22px cell, so every part of it is
   unset here rather than fought with. */
.mp__grid td input.cell-in{
  width:100%;min-width:0;height:var(--cell-h);min-height:0;line-height:var(--cell-h);
  border:0;border-radius:0;background:transparent;box-shadow:none;
  padding:0 var(--cell-x);font-size:var(--cell-font);font-family:inherit;color:inherit;
  box-sizing:border-box;text-overflow:ellipsis;
}
.mp__grid td input::placeholder{color:var(--n-300,#c8d2da)}
/* The ring is inset, so it needs no stacking help. It must NOT set position:
   relative: the two frozen columns are position:sticky with a left offset, and
   relative turns that offset from "pin here" into "shift right by this much",
   which threw the focused cell one column across. */
.mp__grid td.is-focus{box-shadow:inset 0 0 0 2px var(--brand-500,#12805E)}
.mp__grid td input:focus{outline:0;border:0;box-shadow:none}
.mp__grid td.is-num,.mp__grid td.is-num input{text-align:right;font-variant-numeric:tabular-nums}
.mp__grid td.is-code input{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}
.mp__grid td.is-calc{background:var(--n-50);color:var(--n-700);text-align:right;
  padding:0 var(--cell-x);font-variant-numeric:tabular-nums;font-weight:500}
.mp__grid td.is-calc.is-neg{color:var(--danger);background:var(--danger-soft);font-weight:700}
.mp__grid td.is-calc.is-thin{color:var(--warn);background:var(--warn-soft);font-weight:600}
.mp__grid td.is-calc.is-strong{color:var(--ok);font-weight:700}

/* A row that has nothing in it yet is quiet: no flag, no colour, no total. */
.mp__grid tbody tr.is-blank td.is-calc{color:var(--n-300,#c8d2da);background:var(--n-25)}
.mp__grid tbody tr.is-flagged .mp__gut{box-shadow:inset 3px 0 0 var(--warn)}
.mp__grid tbody tr:hover td:not(.is-calc){background:var(--n-25)}

.mp__grid tfoot td{position:sticky;bottom:0;z-index:3;background:var(--n-100);
  border-top:1px solid var(--n-300,#c8d2da);font-weight:700;text-align:right;
  padding:0 var(--cell-x);height:24px;font-variant-numeric:tabular-nums;font-size:11.5px}
.mp__grid tfoot td.is-stick{z-index:5}
.mp__grid tfoot td.mp__total-label{text-align:center;color:var(--n-600);font-size:10px;
  text-transform:uppercase;letter-spacing:.05em}

/* ------------------------------------------------------------------ checks */
.mp__checks{border-top:1px solid var(--n-200);background:var(--n-0)}
.mp__checks>summary{display:flex;align-items:center;gap:9px;padding:7px 12px;cursor:pointer;
  list-style:none;user-select:none;flex-wrap:wrap}
.mp__checks>summary::-webkit-details-marker{display:none}
.mp__checks-title{font-weight:700;font-size:11.5px}
.mp__checks-sub{color:var(--n-500);font-size:11px}
.mp__checks-list{margin:0;padding:0;list-style:none;max-height:150px;overflow:auto;
  border-top:1px dashed var(--n-150)}
.mp__checks-list li{display:flex;gap:10px;align-items:baseline;padding:5px 12px;
  border-bottom:1px solid var(--n-150);font-size:11.5px}
.mp__checks-list li:last-child{border-bottom:0}
.mp__checks-row{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10.5px;
  color:var(--n-500);min-width:40px}
.mp__dot{width:6px;height:6px;border-radius:50%;flex:none;transform:translateY(-1px)}
.mp__dot--error{background:var(--danger)}
.mp__dot--warning{background:var(--warn)}
.mp__checks-empty{padding:10px 12px;color:var(--n-500);font-size:11.5px}
.mp__flag{margin:9px 0 0;color:var(--warn);font-size:12px}

/* ------------------------------------------------------------ paste dialog */
.mp__dlg{border:1px solid var(--n-200);border-radius:10px;padding:0;background:var(--n-0);
  color:var(--n-900);width:min(660px,92vw);box-shadow:var(--shadow-3,0 20px 60px rgba(16,32,42,.28))}
.mp__dlg::backdrop{background:rgba(16,32,42,.5)}
.mp__dlg-head{padding:14px 18px 6px}
.mp__dlg-head h2{margin:0 0 3px;font-size:15px}
.mp__dlg-head p{margin:0;color:var(--n-500);font-size:12px}
.mp__dlg-body{padding:12px 18px}
.mp__dlg-body textarea{width:100%;height:200px;resize:vertical;padding:10px;
  border:1px solid var(--n-200);border-radius:7px;background:var(--n-25);color:var(--n-900);
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;line-height:1.6}
.mp__dlg-foot{display:flex;gap:8px;align-items:center;padding:12px 18px 16px;
  border-top:1px solid var(--n-150)}
.mp.is-drop{outline:2px dashed var(--brand-500,#12805E);outline-offset:-4px}

@media (max-width:860px){
  /* A finger needs 44px. The shared touch-target rule already forces that on
     every input, so the row is sized to match rather than left fighting it. */
  .mp{--cell-h:44px; --cell-font:14px; --cell-x:8px}
  .mp__band-cell{min-width:84px;padding:5px 10px}
  .mp__band-v{font-size:12.5px}
  .mp__rate-acts{margin-left:0;width:100%}
  .mp__wrap{max-height:none;min-height:300px}
}

/* ==========================================================================
   ADD AS YOU GO
   The dialog behind every "+ Add new" option in a picker, and the option
   itself. One dialog per page, shared by every picker on it.
   ========================================================================== */
.qc{border:1px solid var(--n-200);border-radius:10px;padding:0;background:var(--n-0);
  color:var(--n-900);width:min(460px,92vw);box-shadow:var(--shadow-3,0 20px 60px rgba(16,32,42,.28))}
.qc::backdrop{background:rgba(16,32,42,.5)}
.qc__head{padding:16px 20px 4px}
.qc__head h2{margin:0 0 4px;font-size:16px}
.qc__head p{margin:0;color:var(--n-500);font-size:12.5px}
.qc__body{padding:14px 20px;display:flex;flex-direction:column;gap:12px}
.qc__acct{display:flex;flex-direction:column;gap:12px}
.qc__foot{display:flex;gap:8px;align-items:center;padding:12px 20px 16px;
  border-top:1px solid var(--n-150)}
.qc__gap{flex:1}
.qc__err{margin:0;padding:8px 10px;border-radius:6px;background:var(--danger-soft);
  color:var(--danger);font-size:12.5px}
.qc__form .hint{margin:4px 0 0;color:var(--n-500);font-size:11.5px}
/* The option that opens it, so it does not read as another account. */
option[value="__qc_new"]{font-style:italic;color:var(--accent)}

/* ===================================== containers that must hold their content
   Two faults found by measuring every screen at 1280, 1440 and 1600, rather
   than by looking at the ones that happened to be noticed.

   1. A ROW MENU'S FORM WAS LAYING ITS FIELDS OUT IN A LINE.

   `.rowmenu__menu form{display:block}` is one class and one type, so it beats
   `.rowmenu__form{display:grid}`, which is one class. The grid never applied.
   A block box whose children are inline and inline-block puts them all on one
   line, and the menu is absolutely positioned with visible overflow, so the
   fields ran off the right of the window: on the sourcing menu the last one
   started 416px past the edge and could not be reached at all. Raising the
   specificity is the whole fix. */
.rowmenu__menu form.rowmenu__form{display:grid;gap:var(--s-2);padding:var(--s-2);min-width:230px}
.rowmenu__menu form.rowmenu__form > *{min-width:0;max-width:100%}
.rowmenu__form input,.rowmenu__form select,.rowmenu__form textarea{width:100%;box-sizing:border-box}

/* The menu is anchored to the right edge already. This stops a wide one from
   opening off the left of a narrow screen instead. */
@media (max-width:640px){
  .rowmenu__menu{max-width:calc(100vw - 24px)}
}

/* 2. A TABLE WITHOUT ITS WRAPPER PUSHED THE WHOLE PAGE WIDER.

   Every table in this application belongs inside .table-wrap, which scrolls.
   One added to a card without it took the page 222px past the window at 1280,
   which is the bleed that gets reported as "containers are not set properly".
   Markup is the real fix and it has been applied; this is the net underneath,
   so the next one that forgets scrolls instead of bleeding. */
.section__body > table.data,
.card__body > table.data{display:block;max-width:100%;overflow-x:auto}

/* A long unbroken string, an id, a URL, a user agent, breaks rather than
   stretching whatever holds it. */
.section__body .mono,.section__body code,.section__body .break{overflow-wrap:anywhere;word-break:break-word}
.kv dd{min-width:0;overflow-wrap:anywhere}

/* Grid and flex children default to min-width:auto, which is their content, so
   a wide child widens the row instead of the row constraining the child. This
   is the single most common cause of the bleed above. */
.form-grid > *,.sumstrip > *,.filters > *{min-width:0}

/* The last item the width sweep found: an inline label in a toolbar sat 4px
   past its row at 1024, because a flex child's default min-width is its
   content. */
.toolbar > *,.toolbar__inline{min-width:0;max-width:100%}
