/* Tech Dynamix — web app baseline (Gorelo-slate), plus the portal module list.
   The baseline half is the same tokens and components the quotes module uses, so
   the portal and every module land on one visual system. */

:root{
  --bg:#1E1F22;--panel:#2B2D31;--panel-2:#35373C;--line:#3A3C42;--line-2:#45474E;
  --ink:#F2F3F5;--muted:#A6ABB5;--dim:#70747C;
  --orange:#E87722;--orange-dim:#a8551a;--orange-tint:rgba(232,119,34,.18);
  --teal:#5CC3D8;--violet:#A396CF;
  --blue:#9575CD;--blue-bright:#B39DDB;
  --green:#66BB6A;--warn:#FFB300;--hot:#D64541;
  --shadow:0 1px 2px rgba(0,0,0,.25);
  /* grid tokens, same values the CRM sheet uses */
  --orange-dk:#F08A3C;--orange-soft:rgba(232,119,34,.14);--orange-ring:rgba(232,119,34,.30);
  --grid-head:#35373C;--grid-line:#3A3C42;--grid-gutter:#26282B;
  /* soft fill + ring per status colour, as CRM's badge-c-* set */
  --gray-soft:rgba(166,171,181,.14);--gray-ring:rgba(166,171,181,.30);
  --teal-soft:rgba(92,195,216,.14);--teal-ring:rgba(92,195,216,.32);
  --violet-soft:rgba(163,150,207,.16);--violet-ring:rgba(163,150,207,.34);
  --green-soft:rgba(102,187,106,.14);--green-ring:rgba(102,187,106,.32);
  --warn-soft:rgba(255,179,0,.14);--warn-ring:rgba(255,179,0,.32);
}
:root[data-theme="light"]{
  --bg:#EEF0F3;--panel:#FFFFFF;--panel-2:#E8EAEE;--line:#DFE2E7;--line-2:#C8CCD4;
  --ink:#1F2226;--muted:#5B6069;--dim:#9096A0;
  --orange:#D96A15;--orange-dim:#A8551A;--orange-tint:rgba(232,119,34,.12);
  --teal:#1E7A8C;--violet:#7566A0;
  --blue:#6A46B0;--blue-bright:#7E57C2;
  --green:#2E7D32;--warn:#B26A00;--hot:#D93B37;
  --orange-dk:#A8551A;--orange-soft:rgba(232,119,34,.12);--orange-ring:rgba(217,106,21,.28);
  --grid-head:#E8EAEE;--grid-line:#DFE2E7;--grid-gutter:#F3F4F6;
  --gray-soft:rgba(91,96,105,.10);--gray-ring:rgba(91,96,105,.26);
  --teal-soft:rgba(30,122,140,.10);--teal-ring:rgba(30,122,140,.28);
  --violet-soft:rgba(117,102,160,.10);--violet-ring:rgba(117,102,160,.28);
  --green-soft:rgba(46,125,50,.10);--green-ring:rgba(46,125,50,.28);
  --warn-soft:rgba(178,106,0,.10);--warn-ring:rgba(178,106,0,.28);
}

*{box-sizing:border-box}
/* The hidden attribute must win over a component's own display rule. Without
   this, .bulkbar{display:flex} and .btn{display:inline-flex} keep showing
   elements the JS has marked hidden — the bulk bar sat there reading
   "0 selected", and Clear never went away. */
