/* Direct Connect shared chrome, ui-v3.
 *
 * Sits between tokens.css (colour, type, radius, the source of truth) and each
 * page's own <style> block (page-specific layout only). Everything here reads
 * from tokens.css. Link order on every page: tokens.css, then shell.css, then
 * the page block. A page rule that needs to win simply comes later.
 */

*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:var(--fs-body);
  line-height:1.5;-webkit-font-smoothing:antialiased}
/* .hide MOVED TO THE END OF THIS FILE. It was declared here, first, which is the
   natural place for a utility and the wrong one: every component below that sets
   its own display beats it on source order at equal specificity. .scope did
   exactly that, so a single-account admin, which is every client admin, got an
   18x10 empty focusable button sitting in the navy bar with no accessible name.
   Found by the brief's own "no control without an accessible name" check. */

/* ---- top bar: full-bleed navy, nav inside it -----------------------------
 * FLAT NAVY, NOT A GRADIENT. The bar carried a radial gradient from --navy-2
 * to --ink-navy. At 52px tall nobody could see it was a gradient, and it cost
 * two palette entries plus the only gradient in the chrome. A gradient that
 * carries no meaning is decoration, and the one thing that separates this
 * product from a template is that colour here always means something. Flat
 * --navy reads more confident, not less, and it is the value every other navy
 * surface in the product already uses. */
