/* ===========================================================================
   Admin Directory — .ad-*
   ---------------------------------------------------------------------------
   Two views over one population: a people list, and the reporting tree.

   THE TREE IS INDENTED, NOT CENTRED, and that is a scale decision rather than a
   taste one. A classic centred org chart is beautiful at twelve people and
   unusable at four hundred — it grows sideways without bound and every node
   needs horizontal scrolling to reach. An indented tree grows DOWNWARD, which
   is the direction a browser already scrolls, stays readable at any depth, and
   lets a row carry real columns (brands, accounts, last activity) instead of
   just a name in a box.

   No `#page-admin-directory { display: ... }` rule here. layout.css owns the
   shell; an id selector scores 1-0-0 against the class's 0-1-0, and one such
   line in entities.css once pinned a page visible on every route in the app.
   =========================================================================== */

/* ===== SHELL ===== */

/* The gaps are part of the table's height budget: every pixel spent between
   the bands above it is a row the reader does not see. */
.ad-wrap { display: flex; flex-direction: column; gap: .7rem; }

.ad-bar {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex-wrap: wrap;
}

.ad-tabs { display: flex; gap: .25rem; }

.ad-tab {
    appearance: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: .34rem .85rem;
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    color: var(--text-soft);
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    cursor: pointer;
    transition: background .15s var(--ease), color .15s var(--ease),
                border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.ad-tab:hover { color: var(--text); border-color: var(--gold-300); }
.ad-tab.on {
    background: var(--accent-weak);
    border-color: var(--accent-line);
    color: var(--accent-ink);
    font-weight: var(--fw-medium);
    box-shadow: var(--shadow);
}

/* ===== THE SUMMARY STRIP =====
   Two figures and three small charts on ONE row, in one card. It replaced five
   floating count cards, two of which showed the same number, and then a "rep
   coverage" meter whose implied goal - every rep linked to a login - was a
   goal nobody set. The people split says what the population is; the
   sales-role gap is the one measure on the strip that is a to-do.

   THE HEIGHT IS THE POINT. It is capped so the table starts high on the page:
   a summary that pushes the thing it summarises below the fold has cost more
   than it gave. The charts drop under the chips on a narrow window instead of
   squeezing to unreadable. */

.ad-strip {
    display: flex;
    align-items: stretch;
    gap: .9rem;
    flex-wrap: wrap;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: var(--shadow);
    padding: .7rem .9rem;
}

/* EVERY FIGURE IN A ROW OF FIGURES MUST SIT ON THE SAME LINE. These were
   `justify-content: center`, which centres each chip against its own content —
   so the one chip whose label wrapped to two lines pushed its number UP and out
   of line with its neighbours, which is the misalignment in the screenshot.
   A three-row grid with `align-content: start` puts every value on row 1 at the
   same y, whatever the label below it does. */
.ad-chip {
    display: grid;
    grid-template-rows: auto auto auto;
    align-content: start;
    flex: 0 0 auto;                 /* chips size to content; the charts take the
                                       slack, see below */
    min-width: 116px;
    padding-right: .9rem;
    border-right: 1px solid var(--border-soft);
}
.ad-chip:last-of-type { border-right: 0; }
.ad-chip-v {
    font-size: 1.34rem;
    font-weight: var(--fw-medium);
    color: var(--text);
    line-height: 1.15;
    letter-spacing: -.01em;
}
/* Reserves two lines whether the label needs them or not, so the sub-line under
   a one-line label still aligns with the sub-line under a two-line one. */
.ad-chip-k {
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    color: var(--text-soft);
    line-height: 1.25;
    min-height: 2.5em;
    margin-top: .12rem;
}
.ad-chip-s { font-size: var(--fs-2xs); color: var(--text-dim); line-height: 1.3; }

/* ---- a chart inside the strip ---- */

/* BOTH charts had `flex: 1 1 190px`, so they split every spare pixel between
   them - which on a wide monitor drew a 700px-wide progress meter with a hole
   in the strip beside it. A meter is a fixed-size component; the bar chart is
   the one that reads better with more room, so it takes the slack alone. */
.ad-chart {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: .34rem;
    flex: 0 0 232px;
    padding-right: .9rem;
    border-right: 1px solid var(--border-soft);
}
.ad-chart:last-child { border-right: 0; padding-right: 0; }
.ad-chart.wide { flex: 1 1 340px; min-width: 300px; }
/* NO max-width here, and that is deliberate arithmetic rather than an omission.
   At 1920px the strip has ~1721px to hand out; chips take ~393 and the meter a
   fixed 232, so a 640px cap on this chart would have left 456px of dead white
   in the middle of the card - which is the exact fault this pass exists to fix,
   reintroduced one element to the right. The bars are anchored left and their
   values sit in a fixed 34px column at the end, so length costs them nothing. */

@media (max-width: 1150px) {
    /* Below this the three charts cannot hold their shape beside the chips, so
       they wrap to their own rows rather than compressing to unreadable. */
    .ad-chart { flex: 1 1 240px; border-right: 0; }
    /* ...and once the charts have their own rows the two chips have a row to
       themselves, so they SHARE it. Sized to content they sat packed left with
       half of the band empty beside them at 768px. */
    .ad-strip .ad-chip { flex: 1 1 40%; }
    /* The second chip ends its row now, so its divider led into nothing. */
    .ad-strip .ad-chip.ad-live:nth-child(2) { border-right: 0; }
    .ad-chart.wide { flex: 2 1 340px; }
}

.ad-chart-h {
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    color: var(--text-dim);
    display: flex;
    align-items: center;
    gap: .25rem;
}

/* ---- a meter ----
   One measure against one whole. A track and a fill, not a ring: two slices of
   a donut say nothing a bar does not say in a tenth of the height. It carried
   "rep coverage" - every rep against every rep linked to a login, a goal nobody
   set - and now carries the sales-role gap, the one measure on this strip that
   is a to-do. */

/* At 0% the fill draws nothing, and a bare grey track reads as a control that
   failed rather than a real zero - so the track itself carries a faint hatch,
   which is the same "this is empty on purpose" signal the forecast bars use. */
.ad-meter {
    height: 10px;
    border-radius: 999px;
    background: var(--surface-3);
    border: 1px solid var(--border-soft);
    overflow: hidden;
    background-image: repeating-linear-gradient(135deg, transparent 0 5px, var(--border-soft) 5px 6px);
}
.ad-meter-fill {
    height: 100%;
    background: var(--gold-500);
    border-radius: 999px;
    transform-origin: left;
}
/* ---- THE METER FILLS, ONCE ----
   `transition: width .5s` used to sit on this rule and had NEVER ONCE RUN: a
   transition needs a change on an element that already existed, and _strip()
   rebuilds this fill from scratch on every paint - so the browser had nothing
   to interpolate from and the meter simply appeared. Width is layout anyway.
   admin-directory.js hands .ad-gx out through _gx('strip'), which answers once
   per arrival of the summary, so the fill grows when the figures land and sits
   still through every search keystroke and every view chip after it.
   BACKWARDS FILL: the resting state is the full meter. */
.ad-meter.ad-gx .ad-meter-fill {
    animation: adGrowX .32s var(--ease-out) backwards; animation-delay: 120ms;
}
@keyframes adGrowX { from { transform: scaleX(0); } }

/* ===== THE PAGE ARRIVES ONCE, AND THEN IT IS QUIET =====================
   admin-directory.js puts .ad-quiet on the strip and on the people panel for
   every paint after that region's first. _render() rebuilds the whole body on
   every search keystroke, and a CSS animation runs whenever an element
   carrying one is inserted - so before this rule ONE keystroke started 20-26
   animations: adRise on .ad-strip, adRise on .ad-panel and adRise on every
   visible row. On a three-letter search the page faded and rose three times,
   which reads as the page reloading rather than as the numbers moving.

   `*`, NOT A LIST, for the reason Home's own gate records: a list of what
   must not move was wrong by a hundred there, and one spelled `*` cannot be
   incomplete. !important because the entrances are declared across this file
   and motion.css, which loads last.

   THE ORG TAB IS DELIBERATELY NOT GATED, and the exemption is earned rather
   than assumed: its pane is drawn on demand, it is a view change rather than
   a repaint, and it holds an interactive reveal - .ad-oc.has-spot .ad-oc-spot
   slides in when a card is picked - which is a control answering a click, not
   an arrival replaying. Gating it would have made that control silent.

   TRANSITIONS ARE UNTOUCHED: the row hover, the filter focus ring and the
   chip wash are state, not arrival. */
.ad-quiet, .ad-quiet * { animation: none !important; }

.ad-meter-legend {
    display: flex;
    gap: .7rem;
    font-size: var(--fs-2xs);
    color: var(--text-dim);
}
.ad-meter-legend span { display: inline-flex; align-items: center; gap: .28rem; }

/* The legend swatch. Identity is carried by the LABEL beside it; the swatch
   only reinforces, which is why an 8px square is enough. */
.ad-sw {
    width: 8px; height: 8px; border-radius: 2px; display: inline-block;
    flex: 0 0 auto;
}

/* ---- people, by kind ----
   ONE MEASURE SPLIT THREE WAYS: a login only, a login and a rep identity, a rep
   identity only. That is a part-to-whole, so a stacked bar; its segments ARE
   the subject, so they take the first three series colours in their fixed order
   (--s1..--s3 are the only colours that may encode). Every segment is also
   named and counted in the legend, so colour never carries identity alone, and
   a 2px gap in the ground separates the segments rather than a stroke drawn
   around them.

   THE FILLS ARE THE DARK-THEME STEPS IN BOTH THEMES. The strip is --shell-800
   in both themes by construction, and validated on that ground the light-theme
   wine (#B02A46) measures 2.89:1, under the 3:1 floor, while the dark steps
   pass every check on #141310 (light app theme) and on #1E1C17 (dark app
   theme): lightness band, chroma, worst adjacent CVD dE 9.5 (deutan), normal
   dE 19.1, contrast >= 3:1. Measured with the dataviz validator, not by eye;
   the tokens are set on .ad-strip, where the ground is. */
/* Per-chart spacing; also the hooks the harness and the render read. */
.ad-people { gap: .38rem; }
.ad-gap-chart { gap: .32rem; }

.ad-stack {
    display: flex;
    gap: 2px;
    height: 10px;
    border-radius: 999px;
    overflow: hidden;
}
.ad-seg { display: block; height: 100%; min-width: 3px; }
.ad-seg.ad-k-rep,    .ad-sw.ad-k-rep    { background: var(--ad-seg-rep,    var(--s1)); }
.ad-seg.ad-k-both,   .ad-sw.ad-k-both   { background: var(--ad-seg-both,   var(--s2)); }
.ad-seg.ad-k-portal, .ad-sw.ad-k-portal { background: var(--ad-seg-portal, var(--s3)); }
.ad-stack-legend {
    display: flex;
    flex-wrap: wrap;
    gap: .15rem .55rem;
    font-size: var(--fs-2xs);
    color: var(--text-dim);
}
/* A legend entry that is also the view it counts. Text wears the text tokens,
   never the series colour - the swatch beside it carries the identity. */
.ad-seg-k {
    appearance: none;
    background: none;
    border: 0;
    padding: .05rem .3rem;
    margin: 0 -.3rem;
    border-radius: var(--r-sm);
    font: inherit;
    color: inherit;
    display: inline-flex;
    align-items: center;
    gap: .28rem;
    white-space: nowrap;
}
.ad-seg-k b { font-weight: var(--fw-medium); color: var(--text); }
button.ad-seg-k { cursor: pointer; transition: background .14s var(--ease); }
button.ad-seg-k:hover { background: var(--accent-weak); }
.ad-seg-k.on { background: var(--accent-weak); box-shadow: inset 2px 0 0 var(--gold-500); }

/* ---- the sales-role gap ----
   A figure over the meter. When there is nothing to measure it is a sentence,
   because a 0% bar over an empty denominator reads as "everyone unlinked",
   which is the opposite of the truth. */
.ad-gap-v { font-size: var(--fs-xl); font-weight: var(--fw-medium); color: var(--text); line-height: 1.2; }
.ad-gap-v b { font-weight: var(--fw-bold); }
.ad-gap-empty {
    font-size: var(--fs-xs);
    color: var(--text-soft);
    line-height: 1.35;
    display: inline-flex; align-items: center; gap: .3rem;
}
.ad-gap-failed { color: var(--bad-ink); }
.ad-gap-i { color: var(--warn-ink); }

/* ---- the source bars ----
   ONE HUE. A single series is not encoding identity, so gold here is chrome,
   exactly as it is on a button or a card rule. The brand is stated by its mark
   and its name in the label, never by the bar's colour — the four brand inks
   measure CVD dE 3.4 against each other and cannot carry identity alone. */

.ad-bars { display: flex; flex-direction: column; gap: .22rem; }

.ad-bar-row {
    display: grid;
    /* 140px, not 118: "Messenger Sales" needs 123px with its mark and was
       ellipsised at every width while the track beside it ran 700px. */
    /* 184px since a shared kind carries its brand ("Sage Territory ·
       Thumbies", ~176px with its mark). */
    /* AND IT HAD TO STOP BEING A px LITERAL. The label is var(--fs-xs) — rem —
       and this track was not, so at the 20px root base.css sets above 2560px
       the text grew and its column did not. Measured through Chrome on both
       Sage rows, which carry their brand in words since 2026-09-21:

           label                        needs @16px   @20px   this track had
           Messenger · Sage Territory      156          196      155 / 154
           Thumbies · Sage Territory       149          186      155 / 154

       (the cell is the track less the 22px badge and its .3rem gap, ~29px).
       So both clipped at 2560x1440 and the longer one clipped by a pixel at
       every ordinary width. 11.75rem is 188px at a 16px root and 235px at a
       20px one, against a requirement of 185 and 225 — the same fault, and the
       same fix, as the Products column track that had to go to rem for exactly
       this reason. The phone track below stays px: that media query only fires
       under 768px, where the root is 16. */
    grid-template-columns: 11.75rem minmax(40px, 1fr) 34px;
    align-items: center;
    gap: .45rem;
    font-size: var(--fs-xs);
}
.ad-bar-k {
    display: inline-flex; align-items: center; gap: .3rem; min-width: 0;
    color: var(--text-soft);
}
.ad-bar-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ad-bar-track {
    height: 9px;
    background: var(--surface-3);
    border-radius: 999px;
    overflow: hidden;
}
.ad-bar-fill {
    display: block;
    height: 100%;
    background: var(--gold-500);
    /* Rounded at the data end only, anchored to the baseline. */
    border-radius: 999px;
    transform-origin: left;
}
/* Same gate, same reason as the meter above; the rows stagger, capped at six
   because more than that is a ripple rather than an arrival. */
.ad-bar-track.ad-gx .ad-bar-fill { animation: adGrowX .32s var(--ease-out) backwards; }
.ad-bar-row:nth-child(2) .ad-bar-track.ad-gx .ad-bar-fill { animation-delay: 30ms; }
.ad-bar-row:nth-child(3) .ad-bar-track.ad-gx .ad-bar-fill { animation-delay: 60ms; }
.ad-bar-row:nth-child(4) .ad-bar-track.ad-gx .ad-bar-fill { animation-delay: 90ms; }
.ad-bar-row:nth-child(5) .ad-bar-track.ad-gx .ad-bar-fill { animation-delay: 120ms; }
.ad-bar-row:nth-child(n+6) .ad-bar-track.ad-gx .ad-bar-fill { animation-delay: 150ms; }
.ad-bar-row:hover .ad-bar-fill { background: var(--gold-600); }
.ad-bar-v { text-align: right; font-weight: var(--fw-medium); color: var(--text); }

@media (prefers-reduced-motion: reduce) {
    /* The two bare selectors carry no transition any more - both were dead,
       see the note on .ad-meter-fill - and these are the live forms. Named at
       their own specificity, not relying on the bare class, which loses. */
    .ad-meter.ad-gx .ad-meter-fill,
    .ad-bar-track.ad-gx .ad-bar-fill { animation: none; animation-delay: 0ms; }
}

/* ===== FILTER ROW ===== */

.ad-filters {
    display: flex; gap: .5rem; flex-wrap: wrap; align-items: center;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
}
.ad-filters .form-control,
.ad-filters .form-select { max-width: 180px; }
/* Sized so search, the six views and both pickers hold ONE line at 1366px.
   At 300 + 220 + 220 the brand picker wrapped alone onto a second row on the
   laptop this app is budgeted against - and it did so again when the views
   stopped abbreviating a kind of person as a defect ("No access" became "Reps
   without a login", 65px longer). Measured at 1366: 250 + ~580 of views + 180
   + 180 + gaps is 1,230px against ~1,180 of row, so below 1440 the search and
   the two pickers give up 50 and 20px each rather than the row wrapping. */
.ad-search { max-width: 250px !important; }
@media (max-width: 1439.98px) {
    .ad-search { max-width: 200px !important; }
    .ad-filters .form-select { max-width: 160px; }
}

/* Segmented view switch. `.on` is the quiet state on purpose — a row of loud
   chips at rest is an alarm about nothing. */
.ad-views { display: flex; border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; }
.ad-view {
    appearance: none;
    background: var(--surface);
    border: 0;
    border-right: 1px solid var(--border);
    padding: .34rem .7rem;
    font-size: var(--fs-sm);
    color: var(--text-soft);
    cursor: pointer;
    white-space: nowrap;
    transition: background .15s var(--ease), color .15s var(--ease);
}
.ad-view:last-child { border-right: 0; }
.ad-view:hover { background: var(--surface-2); color: var(--text); }
.ad-view.on { background: var(--accent-weak); color: var(--accent-ink); font-weight: var(--fw-medium); }

/* ===== PEOPLE TABLE ===== */

.ad-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: var(--shadow);
    overflow: hidden;
}

