/* Rigline-3W. THREE_W_STYLING_COMMAND.md is binding, and this sheet is its section
   15.1: rewritten from section 4's :root, not edited. No sprint 002 class survives that
   was not re-derived. Every hex is in :root and nowhere else. Rules are one per line and
   comments are terse on purpose: check_file_length.py holds .css to 200 lines and
   CLAUDE.md section 2 allows exactly one stylesheet. Density is the price of both. */
:root {
  /* Base. Section 4 values, unaltered. */
  --ink:#000000; --paper:#FFFFFF; --caliche:#F4F4F2;      /* tab bar+ink / page+pane / recessed */
  --steel-70:#4A4A4A; --steel-40:#9A9A9A; --rule:#E2E2E0; /* secondary / faint / every 1px line */
  /* Brand. --red is the sRGB of three-w-services --color-red-900, oklch(39.6% 0.141
     25.723). Verified 2026-09-15. Do not alter. */
  --red:#82181A; --red-press:#5F1113; --red-tint:#F8ECEC; /* brand / pressed / selected wash */
  /* Status, section 8. --crit equals --red on purpose. */
  --ok:#1F8A4C; --attn:#E8590C; --crit:#82181A; --idle:#9A9A9A;
  --attn-tint:#FCEDE3;   /* v1.4: the payroll card's Differs shading */
  /* Type. Barlow speaks, Semi Condensed titles, Plex Mono counts. */
  --font:"Barlow",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --cond:"Barlow Semi Condensed","Barlow",system-ui,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  /* Space (the only eight) */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s6:24px; --s8:32px; --s12:48px; --s16:64px;
  /* Shape: 4px means pressable, 0 means everything else. */
  --r-press:4px; --r-none:0;
  /* Layout */
  --hdr:56px; --tabbar:56px;
  --pane-list:320px; --pane-list-wide:360px; --pane-ctx:360px;
  --row:36px; --row-touch:44px; --ctl:32px; --ctl-touch:44px;
  --fld-num:120px;   /* 022: a compact field - a money box, a pinned select */
  --page-margin:0.5in;   /* 058: the printed invoice's Letter margin */
  /* Motion: the only duration. */
  --t:160ms ease-out;
  /* Built up from sections 5 and 6 so that no literal lands below this block. The type
     scale is fixed at eight sizes and nothing between them exists. */
  --fs-11:11px; --fs-13:13px; --fs-14:14px; --fs-15:15px;
  --fs-17:17px; --fs-22:22px; --fs-28:28px; --fs-40:40px;
  --lh-prose:1.5; --lh-title:1.3; --lh-figure:1;
  --w-normal:400; --w-medium:500; --w-semi:600;   /* 700 is deleted */
  --prose:72ch; --form:720px; --summary:120px;    /* prose cap / form cap / summary ceiling */
  --icon:20px; --stroke:1.5; --dot:6px; --dot-touch:8px;
  --ctl-wide:40px; --kpi:72px; --edge:2px; --bar:2px;  /* pinned primary / KPI / 2px marks */
  --shadow:0 0 var(--s6) rgb(0 0 0 / 0.14); --scrim:0.4; /* sheet and context overlay only */
  /* D2, owner ruling R56 (2026-09-29): the approved mockup. A black header carrying the red
     3W mark, content on cards over a warm page, a black footer. These tokens are the whole
     of it; the rules below read nothing else. */
  --hdr-ink:#111111; --nav-ink:#BDBDBD; --nav-mark:#C4302B; --card-head:#FAFAF8; --rule-soft:#EDEDEB;
  --shadow-card:0 1px 2px rgb(17 17 17 / 0.06), 0 4px 12px rgb(17 17 17 / 0.04);
  --r-card:8px; --mark-w:44px; --mark-h:36px; --nav-bar:3px;
  --inv-cols:2.42fr 2.63fr 1.78fr;  /* v1.9: the invoice box, as app/pdf/_header.WEIGHTS */
}
@font-face{font-family:"Barlow";font-style:normal;font-weight:400;font-display:swap;src:url("../fonts/barlow-400.woff2") format("woff2")}
@font-face{font-family:"Barlow";font-style:normal;font-weight:500;font-display:swap;src:url("../fonts/barlow-500.woff2") format("woff2")}
@font-face{font-family:"Barlow Semi Condensed";font-style:normal;font-weight:600;font-display:swap;src:url("../fonts/barlow-semi-condensed-600.woff2") format("woff2")}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:400;font-display:swap;src:url("../fonts/ibm-plex-mono-400.woff2") format("woff2")}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:500;font-display:swap;src:url("../fonts/ibm-plex-mono-500.woff2") format("woff2")}
/* Base. tabular-nums is set once here so it inherits to every number, Barlow included. */
*,*::before,*::after{box-sizing:border-box}
html{height:100%}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--font);font-size:var(--fs-15);font-weight:var(--w-normal);line-height:var(--lh-prose);font-variant-numeric:tabular-nums}
h1,h2,h3{margin:0;font-family:var(--cond);font-weight:var(--w-semi);line-height:var(--lh-title)}
*:focus-visible,.is-focus{outline:var(--edge) solid var(--red);outline-offset:var(--edge)}
.skip-link{position:absolute;left:var(--s2);top:var(--s2);z-index:40;padding:var(--s2) var(--s3);background:var(--ink);color:var(--paper);border-radius:var(--r-press);font-size:var(--fs-14);font-weight:var(--w-medium);text-decoration:none;transform:translateY(-200%);transition:transform var(--t)}
.skip-link:focus{transform:translateY(0)}
.icon{display:block;width:var(--icon);height:var(--icon);flex:0 0 auto;stroke-width:var(--stroke)}
.mono{font-family:var(--mono);font-size:var(--fs-13)}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* Shell. The page does not scroll; panes do (section 3.2). */
/* The `sub` row is auto, not a fixed height: with no element in it the track is 0, which
   is how charter R37 gets a sub-header that is ABSENT rather than an empty 33px band.
   _shell/_subnav.html emitting nothing is the other half; an empty <nav> would defeat it. */
.app{height:100dvh;overflow:hidden;display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:var(--hdr) auto minmax(0,1fr) auto;grid-template-areas:"hdr" "sub" "panes" "foot";background:var(--caliche)}
.plain{min-height:100dvh;overflow-y:auto}
/* Top nav (charter R36). The active marker is the section 7 tab recipe at shell scale:
   a 2px --ink bottom border, never red - section 14 row 2.4 killed red in the header and
   the one-red exemption named the rail indicator, which no longer exists.
   position:relative and no overflow:hidden are deliberate and currently unused: .hdr is
   overflow:hidden with no stacking context, so the overflow menu that comes next has to
   anchor here. overflow-x:auto is the honest floor until then - the strip may scroll
   inside itself, but it must never push the page. */
.topnav{display:flex;align-items:center;justify-self:center;white-space:nowrap}
.topnav-item{display:inline-flex;align-items:center;flex:0 0 auto;height:var(--hdr);padding:var(--nav-bar) var(--s3) 0;border-bottom:var(--nav-bar) solid transparent;color:var(--nav-ink);font-size:var(--fs-14);font-weight:var(--w-medium);text-decoration:none;white-space:nowrap}
.topnav-item:hover,.topnav-item.is-hover{color:var(--paper)}
.topnav-item.is-active{color:var(--paper);border-bottom-color:var(--nav-mark)}  /* D2: the red returns to the header (R56) */
/* Three tracks, not flex-with-margins: a middle `auto` column between two equal
   minmax(0,1fr) flanks centres the nav on the header's own midline however wide the
   wordmark and the member block happen to be. The flanks yield; the nav never does.
   position/z-index anchor the More menu: .panes is position:relative and later in DOM
   order, so it would otherwise paint over the dropdown. overflow:hidden is gone - it was
   only ever containing .hdr-crumb's ellipsis, and the crumb now lives in the sub-header. */
.hdr{grid-area:hdr;position:relative;z-index:10;display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:var(--s3);padding:0 var(--s6);background:var(--hdr-ink);border-bottom:0}
.hdr-name{display:inline-flex;align-items:center;gap:var(--s3);justify-self:start;font-family:var(--cond);font-weight:var(--w-semi);line-height:var(--lh-title);color:var(--paper);text-decoration:none;white-space:nowrap}
/* D2: the 3W mark - a red block, the wordmark beside it in tracked capitals. */
.hdr-mark{display:inline-flex;align-items:center;justify-content:center;width:var(--mark-w);height:var(--mark-h);background:var(--red);color:var(--paper);border-radius:var(--r-press);font-family:var(--cond);font-size:var(--fs-22);font-weight:var(--w-semi);letter-spacing:.02em}
.hdr-word{font-size:var(--fs-17);letter-spacing:.08em;text-transform:uppercase}
.hdr-right{justify-self:end;min-width:0;display:flex;align-items:center;gap:var(--s3)}
.hdr-user{min-width:0;overflow:hidden;color:var(--nav-ink);font-size:var(--fs-13);white-space:nowrap;text-overflow:ellipsis}
/* overflow-x: nine office sections measure 443px against a 390px bar (sprint 012). The
   bar scrolls; the PAGE must not, because a horizontally scrolling document breaks every
   screen and not just the nav. Field keeps three tabs filling the width -- flex-grow 1
   stretches them when there is room, flex-shrink 0 stops them crushing when there is not. */