[hidden]{display:none!important}
body{margin:0;background:var(--bg);color:var(--ink);
  font:15px/1.5 "IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif;}
a{color:var(--orange);text-decoration:none}
a:hover{text-decoration:underline}
h1,h2,h3{margin:0;font-weight:600}
.muted{color:var(--muted);font-size:13px}
.dim{color:var(--dim);font-size:12px}
.spacer{flex:1}
.hidden{display:none!important}
.num{text-align:right;font-family:"IBM Plex Mono",monospace;font-size:13px;font-variant-numeric:tabular-nums}
.err{color:var(--hot);font-size:13px}
.ok{color:var(--green);font-size:13px}

/* ---- chrome: QBR-style standalone topline, same structure and metrics as
   R:\crm (topline-outer wrapper + topline-nav on the right) ---- */
.topline-outer{max-width:1180px;margin:0 auto;padding:18px 28px 0}
.layout-wide .topline-outer{max-width:none;width:100%;padding:12px 16px 0}
.topline{display:flex;align-items:center;gap:14px;margin-bottom:18px}
.topline .logo{height:52px;width:auto;max-width:380px;object-fit:contain;display:block}
:root:not([data-theme="light"]) .topline .logo{filter:drop-shadow(0 0 22px rgba(232,119,34,.25))}
.topline .appname{font-family:"Bricolage Grotesque",sans-serif;font-weight:800;font-size:19px;
  letter-spacing:.5px;color:var(--ink)}
.topline .sub{color:var(--dim);font-size:13px}
.topline .spacer{flex:1}
.topline-nav{display:flex;align-items:center;gap:8px;flex-shrink:0;flex-wrap:wrap}
.brand{display:flex;align-items:center}
.brand:hover{text-decoration:none}
:root:not([data-theme="light"]) .logo-light{display:none}
:root[data-theme="light"] .logo-dark{display:none}
@media(max-width:720px){.topline .logo{display:none}}
.mast-user{font-size:12.5px;color:var(--muted);font-weight:500;max-width:160px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---- layout ---- */
/* 28px gutter matches .topline-outer, so content lines up under the masthead. */
.wrap{max-width:1180px;margin:24px auto;padding:0 28px}
/* The catalog grid (many columns) spans the full viewport — no max-width cap and
   only a narrow gutter — so the table runs edge to edge. Forms and the quote
   builder keep the narrow 1180 width. Mirrors CRM's .layout-wide. */
.layout-wide .wrap{max-width:none;width:100%;margin:0;padding:0 16px 24px}
/* Form-only pages inside a wide layout: same left gutter as every other page,
   but the cards stop at a readable width instead of stretching. Capping the
   cards rather than the wrap is what keeps the page margin identical. */
.layout-wide .wrap-forms > .card,
.layout-wide .wrap-forms > .tabs{max-width:1180px}

/* ---- chips / buttons ---- */
/* Transition background-COLOR, not the `background` shorthand: transitioning
   the shorthand leaves the element stuck on its old colour when the theme
   swaps the custom properties, so chips kept dark-mode colours in light mode
   until the page was reloaded. */
.chip,.btn{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border:1px solid var(--line-2);
  border-radius:4px;background:transparent;color:var(--muted);cursor:pointer;white-space:nowrap;
  font:600 13px/1.5 "IBM Plex Sans",sans-serif;letter-spacing:.02em;text-decoration:none;
  transition:background-color .12s,border-color .12s,color .12s}
.chip{height:30px}
.chip:hover,.btn:hover{color:var(--ink);border-color:#565A63;background:var(--panel);text-decoration:none}
.chip.active{background:transparent;color:var(--orange);border-color:var(--orange)}
.chip.icon{padding:4px 9px}
.chip.icon svg{display:block;width:14px;height:14px}
.chip.primary{background:transparent;color:var(--orange);border-color:var(--orange)}
.chip svg,.chip .ico{display:block;width:14px;height:14px}
.btn{padding:6px 16px}
.btn-primary{color:var(--orange);border-color:var(--orange)}
.btn-primary:hover{color:var(--orange);border-color:var(--orange);background:var(--orange-tint)}
.btn-danger:hover{border-color:var(--hot);color:var(--hot);background:transparent}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn-sm{padding:3px 9px;font-size:12px}

/* ---- cards ---- */
.card{background:var(--panel);border:1px solid transparent;border-radius:4px;
  box-shadow:var(--shadow);padding:20px 24px;margin-bottom:16px}
.card-h{font-size:13px;text-transform:uppercase;letter-spacing:2px;font-weight:700;
  color:var(--ink);margin-bottom:14px;display:flex;align-items:center;gap:10px}
.card-h .spacer{flex:1}

/* ---- fields ---- */
input,select,textarea{background:var(--panel-2);border:1px solid var(--line-2);border-radius:4px;
  color:var(--ink);font:14px/1.5 "IBM Plex Sans",sans-serif;padding:8px 10px;width:100%}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--orange)}
textarea{resize:vertical}
/* A label is a BLOCK, not a flex column. As a flex column every child became
   its own row, so a required mark or a "(qualifier)" span dropped onto a line
   of its own beneath the caption and shoved the field out of line with its
   neighbours. As a block the caption text, its qualifier and the required mark
   all flow inline, and the control below is the only block. */
label{display:block;font-size:11px;text-transform:uppercase;letter-spacing:1.5px;
  color:var(--muted);font-weight:600;
  /* One-line captions keep every field in a row on the same baseline; a long
     one clips rather than wrapping and dragging its input down. */
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
label > input,label > select,label > textarea{display:block;width:100%;margin-top:5px;
  white-space:normal;text-transform:none;letter-spacing:normal;font-weight:400;font-size:14px}
label input,label select,label textarea{text-transform:none;letter-spacing:normal;font-weight:400;font-size:14px}
/* Optional explicit caption wrapper — same result, used where the markup reads
   better for it. */
.lbl{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.q{text-transform:none;letter-spacing:0;color:var(--dim);font-weight:400}
.req::after{content:" *";color:var(--orange)}
.field-err{border-color:var(--hot)!important}

/* Checkbox / radio captions sit on the box's own line, and bottom-align in a
   grid row so they land level with the inputs beside them. */
label.check{display:flex;flex-direction:row;align-items:center;gap:8px;text-transform:none;
  letter-spacing:0;font-size:14px;font-weight:400;color:var(--muted);cursor:pointer;white-space:nowrap}
label.check > input{display:inline-block;width:auto;margin:0;accent-color:var(--orange);flex:0 0 auto}
.grid > label.check{height:39px;margin-top:auto}

.controls-bar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:14px;background:var(--panel);
  border:1px solid transparent;border-radius:4px;box-shadow:var(--shadow);padding:18px 24px;margin-bottom:24px}
.fld{display:flex;flex-direction:column;gap:5px}
.fld input,.fld select{width:auto;min-width:200px;height:39px}
.fld.wide input,.fld.wide select{min-width:260px}
.fld-row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:14px}
.controls-bar .btn,.fld-row .btn{padding:8px 16px;font-size:14px;line-height:1.5;height:39px}
.grid{display:grid;gap:16px}
.grid-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid-3{grid-template-columns:repeat(3,minmax(0,1fr))}
.grid-4{grid-template-columns:repeat(4,minmax(0,1fr))}
.span2{grid-column:span 2}
/* Collapse the wide cells too, or the single-column layout keeps a stray
   zero-width second track. */
@media(max-width:820px){.grid-2,.grid-3,.grid-4{grid-template-columns:1fr}.span2{grid-column:auto}}

/* ---- login ---- */
.login-wrap{min-height:80vh;display:flex;align-items:center;justify-content:center}
.login-card{width:380px}
.login-card .card-h{justify-content:center}

/* ---- portal: the module list -------------------------------------------- */
.portal-intro{color:var(--muted);font-size:14px;margin:0 0 20px}
.modules{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px}
.mod{display:flex;flex-direction:column;gap:8px;background:var(--panel);
  border:1px solid transparent;border-radius:4px;box-shadow:var(--shadow);
  padding:20px 24px;color:var(--ink);text-decoration:none;
  transition:border-color .12s,transform .12s}
a.mod:hover{text-decoration:none;border-color:var(--orange);transform:translateY(-1px)}
.mod-top{display:flex;align-items:center;gap:10px}
.mod-ico{flex:0 0 auto;width:26px;height:26px;color:var(--orange)}
.mod-ico svg{width:26px;height:26px;display:block}
.mod-name{font-family:"Bricolage Grotesque",sans-serif;font-weight:800;font-size:17px;
  letter-spacing:.3px}
.mod-sub{color:var(--muted);font-size:13px;line-height:1.5}
.mod-go{margin-top:auto;padding-top:10px;color:var(--orange);font-size:12px;font-weight:600;
  letter-spacing:.04em;text-transform:uppercase}
/* A module that is registered but not reachable yet reads as inert rather than
   broken: no hover lift, no orange call to action. */
.mod.is-soon{opacity:.55;cursor:default}
.mod.is-soon:hover{border-color:transparent;transform:none}
.mod.is-soon .mod-ico,.mod.is-soon .mod-go{color:var(--dim)}

/* The signed-in identity, shown only when SSO can name a person. */
.who{display:flex;align-items:center;gap:10px;color:var(--muted);font-size:13px}

/* ---- shared components the platform pages use --------------------------- */
/* Lifted from the same baseline as the rest of this file. They were missing:
   portal.css was cut at the status pills when it was first extracted, so every
   .pill rendered as bare text inheriting .card-h's uppercase + letter-spacing,
   and every .tbl rendered as an unstyled table. */

/* ---- status pills: same box as CRM's .badge — inline-block, natural height,
   4px radius, body font 10.5px, normal case, soft fill + coloured ring. Colour
   is always paired with the text label. ---- */
.pill{display:inline-block;padding:1px 8px;border-radius:4px;white-space:nowrap;
  font:600 10.5px/1.55 "IBM Plex Sans",sans-serif;letter-spacing:0;text-transform:none;
  border:1px solid var(--line-2);color:var(--muted);background:var(--gray-soft)}
/* Buttons don't inherit font-family, so the pill-as-button needs it restated. */
button.pill{font-family:"IBM Plex Sans",sans-serif;line-height:1.55;cursor:pointer;
  transition:box-shadow .15s}
button.pill:hover{box-shadow:0 0 0 2px var(--orange-ring)}
button.pill:focus-visible{outline:none;box-shadow:0 0 0 2px var(--orange)}
.pill-draft{background:var(--gray-soft);color:var(--muted);border-color:var(--line-2)}
.pill-sent{background:var(--teal-soft);color:var(--teal);border-color:var(--teal-ring)}
.pill-viewed{background:var(--violet-soft);color:var(--violet);border-color:var(--violet-ring)}
.pill-accepted{background:var(--green-soft);color:var(--green);border-color:var(--green-ring)}
.pill-declined{background:var(--gray-soft);color:var(--muted);border-color:var(--gray-ring)}
.pill-expired{background:var(--warn-soft);color:var(--warn);border-color:var(--warn-ring)}

/* ---- tables ---- */
.tbl{width:100%;border-collapse:collapse}
.tbl th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:1.5px;
  color:var(--muted);font-weight:600;padding:0 8px 8px;white-space:nowrap}