/* THE STICKY HEADER WAS SITTING ON TOP OF THE FIRST ROW, and the cause is a
   rule this project has already recorded once: `overflow-x: auto` forces
   `overflow-y` to `auto` too, which makes this wrapper a SCROLL CONTAINER. A
   sticky child sticks to its nearest scrollport, so `top: 62px` was measured
   from the top of the WRAPPER rather than the viewport — parking the header
   62px down and covering the first row, which is exactly what the screenshot
   showed happening to Andy.

   Two halves to the fix and both are needed: the wrapper is given a real height
   so it genuinely scrolls in both axes, and the offset becomes `top: 0`,
   measured from the wrapper it is actually inside. */
/* Sized so the PAGE itself does not also need to scroll on a normal screen -
   two scrollbars, one inside the other, is the thing that makes a data table
   feel fiddly. The constant is the chrome above it: 62px header + 24px page
   padding + ~104px strip + ~34px tabs + ~52px filters + ~30px footnote + the
   three .7rem gaps. */
.ad-tbl-wrap {
    overflow: auto;
    max-height: calc(100vh - 330px);
    min-height: 240px;
}

.ad-tbl { width: 100%; min-width: 900px; border-collapse: collapse; }
.ad-tbl th {
    text-align: left;
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    color: var(--text-dim);
    padding: .5rem .7rem;
    border-bottom: 1px solid var(--border);
    background: var(--inset);
    position: sticky;
    top: 0;                 /* the wrapper IS the scrollport now — see above */
    z-index: 3;
    white-space: nowrap;
}
.ad-tbl td {
    padding: .42rem .7rem;
    font-size: var(--fs-md);
    color: var(--text);
    border-bottom: 1px solid var(--border-soft);
    vertical-align: middle;
}

/* An absent value is drawn quieter than a real one. A column of hard black
   dashes competes with the figures beside it for attention it has not earned. */
/* The marker on a date measured as-of a static extract rather than today. */
.ad-asof { font-size: var(--fs-2xs); margin-left: .3rem; color: var(--text-dim); vertical-align: baseline; }

.ad-dash { color: var(--text-dim); }
.ad-tbl tbody tr { transition: background .12s var(--ease); }
.ad-tbl tbody tr:nth-child(even) { background: var(--surface-2); }
.ad-tbl tbody tr:hover { background: var(--accent-weak); }
.ad-tbl td.num { text-align: right; white-space: nowrap; }
/* A numeric HEADER sits over its figures. `th` was left-aligned whatever its
   class, so "Accounts" started 60px left of the right-aligned numbers under it
   and "Last sale" ~100px left of its dates - on this table and in the accounts
   dialog alike. */
.ad-tbl th.num { text-align: right; }

/* THE ROW RULE MARKS THE GAP, NOT THE KIND. It sat on every rep without a login
   - 158 of 163 rows, in the warning colour - which is an alarm about nothing: a
   rep with no login is a normal, permanent kind of person. It marks a
   sales-role login with no rep identity now, the one state on this page that is
   a to-do, and a bucket keeps its dim rule because it is not a person. */
.ad-tbl tr.needs-rep td:first-child { box-shadow: inset 3px 0 0 var(--warn); }
.ad-tbl tr.is-house td:first-child  { box-shadow: inset 3px 0 0 var(--text-dim); }

/* A fixed line box on both lines, so a row whose second line is absent is
   exactly as tall as one where it is present. */
.ad-who { display: flex; align-items: center; gap: .55rem; min-width: 0; height: 34px; }
.ad-av {
    width: 32px; height: 32px; border-radius: 50%; flex: 0 0 auto;
    display: grid; place-items: center;
    font-size: var(--fs-xs); font-weight: var(--fw-medium);
    color: var(--accent-ink);
    background: var(--accent-weak);
    border: 1px solid var(--accent-line);
}
.ad-av.ghost { background: var(--surface-3); color: var(--text-dim); border-color: var(--border); }
.ad-who-t { min-width: 0; }
.ad-who-n {
    font-weight: var(--fw-medium); line-height: 1.25;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ad-who-s {
    font-size: var(--fs-xs); color: var(--text-dim); line-height: 1.25;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ===== BADGES ===== */

.ad-badge {
    display: inline-flex; align-items: center; gap: .28rem;
    padding: .1rem .48rem;
    border-radius: 999px;
    font-size: var(--fs-xs); font-weight: var(--fw-medium); line-height: 1.5;
    border: 1px solid transparent;
    white-space: nowrap;
}
.ad-badge.ok      { background: var(--ok-weak);   color: var(--ok-ink);   border-color: var(--ok-line); }
.ad-badge.warn    { background: var(--warn-weak); color: var(--warn-ink); border-color: var(--warn-line); }
.ad-badge.bad     { background: var(--bad-weak);  color: var(--bad-ink);  border-color: var(--bad-line); }
.ad-badge.neutral { background: var(--surface-3); color: var(--text-soft); border-color: var(--border); }

/* Role colours. These are the ONE place a role is encoded by colour, and every
   badge carries its label as well — colour reinforces, it never carries. */
.ad-role { display: inline-flex; align-items: center; gap: .3rem;
           padding: .1rem .5rem; border-radius: 999px;
           font-size: var(--fs-xs); font-weight: var(--fw-medium); white-space: nowrap;
           border: 1px solid transparent; }
.ad-role.r-sales_rep     { background: var(--info-weak);  color: var(--info-ink);  border-color: var(--info-line); }
.ad-role.r-sales_manager { background: var(--ok-weak);     color: var(--ok-ink);     border-color: var(--ok-line); }
.ad-role.r-executive     { background: var(--accent-weak); color: var(--accent-ink);   border-color: var(--accent-line); }
.ad-role.r-admin         { background: var(--shell-800);   color: var(--gold-200);   border-color: var(--shell-700); }
.ad-role.r-none          { background: var(--surface-3);   color: var(--text-dim);   border-color: var(--border); }

.ad-brands { display: inline-flex; gap: .3rem; align-items: center; flex-wrap: wrap; }

/* THE BADGE ALONE (2026-09-17). The brand colours measure CVD dE 3.4 against
   each other at best, which is why a colour-only mark was once given its name
   beside it. The circular badge carries the brand's INITIALS, so colour is no
   longer what identifies it; the full name is on the tooltip and aria-label. */
.ad-brand {
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
}

/* NO IDENTITY IS NOT A MISSING VALUE. An executive or an administrator holds no
   rep identity by design, so the Brands cell says that in words, in the dim ink
   an absent value takes - never a dash, which says "missing". */
.ad-kind-none { font-size: var(--fs-xs); color: var(--text-dim); white-space: nowrap; }

/* THE ONE AMBER NOTE ON THE PAGE: a sales-role login with no rep identity. The
   only state here that is a to-do, so the only one allowed the warning colour,
   and it is a control - it opens Manage, where the link is made. Two lines in
   the cell: what is missing, and what it costs. */
.ad-gap-note {
    appearance: none;
    display: inline-flex;
    align-items: flex-start;
    gap: .3rem;
    background: var(--warn-weak);
    border: 1px solid var(--warn-line);
    color: var(--warn-ink);
    border-radius: var(--r-sm);
    padding: .2rem .5rem;
    font: inherit;
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    line-height: 1.25;
    text-align: left;
    cursor: pointer;
    transition: border-color .14s var(--ease);
}
.ad-gap-note:hover { border-color: var(--warn); }
.ad-gap-note i { line-height: 1.25; flex: 0 0 auto; }
.ad-gap-note small {
    display: block;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-normal);
    opacity: .85;
}


/* ===== EMPTY / SOON ===== */

.ad-empty {
    display: flex;
    align-items: center;          /* fallback first: a browser that does not
                                     know `safe` discards the whole declaration */
    align-items: safe center;
    justify-content: center;
    flex-direction: column;
    gap: .4rem;
    text-align: center;
    padding: 2rem 1rem;
    color: var(--text-dim);
}
.ad-empty i { font-size: 1.6rem; opacity: .55; }
.ad-empty b { font-size: var(--fs-lg); color: var(--text-soft); font-weight: var(--fw-medium); }
.ad-empty span { font-size: var(--fs-md); max-width: 52ch; line-height: 1.45; }

.ad-note {
    display: flex; gap: .55rem; align-items: flex-start;
    background: var(--accent-weak);
    border: 1px solid var(--accent-line);
    border-radius: var(--r-sm);
    padding: .6rem .75rem;
    font-size: var(--fs-md);
    color: var(--text);
}
.ad-note i { color: var(--accent-ink); font-size: var(--fs-lg); line-height: 1.4; flex: 0 0 auto; }
.ad-note b { font-weight: var(--fw-medium); }
.ad-note span { color: var(--text-soft); }

.ad-foot { margin: .2rem .2rem 0; font-size: var(--fs-sm); color: var(--text-soft); }

/* ===== DIALOG BITS ===== */

.ad-form { display: grid; grid-template-columns: 1fr 1fr; gap: .7rem .9rem; }
@media (max-width: 560px) { .ad-form { grid-template-columns: 1fr; } }
.ad-form .wide { grid-column: 1 / -1; }
.ad-form label {
    display: block;
    font-size: var(--fs-xs); font-weight: var(--fw-medium); color: var(--text-dim);
    margin-bottom: .2rem;
}
.ad-form .form-text { font-size: var(--fs-sm); }

/* The two axes of the Manage dialog, headed. This was .ad-form-h, the dim
   micro-label the dialog used for "Rep identities" - a section that is one of
   two needs a heading that reads as one. Not an inline style, which is the one
   thing a CSS sweep can never reach. */
.ad-form-sec {
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    color: var(--text);
    margin: 0 0 .55rem;
}
/* The kind of person the dialog is about, beside their name. */
.ad-kind-tag {
    display: inline-block;
    padding: .02rem .45rem;
    border-radius: 999px;
    background: var(--surface-3);
    border: 1px solid var(--border);
    color: var(--text-soft);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-medium);
    vertical-align: middle;
}
/* The gap, stated in the dialog that closes it. */
.ad-gap-copy {
    display: flex; gap: .5rem; align-items: flex-start;
    background: var(--warn-weak);
    border: 1px solid var(--warn-line);
    border-radius: var(--r-sm);
    padding: .55rem .7rem;
    margin: 0 0 .6rem;
    font-size: var(--fs-md); color: var(--text); line-height: 1.45;
}
.ad-gap-copy i { color: var(--warn-ink); flex: 0 0 auto; line-height: 1.4; }
.ad-gap-copy b { color: var(--warn-ink); font-weight: var(--fw-medium); }