.topbar{background:var(--navy);color:#fff}
.topbar .in{max-width:var(--shell);margin:0 auto;padding:0 var(--s-4);display:flex;align-items:center;
  gap:var(--s-4);min-height:52px;flex-wrap:wrap;position:relative}
.brand{display:flex;align-items:center;gap:var(--s-2);padding:var(--s-3) 0;flex:none}
.brand .dot{width:8px;height:8px;border-radius:50%;background:var(--brand)}
.brand b{font-family:var(--display);font-size:var(--fs-title);font-weight:700;letter-spacing:var(--tr-tight);color:#fff;white-space:nowrap}
.brand b .a{color:var(--brand-2)}

/* ---- the two axes ---------------------------------------------------------
 *
 * The bar used to flatten two different questions onto one row of tabs:
 * "which client am I looking at" (Accounts) sat beside "which part of this
 * client am I looking at" (Leads, Reporting, Team), as though they were peers.
 * They are not, and the account <select> on the right was a second control for
 * the first question that could disagree with the first one.
 *
 * Now there is one control per question, and they look like what they are:
 *
 *   brand  >  [ scope switcher ]   Leads  Reporting  Team        you
 *             ^ where am I         ^ what am I looking at
 *
 * The chevron between the brand and the switcher makes it read as a path, so
 * the switcher cannot be mistaken for a fourth tab. Operator-only actions
 * (Demo, adding a client, group wiring) left the bar entirely and live in the
 * user menu, because administration is not navigation.
 */

.crumb{color:rgba(255,255,255,.3);font-size:var(--fs-title);line-height:1;flex:none;margin:0 -8px 0 -6px;user-select:none}

/* The scope switcher. A button, not a select: it has to show a group above a
 * store, an attention count beside each row, and a search field, none of which
 * a native select can carry. */
.scope{display:flex;align-items:center;gap:var(--s-2);flex:none;max-width:min(46vw,320px);
  font-family:var(--display);font-size:var(--fs-body);font-weight:600;color:#fff;text-align:left;
  background:rgba(255,255,255,.09);border:1px solid rgba(255,255,255,.22);
  border-radius:var(--r-sm);padding:var(--s-1) var(--s-2);cursor:pointer;
  transition:background 140ms ease-out,border-color 140ms ease-out}
.scope:hover{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.38)}
.scope[aria-expanded=true]{background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.5)}
.scope:focus-visible{outline:2px solid #fff;outline-offset:1px}
.scope .sname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The group a store belongs to, above its name. A store admin never sees this,
 * because a store admin is never inside a group in the product's eyes. */
.scope .sgroup{display:block;font-size:var(--fs-label);font-weight:600;letter-spacing:var(--tr-label);
  text-transform:uppercase;color:rgba(255,255,255,.55);line-height:1.4}
.scope .schev{flex:none;opacity:.65;transition:transform 140ms ease-out}
.scope[aria-expanded=true] .schev{transform:rotate(180deg);opacity:1}
/* Unclaimed and escalated across everything the switcher can reach. Present
 * only when it is not zero, so an empty bar means nothing is waiting.
 * White on navy rather than the brand orange: at 10.5px this is small text, and
 * --brand is 3.38:1 against white, which is a fill colour and not a text one. */
.scope .sdot{flex:none;font-family:var(--display);font-size:var(--fs-label);font-weight:700;
  font-variant-numeric:tabular-nums;background:#fff;color:var(--ink-navy);
  border-radius:var(--r-pill);padding:1px var(--s-1);line-height:1.5}

.nav{display:flex;gap:2px;align-self:stretch;overflow-x:auto;scrollbar-width:none}
.nav::-webkit-scrollbar{display:none}
.nav a{display:flex;align-items:center;padding:0 var(--s-3);color:rgba(255,255,255,.75);
  text-decoration:none;font-family:var(--display);font-size:var(--fs-body);font-weight:600;
  border-bottom:2px solid transparent;white-space:nowrap;
  transition:color 140ms ease-out,border-color 140ms ease-out}
.nav a:hover{color:#fff}
.nav a[aria-current=page]{color:#fff;border-bottom-color:var(--brand-2)}
.nav a:focus-visible{outline:2px solid #fff;outline-offset:-2px}

.topbar .right{margin-left:auto;display:flex;align-items:center;gap:var(--s-2);padding:var(--s-2) 0}
.userbtn{font-family:var(--display);font-size:var(--fs-meta);font-weight:600;color:rgba(255,255,255,.85);
  background:none;border:0;padding:var(--s-1) var(--s-1);cursor:pointer}
.userbtn:hover{color:#fff}
.userbtn:focus-visible{outline:2px solid #fff;outline-offset:1px}

/* ---- the scope panel ------------------------------------------------------
 * position:fixed so it escapes the bar's stacking and any overflow above it,
 * anchored in JavaScript to the button it belongs to. */
.scrim{position:fixed;inset:0;z-index:70;background:transparent}
.spanel{position:fixed;z-index:71;width:min(360px,calc(100vw - 24px));
  background:#fff;color:var(--ink);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:var(--sh-modal);overflow:hidden;
  animation:spanel-in 150ms cubic-bezier(.16,1,.3,1)}
@keyframes spanel-in{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){
  .spanel{animation:none}
  .scope,.scope .schev,.nav a{transition:none}
}
.spanel .sfind{padding:var(--s-2) var(--s-2);border-bottom:1px solid var(--line)}
.spanel .sfind input{width:100%;font-family:var(--sans);font-size:var(--fs-body);color:var(--ink);
  border:1px solid var(--line-strong);border-radius:var(--r-sm);padding:var(--s-2) var(--s-2);background:#fff}
.spanel .sfind input:focus-visible{outline:2px solid var(--blue-d);outline-offset:-1px;border-color:var(--blue-d)}
.spanel .slist{max-height:min(56vh,420px);overflow-y:auto;padding:var(--s-1)}
.spanel .shead{font-family:var(--display);font-size:var(--fs-label);font-weight:600;text-transform:uppercase;
  letter-spacing:var(--tr-label);color:var(--faint);padding:var(--s-2) var(--s-2) var(--s-1)}
.spanel .srow{display:flex;align-items:center;gap:var(--s-2);width:100%;text-align:left;
  background:none;border:0;cursor:pointer;padding:var(--s-2) var(--s-2);border-radius:var(--r-sm);
  font-family:var(--sans);font-size:var(--fs-body);color:var(--ink)}
.spanel .srow:hover,.spanel .srow.on{background:var(--n-50)}
/* Keyboard position is a distinct state from the current scope: one is where
 * the arrow keys are, the other is where you already are. They can be on
 * different rows at the same time and must not look identical. */
.spanel .srow.on{box-shadow:inset 0 0 0 1px var(--line-strong)}
.spanel .srow:focus-visible{outline:2px solid var(--blue-d);outline-offset:-2px}
.spanel .srow .sn{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.spanel .srow.child .sn{padding-left:var(--s-3);position:relative}
.spanel .srow.child .sn::before{content:"";position:absolute;left:4px;top:50%;width:6px;height:1px;background:var(--n-300)}
.spanel .srow .sc{flex:none;font-family:var(--display);font-size:var(--fs-label);font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--faint)}
.spanel .srow .sc.hot{color:var(--status-alert-txt)}
.spanel .srow .stick{flex:none;color:var(--blue-d);font-weight:700}
.spanel .sempty{padding:var(--s-5) var(--s-3);text-align:center;font-size:var(--fs-body);color:var(--mut)}
.spanel .sfoot{border-top:1px solid var(--line);padding:var(--s-2) var(--s-3);font-size:var(--fs-meta);color:var(--faint);
  display:flex;justify-content:space-between;gap:var(--s-2)}
.spanel kbd{font-family:var(--sans);font-size:var(--fs-label);background:var(--n-50);
  border:1px solid var(--line);border-radius:3px;padding:1px var(--s-1);color:var(--mut)}

/* user menu: mobile number, two-factor and sign out live here, off the board */
.menu{position:absolute;right:20px;top:48px;background:#fff;border:1px solid var(--line);
  border-radius:var(--r-md);box-shadow:var(--sh-modal);min-width:270px;z-index:60;padding:var(--s-1);
  color:var(--ink);text-align:left}
.menu .mrow{padding:var(--s-2) var(--s-3);font-size:var(--fs-body);color:var(--ink);border-radius:var(--r-sm)}
.menu .mrow .lbl{font-family:var(--display);font-size:var(--fs-label);font-weight:600;text-transform:uppercase;
  letter-spacing:var(--tr-label);color:var(--faint);display:block;margin-bottom:3px}
.menu button.mact{display:block;width:100%;text-align:left;background:none;border:0;
  padding:var(--s-2) var(--s-3);font-family:var(--sans);font-size:var(--fs-body);color:var(--ink);cursor:pointer;border-radius:var(--r-sm)}
.menu button.mact:hover{background:var(--n-50)}
.menu hr{border:0;border-top:1px solid var(--line);margin:var(--s-1) 0}

/* ---- page scaffold -------------------------------------------------------- */
.wrap{max-width:var(--shell);margin:0 auto;padding:var(--s-4) var(--s-4) 64px}

/* ---- the two-zone grid ----------------------------------------------------
 * The recovered width buys STRUCTURE, not a wider reading measure. Content on
 * the left, a rail on the right for the figures that were stacked above the
 * work: on Leads that is nine KPI tiles occupying about 500px before the first
 * lead row, on a screen where a 1280x720 laptop showed two leads.
 *
 * min-width:0 ON THE CONTENT COLUMN IS LOAD BEARING, and it is the same trap
 * .tblwrap already documents. A grid item defaults to min-width:auto, which
 * refuses to shrink below its content's intrinsic minimum, so a wide table
 * inside one pushes the whole track past the viewport and the PAGE scrolls
 * sideways instead of the table doing it inside itself.
 *
 * Two breakpoints, and each is where the rail stops earning its width rather
 * than a round number. Below 1240px the rail can no longer hold a figure and
 * its label side by side, so it becomes a horizontal strip above the content.
 * Below 900px there is no width to spend on structure at all and the strip
 * folds away, which is correct on a phone: everything in the rail is also
 * stated on the screen it summarises. */
.zones{display:grid;grid-template-columns:minmax(0,1fr) var(--rail);gap:var(--s-5);align-items:start}
.zones>.zmain{min-width:0}
.zones>.zrail{position:sticky;top:var(--s-4);display:grid;gap:var(--s-4);min-width:0}
@media (max-width:1240px){
  .zones{grid-template-columns:minmax(0,1fr)}
  .zones>.zrail{position:static;grid-row:1;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);
    overflow-x:auto;scrollbar-width:none}
  .zones>.zrail::-webkit-scrollbar{display:none}
}
@media (max-width:900px){
  .zones>.zrail{display:none}
}
/* More space BELOW a page title than above it, and more than there was. The
   heading and the first card were 12px apart, which is the same distance as two
   rows inside a table: the title read as part of the card under it rather than
   as the name of the page. 24px is the between-groups step and this is the
   largest group boundary on any screen. */
.pagehead{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-3);
  flex-wrap:wrap;margin:0 0 var(--s-5)}
.pagehead h1{font-family:var(--display);font-size:var(--fs-page);font-weight:var(--fw-bold);color:var(--navy);
  margin:0;letter-spacing:var(--tr-tight);line-height:var(--lh-tight)}
.pagehead .sub{font-size:var(--fs-meta);color:var(--mut)}
.back{display:inline-block;margin:0 0 var(--s-2);color:var(--blue-d);text-decoration:none;font-size:var(--fs-body);cursor:pointer}
.back:hover{text-decoration:underline}

/* overflow:hidden IS THE MOST VISIBLE "NOT SHARP" DEFECT IN THE PRODUCT, AND IT
 * IS ONE DECLARATION. The card has a 12px radius and nothing clipping to it,
 * while .kpis right below already has both. So every row hover, every open-row
 * background and every test-row hatch painted a SQUARE rectangle over the card's
 * rounded corner: the first and last rows of all thirteen tables in the product
 * cut the corner off the card containing them, on hover, which is exactly when
 * somebody is looking at it. */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden}
/* A column of cards about separate subjects. Team stacks seven of them and each
 * one was carrying its own inline margin-top, which is a layout number set in
 * seven places and an inline style that beats every media query under it. The
 * gap is wider than the 14px used between cards that belong together, because
 * nobody scans this page the way they scan the board and seven subjects at
 * 14px read as one long card.
 *
 * :empty IS LOAD BEARING. Half of these are placeholder divs an async load
 * fills in later, and several of them stay empty for good on a client admin's
 * session. A grid gap is drawn around an empty item and not around a
 * display:none one, so without this line the page grows a run of dead space
 * wherever a card decided not to exist. */
.stack{display:grid;gap:var(--s-4);min-width:0}
.stack>*{min-width:0}
.stack>*:empty{display:none}
.card .chead{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);flex-wrap:wrap;
  padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--line)}
/* A card title used to be 14px bold, which is the body size: the only things
   separating a heading from the paragraph under it were weight and colour. It
   moves up a step on the scale and drops a weight, so it gains hierarchy
   without gaining heaviness on a board that stacks six cards down a page. */
.card .chead h2{font-family:var(--display);font-size:var(--fs-title);font-weight:var(--fw-semi);
  margin:0;color:var(--navy);letter-spacing:var(--tr-tight);line-height:var(--lh-tight)}
.card .cbody{padding:var(--s-4)}
/* ROOM ABOVE, AND IT HAD NONE. This paragraph sits directly under the card
   head's bottom rule and directly above a table's top rule, and its top padding
   was zero, so the text was pressed against a hairline with about three pixels
   of leading standing in for space. Text touching a border is the thing that
   makes a dense layout read as cramped rather than as tight, and it is the one
   Chirag pointed at on two separate cards.
   Twelve above and sixteen below, which is the reference mock's own value and
   the rule the rest of this system already follows: more space between groups
   than inside one, and the rule above this paragraph belongs to the heading
   while the space below it belongs to the table. */
.card .factline{padding:var(--s-3) var(--s-4) var(--s-4)}

/* ---- KPI strip: one card, internal hairline dividers, number is the hero -- */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(128px,1fr));
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);
  overflow:hidden;margin:0 0 var(--s-3)}