.tbl th.r,.tbl td.r{text-align:right}
.tbl td{padding:7px 8px;border-top:1px solid var(--line);vertical-align:middle}
.tbl tbody tr:hover{background:var(--panel-2)}
.tbl a.row-title{color:var(--ink);font-weight:600}
.tbl .grow{width:99%}
.sortlink{color:var(--muted)}
.sortlink.on{color:var(--orange)}
.empty-card{color:var(--muted)}

/* Right-aligned cells, and anything that should not reach the printer. */
.r{text-align:right}
@media print{ .no-print{display:none!important} }

/* ---- tabs (admin) -------------------------------------------------------
   The base rule. portal.css already carried the SCOPED
   `.layout-wide .wrap-forms > .tabs{max-width:1180px}` from the layout block,
   which is why a search for ".tabs" found something and this rule looked
   present — but that selector needs a .layout-wide ancestor the platform pages
   do not have, so the tab row had no flex, no gap and no bottom margin: the
   chips sat on a text baseline and the first card butted straight up under
   them. Presence of a class name in some selector is not the same as a rule
   that applies. */
.tabs{display:flex;gap:8px;margin-bottom:18px;flex-wrap:wrap}

@media print{
  .wrap{padding:0;max-width:none}
}


/* ---- the sign-in page ----------------------------------------------------
   Deliberately not the app masthead: a signed-out visitor has no navigation to
   offer, so the brand sits centred over a single card and the theme toggle
   floats clear of it. */