.ad-repline {
    display: flex; align-items: center; gap: .5rem;
    padding: .4rem .55rem;
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    font-size: var(--fs-md);
    margin-bottom: .35rem;
}
.ad-repline .ad-rep-n { font-weight: var(--fw-medium); }
.ad-repline .ad-rep-m { color: var(--text-dim); font-size: var(--fs-sm); }
.ad-repline button { margin-left: auto; }
/* The identity's brand badge leads the line and never shrinks. */
.ad-repline > .bbadge { flex: 0 0 auto; }
/* The ad-clear chip's brand badge, pulled into the chip's line box. */
.ad-clear .bbadge { margin: -3px 0; transform: scale(.84); }
/* An identity the invitation is locked to: a rep line with a state where the
   Unlink button would be. */
.ad-locked .ad-badge { margin-left: auto; }

.ad-pass {
    display: flex;
    gap: .4rem;
}
.ad-pass .form-control { flex: 1 1 auto; }

/* ===== MANAGE ACCESS AND INVITE, REDESIGNED (2026-09-17) =====
   Reported off a screenshot: a wide plain form, a Role <select> with its
   description under it, Save always live, nothing saying what a change MEANS.
   The dialog reads top to bottom now: who this is, the four roles as cards
   (label, what it sees, its whole description), the three access states with
   the consequence of the chosen one, the reporting line, employment, and a
   searchable identity picker. Every colour is a token pair this stylesheet
   already uses, so both themes are covered by base.css's declarations. */

/* ---- who this is: one line of context, never a sentence -------------- */
.ad-mg-head {
    display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
    padding: .7rem .85rem;
    background: var(--inset);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    margin-bottom: 1rem;
}
.ad-mg-av { width: 40px; height: 40px; font-size: var(--fs-md); }
.ad-mg-id { min-width: 0; flex: 1 1 12rem; }
.ad-mg-n { font-weight: var(--fw-bold); font-size: var(--fs-lg); line-height: 1.2; color: var(--text); }
.ad-mg-e { font-size: var(--fs-sm); color: var(--text-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ad-mg-tags { display: inline-flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.ad-mg-seen {
    display: inline-flex; align-items: center; gap: .3rem;
    font-size: var(--fs-xs); color: var(--text-soft); white-space: nowrap;
}
/* A lede under a section heading, and the consequence line under a control:
   prose, so it wraps and takes the soft ink - not --text-dim, which is held to
   3:1 for labels only. */
.ad-mg-lede { font-size: var(--fs-md); color: var(--text-soft); line-height: 1.45; margin: -.25rem 0 .6rem; }
.ad-mg-note { font-size: var(--fs-sm); color: var(--text-soft); line-height: 1.45; margin: .45rem 0 0; min-height: 1.4em; }
/* The role-aware identity sentence: the SPECIFIC statement under the generic
   lede, so it takes the lede's size in the body ink rather than the table's
   2xs sub-label it used to borrow. */
.ad-ident-note { font-size: var(--fs-md); color: var(--text); line-height: 1.45; margin: 0 0 .6rem; }
/* The Access note on an invitation stands where Manage's control would be. */
.ad-access-note { margin-top: .1rem; }
#adManageBody .ad-form-sec, #adInviteBody .ad-form-sec { margin-top: 1.05rem; }
#adManageBody .ad-form-sec:first-of-type, #adInviteBody .ad-form-sec:first-of-type { margin-top: 0; }

/* ---- the role cards: radio semantics, one chosen, the current one marked -- */
.ad-cards {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .55rem;
}
@media (max-width: 560px) { .ad-cards { grid-template-columns: 1fr; } }
.ad-card {
    appearance: none;
    display: flex; flex-direction: column; align-items: flex-start; gap: .22rem;
    text-align: left;
    font: inherit;
    padding: .65rem .8rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    color: var(--text);
    cursor: pointer;
    transition: border-color .14s var(--ease), box-shadow .14s var(--ease), background .14s var(--ease);
}
.ad-card:hover { border-color: var(--accent-line); }
.ad-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The chosen card is the only one with a coloured ground: gold is chrome. */
.ad-card.on {
    background: var(--accent-weak);
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
}
.ad-card[aria-disabled="true"] { cursor: not-allowed; opacity: .72; }
.ad-card[aria-disabled="true"]:hover { border-color: var(--border); }
.ad-card.on[aria-disabled="true"] { border-color: var(--accent-line); box-shadow: none; }
.ad-card-t { font-weight: var(--fw-bold); font-size: var(--fs-md); display: inline-flex; align-items: center; gap: .4rem; }
.ad-card-cur {
    font-style: normal; font-size: var(--fs-2xs); font-weight: var(--fw-medium);
    padding: .02rem .4rem; border-radius: 999px;
    background: var(--surface-3); color: var(--text-soft); border: 1px solid var(--border);
}
.ad-card.on .ad-card-t { color: var(--accent-ink); }
.ad-card-scope { font-size: var(--fs-xs); font-weight: var(--fw-medium); color: var(--text-soft); }
.ad-card-d { font-size: var(--fs-sm); color: var(--text-soft); line-height: 1.4; }
/* What Admin can do, in the warning pair - the one card whose grant reaches
   every other account on the platform. */
.ad-card-warn {
    display: inline-flex; align-items: flex-start; gap: .3rem;
    margin-top: .15rem;
    font-size: var(--fs-xs); font-weight: var(--fw-medium);
    color: var(--warn-ink);
    background: var(--warn-weak);
    border: 1px solid var(--warn-line);
    border-radius: var(--r-sm);
    padding: .15rem .45rem;
    line-height: 1.35;
}
.ad-card-warn i { flex: 0 0 auto; line-height: 1.35; }

/* ---- access: a segmented control ------------------------------------- */
.ad-tog {
    display: inline-flex;
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    background: var(--surface-2);
    padding: 2px;
    gap: 2px;
}
.ad-tog-b {
    appearance: none; border: 0; background: transparent;
    font: inherit; font-size: var(--fs-sm); font-weight: var(--fw-medium);
    color: var(--text-soft);
    padding: .3rem .85rem;
    border-radius: var(--r-pill);
    cursor: pointer;
    transition: background .14s var(--ease), color .14s var(--ease);
}
.ad-tog-b:hover { color: var(--text); }
.ad-tog-b:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The chosen state wears the state pair the directory's access chip already
   uses for it, so the control and the row agree about what the word means. */
.ad-tog-b.on.st-active    { background: var(--ok-weak);    color: var(--ok-ink);   box-shadow: inset 0 0 0 1px var(--ok-line); }
.ad-tog-b.on.st-inactive  { background: var(--surface);   color: var(--text);     box-shadow: inset 0 0 0 1px var(--border); }
.ad-tog-b.on.st-suspended { background: var(--bad-weak);   color: var(--bad-ink);  box-shadow: inset 0 0 0 1px var(--bad-line); }
.ad-tog-b[aria-disabled="true"] { cursor: not-allowed; opacity: .72; }

/* ---- the identity picker: a search box, the largest books, then matches -- */
/* Every colour here is a theme-aware token, so light and dark need no second
   declaration - the tokens are already declared twice in base.css. */
.ad-pick { margin-top: .35rem; }
.ad-pick-row { display: flex; gap: .4rem; }
.ad-pick-row .form-control { flex: 1 1 auto; }
.ad-pick-list {
    margin-top: .4rem;
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface);
    box-shadow: var(--shadow);
    max-height: 19rem;
    overflow-y: auto;
}
.ad-pick-hint {
    display: flex; align-items: flex-start; gap: .4rem;
    padding: .55rem .75rem;
    font-size: var(--fs-sm); color: var(--text-soft); line-height: 1.45;
}
.ad-pick-sub { border-top: 1px solid var(--border-soft); background: var(--surface-2); font-size: var(--fs-xs); }
.ad-pick-sub .bi { color: var(--text-dim); line-height: 1.5; }
.ad-pick-fail { color: var(--bad-ink); }
.ad-pick-g {
    display: flex; align-items: center; gap: .35rem;
    padding: .45rem .75rem .25rem;
    font-size: var(--fs-2xs); font-weight: var(--fw-medium); letter-spacing: .04em; text-transform: uppercase;
    color: var(--text-dim);
    background: var(--inset);
    border-top: 1px solid var(--border-soft);
}
.ad-pick-g:first-child { border-top: 0; }
.ad-pick-top .bi { color: var(--accent-ink); }
.ad-pick-o {
    appearance: none; border: 0; background: transparent;
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: .65rem;
    width: 100%; text-align: left;
    font: inherit; font-size: var(--fs-md); color: var(--text);
    padding: .5rem .75rem;
    cursor: pointer;
    border-top: 1px solid var(--border-soft);
    transition: background-color .12s ease;
}
.ad-pick-o:hover, .ad-pick-o.hi { background: var(--accent-weak); }
.ad-pick-o.hi { box-shadow: inset 3px 0 0 var(--accent); }
.ad-pick-o:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.ad-pick-main { display: flex; flex-direction: column; min-width: 0; gap: .05rem; }
.ad-pick-n { font-weight: var(--fw-bold); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ad-pick-m { font-size: var(--fs-xs); color: var(--text-dim); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ad-pick-a { font-size: var(--fs-xs); color: var(--text-soft); white-space: nowrap; text-align: right; }
.ad-pick-a b { display: block; font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--text); font-variant-numeric: tabular-nums; line-height: 1.2; }
.ad-pick-o.hi .ad-pick-a b, .ad-pick-o:hover .ad-pick-a b { color: var(--accent-ink); }
/* The chosen identity, drawn as a rep line with a Change link where Unlink
   would be. */
.ad-picked { margin-top: .35rem; margin-bottom: 0; }
.ad-pick-x { margin-left: auto; padding: 0 .3rem; font-size: var(--fs-sm); }

/* ---- the person picker: Reports to --------------------------------------
   A trigger showing the chosen person, and a panel that expands INLINE under
   it. Inline, not floating: the modal body is the scroll container, and a
   positioned panel inside it is clipped at the body's edge. */
.ad-pp { position: relative; }
.ad-pp-btn {
    appearance: none; width: 100%;
    display: flex; align-items: center; gap: .7rem;
    padding: .55rem .75rem;
    font: inherit; font-size: var(--fs-md); color: var(--text); text-align: left;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    box-shadow: var(--shadow);
    cursor: pointer;
    transition: border-color .15s ease, box-shadow .15s ease;
}
.ad-pp-btn:hover { border-color: var(--accent-line); }
.ad-pp-btn:focus-visible,
.ad-pp.is-open .ad-pp-btn {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-weak), var(--shadow);
}
.ad-pp-face { display: flex; align-items: center; gap: .7rem; min-width: 0; flex: 1 1 auto; }
.ad-pp-chg {
    flex: 0 0 auto; display: inline-flex; align-items: center; gap: .3rem;
    font-size: var(--fs-xs); font-weight: var(--fw-medium); color: var(--accent-ink);
}
.ad-pp-chg .bi { transition: transform .18s ease; }
.ad-pp.is-open .ad-pp-chg .bi { transform: rotate(180deg); }

.ad-pp-av {
    width: 36px; height: 36px; border-radius: 50%; flex: 0 0 auto;
    display: grid; place-items: center; overflow: hidden;
    font-size: var(--fs-xs); font-weight: var(--fw-bold); letter-spacing: .02em;
    color: var(--accent-ink);
    background: var(--accent-weak);
    border: 1px solid var(--accent-line);
}
.ad-pp-img { object-fit: cover; padding: 0; background: var(--surface-3); }
.ad-pp-av.is-none { color: var(--text-dim); background: var(--surface-3); border-color: var(--border); font-size: var(--fs-md); }

.ad-pp-main { display: flex; flex-direction: column; gap: .12rem; min-width: 0; }
.ad-pp-l1 { display: flex; align-items: center; gap: .45rem; min-width: 0; }
.ad-pp-n { font-weight: var(--fw-bold); color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ad-pp-m {
    display: flex; align-items: center; flex-wrap: wrap; gap: .1rem .45rem;
    font-size: var(--fs-xs); color: var(--text-soft); min-width: 0;
}
.ad-pp-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ad-pp-e { font-size: var(--fs-xs); color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ad-pp-main .ad-role { flex: 0 0 auto; }
/* The Admin chip is a --shell-800 plate, which on a dark card is the card.
   A hairline gives it an edge in dark mode - declared twice, so the toggle
   beats the OS both ways. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .ad-pp .ad-role.r-admin { border-color: rgba(255, 255, 255, .22); }
}
:root[data-theme="dark"] .ad-pp .ad-role.r-admin { border-color: rgba(255, 255, 255, .22); }
/* A button's text is not body text: the meta lines are set at the regular
   weight explicitly, so only the name reads bold. */
.ad-pp-m, .ad-pp-e, .ad-pp-seen, .ad-pp-foot, .ad-pick-m, .ad-pick-a { font-weight: var(--fw-normal); }

.ad-pp-panel {
    margin-top: .45rem;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface);
    box-shadow: var(--shadow-md);
    overflow: hidden;
    animation: enterSm .16s ease backwards;
}
.ad-pp-panel:empty { display: none; }
.ad-pp-search {
    position: relative;
    padding: .6rem;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border-soft);
}
.ad-pp-search .bi { position: absolute; left: 1.2rem; top: 50%; transform: translateY(-50%); color: var(--text-dim); pointer-events: none; }
.ad-pp-search .form-control { padding-left: 2.1rem; border-radius: var(--r-pill); font-size: var(--fs-md); }
.ad-pp-search .form-control:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-weak); }
.ad-pp-list { max-height: 20rem; overflow-y: auto; }
.ad-pp-o {
    appearance: none; border: 0; background: transparent;
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto 1.1rem; align-items: center; gap: .7rem;
    width: 100%; text-align: left;
    font: inherit; font-size: var(--fs-md); color: var(--text);
    padding: .55rem .8rem;
    border-top: 1px solid var(--border-soft);
    cursor: pointer;
    transition: background-color .12s ease;
}
.ad-pp-o:first-child { border-top: 0; }
.ad-pp-o:hover, .ad-pp-o.hi { background: var(--accent-weak); }
.ad-pp-o.hi { box-shadow: inset 3px 0 0 var(--accent); }
.ad-pp-o:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.ad-pp-o.on .ad-pp-n { color: var(--accent-ink); }
.ad-pp-o.is-nobody { grid-template-columns: auto minmax(0, 1fr) 1.1rem; background: var(--surface-2); }
.ad-pp-o.is-nobody:hover, .ad-pp-o.is-nobody.hi { background: var(--accent-weak); }
.ad-pp-side {
    display: flex; flex-direction: column; align-items: flex-end; gap: .12rem;
    font-size: var(--fs-xs); color: var(--text-dim); white-space: nowrap;
}
.ad-pp-r { font-weight: var(--fw-medium); color: var(--text-soft); font-variant-numeric: tabular-nums; }
.ad-pp-seen { font-size: var(--fs-2xs); }
.ad-pp-check { grid-column: -2; color: var(--accent-ink); font-size: var(--fs-lg); }
.ad-pp-note {
    display: flex; align-items: flex-start; gap: .45rem;
    padding: .6rem .8rem;
    font-size: var(--fs-sm); color: var(--text-soft); line-height: 1.45;
}
.ad-pp-note.is-fail { color: var(--warn-ink); background: var(--warn-weak); border-bottom: 1px solid var(--warn-line); }
.ad-pp-foot {
    display: flex; align-items: flex-start; gap: .4rem;
    padding: .5rem .8rem;
    font-size: var(--fs-xs); color: var(--text-dim); line-height: 1.45;
    background: var(--surface-2);
    border-top: 1px solid var(--border-soft);
}
.ad-pp-foot:empty { display: none; }
.ad-pp-list .m1-load { padding: 1.2rem 0; }