.tabbar{display:none;grid-area:tab;background:var(--ink);overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.tabbar::-webkit-scrollbar{display:none}
.tabbar-item{position:relative;display:flex;flex:1 0 auto;padding:0 var(--s3);flex-direction:column;align-items:center;justify-content:center;gap:var(--s1);color:var(--steel-40);font-size:var(--fs-14);line-height:var(--lh-figure);text-decoration:none}
.tabbar-item.is-active{color:var(--paper)}
.tabbar-item.is-active::before{content:"";position:absolute;top:0;left:var(--s4);right:var(--s4);height:var(--edge);background:var(--red)}
/* Sub-header (charter R37). .tabs supplies the flex, the gap, the padding, the 1px rule
   and the horizontal scroll - there is no second tab vocabulary, which is why this rule
   sets almost nothing. --ctl 32 plus that rule is the 33px R37 names. */
.subnav{grid-area:sub;position:relative;align-items:center;min-height:calc(var(--ctl) + var(--s3));padding-inline:var(--s6);background:var(--paper)}  /* D2: a real band under the black header, not a hairline strip */
.subnav-crumb{margin-left:auto;display:flex;align-items:center;gap:var(--s2);min-width:0;color:var(--steel-70);font-size:var(--fs-13);white-space:nowrap}
.subnav-crumb:first-child{margin-left:0}
.subnav-crumb a{color:var(--steel-70)}
.subnav-stamp{padding-left:var(--s4);color:var(--steel-40);font-family:var(--mono);font-size:var(--fs-11)}
/* The More overflow. A dropdown floats over other content, which is the one case the
   method allows a shadow; every other surface earns its depth from a 1px border. */
.hdr-menu{position:relative;display:flex;align-items:center}
.hdr-menu-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--ctl);height:var(--ctl);padding:0;background:none;border:0;border-radius:var(--r-press);color:var(--nav-ink);cursor:pointer;transition:color var(--t)}
.hdr-menu-btn:hover,.hdr-menu-btn[aria-expanded="true"],.hdr-menu-btn.is-active{color:var(--paper)}
.hdr .btn{background:transparent;color:var(--paper);border-color:var(--steel-70)}
.hdr .btn:hover{border-color:var(--nav-ink)}
.hdr-menu-list{position:absolute;top:100%;right:0;z-index:1;min-width:180px;margin:0;padding:var(--s1) 0;list-style:none;background:var(--paper);border:1px solid var(--rule);border-radius:var(--r-press);box-shadow:var(--shadow)}
.hdr-menu-item{display:block;padding:var(--s2) var(--s4);color:var(--steel-70);font-size:var(--fs-14);text-decoration:none;transition:color var(--t)}
.hdr-menu-item:hover,.hdr-menu-item.is-active{color:var(--ink)}
/* D2 (R56): the page footer - black, the company in tracked capitals. */
.app-foot{grid-area:foot;display:flex;align-items:center;gap:var(--s4);padding:var(--s2) var(--s6);background:var(--hdr-ink);color:var(--steel-40);font-size:var(--fs-11)}
.app-foot-name{color:var(--paper);font-family:var(--cond);font-weight:var(--w-semi);letter-spacing:.08em;text-transform:uppercase}
.phone-action{display:none;grid-area:act;padding:var(--s2) var(--s3);border-top:1px solid var(--rule)}
/* Panes. Pinned is pinned, content scrolls (section 6.6). */
.panes{grid-area:panes;position:relative;display:grid;grid-template-columns:var(--pane-list) minmax(0,1fr) var(--pane-ctx);gap:var(--s4);padding:var(--s4);overflow:hidden}
/* D2: every pane is a card - 1px rule, 8px corners, a soft shadow - on the warm page. */
.pane{display:flex;flex-direction:column;min-width:0;overflow:hidden;background:var(--paper);border:1px solid var(--rule);border-radius:var(--r-card);box-shadow:var(--shadow-card)}
.pane-head{flex:0 0 auto;display:flex;align-items:center;gap:var(--s2);padding:var(--s3) var(--s4);background:var(--card-head);border-bottom:1px solid var(--rule)}
.pane-body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:var(--s4)}
.pane-head-stack{flex-wrap:wrap}.pane-body-flush{padding:0}
.pane-foot{flex:0 0 auto;padding:var(--s3) var(--s4);border-top:1px solid var(--rule)}
.pane-title{font-size:var(--fs-22);margin-right:auto}
.summary{flex:0 0 auto;max-height:var(--summary);overflow:hidden;padding:var(--s3) var(--s4);border-bottom:1px solid var(--rule)}
.summary-line{display:flex;align-items:center;gap:var(--s2);min-width:0}
.meta,.summary-meta{margin:var(--s1) 0 0;color:var(--steel-70);font-size:var(--fs-13)}
.section{margin:0 0 var(--s6);padding-bottom:var(--s6);border-bottom:1px solid var(--rule)}
.section:last-child{margin-bottom:0;padding-bottom:0;border-bottom:0}
.section-head{margin:0 0 var(--s2);font-family:var(--font);font-size:var(--fs-15);font-weight:var(--w-medium);line-height:var(--lh-prose)}
.prose,.empty{max-width:var(--prose);margin:0;color:var(--steel-70);font-size:var(--fs-15)}
.bar{flex:0 0 auto;height:var(--bar);width:0;background:var(--ink);opacity:0;transition:opacity 0s linear 250ms}
.htmx-request .bar,.bar.htmx-request{width:100%;opacity:1;transition:width 900ms ease-out,opacity 0s linear 250ms}
/* Components, section 7. The .is-* twins exist so /_dev/components can show every state. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--s2);height:var(--ctl);padding:0 var(--s3);background:var(--paper);color:var(--ink);border:1px solid var(--rule);border-radius:var(--r-press);font-family:var(--font);font-size:var(--fs-14);font-weight:var(--w-medium);line-height:1;text-decoration:none;white-space:nowrap;cursor:pointer}
.btn:hover,.btn.is-hover{background:var(--caliche)}
.btn:active,.btn.is-pressed{background:var(--caliche);border-color:var(--steel-40)}
.btn-primary{background:var(--red);color:var(--paper);border-color:var(--red)}
.btn-primary:hover,.btn-primary.is-hover{background:var(--red-press);border-color:var(--red-press)}
.btn-primary:active,.btn-primary.is-pressed{background:var(--red-press);border-color:var(--ink)}
.btn:disabled,.btn.is-disabled,.btn-primary:disabled,.btn-primary.is-disabled{background:var(--caliche);color:var(--steel-40);border-color:var(--rule);cursor:default}
.btn-wide{width:100%;height:var(--ctl-wide)}.btn-icon{padding:0;width:var(--ctl)}
.field{margin:0 0 var(--s4);max-width:var(--form)}
.label{display:block;margin-bottom:var(--s1);font-size:var(--fs-14);font-weight:var(--w-medium)}
.input,.select{display:block;width:100%;height:var(--ctl);padding:0 var(--s2);background:var(--paper);color:var(--ink);border:1px solid var(--rule);border-radius:var(--r-press);font-family:var(--font);font-size:var(--fs-14)}
.input-mono{font-family:var(--mono);font-size:var(--fs-13)}.input::placeholder{color:var(--steel-40)}
.input:hover,.select:hover,.input.is-hover{border-color:var(--steel-40)}
.input.is-error,.input[aria-invalid="true"]{border-color:var(--crit)}
.input:disabled,.select:disabled,.input.is-disabled,.select.is-disabled{background:var(--caliche);color:var(--steel-40);cursor:default}
.help{max-width:var(--prose);margin:var(--s1) 0 0;color:var(--steel-70);font-size:var(--fs-13)}.help-error{color:var(--crit)}
.tabs{display:flex;flex:0 0 auto;gap:var(--s4);padding:0 var(--s4);border-bottom:1px solid var(--rule);overflow-x:auto}
.tabs-item{display:inline-flex;align-items:center;flex:0 0 auto;height:var(--ctl);padding:0;background:none;border:0;border-bottom:var(--edge) solid transparent;color:var(--steel-70);font-family:var(--font);font-size:var(--fs-14);font-weight:var(--w-medium);text-decoration:none;cursor:pointer}
.tabs-item:hover,.tabs-item.is-hover{color:var(--ink)}.tabs-item.is-active,.tabs-item[data-active="true"]{color:var(--ink);border-bottom-color:var(--ink)}
.tabs-item:disabled,.tabs-item.is-disabled{color:var(--steel-40);cursor:default}
.chip{display:inline-flex;align-items:center;gap:var(--s1);height:var(--s6);padding:0 var(--s2);background:var(--paper);color:var(--steel-70);border:1px solid var(--rule);border-radius:var(--r-press);font-family:var(--font);font-size:var(--fs-13);cursor:pointer}
.chip:hover,.chip.is-hover{background:var(--caliche)}
.chip.is-on{background:var(--caliche);color:var(--ink);border-color:var(--steel-40)}.chip:disabled,.chip.is-disabled{color:var(--steel-40);cursor:default}
.dot{flex:0 0 auto;width:var(--dot);height:var(--dot);border-radius:50%;background:var(--idle)}
.dot-ok{background:var(--ok)}.dot-attn{background:var(--attn)}.dot-crit{background:var(--crit)}.dot-idle{background:var(--idle)}
.rows{list-style:none;margin:0;padding:0}
.row{display:flex;align-items:center;gap:var(--s2);width:100%;height:var(--row);padding:0 var(--s4) 0 var(--s3);background:none;border:0;border-bottom:1px solid var(--rule);color:var(--ink);font-family:var(--font);font-size:var(--fs-15);text-align:left;text-decoration:none;cursor:pointer}
.row:hover,.row.is-hover{background:var(--caliche)}.row.is-selected{background:var(--red-tint)}
.row-id{flex:0 0 auto;font-family:var(--mono);font-size:var(--fs-13);color:var(--steel-70)}
.row-name{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:var(--fs-15)}
.row-meta{margin-left:auto;flex:0 0 auto;font-family:var(--mono);font-size:var(--fs-13);color:var(--steel-70)}
.table{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:var(--fs-13)}
.table th{position:sticky;top:0;z-index:1;padding:var(--s2) var(--s3);background:var(--card-head);border-bottom:1px solid var(--rule);text-align:left;font-family:var(--font);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.06em;text-transform:uppercase;color:var(--steel-70);white-space:nowrap}
.table td{height:var(--row);padding:var(--s2) var(--s3);border-bottom:1px solid var(--rule-soft)}
/* D2: no zebra - rows differ on hover only. */
.table tbody tr:hover,.table tbody tr.is-hover{background:var(--caliche)}
.table tbody tr.is-selected{background:var(--red-tint)}
.cell-name{font-family:var(--font);font-size:var(--fs-15)}.cell-num{text-align:right}
.kpis{display:flex;flex:0 0 auto;border-bottom:1px solid var(--rule)}
.kpi{flex:1 1 0;min-width:0;height:var(--kpi);padding:var(--s3) var(--s4)}
.kpi+.kpi{border-left:1px solid var(--rule)}
.kpi-edge-ok{box-shadow:inset var(--s1) 0 0 0 var(--ok)}.kpi-label{margin:0;color:var(--steel-70);font-size:var(--fs-13)}
.kpi-figure{margin:0;font-family:var(--mono);font-size:var(--fs-28);font-weight:var(--w-medium);line-height:var(--lh-figure)}
.scrim{position:fixed;inset:0;z-index:20;background:var(--ink);opacity:var(--scrim)}
.sheet{position:fixed;top:0;right:0;bottom:0;z-index:21;display:flex;flex-direction:column;width:var(--pane-ctx);max-width:100%;background:var(--paper);border-left:1px solid var(--rule);box-shadow:var(--shadow);transform:translateX(100%);transition:transform var(--t)}.sheet.is-open{transform:none}
.flash-region,.error-region{flex:0 0 auto;padding:var(--s3) var(--s4) 0}
.error-region:empty{display:none}
.flash,.error-alert{margin:0 0 var(--s2);padding:var(--s2) var(--s3);background:var(--caliche);border-left:var(--s1) solid var(--steel-40);font-size:var(--fs-14)}
.flash[data-flash="success"]{border-left-color:var(--ok)}.flash[data-flash="warning"]{border-left-color:var(--attn)}.flash[data-flash="info"]{border-left-color:var(--steel-70)}
.flash[data-flash="danger"],.error-alert{border-left-color:var(--crit);background:var(--red-tint)}
.error-code{margin:0;font-family:var(--mono);font-size:var(--fs-13);color:var(--steel-70)}.error-text{margin:0;color:var(--ink)}
.page{max-width:var(--form);padding:var(--s12) var(--s4) var(--s6)}
/* .page-auth is the ONE sanctioned exception to section 12's no-centred-container rule.
   Owner's ruling 2026-09-15; reasoning in sprints/003_shell_and_tokens.md. Do not widen
   it past the auth pages and do not "fix" it back. */
.page-auth{display:flex;flex-direction:column;align-items:center;gap:var(--s6);max-width:none;min-height:100dvh;padding:var(--s8) var(--s4)}.auth-box{margin-block:auto}
.auth-box,.page-auth .flash-region,.page-auth .error-region{width:100%;min-width:0;max-width:var(--pane-ctx)}
.page-auth .flash-region{padding:0}
.page-wordmark{margin:0;font-family:var(--cond);font-size:var(--fs-40);font-weight:var(--w-semi);line-height:var(--lh-title);text-align:center}
.page-auth .page-sub{margin-top:var(--s4);text-align:center}
.page-auth .page-note{max-width:none;text-align:center}
.page-footnote{margin:0;color:var(--steel-40);font-size:var(--fs-11);text-align:center}
.page-title{font-size:var(--fs-22);margin-top:var(--s2)}
.page-sub{margin-top:var(--s6);font-family:var(--font);font-size:var(--fs-15);font-weight:var(--w-medium)}
.page-note{max-width:var(--prose);margin:var(--s1) 0 0;color:var(--steel-70);font-size:var(--fs-13)}
.form{margin-top:var(--s4);max-width:var(--form)}
.actions{display:flex;gap:var(--s2);margin-top:var(--s6)}.stack{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2)}.grow{flex:1 1 auto;min-width:0}
.spec{margin:0 0 var(--s6);padding-bottom:var(--s6);border-bottom:1px solid var(--rule)}
.spec-head{margin:0 0 var(--s3);font-size:var(--fs-17)}
.spec-grid{display:grid;grid-template-columns:var(--pane-list) minmax(0,var(--form));gap:var(--s2) var(--s4);align-items:center}
.spec-label{font-family:var(--mono);font-size:var(--fs-13);color:var(--steel-70)}.swatch{display:inline-block;width:var(--s16);height:var(--s6);border:1px solid var(--rule)}
/* Reference-page helpers. /_dev/components renders every token and every state, and
   a swatch cannot take an inline style or a hex (section 3.6), so each one is a class. */