.signin-page{min-height:100vh;display:flex;flex-direction:column;align-items:center;
  justify-content:center;padding:32px 20px}
.signin-brand{display:block;margin-bottom:26px}
.signin-brand:hover{text-decoration:none}
.signin-brand img{height:50px;width:auto;max-width:270px;object-fit:contain;display:block}
:root:not([data-theme="light"]) .signin-brand img{filter:drop-shadow(0 0 26px rgba(232,119,34,.28))}

.signin-card{width:100%;max-width:400px;padding:30px 30px 28px;margin-bottom:0}
.signin-title{font-family:"Bricolage Grotesque",sans-serif;font-weight:800;font-size:25px;
  line-height:1.2;letter-spacing:.2px;color:var(--ink);margin:0 0 7px}
.signin-sub{color:var(--muted);font-size:13.5px;line-height:1.5;margin:0 0 22px}
.signin-foot{margin-top:22px;color:var(--dim);font-size:12px;letter-spacing:.04em;text-align:center}
.theme-float{position:fixed;top:18px;right:18px;z-index:5}

/* Sign in with Microsoft. The four-square mark keeps its own colours — it is
   Microsoft's, not ours to re-tint — while the button itself follows the
   surrounding system so it does not read as a foreign object. */
.btn-ms{width:100%;justify-content:center;gap:10px;height:46px;font-size:14.5px;
  background:var(--panel-2);border-color:var(--line-2);color:var(--ink);font-weight:600}
.btn-ms:hover{background:var(--panel-2);border-color:var(--orange-ring);color:var(--ink)}
.ms-mark{width:17px;height:17px;flex:0 0 auto;display:block}

/* The rule between the two routes. */
.or{display:flex;align-items:center;gap:12px;margin:20px 0;
  color:var(--dim);font-size:11px;font-weight:600;letter-spacing:1.6px}