.kpi{padding:var(--s-3) var(--s-4) var(--s-3);border-right:1px solid var(--line);min-width:0}
.kpi:last-child{border-right:0}
.kpi b{font-family:var(--display);font-size:var(--fs-stat);font-weight:var(--fw-semi);color:var(--navy);
  display:block;line-height:var(--lh-tight);letter-spacing:var(--tr-tighter);font-variant-numeric:tabular-nums}
/* A duration reads as a word, not a figure, so it takes the step below the
   number it sits beside rather than being set at the same size and wrapping. */
.kpi b.txt{font-size:var(--fs-page);padding-top:var(--s-1)}
.kpi span{font-size:var(--fs-label);font-weight:var(--fw-semi);letter-spacing:var(--tr-label);
  text-transform:uppercase;color:var(--faint)}

/* ---- tables --------------------------------------------------------------- */
/* min-width:0 is what makes overflow-x:auto mean anything here. A grid or flex
   item defaults to min-width:auto, which refuses to shrink below its content's
   minimum, so a wide table inside one pushes the whole track past the viewport
   and the page scrolls sideways instead of the table doing it. */
.tblwrap{overflow-x:auto;min-width:0}
table{width:100%;border-collapse:collapse;font-size:var(--fs-body);line-height:1.35}
/* table-layout:fixed GOES WITH A COLGROUP AND IS HALF OF WHAT MAKES IT WORK.
 * Under the default auto layout the browser reads every cell in the table before
 * deciding the columns, so the longest value in any single row sets the
 * positions for all of them: one customer with a long name shifts Source, Status
 * and Owner across the whole table, and a board that pages at 200 rows changes
 * shape as somebody pages through it. Fixed takes the widths from the colgroup
 * and stops reading. It also stops the table growing past its wrapper, which is
 * what .tblwrap's overflow-x is there to catch. Only on tables that declare a
 * colgroup, because a fixed table without one distributes width evenly and that
 * is worse than auto. */
table.board{table-layout:fixed}
table.board td{overflow:hidden;text-overflow:ellipsis}

/* THE HEADER NEEDS A GROUND, AND THIS IS THE SINGLE CHANGE THAT MAKES A TABLE
 * READ AS ONE. th and td used to share identical padding and an identical
 * border, so the header row was a body row that happened to hold different
 * words: nothing separated the labels from the data, and thirteen tables all
 * started with a row that was neither.
 *
 * SENTENCE CASE, NOT SMALL CAPS. Small caps survive in this system only where
 * they mark structure, meaning a section kicker. A column header is a label on
 * the thing under it, and an eyebrow above every column is grammar nobody chose.
 * The tracking goes with the case: --tr-label exists to keep uppercase legible
 * and does nothing but loosen sentence case. */