.sw-ink{background:var(--ink)}.sw-paper{background:var(--paper)}.sw-caliche{background:var(--caliche)}.sw-steel-70{background:var(--steel-70)}.sw-steel-40{background:var(--steel-40)}.sw-rule{background:var(--rule)}
.sw-red{background:var(--red)}.sw-red-press{background:var(--red-press)}.sw-red-tint{background:var(--red-tint)}.sw-ok{background:var(--ok)}.sw-attn{background:var(--attn)}.sw-crit{background:var(--crit)}.sw-idle{background:var(--idle)}
.fs-11{font-size:var(--fs-11)}.fs-13{font-size:var(--fs-13)}.fs-14{font-size:var(--fs-14)}.fs-15{font-size:var(--fs-15)}.fs-17{font-size:var(--fs-17)}.fs-22{font-size:var(--fs-22)}.fs-28{font-size:var(--fs-28)}.fs-40{font-size:var(--fs-40)}
.cond{font-family:var(--cond);font-weight:var(--w-semi);line-height:var(--lh-title)}.w-medium{font-weight:var(--w-medium)}.figure{line-height:var(--lh-figure)}
.hdr-short,.ctx-toggle,.pane-back{display:none}  /* shown only by the breakpoint that needs them, so these must sit above the media queries */
[x-cloak]{display:none}
/* Breakpoints, section 6.3, all five rows. */
@media (min-width:1600px){.panes{grid-template-columns:var(--pane-list-wide) minmax(0,1fr) var(--pane-ctx)}}
@media (max-width:1279px){
  .panes{grid-template-columns:var(--pane-list) minmax(0,1fr)}
  .pane-ctx{position:absolute;top:0;right:0;bottom:0;z-index:5;width:var(--pane-ctx);max-width:100%;box-shadow:var(--shadow);transform:translateX(100%);transition:transform var(--t)}
  .panes[data-context="open"] .pane-ctx{transform:none}.ctx-toggle{display:inline-flex}
  .col-low{display:none}  /* section 7: the lowest-priority column hides first */
}
@media (max-width:1023px){
  .panes{grid-template-columns:minmax(0,1fr)}
  .panes[data-pane="list"] .pane-detail{display:none}.panes[data-pane="detail"] .pane-list{display:none}.pane-back{display:inline-flex}
  /* Six labels plus the hamburger need about 500px. Below 1024 the flanks give it up
     first - the wordmark shortens to "3W" and the item padding tightens - so the nav
     stays on one row and stays centred rather than wrapping or scrolling. */
  .hdr-name{display:none}.hdr-short{display:inline-flex;align-items:center;justify-content:center;justify-self:start;width:var(--mark-w);height:var(--mark-h);background:var(--red);border-radius:var(--r-press);font-family:var(--cond);font-size:var(--fs-22);font-weight:var(--w-semi);color:var(--paper);text-decoration:none}
  .topnav-item{padding:0 var(--s2)}
  /* Icon-only sign-out below 1024: the label goes visually hidden rather than
     display:none, so the button keeps an accessible name. Measured at 768 - with
     the label in, .hdr-right ran 30px past the viewport and scrolled the page. */
  .hdr-signout{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
}
@media (max-width:767px){
  .app{grid-template-columns:minmax(0,1fr);grid-template-rows:var(--s12) auto minmax(0,1fr) auto var(--tabbar);grid-template-areas:"hdr" "sub" "panes" "act" "tab"}
  /* .topnav-item, not .topnav: the ITEMS go and the More menu stays, so the overflow
     destinations are still reachable on a phone without spending a seventh tab on them.
     field renders no menu at all, so their header is byte-identical to before R37. */
  .topnav-item{display:none}.tabbar{display:flex}.phone-action{display:block}
  .phone-action .btn{width:100%;height:var(--s12)}
  .hdr{padding:0 var(--s3);gap:var(--s2)}
  .panes{gap:var(--s2);padding:var(--s2)}.app-foot{display:none}
  .subnav{padding-inline:var(--s3)}
  .pane-ctx,.ctx-toggle{display:none}  /* section 6.5: one task per screen, no side-by-side; context facts fold into the summary block */
  .pane-head,.pane-foot,.summary,.flash-region,.error-region{padding-left:var(--s3);padding-right:var(--s3)}
  .pane-body{padding:var(--s3)}.page{padding:var(--s8) var(--s3) var(--s6)}.page-auth{padding:var(--s6) var(--s3)}.page-wordmark{font-size:var(--fs-28)}
  .btn,.input,.select,.tabs-item,.chip{height:var(--ctl-touch)}.btn-wide{height:var(--s12)}
  .row,.table td{height:var(--row-touch)}.dot{width:var(--dot-touch);height:var(--dot-touch)}
  .col-min{display:none}  /* never a horizontal scrollbar on a table */
  .row-id,.row-meta,.table,.mono,.help,.meta,.hdr-user,.page-note,.summary-meta,.kpi-label,.error-code,.spec-label{font-size:var(--fs-14)}
  .input,.select,.btn,.label,.tabs-item,.chip,.flash,.error-alert{font-size:var(--fs-15)}
}
@media (prefers-reduced-motion:reduce){.skip-link,.sheet,.pane-ctx,.bar,.disclose .icon,.toggle-track,.toggle-knob{transition:none}}
.panes.panes-single{grid-template-columns:minmax(0,1fr)}
/* Team, sprint 010. The roster row's role tag, the list-pane notes, the two form grids,
   the permission grid, and the one place an invite link is rendered. No new token: every
   value below is one section 4 already declares. These rules sit after the breakpoint
   block on purpose, and carry their own two media queries, so nothing above has to be
   reordered to let them land. */
.row-tag{flex:0 0 auto;margin-left:auto;color:var(--steel-70);font-size:var(--fs-13)}
/* Doubles as the list's empty state and its "show more": both are a padded line inside a
   flush pane body, where a bare .empty would sit hard against the pane edge. */
.list-note{display:block;padding:var(--s3) var(--s4);color:var(--steel-70);font-size:var(--fs-13)}
a.list-note{color:var(--ink);font-weight:var(--w-medium);text-decoration:none}
a.list-note:hover{background:var(--caliche)}
.fields,.flags{display:grid;grid-template-columns:minmax(0,1fr);gap:0 var(--s4);max-width:var(--form)}
.flag{display:flex;align-items:center;gap:var(--s2);min-height:var(--ctl);font-size:var(--fs-14);cursor:pointer}
.check{flex:0 0 auto;width:var(--s4);height:var(--s4);accent-color:var(--red);cursor:pointer}
.check:disabled{cursor:default}
.fact{margin:0;color:var(--ink);font-size:var(--fs-15);overflow-wrap:anywhere}
/* An invite link is an identifier, not prose, so it is mono; it wraps because a token is
   longer than the context pane is wide and a horizontal scrollbar is not an option. */
.link-mono{display:block;margin-top:var(--s2);font-family:var(--mono);font-size:var(--fs-13);color:var(--ink);text-decoration:underline;overflow-wrap:anywhere}
/* 021. An invite is a code an administrator reads aloud, not a link to send, so it
   is sized to be read across a desk and tracked to keep the eight characters apart.
   .link-mono stays: change orders still render a real link with it. */
.setup-code{display:block;margin-top:var(--s2);font-family:var(--mono);font-size:var(--fs-22);font-weight:var(--w-medium);letter-spacing:.08em;color:var(--ink)}
@media (min-width:1280px){.fields,.flags{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:767px){
  .flag{min-height:var(--ctl-touch);font-size:var(--fs-15)}
  .list-note{padding:var(--s3);font-size:var(--fs-14)}
}
/* A member's name is longer than a section label, and the phone header has to hold both
   it and the member menu. A flex child does not shrink below its own content without
   min-width:0, so the ellipsis already on .subnav-crumb never fired and the current step ran
   under the member name at 390. Only the last step shrinks: the separators and the "Team"
   link back to the roster stay whole, which is what the breadcrumb is there for. */
.subnav-crumb>*:last-child{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
/* The read-only permission grid. A member who may not edit permissions still has to be
   able to read them, so the state is a word and the dot only repeats it. */
p.flag{margin:0}
.flag-state{margin-left:auto;padding-left:var(--s3);color:var(--steel-70);font-size:var(--fs-13)}
/* Customers, sprint 011. Two class tokens and one scoped breakpoint block. No new custom
   property: every value below is one section 4 already declares. */
/* A bordered sub-block inside a section: one work site, one contact, one new-x form. The
   section rule already separates the groups, so these separate the rows INSIDE a group -
   1px, never a shadow (section 12). Tighter than a .section because a context pane is
   360px wide and these stack. */
.subform{margin:0 0 var(--s3);padding:var(--s3);border:1px solid var(--rule)}
.subform:last-child{margin-bottom:0}
.subform-head{margin:0 0 var(--s2);font-size:var(--fs-14);font-weight:var(--w-medium)}
.subform .field{margin-bottom:var(--s3)}
.subform .actions{margin-top:var(--s3)}
/* The Customers console's context pane holds the work sites and the contacts, and both
   are editable. The shell hides .pane-ctx outright below 768 (section 6.5, one task per
   screen), which on this screen would take both away from a phone entirely. So here the
   context pane becomes the third destination of the SAME data-pane switch - still one
   task per screen - reached by the button in the detail pane's summary and left by the
   one in its own header. Every rule is scoped to .panes-customers: no other console
   changes, and the shell's own list/detail behaviour is untouched. */
@media (max-width:1023px){
  .panes-customers[data-pane="context"] .pane-list,
  .panes-customers[data-pane="context"] .pane-detail{display:none}
  .panes-customers[data-pane="context"] .pane-ctx{display:flex;position:static;width:auto;max-width:none;transform:none;box-shadow:none;border-left:0}
}
@media (max-width:767px){.panes-customers .ctx-toggle{display:inline-flex}}
/* The context pane's pinned primary and the thumb-zone one in .phone-action are the same
   action, and below 768 the rules above put both on screen at once. The thumb zone wins:
   section 6.5 pins the primary above the tab bar, and .phone-action is that slot. */
@media (max-width:767px){.panes-customers .pane-foot{display:none}}
/* Reaching the new-customer form from the list where the detail pane is off screen. */
button.list-note{width:100%;background:none;border:0;border-bottom:1px solid var(--rule);font-family:var(--font);font-weight:var(--w-medium);color:var(--ink);text-align:left;cursor:pointer}
button.list-note:hover{background:var(--caliche)}
.list-add{display:none}
@media (max-width:1023px){.panes-customers .list-add{display:block}}
/* Equipment and Vendors, sprint 012. Two class tokens and two scoped breakpoint blocks. No
   new custom property: every value below is one section 4 already declares. */
/* The Vendors console has two panes, not three: a vendor's columns all live in the detail
   pane and there is no second editable region beside them, so the shell's third grid
   column would be an empty gutter. Single class inside the breakpoint, never a bare
   .panes-pair: below 1024 the shell is one column and that rule must keep winning. */
@media (min-width:1024px){.panes.panes-pair{grid-template-columns:var(--pane-list) minmax(0,1fr)}}
/* The Equipment console's context pane holds the rate history and the form that starts a
   new period, so it is editable, and the shell hides .pane-ctx outright below 768 (section
   6.5, one task per screen) - which on this screen would take rates away from a phone
   entirely. Same answer as .panes-customers, scoped the same way: the context pane becomes
   the third destination of the SAME data-pane switch, reached by the button in the detail
   pane's summary and left by the one in its own header. No other console changes. */
@media (max-width:1023px){
  .panes-equipment[data-pane="context"] .pane-list,
  .panes-equipment[data-pane="context"] .pane-detail{display:none}
  .panes-equipment[data-pane="context"] .pane-ctx{display:flex;position:static;width:auto;max-width:none;transform:none;box-shadow:none;border-left:0}
}
@media (max-width:767px){.panes-equipment .ctx-toggle{display:inline-flex}}
/* Reaching the new-unit and new-vendor forms from a list whose detail pane is off screen. */
@media (max-width:1023px){.panes-equipment .list-add,.panes-pair .list-add{display:block}}
/* Lookups, sprint 014. One class token, no new custom property: every value below is one
   section 4 already declares. The five panes are rows of FORMS rather than a table,
   because one macro renders both the rows a person edits and the rows they only act on,
   and a table cell is the wrong box for an input. The grid is what keeps that from
   reading as five stacked cards: the label, its facts and its fields run down the left
   column and the control sits on the first line at the right, so a row is one line tall
   when it has nothing to edit. Capped at --form because a 1366px-wide text input is not
   a text input anyone can read. */
.panes-lookups .subform{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:var(--s4);align-items:start;max-width:var(--form)}
.panes-lookups .subform-head,.panes-lookups .subform .meta,.panes-lookups .subform .fields{grid-column:1}
.panes-lookups .subform>.actions{grid-column:2;grid-row:1;margin-top:0}
.panes-lookups .subform .field{margin-bottom:var(--s3)}
.panes-lookups .subform .field:last-child{margin-bottom:0}
/* Below 768 the second column would leave a name with about 200px. One column, and the
   control returns to the bottom of the row where a thumb expects it. */
@media (max-width:767px){
  .panes-lookups .subform{grid-template-columns:minmax(0,1fr)}
  .panes-lookups .subform>.actions{grid-column:1;grid-row:auto;margin-top:var(--s3)}
}
/* Inventory, sprint 017. Four class tokens and one scoped breakpoint. No new custom
   property: every value below is one section 4 already declares. */
/* The first search box in this repository, and 865 rows is what it is for. It sits on its
   own line in the PINNED pane head - flex-basis 100% forces the wrap that
   .pane-head-stack's flex-wrap allows - so it is outside the region its own request
   swaps and cannot lose the caret every 300ms. The icon is the affordance; it carries no
   meaning the placeholder does not repeat, so it is aria-hidden by the icon macro. */
.search{flex:1 0 100%;display:flex;align-items:center;gap:var(--s2);min-width:0}
.search-icon{flex:0 0 auto;color:var(--steel-40)}
.search-input{flex:1 1 auto;min-width:0}
/* A rename row: the name down the left, its control on the first line at the right, so a
   row with one field is one line tall rather than three. This is the shape the
   .panes-lookups pane needed first, as a modifier on the subform rather than a second
   console selector - neither screen can then restyle the other. */
.subform-row{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:var(--s4);align-items:start;max-width:var(--form)}
.subform-row>.field{grid-column:1;margin-bottom:0}
.subform-row>.actions{grid-column:2;grid-row:1;margin-top:0}
@media (max-width:767px){
  .subform-row{grid-template-columns:minmax(0,1fr)}
  .subform-row>.actions{grid-column:1;grid-row:auto;margin-top:var(--s3)}
}
/* Reaching the new-item form from a list whose detail pane is off screen. That is the
   whole of .panes-inventory: the context pane on this console is READ ONLY, so it needs
   none of the data-pane="context" rules the Customers and Equipment consoles carry to put
   an editable one back on a phone. */
@media (max-width:1023px){.panes-inventory .list-add{display:block}}

/* ======================================================================================
   TABLE CONSOLES, sprint 018. Charter R38.

   The Inventory and Equipment LIST screens are the layout reference's option C -- a
   full-width table -- sprint 022 took the strip off the Inventory one -- with one
   change: option C docks a 480px sheet for the detail, and this docks nothing. A row expands in place instead, which is why the
   table gives up no columns and the editor gets the full width rather than 480px. The
   reference's own note on C is "Forms want more room than a sheet gives"; this is that
   note taken seriously. The DETAIL screens, /inventory/<id> and /equipment/<id>, are
   untouched option A.

   Nine class tokens, no new custom property, no hex, borders not shadows.
   ====================================================================================== */

/* A <tbody> PER ITEM is the grouping element: it carries the id a swap targets, the
   selected wash, and the zebra. THAT MOVES THE STRIPE OFF `.table tbody tr:nth-child(even)`,
   which can never be right again here -- a collapsed group holds one <tr> and an open one
   holds two, so nth-child would stripe the open panel rather than alternate rows. Do not
   "restore" the old rule; it is not a tidy, it is the bug. D2 (R56): no zebra at all now -
   rows differ on hover and selection only. */
.table-rows tbody:hover>tr,.table-rows tbody.is-hover>tr{background:var(--caliche)}
.table-rows tbody.is-selected>tr{background:var(--red-tint)}

/* The disclosure fills its cell so the row stays 36px. A --ctl-tall button inside an
   8px-padded cell would make the row 48 and break section 6.4's density rule. */
.table td.cell-disclose{width:var(--s8);padding:0 0 0 var(--s3)}
.disclose{display:flex;align-items:center;gap:var(--s2);width:100%;height:var(--row);padding:0;background:none;border:0;color:var(--steel-70);cursor:pointer}
/* There is no chevron-down in static/icons/ and vendoring one means vendor_assets.sh,
   _vendor.json and test_vendored_assets.py together. chevron-left rotated a quarter turn is
   the same glyph pointing the right way: -90deg reads "v" (open me), +90deg reads "^". */
.disclose .icon{transform:rotate(-90deg);transition:transform var(--t)}
.disclose[aria-expanded="true"] .icon{transform:rotate(90deg)}

/* The expanded panel: one cell spanning the table, a caliche well, a rule above. The height
   is NOT animated -- there is no measured height without JavaScript and section 3.5 forbids
   JavaScript that computes. It opens instantly, which is also honest about the round trip. */
.row-open>.row-panel{padding:var(--s4);background:var(--caliche);border-top:1px solid var(--rule)}

/* A word beside a figure in a numeric cell: "Override", "No rate set". Barlow, not mono,
   because it is read rather than counted. */
.cell-note{margin-left:var(--s2);color:var(--steel-70);font-family:var(--font);font-size:var(--fs-13)}

/* A KPI tile that filters. .kpi is already a div (dashboard overview); this makes one
   pressable without inventing a second tile vocabulary. The on state is caliche, NOT red:
   the selected row already holds this view's one red and section 3.4 allows no second. */
button.kpi{display:block;width:100%;text-align:left;background:none;border:0;font-family:var(--font);cursor:pointer}
button.kpi:hover,button.kpi.is-hover,button.kpi.is-on{background:var(--caliche)}

@media (max-width:767px){
  /* Touch targets, matching .table td's --row-touch in the block above. */
  .disclose{height:var(--row-touch)}
  .row-open>.row-panel{padding:var(--s3)}
}

/* ---- Field tickets. Sprint 043+044, added with the seam so no lane edits this file. ----
   Reuses .rows/.row/.chip/.fields wherever they fit; only the shapes those cannot make are
   here. Borders carry the structure and hover changes colour only: no depth effects, which
   the stylesheet test enforces by scanning this file's TEXT -- naming one here trips it. */

/* The phone ticket's header block: job number, work date, status, one under the other. A
   .row cannot hold it -- .row is one fixed-height line and this is three stacked facts. */
.ticket-head{display:flex;flex-direction:column;gap:var(--s1);padding:var(--s3) var(--s4);border-bottom:1px solid var(--rule)}
.ticket-head .meta{color:var(--steel-70);font-size:var(--fs-13)}

/* Labour, equipment and materials are three groups on one ticket. The heading is the house
   label: small, medium, uppercase, widely tracked, in the faint ink. */
.line-group{padding:var(--s3) var(--s4) var(--s1);color:var(--steel-70);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.08em;text-transform:uppercase}

/* One line on a phone: a main line and a quieter one under it, which is why this is not a
   .row. The remove control sits at the end of the main line, in the thumb zone. */
.line-row{display:flex;align-items:flex-start;gap:var(--s2);padding:var(--s2) var(--s4);border-bottom:1px solid var(--rule)}
.line-body{display:flex;flex-direction:column;gap:var(--s1);min-width:0;flex:1 1 auto}
.line-main{color:var(--ink);font-size:var(--fs-15)}
.line-meta{color:var(--steel-70);font-size:var(--fs-13)}
.line-drop{flex:0 0 auto;min-width:var(--ctl);min-height:var(--ctl);background:none;border:0;color:var(--steel-70);font-family:var(--font);font-size:var(--fs-13);cursor:pointer}
.line-drop:hover,.line-drop.is-hover{color:var(--red)}

/* 055: what is left on the job, under the ticket's own lines. The strip is the recessed
   surface, as a card header is; the one-tap control keeps its width in the line row. */
.remaining{border-top:1px solid var(--rule)}
.remaining-head{margin:0;padding:var(--s3) var(--s4);background:var(--caliche);border-bottom:1px solid var(--rule);color:var(--ink);font-size:var(--fs-14);font-weight:var(--w-medium)}
.line-add{flex:0 0 auto}

/* The office queue's two decisions, side by side on one row. */
.queue-act{display:flex;align-items:center;gap:var(--s2);margin-left:auto}

/* R50's opt-in overtime span on the labor sub-form (sprint 054). The disclosure is the table
   one, sized to its words rather than its cell; the fieldset is only a disabling boundary,
   so the browser's frame is taken off and the fields inside read like their siblings. */
.ot-span{margin-bottom:var(--s3)}
.disclose.disclose-inline{width:auto;height:var(--ctl);font-family:var(--font);font-size:var(--fs-14)}
.disclose-inline:hover,.disclose-inline.is-hover{color:var(--ink)}
.ot-fields{min-width:0;margin:var(--s2) 0 0;padding:0;border:0}
.ot-fields .help{margin:0 0 var(--s3)}
@media (max-width:767px){.disclose.disclose-inline{height:var(--ctl-touch);font-size:var(--fs-15)}}

/* ---- Inventory, sprint 022. Seven class tokens, one new custom property (--fld-num),
   no hex, borders not shadows. The expanded row, the pinned row, and the switch. ---- */

/* THE PANEL USES THE ROW IT IS IN. It is one cell spanning the whole table, and .form and
   .fields both cap at --form, so inside a 1366px row more than half of it was blank while
   five fields stacked ~380px tall. The cap is lifted HERE ONLY -- every other form on the
   site still wants it, because a 1366px text input is not a text input anyone can read.
   One line: the fields, the derived figure, then the actions, reading left to right. Flex
   wrap is what makes it fold rather than crush - the actions drop to a second line before
   anything is squeezed, and the phone block at the bottom stacks every field. */
.row-panel .form{max-width:none;margin-top:0}
.panel-line{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--s3) var(--s4)}
.panel-fields{flex:1 1 var(--form);display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--s3) var(--s4);min-width:0}
.panel-fields>.field{flex:0 1 var(--fld-num);margin:0;min-width:0;max-width:none}
/* The name takes the free space and the money boxes keep --fld-num. Its BASIS is
   --fld-num too, not --pane-list: a flex line wraps before it shrinks, so a wide
   basis put Active alone on a second line at 1100 rather than narrowing the name. */
.panel-fields>.field-name{flex:5 1 var(--fld-num)}
.panel-fields>.field-flag{flex:0 0 auto}
.panel-derived{flex:0 0 auto;min-width:0}
.panel-derived .field{margin:0;max-width:none}
.panel-line>.actions{flex:0 0 auto;margin-top:0;margin-left:auto}
/* A panel is a FORM that happens to sit in a table cell, so its labels are Barlow. .table
   sets the mono face on the whole element and a label inherited it, which read as a figure
   and not as a name. The figures themselves - .fact, the cells, the .input-mono boxes -
   keep the mono they are counted in. */
.row-panel .label,.row-panel .meta,.row-panel .toggle{font-family:var(--font)}

/* Three pinned controls where sprint 018 had two. All three sit on ONE line at desk width
   and wrap only when there is genuinely no room, rather than being crushed to a caret at
   390. Scoped to this head: the Jobs pinned row carries two selects and no term and is not
   this shape.

   THE TERM NEEDS A BASIS OF ITS OWN or the row wraps into three lines the moment wrapping
   is allowed at all. `.input` is `width:100%`, which a flex item reads as a 100% BASIS, so
   the term claimed a whole line and pushed the icon onto one of its own and both selects
   onto a third. `flex-basis` overrides that; `width:auto` stops the two disagreeing. */
.search-wrap{flex-wrap:wrap}
.search-wrap .search-input{flex:1 1 var(--pane-ctx);width:auto;min-width:0}
.search-wrap .select{flex:1 1 var(--fld-num);width:auto;min-width:0;max-width:var(--pane-list)}

/* Active is a toggle, and in this house a toggle is SQUARE: --r-press is the only radius
   there is, so the track and the knob are 4px boxes and the state reads from the fill and
   from the side the knob sits on. It is CSS over a REAL checkbox, never a div and a script:
   an unticked checkbox sends nothing at all and the route reads that silence as "retire
   it". The input keeps the name, the value, the keyboard and the focus ring; it is
   transparent and lies over its own track, so a pointer lands on the input either way.
   The knob's journey is the track's inside less the knob: --s8 less two 1px borders and
   two 1px pads is 28, the knob is --s3, so it travels --s4. Colour is the whole of the
   hover; the reduced-motion block above turns the slide off. */
.toggle{position:relative;display:inline-flex;align-items:center;gap:var(--s2);min-height:var(--ctl);font-size:var(--fs-14);cursor:pointer}
.toggle-box{position:absolute;left:0;top:0;bottom:0;width:var(--s8);margin:0;opacity:0;cursor:pointer}
.toggle-track{flex:0 0 auto;display:flex;align-items:center;width:var(--s8);height:var(--s4);padding:1px;background:var(--paper);border:1px solid var(--steel-40);border-radius:var(--r-press);transition:background var(--t),border-color var(--t)}
.toggle-knob{width:var(--s3);height:var(--s3);background:var(--steel-40);border-radius:var(--r-press);transition:transform var(--t),background var(--t)}
.toggle-box:checked+.toggle-track{background:var(--red);border-color:var(--red)}
.toggle-box:checked+.toggle-track .toggle-knob{background:var(--paper);transform:translateX(var(--s4))}
.toggle:hover .toggle-track,.toggle.is-hover .toggle-track{border-color:var(--ink)}
.toggle-box:focus-visible+.toggle-track{outline:var(--edge) solid var(--red);outline-offset:var(--edge)}
.toggle-box:disabled+.toggle-track{background:var(--caliche);border-color:var(--rule);cursor:default}
.toggle-text{white-space:nowrap}

@media (max-width:767px){
  /* One task per screen, one field per line, and a 44px switch under a thumb. */
  .panel-fields>.field,.panel-fields>.field-name,.panel-fields>.field-flag,
  .panel-derived{flex:1 1 100%}
  .panel-line>.actions{margin-left:0}
  .toggle{min-height:var(--ctl-touch);font-size:var(--fs-15)}
}
/* Sprint ui1. The sub-header's two page slots (_shell/_subnav.html): counters centred on
   the band's own midline, a compact status action at the right beside the crumb. Then the
   Jobs list's one-line head, rows that are links, and the job screen's two-column detail. */
.subnav-center{position:absolute;top:0;bottom:0;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:var(--s1)}
.subnav-count{display:inline-flex;align-items:center;gap:var(--s2);height:var(--s6);padding:0 var(--s2);background:none;border:0;border-radius:var(--r-press);color:var(--ink);font-family:var(--font);font-size:var(--fs-14);font-weight:var(--w-semi);white-space:nowrap;cursor:pointer}
.subnav-count:hover,.subnav-count.is-on{background:var(--caliche)}
.subnav-count .mono{font-weight:var(--w-semi)}
.subnav-actions{display:flex;align-items:center;gap:var(--s2);padding-left:var(--s3)}
/* A crumb with a page action beside it holds the right edge, with the action, even with no tabs. */
.subnav-crumb:first-child:has(+ .subnav-actions){margin-left:auto}
.btn-sub{height:var(--s6);padding:0 var(--s2);font-size:var(--fs-13);white-space:nowrap}
.pane-head-row{flex-wrap:nowrap}
.filters{display:flex;align-items:center;gap:var(--s2);min-width:0}
.filters .select{width:auto;min-width:0}
.row-link{color:inherit;text-decoration:none}
.table-rows tr.is-link{cursor:pointer}
.detail-cols{display:grid;grid-template-columns:minmax(0,1fr);gap:0 var(--s8)}
@media (min-width:1024px){.detail-cols{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:767px){
  .subnav-center{position:static;transform:none}
  .pane-head-row,.filters{flex-wrap:wrap}
  .btn-sub{height:var(--ctl-touch)}
}
/* Sprint ui2. The row editor's price: a read-only box level with the cost and markup inputs,
   caliche because it is not typed into. */
.input-derived{display:flex;align-items:center;gap:var(--s2);min-width:var(--fld-num);background:var(--caliche);white-space:nowrap}
/* A numeric column's header sits over its figures. `.table th` outranks `.cell-num`, so every
   right-aligned column's header was left-aligned above right-aligned numbers (ui2). */
.table th.cell-num{text-align:right}
/* The row editor's locked name is read, not counted: Barlow, though the row is a table cell. */
.row-panel .field-name .fact{font-family:var(--font)}
/* Sprint ui3: the one-page field ticket. A multi-line notes box, the repeating line rows of
   the labor, equipment and material sections, and the material search results. */
.textarea{display:block;width:100%;min-height:calc(var(--s16) * 2);padding:var(--s2);background:var(--paper);color:var(--ink);border:1px solid var(--rule);border-radius:var(--r-press);font-family:var(--font);font-size:var(--fs-15);line-height:var(--lh-prose);resize:vertical}
.line-rows{display:flex;flex-direction:column;gap:var(--s3);margin-bottom:var(--s3)}
.line-row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--s2) var(--s3);padding:0 0 var(--s3);border-bottom:1px solid var(--rule)}
.line-row>.field{flex:0 1 var(--fld-num);margin:0;min-width:0;max-width:none}
.line-row>.field-wide{flex:2 1 var(--pane-list)}
.line-row>.field-check{flex:0 0 auto;align-self:center}
.line-row>.actions{flex:0 0 auto;margin:0 0 0 auto}
.line-figure,.line-hours{min-height:var(--ctl);display:flex;align-items:center;font-family:var(--mono);font-size:var(--fs-13);white-space:nowrap}
/* ui9: a floating list over the rows below it, never pushing them down; its own scroll. */
.search-results{position:absolute;top:100%;left:0;right:0;z-index:5;max-height:calc(var(--row) * 8);overflow-y:auto;margin:var(--s1) 0 0;padding:0;list-style:none;border:1px solid var(--rule);border-radius:var(--r-press);background:var(--paper);box-shadow:var(--shadow)}
.search-results:empty{display:none}
.search-result{display:flex;justify-content:space-between;width:100%;gap:var(--s3);padding:var(--s2) var(--s3);background:none;border:0;text-align:left;font-family:var(--font);font-size:var(--fs-14);color:var(--ink);cursor:pointer}
.search-result:hover,.search-result:focus-visible,.search-result.is-active{background:var(--caliche)}
.search-unit{color:var(--steel-70);font-size:var(--fs-13)}
.search-sku{flex:none;font-family:var(--mono);font-size:var(--fs-13);color:var(--steel-70)}
.search-name{flex:1}
@media (max-width:767px){.line-row>.field,.line-row>.field-wide{flex:1 1 100%}}
/* Sprint ui4 (owner): the ticket page's Save is a full-size primary with room around it, the
   sections read as bold headings over a heavier rule, and Match first has nothing to match on
   the first labor row entered - the bottom one, as Add labor puts new rows on top (V2.7). */