.or::before,.or::after{content:"";flex:1;height:1px;background:var(--line-2)}

/* Roomier fields than the dense in-app forms: this page has three controls on
   it, so they can afford the space. */
.signin-card .fld-tall{margin-bottom:14px}
.signin-card .fld-tall input{height:48px;font-size:14.5px;padding:0 14px;background:var(--panel-2)}

/* The one filled button in the system. Everything in-app is an outline control,
   which is right when a page has twenty of them and wrong when a page has one
   and it is the whole point of the page. */
.btn-solid{width:100%;justify-content:center;height:46px;background:var(--orange);
  border-color:var(--orange);color:#fff;font-weight:700;font-size:13.5px;
  letter-spacing:.08em;text-transform:uppercase}
.btn-solid:hover{background:var(--orange-dk);border-color:var(--orange-dk);color:#fff}

/* ---- portal: sections ----------------------------------------------
   A heading with a rule running off to the right, so several sections read as
   parts of one page rather than as separate cards. */
.section{margin-top:28px}
.section:first-of-type{margin-top:0}
.section-h{display:flex;align-items:center;gap:12px;margin-bottom:13px;
  font-size:11.5px;font-weight:700;letter-spacing:2px;text-transform:uppercase;
  color:var(--muted)}
.section-h::after{content:"";flex:1;height:1px;background:var(--line-2)}

/* ---- portal: arranging (admins only) ------------------------------------ */
.mod{position:relative}
.mod-grip{position:absolute;top:8px;right:8px;width:22px;height:22px;display:none;
  align-items:center;justify-content:center;border-radius:4px;color:var(--dim);
  cursor:grab;touch-action:none;user-select:none;line-height:1}
.can-arrange .mod-grip{display:flex}
.mod-grip:hover{color:var(--orange);background:var(--orange-soft)}
.mod-grip svg{width:13px;height:13px;display:block;pointer-events:none}
.mod.is-dragging{opacity:.45;border-color:var(--orange)}
body.is-arranging{cursor:grabbing;user-select:none}
body.is-arranging .mod{transition:none}
.arrange-hint{margin-top:14px;font-size:12px;color:var(--dim)}
.arrange-hint.is-ok{color:var(--green);font-weight:600}
.arrange-hint.is-err{color:var(--hot);font-weight:600}

/* ---- the data grid ------------------------------------------------------
   Lifted from the same baseline as the rest of this file so any module can
   render the CRM/quotes table without reaching into another module's assets.
   assets/grid.js drives it. */

/* ---- catalog grid (ported from the CRM sheet: same autofilter behaviour,
       same header treatment, same tokens) ---- */
.sheet{background:var(--panel);border:1px solid var(--line-2);border-radius:4px 4px 0 0;
  overflow:auto;max-height:calc(100vh - 330px);min-height:180px;box-shadow:var(--shadow)}
table.dgrid{width:100%;border-collapse:separate;border-spacing:0;
  font-family:"Inter","IBM Plex Sans",sans-serif;font-size:13px;font-variant-numeric:tabular-nums}

table.dgrid thead tr.fields th{position:sticky;top:0;z-index:3;background:var(--grid-head);
  border-right:1px solid var(--grid-line);border-bottom:2px solid var(--line-2);
  color:var(--ink);font-family:"IBM Plex Sans",sans-serif;font-size:11.5px;font-weight:600;
  letter-spacing:.05em;text-transform:uppercase;text-align:left;padding:0;white-space:nowrap;
  user-select:none}
table.dgrid thead tr.fields th .sort{display:flex;align-items:center;justify-content:space-between;
  gap:4px;padding:6px 5px 6px 8px;color:inherit;text-decoration:none;cursor:pointer}
table.dgrid thead tr.fields th .sort:hover{background:var(--orange-soft);color:var(--orange-dk);text-decoration:none}
/* Layer the translucent tint over the opaque header so the sticky sorted header
   stays opaque — otherwise scrolled rows bleed through it. */
table.dgrid thead tr.fields th.sorted{background:linear-gradient(var(--orange-soft),var(--orange-soft)),var(--grid-head);
  color:var(--orange-dk)}
/* Right-aligned columns keep the label-then-caret order of every other header;
   only the whole group shifts right. */
table.dgrid thead tr.fields th.r .sort{justify-content:flex-end}

table.dgrid thead tr.fields th .th-caret{display:inline-flex;align-items:center;justify-content:center;
  flex:0 0 auto;width:18px;height:18px;border:1px solid transparent;border-radius:3px;
  color:var(--dim);background:var(--panel)}
table.dgrid thead tr.fields th .th-caret svg{width:11px;height:11px}
/* The funnel only appears once the column is actually filtering something. */
table.dgrid thead tr.fields th .th-caret svg:last-child{display:none}
table.dgrid thead tr.fields th.filtered .th-caret svg:first-child{display:none}
table.dgrid thead tr.fields th.filtered .th-caret svg:last-child{display:inline}
table.dgrid thead tr.fields th.filtered .th-caret{color:var(--orange);border-color:var(--orange-ring);background:var(--panel)}
table.dgrid thead tr.fields th .sort:hover .th-caret{border-color:var(--line-2);color:var(--orange-dk)}
table.dgrid thead tr.fields th.sorted .th-caret{color:var(--orange-dk)}

table.dgrid td{padding:5px 8px;border-right:1px solid var(--grid-line);
  border-bottom:1px solid var(--grid-line);color:var(--ink);vertical-align:middle;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:260px}
table.dgrid td.r{text-align:right}
table.dgrid tbody tr{cursor:cell}
table.dgrid tbody tr:hover td{background:var(--orange-soft)}
table.dgrid tbody tr:hover .rownum{background:var(--orange-ring);color:var(--ink)}
table.dgrid .empty-cell{color:var(--dim)}
table.dgrid tr.archived td{opacity:.55}

/* Frozen left gutter — checkbox then row number. Both sit on --grid-gutter so
   they read as chrome rather than data, matching CRM. */
table.dgrid .cell-check{width:32px;min-width:32px;padding:0;text-align:center;
  vertical-align:middle;background:var(--grid-gutter);
  border-right:1px solid var(--grid-line);border-bottom:1px solid var(--grid-line);
  position:sticky;left:0;z-index:2}
/* Needs its own rule: `thead tr.fields th` sets text-align:left for the sort
   labels and outranks .cell-check, which drags the select-all checkbox
   off-centre from the ones in the rows below it. */
table.dgrid thead tr.fields th.cell-check{z-index:4;background:var(--grid-head);
  text-align:center;padding:0}
table.dgrid .cell-check input{accent-color:var(--orange);cursor:pointer;margin:0;
  vertical-align:middle;width:auto}
table.dgrid .rownum{width:40px;min-width:40px;background:var(--grid-gutter);
  border-right:1px solid var(--grid-line);border-bottom:1px solid var(--grid-line);
  color:var(--dim);font-family:"IBM Plex Mono",monospace;font-size:10.5px;font-weight:500;
  text-align:center;padding:0;user-select:none;
  position:sticky;left:32px;z-index:2}   /* left MUST equal .cell-check width */
table.dgrid thead .rownum{z-index:4;background:var(--grid-head)}
table.dgrid tbody tr.is-selected td{background:var(--orange-soft)}
table.dgrid tbody tr.is-selected .rownum,
table.dgrid tbody tr.is-selected .cell-check{background:var(--orange-ring)}
table.dgrid td .rowlink{color:var(--ink);font-weight:600}

/* ── autofilter dropdown ── */
.af-panel{position:fixed;z-index:100;background:var(--panel);border:1px solid var(--line-2);
  border-radius:4px;box-shadow:0 6px 28px rgba(0,0,0,.4);padding:4px;
  font-family:"IBM Plex Sans",sans-serif;font-size:12.5px;color:var(--ink);
  text-transform:none;letter-spacing:0;font-weight:400}
.af-item{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border:0;
  border-radius:3px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}
.af-item:hover{background:var(--orange-soft);color:var(--orange-dk)}
.af-item svg{flex:0 0 auto;color:var(--muted)}
.af-item:hover svg{color:var(--orange)}
.af-sep{height:1px;background:var(--line);margin:4px 2px}
.af-search{padding:4px 5px 6px}
.af-search input{width:100%;padding:5px 8px;font-size:12.5px;background:var(--panel-2);
  border:1px solid var(--line-2);border-radius:3px;color:var(--ink)}
.af-search input:focus{outline:none;border-color:var(--orange)}
.af-list{max-height:240px;overflow-y:auto;padding:0 2px 2px}
.af-opt{display:flex;align-items:center;gap:8px;padding:4px 6px;border-radius:3px;
  cursor:pointer;text-transform:none;letter-spacing:0;font-size:12.5px;color:var(--ink);
  font-weight:400;flex-direction:row}
.af-opt:hover{background:var(--orange-soft)}
.af-opt input{flex:0 0 auto;accent-color:var(--orange);margin:0;width:auto}
.af-opt span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.af-all{border-bottom:1px solid var(--line);border-radius:0;margin-bottom:3px;padding-bottom:5px}
.af-none{padding:10px;text-align:center;color:var(--dim);font-size:12px}

/* ── grid toolbar, bulk bar, pager ── */
/* The toolbar IS the card (same as CRM's .toolbar) — the grid below it is not
   wrapped in one, so the sheet can run the full page width. */
/* Metrics copied from CRM's .toolbar exactly — gap 14, padding 18/24,
   margin-bottom 14 — so the rows below line up between the two apps. */
.grid-toolbar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:14px;margin-bottom:14px;
  background:var(--panel);border:1px solid transparent;border-radius:4px;
  box-shadow:var(--shadow);padding:18px 24px}