thead th{background:var(--n-50);border-bottom:1px solid var(--n-200)}
th{font-family:var(--display);font-size:var(--fs-meta);letter-spacing:normal;text-transform:none;
  color:var(--n-600);text-align:left;padding:var(--s-2) var(--s-3);font-weight:var(--fw-semi);
  border-bottom:1px solid var(--line);white-space:nowrap}

/* STICKY, BECAUSE THE BOARD PAGES AT 200 ROWS. There was no position:sticky
 * anywhere in the codebase, so scrolling past row twenty left somebody reading
 * six unlabelled columns of names, times and statuses. z-index:2 clears the row
 * hover background, which is the only thing that paints over it. */
.tblwrap thead th{position:sticky;top:0;z-index:2}

td{padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--line);vertical-align:top}
tr:last-child td{border-bottom:0}

/* ---- THE FOUR PIXELS THAT MADE EVERY CARD LOOK HAND ASSEMBLED --------------
 *
 * A card's heading pays 16px (.chead), its explanatory paragraph pays 16px
 * (.card .factline), and its table paid 12px, because a cell's padding is tuned
 * for the space BETWEEN columns and was doing double duty as the gutter at the
 * edge of the card. So in every card in the product that stacks a heading, a
 * sentence and a table, which is most of them, the first column started four
 * pixels to the left of the two blocks above it.
 *
 * Four pixels is beneath noticing on its own and that is exactly why it
 * survived: nobody can name it, and everybody sees that nothing quite lines up.
 * It is the single most repeated version of the defect Chirag pointed at, and
 * it appears on the board, the roll-up, Team, Reporting, Group, the connection
 * picker, the delivery log and the notification table.
 *
 * FIRST AND LAST CHILD ONLY, so the rhythm BETWEEN columns is untouched and
 * only the two edges that meet the card move. Setting the whole cell to 16px
 * would have widened every gap in every table to fix an edge.
 *
 * The phone override below is written the same way and for the same reason: it
 * tightens cells to 8px to keep a table on screen, and these two lines keep the
 * outer edge on the card's own gutter while it does. */
th:first-child,td:first-child{padding-left:var(--s-4)}
th:last-child,td:last-child{padding-right:var(--s-4)}
/* The row's subject: a name, a client, a connection. WEIGHT ONLY, NOT COLOUR.
   It was navy at 500 while every other cell was --ink at 400, so the same
   consultant's name was navy in the table and ink in the select below it: one
   value, two treatments, ten pixels apart. Navy is this product's link colour
   and a cell that is not a link should not wear it. Medium weight is enough to
   mark which column is the subject of the row, and it leaves the name looking
   like the same name everywhere it appears. */
td.n{color:var(--ink);font-weight:var(--fw-med)}
tr.rowlink{cursor:pointer}
tr.rowlink:hover td{background:var(--n-25)}
/* Eight tables carry tabindex="0" rows and had nothing but the browser default,
 * which on a row with a background change is close to invisible. Inset, so the
 * ring is drawn inside the row rather than over the row above it. */
tr.rowlink:focus-visible{outline:2px solid var(--navy);outline-offset:-2px}
td a{color:var(--blue-d);text-decoration:none}
td a:hover{text-decoration:underline}
.faint{color:var(--faint)}.mut{color:var(--mut)}
/* TWO CLASSES, BECAUSE ALIGNMENT AND FIGURES ARE TWO DIFFERENT DECISIONS AND
 * .num WAS DOING BOTH BADLY.
 *
 * A figure or a timestamp is one token and must never break across two lines:
 * "27/07/2026" above "07:52" reads as two values. That is what .num is for and
 * it stays LEFT, because it carries phone numbers, timestamps and identifiers,
 * every one of which is read left to right like words.
 *
 * .qty is the other half. There was no text-align:right anywhere in a product
 * sold on numbers, so every count, every duration and every credit balance was
 * left aligned and none of them could be compared down a column. Right aligning
 * .num wholesale would have taken the phone numbers with it, which is why this
 * is a second class rather than an edit to the first. */
.num{font-variant-numeric:tabular-nums;white-space:nowrap;text-align:left}
.qty{font-variant-numeric:tabular-nums;white-space:nowrap;text-align:right}
th.qty{text-align:right}
/* A heading, one line, one action. An empty state is the only thing on the screen
   when it shows, so it is the one place where saying nothing useful costs most:
   the reader has arrived somewhere blank with no idea whether that is a problem.
   The <b> is the state and .esub is what to do about it. */
.emptycell{padding:var(--s-5) var(--s-3);text-align:center;color:var(--mut)}
.emptycell b{display:block;color:var(--ink);font-weight:var(--fw-semi);font-size:var(--fs-body)}
.emptycell .esub{display:block;font-size:var(--fs-meta);color:var(--mut);
  margin-top:var(--s-1);max-width:52ch;margin-left:auto;margin-right:auto}

/* ---- pills and the test tag ----------------------------------------------- */
.pill{display:inline-flex;align-items:center;gap:var(--s-1);font-family:var(--display);font-size:var(--fs-label);
  font-weight:600;border-radius:var(--r-pill);padding:2px var(--s-2);white-space:nowrap}
.pill::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
.pill.new{background:var(--status-new-bg);color:var(--status-new-txt)}
.pill.ok{background:var(--status-active-bg);color:var(--status-active-txt)}
.pill.alert{background:var(--status-alert-bg);color:var(--status-alert-txt)}
.pill.off{background:var(--status-off-bg);color:var(--status-off-txt)}
/* Work in progress. Navy on the neutral ramp rather than a fifth status colour:
   the four above are about a lead, this one is about a piece of work, and it
   must not read as a lead state. Added for the roadmap; it belongs here rather
   than in one page's <style> or the next page to need it declares it again. */
.pill.work{background:var(--n-100);color:var(--navy)}
/* Appointment booked: the last stage of the sales cycle and the only good news
 * on the board, so it is the one pill allowed to use the brand. Navy fill with
 * white text rather than a fifth pale background, because the four pale ones
 * are all about a lead's problems and this one is about it being finished.
 * White on --navy is 15.4:1. */