/* Changing your own role or access: amber, a statement, never a block. */
.ad-self-warn {
    display: flex; align-items: flex-start; gap: .55rem;
    margin-top: .6rem; padding: .6rem .8rem;
    font-size: var(--fs-sm); line-height: 1.45; color: var(--text);
    background: var(--warn-weak);
    border: 1px solid var(--warn-line);
    border-radius: var(--r-sm);
}
.ad-self-warn .bi { color: var(--warn-ink); line-height: 1.4; flex: 0 0 auto; }
.ad-self-warn b { color: var(--warn-ink); font-weight: var(--fw-bold); }

@media (prefers-reduced-motion: reduce) {
    .ad-pp-panel { animation: none; }
    .ad-pp-chg .bi, .ad-pp-btn, .ad-pp-o, .ad-pick-o { transition: none; }
}

/* ---- what is about to change, beside Save --------------------------- */
#adManageModal .modal-footer { justify-content: flex-end; }
.ad-pend {
    flex: 1 1 12rem; min-width: 0;
    display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .6rem;
    margin-right: auto;
    font-size: var(--fs-sm); color: var(--text-soft); line-height: 1.4;
}
.ad-pend-none { color: var(--text-dim); font-style: italic; }
.ad-pend-i {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .12rem .5rem;
    background: var(--accent-weak);
    border: 1px solid var(--accent-line);
    border-radius: 999px;
    color: var(--accent-ink);
    font-size: var(--fs-xs);
}
.ad-pend-i b { font-weight: var(--fw-medium); }
.ad-pend-c {
    display: inline-flex; align-items: flex-start; gap: .3rem;
    width: 100%;
    font-size: var(--fs-xs); color: var(--text-soft);
}
.ad-pend-c i { flex: 0 0 auto; line-height: 1.4; }

/* ---- the invitation: the email note and what happens next ------------ */
.ad-field-bad { color: var(--bad-ink); }
.ad-next {
    margin-top: 1rem;
    padding: .7rem .85rem;
    background: var(--inset);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    font-size: var(--fs-sm); color: var(--text-soft); line-height: 1.45;
}
.ad-next-h {
    display: flex; align-items: center; gap: .4rem;
    font-size: var(--fs-md); font-weight: var(--fw-medium); color: var(--text);
    margin-bottom: .3rem;
}
.ad-next-h i { color: var(--accent-ink); }
.ad-next-l { margin: 0; padding-left: 1.2rem; }
.ad-next-l li { margin: .15rem 0; }
.ad-next-l b { color: var(--text); font-weight: var(--fw-medium); }

/* ===== DRILL-DOWN =====
   Every figure on this page opens the rows it was counted from. A number that
   states a total and cannot show you what it counted is a dead end, and on a
   page whose whole job is "who are these 113 reps" that is the wrong kind. */

/* A figure that is also a control. It has to look clickable without becoming a
   second colour in a numeric column, so it is the ink it already was plus an
   underline that only appears on hover. */
.ad-link {
    appearance: none;
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-weight: var(--fw-medium);
    color: var(--text);
    cursor: pointer;
    border-bottom: 1px dashed var(--border);
    transition: color .12s var(--ease), border-color .12s var(--ease);
}
.ad-link:hover { color: var(--accent-ink); border-bottom-color: var(--gold-500); }

/* A chip that applies the view it is the count of. `.on` is quiet: a loud chip
   at rest is an alarm about nothing, and the point is to show WHICH one is
   currently applied, not to shout at all three. */
/* A live chip must sit on exactly the same grid as a static one. The first cut
   gave it a negative block margin to fake the hover padding, which lifted it
   above its neighbours and put it at risk of clipping against the card edge. It
   takes horizontal padding only. */
.ad-chip.ad-live {
    display: grid;
    grid-template-rows: auto auto auto;
    align-content: start;
    appearance: none;
    border: 0;
    border-right: 1px solid var(--border-soft);
    background: none;
    text-align: left;
    cursor: pointer;
    border-radius: var(--r-sm);
    padding: 0 .9rem 0 .35rem;
    margin-left: -.35rem;
    transition: background .14s var(--ease);
}
.ad-chip.ad-live:hover { background: var(--accent-weak); }
.ad-chip.ad-live.on { background: var(--accent-weak); box-shadow: inset 2px 0 0 var(--gold-500); }

/* A bar that filters to its own kind. */
.ad-bar-row {
    appearance: none;
    background: none;
    border: 0;
    width: 100%;
    text-align: left;
    cursor: pointer;
    padding: .1rem .2rem;
    border-radius: var(--r-sm);
    transition: background .14s var(--ease);
}
.ad-bar-row:hover { background: var(--accent-weak); }
.ad-bar-row.on { background: var(--accent-weak); }
.ad-bar-row.on .ad-bar-fill { background: var(--gold-600); }

/* The active chart filter, shown and clearable. A filter applied with nothing
   on screen saying so is the worst of the three states — the reader sees a
   short list and concludes the data is missing. */
.ad-clear {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    background: var(--accent-weak);
    border: 1px solid var(--accent-line);
    color: var(--accent-ink);
    border-radius: 999px;
    padding: .25rem .6rem;
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    cursor: pointer;
}
.ad-clear:hover { background: var(--gold-100); }
.ad-clear .bi-x-lg { font-size: var(--fs-2xs); opacity: .7; }

/* ---- the accounts dialog ---- */

.ad-acct-head {
    display: flex;
    flex-wrap: wrap;               /* on a phone "Measure: funded" ran off the dialog */
    align-items: baseline;
    row-gap: .4rem;
    gap: 1.4rem;
    padding: 0 0 .7rem;
    font-size: var(--fs-md);
    color: var(--text-soft);
    border-bottom: 1px solid var(--border);
    margin-bottom: .2rem;
}
.ad-acct-head b { color: var(--text); font-size: var(--fs-xl); font-weight: var(--fw-medium); }

/* Bounded so the dialog never grows past the viewport, and scrolling its own
   body rather than the page behind it. The sticky header inside works because
   THIS is the scrollport — the same arrangement the main table needed. */
.ad-acct-wrap { max-height: min(56vh, 520px); }
.ad-acct-tbl { min-width: 560px; }
.ad-acct-foot {
    padding-top: .7rem;
    font-size: var(--fs-sm);
    color: var(--text-dim);
    display: flex;
    align-items: center;
}

/* ===== REP PERFORMANCE ===== */

.ad-perf-head {
    /* A grid that shares the width, not a flex row packed left: in a 95vw
       dialog five figures took a third of the band and left the rest empty. */
    display: grid; gap: .8rem 1.6rem;
    grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
    padding: .7rem .85rem;
    background: var(--inset);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    margin-bottom: .7rem;
}
.ad-perf-s { display: flex; flex-direction: column; }
.ad-perf-v { font-size: 1.1rem; font-weight: var(--fw-medium); color: var(--text); line-height: 1.2; }
.ad-perf-k { font-size: var(--fs-2xs); font-weight: var(--fw-medium); color: var(--text-dim); }

/* The gap between the two attributions is the finding, so it is a sentence
   rather than a third number nobody can interpret. */
.ad-perf-note {
    font-size: var(--fs-md); color: var(--text-soft); line-height: 1.45;
    background: var(--accent-weak);
    border: 1px solid var(--accent-line);
    border-radius: var(--r-sm);
    padding: .55rem .7rem;
    margin: 0 0 .7rem;
}
.ad-perf-note b { color: var(--accent-ink); }
/* A disagreement between the row and the dialog is a warning, not a finding. */
.ad-perf-note.warn { background: var(--warn-weak); border-color: var(--warn-line); }
.ad-perf-note.warn b { color: var(--warn-ink); }

.ad-perf-h { font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text); margin: .9rem 0 .4rem; }
.ad-perf-mix { display: flex; flex-direction: column; gap: .22rem; }
.ad-perf-f {
    display: grid;
    grid-template-columns: minmax(160px, 22rem) minmax(60px, 1fr) 90px 110px;
    gap: .5rem; align-items: baseline;
    font-size: var(--fs-sm);
    padding: .22rem 0;
    border-top: 1px solid var(--border-soft);
}
.ad-perf-f:first-child { border-top: 0; }
.ad-perf-f span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.ad-perf-f b { text-align: right; font-weight: var(--fw-medium); }
.ad-perf-f em { font-size: var(--fs-xs); color: var(--text-dim); font-style: normal; text-align: right; }
.ad-perf-bar {
    display: block; height: 7px; border-radius: 999px;
    background: var(--surface-3); overflow: hidden; align-self: center;
}
.ad-perf-bar > i { display: block; height: 100%; border-radius: 999px; background: var(--gold-500); }

/* The one brand and one measure a page of accounts shares, stated once. */
.ad-acct-of { margin-left: auto; display: inline-flex; align-items: center; gap: .9rem; }

.ad-perf-foot { margin: .8rem 0 0; font-size: var(--fs-xs); color: var(--text-dim); }

/* THE ACTIVITY DIALOG'S BODY IS THE SHARED ActivityLog COMPONENT (.al-*, in
   activity-log.css). The .ad-act-* rows this file carried were a second copy
   of profile.css's paint, and two copies of one paint drift; nothing emits
   them any more, so they are gone rather than left as dead rules. */
#adActBody .al { --al-sticky-top: 0; }   /* the dialog body is the scroller, not the page */
#adActModal .modal-body { overflow-y: auto; min-height: 0; }
#adActModal .modal-content { max-height: calc(100vh - 3.5rem); }

/* ===========================================================================
   THE PAGE LEADS WITH A DARK BAND, AND THE TREE HAS WEIGHT
   ---------------------------------------------------------------------------
   Reported directly: "the entire admin directory page is soo ugly... and the
   company hierarchy tree is also so ugly."

   Both were flat white on flat grey with small type throughout - technically
   complete, uniformly light, nothing dominant. That is the same fault the
   Products page and the entity record both had, and it has the same answer:
   --shell-800 is this app's OWN dark surface in both themes (the rail, the
   tooltips, the toasts, the sign-in canvas), so a dark lead band is the house
   voice rather than a new one, and it is the one ground on which this app's
   gold reads as gold.

   Contrast measured on #141310, not claimed: shell-ink 14.40, gold-200 14.10,
   gold-300 11.10, white 18.58, and rgba(255,255,255,.62) over it 8.7. All AA.
   =========================================================================== */
.ad-strip {
    position: relative;
    overflow: hidden;
    background: var(--shell-800);
    border-color: var(--shell-line);
    box-shadow: var(--lift-2, 0 6px 20px rgba(0, 0, 0, .16));
    color: var(--shell-ink);
    padding: 1rem 1.15rem 1.05rem;
    gap: 1.15rem;
}
/* One wash of gold from the top-left, behind everything at z-index 0 - what
   stops a large dark rectangle reading as a flat slab. No child is lifted:
   giving the BAND a stacking context and then lifting children is the fault
   that once trapped every modal in this app under its own backdrop. */
.ad-strip::before {
    content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 0;
    background: radial-gradient(120% 140% at 0% 0%,
                rgba(201, 165, 95, .17), rgba(201, 165, 95, 0) 58%);
}
.ad-strip > * { position: relative; z-index: 1; }

/* --shell-800 is a fixed token, so the band is dark in BOTH themes by
   construction. In dark mode it must LIFT or it becomes the least visible
   surface on the page. Declared twice so the toggle beats the OS both ways. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .ad-strip { background: var(--shell-700); }
}
:root[data-theme="dark"] .ad-strip { background: var(--shell-700); }

.ad-strip .ad-chip { border-right-color: rgba(255, 255, 255, .12); }
.ad-strip .ad-chip-v { color: #fff; font-size: 1.5rem; font-weight: var(--fw-bold); }
.ad-strip .ad-chip-k { color: rgba(255, 255, 255, .66); }
.ad-strip .ad-chip-s { color: rgba(255, 255, 255, .46); }
/* EXACTLY ONE GOLD FIGURE. A second leaves the band with no focal point, which
   is the fault a lead band exists to fix. The first chip is the one the page is
   about: who can actually get in. */