.grid-toolbar .spacer{flex:1}
.grid-sum{font-family:"IBM Plex Mono",monospace;font-size:12px;color:var(--dim)}
.grid-toolbar .fld-search{flex:1 1 240px}
.grid-toolbar .fld-search input{width:100%;min-width:180px;height:39px}
/* Same rule as the controls bar: anything beside a boxed field is field height. */
.grid-toolbar .btn{height:39px;padding:0 16px;font-size:14px}
.grid-toolbar .fld input,.grid-toolbar .fld select{height:39px}
.grid-toolbar .count{font-size:12px;color:var(--muted);padding-bottom:2px;white-space:nowrap}
.bulkbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;background:var(--panel);
  border:1px solid var(--orange-ring);border-radius:4px;padding:10px 14px;margin-bottom:12px}
.bulkbar .bulk-count{font-size:13px;color:var(--muted)}
.bulkbar .bulk-count strong{color:var(--orange);font-family:"IBM Plex Mono",monospace}
/* ---- bottom bar: view switch · saved-view tabs · + · hint · pager ----
   Ported from CRM's .sheet-tabs so the two apps read identically. */
.sheet-tabs{display:flex;align-items:center;gap:2px;padding:0 10px;margin-top:-1px;
  border-top:1px solid var(--line-2);flex-wrap:wrap;margin-bottom:16px}