.btn-save{height:var(--ctl);padding:0 var(--s6);font-size:var(--fs-14);white-space:nowrap}
.subnav-actions:has(.btn-save){padding-block:var(--s2)}
/* ui11 (owner): Complete job draws the eye - red and a step larger than a sub-header button. */
.subnav-actions:has(.btn-complete){padding-block:var(--s2)}
.btn-complete{height:var(--ctl-wide);padding:0 var(--s6);font-size:var(--fs-15)}
.ticket-form>.section{border-bottom:var(--edge) solid var(--rule)}
.ticket-form .section-head{font-family:var(--cond);font-size:var(--fs-17);font-weight:var(--w-semi)}
.line-row:last-child .btn-match{display:none}
/* Sprint ui5. The customer's notes log: each entry its author and time over the note as
   written, line breaks kept. And the sub-header slot that sits right after the tabs. */
.note-entry{padding:var(--s3) 0;border-top:1px solid var(--rule)}
.note-entry .meta{margin:0 0 var(--s1)}
.note-body{margin:0;max-width:var(--prose);font-size:var(--fs-15);line-height:var(--lh-prose);white-space:pre-line;overflow-wrap:anywhere}
.subnav-tail{display:flex;align-items:center;flex:0 0 auto;margin-left:var(--s2)}
/* Sprint ui6. Labor rows: member and role narrowed to a picker's width so the clocks and the
   row's hours read on one line; the hours figure and the section's labor total. */