.ad-strip .ad-chip:first-child .ad-chip-v { color: var(--gold-200); }
/* The label under it is NOT gold. It was --gold-300, which put a second gold
   element directly under the gold figure - two focal points, and therefore
   none: the fault the shared band check found on Products and Match Review. */

/* The chips centre in the strip's height. The source chart is four rows tall,
   so top-aligned chips left a band of dark ground under three figures while
   the coverage meter beside them sat centred - two alignments in one card.
   Every chip is the same three-row grid, so their values still share a line. */
.ad-strip .ad-chip { align-content: center; }

/* The two charts, on the dark ground. Both are SINGLE-SERIES, so the fill is
   gold as chrome rather than as an encoding - the rule this project already
   applies to a button. */
.ad-strip .ad-chart-h,
.ad-strip .ad-meter-legend { color: rgba(255, 255, 255, .66); }
.ad-strip .ad-bar-track { background: rgba(255, 255, 255, .11); }
.ad-strip .ad-meter {
    background-color: rgba(255, 255, 255, .09);
    background-image: repeating-linear-gradient(135deg,
        transparent 0 5px, rgba(255, 255, 255, .14) 5px 6px);
    border-color: rgba(255, 255, 255, .16);
}
.ad-strip .ad-bar-k,
.ad-strip .ad-bar-t { color: rgba(255, 255, 255, .78); }
.ad-strip .ad-bar-v { color: #fff; }
.ad-strip .ad-bar-fill { background: var(--gold-400); }
.ad-strip .ad-bar-row:hover .ad-bar-fill { background: var(--gold-300); }
/* THE SAME CREAM-ON-BLACK FAULT AS THE PRODUCTS BAND, and it survived here for
   the same reason: this block carefully overrides every COLOUR for the dark
   strip - chip value, label, sub, track, meter, bar ink - and overrides no
   HOVER. So `.ad-bar-row:hover` and `.ad-chip.ad-live:hover` went on taking
   --accent-weak (--gold-100, #F8F1E2) from their unscoped rules further up, and
   pointing at a source bar or a clickable chip painted a near-white slab across
   the near-black strip.
   A white wash at low alpha instead: it composites over the band's own gradient
   rather than replacing it, and it is the idiom the dark rail already uses in
   layout.css. Measured on --shell-800: the .08 wash lifts the ground from
   1.00 to 1.09 relative luminance - visible as a hover, invisible as a slab. */
.ad-strip .ad-bar-row:hover { background: rgba(255, 255, 255, .08); }
.ad-strip .ad-chip.ad-live:hover { background: rgba(255, 255, 255, .08); }
/* AND THE SELECTED STATE HAD THE SAME FAULT, which the hover fix above missed.
   `.ad-bar-row.on` and `.ad-chip.ad-live.on` are --accent-weak too, so clicking
   a source bar painted a cream plate across the band with the bar's white label
   on it - the filter you had just applied became the one row you could not
   read. A stronger white wash plus the gold leading edge the chip already uses. */
.ad-strip .ad-bar-row.on,
.ad-strip .ad-chip.ad-live.on { background: rgba(255, 255, 255, .13); }
.ad-strip .ad-bar-row.on { box-shadow: inset 2px 0 0 var(--gold-400); }
.ad-strip .ad-meter-fill { background: var(--gold-400); }
.ad-strip .bi-question-circle { color: rgba(255, 255, 255, .5); }

/* The people split and the gap, on the dark ground. The series steps are the
   dark-theme ones in BOTH themes - see the people-split block above for the
   measurements. The failure ink is likewise the dark theme's --bad-ink
   (#EC8A80, 8.0:1 here), because the light theme's #96201B would vanish on a
   band that is dark in both. */
.ad-strip { --ad-seg-rep: #B37E00; --ad-seg-both: #009F8E; --ad-seg-portal: #C23751; }
.ad-strip .ad-stack-legend,
.ad-strip .ad-gap-empty { color: rgba(255, 255, 255, .66); }
.ad-strip .ad-seg-k b,
.ad-strip .ad-gap-v { color: #fff; }
.ad-strip .ad-gap-failed { color: #EC8A80; }
.ad-strip .ad-gap-i { color: rgba(255, 255, 255, .78); }
.ad-strip button.ad-seg-k:hover { background: rgba(255, 255, 255, .08); }
.ad-strip .ad-seg-k.on { background: rgba(255, 255, 255, .13); box-shadow: inset 2px 0 0 var(--gold-400); }

/* ===========================================================================
   SMALL SCREENS
   ---------------------------------------------------------------------------
   Measured at 768x1024 and 390x844. The page never scrolls sideways; a TABLE
   may, inside its own container, which is the honest answer for eight columns
   of people. Everything else reflows.
   =========================================================================== */

/* THE VIEW SWITCH WAS CLIPPED, NOT SCROLLED. It is overflow: hidden with five
   nowrap segments, so on a phone "Sales reps" and "Not a rep" were cut off at
   the card edge with no way to reach them. It may scroll along its own axis. */
.ad-views { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.ad-views::-webkit-scrollbar { display: none; }
.ad-view { flex: 0 0 auto; }

/* Prose in a dialog wraps. .ad-who-s is a one-line sub-label in a table row,
   and reused for sentences in the manage dialog it cut "Not linked to any
   sales rep record..." off mid-word at 768px and below. */
#adManageBody .ad-who-s,
#adWelcomeBody .ad-who-s { white-space: normal; overflow: visible; }

@media (max-width: 767.98px) {
    /* The strip: one chip per row reads as a list; two across keeps it a strip,
       and the two small charts share the row under them. */
    .ad-strip { padding: .9rem; gap: .9rem; }
    .ad-strip .ad-chip { flex: 1 1 42%; min-width: 0; border-right: 0; }
    .ad-strip .ad-chip-v { font-size: 1.3rem; }
    .ad-chart { flex: 1 1 42%; min-width: 0; }
    .ad-chart.wide { flex-basis: 100%; }
    .ad-bar-row { grid-template-columns: 128px minmax(40px, 1fr) 30px; }

    /* The bar: tabs on their own line, then Invite and refresh together. At 390px
       the refresh button used to wrap onto a row of its own. */
    .ad-tabs { flex: 1 1 100%; }
    .ad-tab { flex: 1 1 0; justify-content: center; }
    .ad-bar .btn-primary { margin-left: 0 !important; flex: 1 1 auto; }

    .ad-filters .form-control,
    .ad-filters .form-select { max-width: none; flex: 1 1 150px; }
    .ad-search { max-width: none !important; flex: 1 1 100% !important; }
    /* Every view visible, wrapped as separate pills, rather than a segmented
       strip that scrolls with no sign it can. */
    .ad-views { flex-wrap: wrap; gap: .3rem; border: 0; overflow: visible; flex: 1 1 100%; }
    .ad-view { border: 1px solid var(--border); border-radius: var(--r-sm); }
    .ad-view:last-child { border-right: 1px solid var(--border); }
    /* The accounts dialog: names wrap instead of pushing the figures into a
       sideways scroll a phone user would never find. */
    .ad-acct-tbl { min-width: 0; }
    .ad-acct-tbl td:first-child a { overflow-wrap: anywhere; }


    /* Rep performance: name and value on one line, the bar and the customer
       count under them, so nothing is pushed out of a 360px dialog. */
    .ad-perf-f {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas: "n v" "b c";
        row-gap: .25rem;
    }
    .ad-perf-f > span { grid-area: n; }
    .ad-perf-f > .ad-perf-bar { grid-area: b; }
    .ad-perf-f > b { grid-area: v; }
    .ad-perf-f > em { grid-area: c; }
}

/* ===========================================================================
   THE PEOPLE TABLE REFLOWS INSTEAD OF SCROLLING
   ---------------------------------------------------------------------------
   Measured in a real Edge window: at 1024px 50 row actions (Manage, Give
   access, Performance) sat past the visible edge of the table's own scroller,
   at 768 the two activity columns went with them (103 elements), at 600 154.
   No page overflow, and still a table whose one control per row a tablet user
   has to go looking for. So the columns that are read rather than acted on
   step down into a compact line under the name, in two stages, and on a phone
   each row becomes a small card. The action cell is never the one that goes.
   =========================================================================== */
.ad-row-x { display: none; }
.ad-acts { display: inline-flex; gap: .3rem; justify-content: flex-end; align-items: center; }
.ad-acts > .me-1 { margin-right: 0 !important; }

@media (max-width: 1099.98px) {
    .ad-tbl { min-width: 0; }
    .ad-tbl .ad-c-mgr, .ad-tbl .ad-c-in, .ad-tbl .ad-c-sale { display: none; }
    .ad-row-x {
        display: flex; flex-wrap: wrap; align-items: center;
        gap: .15rem .8rem;
        margin: .3rem 0 0 calc(32px + .55rem);
        font-size: var(--fs-xs); color: var(--text-soft); line-height: 1.35;
    }
    .ad-x { display: inline-flex; align-items: center; gap: .25rem; }
    .ad-x-brand { display: none; }
    /* The gap note in the compact line: one line, the cost is on its tooltip. */
    .ad-row-x .ad-gap-note { padding: .05rem .4rem; }
    .ad-row-x .ad-gap-note small { display: none; }
}
@media (max-width: 899.98px) {
    .ad-tbl .ad-c-brand { display: none; }
    .ad-x-brand { display: inline-flex; }
    /* Two buttons stack in their cell rather than widening the row past the
       table: one narrow column of actions beats a scroller. */
    .ad-acts { flex-direction: column; align-items: stretch; }
}
@media (max-width: 599.98px) {
    .ad-tbl thead { display: none; }
    .ad-tbl, .ad-tbl tbody { display: block; }
    .ad-tbl tbody tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        column-gap: .6rem;
        padding: .65rem .8rem;
        border-bottom: 1px solid var(--border-soft);
    }
    .ad-tbl tbody td { display: block; padding: 0; border: 0; }
    .ad-tbl td.ad-c-who    { grid-column: 1; grid-row: 1; min-width: 0; }
    .ad-tbl td.ad-c-acct   { grid-column: 2; grid-row: 1; align-self: start; padding-top: .1rem; }
    .ad-tbl td.ad-c-acct::after {
        content: 'accounts'; display: block;
        font-size: var(--fs-2xs); color: var(--text-dim); font-weight: var(--fw-medium);
    }
    .ad-tbl td.ad-c-access { grid-column: 1 / -1; grid-row: 2; margin: .4rem 0 0 calc(32px + .55rem); }
    .ad-tbl td.ad-c-act    { grid-column: 1 / -1; grid-row: 3; text-align: left; margin: .5rem 0 0 calc(32px + .55rem); }
    .ad-tbl .ad-c-brand, .ad-tbl .ad-c-mgr, .ad-tbl .ad-c-in, .ad-tbl .ad-c-sale { display: none; }
    .ad-acts { flex-direction: row; justify-content: flex-start; flex-wrap: wrap; }
    /* The gap rule moves from the first cell to the card, whose edge it is now. */
    .ad-tbl tr.needs-rep td:first-child, .ad-tbl tr.is-house td:first-child { box-shadow: none; }
    .ad-tbl tr.needs-rep { box-shadow: inset 3px 0 0 var(--warn); }
    .ad-tbl tr.is-house  { box-shadow: inset 3px 0 0 var(--text-dim); }
}

/* The source of a book whose accounts live in another brand than its rep. */
.ad-acct-src { white-space: normal; }

/* ==================== THE HOME VOICE (2026-09-17) ==========================
   The Admin Directory set the way Home sets itself: the strip's figures at the
   size Home gives its own, bold and tracked -.02em; uppercase tracked eyebrows
   over the charts; Home's segmented pill control for the tab switch and the
   views; pill buttons; avatar discs; rows that lift to --inset on hover; and a
   short rise on entry.

   SCOPED TO #page-admin-directory and the page's dialogs. The strip keeps its
   one gold INK rule (.ad-strip .ad-chip:first-child .ad-chip-v) and nothing
   here paints gold ink. Anything that the small-screen blocks above also set
   is fenced into a min-width query, because an id-scoped rule would otherwise
   beat them at every width. */
@keyframes adRise { from { opacity: 0; transform: translateY(10px); } }

#page-admin-directory .ad-wrap { gap: 1.05rem; }

/* ---- the strip ---- */
#page-admin-directory .ad-strip { animation: adRise .5s var(--ease-out) backwards; }
@media (min-width: 768px) {
    #page-admin-directory .ad-strip { padding: 1.2rem 1.5rem; gap: 1.2rem 1.5rem; }
    #page-admin-directory .ad-strip .ad-chip { min-width: 140px; padding-right: 1.5rem; }
    #page-admin-directory .ad-strip .ad-chip-v { font-size: var(--fs-4xl); letter-spacing: -.02em; line-height: 1.05; }
}
#page-admin-directory .ad-strip .ad-chip-k { font-size: var(--fs-sm); color: rgba(255,255,255,.82); font-weight: var(--fw-bold); margin-top: .3rem; min-height: 0; }
#page-admin-directory .ad-strip .ad-chip-s { font-size: var(--fs-xs); color: rgba(255,255,255,.58); margin-top: .15rem; }
#page-admin-directory .ad-strip .ad-chip.ad-live { border-radius: var(--r-sm); padding-left: .6rem; margin-left: -.6rem; transition: background .2s var(--ease); }
#page-admin-directory .ad-strip .ad-chart-h {
    font-size: var(--fs-2xs); font-weight: var(--fw-bold); letter-spacing: .14em;
    text-transform: uppercase; color: rgba(255,255,255,.72);
}
#page-admin-directory .ad-strip .ad-chart { gap: .5rem; }
#page-admin-directory .ad-stack, #page-admin-directory .ad-strip .ad-meter { height: 8px; }
#page-admin-directory .ad-strip .ad-bars { gap: .18rem; }
#page-admin-directory .ad-strip .ad-bar-row { border-radius: var(--r-sm); padding: .12rem .35rem; margin: 0 -.35rem; transition: background .2s var(--ease); }
#page-admin-directory .ad-strip .ad-bar-track { height: 7px; }
#page-admin-directory .ad-strip .ad-bar-v { font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
#page-admin-directory .ad-strip .ad-gap-v { font-size: var(--fs-2xl); font-weight: var(--fw-bold); letter-spacing: -.02em; }

/* ---- the tab switch: Home's segmented control ---- */
@media (min-width: 768px) {
    #page-admin-directory .ad-tabs {
        gap: 2px; padding: 3px; border-radius: var(--r-pill);
        background: var(--inset); border: 1px solid var(--border-soft);
    }
    #page-admin-directory .ad-tab {
        border: 0; background: transparent; border-radius: var(--r-pill);
        padding: .38rem .95rem; font-size: var(--fs-sm); color: var(--text-soft);
    }
    #page-admin-directory .ad-tab:hover { color: var(--text); border: 0; }
    #page-admin-directory .ad-tab.on {
        background: var(--surface); color: var(--text); font-weight: var(--fw-bold);
        box-shadow: var(--shadow); border: 0;
    }
}
#page-admin-directory .ad-tab:focus-visible, #page-admin-directory .ad-view:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ---- buttons: pills ---- */
#page-admin-directory .btn { border-radius: var(--r-pill); font-weight: var(--fw-bold); }
#page-admin-directory .btn-sm { font-size: var(--fs-xs); padding: .3rem .6rem; }
#page-admin-directory .ad-bar .btn-primary { padding: .42rem 1.15rem; box-shadow: var(--shadow); transition: transform .2s var(--ease), box-shadow .2s var(--ease); }
#page-admin-directory .ad-bar .btn-primary:hover { transform: translateY(-2px); box-shadow: var(--lift-2); }
/* The refresh disc. Keyed on the ICON, never on :only-child - a text node is
   not an element, so "icon + words" is an only-child too and Invite collapsed
   into a 34px disc. */