.viewtabs{display:inline-flex;gap:4px;margin-right:8px}
.sheet-tabs .chip.icon{height:26px;padding:0 8px}
.sheet-tabs .chip.icon svg{width:15px;height:15px}

.sheet-tab{font:600 12px/1.4 "IBM Plex Sans",sans-serif;color:var(--dim);
  background:var(--bg);border:1px solid var(--line-2);border-top:3px solid transparent;
  border-radius:0 0 4px 4px;padding:3px 14px 4px;cursor:pointer;max-width:180px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sheet-tab:hover{color:var(--ink);background:var(--panel-2)}
/* The active sheet pops: panel fill, thick orange top bar, and stands proud. */
.sheet-tab.is-active{color:var(--orange);font-weight:700;background:var(--panel);
  border-color:var(--line-2);border-top:3px solid var(--orange);padding:6px 16px 8px;
  box-shadow:0 3px 8px rgba(232,119,34,.20)}
.sheet-tab.is-active:hover{background:var(--panel);color:var(--orange)}
.tab-add{font:600 14px/1 "IBM Plex Sans",sans-serif;color:var(--dim);background:none;
  border:1px solid transparent;border-radius:4px;padding:4px 9px;margin-left:4px;cursor:pointer}
.tab-add:hover{color:var(--orange);border-color:var(--orange-ring);background:var(--orange-soft)}
.sheet-tabs .hint{margin-left:10px;font-size:11.5px;color:var(--dim)}
.sheet-tabs .hint.is-ok{color:var(--green);font-weight:600}
.sheet-tabs .hint.is-err{color:var(--hot);font-weight:600}

.pager{display:flex;align-items:center;gap:10px;margin-left:auto;padding:3px 0}
.pager-size-btn{display:inline-flex;align-items:center;gap:5px}
.pager-info{font-size:11.5px;color:var(--muted);white-space:nowrap;min-width:78px;text-align:center}
.pager-nav{display:inline-flex;align-items:center;gap:4px}
.pager .btn-sm[disabled]{opacity:.4}
.pager-menu{min-width:156px}
.pager-menu .sm-item{font-size:12.5px;padding:5px 8px}
.pager-custom{display:flex;align-items:center;gap:6px;padding:4px}
.pager-custom-input{width:74px;font:inherit;font-size:12px;padding:4px 6px;
  border:1px solid var(--line-2);border-radius:4px;background:var(--panel-2);color:var(--ink)}
.pager-custom-input.is-err{border-color:var(--hot)}
@media(max-width:640px){.pager{margin-left:0;width:100%;justify-content:flex-end}}
.sm-panel{position:fixed;z-index:100;background:var(--panel);border:1px solid var(--line-2);
  border-radius:4px;box-shadow:0 6px 28px rgba(0,0,0,.4);padding:4px}
.sm-item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;
  padding:6px 10px;border:0;border-radius:3px;background:transparent;color:var(--ink);
  font:400 13px/1.4 "IBM Plex Sans",sans-serif;text-align:left;cursor:pointer}