.line-row>.field-pick{flex:1 1 calc(var(--fld-num) * 1.5);max-width:calc(var(--fld-num) * 2)}
/* ui9: the labor row's figures and controls on one strip under its inputs; three labelled
   cells whether or not there are figures, so every row is the same width. */
.line-foot{flex:1 0 100%;display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s6)}
.line-foot>.actions{margin:0 0 0 auto}
.line-hours{gap:var(--s6)}
.hours-cell{display:flex;flex-direction:column;gap:var(--s1);min-width:var(--summary)}
.hours-label{font-family:var(--font);font-size:var(--fs-11);font-weight:var(--w-medium);color:var(--steel-70);letter-spacing:.06em;text-transform:uppercase}
.hours-figure{font-family:var(--mono);font-size:var(--fs-14)}
.hours-cell.is-sum .hours-figure{font-weight:var(--w-semi)}
.labor-total{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s6);margin:0 0 var(--s3);padding:var(--s3) var(--s4);background:var(--caliche);border-radius:var(--r-press)}
.labor-total-label{font-size:var(--fs-14);font-weight:var(--w-medium)}
.labor-total-label{min-width:var(--summary)}
/* ui9: Add sits top right of each ticket section, beside its heading. */
.section-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);margin:0 0 var(--s3)}
.section-bar>.section-head,.ticket-form .section-bar>.section-head{margin:0}
/* ui9: material is a compact table - one grid row per item, column heads once. */
.mat-head,.line-row.mat-row{display:grid;grid-template-columns:minmax(0,1fr) var(--summary) var(--fld-num) var(--kpi);gap:var(--s3);align-items:center}
.mat-head.is-priced,.line-row.mat-row.is-priced{grid-template-columns:minmax(0,1fr) var(--summary) var(--fld-num) calc(var(--fld-num) * 1.5) var(--kpi)}
.mat-head{padding:0 0 var(--s1);border-bottom:1px solid var(--rule);color:var(--steel-70);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.06em;text-transform:uppercase}
.mat-rows{gap:0}
.line-row.mat-row{padding:var(--s2) 0}
.mat-item{position:relative;min-width:0}
.mat-unit{color:var(--steel-70);font-size:var(--fs-14)}
.mat-qty,.mat-price{min-width:0}
.mat-act{display:flex;justify-content:flex-end}
.mat-price .line-figure{min-height:0}
@media (max-width:767px){.mat-head{display:none}.line-row.mat-row,.line-row.mat-row.is-priced{grid-template-columns:minmax(0,1fr) var(--fld-num) auto}.mat-item,.mat-price{grid-column:1/-1}}
@media (max-width:767px){.line-row>.field-pick{flex:1 1 100%;max-width:none}}
/* Sprint 058. The invoice screen, in the owner's layout: a compact header of facts, one
   section per field ticket, the summary and the totals. Rules only, no zebra; mono counts. */
.inv-body{padding:var(--s4) var(--s4) var(--s8)}
.inv{display:flex;flex-direction:column;gap:var(--s6)}
.inv-head{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s3) var(--s6)}
.inv-ids{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s1) var(--s4)}
.inv-id{margin:0;font-family:var(--mono);font-size:var(--fs-22);font-weight:var(--w-medium);line-height:var(--lh-title)}
.inv-ref{margin:0;color:var(--steel-70);font-size:var(--fs-14)}
.inv-link{font-family:var(--mono);font-size:var(--fs-13);color:var(--ink)}
/* ui15: the facts are one box in three columns (bill to, job, invoice) that stack on a
   phone; customer notes and codes run full width under them. */
.inv-facts{display:flex;flex-direction:column;gap:var(--s4)}
.inv-cols{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s4) var(--s6)}
.inv-col{min-width:0}
.inv-col-head{margin:0 0 var(--s2);padding-bottom:var(--s1);border-bottom:1px solid var(--rule);font-family:var(--font);font-size:var(--fs-13);font-weight:var(--w-medium);letter-spacing:.06em;text-transform:uppercase}
.inv-list{display:grid;gap:var(--s2);margin:0}
.inv-notes{padding-top:var(--s3);border-top:1px solid var(--rule)}
.inv-fact{min-width:0}
@media (min-width:768px){.inv-cols{grid-template-columns:repeat(3,minmax(0,1fr))}}
.inv-key{color:var(--steel-70);font-size:var(--fs-13)}
.inv-val{margin:0;font-size:var(--fs-15);overflow-wrap:anywhere}
.inv-line{display:block;color:var(--steel-70);font-size:var(--fs-14)}
.inv-num{font-family:var(--mono);font-size:var(--fs-13)}
.inv-text{max-width:var(--prose);white-space:pre-line}
.inv-section{display:flex;flex-direction:column;gap:var(--s3);padding-top:var(--s4);border-top:1px solid var(--rule)}
.inv-keep{display:flex;flex-direction:column;gap:var(--s3)}
.inv-ticket{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s4);font-family:var(--font);font-size:var(--fs-17);font-weight:var(--w-medium)}
.inv-date{color:var(--steel-70)}
.inv-note{margin:0;max-width:var(--prose);font-size:var(--fs-15);white-space:pre-line;overflow-wrap:anywhere}
.inv-table{width:100%;table-layout:fixed;border-collapse:collapse;font-family:var(--mono);font-size:var(--fs-13)}
.inv-kind{margin:0;padding:0 0 var(--s1);text-align:left;font-family:var(--font);font-size:var(--fs-14);font-weight:var(--w-medium)}
.inv-table th{padding:var(--s1) var(--s3);border-bottom:1px solid var(--ink);text-align:left;font-family:var(--font);font-size:var(--fs-13);font-weight:var(--w-medium);color:var(--steel-70);white-space:nowrap}
.inv-table th.cell-num{text-align:right}
.inv-table td{padding:var(--s1) var(--s3);border-bottom:1px solid var(--rule);overflow-wrap:anywhere}
.inv-table th:first-child,.inv-table td:first-child{padding-left:0}.inv-table th:last-child,.inv-table td:last-child{padding-right:0}
.inv-table th:nth-child(-n+2){width:30%}
.inv-table .inv-name{font-family:var(--font);font-size:var(--fs-14);white-space:normal}
.inv-table tr[data-row="labor-overtime"] .inv-name:first-child{padding-left:var(--s4);color:var(--steel-70)}
.inv-table tr[data-lone] .inv-name:first-child{padding-left:0;color:inherit}
.inv-ot{display:block;padding-left:var(--s4);color:var(--steel-70)}
.inv-totals{display:grid;margin:0 0 0 auto;min-width:calc(var(--fld-num) * 2.5)}
.inv-total{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s6);padding:var(--s1) 0;font-size:var(--fs-14)}
.inv-total dd{margin:0}
.inv-end{display:flex;flex-direction:column;gap:var(--s2);padding-top:var(--s4);border-top:1px solid var(--ink)}
.inv-total-rule{border-top:1px solid var(--rule);margin-top:var(--s1);padding-top:var(--s2)}
.inv-grand{border-top-color:var(--ink);font-weight:var(--w-medium)}
.inv-grand .inv-num{font-size:var(--fs-15);font-weight:var(--w-medium)}
.inv-actions{flex-wrap:wrap}
/* D3 (R56): the screen invoice wears the PDF's frame - letterhead strip over a red rule,
   facts on the warm surface, a dark band per ticket day, the total due on a red row. */
.inv-brand{display:flex;align-items:center;gap:var(--s3);padding-bottom:var(--s3);border-bottom:var(--edge) solid var(--red)}
.inv-lockup{display:flex;align-items:stretch;gap:0}  /* ui16: the boxes touch, squared where they meet */
.inv-mark{display:inline-flex;align-items:center;justify-content:center;width:var(--mark-w);height:var(--mark-h);background:var(--red);color:var(--paper);border-radius:var(--r-press) 0 0 var(--r-press);font-family:var(--cond);font-size:var(--fs-22);font-weight:var(--w-semi)}
.inv-word{display:inline-flex;align-items:center;height:var(--mark-h);padding:0 var(--s3);background:var(--ink);color:var(--paper);border-radius:0 var(--r-press) var(--r-press) 0;font-family:var(--cond);font-size:var(--fs-17);font-weight:var(--w-semi);letter-spacing:.08em;text-transform:uppercase;white-space:nowrap}  /* ui15: white on a black box */
.inv-doc{margin-left:auto;color:var(--red);font-family:var(--cond);font-size:var(--fs-22);font-weight:var(--w-semi);letter-spacing:.06em;text-transform:uppercase}
.inv-facts{padding:var(--s4);background:var(--caliche);border-radius:var(--r-press)}
.inv-key{font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.06em;text-transform:uppercase}
.inv-section{padding-top:0;border-top:0}
.inv-ticket{padding:var(--s2) var(--s3);background:var(--hdr-ink);color:var(--paper);border-radius:var(--r-press) var(--r-press) 0 0;font-size:var(--fs-15)}
.inv-ticket .inv-date{color:var(--nav-ink)}
.inv-grand{margin-top:var(--s2);padding:var(--s2) var(--s3);background:var(--red);color:var(--paper);border-top:0;border-radius:var(--r-press)}
/* v1.9 (owner, 2026-10-12): the header as the PDF prints it. The number sits under the red
   document word; the box's columns take the PDF's widths and head in small red caps; a fact is
   its label beside its value (`.inv-bare` has no label); the job description, long codes and
   customer notes follow the box, before the first ticket. */
.inv-ids{display:flex;flex-direction:column;align-items:flex-end;gap:var(--s1);margin-left:auto}
.inv-ids .inv-doc{margin-left:0;line-height:1}
.inv-ids .inv-id{font-size:var(--fs-14);font-weight:var(--w-normal)}
.inv-col-head{margin:0 0 var(--s2);padding:0;border:0;color:var(--red);font-size:var(--fs-11)}
.inv-head .inv-list{grid-template-columns:max-content minmax(0,1fr);gap:var(--s1) var(--s3);align-items:baseline}
.inv-head .inv-fact{display:contents}
.inv-bare .inv-val{grid-column:1/-1}
.inv-fact .inv-val{font-size:var(--fs-13);overflow-wrap:break-word}
.inv-head .inv-cols{gap:var(--s4)}
.inv-below{display:grid;gap:var(--s1)}
.inv-below .inv-key{margin:0}
@media (min-width:768px){.inv-cols{grid-template-columns:var(--inv-cols)}}
@media (max-width:767px){.inv-body{padding:var(--s3) var(--s3) var(--s6)}.inv-table th:nth-child(-n+2){width:auto}.inv-table td,.inv-table th{padding-inline:var(--s1)}}
/* Print: the invoice alone, black on white, Letter. The shell, nav, flashes and every button
   go; the fixed-viewport grid unwinds so the document flows across pages; column heads
   repeat; a section's heading, note and first table stay together, and so do the totals. */