#page-admin-directory .btn:has(> .bi-arrow-clockwise) { width: 34px; height: 34px; padding: 0; display: inline-grid; place-items: center; }
#page-admin-directory .btn-outline-secondary {
    background: var(--inset); border-color: var(--border-soft); color: var(--text);
    transition: background .2s var(--ease), border-color .2s var(--ease), color .2s var(--ease), transform .2s var(--ease);
}
#page-admin-directory .btn-outline-secondary:hover { background: var(--surface); border-color: var(--accent-line); color: var(--accent-ink); }

/* ---- the panel and its filters ---- */
#page-admin-directory .ad-panel { position: relative; animation: adRise .55s var(--ease-out) backwards; animation-delay: 90ms; }
#page-admin-directory .ad-filters { padding: .9rem 1.2rem; border-bottom-color: var(--border-soft); }
#page-admin-directory .ad-filters .form-control, #page-admin-directory .ad-filters .form-select {
    border-radius: var(--r-pill); background-color: var(--inset); border-color: var(--border-soft);
    font-size: var(--fs-sm); font-weight: var(--fw-medium); padding-left: .95rem;
    transition: border-color .2s var(--ease), box-shadow .2s var(--ease), background-color .2s var(--ease);
}
#page-admin-directory .ad-filters .form-select { padding-right: 2.1rem; background-position: right .8rem center; }
#page-admin-directory .ad-filters .form-control:focus, #page-admin-directory .ad-filters .form-select:focus {
    border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-weak); background-color: var(--surface);
}
@media (min-width: 768px) {
    #page-admin-directory .ad-views {
        gap: 2px; padding: 3px; border-radius: var(--r-pill);
        background: var(--inset); border: 1px solid var(--border-soft);
    }
    #page-admin-directory .ad-view {
        border: 0; background: transparent; border-radius: var(--r-pill);
        padding: .3rem .75rem; font-size: var(--fs-xs); font-weight: var(--fw-medium);
    }
    #page-admin-directory .ad-view:hover { background: transparent; color: var(--text); }
    #page-admin-directory .ad-view.on { background: var(--surface); color: var(--text); font-weight: var(--fw-bold); box-shadow: var(--shadow); }
}

/* ---- the table ---- */
/* Tracked a little tighter than Home's eyebrows: a header's width sets its
   column's, and at 1100px the table already overruns its scroller. */
#page-admin-directory .ad-tbl th {
    font-size: var(--fs-3xs); font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-soft); padding: .6rem .7rem;
}
/* Horizontal padding stays at the table's own .7rem: at 1100px the eight
   columns already overran their scroller by 10px, and .8rem added 60 more. */
#page-admin-directory .ad-tbl td { padding-top: .55rem; padding-bottom: .55rem; }
#page-admin-directory .ad-tbl tbody tr:nth-child(even) { background: transparent; }
#page-admin-directory .ad-tbl tbody tr { transition: background .2s var(--ease); }
#page-admin-directory .ad-tbl tbody tr:hover { background: var(--inset); }
#page-admin-directory .ad-tbl tbody tr:nth-child(-n+12) { animation: adRise .4s var(--ease-out) backwards; }
#page-admin-directory .ad-tbl tbody tr:nth-child(2)  { animation-delay: 25ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(3)  { animation-delay: 50ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(4)  { animation-delay: 75ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(5)  { animation-delay: 100ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(6)  { animation-delay: 125ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(7)  { animation-delay: 150ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(8)  { animation-delay: 175ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(9)  { animation-delay: 200ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(10) { animation-delay: 225ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(11) { animation-delay: 250ms; }
#page-admin-directory .ad-tbl tbody tr:nth-child(12) { animation-delay: 275ms; }
#page-admin-directory .ad-who { height: 38px; }
#page-admin-directory .ad-av { font-weight: var(--fw-bold); }
#page-admin-directory .ad-who-n { font-weight: var(--fw-bold); }
#page-admin-directory .ad-who-s { font-size: var(--fs-2xs); color: var(--text-soft); }
#page-admin-directory .ad-tbl td.num { font-variant-numeric: tabular-nums; }

/* ---- the dialogs ----
   They live in index.html, not under #page-admin-directory, so they are
   reached by their own ids. */
#adManageModal .modal-content, #adAcctModal .modal-content, #adPerfModal .modal-content, #adInviteModal .modal-content, #adWelcomeModal .modal-content {
    border-radius: 18px; border: 1px solid var(--border); box-shadow: var(--shadow-lg); overflow: hidden;
}
#adManageModal .modal-title, #adAcctModal .modal-title, #adPerfModal .modal-title, #adInviteModal .modal-title, #adWelcomeModal .modal-title {
    font-size: var(--fs-lg); font-weight: var(--fw-bold); letter-spacing: -.01em;
}
#adManageModal .modal-footer .btn, #adAcctModal .modal-footer .btn, #adPerfModal .modal-footer .btn, #adInviteModal .modal-footer .btn, #adWelcomeModal .modal-footer .btn { border-radius: var(--r-pill); font-weight: var(--fw-bold); }

/* THE PRIMARY BUTTON MUST NOT BE BELOW THE FOLD.
   These two dialogs ask the same eight questions, and the invitation grew by
   three of them on 2026-09-17. Measured in Chrome against the real stylesheets:
   the invite body is 728px, and on a 768px SCREEN - roughly 650px of browser
   viewport, the figure this project already budgets the sign-in card against -
   "Create account" sat 89px under the fold. The whole dialog scrolled, so
   nothing was lost, but the primary action of the form was not on screen.
   Manage has the same shape from the other side: it measures 580px today and
   grows by ~60px for every linked rep identity, so it crosses the same line
   the moment somebody holds three.
   Capping the CONTENT and scrolling the BODY is what `modal-dialog-scrollable`
   does, and the class belongs to markup in index.html that this page does not
   own. Header and footer stay pinned; only the fields scroll.
   `min-height: 0` is the half that is easy to miss - a flex item will not
   shrink below its content without it, so the cap alone would be overridden
   from the inside and read as unfixed. Nothing in either body is sticky, so an
   overflow here changes nothing else. */
#adManageModal .modal-content,
#adInviteModal .modal-content { max-height: calc(100vh - 3.5rem); }
#adManageModal .modal-body,
#adInviteModal .modal-body { overflow-y: auto; min-height: 0; }
/* THE INVITE FORM IS THE CONTENT'S ONLY FLEX CHILD. index.html wraps the
   invitation's header, body and footer in a <form> (the submit is what makes
   Enter create the account), so it - not .modal-body - is what the capped
   .modal-content lays out. A block-level form keeps `min-height: auto`, grows
   to its content, overflows the cap and is clipped by `overflow: hidden`:
   measured at 1366x768 the Create button sat 416px below the dialog's edge
   with no scrollbar anywhere. The form has to be a flex column that may
   shrink, and then the body rule above does what it says. Welcome shares the
   markup and the rule. */
#adInviteModal .modal-content > form,
#adWelcomeModal .modal-content > form { display: flex; flex-direction: column; min-height: 0; }

@media (prefers-reduced-motion: reduce) {
    #page-admin-directory .ad-strip, #page-admin-directory .ad-panel,
    #page-admin-directory .ad-tbl tbody tr,
    /* THE :nth-child FORM HAS TO BE NAMED. The row entrance is declared on
       `... tbody tr:nth-child(-n+12)` (1,2,2) and the plain selector above is
       (1,1,2), so this opt-out lost the cascade and twelve rows went on rising
       for a reader who had asked for no motion. Measured in headless Chrome
       with the preference set: twelve adRise animations still running. */
    #page-admin-directory .ad-tbl tbody tr:nth-child(-n+12) { animation: none; }
    #page-admin-directory .ad-bar .btn-primary:hover { transform: none; }
}

/* The identity picker's search box and its Link button sit at the full field
   size, so the last row of Manage reads as part of the same form. */
#adManageModal .ad-pick-row .btn { min-height: 38px; font-size: var(--fs-md); border-radius: var(--r-pill); padding-inline: 1.1rem; font-weight: var(--fw-bold); }

/* ===========================================================================
   THE ORG CHART - a canvas
   ---------------------------------------------------------------------------
   Top-down, on a dotted ground that pans and zooms. Card geometry is FIXED
   (256 x 120) because the layout in admin-directory.js is arithmetic over
   those two numbers - change one here and OC.W / OC.H must follow, or the
   connectors land beside the cards they join.

   Role colour is --s1..--s4, the four categorical colours validated all-pairs.
   It is never the only carrier: every card prints its role badge.
   =========================================================================== */

#page-admin-directory .ad-oc {
    --oc-dot:   rgba(17, 17, 19, .13);
    --oc-glow:  rgba(178, 140, 84, .13);
    --oc-wire:  #84868F;   /* 3.10:1 on --inset: a connector carries the reporting line, so it is held to the 3:1 non-text floor */
    --oc-glass: rgba(255, 255, 255, .84);
    position: relative;
    overflow: hidden;
    padding: 0;
    isolation: isolate;
    background:
        radial-gradient(circle at 1px 1px, var(--oc-dot) 1px, transparent 1.6px) 0 0 / 22px 22px,
        radial-gradient(ellipse 70% 55% at 50% 0%, var(--oc-glow), transparent 72%),
        var(--inset);
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) #page-admin-directory .ad-oc {
        --oc-dot:   rgba(255, 255, 255, .075);
        --oc-glow:  rgba(178, 140, 84, .10);
        --oc-wire:  #66625A;   /* 3.16:1 on dark --inset */
        --oc-glass: rgba(28, 26, 22, .86);
    }
}
:root[data-theme="dark"] #page-admin-directory .ad-oc {
    --oc-dot:   rgba(255, 255, 255, .075);
    --oc-glow:  rgba(178, 140, 84, .10);
    --oc-wire:  #66625A;   /* 3.16:1 on dark --inset */
    --oc-glass: rgba(28, 26, 22, .86);
}

#page-admin-directory .ad-oc .s-0 { --oc-tone: var(--text-dim); }
#page-admin-directory .ad-oc .s-1 { --oc-tone: var(--s1); }
#page-admin-directory .ad-oc .s-2 { --oc-tone: var(--s2); }
#page-admin-directory .ad-oc .s-3 { --oc-tone: var(--s3); }
#page-admin-directory .ad-oc .s-4 { --oc-tone: var(--s4); }

/* ---- the moving parts ---- */

#page-admin-directory .ad-oc-view {
    position: absolute; inset: 0;
    overflow: hidden;
    cursor: grab;
    /* Vertical touch still scrolls the page; a horizontal drag moves the chart. */
    touch-action: pan-y;
    user-select: none;
}
#page-admin-directory .ad-oc-view.is-grab { cursor: grabbing; }

#page-admin-directory .ad-oc-stage { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
#page-admin-directory .ad-oc-stage.glide { transition: transform .45s var(--ease-out); }

#page-admin-directory .ad-oc-wires { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }
#page-admin-directory .ad-oc-wire {
    fill: none;
    stroke: var(--oc-wire);
    stroke-width: 1.75;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 1;
    transition: d .45s var(--ease-out), stroke .2s var(--ease), stroke-width .2s var(--ease),
                opacity .25s var(--ease);
}
/* ---- THE CHART ARRIVES ONCE ----
   Scoped under .ad-gx, which admin-directory.js puts on the chart frame for
   the first DRAWN pass only. _render() repaints this pane - a view chip, a
   search keystroke, a role filter, a tab switch - and unscoped the whole chart
   re-animated each time. (It is NOT rebuilt by expand, collapse, zoom, drag or
   a legend press: those go through _ocRelayout(), measured. An earlier draft of
   this note said they did and was wrong.)

   THE WIRES FADE, THEY NO LONGER DRAW. This was
   `@keyframes adOcDraw { from { stroke-dashoffset: 1 } }` over 800ms: a
   property that is neither transform nor opacity, at more than twice the house
   duration band, in the one file whose ring-arc note two pages up says "not a
   stroke draw". A connector arriving is an arrival either way, and opacity says
   it in 320ms on the compositor.

   THE STAGGER IS BY DEPTH, and --d is the node's depth rather than its index,
   so it is bounded by the HEIGHT of the tree rather than by its size - three
   levels on the live hierarchy. Capped at six anyway, and at 30ms rather than
   the 120ms it carried, so the deepest wire starts 210ms in instead of 500. */