.pill.book{background:var(--navy);color:#fff}
.pill.book::before{background:var(--brand-2)}
/* Test data marker. Dashed border and diagonal hatching: nothing else in the
 * system is dashed or striped, so it can never be read as a status. */
.tag-test{display:inline-block;font-family:var(--display);font-size:var(--fs-label);font-weight:700;
  letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--n-600);
  border:1px dashed var(--n-400);border-radius:4px;padding:1px var(--s-1);
  background:repeating-linear-gradient(135deg,var(--n-50) 0 4px,#fff 4px 8px)}
tr.testrow td{background:repeating-linear-gradient(135deg,rgba(15,23,51,.025) 0 6px,transparent 6px 12px)}

/* ---- filter tabs (quiet text tabs above a table) --------------------------- */
.ftabs{display:flex;gap:2px;flex-wrap:wrap}
.ftabs button{font-family:var(--sans);font-size:var(--fs-meta);font-weight:500;padding:var(--s-1) var(--s-2);
  border:0;background:none;color:var(--mut);cursor:pointer;border-radius:var(--r-sm)}
.ftabs button:hover{color:var(--ink);background:var(--n-50)}
.ftabs button[aria-pressed=true]{background:var(--n-100);color:var(--navy);font-weight:600}
.ftabs button:focus-visible{outline:2px solid var(--navy);outline-offset:-1px}
.ftabs .count{color:var(--faint);font-variant-numeric:tabular-nums;margin-left:var(--s-1)}

/* ---- segmented control ----------------------------------------------------- */
/* max-width plus scroll rather than flex:none. A four option control at its
   natural width is wider than a phone, and a segmented control that sets the
   width of the page is worse than one you swipe. */
.seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--r-sm);overflow-x:auto;
  max-width:100%;flex:none;scrollbar-width:none}
.seg::-webkit-scrollbar{display:none}
.seg button{flex:none;white-space:nowrap}
/* SIZED FOR THE DECISION IT CARRIES, NOT FOR THE SPACE IT TAKES.
 *
 * This was 12.5px type with 4px of vertical padding, giving a 23px control. On
 * the Integrations page that is the button somebody presses to say which CRM
 * their business runs, which is the first real decision in the whole setup and
 * the one every following step depends on. It was the smallest interactive
 * thing on the screen.
 *
 * 23px also failed two thresholds it should never have been near: 24px is the
 * minimum target size at AA, and 44px is what a finger needs. The coarse
 * pointer block below is keyed on the device rather than the window width, the
 * same way the 16px input floor in tokens.css is, and for the same reason: a
 * narrow desktop window is not a phone and should not be treated as one. */
.seg button{font-family:var(--sans);font-size:var(--fs-body);font-weight:var(--fw-med);
  padding:var(--s-2) var(--s-4);border:0;
  background:var(--n-0);color:var(--n-600);cursor:pointer;border-right:1px solid var(--line)}