@page{size:letter;margin:var(--page-margin)}
@media print{
  .hdr,.subnav,.tabbar,.phone-action,.skip-link,.flash-region,.error-region,.inv-actions,.inv-lifecycle,.inv-log,.inv-paper>.section-head,.btn,.app-foot{display:none}
  .panes,.pane{padding:0;border:0;border-radius:0;box-shadow:none}
  html,body,.app,.panes,.pane,.pane-body{display:block;height:auto;overflow:visible;background:var(--paper);color:var(--ink)}
  .inv-body{padding:0}
  .inv,.inv *{color:var(--ink);background:var(--paper)}
  .inv-link{text-decoration:none}
  .inv-table thead{display:table-header-group}
  .inv-table tr{break-inside:avoid}
  .inv-keep,.inv-end{break-inside:avoid}
  .inv-ticket,.inv-kind,.inv-table thead{break-after:avoid}
  .inv-cols{grid-template-columns:var(--inv-cols)}
  /* D3: the letterhead mark, the day bands and the red total print in colour, as the PDF. */
  .inv .inv-mark,.inv .inv-word,.inv .inv-ticket,.inv .inv-grand{-webkit-print-color-adjust:exact;print-color-adjust:exact;color:var(--paper)}
  .inv .inv-mark,.inv .inv-grand,.inv .inv-grand *{background:var(--red);color:var(--paper)}
  .inv .inv-ticket,.inv .inv-ticket *{background:var(--hdr-ink);color:var(--paper)}
  .inv .inv-word{background:var(--ink);color:var(--paper)}
  .inv .inv-doc{color:var(--red);background:transparent}
  .inv .inv-facts,.inv .inv-facts *{background:var(--caliche);-webkit-print-color-adjust:exact;print-color-adjust:exact}
}
/* C2: a flagged invoice on the board draws an amber left edge and names what needs fixing. */
.table tr.row-flagged>td:first-child{border-left:var(--edge) solid var(--attn)}
.flag-note{color:var(--ink);font-weight:var(--w-medium)}
.row-approve{margin:0}
/* C3: the Pending invoice editor frame. The lines lane appends its own rules below this. */
.editor{display:grid;gap:var(--s6)}
.editor-saved{margin:0;padding:var(--s2) var(--s3);border-left:var(--edge) solid var(--ok);font-size:var(--fs-14)}
.editor-totals{margin:0}
/* C3, lane lines: a section's line tables, the void row, the deleted list and the log. */
.edit-section{display:grid;gap:var(--s3)}
.edit-lines{margin:0;overflow-x:auto}
.editor>*,.edit-section>*{min-width:0}
.edit-wide{overflow-x:auto}
.edit-table{margin:0 0 var(--s4)}
.edit-kind{padding:0 0 var(--s2);text-align:left;font-family:var(--font);font-size:var(--fs-11);font-weight:var(--w-medium);text-transform:uppercase;color:var(--steel-70)}
.edit-num{width:var(--fld-num);margin-left:auto;text-align:right}
.edit-hint{display:block;color:var(--steel-70);font-family:var(--font);font-size:var(--fs-13)}
.edit-row-actions{justify-content:flex-end}
.edit-void>td:not(:last-child){color:var(--steel-40);text-decoration:line-through}
.edit-log-more>td{text-align:center}
/* C4: a back move's reason, under its step in the invoice history. */
.event-note{display:block}
/* C5 payments: the invoice's payments region. Figures run label-left, value-right; a
   canceled payment stays in the table, muted and struck, with who, when and why. */
.inv-payments{min-width:0}
.pay-figures{display:grid;grid-template-columns:auto minmax(0,max-content);gap:var(--s1) var(--s4);align-items:baseline;margin:0 0 var(--s4)}
.pay-figures>dt,.pay-figures>dd{margin:0}
.pay-wide{margin:0 0 var(--s4);overflow-x:auto}
.table tr.pay-canceled>td{color:var(--steel-40)}
.table tr.pay-canceled>td.pay-struck{text-decoration:line-through}
.pay-struck{white-space:nowrap}
.pay-why{display:block;margin:0}
.pay-cancel{margin:0 0 var(--s3)}
.pay-cancel>summary{cursor:pointer}
.pay-cancel>summary:hover{color:var(--red)}
@media print{.inv-payments{display:none}}
/* P1 purchase orders: the PO console, its lines by stage and the new-PO sheet. The sheet is
   the .sheet dialog widened for the material rows; the rows are the ticket's .mat-* cells on
   a grid of their own (item, unit, quantity, price ordered, remove). */
.po-number{font-family:var(--mono)}
.po-facts{display:grid;grid-template-columns:repeat(auto-fill,minmax(calc(var(--fld-num) * 1.5),1fr));gap:var(--s3) var(--s4);margin:0 0 var(--s6)}
.po-facts dd{margin:0}.po-facts .label{margin-bottom:0}
.po-facts-wide{grid-column:1/-1}.po-notes{white-space:pre-line}
.po-wide{margin:0 0 var(--s3);overflow-x:auto}
.po-lines .po-figure{width:var(--fld-num);margin-left:auto;text-align:right}
.po-lines tfoot td{padding:var(--s2) var(--s3);border-top:1px solid var(--rule);font-family:var(--font);font-weight:var(--w-medium)}
.po-lines tfoot td.cell-num{font-family:var(--mono)}
.sheet.po-sheet{width:var(--form)}
.po-head,.line-row.po-row{display:grid;grid-template-columns:minmax(0,1fr) var(--kpi) var(--fld-num) var(--fld-num) var(--kpi);gap:var(--s3);align-items:center}
.po-head{padding:0 0 var(--s1);border-bottom:1px solid var(--rule);color:var(--steel-70);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.06em;text-transform:uppercase}
.line-row.po-row{padding:var(--s2) 0}
/* V2.7: "Edit after close" adds Received and Actual price to every row, so the sheet widens. */
.sheet.po-sheet.is-after-close{width:calc(var(--form) + var(--pane-ctx))}
.po-sheet.is-after-close .po-head,.po-sheet.is-after-close .line-row.po-row{grid-template-columns:minmax(0,1fr) var(--kpi) var(--fld-num) var(--fld-num) var(--fld-num) var(--fld-num) var(--kpi)}
.po-log-head{margin-top:var(--s4)}
.po-log{display:grid;gap:var(--s2);margin:0;padding:0;list-style:none}.po-log li{display:grid;gap:var(--s1)}
@media (max-width:767px){.po-sheet.is-after-close .line-row.po-row{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .po-sheet.is-after-close .po-row .mat-act{grid-column:1/-1}
  /* the column heads are hidden at this width, so each figure shows its own label */
  .po-sheet.is-after-close .po-row .mat-qty .vh,.po-sheet.is-after-close .po-row .mat-price .vh{position:static;width:auto;height:auto;overflow:visible;clip-path:none;display:block;margin-bottom:var(--s1);color:var(--steel-70);font-size:var(--fs-11)}}
.po-total-phone{display:none}
.po-correct>summary{cursor:pointer;color:var(--steel-70);font-size:var(--fs-14)}
.po-correct>summary:hover{color:var(--ink)}.po-correct[open]>summary{margin-bottom:var(--s3)}
/* P2: who canceled a PO, when and why, above its read-only lines. */
.po-canceled{max-width:var(--prose);margin:0 0 var(--s6);white-space:pre-line}
/* P3: a PO's job links to the job screen in ink, as .link-mono does. */
.po-job{color:var(--ink);text-decoration:underline;transition:color var(--t)}.po-job:hover{color:var(--red)}
@media (max-width:767px){.po-head,.po-new{display:none}.po-total-phone{display:inline}.line-row.po-row{grid-template-columns:minmax(0,1fr) minmax(0,1fr) auto}.po-row .mat-item{grid-column:1/-1}.po-row .mat-price{grid-column:auto}.po-row .mat-unit{display:none}.po-lines th,.po-lines td{padding-left:var(--s1);padding-right:var(--s1)}.po-lines .po-figure{width:var(--kpi)}.po-facts{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* ui15: the billing board's status tiles sit on the title row, one line each, the count
   right of the word. The chosen tile is caliche with an ink underline, as .tabs-item marks
   its own; the row wraps rather than push the page sideways on a phone. */
.board-head{flex-wrap:wrap;gap:var(--s2) var(--s4)}
.board-tiles{flex:1 1 0;display:flex;flex-wrap:wrap;align-items:center;gap:var(--s1);min-width:0}
.board-tile{display:inline-flex;align-items:center;gap:var(--s2);height:var(--ctl);padding:0 var(--s3);border-bottom:var(--edge) solid transparent;border-radius:var(--r-press) var(--r-press) 0 0;color:var(--ink);font-size:var(--fs-14);font-weight:var(--w-medium);text-decoration:none;white-space:nowrap;transition:background-color var(--t)}
.board-tile:hover,.board-tile.is-active{background:var(--caliche)}.board-tile.is-active{border-bottom-color:var(--ink)}
.board-tile-count{font-family:var(--mono);font-size:var(--fs-13)}
.board-tile-attn{border-left:var(--edge) solid var(--attn)}
@media (max-width:767px){.board-tiles{flex-basis:100%}.board-tile{height:var(--ctl-touch)}}
/* ui13: the invoice preview window, the .sheet dialog widened to the form cap for the document. */
.cell-id{white-space:nowrap}  /* ui13: INV-2051 never breaks at its hyphen */
.job-window.inv-window{max-width:calc(var(--form) + var(--pane-ctx))}  /* V2.8: the preview, centred */
.preview-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);margin:0 0 var(--s6)}
.preview-status{margin:0 auto 0 0;color:var(--steel-70);font-size:var(--fs-14);font-weight:var(--w-medium)}
/* ui14: the board's inline flag form, a row under a Pending invoice. Prose font, no hover wash,
   a short note box. */
.table tbody tr.flag-row:hover{background:none}.flag-row>td{height:auto;font-family:var(--font)}
.textarea.flag-text{min-height:var(--s16)}
/* ui16: the invoice screen's regions (status, payments, log, the document) are cards: a 1px
   rule frame, a caliche header strip in the house label, one inset, a gap between cards. */
.inv-body>.section{margin:0 0 var(--s6);padding:0 var(--s4) var(--s4);border:1px solid var(--rule);border-radius:var(--r-press)}
.inv-body>.section:last-child{margin-bottom:0}
.inv-body>.section>.section-head,.inv-body>.section>.section-bar{margin:0 calc(-1 * var(--s4)) var(--s4);padding:var(--s2) var(--s4);background:var(--caliche);border-bottom:1px solid var(--rule);border-radius:var(--r-press) var(--r-press) 0 0}
.inv-body>.section>.section-head,.inv-body>.section>.section-bar>.section-head{color:var(--steel-70);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.06em;text-transform:uppercase}
.inv-log{min-width:0;overflow-x:auto}  /* a wide log table scrolls inside its card */
.inv-moves{flex-wrap:wrap;align-items:center;margin-top:var(--s4)}
.inv-move{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s3);margin:0}
.inv-move-noted{flex:1 1 100%}
.inv-sent-note{flex:1 1 100%}
.inv-reveal{max-width:var(--form);margin:var(--s3) 0 0}
.inv-reveal>.select{margin-bottom:var(--s3)}
.inv-reveal>.actions{flex-wrap:wrap}
@media (max-width:767px){.inv-body>.section{padding:0 var(--s3) var(--s3)}.inv-body>.section>.section-head,.inv-body>.section>.section-bar{margin:0 calc(-1 * var(--s3)) var(--s3);padding:var(--s2) var(--s3)}}
@media print{.inv-body>.section{margin:0;padding:0;border:0}}
/* ui17: the editor's sales-tax switches. A row per scope; its one or two buttons sit inline. */
.tax-switches td{vertical-align:middle}.tax-switch-actions{text-align:right;white-space:nowrap}
.tax-switch{display:inline-flex;margin-left:var(--s2)}
/* ui18 jobs-form: the New job window - the .scrim plus a centred dialog at the form cap,
   1px rule, the overlay --shadow (floating layer); body scrolls, foot pinned at the bottom.
   Description and codes span the two-column grid; a phone gets the full screen. */
.job-window{position:fixed;top:50%;left:50%;z-index:21;display:flex;flex-direction:column;width:calc(100% - var(--s8));max-width:var(--form);max-height:calc(100% - var(--s12));background:var(--paper);border:1px solid var(--rule);border-radius:var(--r-card);box-shadow:var(--shadow);overflow:hidden;transform:translate(-50%,-50%)}
.job-window-form{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
.job-window .summary-meta{margin:0 0 var(--s4)}
.job-window-foot{display:flex;justify-content:flex-end;gap:var(--s2)}
/* 2026-10-19: the New location / New company man window over the New job window */
.scrim.quick-scrim{z-index:22}.job-window.quick-window{z-index:23}
.job-fields{grid-template-columns:repeat(2,minmax(0,1fr))}.job-wide{grid-column:1/-1}
@media (max-width:767px){
  .job-window{top:0;left:0;width:100%;max-width:none;height:100%;max-height:none;border:0;border-radius:var(--r-none);transform:none}
  .job-fields{grid-template-columns:minmax(0,1fr)}.job-window-foot .btn{flex:1 1 0}
  .job-window.inv-window{max-width:none}
}
/* ui18 job-detail: the field ticket summary. Scrolls sideways inside its own wrapper; the
   entry column sticks left; tbody row heads read as text, not as uppercase column labels. */
.sum-wide{margin:0 0 var(--s3);overflow-x:auto}
.sum-table td{white-space:nowrap;vertical-align:top}
.sum-table .sum-name{position:sticky;left:0;z-index:1;min-width:calc(var(--fld-num) * 1.5)}
.sum-table thead th.sum-name{z-index:2}
.sum-table tbody th{top:auto;padding:var(--s2) var(--s3);background:var(--paper);border-bottom:1px solid var(--rule-soft);font-family:var(--font);font-size:var(--fs-14);font-weight:var(--w-normal);letter-spacing:normal;text-transform:none;color:var(--ink);white-space:normal;vertical-align:top}
.sum-table tbody tr:hover>th.sum-name{background:var(--caliche)}
.sum-table .sum-group>th{position:sticky;left:0;background:var(--card-head);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.06em;text-transform:uppercase;color:var(--steel-70)}
.sum-block>tr:last-child>*{border-bottom:1px solid var(--rule)}
.sum-detail,.sum-ot{display:block;color:var(--steel-70);font-size:var(--fs-13)}
/* ui18 ticket-form: a refused save is a notice fixed under the Save button, outside the pane's
   scroll; on a phone it sits above the tab bar. Red refuses, amber warns. */
.save-alert{position:fixed;top:calc(var(--hdr) + var(--ctl) + var(--s3) + var(--s2));right:var(--s6);z-index:15;display:flex;align-items:flex-start;gap:var(--s3);width:var(--pane-ctx);max-width:calc(100% - var(--s6) * 2);padding:var(--s3) var(--s4);background:var(--red-tint);border:1px solid var(--crit);border-left:var(--s1) solid var(--crit);border-radius:var(--r-press);box-shadow:var(--shadow);font-size:var(--fs-15)}
.save-alert[data-flash="warning"]{background:var(--paper);border-color:var(--attn)}
.save-alert[data-flash="success"]{background:var(--paper);border-color:var(--ok)}
.save-alert[data-flash="info"]{background:var(--paper);border-color:var(--steel-70)}
.save-alert-text{flex:1 1 auto;min-width:0;margin:0}
.save-alert-head{font-weight:var(--w-medium)}
.save-alert>.btn{flex:0 0 auto}
@media (max-width:767px){.save-alert{top:auto;bottom:calc(var(--tabbar) + var(--s3));left:var(--s3);right:var(--s3);width:auto;max-width:none}}
/* ui19: the job detail block. The description leads; three labelled columns follow, each a
   stack of facts, divided by hairlines; one column below 1024. */
.job-desc{margin:0 0 var(--s4);font-size:var(--fs-17);max-width:var(--prose);overflow-wrap:anywhere}
.job-cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s6);padding-top:var(--s4);border-top:1px solid var(--rule)}
.job-col{display:flex;flex-direction:column;gap:var(--s3);min-width:0}
.job-col+.job-col{padding-left:var(--s6);border-left:1px solid var(--rule)}
.job-col-head{margin:0;color:var(--steel-70);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.08em;text-transform:uppercase}
@media (max-width:1023px){.job-cols{grid-template-columns:minmax(0,1fr)}.job-col+.job-col{padding-left:0;border-left:0;padding-top:var(--s4);border-top:1px solid var(--rule)}}
/* 066: the estimate detail on the job's layout. An open estimate's project description is a
   large box across the top at the job description's size; the decided note sits below. */