#page-admin-directory .ad-oc.ad-gx .ad-oc-wire {
    animation: adOcWireIn .32s var(--ease-out) backwards;
    animation-delay: calc(min(var(--d, 0), 5) * 30ms + 60ms);
}
@keyframes adOcWireIn { from { opacity: 0; } }
#page-admin-directory .ad-oc-wire.gone { opacity: 0; }
/* A lit line of management: the hovered card's chain, gold, all the way up. */
#page-admin-directory .ad-oc-wire.lit,
#page-admin-directory .ad-oc-wire.is-path {
    stroke: var(--gold-500);
    stroke-width: 2.75;
    filter: drop-shadow(0 0 4px rgba(178, 140, 84, .45));
}

/* ---- the card ---- */

#page-admin-directory .ad-oc-node {
    position: absolute; left: 0; top: 0;
    width: 256px; height: 120px;
    outline: none;
    transition: transform .45s var(--ease-out), opacity .25s var(--ease);
}
#page-admin-directory .ad-oc-stage:not(.folding) .ad-oc-node { transition: opacity .3s var(--ease), filter .3s var(--ease); }
#page-admin-directory .ad-oc-node.gone { opacity: 0; visibility: hidden; pointer-events: none;
                   transition: transform .45s var(--ease-out), opacity .2s var(--ease), visibility 0s .45s; }

#page-admin-directory .ad-oc .ad-node {
    position: relative;
    box-sizing: border-box;
    height: 100%;
    display: flex; flex-direction: column; justify-content: space-between;
    padding: .7rem .8rem .6rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: var(--lift-1, var(--shadow));
    overflow: hidden;
    cursor: pointer;
    transition: transform .25s var(--ease-out), box-shadow .25s var(--ease-out),
                border-color .25s var(--ease-out);
}
/* The cards, on the same first-paint gate and the same stagger as the wires
   above. 550ms -> 320ms: the house band is 160-320 and this rule sat outside
   it, which the lane's own "durations were normalised" note did not cover. */
#page-admin-directory .ad-oc.ad-gx .ad-node {
    animation: adOcIn .32s var(--ease-out) backwards;
    animation-delay: calc(min(var(--d, 0), 5) * 30ms);
}
@keyframes adOcIn { from { opacity: 0; transform: translateY(14px) scale(.96); } }

/* The role band across the top, and a soft wash of the same tone behind the
   avatar - the colour is an accent, the badge is the identity. */
#page-admin-directory .ad-oc .ad-node::before {
    content: ''; position: absolute; left: 0; right: 0; top: 0; height: 3px;
    background: linear-gradient(90deg, var(--oc-tone), transparent 115%);
}
#page-admin-directory .ad-oc .ad-node::after {
    content: ''; position: absolute; left: -46px; top: -58px; width: 160px; height: 160px;
    border-radius: 50%; pointer-events: none;
    background: radial-gradient(closest-side, var(--oc-tone), transparent);
    opacity: .09;
}

#page-admin-directory .ad-oc-node:hover > .ad-node {
    transform: translateY(-3px);
    border-color: var(--gold-400);
    box-shadow: var(--lift-2, var(--shadow-lg));
}
#page-admin-directory .ad-oc-node.is-up > .ad-node { border-color: var(--gold-400); }
#page-admin-directory .ad-oc-node.is-sel > .ad-node {
    border-color: var(--gold-500);
    box-shadow: 0 0 0 2px var(--gold-500), var(--lift-gold, var(--lift-2, var(--shadow-lg)));
}
#page-admin-directory .ad-oc-node:focus-visible > .ad-node {
    box-shadow: 0 0 0 3px var(--accent-line), 0 0 0 1.5px var(--gold-500);
}
#page-admin-directory .ad-oc-node.is-me > .ad-node { border-color: var(--gold-300); }
#page-admin-directory .ad-oc-node.is-off > .ad-node { background: var(--surface-2); }
#page-admin-directory .ad-oc-node.is-off .ad-n-name { color: var(--text-soft); }
#page-admin-directory .ad-oc-node.is-orphan > .ad-node { border-style: dashed; border-color: var(--warn-line); }

/* THE LENS. Whoever is outside the filter, or outside the hovered or selected
   line of management, goes black and white and steps back; the rest keep
   their colour. The filter is on the positioned wrapper, so the team chip
   below the card goes grey with it. */
#page-admin-directory .ad-oc-node.is-mono { filter: grayscale(1); opacity: .5; }
#page-admin-directory .ad-oc-node.is-hit > .ad-node { border-color: var(--gold-500); }
#page-admin-directory .ad-oc-wire.is-mono { stroke: var(--oc-wire); opacity: .3; filter: none; }
#page-admin-directory .ad-oc-mini rect.is-mono { fill: var(--text-dim); opacity: .3; }
/* GONE BEATS GREY. Same specificity and later in the file, the grey rules
   drew hidden connectors at 30% during a filtered fold. */
#page-admin-directory .ad-oc-wire.gone.is-mono, #page-admin-directory .ad-oc-node.gone.is-mono { opacity: 0; }

/* FAR AWAY, A CARD IS A NAME (admin-directory.js sets .lod-far below OC.FAR).
   At half size a 13px name is 6px on screen; drawn at 26px it is 13px. The
   card keeps its box, so nothing re-flows while zooming through it. */
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-n-sub,
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-oc-tags,
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-oc-foot { display: none; }
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-node { justify-content: center; padding: .6rem 1rem; }
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-oc-top { align-items: center; gap: .9rem; }
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-oc-av { width: 62px; height: 62px; flex-basis: 62px; padding: 3px; }
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-oc-av > span { font-size: var(--fs-xl); }
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-oc-dot { width: 18px; height: 18px; }
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-n-name {
    font-size: 1.6rem; line-height: 1.12; white-space: normal;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
#page-admin-directory .ad-oc.lod-far .ad-oc-stage .ad-oc-kids { transform: translateX(-50%) scale(1.35); transform-origin: 50% 50%; }

#page-admin-directory .ad-oc-top { position: relative; z-index: 1; display: flex; align-items: flex-start; gap: .7rem; min-width: 0; }
#page-admin-directory .ad-oc-id  { min-width: 0; flex: 1 1 auto; }
#page-admin-directory .ad-oc .ad-n-name {
    font-size: var(--fs-md); font-weight: var(--fw-bold); letter-spacing: -.01em;
    color: var(--text); line-height: 1.25;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#page-admin-directory .ad-oc .ad-n-sub {
    font-size: var(--fs-xs); color: var(--text-dim); line-height: 1.35;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#page-admin-directory .ad-oc-tags { display: flex; align-items: center; gap: .25rem; margin-top: .3rem; min-width: 0; overflow: hidden; }
#page-admin-directory .ad-oc-tags .ad-role, #page-admin-directory .ad-oc-tags .ad-badge {
    padding: 0 .4rem; font-size: var(--fs-2xs); line-height: 1.6; font-weight: var(--fw-medium);
}
/* The subtitle is a row: the job title truncates, the unit badge sits beside it. */
#page-admin-directory .ad-oc .ad-n-sub { display: flex; align-items: center; gap: .3rem; min-width: 0; }
#page-admin-directory .ad-oc-job { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#page-admin-directory .ad-oc .ad-n-sub .bbadge { width: 18px; height: 18px; }
#page-admin-directory .ad-oc-hero-s { display: flex; align-items: center; justify-content: center; gap: .35rem; }
#page-admin-directory .ad-oc-tags > * { flex: 0 0 auto; }
/* The Admin badge is a near-black plate, which on a dark card is the card. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) #page-admin-directory .ad-oc .ad-role.r-admin { border-color: rgba(255, 255, 255, .22); }
}
:root[data-theme="dark"] #page-admin-directory .ad-oc .ad-role.r-admin { border-color: rgba(255, 255, 255, .22); }

/* The avatar is a ring in the role's tone around the initials. */
#page-admin-directory .ad-oc-av {
    position: relative;
    width: 40px; height: 40px; flex: 0 0 40px;
    border-radius: 50%;
    padding: 2.5px;
    background: conic-gradient(from 210deg, var(--oc-tone), var(--gold-300), var(--oc-tone));
}
#page-admin-directory .ad-oc-av > span {
    width: 100%; height: 100%; border-radius: 50%;
    display: grid; place-items: center;
    background: var(--surface);
    box-shadow: inset 0 0 0 2px var(--surface);
    color: var(--text);
    font-size: var(--fs-sm); font-weight: var(--fw-bold); letter-spacing: .02em;
}
#page-admin-directory .ad-oc-av.ghost { background: var(--border); }
#page-admin-directory .ad-oc-av.ghost > span { color: var(--text-soft); }
#page-admin-directory .ad-oc-dot {
    position: absolute; right: -1px; bottom: -1px;
    width: 13px; height: 13px; border-radius: 50%;
    background: var(--text-dim);
    border: 2.5px solid var(--surface);
}
#page-admin-directory .ad-oc-dot.on { background: var(--ok); }

#page-admin-directory .ad-oc-you {
    display: inline-flex; align-items: center;
    padding: 0 .35rem; border-radius: var(--r-pill);
    font-size: var(--fs-3xs); font-weight: var(--fw-bold); line-height: 1.7;
    letter-spacing: .06em; text-transform: uppercase;
    background: var(--shell-800); color: var(--gold-200);
}

#page-admin-directory .ad-oc-foot {
    position: relative; z-index: 1;
    display: flex; align-items: center; justify-content: space-between; gap: .6rem;
    padding-top: .5rem;
    border-top: 1px dashed var(--border);
    min-width: 0;
}
#page-admin-directory .ad-oc-fact {
    display: inline-flex; align-items: center; gap: .3rem;
    font-size: var(--fs-xs); color: var(--text-soft);
    white-space: nowrap; min-width: 0; overflow: hidden;
}
#page-admin-directory .ad-oc-fact i { color: var(--text-dim); }
#page-admin-directory .ad-oc-fact b { color: var(--text); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
#page-admin-directory .ad-oc-fact.is-none { color: var(--text-dim); }
#page-admin-directory .ad-oc-fact .ad-brands { margin-left: .2rem; }

/* The team chip sits ON the card's bottom edge, where the connector leaves -
   the fold control is exactly where the eye goes next. */
#page-admin-directory .ad-oc-kids {
    position: absolute; left: 50%; bottom: -12px; z-index: 2;
    transform: translateX(-50%);
    display: inline-flex; align-items: center;
    height: 24px; padding: 0 .6rem;
    border-radius: var(--r-pill);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-soft);
    font-size: var(--fs-2xs); font-weight: var(--fw-medium);
    white-space: nowrap;
    box-shadow: var(--lift-1, var(--shadow));
    cursor: pointer;
    transition: background .2s var(--ease), color .2s var(--ease), border-color .2s var(--ease);
}
#page-admin-directory .ad-oc-kids > span { display: inline-flex; align-items: center; gap: .35rem; }
#page-admin-directory .ad-oc-kids em { font-style: normal; color: var(--text-dim); }
#page-admin-directory .ad-oc-kids .bi-chevron-up, #page-admin-directory .ad-oc-kids .bi-chevron-down { font-size: var(--fs-2xs); margin-left: .1rem; }
#page-admin-directory .ad-oc-kids:hover { border-color: var(--gold-400); background: var(--accent-weak); color: var(--accent-ink); }
#page-admin-directory .ad-oc-kids:focus-visible { outline: 2px solid var(--gold-500); outline-offset: 2px; }
#page-admin-directory .ad-oc-shut { display: none !important; }
#page-admin-directory .ad-oc-node.collapsed .ad-oc-open { display: none !important; }
#page-admin-directory .ad-oc-node.collapsed .ad-oc-shut { display: inline-flex !important; }
/* Folded, the chip turns dark: something is hidden under here. */
#page-admin-directory .ad-oc-node.collapsed .ad-oc-kids {
    background: var(--shell-800); border-color: var(--shell-800); color: #fff;
}
#page-admin-directory .ad-oc-node.collapsed .ad-oc-kids b { color: var(--gold-300); font-weight: var(--fw-bold); }
#page-admin-directory .ad-oc-node.collapsed .ad-oc-kids:hover { background: var(--shell-700); color: #fff; }

/* ---- the floating chrome ---- */