.seg button:last-child{border-right:0}
.seg button:hover{background:var(--n-50);color:var(--navy)}
.seg button[aria-pressed=true]{background:var(--navy);color:#fff}
.seg button[aria-pressed=true]:hover{background:var(--navy-2);color:#fff}
.seg button:focus-visible{outline:2px solid var(--navy);outline-offset:-2px}
@media (pointer:coarse){
  .seg button{padding:var(--s-3) var(--s-4);min-height:44px}
}

/* ---- buttons: sizes here, the colour hierarchy comes from tokens.css ------- */
button{font-family:var(--display);font-size:var(--fs-body);padding:var(--s-2) var(--s-4)}
button.sm{padding:var(--s-1) var(--s-3);font-size:var(--fs-meta)}
button.danger{background:#fff;color:var(--status-alert-txt);border:1px solid var(--status-alert-line)}
button.danger:hover{background:var(--status-alert-bg);border-color:var(--status-alert-txt)}
button:focus-visible{outline:2px solid var(--navy);outline-offset:1px}

/* ---- ONE FILLED BUTTON PER SCREEN, AND IT IS A RULE, NOT 116 EDITS --------
 *
 * tokens.css makes a bare `button` navy filled and calls it the secondary
 * treatment. That is the right default for the one or two utility actions on a
 * form. It is the wrong default for a control that repeats: every unclaimed
 * row on the board carries a Claim, so a 900px viewport showed roughly 18
 * filled navy buttons stacked in one column, and Team put 5 filled, 29
 * outlined and 9 red into one page. Filled is a rank. A rank only means
 * anything while one thing holds it.
 *
 * So a button in a table cell, in a row action group or in a card header is
 * demoted here, once, and every page inherits it. Filled navy is left to a
 * page head and to a form's own submit. The lead detail screen is the
 * reference for what that looks like: exactly one filled action in the head
 * and nothing competing with it.
 *
 * DIRECT CHILDREN ONLY, and that is what keeps the exclusion list to four
 * names. The card's info button lives inside its <h2> and the filter tabs live
 * inside .ftabs, so neither is a child of .chead and neither is touched. The
 * four classes named below already declare their own appearance: .btn-primary
 * is the one orange conversion action, .danger is the outlined red, and
 * .pillbtn and .hoursbtn are a pill and a link that happen to be buttons.
 *
 * button.ghost joins the same rule rather than sitting a shade lighter. Its
 * edge was --line at 1.35:1, defensible while a ghost button was the rare one;
 * it is now the common treatment across the product, so it takes --line-strong,
 * which tokens.css defines as the boundary of anything operable. One quiet
 * treatment, not two that are nearly the same.
 */
button.ghost,
td>button:not(.btn-primary):not(.danger):not(.pillbtn):not(.hoursbtn),
.rowacts>button:not(.btn-primary):not(.danger):not(.pillbtn):not(.hoursbtn),
.card>.chead>button:not(.btn-primary):not(.danger):not(.pillbtn):not(.hoursbtn){
  background:var(--n-0);color:var(--navy);border-color:var(--line-strong)}
/* THE HOVER FILLS NAVY, AND THAT IS THE OTHER HALF OF THE DEMOTION.
 * The rule above is correct and the rule below used to undo most of it: a demoted
 * button hovered to --n-50 with a navy edge, which is a border change and a 2%
 * background change. On the board that is the product's core action, repeated
 * once per unclaimed row, and there was no filled button anywhere on the screen
 * for it to be quieter than. Quiet at rest and unmistakably the action under the
 * pointer is what a demotion is supposed to buy. Filling on hover costs nothing
 * at rest, because a pointer is on one row at a time. */
button.ghost:hover,
td>button:not(.btn-primary):not(.danger):not(.pillbtn):not(.hoursbtn):hover,
.rowacts>button:not(.btn-primary):not(.danger):not(.pillbtn):not(.hoursbtn):hover,
.card>.chead>button:not(.btn-primary):not(.danger):not(.pillbtn):not(.hoursbtn):hover{
  background:var(--navy);color:#fff;border-color:var(--navy)}

/* ---- forms ----------------------------------------------------------------- */
label{font-family:var(--display);font-size:var(--fs-label);font-weight:600;text-transform:uppercase;
  letter-spacing:var(--tr-label);color:var(--faint);display:block;margin:0 0 var(--s-1)}
/* THE BORDER IS --line-strong, NOT --line, AND tokens.css ALREADY SAID SO.
   Its own comment reads "A card edge is decoration, so this is fine there and
   wrong on a control", with --line-strong described as "the boundary of anything
   operable". This rule used --line anyway, which put every text input and select
   in the console on a 1.35:1 edge against a 3:1 requirement. The customer connect
   page got this right by declaring --line-strong itself, so the shared rule was
   the only place still failing, on eleven screens. One token reference. */
/* EVERY CONTROL, NOT `input` AND `select.inp`. This rule named one class of
 * select and left the bare ones to the browser, so half the dropdowns in the
 * product rendered in the operating system's own UI font, at the operating
 * system's own size, in the operating system's own link blue, next to inputs
 * set in Inter at 16px in --ink. On the client onboarding form that is four
 * fields in the house face and two that are visibly not, on the same row.
 *
 * A form control that keeps its platform default beside one that does not is
 * the single loudest tell that a page was assembled rather than designed, and it
 * is the thing Chirag pointed at. It is one selector.
 *
 * appearance:none IS WHAT MAKES THE FONT STICK. Without it the platform keeps
 * ownership of the control and several of these declarations are advisory: on
 * Windows the native select ignores font-family outright. Turning the native
 * appearance off means the chevron has to be drawn, which is the background-image
 * below: an inline SVG data URI in --n-500, so it needs no network request and
 * no entry in the CSP. */
/* ONE HEIGHT FOR EVERY CONTROL, AND THIS IS THE OTHER HALF OF THE SAME DEFECT.
 * Even after the font was shared, a select measured 39px and a text input 41px
 * on the SAME ROW of the same form, because the two have different intrinsic
 * content heights for identical padding. Two pixels is invisible on its own and
 * fatal in a row: it is exactly the 13px-against-14px problem the spacing scale
 * was built to end, and it is why a form of mixed control types never looks
 * set. An explicit min-height makes the row a row. 40px is the value the six
 * inline style="height:40px" buttons were already using by hand. */
/* The size comes from tokens.css, which keys the iOS 16px floor to a coarse
   pointer rather than to every device. Setting it again here would undo that. */
input,select,textarea{width:100%;font-family:var(--sans);
  padding:var(--s-2) var(--s-3);border:1px solid var(--line-strong);border-radius:var(--r-sm);
  color:var(--ink);background:#fff;line-height:var(--lh-snug)}
/* A FIXED height, not a minimum, on everything that holds one line. min-height
   left a date field at 41px next to a select at 40px, because a date input
   carries a native picker indicator that sets its own intrinsic height and a
   minimum cannot pull it back down. Checkboxes and radios are excluded because
   they are a shape rather than a field. */
input:not([type=checkbox]):not([type=radio]),select{height:40px}
textarea{height:auto;min-height:76px}
/* The submit that sits at the end of a field row, aligned to the controls beside
   it rather than to the label above them. Fifteen call sites were carrying
   style="height:40px" to say this, which is a layout number in fifteen places
   and an inline style no media query can reach. */
.repform button{min-height:40px}
select{
  appearance:none;-webkit-appearance:none;-moz-appearance:none;
  /* Room on the right for the chevron, or a long option runs underneath it. */
  padding-right:var(--s-6);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%235b6577' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right var(--s-3) center;
  cursor:pointer}
/* The list itself is drawn by the operating system and cannot be styled, but the
   colour of the options can be, which stops a dark mode browser rendering white
   on white. */
select option{color:var(--ink);background:#fff}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--navy);box-shadow:var(--focus-ring)}
input:disabled,select:disabled,textarea:disabled{background:var(--n-50);color:var(--mut);cursor:not-allowed}
/* A date and a time field render their own spinners and separators in the
   platform face. They cannot be fully restyled, but the value itself follows the
   rest once the font is set on the control. */
input[type=date],input[type=time]{font-family:var(--sans);font-variant-numeric:tabular-nums}
/* A placeholder is not body text: it is a hint, and at --ink it reads as a value
   somebody already typed. This is the one place --faint is correct on a control,
   and it passes AA against white at 4.80:1. */
input::placeholder,textarea::placeholder{color:var(--faint);opacity:1}

/* ---- info button ------------------------------------------------------------
 * 24px, not 16px. There are 25 of these and 16px is below the 24px WCAG 2.2 AA
 * minimum on its own, before considering that this product's main surface is a
 * phone. The border moves to --line-strong for the same reason every other
 * operable edge did: tokens.css says --line is 1.35:1 and is "wrong on a
 * control", in its own words, and this rule used it anyway. */
.i{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;
  border-radius:50%;border:1px solid var(--line-strong);background:#fff;color:var(--mut);
  font-family:var(--display);font-size:var(--fs-label);font-weight:700;cursor:pointer;padding:0;
  vertical-align:middle;margin-left:var(--s-1);line-height:1;flex:none}
.i:hover{border-color:var(--navy);color:var(--navy)}
.i:focus-visible{outline:2px solid var(--navy);outline-offset:1px}
/* Unread: a key this person has not opened. The brand edge and a 4px dot, and
   nothing else, because this is a nudge and not a notification. */
.i.unread{border-color:var(--brand-solid);color:var(--brand-solid);position:relative}
.i.unread::after{content:"";position:absolute;top:-1px;right:-1px;width:4px;height:4px;
  border-radius:50%;background:var(--brand)}

/* A heading and its infotip, side by side. The button is a SIBLING of the
   heading and never a child of it: inside an <h2> it becomes part of the
   heading's accessible name, which is how twelve headings came to announce
   themselves with a letter i on the end. */
.ctitle{display:flex;align-items:center;gap:var(--s-1);min-width:0}
.ctitle>h1,.ctitle>h2,.ctitle>h3{min-width:0}

/* ---- the infotip popover ----------------------------------------------------
 * NO SCRIM, and that is the point of it. A dimmed page and a centred card is
 * what this product uses to ask "remove this consultant". Using the same
 * treatment to say what the word Age means teaches people that the small circle
 * is a commitment, which is why nobody pressed it. This is anchored to the thing
 * it explains and the page stays readable behind it. */
.infopop{position:fixed;z-index:120;width:320px;max-width:calc(100vw - 16px);
  background:var(--card);border:1px solid var(--line-strong);border-radius:var(--r-md);
  box-shadow:var(--sh-modal);padding:var(--s-3) var(--s-4) var(--s-3)}
.infopop h2{font-family:var(--display);font-size:var(--fs-body);font-weight:var(--fw-semi);
  color:var(--navy);margin:0 0 var(--s-1);letter-spacing:var(--tr-tight);line-height:var(--lh-tight)}
.infopop p{font-size:var(--fs-meta);color:var(--ink);line-height:var(--lh-body);margin:0 0 var(--s-3)}

/* ---- coarse pointer ---------------------------------------------------------
 * There was no @media (pointer:coarse) block anywhere in the product, and this
 * is a tool people use one handed in a car park. Measured before: .i 16px,
 * .switch 19px, .hoursbtn about 17px, .pillbtn about 21px, and button.sm about
 * 29px including the board's own Claim and Call.
 *
 * The query is on the POINTER, not the viewport width. A tablet at 1024px has a
 * finger on it and a laptop at 900px does not, so keying this to a breakpoint
 * would give the 44px to the wrong device in both directions. */
@media (pointer:coarse){
  button,.i,.hoursbtn,.pillbtn{min-height:44px}
  .i{width:44px;height:44px}
  /* THE SWITCH GROWS ITS HIT AREA, NOT ITS BODY, and that distinction is the
   * whole rule. min-height on .switch would work on any normal element and
   * breaks this one: .track is position:absolute with inset:0, so a 44px switch
   * gets a 44px tall track and the control stops looking like a switch. The
   * input is already an invisible overlay covering the track, so stretching
   * that instead gives a 45px target around a control that still reads as
   * 19px. height:auto is required because inset:0 is paired with height:100%,
   * which would otherwise pin it back to the track. */
  .switch input{top:-13px;bottom:-13px;height:auto}
}
/* ---- the dialog -------------------------------------------------------------
 * A real head with a real <h2> and a real close control, because ten of the
 * thirteen hand rolled dialogs this replaces used a bare <p> as their title. A
 * paragraph is not a heading: it is not in the document outline, it is not what
 * aria-labelledby should point at, and it gives a screen reader nothing to
 * announce the dialog as.
 *
 * The padding moved off .mc and onto .mhead and .mbody so the head can carry a
 * rule under it without the body's padding pushing that rule inwards. */
.modal{position:fixed;inset:0;background:rgba(15,23,51,.45);display:flex;align-items:center;
  justify-content:center;padding:var(--s-4);z-index:100}
.modal .mc{background:#fff;border-radius:var(--r-md);max-width:380px;width:100%;
  box-shadow:var(--sh-modal);max-height:calc(100vh - var(--s-6));display:flex;flex-direction:column}
.modal .mc.wide{max-width:640px}
.modal .mhead{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-3);
  padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--line);flex:none}
.modal .mhead h2{font-family:var(--display);font-size:var(--fs-title);font-weight:var(--fw-semi);
  margin:0;color:var(--navy);letter-spacing:var(--tr-tight);line-height:var(--lh-tight)}
/* 44px, and it is the one control in a dialog somebody reaches for without
   reading. Transparent border so it does not read as a third button beside the
   pair in the footer. */
.modal .mx{width:44px;height:44px;flex:none;margin:calc(var(--s-2) * -1) calc(var(--s-2) * -1) 0 0;
  background:none;border:1px solid transparent;color:var(--mut);
  font-size:var(--fs-body);line-height:1;padding:0;cursor:pointer;border-radius:var(--r-sm)}
.modal .mx:hover{background:var(--n-50);color:var(--ink);border-color:var(--line-strong)}
.modal .mx:focus-visible{outline:2px solid var(--navy);outline-offset:1px}
.modal .mbody{padding:var(--s-4);overflow-y:auto;min-height:0}
.modal .mbody>:first-child{margin-top:0}
.modal .mbody>:last-child{margin-bottom:0}
.modal .mc p{font-size:var(--fs-body);color:var(--ink);margin:0 0 var(--s-3);line-height:1.55}
/* The action row every dialog ends with, named once instead of an inline
   display:flex on each of thirteen. */
.modal .macts{display:flex;gap:var(--s-2);justify-content:flex-end;flex-wrap:wrap}

/* ---- toast and spinner ------------------------------------------------------ */
.toast{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);background:var(--navy);
  color:#fff;font-size:var(--fs-body);padding:var(--s-3) var(--s-4);border-radius:var(--r-sm);opacity:0;
  transition:opacity .2s;z-index:150;box-shadow:var(--sh-modal);pointer-events:none}
.toast.show{opacity:1}
.toast.err{background:var(--status-alert-txt)}
.spin{width:24px;height:24px;border:3px solid var(--line);border-top-color:var(--brand-solid);
  border-radius:50%;animation:sp .8s linear infinite;margin:var(--s-5) auto}
@keyframes sp{to{transform:rotate(360deg)}}

/* ---- bars, in CSS, so the labels are real text at a real size ---------------
 * Fill --navy, no radius, no gradient, no gridline, one 1px baseline in --line,
 * a direct value label on the tallest bar only.
 *
 * WHY THIS IS NOT AN SVG ANY MORE: the old one had a fixed 700 wide viewBox
 * stretched to the container, so every glyph inside it scaled with the page.
 * An 11px axis label rendered at roughly 25px on a wide monitor and about 6px
 * on a phone. Text that is a function of the reader's monitor is not typography,
 * and the fix is not a different number, it is taking the words out of the
 * scaled coordinate system altogether. barChart in index.html carries the long
 * version of this. */
.cbars{display:block}
.cplot{display:grid;grid-template-columns:repeat(var(--n,7),minmax(0,1fr));
  align-items:end;gap:2px;height:156px;border-bottom:1px solid var(--line);padding-bottom:0}
/* The COLUMN is the hit area and the bar sits inside it, so a zero value is
   still pointable and still shows its figure on hover. */
.cbar{position:relative;display:flex;align-items:flex-end;justify-content:center;
  height:100%;min-width:0;cursor:default}
.cbar>i{display:block;width:min(66%,42px);background:var(--navy);transition:background 120ms ease-out}
.cbar:hover>i{background:var(--brand-solid)}
.cbar .cval{position:absolute;top:0;left:0;right:0;text-align:center;
  font-family:var(--sans);font-size:var(--fs-label);font-weight:var(--fw-semi);color:var(--n-600)}
.cax{display:grid;grid-template-columns:repeat(var(--n,7),minmax(0,1fr));gap:2px;
  padding-top:var(--s-1);font-family:var(--sans);font-size:var(--fs-label);color:var(--n-400)}
.cax>span{min-width:0;overflow:hidden;white-space:nowrap;text-align:center}
/* The end labels anchor to the edges instead of centring on their slot, which
   is what keeps the last one inside the card at any width. */
.cax>span:first-child{text-align:left}
.cax>span:last-child{text-align:right}
@media(prefers-reduced-motion:reduce){ .cbar>i{transition:none} }

/* ranked label / track / value rows */
.brows{display:grid;gap:var(--s-2)}
.brow{display:grid;grid-template-columns:minmax(84px,30%) 1fr auto;gap:var(--s-2);align-items:center;font-size:var(--fs-meta)}
.brow .k{color:var(--n-600);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.brow .t{background:var(--n-100);height:9px;overflow:hidden;border-radius:2px}
.brow .f{background:var(--navy);height:100%;display:block}
.brow .v{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums;min-width:26px;text-align:right}

/* text-first list, used below the sparse threshold */
.tlist{margin:0;padding:0;list-style:none}
.tlist li{display:flex;justify-content:space-between;gap:var(--s-3);padding:var(--s-2) 0;
  border-bottom:1px solid var(--line);font-size:var(--fs-body)}
.tlist li:last-child{border-bottom:0}
.tlist .k{color:var(--n-600)}
.tlist .v{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}

/* ---- empty states ----------------------------------------------------------- */
.empty{padding:var(--s-6) var(--s-4);text-align:center;color:var(--mut);font-size:var(--fs-body)}

@media (max-width:720px){
  /* Columns a phone can do without, on a table that must not scroll sideways.
     The board carries Age, Customer, Source, Status, Owner and Account; at
     375px the first four of those plus the action fit, and Source, Owner and
     Account do not. Nothing is lost: every row opens the lead, where all three
     are stated as facts, and a claimed lead with no call names its owner in
     the attention band above the table. .tblwrap keeps its overflow-x as the
     fallback for a cell that still runs long. */
  .hide-sm{display:none}
  /* Eight pixels a side, not twelve. Four columns at the desktop gutter put the
     consultant table eighteen pixels past a 375px screen, so the table scrolled
     inside itself for the sake of padding. Tighter cells on a phone is the right
     trade on an operational table anyway: rows on screen is the whole job, and
     the tap target here is the row, which keeps its full height. */
  td,th{padding:var(--s-2) var(--s-2)}
  /* Restated after the shorthand above, which would otherwise reset both edges
     back to 8px and leave the phone layout 8px out instead of the 4px this rule
     was written to remove.
     16px and not 12px, because .card .factline keeps its 16px gutter on a phone
     too, and matching the paragraph is the entire point. The tightening above
     stays where it earns its keep, which is the space BETWEEN columns. Verified
     at 375px: no page in the product overflows horizontally with this on. */
  th:first-child,td:first-child{padding-left:var(--s-4)}
  th:last-child,td:last-child{padding-right:var(--s-4)}
  .wrap{padding:var(--s-3) var(--s-3) 56px}
  .topbar .in{padding:0 var(--s-3);gap:var(--s-2)}
  .kpi{padding:var(--s-2) var(--s-3)}
  .kpi b{font-size:var(--fs-page)}
  .kpi b.txt{font-size:var(--fs-title)}
  .menu{right:12px}

  /* The bar wraps to two rows on a phone: brand and scope on the first, the
   * section tabs on the second, full width. The scope switcher stays on the
   * first row because it is the one control that says what you are looking at,
   * and losing it behind a wrap is how somebody acts on the wrong client. */
  .topbar .in{gap:var(--s-2) var(--s-2)}
  .brand{padding:var(--s-3) 0}
  .brand b{font-size:var(--fs-body)}
  .scope{max-width:calc(100vw - 190px);order:2}
  .crumb{order:1}
  .topbar .right{order:3;padding:var(--s-2) 0}
  .nav{order:4;flex-basis:100%;margin:0 -12px;padding:0 var(--s-3) 0;border-top:1px solid rgba(255,255,255,.12)}
  /* 44px of vertical target on a surface people use one handed in a car park. */
  .nav a{min-height:44px;padding:0 var(--s-3)}
  .spanel{width:calc(100vw - 16px)}
  .spanel .slist{max-height:60vh}
  .spanel .srow{padding:var(--s-3) var(--s-2);min-height:44px}
}

/* ---- utilities, last, so they win ------------------------------------------
 * A utility that says "do not show this" has to beat every component rule that
 * sets display, and at equal specificity that means coming last in the file.
 * This lived at the top and lost to .scope's display:flex, which put an empty
 * unlabelled button in the top bar of every single-account session. Nothing here
 * uses !important: this file has never had one and does not need one, because
 * source order is the whole mechanism. */
.hide{display:none}