.est-desc{max-width:var(--prose)}
.est-desc .textarea{min-height:calc(var(--s16) * 2.5);font-size:var(--fs-17)}
.est-note{margin-top:var(--s4)}
/* v1.10: the job summary. v1.11 (owner): four bordered boxes left to right, Total last and
   set on the card-head surface; two by two below 768. */
.job-sum{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s3)}
.job-sum-box{display:flex;flex-direction:column;gap:var(--s2);padding:var(--s3) var(--s4);border:1px solid var(--rule);border-radius:var(--r-card);min-width:0}
.job-sum-box.is-total{background:var(--card-head)}
.job-sum-fig{margin:0;color:var(--ink);font-size:var(--fs-17);font-weight:var(--w-medium)}
@media (max-width:767px){.job-sum{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* ui19: the In development tag on a screen not built yet. A solid muted badge, inverse text. */
.dev-tag{display:inline-flex;align-items:center;height:var(--s6);padding:0 var(--s2);background:var(--attn);color:var(--paper);border-radius:var(--r-press);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.08em;text-transform:uppercase}
/* ui19: the version, white, in the footer's right corner. */
.app-foot-version{margin-left:auto;color:var(--paper);font-family:var(--mono)}
/* ui20 (owner): the invoice summary opens on a red bar, the ticket bars' shape in brand red. */
.inv-sum-bar{margin:0 0 var(--s3);padding:var(--s2) var(--s3);background:var(--red);color:var(--paper);border-radius:var(--r-press) var(--r-press) 0 0;font-size:var(--fs-15);font-weight:var(--w-medium)}
@media print{.inv .inv-sum-bar{-webkit-print-color-adjust:exact;print-color-adjust:exact;color:var(--paper)}}
/* ui21 estimates-dash: the Dashboard's purchase order card links each count to the POs screen;
   underlined ink, red on hover, the PO screen's own link. The estimate window is ui18's. */
.dash-link{color:var(--ink);text-decoration:underline;transition:color var(--t)}.dash-link:hover{color:var(--red)}
/* ui21 jobs-list: the bulk Complete. Its controls sit in the jobs head right of the field
   lead filter; the pick column is narrow; the confirm list is the job label in mono then
   the description, one per hairline row, inside the ui18 .job-window. */
.bulk-bar{display:flex;align-items:center;gap:var(--s2);flex:0 0 auto}
.bulk-count{color:var(--steel-70);font-size:var(--fs-13);white-space:nowrap}
.table .cell-pick{width:var(--s8);padding-right:0}
.pick{display:inline-flex;align-items:center;min-height:var(--ctl);cursor:pointer}
.bulk-list{margin:0;padding:0;list-style:none;border-top:1px solid var(--rule-soft)}
.bulk-item{display:flex;gap:var(--s3);padding:var(--s2) 0;border-bottom:1px solid var(--rule-soft)}
.bulk-label{flex:0 0 auto;font-family:var(--mono);font-size:var(--fs-13);font-weight:var(--w-medium)}
.bulk-desc{min-width:0;color:var(--ink);overflow-wrap:anywhere}
/* ui21 job-detail: Back to jobs is a visible labelled button at every width (the old
   .pane-back chevron showed only on narrow screens); the Edit details form stacks its fields. */
.job-back{flex:0 0 auto}
.job-edit{display:flex;flex-direction:column;gap:var(--s4)}
/* ui21 invoice-basis: the Pending editor's Bill from choice. The current basis carries a solid
   muted tag with inverse text; a basis that cannot bill says why in muted ink. */
.basis-choices td{vertical-align:middle}.basis-actions{text-align:right;white-space:nowrap}
.basis-switch{display:inline-flex;margin-left:var(--s2)}
.basis-current{display:inline-flex;align-items:center;height:var(--s6);margin-left:var(--s2);padding:0 var(--s2);background:var(--ok);color:var(--paper);border-radius:var(--r-press);font-family:var(--font);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.08em;text-transform:uppercase}
.basis-gap{color:var(--steel-70);font-family:var(--font);font-size:var(--fs-13)}
.edit-basis,.edit-pickup{display:grid;gap:var(--s3)}
/* v1.1 contacts panel: the customer's Locations and Company Men are lists, one hairline row
   each (name over its key detail), not cards. A row is a button that opens its edit beneath
   it; the open row and hover share caliche. Inactive rows stay listed, the text faint and a
   solid tag with inverse text saying so. The Add windows are ui18's .job-window; their host
   is display:contents so it never takes a cell of the .panes grid. */
.ctx-list{margin:0;padding:0;list-style:none;border-top:1px solid var(--rule)}
.ctx-item{border-bottom:1px solid var(--rule)}
.ctx-row{display:flex;align-items:center;gap:var(--s2);width:100%;min-height:var(--row-touch);padding:var(--s1) var(--s2);background:none;border:0;color:var(--ink);font-family:var(--font);text-align:left;cursor:pointer;transition:background-color var(--t)}
.ctx-row:hover,.ctx-item.is-open>.ctx-row{background:var(--caliche)}
.ctx-row-text{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;line-height:var(--lh-title)}
.ctx-row-name,.ctx-row-meta{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.ctx-row-name{font-size:var(--fs-15);font-weight:var(--w-medium)}
.ctx-row-meta{color:var(--steel-70);font-size:var(--fs-13)}
.ctx-item.is-inactive .ctx-row-name,.ctx-item.is-inactive .ctx-row-meta{color:var(--steel-40)}
.ctx-tag{flex:0 0 auto;color:var(--steel-70);font-size:var(--fs-13)}
.ctx-off{flex:0 0 auto;display:inline-flex;align-items:center;height:var(--s6);padding:0 var(--s2);background:var(--steel-70);color:var(--paper);border-radius:var(--r-press);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.08em;text-transform:uppercase}
.ctx-edit{padding:var(--s3) var(--s2) var(--s4);border-top:1px solid var(--rule-soft)}
.ctx-edit .field{margin-bottom:var(--s3)}.ctx-edit .actions{margin-top:var(--s3)}
.ctx-window-host{display:contents}
.ctx-add{display:none}
@media (max-width:767px){.panes-customers .ctx-add{display:block;margin-top:var(--s3);border-top:1px solid var(--rule)}.ctx-row-meta{font-size:var(--fs-14)}}
/* The window's message sits in the window body's own inset, not the pane-level one. On a
   phone the customers rule above hides .pane-foot in this console; the window's foot holds
   Create and Cancel, so it stays. */
.ctx-window-host .flash-region{padding:0 0 var(--s1)}
@media (max-width:767px){.panes-customers .job-window-foot{display:flex}}
/* v1.2 batch add (Add inventory items): one row per item. At 1280 and up a row is one line
   under a header that names the columns, and each input's own label is visually hidden;
   below that the header goes and each row stacks into a numbered block with its labels
   shown, two columns of fields. Remove sits at the row's end, or its block's top. Save and
   Add row stay pinned at the foot of the pane, so the action is never scrolled away. */
.batch-head{display:none}
.batch-rows{margin-bottom:var(--s3);counter-reset:batch-row}
.batch-row{padding:var(--s3) 0;border-bottom:1px solid var(--rule);counter-increment:batch-row}
.batch-row:first-child{padding-top:0}
.batch-row>.help-error{margin:var(--s2) 0 0}
.batch-row.is-refused>.help-error{font-weight:var(--w-medium)}
.batch-cells{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s3) var(--s4)}
.batch-cell{min-width:0}
.batch-cell-name{grid-column:1/-1}
.batch-cell-flag{display:flex;align-items:flex-end}
.batch-cell-act{grid-column:1/-1;grid-row:1;display:flex;align-items:center;justify-content:space-between}
.batch-cell-act::before{content:"Item " counter(batch-row);font-size:var(--fs-14);font-weight:var(--w-medium)}
.batch-label{display:block}
.batch>.actions{position:sticky;bottom:calc(-1 * var(--s4));z-index:1;margin-top:var(--s3);padding:var(--s3) 0 var(--s4);background:var(--paper);border-top:1px solid var(--rule)}
@media (min-width:1280px){
  .batch-head,.batch-cells{display:flex;align-items:center;gap:var(--s2)}
  .batch-head{padding-bottom:var(--s2);border-bottom:1px solid var(--rule);color:var(--steel-70);font-size:var(--fs-13);font-weight:var(--w-medium)}
  .batch-row,.batch-row:first-child{padding:var(--s2) 0}
  .batch-cell-name{flex:4 1 0}.batch-cell-pick{flex:3 1 0}.batch-cell-part{flex:2 1 0}
  .batch-cell-num{flex:0 0 var(--fld-num)}.batch-cell-flag{flex:0 0 var(--s16)}.batch-cell-act{flex:0 0 var(--ctl)}
  .batch-label{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
  .batch-cell-act::before{content:none}
}
@media (max-width:767px){
  .batch-cells{grid-auto-flow:row dense}.batch-cell-pick,.batch-cell-part{grid-column:1/-1}
  .batch>.actions{bottom:calc(-1 * var(--s3));padding-bottom:var(--s3)}
}
/* v1.3: the editor-wide Add line sits above the invoice fields; the Log prints each entry as a
   sentence with a Print log link on its heading row; the printable log carries the invoice's
   facts above the same table and prints with the shell hidden. */
.edit-add-top{display:grid;gap:var(--s3)}
.log-head{display:flex;align-items:center;justify-content:space-between;gap:var(--s2)}
.log-table .log-when{white-space:nowrap}
.log-table .log-what{color:var(--ink);font-family:var(--font);font-size:var(--fs-14);white-space:normal}
.log-table td[data-cell=log-by]{white-space:nowrap}
@media (max-width:767px){.log-table th:first-child,.log-table td.log-when,.log-table td[data-cell=log-by]{white-space:normal}}
/* v1.4 (owner): the executive's payroll page. The pay period picker comes first, a select and
   its button on one line; the Hours heading carries Download report at its right; a member's
   name is a plain button that opens their days beneath it; Check is a solid muted badge,
   Differs a shaded cell; wide tables scroll inside the section. */
.payroll-pick{max-width:var(--form)}
.payroll-pick-row{display:flex;gap:var(--s2);align-items:center}
.payroll-bar{flex-wrap:wrap}
.payroll-wide{margin:var(--s3) 0 0;overflow-x:auto}
.payroll-table tfoot td{padding:var(--s2) var(--s3);border-top:1px solid var(--rule);font-weight:var(--w-medium)}
.payroll-name{padding:0;background:none;border:0;color:var(--ink);font:inherit;text-decoration:underline;cursor:pointer;transition:color var(--t)}
.payroll-name:hover{color:var(--red)}
.payroll-window{margin:var(--s3) 0 0;overflow-x:auto}.payroll-window>.btn{margin:0 0 var(--s3)}
.payroll-window-head{display:flex;align-items:center;flex-wrap:wrap;gap:var(--s2) var(--s4);margin:0 0 var(--s3)}
.payroll-window-name{margin:0;font-size:var(--fs-17);font-weight:var(--w-medium)}
.payroll-window-sum{color:var(--steel-70);font-size:var(--fs-13)}
.payroll-days td{white-space:nowrap}
.payroll-job,.payroll-status{color:var(--steel-70)}
.payroll-table td.is-differs,.payroll-days td.is-differs{background:var(--attn-tint)}
.payroll-fix-row td{color:var(--steel-70)}.payroll-fix-row td[data-label="Reason"]{white-space:normal}
.pay-flag{display:inline-flex;align-items:center;height:var(--s6);margin-left:var(--s1);padding:0 var(--s2);background:var(--attn);color:var(--paper);border-radius:var(--r-press);font-family:var(--font);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.08em;text-transform:uppercase}
@media (max-width:767px){.payroll-days thead{display:none}
  .payroll-days,.payroll-days tbody,.payroll-days tr,.payroll-days td{display:block}
  .payroll-days tr{padding:var(--s2) 0;border-bottom:1px solid var(--rule-soft)}
  .payroll-days td{height:auto;padding:var(--s1) 0;border:0;text-align:left;white-space:normal}
  .payroll-days td:empty{display:none}
  .payroll-days td[data-label]::before{content:attr(data-label) " ";color:var(--steel-70);font-family:var(--font);font-size:var(--fs-11);letter-spacing:.06em;text-transform:uppercase}}
/* v1.5 (owner): the dashboard's Items missing a price card. The count is a plain underlined
   button in mono, red on hover, opening ui18's centred window, widened for the item table. */
.unpriced-line{display:flex;align-items:baseline;gap:var(--s3);margin:0}
.unpriced-count,.unpriced-zero{padding:0;background:none;border:0;color:var(--ink);font-family:var(--mono);font-size:var(--fs-22);line-height:var(--lh-figure)}
.unpriced-count{text-decoration:underline;cursor:pointer;transition:color var(--t)}.unpriced-count:hover{color:var(--red)}
.unpriced-window{max-width:calc(var(--form) + var(--pane-ctx))}
.unpriced-wide{overflow-x:auto}.unpriced-table td{white-space:nowrap}.unpriced-table td.cell-name{white-space:normal}
.unpriced-wide+.label,.unpriced-window .help+.label{margin-top:var(--s4)}  /* V2.6: two lists in one window */
/* v1.6 (owner): the inventory screen's Inventory report window - ui18's centred window,
   widened like the dashboard's missing-price window; the two filters sit under its head on
   one line, the count and Create report on the list's first line. */
.report-host{display:contents}
.report-window{max-width:calc(var(--form) + var(--pane-ctx))}
.report-filters{display:flex;flex-wrap:wrap;gap:var(--s2);padding:var(--s3) var(--s4);border-bottom:1px solid var(--rule)}
.report-filters .select{width:auto;min-width:calc(var(--fld-num) * 2)}
.report-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);margin:0 0 var(--s3)}
.report-table td{white-space:nowrap}.report-table td.cell-name{white-space:normal}
/* v1.6 (owner): several categories at once. Categories opens a ruled checklist under the
   filters, scrolling inside itself; each box sits on its own line with its name. */
.report-filters{align-items:flex-start}
.report-cats{position:relative}.report-cats>summary{list-style:none}.report-cats>summary::-webkit-details-marker{display:none}
.report-cat-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(calc(var(--fld-num) * 1.75),1fr));gap:var(--s1) var(--s4);max-height:calc(var(--row) * 6);margin:var(--s2) 0 0;padding:var(--s2) var(--s3);overflow-y:auto;border:1px solid var(--rule);border-radius:var(--r-press)}
.report-cat{display:flex;align-items:center;gap:var(--s2);font-size:var(--fs-14);cursor:pointer}
.report-cats[open]{flex:1 1 100%}
/* v1.8 (owner): one-off invoices. The details sit two to a row with the address and notes
   across; a line is added on one row under the charges; the totals read as a right-aligned
   list; the payment and void forms are one line each. */