.sm-item:hover{background:var(--orange-soft);color:var(--orange-dk)}
.sm-tick{color:var(--orange)}
/* System-set statuses are shown but not offered — hiding them just invites
   "why can't I set this?". */
.sm-item.is-disabled{cursor:default;opacity:.6;flex-direction:column;align-items:flex-start;gap:2px}
.sm-item.is-disabled:hover{background:transparent;color:var(--ink)}
.sm-why{font-size:11px;color:var(--dim)}
.status-menu .pill{pointer-events:none}
button.pill.status-pick{cursor:pointer}
button.pill.status-pick:hover{filter:brightness(1.15)}
.grid-none{padding:20px;text-align:center;color:var(--muted)}

/* ---- the profile bubble and its menu ------------------------------------
   Always the last thing in the masthead, at every width, so people learn one
   place to look. Everything about the person rather than the page lives inside
   it — appearance, settings, signing out. */
.usermenu{position:relative;flex:0 0 auto;margin-left:10px}
.avatar-btn{width:32px;height:32px;padding:0;border:0;border-radius:50%;background:none;
  cursor:pointer;display:block;line-height:0}
.avatar-btn:focus-visible{outline:none;box-shadow:0 0 0 2px var(--orange)}
.avatar{width:32px;height:32px;border-radius:50%;display:block;object-fit:cover;
  background:var(--panel-2)}
.avatar-initials{display:flex;align-items:center;justify-content:center;background:var(--orange);
  color:#fff;font:700 12.5px/1 "IBM Plex Sans",sans-serif;letter-spacing:.02em;
  text-transform:uppercase;user-select:none}
.avatar-btn:hover .avatar{box-shadow:0 0 0 2px var(--orange-ring)}

.usermenu-panel{position:absolute;top:calc(100% + 10px);right:0;z-index:120;min-width:236px;
  background:var(--panel);border:1px solid var(--line-2);border-radius:4px;
  box-shadow:0 8px 28px rgba(0,0,0,.4);padding:4px}
.usermenu-head{padding:12px 12px 10px;border-bottom:1px solid var(--line);margin-bottom:4px}
.usermenu-name{font-weight:700;font-size:14px;color:var(--ink)}
.usermenu-mail{display:flex;align-items:center;gap:7px;margin-top:4px;
  font-size:12.5px;color:var(--muted);word-break:break-all}
.usermenu-mail svg{width:14px;height:14px;flex:0 0 auto;color:var(--dim)}

/* The menu rows reuse .sm-item, so this panel and the grid's menus stay one
   component rather than two that drift. */
.usermenu-panel .sm-item{gap:10px;justify-content:flex-start;padding:8px 10px;
  font-size:13.5px;text-decoration:none;cursor:pointer}
.usermenu-panel .sm-item svg{width:16px;height:16px;flex:0 0 auto;color:var(--muted)}
.usermenu-panel .sm-item:hover svg{color:var(--orange)}
.usermenu-panel .theme-ico{display:flex;width:16px;height:16px;flex:0 0 auto;color:var(--muted)}
.usermenu-panel .theme-ico svg{width:16px;height:16px}
.usermenu-panel .sm-item:hover .theme-ico{color:var(--orange)}

/* Masthead actions: icon and label, sitting beside the module they belong to
   rather than against the profile bubble. They wrap before they squeeze. */
.topline-nav{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0;
  margin-left:6px}
.topline-nav .chip{gap:7px}
.topline-nav .chip svg{width:14px;height:14px;flex:0 0 auto}
/* The module name and its one-line sub must not squash the actions out. */
.topline-id{min-width:0;flex:0 0 auto}
/* Narrow: the actions drop to their own row under the identity rather than
   colliding with the bubble. */
@media(max-width:820px){
  .topline{flex-wrap:wrap}
  .topline-nav{order:3;width:100%;margin-left:0;margin-top:2px}
  .topline .spacer{order:2}
  .usermenu{order:2}
}