#page-admin-directory .ad-oc-tools, #page-admin-directory .ad-oc-count, #page-admin-directory .ad-oc-legend, #page-admin-directory .ad-oc-mini {
    position: absolute; z-index: 5;
    background: var(--oc-glass);
    backdrop-filter: blur(10px) saturate(1.15);
    -webkit-backdrop-filter: blur(10px) saturate(1.15);
    border: 1px solid var(--border);
    box-shadow: var(--lift-2, var(--shadow));
}
#page-admin-directory .ad-oc-tools {
    left: 14px; top: 14px;
    display: flex; align-items: center; gap: .15rem;
    padding: .3rem;
    border-radius: 14px;
}
#page-admin-directory .ad-oc-find {
    display: flex; align-items: center; gap: .45rem;
    height: 34px; padding: 0 .65rem; margin: 0;
    border-radius: 10px;
    color: var(--text-dim);
    transition: background .2s var(--ease), box-shadow .2s var(--ease);
}
#page-admin-directory .ad-oc-find:focus-within { background: var(--surface); box-shadow: 0 0 0 2px var(--accent-line); }
#page-admin-directory .ad-oc-find input {
    width: 170px; border: 0; outline: 0; background: transparent;
    font-size: var(--fs-sm); color: var(--text);
}
#page-admin-directory .ad-oc-find input::placeholder { color: var(--text-dim); }
#page-admin-directory .ad-oc-find-n { font-size: var(--fs-2xs); color: var(--text-dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
#page-admin-directory .ad-oc-sep { width: 1px; height: 20px; background: var(--border); margin: 0 .3rem; }
#page-admin-directory .ad-oc-btn {
    width: 34px; height: 34px; border: 0; border-radius: 10px;
    display: grid; place-items: center;
    background: transparent; color: var(--text-soft);
    transition: background .15s var(--ease), color .15s var(--ease);
}
#page-admin-directory .ad-oc-btn:hover { background: var(--inset); color: var(--text); }
#page-admin-directory .ad-oc-btn:focus-visible { outline: 2px solid var(--gold-500); outline-offset: 1px; }
#page-admin-directory .ad-oc-zoom {
    min-width: 46px; text-align: center;
    font-size: var(--fs-xs); font-weight: var(--fw-medium);
    color: var(--text-soft); font-variant-numeric: tabular-nums;
}

#page-admin-directory .ad-oc-count {
    right: 14px; top: 14px;
    padding: .5rem .85rem;
    border-radius: var(--r-pill);
    font-size: var(--fs-sm); color: var(--text-soft);
}
#page-admin-directory .ad-oc-count b { color: var(--text); font-weight: var(--fw-bold); }

#page-admin-directory .ad-oc-legend {
    left: 14px; bottom: 14px;
    max-width: calc(100% - 232px);
    display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .45rem;
    padding: .3rem .45rem .3rem .8rem;
    border-radius: var(--r-pill);
    font-size: var(--fs-xs); color: var(--text-soft);
}
#page-admin-directory .ad-oc-lg-h { font-size: var(--fs-3xs); font-weight: var(--fw-bold); letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-dim); margin-right: -.3rem; }
#page-admin-directory .ad-oc-lg-sep { width: 1px; height: 18px; background: var(--border); }
#page-admin-directory .ad-oc-lg {
    display: inline-flex; align-items: center; gap: .35rem; white-space: nowrap;
    height: 26px; padding: 0 .55rem; border: 1px solid transparent; border-radius: var(--r-pill);
    background: transparent; color: var(--text-soft); font-size: var(--fs-xs);
    transition: background .15s var(--ease), border-color .15s var(--ease), color .15s var(--ease);
}
#page-admin-directory .ad-oc-lg:hover { background: var(--inset); color: var(--text); }
#page-admin-directory .ad-oc-lg:focus-visible { outline: 2px solid var(--gold-500); outline-offset: 1px; }
#page-admin-directory .ad-oc-lg em { font-style: normal; color: var(--text-soft); font-variant-numeric: tabular-nums; }
#page-admin-directory .ad-oc-lg.on { background: var(--shell-800); border-color: var(--shell-800); color: #fff; }
#page-admin-directory .ad-oc-lg.on em { color: var(--gold-300); }
#page-admin-directory .ad-oc-lg .bbadge { width: 18px; height: 18px; }
#page-admin-directory .ad-oc-clear {
    display: inline-flex; align-items: center; gap: .4rem; height: 26px; padding: 0 .6rem;
    border: 1px solid var(--gold-400); border-radius: var(--r-pill);
    background: var(--accent-weak); color: var(--accent-ink); font-size: var(--fs-xs);
}
#page-admin-directory .ad-oc-clear b { font-weight: var(--fw-bold); }
#page-admin-directory .ad-oc-clear:hover { border-color: var(--gold-500); }
#page-admin-directory .ad-oc-folded {
    margin-left: .5rem; padding: .05rem .45rem; border-radius: var(--r-pill);
    background: var(--accent-weak); color: var(--accent-ink); font-size: var(--fs-2xs);
}
#page-admin-directory .ad-oc-kids-m { color: var(--gold-300) !important; }
#page-admin-directory .ad-oc-sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; background: var(--oc-tone); }
#page-admin-directory .ad-oc-hint { color: var(--text-dim); padding: 0 .4rem 0 .7rem; border-left: 1px solid var(--border); white-space: nowrap; font-size: var(--fs-xs); }
#page-admin-directory .ad-oc-hint i { margin-right: .3rem; }
#page-admin-directory .ad-oc-tail { display: contents; }
/* ONE ROW ON A LAPTOP. At 1366 the bar wrapped to two rows over the cards.
   Below 1500px the section labels and the hint go, and a brand chip keeps
   its badge and count while its name moves to the chip's tooltip and
   aria-label - the badge's initials are still printed, so colour never
   carries the brand alone. */
@media (max-width: 1499.98px) {
    #page-admin-directory .ad-oc-lg-h, #page-admin-directory .ad-oc-hint, #page-admin-directory .ad-oc-lg .ad-oc-lg-n { display: none; }
    #page-admin-directory .ad-oc-lg { padding: 0 .45rem; gap: .3rem; }
}
@media (max-width: 1199.98px) {
    #page-admin-directory .ad-oc-legend { gap: .2rem .25rem; padding: .25rem .3rem .25rem .5rem; }
    #page-admin-directory .ad-oc-lg, #page-admin-directory .ad-oc-clear { padding: 0 .35rem; gap: .25rem; font-size: var(--fs-2xs); }
    #page-admin-directory .ad-oc-lg-sep { display: none; }
}

#page-admin-directory .ad-oc-mini {
    right: 14px; bottom: 14px;
    display: none;
    padding: 8px;
    border-radius: 12px;
    cursor: pointer;
}
#page-admin-directory .ad-oc.has-mini .ad-oc-mini { display: block; }
#page-admin-directory .ad-oc-mini svg { display: block; }
#page-admin-directory .ad-oc-mini rect { fill: var(--oc-tone); opacity: .75; }
#page-admin-directory .ad-oc-mini rect.ad-oc-mini-vp { fill: rgba(178, 140, 84, .12); stroke: var(--gold-500); stroke-width: 1.5; opacity: 1; }

/* ---- the spotlight: who this person is, and where they sit ---- */

#page-admin-directory .ad-oc-spot {
    position: absolute; top: 12px; right: 12px; bottom: 12px; z-index: 6;
    width: 336px;
    display: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 18px;
    box-shadow: var(--shadow-lg);
    overflow-x: hidden; overflow-y: auto;
    padding-bottom: 1.1rem;
    scrollbar-width: thin;
}
#page-admin-directory .ad-oc.has-spot .ad-oc-spot { display: block; animation: adOcSlide .42s var(--ease-out) backwards; }
@keyframes adOcSlide { from { opacity: 0; transform: translateX(28px); } }
#page-admin-directory .ad-oc.has-spot .ad-oc-count { display: none; }
/* With the spotlight open there is no room for the minimap beside the filter
   bar - moved left of the panel it sat on top of it - so it steps aside. */
#page-admin-directory .ad-oc.has-spot .ad-oc-mini  { display: none; }
#page-admin-directory .ad-oc.has-spot .ad-oc-legend { max-width: calc(100% - 380px); }
#page-admin-directory .ad-oc.has-spot .ad-oc-hint { display: none; }
/* On a narrow screen the spotlight leaves no room for the filter bar beside
   it; it returns the moment the panel closes. */
@media (max-width: 1299.98px) {
    #page-admin-directory .ad-oc.has-spot .ad-oc-legend { display: none; }
}

#page-admin-directory .ad-oc-x {
    position: absolute; top: 10px; right: 10px; z-index: 2;
    width: 30px; height: 30px; border: 0; border-radius: 50%;
    display: grid; place-items: center;
    background: rgba(255, 255, 255, .1); color: #fff;
    transition: background .15s var(--ease);
}
#page-admin-directory .ad-oc-x:hover { background: rgba(255, 255, 255, .2); }

#page-admin-directory .ad-oc-hero {
    position: relative; overflow: hidden;
    padding: 1.6rem 1.2rem 1.15rem;
    text-align: center;
    background: var(--shell-800);
    color: #fff;
}
#page-admin-directory .ad-oc-hero::before {
    content: ''; position: absolute; left: 50%; top: -120px;
    width: 340px; height: 260px; transform: translateX(-50%);
    background: radial-gradient(closest-side, var(--oc-tone), transparent);
    opacity: .38; pointer-events: none;
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) #page-admin-directory .ad-oc-hero { background: var(--shell-700); }
}
:root[data-theme="dark"] #page-admin-directory .ad-oc-hero { background: var(--shell-700); }
#page-admin-directory .ad-oc-hero > * { position: relative; }
#page-admin-directory .ad-oc-av.lg { width: 78px; height: 78px; margin: 0 auto .75rem; padding: 3px; }
#page-admin-directory .ad-oc-av.lg > span { background: var(--shell-700); box-shadow: inset 0 0 0 3px var(--shell-800); color: var(--gold-200); font-size: var(--fs-xl); }
#page-admin-directory .ad-oc-av.lg .ad-oc-dot { width: 17px; height: 17px; border-color: var(--shell-800); right: 2px; bottom: 2px; }
#page-admin-directory .ad-oc-hero-n { font-size: var(--fs-xl); font-weight: var(--fw-bold); letter-spacing: -.01em; }
#page-admin-directory .ad-oc-hero-s { font-size: var(--fs-sm); color: rgba(255, 255, 255, .72); margin-top: .1rem; }
#page-admin-directory .ad-oc-hero-c { display: flex; justify-content: center; flex-wrap: wrap; gap: .35rem; margin-top: .65rem; }
#page-admin-directory .ad-oc-hero .ad-role.r-admin { border-color: rgba(255, 255, 255, .24); }
#page-admin-directory .ad-oc-hero .ad-oc-you { background: rgba(255, 255, 255, .12); }

#page-admin-directory .ad-oc-sec { padding: 1rem 1.1rem 0; }
#page-admin-directory .ad-oc-sec-h {
    font-size: var(--fs-2xs); font-weight: var(--fw-bold);
    letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-dim); margin-bottom: .5rem;
}
#page-admin-directory .ad-oc-sec-h span { text-transform: none; letter-spacing: 0; font-weight: var(--fw-medium); color: var(--text-soft); }
#page-admin-directory .ad-oc-chain, #page-admin-directory .ad-oc-list { display: grid; gap: .35rem; }
#page-admin-directory .ad-oc-chev { display: block; width: 36px; text-align: center; color: var(--text-dim); font-size: var(--fs-2xs); line-height: 1; }

#page-admin-directory .ad-oc-pp {
    display: flex; align-items: center; gap: .6rem;
    width: 100%; min-width: 0;
    padding: .4rem .6rem;
    border: 1px solid var(--border-soft); border-radius: 12px;
    background: var(--surface); text-align: left;
    transition: border-color .15s var(--ease), background .15s var(--ease);
}
#page-admin-directory .ad-oc-pp:hover { border-color: var(--gold-400); background: var(--accent-weak); }
#page-admin-directory .ad-oc-pp-n { flex: 1 1 auto; min-width: 0; font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text);
              white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#page-admin-directory .ad-oc-pp-r { flex: 0 0 auto; font-size: var(--fs-2xs); color: var(--text-dim); }
#page-admin-directory .ad-oc-av.xs { width: 28px; height: 28px; flex-basis: 28px; padding: 1.5px; }
#page-admin-directory .ad-oc-av.xs > span { font-size: var(--fs-2xs); box-shadow: none; }

#page-admin-directory .ad-oc-muted { display: flex; align-items: center; gap: .45rem; font-size: var(--fs-sm); color: var(--text-dim); padding: .25rem 0; }

#page-admin-directory .ad-oc-dl {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    gap: .5rem 1rem; margin: 0;
    font-size: var(--fs-sm);
}
#page-admin-directory .ad-oc-dl dt { font-weight: var(--fw-normal); color: var(--text-dim); }
#page-admin-directory .ad-oc-dl dd { margin: 0; text-align: right; color: var(--text); overflow-wrap: anywhere; }
#page-admin-directory .ad-oc-dl a { color: var(--accent-ink); text-decoration: none; }
#page-admin-directory .ad-oc-dl a:hover { text-decoration: underline; }
#page-admin-directory .ad-oc-none { color: var(--text-dim); }

#page-admin-directory .ad-oc-acts { display: flex; gap: .5rem; padding: 1.15rem 1.1rem 0; }
#page-admin-directory .ad-oc-acts .btn { flex: 1 1 0; border-radius: var(--r-pill); font-weight: var(--fw-bold); }

@media (max-width: 767.98px) {
    #page-admin-directory .ad-oc-tools { right: 14px; flex-wrap: wrap; }
    #page-admin-directory .ad-oc-find { flex: 1 1 100%; }
    #page-admin-directory .ad-oc-find input { width: 100%; }
    #page-admin-directory .ad-oc-count, #page-admin-directory .ad-oc-hint, #page-admin-directory .ad-oc.has-mini .ad-oc-mini { display: none; }
    #page-admin-directory .ad-oc-spot { left: 12px; width: auto; }
}

@media (prefers-reduced-motion: reduce) {
    #page-admin-directory .ad-oc .ad-node, #page-admin-directory .ad-oc-wire, #page-admin-directory .ad-oc.has-spot .ad-oc-spot,
    /* The gated forms: the entrance moved onto .ad-gx and those selectors are
       (1,3,0), so the plain pair above no longer reaches them. */
    #page-admin-directory .ad-oc.ad-gx .ad-node,
    #page-admin-directory .ad-oc.ad-gx .ad-oc-wire { animation: none; }
    #page-admin-directory .ad-oc-stage.glide, #page-admin-directory .ad-oc-node, #page-admin-directory .ad-oc-node.gone, #page-admin-directory .ad-oc .ad-node, #page-admin-directory .ad-oc-wire { transition: none; }
    #page-admin-directory .ad-oc-node:hover > .ad-node { transform: none; }
}