.oneoff-fields{grid-template-columns:repeat(4,minmax(0,1fr));max-width:none}.oneoff-wide{grid-column:span 2}
.oneoff-fields .textarea{min-height:calc(var(--row) * 2)}
.oneoff-pay .field{flex-basis:calc(var(--fld-num) * 1.5)}
.oneoff-address{white-space:pre-line}
.oneoff-add,.oneoff-pay,.oneoff-void{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--s2) var(--s3);margin:var(--s3) 0 0}
.oneoff-add .field,.oneoff-pay .field,.oneoff-void .field{margin:0;flex:0 1 var(--fld-num)}
.oneoff-add .oneoff-desc,.oneoff-void .field{flex:1 1 calc(var(--fld-num) * 2)}
.oneoff-tax{align-self:center}
.oneoff-sums{display:grid;grid-template-columns:auto var(--fld-num);justify-content:end;gap:var(--s1) var(--s4);margin:var(--s4) 0 0;font-size:var(--fs-15)}
.oneoff-sums dd{margin:0;text-align:right;font-family:var(--mono)}.oneoff-sums dt:last-of-type,.oneoff-sums dd:last-of-type{font-weight:var(--w-medium)}
.oneoff-steps .stack{margin:0 0 var(--s2)}
@media (max-width:1023px){.oneoff-fields{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:767px){.oneoff-fields{grid-template-columns:minmax(0,1fr)}.oneoff-wide{grid-column:auto}}
/* V2.8: the Sent tab's marks (Sent for approval, Sent to AP), the tax breakdown, the job's
   Estimated row label. */
.mark-cell{white-space:nowrap}
.mark-cell>.check{vertical-align:middle;margin-right:var(--s2)}
.mark-cell .job-window{white-space:normal}
.mark-detail{margin:var(--s2) 0 0;padding-top:var(--s2);border-top:1px solid var(--rule-soft);max-width:calc(var(--fld-num) * 2);white-space:normal}
.mark-note{margin:0 0 var(--s1);font-family:var(--font);font-size:var(--fs-13);color:var(--ink);white-space:pre-wrap;overflow-wrap:anywhere}
.inv-tax-basis{color:var(--steel-70);font-size:var(--fs-13)}
.inv-tax-note{margin:0 0 0 auto;max-width:var(--prose);color:var(--steel-70);font-size:var(--fs-13);text-align:right}
.job-sum-label{margin:var(--s3) 0 var(--s2)}
/* V2.8 (owner 2026-10-19): Bill from on the Pending row, before Preview. */
.bill-from{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--s2)}
.table .col-bill,.table .col-dates{white-space:nowrap}
.table .col-balance{text-align:center;white-space:nowrap}
.bill-from-pick{display:inline-flex}
.bill-from-on{display:inline-flex;align-items:center;height:var(--s6);padding:0 var(--s2);background:var(--ok);color:var(--paper);border-radius:var(--r-press);font-family:var(--font);font-size:var(--fs-13);font-weight:var(--w-medium)}
.bill-from-off{color:var(--steel-70);font-family:var(--font);font-size:var(--fs-13)}
/* V2.8 (owner 2026-10-19): the Sent table, spaced so no column crowds the next, and a
   recorded mark as a box (check, date, who) that opens to its note. */
.sent-table td{vertical-align:middle}
.sent-col-id,.sent-col-act{width:1%;white-space:nowrap}
.table .sent-col-days{width:var(--fld-num);padding-left:var(--s6);padding-right:var(--s6);text-align:center;white-space:nowrap}
.table .sent-col-mark{width:calc(var(--fld-num) * 2);padding-left:var(--s6)}
.table .sent-col-money{padding-left:var(--s6);white-space:nowrap}
.mark-box{display:inline-flex;align-items:center;gap:var(--s2);padding:var(--s1) var(--s3) var(--s1) var(--s1);background:var(--paper);color:var(--ink);border:1px solid var(--rule);border-radius:var(--r-press);text-align:left;cursor:pointer;transition:background-color var(--t)}
.mark-box:hover,.mark-box[aria-expanded="true"]{background:var(--caliche)}
.mark-tick{display:inline-flex;align-items:center;justify-content:center;width:var(--s6);height:var(--s6);background:var(--ok);color:var(--paper);border-radius:var(--r-press)}
.mark-tick .icon{width:var(--s4);height:var(--s4)}
.mark-box-text{display:flex;flex-direction:column;gap:var(--s1)}
.mark-box-date{font-family:var(--mono);font-size:var(--fs-13);line-height:1}
.mark-box-who{font-family:var(--font);font-size:var(--fs-13);line-height:1;color:var(--steel-70)}
/* V2.9 (owner 2026-10-19): the PO window, centred over the PO table, wide enough for lines. */
.job-window.po-window{max-width:calc(var(--form) + var(--pane-ctx))}
@media (max-width:767px){.job-window.po-window{max-width:none}}
.scrim:has(+ .po-sheet){z-index:22}
.sheet.po-sheet{z-index:23}

/* V4.0 lane C: purchase order type, destination and field leads (R61). */
.po-choice{min-width:0;padding:0;border:0}
.po-choice>legend{padding:0}
.po-leads-phone{display:none}
@media (max-width:767px){.po-leads-phone{display:block;color:var(--steel-70);font-size:var(--fs-13)}}

/* V4.0 lane D: the company calendar (R65). Month grid at 768 and wider, day list below. */
.cal-head{flex-wrap:wrap}.cal-nav{display:flex;align-items:center;gap:var(--s2)}.cal-next .icon{transform:scaleX(-1)}
.cal-body{display:flex;flex-direction:column}
.cal-grid{display:grid;flex:1 0 auto;grid-template-rows:auto;grid-auto-rows:minmax(var(--s16),1fr);border-top:1px solid var(--rule);border-left:1px solid var(--rule)}
.cal-row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr))}.cal-weekdays{min-height:0}
.cal-weekday{padding:var(--s1) var(--s2);background:var(--card-head);border-right:1px solid var(--rule);border-bottom:1px solid var(--rule);color:var(--steel-70);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.06em;text-transform:uppercase}
.cal-day{display:flex;flex-direction:column;gap:var(--s1);min-width:0;padding:var(--s1);border-right:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.cal-day.is-out{background:var(--caliche)}.cal-day.is-out .cal-num{color:var(--steel-40)}
.cal-num{align-self:flex-start;padding:0 var(--s1);color:var(--steel-70);font-size:var(--fs-13);line-height:var(--lh-title)}
.cal-day.is-today .cal-num{background:var(--red);color:var(--paper);border-radius:var(--r-press)}
.cal-item{display:block;padding:0 var(--s1);border-left:var(--edge) solid var(--steel-40);color:var(--ink);font-size:var(--fs-13);line-height:var(--lh-title);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cal-item-event{border-left-color:var(--red);text-decoration:none;transition:background var(--t)}.cal-item-event:hover{background:var(--red-tint)}
.cal-item-holiday{border-left-color:var(--ok)}.cal-item-off{border-left-color:var(--attn);color:var(--steel-70)}
.cal-item-event.cal-item-holiday:hover{background:var(--card-head)}
.cal-time{font-family:var(--mono);color:var(--steel-70);white-space:nowrap}.cal-text{overflow-wrap:anywhere}
.cal-days{display:none}
.cal-list-day{padding:var(--s3) 0;border-bottom:1px solid var(--rule)}.cal-list-day:first-child{padding-top:0}.cal-list-day:last-child{border-bottom:0}
.cal-list-head{margin:0 0 var(--s2);font-family:var(--font);font-size:var(--fs-15);font-weight:var(--w-medium)}
.cal-list-day.is-today .cal-list-head{color:var(--red)}
.cal-list-day .cal-item{display:flex;align-items:center;gap:var(--s2);min-height:var(--row-touch);padding:0 var(--s2);white-space:normal;font-size:var(--fs-15)}
.cal-facts{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:var(--s2) var(--s4);margin:0}
.cal-facts dt{color:var(--steel-70);font-size:var(--fs-13)}.cal-facts dd{margin:0}.cal-notes{white-space:pre-line}
.cal-seen-choices{display:flex;flex-wrap:wrap;gap:var(--s4)}
@media (max-width:767px){.cal-grid{display:none}.cal-days{display:block}}

/* V4.0 lane F / owner test: reconciliation rows open the item window. */
.recon-table tbody tr.is-link{cursor:pointer}
.recon-open{padding:0;background:none;border:0;color:inherit;font:inherit;text-align:left;cursor:pointer;transition:color var(--t)}
.recon-open:hover{color:var(--red)}
.list-note.help-error{color:var(--crit)}

/* V4.0 lane H2: 3W trends (R68). Inline SVG line charts built in app/charts. */
.trends-charts{display:grid;grid-template-columns:repeat(auto-fit,minmax(calc(var(--s16) * 4.5),1fr));gap:var(--s6)}
.trend{min-width:0;margin:0}
.trend-title{margin:0 0 var(--s2);font-size:var(--fs-11);font-weight:var(--w-medium);letter-spacing:.06em;text-transform:uppercase;color:var(--steel-70)}
.trend-svg{display:block;width:100%;height:auto;overflow:visible}
.trend-grid{stroke:var(--rule);stroke-width:1px}.trend-axis{stroke:var(--steel-40);stroke-width:1px}
.trend-date{fill:var(--steel-70);font-family:var(--mono)}
.trend-tick{fill:var(--steel-70);font-family:var(--mono);font-size:var(--fs-11)}
.trend-line{fill:none;stroke:var(--red);stroke-width:var(--edge);stroke-linejoin:round;stroke-linecap:round}
.trend-dot{fill:var(--red);transition:fill var(--t)}.trend-dot:hover{fill:var(--ink)}
.trends-wide{margin:var(--s3) 0 0;overflow-x:auto}
.trends-table tfoot td{padding:var(--s2) var(--s3);border-top:1px solid var(--rule);font-weight:var(--w-medium)}

/* V4.0 lane H1 (R68): customer statement and AR report. */
.report-form{display:flex;flex-wrap:wrap;align-items:flex-end;gap:0 var(--s4);max-width:var(--form)}
.report-form>.field,.report-range .field{margin:0 0 var(--s3)}
.report-field-wide{flex:1 1 100%}
.report-set{flex:1 1 100%;margin:0 0 var(--s3);padding:0;border:0;min-width:0}
.report-range,.report-dates{display:flex;flex-wrap:wrap;align-items:flex-end;gap:0 var(--s4)}
.report-field{flex:0 1 var(--fld-num);min-width:var(--fld-num)}
.report-dates[x-cloak]{display:none}
.report-submit{margin:0 0 var(--s3)}
.report-bar{flex-wrap:wrap}
.report-head{margin:var(--s6) 0 var(--s2);color:var(--ink);font-family:var(--font);font-size:var(--fs-15);font-weight:var(--w-medium)}
.report-wide{overflow-x:auto}
.report-table td{white-space:nowrap}.report-table td.cell-name{white-space:normal;min-width:var(--fld-num)}
.report-id{font-family:var(--mono)}
.report-table .report-group td{background:var(--card-head);border-bottom:1px solid var(--rule);font-weight:var(--w-medium)}
.report-table .report-sub td,.report-table tfoot td{padding:var(--s2) var(--s3);border-top:1px solid var(--rule);font-weight:var(--w-medium)}
.report-total{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s4);max-width:var(--form);margin:var(--s6) 0 0;padding:var(--s3) 0 0;border-top:1px solid var(--ink);font-size:var(--fs-17);font-weight:var(--w-medium)}
.report-total-label{font-size:var(--fs-11);letter-spacing:.06em;text-transform:uppercase;color:var(--steel-70)}
@media (max-width:767px){.report-dates{flex:1 1 100%}.report-dates .report-field{flex:1 1 0}}

/* V4.0 lane G (R66, R67): leave and write-ups on the member file. */
.file-scroll{overflow-x:auto}
.file-scroll .cell-num{white-space:nowrap}
