/* The column is narrow on purpose. A settings row read at 1100px wide puts a
   toggle a hand's width from the label it belongs to, and the eye stops
   pairing them. 46rem is the widest this stays legible. */
.set2-col { max-width: 46rem; margin: 0 auto; padding: 2.25rem 2rem 6rem; }

/* ══ SCROLLBARS ══════════════════════════════════════════════════════════
   ONE TREATMENT, EVERY SCROLL REGION ON THIS SURFACE.

   Each of these carried `scrollbar-width: thin` and nothing else, which is the
   half of the contract that BREAKS the other half: declaring it switches
   Chrome to the standard scrollbar, and the standard scrollbar ignores the
   `::-webkit-scrollbar` rules the design system styles the rest of the product
   with. So every one of them — the scope picker, the popovers, the skill
   library, the tenancy canvas — grew a pale native bar down its edge while
   everything around it had the quiet one.

   `scrollbar-color` is the standard-scrollbar equivalent and carries the DS's
   own values; the webkit rules stay for the engines that still read those.
   Listed rather than applied to `*`, because a rule that broad would also
   reach the page scroller the shell owns.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-pick-bd,
.set2-pop,
.set2-pop-bd,
.set2-sc-canvas,
.set2-body {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.14) transparent;
}
.set2-pick-bd::-webkit-scrollbar,
.set2-pop::-webkit-scrollbar,
.set2-pop-bd::-webkit-scrollbar,
.set2-sc-canvas::-webkit-scrollbar,
.set2-body::-webkit-scrollbar { width: 0.3125rem; height: 0.3125rem; }
.set2-pick-bd::-webkit-scrollbar-track,
.set2-pop::-webkit-scrollbar-track,
.set2-pop-bd::-webkit-scrollbar-track,
.set2-sc-canvas::-webkit-scrollbar-track,
.set2-body::-webkit-scrollbar-track { background: transparent; }
.set2-pick-bd::-webkit-scrollbar-thumb,
.set2-pop::-webkit-scrollbar-thumb,
.set2-pop-bd::-webkit-scrollbar-thumb,
.set2-sc-canvas::-webkit-scrollbar-thumb,
.set2-body::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.14); border-radius: 0.1875rem;
}
.set2-pick-bd::-webkit-scrollbar-thumb:hover,
.set2-pop::-webkit-scrollbar-thumb:hover,
.set2-pop-bd::-webkit-scrollbar-thumb:hover,
.set2-sc-canvas::-webkit-scrollbar-thumb:hover,
.set2-body::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.24); }

:root[data-theme="light"] .set2-pick-bd,
:root[data-theme="light"] .set2-pop,
:root[data-theme="light"] .set2-pop-bd,
:root[data-theme="light"] .set2-sc-canvas,
:root[data-theme="light"] .set2-body { scrollbar-color: rgba(16, 24, 40, 0.18) transparent; }
:root[data-theme="light"] .set2-pick-bd::-webkit-scrollbar-thumb,
:root[data-theme="light"] .set2-pop::-webkit-scrollbar-thumb,
:root[data-theme="light"] .set2-pop-bd::-webkit-scrollbar-thumb,
:root[data-theme="light"] .set2-sc-canvas::-webkit-scrollbar-thumb,
:root[data-theme="light"] .set2-body::-webkit-scrollbar-thumb { background: rgba(16, 24, 40, 0.18); }
:root[data-theme="light"] .set2-pick-bd::-webkit-scrollbar-thumb:hover,
:root[data-theme="light"] .set2-pop::-webkit-scrollbar-thumb:hover,
:root[data-theme="light"] .set2-pop-bd::-webkit-scrollbar-thumb:hover,
:root[data-theme="light"] .set2-sc-canvas::-webkit-scrollbar-thumb:hover,
:root[data-theme="light"] .set2-body::-webkit-scrollbar-thumb:hover { background: rgba(16, 24, 40, 0.30); }

/* ══ HEAD ════════════════════════════════════════════════════════════════ */

/* ── WHY THE TITLE STOPPED SHOUTING ──
   It was `--ty-display` at bold — the size and weight reserved for a DOCUMENT
   title, on a page whose title is one word. Across the surveyed set (Plain,
   Obvious, Arcade, GitBook) the settings title is set at roughly body-plus-one
   and semibold; none of them are heavy. The reason is structural: on a
   document the title is the content, and on a settings page the CONTROLS are
   the content and the title is a label for where you are.

   The size is kept and the weight comes off. Hierarchy here is carried by the
   scope line directly beneath it, which is the thing that actually changes.

   NO LEDE PARAGRAPH under it, deliberately — every surveyed screen has one and
   this file's own T1 rule removed them on the grounds that layout carries
   meaning and copy does not. That decision stands; it is not overturned by
   five products agreeing with each other. */
/* ── THE HEADINGS WEAR THE PRODUCT'S HEADING FACE ──
   Every heading in this product is set in `--font-display` — the corpus page
   title, a card title, a document title, a peek's title, a comment head. Not
   one heading in settings was: they inherited `--font-sans` because nothing
   here ever named a family, so the one surface with the most headings on it
   was the only surface setting them in the body face.

   Nothing else about them changes. The pairing is the design system's, this
   file simply was not using it. */
.set2-title {
  font-family: var(--font-display);
  font-size: var(--ty-display);
  font-weight: var(--fw-extrabold);
  letter-spacing: var(--ls-tight);
  color: var(--ink-primary);
  line-height: var(--lh-tight);
}

/* Scope reads, the lens writes. One editable control in the chrome that
   re-scopes the page under it is the shape behind every wrong-context incident
   worth reading about, and the remedy in each of those write-ups was
   visibility of the active context rather than one more gate. So the scope is
   text, and only the lens is a control. */
.set2-bar {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  margin-top: 0.875rem;
}
.set2-scope { display: flex; align-items: center; gap: 0.375rem; flex-wrap: wrap; min-width: 0; }
.set2-scope-i { font-size: var(--ty-micro); color: var(--ink-quiet); white-space: nowrap; }
.set2-scope-i b { color: var(--ink-primary); font-weight: var(--fw-semibold); }
/* --ink-rule says "NON-TEXT. Hairlines and dots. Never type." in aimy-ds.css,
   and this is a glyph: the › between the two halves of the scope line. It
   measured 4.02:1 in dark on --d600.

   --d500 was the first answer, on knowledge.css's note that it is "the step
   this ramp uses for punctuation" — and css-audit.js rejected it: this
   product's ink floor for anything that is TEXT is the --ink-* set, and
   --d500 and below are non-text only. The note it cites is about a ::before
   marker, which the audit does not scan, so the two never met before now.

   --ink-faint is the quietest token the floor allows, and it measures 6.72.
   The › is still the least of the three things on the line. */
.set2-scope-s { color: var(--ink-faint); font-size: var(--ty-micro); }
.set2-bar-end { margin-left: auto; display: flex; align-items: center; gap: 0.5rem; }

/* The library's `.seg-btn.active` pairs `--accent` as TEXT with `--accent-dim`
   as fill. Measured on this page: 2.86:1 dark, 3.77:1 light -- both under the
   4.5 floor, on what is the only writable control in the chrome. Swapping in
   `--ink-action` (the library's own "accent AS TEXT" token) lifts it to 4.18
   and 4.90, so light passes and dark still does not.

   So the lens drops hue altogether. Its selected state is already carried by
   a surface step, and a control that needs colour ON TOP of a fill to say
   which segment is chosen was saying it twice. This also returns the whole
   accent budget to the one primary action per view, which is where it was
   supposed to be.

   Raised as a library gap regardless: every other consumer of `.seg` is still
   failing quietly. */
.set2-bar .seg { background: var(--card-bg); }
.set2-bar .seg-btn.active { background: var(--card-bg-raised); color: var(--ink-primary); }

/* ══ SECTION ═════════════════════════════════════════════════════════════ */

/* ── WHY SECTIONS GOT FURTHER APART AND QUIETER ──
   Once the rows lost their boxes, the only thing separating one group of
   settings from the next is space — so the space has to be big enough to read
   as a break rather than as a wide gap between two rows. 3.5rem is the
   distance at which the eye stops looking for a relationship.

   The header itself is the reverse move: uppercase micro rather than body
   semibold. A section head is a SIGNPOST, not a heading — it is read once, on
   the way past, and every surveyed product sets theirs quieter than the
   content beneath. Making it smaller and letting tracking do the work leaves
   the row names as the largest thing in the column, which is correct: the
   names are what you came to find. */
/* ── THE HOLE IN THE SCALE, AND WHY IT CLOSED ──
   These were micro uppercase at `--ink-faint`, on the reasoning that a section
   head is a signpost read once on the way past. That was overturned once,
   correctly, for the page as it then stood: the title was 28px and the next
   thing down was 12px, so nothing carried the middle and every section floated
   unanchored above its own content. `--ty-title` restored the step.

   THE PAGE UNDER IT HAS CHANGED. That hole existed because a module was five
   or six sections long and the title named the whole module — so the section
   head was the only thing standing between a 28px page name and a 13px row,
   and it had to carry the middle alone. A module is a group of PAGES now. The
   title names the page, there is one section on most of them and two at most,
   and the rail carries the map. There is no middle left to hold.

   So the signpost reasoning applies again, and it is what was asked for: the
   head goes quiet and the content gets the contrast. What is left is three
   levels with the loud one in the middle, which is right — the row names are
   what somebody came to read:

     section head   micro uppercase, --ink-faint   read once
     row name       13px, --ink-primary            what you came for
     row detail     meta, --ink-quiet              why it matters

   The rule under it does the separating the size used to. */
.set2-sec { margin-top: 2.75rem; }
.set2-sec:first-child { margin-top: 1.75rem; }
.set2-sec-h {
  display: flex; align-items: baseline; gap: 0.75rem;
  margin-bottom: 0.75rem; padding-bottom: 0.5rem;
  border-bottom: 0.0625rem solid var(--hairline);
}
/* ── NO RULE UNDER A HEAD THAT IS A TOOLBAR ──
   The border exists to separate a section's TITLE from its content. On a page
   whose single section is titled by the page, the head carries no title — it
   is a row of controls — and the rule under it became the first of three
   stacked hairlines before any data: this, the column head's, then the first
   row's. Same call as `.set2-tabs.is-bare`, same modifier name: a toolbar is
   not a heading and does not get a heading's rule.

   The head aligns on `baseline` for a title beside its counts; a row of boxed
   controls has no shared baseline to sit on, so it centres instead. */
/* A toolbar's controls do not shrink, so on a phone this row wanted 136px more
   than the column had — measured on Dynamic fields at 390 — and took the page
   sideways. It wraps; the rule it does not draw means a second line costs
   nothing but its own height. */
.set2-sec-h.is-bare {
  flex-wrap: wrap;
  align-items: center;
  padding-bottom: 0; border-bottom: 0;
  /* The rule was doing two jobs and only one of them was decoration: it also
     held the toolbar off the column head beneath it. Taking it away left four
     bands — page title, scope, toolbar, table — stacked at 14, 20, 12 and 0
     pixels, which reads as one dense block rather than as four things. The
     space it was providing has to be declared now that the line is not.
     A margin here COLLAPSES: the head is the section's first child and the
     section has no top padding, so `margin-top` escapes the box and merges
     with the space already above it, changing nothing. Padding does not
     collapse, which is why the space above is padding and the space below is
     margin. */
  padding-top: 0.75rem; margin-bottom: 1.5rem;
}
/* ── QUIET, NOT SHOUTED ──
   The first pass at "muted" reached for micro uppercase with wide tracking,
   which is the treatment this file uses for a LABEL — the rail's group names,
   a table's column heads. A section head is a heading, and setting it in the
   label's clothes made it read as a column head over the whole section: all
   caps, letter-spaced, and still the first thing your eye hit because caps
   carry further than the size suggests.

   Muted is a job for colour and weight. So the head keeps normal sentence
   case and normal tracking like every other piece of running type on the
   surface, and recedes by being quieter and smaller than the rows beneath it.
   The rule under it does the separating.

     section head   15px semibold, --ink-quiet    read once
     row name       15px,          --ink-primary  what you came for */
.set2-sec-t {
  font-family: var(--font-display);
  font-size: var(--ty-meta); font-weight: var(--fw-bold);
  letter-spacing: normal; text-transform: none;
  /* `--ink-quiet` is the ATTRIBUTION step — who, when, where. It was borrowed
     here to make the head recede and made it read as washed out instead:
     a heading in the display face at that value looks like text that failed to
     load rather than text set quietly on purpose. `--ink-secondary` is the
     substance step, one below identity, which is what a section head is. It
     still sits under the row names at `--ink-primary`; the recession comes
     from the size and the rule, which is where it belongs. */
  color: var(--ink-secondary);
  line-height: var(--lh-snug);
}
.set2-sec-end {
  font-size: var(--ty-micro);
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* ══ ROW ═════════════════════════════════════════════════════════════════
   The atom that repeats everywhere: a skill, a connector, a person, a policy.

   ── WHY THIS IS NOT A CARD ────────────────────────────────────────────────
   It used to be: a filled surface with a radius, stacked with a 2px gap so the
   page ground showed between them. That is the right treatment for a list of
   OBJECTS you might pick one of. A settings row is not an object — it is a
   STATEMENT WITH A CONTROL ATTACHED, and boxing it makes twelve statements
   read as twelve things to choose between.

   Surveyed the pattern across Arcade, Obvious, Plain, Grain, Sketch and
   Langdock: not one of them boxes a settings row. All six use the same shape —
   name and description hard left, control hard right, a hairline between. The
   line does the separating and costs one pixel instead of a whole surface.

   The gain is not decorative. Removing the fill removes a rounded rectangle
   per row from the page, which is what lets the vertical rhythm open up: rows
   are TALLER now and the page still reads shorter, because the eye follows one
   column of names instead of scanning a stack of tiles. */
.set2-rows { display: flex; flex-direction: column; }
.set2-row {
  display: flex; align-items: center; gap: 0.875rem;
  padding: 0.875rem 0.25rem;
  border-bottom: 0.0625rem solid var(--hairline);
  text-align: left; width: 100%;
  appearance: none; border-left: 0; border-right: 0; border-top: 0;
  background: none; font: inherit;
  transition: background var(--t-fast) var(--ease-out);
}
/* The last row's line would be a lid on a section that has already ended. */
.set2-row:last-child { border-bottom: 0; }
.set2-row[data-go] { cursor: pointer; }
/* A hover tint that bleeds past the text is the only thing left standing in
   for the old surface, so it takes the padding back as a negative inset. */
.set2-row[data-go]:hover { background: var(--card-bg); box-shadow: -0.5rem 0 0 var(--card-bg), 0.5rem 0 0 var(--card-bg); }
.set2-row:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; border-radius: var(--r-xs); }
/* The GLYPH dims on an off row, the NAME does not. Dimming the name made a
   switched-off agent's own label the hardest text in the list to read, and the
   toggle beside it already says off. */
.set2-row.is-off .set2-row-ico { opacity: 0.5; }

/* ── The settings row proper ──
   Label and help text on the left, control on the right. This is the single
   most repeated shape in the surveyed set and it did not exist here: values
   like the relevance window were written as inline sentences, which reads
   well once and cannot be scanned at all when there are six of them.

   The left column is FIXED, not fluid. Ragged control edges are what make a
   settings page feel assembled rather than designed, and a fixed label column
   is the cheapest way to guarantee one vertical line for every control. */
.set2-set { display: flex; flex-direction: column; }
.set2-set-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 20rem);
  align-items: start; gap: 1.5rem;
  padding: 1.125rem 0.25rem;
  border-bottom: 0.0625rem solid var(--hairline);
}
.set2-set-row:last-child { border-bottom: 0; }
.set2-set-l { min-width: 0; }
.set2-set-n {
  font-size: var(--ty-meta); font-weight: var(--fw-semibold); color: var(--ink-primary);
}
.set2-set-d {
  font-size: var(--ty-micro); color: var(--ink-quiet);
  line-height: var(--lh-snug); margin-top: 0.1875rem; max-width: 52ch;
}
.set2-set-c { display: flex; align-items: center; gap: 0.5rem; justify-content: flex-end; }
.set2-set-c .set2-fld { min-width: 0; }

/* One column below the fold, because a 20rem control beside a 12rem label is
   two columns of nothing on a phone. */
@media (max-width: 52rem) {
  .set2-set-row { grid-template-columns: 1fr; gap: 0.625rem; }
  .set2-set-c { justify-content: flex-start; }
}

.set2-row-ico { width: 1rem; height: 1rem; flex-shrink: 0; color: var(--ink-quiet); }
.set2-row-main { flex: 1 1 0; min-width: 0; }
.set2-row-n {
  font-size: var(--ty-meta); font-weight: var(--fw-semibold); color: var(--ink-primary);
  display: flex; align-items: center; gap: 0.5rem;
}
/* -- AN INLINE BOX CANNOT CLIP --
   This is a `<span>`, so it computed to `display: inline` -- and on an inline
   box `overflow`, `text-overflow` and `width` do nothing at all. All three
   rules below were inert, so the sentence ignored its 494px column and ran
   56px past it, straight through "nothing read" on the right.

   Blocked, it obeys the column. And it WRAPS rather than truncating: these
   sentences say what a setting does and where it stops, which is not something
   to cut mid-clause at an arbitrary character. */
.set2-row-d {
  display: block;
  font-size: var(--ty-micro); color: var(--ink-quiet); margin-top: 0.0625rem;
  white-space: normal; line-height: var(--lh-snug);
}
/* The name is a span too and carries a pill after it, so it wraps inside its
   own column rather than over the control at the end of the row. */
.set2-row-n { flex-wrap: wrap; }
.set2-row-end { display: flex; align-items: center; gap: 0.75rem; flex-shrink: 0; }

/* Where a row's value came from. A word, not a badge -- five coloured chips in
   a column is a bar chart nobody asked for. */
.set2-from { font-size: var(--ty-micro); color: var(--ink-quiet); white-space: nowrap; }
.set2-from.is-org { color: var(--ink-quiet); font-weight: var(--fw-semibold); }

.set2-chev { width: 0.875rem; height: 0.875rem; color: var(--ink-rule); flex-shrink: 0; }

/* ══ PILL / TOKEN ════════════════════════════════════════════════════════ */

.set2-pill {
  display: inline-flex; align-items: center; gap: 0.375rem;
  padding: 0.125rem 0.5rem; border-radius: var(--r-pill);
  font-size: var(--ty-micro); font-weight: var(--fw-semibold); white-space: nowrap;
}
.set2-pill i { width: 0.3125rem; height: 0.3125rem; border-radius: var(--r-pill); background: currentColor; flex-shrink: 0; }
.set2-pill.is-ok   { background: var(--ok-bg);   color: var(--ink-ok); }
.set2-pill.is-warn { background: var(--warn-bg); color: var(--ink-warn); }
.set2-pill.is-err  { background: var(--err-bg);  color: var(--ink-err); }
.set2-pill.is-info { background: var(--info-bg); color: var(--ink-info); }
.set2-pill.is-mute { background: var(--card-bg-raised); color: var(--ink-quiet); }

.set2-num { font-variant-numeric: tabular-nums; }
.w-lock { width: 0.75rem; height: 0.75rem; flex-shrink: 0; color: var(--ink-quiet); }

/* The chevron OUTLIVED the ladder. `I.left` and `I.down` carry this class,
   and the scope picker and the popover back button draw both — so the rule
   stays while the six-stop chain it was written for does not. */
.set2-lad-ch {
  width: 0.75rem; height: 0.75rem; color: var(--ink-quiet);
  transition: transform var(--t-base) var(--ease-out);
}

/* ══ TARGETING PICKER ════════════════════════════════════════════════════ */

.set2-pick { background: var(--card-bg); border-radius: var(--r-lg); overflow: hidden; }
.set2-pick-hd { padding: 0.625rem 0.625rem 0.5rem; }
.set2-pick-bd { max-height: 15rem; overflow-y: auto; padding: 0 0.375rem 0.375rem; scrollbar-width: thin; }
.set2-pick-ft {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.5rem 0.875rem; background: var(--card-bg-raised);
  font-size: var(--ty-micro); color: var(--ink-quiet);
}
.set2-pick-ft b { color: var(--ink-primary); font-weight: var(--fw-semibold); }
.set2-pick-ft.is-zero { color: var(--ink-err); }
.set2-pick-ft.is-zero b { color: var(--ink-err); }

.set2-node {
  display: flex; align-items: center; gap: 0.5rem;
  min-height: 1.875rem; padding: 0.1875rem 0.375rem;
  border-radius: var(--r-sm); font-size: var(--ty-micro);
  color: var(--ink-secondary); cursor: pointer;
}
.set2-node:hover { background: var(--card-bg-raised); }
.set2-node:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; }
.set2-node.is-grp > .set2-node-n { color: var(--ink-primary); font-weight: var(--fw-semibold); }
.set2-node.is-l1 { padding-left: 0.375rem; }
.set2-node.is-l2 { padding-left: 1.375rem; }
.set2-node.is-l3 { padding-left: 2.375rem; }
.set2-node.is-l4 { padding-left: 3.375rem; }
.set2-node.is-l5 { padding-left: 4.375rem; }
.set2-node.is-l6 { padding-left: 5.375rem; }
.set2-node-n { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.set2-node-ct { font-size: var(--ty-micro); color: var(--ink-quiet); font-variant-numeric: tabular-nums; flex-shrink: 0; }
.set2-exp {
  width: 0.875rem; height: 0.875rem; flex-shrink: 0; color: var(--ink-quiet);
  transition: transform var(--t-fast) var(--ease-out);
}
.set2-node.is-open > .set2-exp { transform: rotate(90deg); }
.set2-exp.is-none { visibility: hidden; }
.set2-ck svg { width: 0.625rem; height: 0.625rem; color: #fff; opacity: 0; }
.set2-ck[aria-checked="true"]  { background: var(--brand); border-color: var(--brand); }
.set2-ck[aria-checked="true"] svg  { opacity: 1; }
.set2-ck[aria-checked="mixed"] { background: var(--brand-dim); border-color: var(--brand); }
.set2-ck[aria-checked="mixed"] svg { opacity: 1; color: var(--brand); }

/* ══ FIELDS ══════════════════════════════════════════════════════════════ */

.set2-fld {
  appearance: none; width: 100%;
  background: var(--card-bg); border: 0.0625rem solid var(--card-border);
  padding: 0.4375rem 0.6875rem; border-radius: var(--r-md);
  font: inherit; font-size: var(--ty-meta); color: var(--ink-primary);
}
.set2-fld::placeholder { color: var(--ink-quiet); }
.set2-fld:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.0625rem; border-color: transparent; }
/* An invalid field marks itself, so a sheet with one bad address does not
   rely on the reader pairing a message at the bottom with a box at the top.

   TWO TOKENS, ON PURPOSE. The border takes `--err`, which is what the design
   system's `.input.error` uses. The MESSAGE does not: `--err` is #f04438 on
   dark and #d92d20 on light, and knowledge.css records both as failing when
   they were used as text — 4.08:1 and 3.73:1 — which is why `--ink-err`
   exists and what it was corrected to. A border is a UI component at a 3:1
   floor and passes; a sentence is text at 4.5 and does not. */
.set2-fld.is-bad { border-color: var(--err); }
.set2-hint.is-err { color: var(--ink-err); }
textarea.set2-fld { resize: vertical; min-height: 6rem; line-height: var(--lh-base); }
.set2-field + .set2-field { margin-top: 1rem; }
.set2-hint { font-size: var(--ty-micro); color: var(--ink-quiet); margin-top: 0.375rem; }
.set2-modal .set2-fld { background: var(--card-bg-raised); }

/* ── FIELD HELP, ON DEMAND ──
   The design system's `.tooltip-wrap` / `.tooltip`, with two changes it needs
   to carry a sentence rather than a word: it wraps, and it sits to the RIGHT
   rather than above. Above would be clipped — `.set2-modal-bd` scrolls, so it
   establishes a clipping context whether or not it is currently overflowing,
   and the first field's tooltip would be cut off by the top edge every time.
   To the side it clears both edges at any field. */
.set2-lbl-row { display: flex; align-items: center; gap: 0.25rem; margin-bottom: 0.375rem; }
.set2-lbl-row .set2-lbl { margin-bottom: 0; }
.set2-tip-wrap { position: relative; display: inline-flex; }
.set2-tip-b {
  appearance: none; border: 0; background: none; cursor: help; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.125rem; height: 1.125rem; border-radius: var(--r-pill);
  color: var(--ink-rule);
}
.set2-tip-b svg { width: 0.875rem; height: 0.875rem; display: block; }
.set2-tip-b:hover { color: var(--ink-quiet); }
.set2-tip-b:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }
.set2-tip {
  /* Top-aligned to the icon and growing DOWN, not centred on it. Centred, a
     two-line tooltip on the first field reached above `.set2-modal-bd`, which
     scrolls and therefore clips — measured outside its top edge. There is
     always room below a label and never any above the first one, whose row
     sits flush against the top of that box. Flush is why this is `0` and not
     a small negative: four pixels of overhang is still four pixels cut off. */
  position: absolute; left: calc(100% + 0.5rem); top: 0;
  width: max-content; max-width: 15rem; white-space: normal;
  background: var(--card-bg-raised); border: 0.0625rem solid var(--card-border);
  border-radius: var(--r-md); box-shadow: var(--shadow-md);
  padding: 0.375rem 0.5rem;
  font-size: var(--ty-micro); line-height: var(--lh-snug); color: var(--ink-quiet);
  opacity: 0; pointer-events: none; z-index: 4;
  transition: opacity var(--t-fast) var(--ease-out);
}
/* Same intent gate as .tooltip in the library, and split from the focus rule
   for the reason the library's is: hovering is something a cursor does on its
   way somewhere, so it waits; focusing is something a person did, so it does
   not. Written as one selector list both would wait, and a keyboard user would
   pay --t-intent for a tooltip they asked for by name. */
.set2-tip-wrap:hover .set2-tip { opacity: 1; transition-delay: var(--t-intent); }
.set2-tip-b:focus-visible + .set2-tip { opacity: 1; }
/* Below rather than beside, for a glyph on a HEADING. The sideways placement
   above exists because `.set2-modal-bd` scrolls and clips its top edge; a
   heading in the page body has nothing above it to clear and plenty of room
   under it, and going sideways from a tab would put the panel over the next
   tab. Wider too: these carry two sentences, not a phrase. */
/* `max-width` is 21rem because these carry two sentences. On a phone 21rem is
   wider than the column: the panel is `position: absolute` and `opacity: 0`,
   so nothing is SEEN overflowing — but an absolutely positioned box still
   counts toward the scrollable width of `#pageScroll`, and measured at 360 it
   reached 245px past the column and took the page sideways with it. Capping
   against the viewport keeps the two sentences wherever there is room for them
   and stops the invisible panel dragging a scrollbar into being. */
.set2-tip.is-below {
  left: 0; top: calc(100% + 0.4375rem);
  max-width: min(21rem, calc(100vw - 3rem));
}
/* The cap above is the viewport; below 46rem the panel also has to clear
   wherever its ICON sits, and on Skills the second tab's tip started 120px in
   and still ran 293px past the column. Two sentences do not fit beside a tab
   on a phone, so under this width the panel stops trying to sit under its own
   icon and spans the column instead: pinned to the page's left gutter with
   `position: fixed`, which also takes it out of `#pageScroll`'s scrollable
   width — an `opacity: 0` panel was dragging a horizontal scrollbar into
   being on two pages that had nothing else wrong with them. */
@media (max-width: 735.98px) {
  .set2-tip.is-below {
    position: fixed; left: 1rem; right: 1rem; top: auto;
    width: auto; max-width: none;
    margin-top: 0.4375rem;
  }
}
@media (prefers-reduced-motion: reduce) { .set2-tip { transition: none; } }
/* Settings is set in Urbanist throughout — the URLs and tokens included. The
   class stays as the hook for what a monospace face was doing here, because
   one thing it was doing still has to be done: see the masked-token rule at
   the foot of this file. */
.set2-mono { font-family: var(--font-sans); }

/* ══ EMPTY / NOTE ════════════════════════════════════════════════════════ */

/* An empty state is a heading, a sentence and, when there is one, the single
   thing worth doing. It was a text block with a button dropped inline, so the
   action ran on from the sentence as though it were part of it. */
.set2-empty {
  display: flex; flex-direction: column; align-items: center; gap: 0.375rem;
  padding: 3rem 1.5rem; text-align: center;
  background: var(--card-bg); border-radius: var(--r-lg);
  font-size: var(--ty-meta); color: var(--ink-quiet);
}
.set2-empty b {
  font-family: var(--font-display);
  color: var(--ink-primary); font-weight: var(--fw-bold);
  font-size: var(--ty-body); margin-bottom: 0.125rem;
}
.set2-empty .btn { align-self: center; margin-top: 0.875rem; }

/* One line, one accent edge, no icon-and-paragraph. Used where a fact about
   the whole section cannot be carried by a row. */
/* ── WHY THE NEUTRAL NOTE LOST ITS BOX ──
   Once the rows stopped being surfaces, a filled note became the loudest thing
   on the page — a paragraph of context outranking every control on screen.
   A note that merely EXPLAINS is prose and gets set as prose. It keeps a rule
   on its left so it still reads as an aside rather than as body copy.

   The two that WARN keep their fill, and that is the point of the split:
   colour now means consequence and nothing else. When every note was a box,
   the irreversible one looked like the informational one wearing red. */
.set2-note {
  display: flex; gap: 0.625rem; align-items: baseline;
  padding: 0 0 0 0.75rem;
  border-left: 0.125rem solid var(--hairline);
  font-size: var(--ty-micro); color: var(--ink-quiet);
  line-height: var(--lh-snug);
}
.set2-note.is-warn,
.set2-note.is-err {
  padding: 0.625rem 0.875rem; border-left: 0; border-radius: var(--r-md);
}
.set2-note.is-warn { color: var(--ink-warn); background: var(--warn-bg); }
.set2-note.is-err  { color: var(--ink-err);  background: var(--err-bg); }

/* ══ ENTITLEMENT ═════════════════════════════════════════════════════════
   The module stays at FULL contrast and only the controls die. Dimming the
   whole thing hides what you would be buying, which is the one thing an upsell
   surface has to show. */
.set2-tier {
  display: flex; align-items: center; gap: 0.625rem;
  margin-top: 0.125rem;
  padding: 0.625rem 0.875rem;
  border-radius: var(--r-md) var(--r-md) var(--r-lg) var(--r-lg);
  background: var(--card-bg-raised);
  font-size: var(--ty-micro); color: var(--ink-quiet);
}
.set2-tier-end { margin-left: auto; }
.set2-row.is-locked { pointer-events: none; }
.set2-row.is-locked .set2-row-end > *:not(.w-lock) { opacity: 0.4; }

/* ══ SAVE BOUNDARY ═══════════════════════════════════════════════════════
   Each module owns its own. It rises from the bottom of the scroll port with
   the count of what is unsaved, and it belongs to ONE module -- a global save
   bar is how an edit in one place commits an edit in another. */
/* The floating bar is gone. What replaced it is below, under THE UNSAVED
   BLOCK -- it sits in the layout instead of over it. `.set2-save-n` survives
   because the preview modal's footer still uses it for a count line. */
.set2-save-n { font-size: var(--ty-micro); color: var(--ink-quiet); }
/* ══════════════════════════════════════════════════════════════════════════
   SAYING NO, AND SAYING WHERE

   The one moment in this console that had no motion at all and most needed it.
   A modal repainted in place does not re-enter - see `is-seated` in paintModal
   - so what the reader sees is the dialog they are already looking at refusing
   the thing they just asked for.

   Restrained on purpose, and decaying. This is a refusal, not a tantrum: 6px
   at the widest, which is `--distance-small` in the shared scale, and each
   swing smaller than the last so it reads as settling rather than vibrating.
   Transform only, so a dialog with a scrolling body and a backdrop filter
   behind it repaints on none of it.
   ══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: no-preference) {
  /* A dialog that was already open stays where it is. */
  .set2-scrim.is-seated,
  .set2-scrim.is-seated .set2-modal { animation: none; }

  .set2-scrim .set2-modal.is-shaking {
    animation: set2-shake var(--t-slow) var(--ease-out) both;
  }
  @keyframes set2-shake {
    0%, 100% { transform: translateX(0); }
    12%      { transform: translateX(-0.375rem); }
    28%      { transform: translateX(0.375rem); }
    46%      { transform: translateX(-0.25rem); }
    64%      { transform: translateX(0.25rem); }
    82%      { transform: translateX(-0.0625rem); }
  }

  /* ════════════════════════════════════════════════════════════
     THE SAVE BOUNDARY ARRIVES

     It was an attribute flip: the bar that tells you there is something
     uncommitted appeared and disappeared between frames, which on a page you
     are typing into reads as the layout glitching rather than as a state
     changing. Coming out of `display: none` restarts a CSS animation, so the
     rise fires every time it returns and needs no JavaScript to arm it. */
  [data-save]:not([hidden]) {
    animation: set2-save-in var(--t-base) var(--ease-out) backwards;
  }
  @keyframes set2-save-in {
    from { opacity: 0; transform: translateY(0.5rem); }
  }

  /* And the figure itself, when it is a different figure. One short run of
     digits, so the blur is affordable here on the same terms as the thinking
     label: never a list, never more than one at a time. */
  .set2-num.is-bumped {
    animation: set2-num-in var(--t-base) var(--ease-out);
  }
  @keyframes set2-num-in {
    from { opacity: 0; transform: translateY(-0.375rem); filter: blur(var(--blur-small)); }
  }
}


/* ══ DETAIL ══════════════════════════════════════════════════════════════ */

/* THE WAY BACK MOVED INTO THE HEADER.
   It was a labelled link on its own line above the title: `< Skills`, printed
   directly under an <h1> reading Skills and a scope line reading Org
   FlairsTech. Three stacked lines of chrome before the thing you opened, one
   of them the same word twice.

   An icon against the skill's name instead. The label goes to `aria-label` and
   `title`, so it is still named for anyone who needs it named, and the target
   grows rather than shrinks: a 28px square beats a 16px-tall text link. */
.set2-back {
  appearance: none; padding: 0; cursor: pointer; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.75rem; height: 1.75rem;
  border: 0.0625rem solid var(--card-border); border-radius: var(--r-sm);
  background: var(--card-bg); color: var(--ink-quiet);
}
.set2-back svg { width: 0.875rem; height: 0.875rem; }
.set2-back:hover { border-color: var(--ink-rule); color: var(--ink-primary); }
.set2-back:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }

/* The skill body, shown as prose or as the file it actually is. A skill that
   can be uploaded as a file has to be readable as one, or the download and the
   display are two different objects. */
.set2-view { display: inline-flex; gap: 0.125rem; padding: 0.1875rem; border-radius: var(--r-md); background: var(--card-bg-raised); }
.set2-view-b {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  padding: 0.25rem 0.5rem; border-radius: var(--r-sm);
  color: var(--ink-quiet); display: grid; place-items: center;
  min-width: 1.75rem; min-height: 1.75rem;
}
.set2-view-b svg { width: 0.875rem; height: 0.875rem; }
.set2-view-b.is-on { background: var(--card-bg); color: var(--ink-primary); }

.set2-body {
  padding: 0.875rem 1rem; border-radius: var(--r-lg);
  background: var(--card-bg);
  font-size: var(--ty-meta); color: var(--ink-secondary); line-height: var(--lh-base);
}
.set2-body.is-raw {
  font-family: var(--font-sans); font-size: var(--ty-micro);
  color: var(--ink-quiet); white-space: pre-wrap; line-height: var(--lh-snug);
}

/* ══ FIELD MAPPING ═══════════════════════════════════════════════════════
   A table, and the only place in this file where alignment rather than a
   surface step carries the structure. That is the exception T2 allows: in a
   table the columns ARE the separator, so a rule between rows would be a
   second one. There is still not a single border-bottom below.

   The console renders this as a bordered grid with a hairline under every row
   plus alternating tinted fills, which is three separating devices doing one
   job. */
.set2-map { display: flex; flex-direction: column; }
.set2-map-hd {
  display: grid; grid-template-columns: 12rem minmax(0, 1fr) auto;
  gap: 0.75rem; padding: 0 0.25rem 0.5rem;
  font-size: var(--ty-micro); letter-spacing: var(--ls-wide);
  text-transform: uppercase; color: var(--ink-quiet);
}
/* ── A ROW IS A FIELD AND EVERYTHING UNDER IT ──
   The separator was on every `.set2-map-row`, so it fell between a parent and
   its own subfields as readily as between two unrelated fields — six identical
   lines saying nothing about which side of them belonged together, and the
   subfield indent arguing with a rule that cut straight across it.

   The unit is the FIELD plus its children. So the line moves to the top of
   every top-level row, which puts exactly one between each group and the next
   and none inside one. Drawn on the row that starts a group rather than under
   the thing that ends it, because what ends a group varies — the last
   subfield, or the "Add subfield" under it — and what starts one never does. */
.set2-map-row { border-bottom: 0; }
.set2-map-row:not(.is-sub) { border-top: 0.0625rem solid var(--hairline); }
/* The column head already rules off the first group. */
.set2-map-hd + .set2-map-row { border-top: 0; }
.set2-map-ctx {
  font-size: var(--ty-meta); font-weight: var(--fw-semibold); color: var(--ink-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.set2-map-row.is-broken .set2-map-ctx { color: var(--ink-err); }
.set2-x {
  appearance: none; border: 0; background: transparent; padding: 0.1875rem;
  cursor: pointer; color: var(--ink-quiet); display: grid; place-items: center; border-radius: var(--r-sm);
}
.set2-x:hover { color: var(--ink-err); background: var(--err-bg); }
.set2-x svg { width: 0.75rem; height: 0.75rem; }

.set2-add {
  appearance: none; border: 0; background: transparent;
  padding: 0.5rem 0.875rem; margin-top: 0.125rem;
  border-radius: var(--r-md);
  font: inherit; font-size: var(--ty-micro); font-weight: var(--fw-semibold);
  color: var(--ink-action); cursor: pointer; text-align: left;
}
.set2-add:hover { background: var(--card-bg); }
.set2-add.is-inline { padding: 0.1875rem 0.4375rem; margin: 0; }
.set2-sample i { font-style: normal; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.set2-sample i + i::before { content: "·"; margin-right: 0.5rem; color: var(--ink-rule); }
.set2-sample.is-none { color: var(--ink-warn); font-style: italic; }

/* A suggestion is not an error and not a fact. It sits between them, and the
   row says so without dimming: everything stays fully readable, because a
   surface that hides its own guesses cannot be checked. */
.set2-map-row.is-unmapped .set2-map-ctx { color: var(--ink-quiet); font-weight: var(--fw-medium); }

/* The counts and what they are counted FROM, at the left edge where the
   section's own title would be if the page were not already carrying it. */
.set2-sec-lead { display: flex; align-items: center; gap: 0.75rem; min-width: 0; }
.set2-tally { display: flex; align-items: center; gap: 0.75rem; font-size: var(--ty-micro); color: var(--ink-quiet); }
.set2-tally b { color: var(--ink-primary); font-weight: var(--fw-semibold); }
.set2-tally .is-info b { color: var(--ink-info); }
.set2-tally .is-err b  { color: var(--ink-err); }
.set2-tally .is-mute b { color: var(--ink-quiet); }
/* ══ ROLES ═══════════════════════════════════════════════════════════════
   Words, not ticks — that part of the matrix was right and it survives. A
   checkbox encodes a binary and permissions here are ternary: "can read it but
   not change it" is the most common answer and a tick has nowhere to put it.

   What changed is that the words are for ONE role at a time, so they can be
   set as a document instead of packed into cells. No card behind the rows, no
   fill, no radius: hairlines and space carry the structure, because this is a
   page you read rather than a surface you operate. That is also what keeps it
   visually apart from the skill library, which uses the same two columns for
   the opposite job.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-rl {
  display: grid;
  grid-template-columns: 13.5rem minmax(0, 1fr);
  gap: 2rem;
  margin-top: 1.5rem;
  align-items: start;
}

/* ── The list ──
   Six rows and a settled number. It gets no scroll port and no filter of its
   own, because neither would ever do anything. */
.set2-rl-nav { position: sticky; top: 1rem; }
.set2-rl-hd { margin-bottom: 0.75rem; }
.set2-rl-bd { display: flex; flex-direction: column; }
/* ── NO BACKGROUND TRANSITION ON A SELECTED ROW ──
   These four rows take their RESTING background from a theme token, and the
   theme toggle swaps the token on `<html>` without re-rendering anything. A
   property that is mid-transition — or merely declared transitionable — does
   not re-resolve its `var()` when the variable changes underneath it, so the
   selected row kept the previous theme's fill: white-on-white going one way,
   near-black on a light page coming back. Reproduced both directions on the
   live toggle; correct on a fresh load, which is why it hides.

   The 150ms hover fade is not worth a row that renders the wrong theme, so it
   goes. Rows whose resting background is `none` keep theirs — a hover state
   re-resolves when you hover it. */
.set2-rl-r {
  display: flex; align-items: baseline; gap: 0.5rem; width: 100%;
  appearance: none; border: 0; background: none; cursor: pointer;
  text-align: left; font: inherit;
  min-height: 2rem; padding: 0.3125rem 0.5rem;
  border-radius: var(--r-sm);
  color: var(--ink-secondary);
}
.set2-rl-r:hover { background: var(--card-bg); }
.set2-rl-r:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; }
.set2-rl-r.is-on { background: var(--card-bg); }
.set2-rl-r.is-on .set2-rl-n { color: var(--ink-primary); font-weight: var(--fw-semibold); }
.set2-rl-n {
  flex: 1 1 auto; min-width: 0; font-size: var(--ty-meta);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The holder count is the fact that turns a reference into something you can
   act on, so it sits on every row and not only on the open one. Zero says so
   in a word: a role nobody holds is worth noticing, and "0" reads as a
   measurement rather than as a finding. */
.set2-rl-held {
  flex-shrink: 0; font-size: var(--ty-micro); color: var(--ink-quiet);
  font-variant-numeric: tabular-nums;
}
.set2-rl-held.is-zero { color: var(--ink-warn); }
.set2-rl-ft {
  margin-top: 0.5rem; padding: 0 0.5rem;
  font-size: var(--ty-micro); color: var(--ink-rule);
}

/* ── The document ── */
.set2-rl-doc { min-width: 0; }
.set2-rl-t {
  font-family: var(--font-display);
  font-size: var(--ty-title); font-weight: var(--fw-bold);
  color: var(--ink-primary); margin: 0;
}
.set2-rl-d {
  margin: 0.375rem 0 0; max-width: 40rem;
  font-size: var(--ty-meta); color: var(--ink-secondary); line-height: var(--lh-base);
}
/* The segmented control sits in the column the label used to, so the rows
   still line up whether a capability is settable or locked. */
.set2-rl-seg { justify-self: start; }
.set2-rl-seg .seg-btn { font-size: var(--ty-micro); padding: 0.1875rem 0.5rem; }

.set2-rl-bar {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  margin-top: 1.5rem; padding-bottom: 0.625rem;
  border-bottom: 0.0625rem solid var(--hairline);
}
.set2-rl-bar-l { font-size: var(--ty-meta); color: var(--ink-secondary); }
.set2-rl-bar-l b { color: var(--ink-primary); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.set2-rl-cmp { margin-left: auto; display: flex; align-items: center; gap: 0.5rem; }
.set2-rl-cmp-k {
  font-size: var(--ty-micro); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--ink-quiet);
}

/* ── The two columns of a diff ──
   One grid for the heads and every row under them, so the two states line up
   down the page. This is the ONE place in People-and-Roles where alignment
   carries meaning rather than decoration: a comparison whose columns do not
   line up is not a comparison. */
/* The first state column holds a three-button segmented control now, not a
   word, so it is wider than the second — which still holds the compared
   role's LABEL. Sized to the control rather than the other way round: a seg
   squeezed into a label's column is what put "None" on top of "Always". */
.set2-rl-heads,
.set2-rl-cap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 11.5rem 8.5rem;
  align-items: center;
  gap: 0.75rem;
}
.set2-rl-heads {
  padding: 0.625rem 0 0;
  font-size: var(--ty-micro); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--ink-quiet);
}
.set2-rl-h1 { grid-column: 2; }
.set2-rl-h2 { grid-column: 3; }
/* Without a comparison there is one state column and the capability takes the
   rest, so the name is not pinned to a track sized for a column that is not
   being drawn. */
.set2-rl-cap:not(.is-wide) { grid-template-columns: minmax(0, 1fr) 11.5rem 8.5rem; }
.set2-rl-cap { padding: 0.5rem 0; border-bottom: 0.0625rem solid var(--hairline); }
.set2-rl-cap.is-wide { grid-template-columns: minmax(0, 1fr); border-bottom: 0; padding-bottom: 0.25rem; }
.set2-rl-cn { font-size: var(--ty-meta); color: var(--ink-secondary); }

.set2-rl-g { margin-top: 1.5rem; }
.set2-rl-gt {
  font-size: var(--ty-micro); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--ink-quiet);
  padding-bottom: 0.375rem;
}

/* The state, as a word. Same four values the matrix used and the same colour
   for each, so anybody who learned them there has not had to learn them
   again. */
.set2-rl-s { font-size: var(--ty-micro); white-space: nowrap; display: inline-flex; align-items: center; gap: 0.25rem; }
.set2-rl-s.is-ok   { color: var(--ink-ok); font-weight: var(--fw-semibold); }
.set2-rl-s.is-mute { color: var(--ink-quiet); }
.set2-rl-s.is-off  { color: var(--ink-quiet); }
.set2-rl-s .w-lock { color: var(--ink-ok); }

.set2-rl-same {
  display: flex; align-items: center; gap: 0.75rem;
  margin-top: 1rem; padding-top: 0.75rem;
  font-size: var(--ty-micro); color: var(--ink-quiet);
}
.set2-rl-same .set2-lnk { margin-left: auto; }

/* ── Capability-first ──
   The same data read the other way: one capability, every role. Wraps rather
   than scrolling sideways, which is the whole reason the grid had to go. */
.set2-rl-across {
  display: flex; flex-wrap: wrap; gap: 0.375rem;
  padding-bottom: 0.5rem;
}
.set2-rl-x {
  display: inline-flex; align-items: center; gap: 0.4375rem;
  appearance: none; cursor: pointer; font: inherit;
  padding: 0.25rem 0.5rem; min-height: 1.75rem;
  border: 0.0625rem solid var(--hairline); border-radius: var(--r-sm);
  background: var(--card-bg);
}
.set2-rl-x:hover { border-color: var(--ink-rule); }
.set2-rl-x:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }
.set2-rl-xn { font-size: var(--ty-micro); color: var(--ink-primary); font-weight: var(--fw-semibold); }

@media (max-width: 58rem) {
  .set2-rl { grid-template-columns: minmax(0, 1fr); gap: 1.25rem; }
  .set2-rl-nav { position: static; }
  .set2-rl-bd { flex-direction: row; flex-wrap: wrap; }
  .set2-rl-r { width: auto; }
  .set2-rl-heads,
  .set2-rl-cap:not(.is-wide) { grid-template-columns: minmax(0, 1fr) 10.5rem 6.5rem; }
}

/* ══ AND BELOW 46rem THE NAME TAKES A LINE OF ITS OWN ════════════════════
   The block above narrows the two fixed tracks to 10.5rem and 6.5rem, which
   is 168 + 104 + two 12px gaps = 296px before the name gets anything. A 390
   phone's column is 326 and a 360 one is 291, so `1fr` resolved to ZERO: the
   name had no box to wrap inside and "Change what a skill reaches" rendered
   straight across the Full / View / None control.

   Stacked, the name takes the line and the control the next — the only
   arrangement where both are readable at this width. The column heads name
   three columns that are no longer there, so they go with them.

   THIS SITS AFTER THAT BLOCK ON PURPOSE. Written above it, both selectors are
   `.set2-rl-cap:not(.is-wide)` at (0,2,0) and the 58rem rule won on source
   order — measured, the row still reported three tracks with the name at 0px
   and nothing changed. Fourth time this file has caught that; the override
   always goes after. */
@media (max-width: 735.98px) {
  .set2-rl-cap:not(.is-wide) { grid-template-columns: minmax(0, 1fr); row-gap: 0.375rem; }
  .set2-rl-heads { display: none; }
  .set2-rl-seg { justify-self: start; }
}

/* ══ CASCADE ═════════════════════════════════════════════════════════════
   The second casualty. A count says how big the hole is; this says what falls
   into it, which is the part people do not predict. */
.set2-cascade { background: var(--card-bg); border-radius: var(--r-md); padding: 0.75rem 0.875rem; margin-bottom: 1rem; }
.set2-cascade-t {
  font-size: var(--ty-micro); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
  color: var(--ink-quiet); margin-bottom: 0.5rem;
}
.set2-cascade-i { display: flex; gap: 0.625rem; align-items: baseline; padding: 0.1875rem 0; font-size: var(--ty-micro); }
.set2-cascade-k { flex: 0 0 10rem; color: var(--ink-primary); font-weight: var(--fw-semibold); }
.set2-cascade-v { color: var(--ink-quiet); }

/* ══ JOIN BAR ════════════════════════════════════════════════════════════ */
.set2-joinbar {
  display: flex; align-items: center; gap: 0.5rem;
  margin-bottom: 0.5rem; font-size: var(--ty-micro); color: var(--ink-quiet);
}
.set2-joinbar .seg { background: var(--card-bg); }
.set2-joinbar .seg-btn { font-size: var(--ty-micro); padding: 0.1875rem 0.5rem; }
.set2-joinbar .seg-btn.active { background: var(--card-bg-raised); color: var(--ink-primary); }

/* ══ PREVIEW TABLE ═══════════════════════════════════════════════════════
   The mapping and the data in one place, which is the only view where a wrong
   column is obvious. */
.set2-modal.is-wide { max-width: 52rem; }
.set2-pv-wrap { overflow-x: auto; border-radius: var(--r-md); background: var(--card-bg); }
.set2-pv { border-collapse: collapse; width: 100%; font-size: var(--ty-micro); }
.set2-pv th {
  text-align: left; padding: 0.625rem 0.75rem 0.5rem; white-space: nowrap;
  color: var(--ink-primary); font-weight: var(--fw-semibold);
}
.set2-pv th span {
  display: block; font-family: var(--font-sans); font-weight: var(--fw-medium);
  color: var(--ink-quiet); margin-top: 0.125rem;
}
.set2-pv td {
  padding: 0.4375rem 0.75rem; color: var(--ink-secondary);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.set2-pv tbody tr:nth-child(odd) td { background: var(--card-bg-raised); }
.set2-pv td i { color: var(--ink-quiet); }
kbd {
  font-family: var(--font-sans); font-size: var(--ty-micro);
  background: var(--card-bg-raised); color: var(--ink-quiet);
  padding: 0.0625rem 0.25rem; border-radius: var(--r-sm);
}

.set2-scrim.is-top { align-items: flex-start; padding-top: 12vh; }
.set2-pal-empty { padding: 1.5rem 0.75rem; text-align: center; font-size: var(--ty-meta); color: var(--ink-quiet); }
.set2-pal-empty b { color: var(--ink-secondary); }
/* ══ CHIPS ═══════════════════════════════════════════════════════════════ */

.set2-chip {
  display: inline-flex; align-items: center; gap: 0.3125rem;
  padding: 0.1875rem 0.4375rem; border-radius: var(--r-sm);
  background: var(--card-bg-raised);
  font-size: var(--ty-micro); color: var(--ink-quiet); white-space: nowrap;
}
.set2-chip b { color: var(--ink-primary); font-weight: var(--fw-semibold); }
.set2-chip button {
  appearance: none; border: 0; background: transparent; padding: 0; cursor: pointer;
  color: var(--ink-quiet); display: grid; place-items: center;
}
.set2-chip button:hover { color: var(--ink-primary); }
.set2-chip button svg { width: 0.625rem; height: 0.625rem; }
.set2-chip.is-act { border: 0; cursor: pointer; font: inherit; font-size: var(--ty-micro); }
.set2-chip.is-act:hover { color: var(--ink-primary); }

/* ══ CRITERIA ════════════════════════════════════════════════════════════ */

.set2-crit {
  display: flex; align-items: center; gap: 0.375rem; flex-wrap: wrap;
  padding: 0.625rem 0.75rem; background: var(--card-bg); border-radius: var(--r-lg);
}
/* The conjunction is stated. The console shows the same chips side by side
   with nothing between them, so whether two filters mean AND or OR is left
   for the reader to assume. */
.set2-crit-and { font-size: var(--ty-micro); color: var(--ink-quiet); font-style: italic; }
.set2-crit-all { font-size: var(--ty-micro); color: var(--ink-quiet); }

/* ══ BLAST RADIUS ════════════════════════════════════════════════════════
   What the action is about to touch, next to the button that does it. The
   console offers Test Sync and Run Sync with no figure anywhere, so the only
   way to learn the size of a run is to start one. */
/* Two shapes from one class. The retention modal still puts a figure and a
   sentence in here — that number IS the decision, because it counts records
   about to stop existing — while Trigger sync now holds only its actions. So
   the block keeps the fill and the padding, and `:has()` closes the gap the
   count used to fill rather than leaving the buttons adrift at the end of an
   empty bar. */
.set2-blast {
  display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap;
  margin-top: 0.125rem; padding: 0.75rem 0.875rem;
  background: var(--card-bg-raised); border-radius: var(--r-lg);
}
/* With the count gone there is nothing for a container to contain: a filled,
   rounded bar holding two right-aligned buttons draws a box around empty
   space and reads as a section of its own. The actions keep their place in
   the flow and lose the panel. */
.set2-blast:not(:has(.set2-blast-l)) {
  background: none; padding: 0.25rem 0 0; justify-content: flex-end;
}
.set2-blast-n {
  font-size: var(--ty-title); font-weight: var(--fw-bold); color: var(--ink-primary);
  line-height: var(--lh-none);
}
.set2-blast-l { font-size: var(--ty-micro); color: var(--ink-quiet); }
.set2-blast-end { margin-left: auto; display: flex; gap: 0.5rem; align-self: center; }
.set2-blast.is-err { background: var(--err-bg); }
.set2-blast.is-err .set2-blast-n { color: var(--ink-err); }
.set2-blast.is-err .set2-blast-l { color: var(--ink-err); }

/* ══ RUNS ════════════════════════════════════════════════════════════════ */

/* ── The history was one dense block, at every scale at once ──

   2px between rows, 8px of padding inside them, 4px between the chips and 4px
   under them to the failure reason — four different gaps, none of them large
   enough to separate anything from anything else. A failed run's code, its
   reason, its chips and the row below it all sat inside the same 8px band, so
   the eye had no way to tell which of those belonged together.

   The numbers are now on one ladder rather than picked per property: 6px
   between rows, 8px between chips, 12px inside the row vertically, 16px in
   from the edges, 20px between columns. Each step is meaningfully bigger than
   the one below it, which is what makes a gap read as a level. */
.set2-runs { display: flex; flex-direction: column; gap: 0.375rem; }
.set2-run {
  /* ── The date column was 120px for 77px of date ──

     43px of dead space in the narrowest column on the page, paid for by the
     one column that actually runs out: widening the chips pushed a run's third
     criterion onto a second line while a fifth of the row sat empty to its
     left. 88px fits the longest date here with room, and the 32px it gives
     back goes where the content is. */
  display: grid; grid-template-columns: 5.5rem 1fr auto auto;
  gap: 1rem; align-items: center;
  padding: 0.75rem 1rem; background: var(--card-bg); border-radius: var(--r-md);
  font-size: var(--ty-micro);
}
/* ══ A RUN IS FOUR COLUMNS UNTIL THERE IS NO ROOM FOR FOUR ═══════════════
   `5.5rem 1fr auto auto` with a 1rem gap wants 58px more than a 390 column
   has — measured — so the criteria chips and the why-link were pushed past the
   edge, and the date itself rendered 262px wide inside an 88px track.

   Below 46rem the date takes a line of its own and the rest share the next,
   which is the same wrap every other row on this surface makes. `min-width: 0`
   on the date because a grid item's automatic minimum is its content, and a
   date that refuses to shrink is what put 262px in an 88px cell.

   AND IT IS WRITTEN HERE, AFTER the rule it corrects. The first draft sat
   above it: both are (0,1,0), so the base template won on source order and
   nothing changed — measured, the head still reported four fixed tracks. This
   file has now been bitten by that three times; the override always goes
   after. */
@media (max-width: 735.98px) {
  .set2-run {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.375rem 0.75rem;
  }
  .set2-run-when { grid-column: 1 / -1; min-width: 0; }
  /* The column head names four columns that are no longer there. */
  .set2-run.set2-run-hd { display: none; }
}
/* ── One radius, because these are cards now ──

   The rounder outer corners were the treatment for a LIST: rows a hairline
   apart, shaped so the stack reads as one table with a top and a bottom. At a
   gap you can actually see, every row is its own card and two of them being a
   different shape from the other four is just an inconsistency. */
.set2-run-when { color: var(--ink-quiet); }
/* Both axes. The row gap was inherited from the 4px column gap, so criteria
   wrapping to a second line — which is what every failed run does — put the
   two lines closer together than the chips on either line. */
.set2-run-crit { display: flex; gap: 0.5rem; flex-wrap: wrap; min-width: 0; }
/* A chip whose text is "Status: Closed" needs room around the words to read as
   one object. Scoped to the run rows: the criteria builder above uses the same
   chip as an editable control with its own hit-target rules, and widening
   those is a different decision nobody asked for. */
.set2-run .set2-chip { padding: 0.25rem 0.5rem; }
.set2-run-n { color: var(--ink-secondary); text-align: right; }
/* The run's outcome, last in the row. It is a grid cell wrapping a pill, so it
   says only that the pill keeps its own width instead of stretching to the
   column — but it says it, rather than being a class with no rule anywhere. */
/* ── The outcome, and the way into it ──

   The pill says a run failed; the button under it says why. Stacked rather
   than side by side because they are not peers — one is the state, the other
   is a route out of it — and because side by side they pushed the criteria
   column into wrapping on exactly the rows that had the most to say. */
.set2-run-st {
  justify-self: end; display: flex; flex-direction: column;
  align-items: flex-end; gap: 0.375rem;
}
/* ── One height for every row ──

   The point of taking the reason off the row was the rhythm: a table read by
   scanning cannot have two row heights, one for runs that worked and one for
   runs that did not. The failed rows are now the taller of the two by the
   height of one small button, so the height they need becomes the height they
   all get. 15px of air on a succeeded row is not waste — it is the succeeded
   row agreeing to be the same shape as its neighbour. */
.set2-run:not(.set2-run-hd) { min-height: 4.25rem; }

/* ── The progress bar on a running sync ──
   Spans every column, because what is in progress is the run and a bar the
   width of the status cell cannot show a proportion. It is the last child of
   the same grid rather than a wrapper around the row, so the four columns
   above it keep the alignment they share with every other row. */
/* Bar and count on one line, the count to the RIGHT of the bar rather than up
   in the Records column — that column reports what a run wrote and stays empty
   until it has written it. */
.set2-prog-row {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 0.625rem;
}
.set2-prog {
  flex: 1 1 auto; min-width: 0;
  height: 0.1875rem; border-radius: var(--r-pill);
  background: var(--card-bg-raised); overflow: hidden;
}
.set2-prog-c {
  flex-shrink: 0; font-size: var(--ty-micro); color: var(--ink-quiet);
  font-variant-numeric: tabular-nums;
}
.set2-prog-fill {
  display: block; height: 100%; border-radius: inherit;
  background: var(--brand);
  /* Matches the tick, so the fill glides between samples instead of stepping
     sixteen times a second.

     The one duration in the build deliberately off the motion scale, and it
     should stay off it. Every rung there is chosen against a human — how long
     a close may take, how long a cursor will wait. This one is chosen against
     the sampler: it is 60ms because the data arrives every 62.5ms, and a rung
     that answered to taste would tear or stall the moment either changed.
     Snapping it to --t-fast would make the bar lag its own numbers. */
  transition: width 60ms linear;
}
/* A determinate bar already says how far along it is; the sheen says it is
   still MOVING, which is the fact a static screenshot of a stalled sync
   cannot distinguish from a live one. */
.set2-prog-fill::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.35), transparent);
  animation: set2-prog-sheen 2.2s linear infinite;
}
.set2-prog-fill { position: relative; }
@keyframes set2-prog-sheen { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) {
  .set2-prog-fill { transition: none; }
  .set2-prog-fill::after { animation: none; }
}
/* The count reads "128 / 412" — the half that changes is the half that is
   emphasised. */
.set2-run-n b { color: var(--ink-primary); font-weight: var(--fw-semibold); }
.set2-fld-n { width: 5rem; flex: 0 0 auto; text-align: right; }
/* ══ POPOVER ═════════════════════════════════════════════════════════════
   Anchored beside the cell it changes. GAPS 22 and 25 note the library has one
   dialog and it assumes the decision was made before it opened; choosing a CRM
   key IS the decision, and it is a decision about one cell. */
.set2-pop {
  position: fixed; z-index: 80;
  width: 15rem; max-height: 18rem;
  display: flex; flex-direction: column;
  background: var(--card-bg-raised);
  border: 0.0625rem solid var(--card-border);
  border-radius: var(--r-lg);
  /* ── PADDING, AND THE ABILITY TO SCROLL ──
     It had neither. Rows sat 1px from the border, so a hovered row's 6px
     radius pressed against the panel's 10px one and the two corners fought;
     4px of inset lets the row's own shape read.

     And `overflow: hidden` on a box with `max-height` is a clip, not a
     contract: the role picker is 359px of content in 288px of panel, so Read
     Only — the last row — could not be reached at all. It scrolls now, and
     the scroll stops at the panel instead of taking the page with it. */
  padding: 0.375rem;
  overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.22);
}
/* ══ A PANEL ARRIVES FROM THE THING IT BELONGS TO ═══════════════════
   `.set2-pop` declared no entry of any kind: every menu, role picker and kebab
   in this console appeared and disappeared between frames. The corner it grows
   from is written onto the element by placePop, which has already decided
   whether the panel sits above or below its anchor and whether it is left- or
   right-aligned - so the origin is not a guess made here, it is the placement
   decision restated.

   Scale and opacity only. A panel is a box with a shadow and a border radius;
   moving it costs a composite, and anything else costs a paint.
═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: no-preference) {
  .set2-pop {
    transform-origin: left top;
    animation: set2-pop-in var(--t-base) var(--ease-out) backwards;
  }
  .set2-pop[data-origin="top-right"]    { transform-origin: right top; }
  .set2-pop[data-origin="bottom-left"]  { transform-origin: left bottom; }
  .set2-pop[data-origin="bottom-right"] { transform-origin: right bottom; }
  .set2-pop.is-closing { animation: set2-pop-out var(--t-fast) var(--ease-out) both; }
  @keyframes set2-pop-in  { from { opacity: 0; transform: scale(0.97); } }
  @keyframes set2-pop-out { to   { opacity: 0; transform: scale(0.99); } }

  /* ══ AND A DIALOG ARRIVES OVER EVERYTHING ══════════════════════
     The scrim fades and the dialog rises into it, slightly behind - the ground
     goes dark first and the thing you have to answer arrives on top of it.
     Both leave faster than they came. */
  .set2-scrim { animation: set2-scrim-in var(--t-fast) var(--ease-out) backwards; }
  .set2-scrim .set2-modal { animation: set2-modal-in var(--t-base) var(--ease-out) backwards; }
  .set2-scrim.is-closing { animation: set2-scrim-out var(--t-fast) var(--ease-out) both; }
  .set2-scrim.is-closing .set2-modal { animation: set2-modal-out var(--t-fast) var(--ease-out) both; }
  @keyframes set2-scrim-in  { from { opacity: 0; } }
  @keyframes set2-scrim-out { to   { opacity: 0; } }
  @keyframes set2-modal-in  { from { opacity: 0; transform: scale(0.96) translateY(0.5rem); } }
  @keyframes set2-modal-out { to   { opacity: 0; transform: scale(0.98); } }
}

/* ── THE PANEL'S TITLE, SET AS ONE ──
   "Grant to Alex Smith" was micro and faint: the same size and colour as the
   CAPTIONS under the rows beneath it, so the one line naming what the whole
   panel does read as the quietest thing in it. It is the title, and the rows
   are the options — it takes body weight and full-strength ink, and the rows'
   own left edge so the column starts in one place.

   The rule beneath groups it with what it heads rather than leaving it
   floating above the first row. */
.set2-pop-hd {
  padding: 0.375rem 0.5rem 0.5rem;
  margin-bottom: 0.3125rem;
  border-bottom: 0.0625rem solid var(--hairline);
  font-size: var(--ty-meta); font-weight: var(--fw-semibold);
  color: var(--ink-primary); line-height: var(--lh-snug);
}
.set2-pop-hd b { color: var(--ink-action); font-weight: var(--fw-semibold); }
/* The search sits between the heading and the rows and belongs to neither, so
   it takes the rows' inset and its own breathing room below. */
.set2-pop-f { margin: 0 0.125rem 0.3125rem; width: calc(100% - 0.25rem); }
/* ── ROWS NEED AIR BETWEEN THEM, NOT JUST INSIDE THEM ──
   The rows were flush siblings, so a two-line row's second line sat directly
   above the next row's NAME with nothing between them: "Cannot change billing"
   and "Manager" read as one paragraph, and six roles read as a wall of text
   rather than six things you can press. The panel is already a flex column, so
   the separation is a gap — one value, applied between every child, including
   the heading and the rule.

   A row carrying two lines also needs more of its own: 6px top and bottom is
   right under a single line and cramped under two. */
/* ── WHY THE ROWS WERE OVERLAPPING ──
   The panel is a flex COLUMN with a `max-height`, so once six roles exceeded
   288px every row was flex-shrunk to fit instead of the panel scrolling: a row
   measured 42px tall around 59px of text, and each description ran into the
   name beneath it. Adding gaps made it worse, because the gaps came out of the
   same budget.

   Flex items shrink by default. These must not — they scroll. */
.set2-pop { gap: 0.1875rem; }
.set2-pop > * { flex-shrink: 0; }


/* ── SEPARATED BY SPACE, NOT BY A LINE ──
   A rule is a heavy way to say "these two belong to different groups" in a
   panel this small, and it competed with the group label already doing the
   job. Twice the gap says it on its own. */
.set2-pop-i.is-gap { margin-top: 0.5rem; }
.set2-pop-i.is-role,
.set2-pop-i.is-scope { padding-top: 0.5rem; padding-bottom: 0.5rem; }

/* Two lines that are ONE thing. 1px between them read as a rendering
   accident; 2px with the caption a step quieter reads as a caption. */
.set2-pop-tx { gap: 0.125rem; }
.set2-pop-i .set2-pop-p { line-height: var(--lh-snug); }
/* The description is prose and wraps; snug keeps a two-line one compact
   without letting the two lines touch. */
.set2-pop-i.is-role .set2-pop-s { line-height: var(--lh-snug); }

/* ── THE GROUP LABEL ──
   This style was in the file and nothing rendered it. It is what a menu of
   mixed actions needs: three ways to change somebody's access, then the two
   that are not about access at all. The label names the first group and a rule
   closes it, so the reader sorts five items into two ideas before reading any
   of them.

   Set at the quietest level in the panel on purpose — it is scaffolding for
   the rows, and a label that competes with what it labels has inverted the
   hierarchy it was added to create. */
.set2-pop-t {
  padding: 0.375rem 0.5rem 0.25rem;
  font-size: var(--ty-micro); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
  color: var(--ink-rule);
}
.set2-pop-t:not(:first-child) { margin-top: 0.25rem; }
.set2-pop-bd { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0.1875rem; scrollbar-width: thin; }
/* 32px, not 30. Two extra pixels per row is what stops a six-row menu reading
   as a list of text and starts it reading as a list of controls — and it puts
   the single-line rows on the same rhythm as the two-line ones. */
.set2-pop-i {
  display: flex; align-items: center; gap: 0.5rem; width: 100%;
  min-height: 2rem;
  appearance: none; border: 0; background: transparent;
  padding: 0.375rem 0.5rem; border-radius: var(--r-sm);
  font: inherit; font-size: var(--ty-micro); font-family: var(--font-sans);
  color: var(--ink-secondary); text-align: left; cursor: pointer;
}
.set2-pop-i:hover { background: var(--card-bg); color: var(--ink-primary); }
.set2-pop-i span:first-child { flex: 1 1 auto; }
.set2-pop-i .set2-pop-t {
  font-family: var(--font-sans); font-size: var(--ty-micro);
  color: var(--ink-quiet); letter-spacing: var(--ls-wide); text-transform: uppercase;
}
.set2-pop-row {
  display: flex; align-items: baseline; gap: 0.5rem;
  padding: 0.3125rem 0.5rem; font-size: var(--ty-micro); color: var(--ink-quiet);
}
.set2-pop-row code { font-family: var(--font-sans); color: var(--ink-secondary); }
.set2-pop-row b { color: var(--ink-primary); font-weight: var(--fw-semibold); margin-left: auto; }

/* ══ MODAL ═══════════════════════════════════════════════════════════════
   The library's `.modal` is a centred box that grows until something stops it.
   These two surfaces are the case GAPS 22 and 25 describe -- the body IS the
   decision -- so they get a height contract and an internal scroll here rather
   than inheriting one that has neither. */
.set2-scrim {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(0, 0, 0, 0.55);
  display: grid; place-items: center; padding: 1.5rem;
}
/* ── `--card-bg`, which is what the design system's own `.modal` uses ──
   This was `--body-bg`: the PAGE ground, borrowed for a thing that floats
   above the page. Two costs. It read flat, because a dialog painted the same
   value as the surface behind it has only its border to say it is in front;
   and it was the one neutral in a blue-tinted ramp — #171a1c beside #141b24
   and #1c2630 — so the modal was subtly the wrong temperature next to every
   other surface in the product.

   The shadow comes from the same DS rule, and is what actually does the
   lifting now that the fill is a card fill. */
.set2-modal {
  width: 100%; max-width: 34rem;
  max-height: calc(100dvh - 6rem);
  display: flex; flex-direction: column;
  background: var(--card-bg);
  border: 0.0625rem solid var(--card-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
}
.set2-modal-hd { display: flex; align-items: center; gap: 0.75rem; padding: 1rem 1.125rem 0.75rem; }
.set2-modal-t {
  font-family: var(--font-display);
  font-size: var(--ty-title); font-weight: var(--fw-bold); color: var(--ink-primary);
}
.set2-modal-x {
  margin-left: auto; appearance: none; border: 0; background: transparent;
  cursor: pointer; color: var(--ink-quiet); display: grid; place-items: center; padding: 0.25rem;
}
.set2-modal-x:hover { color: var(--ink-primary); }
.set2-modal-x svg { width: 0.875rem; height: 0.875rem; }
.set2-modal-bd { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0 1.125rem 1rem; }
/* Both of these were `--card-bg` while the dialog was `--body-bg`. The dialog
   is `--card-bg` now, so they step up rather than dissolving into it. */
.set2-modal-ft {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.875rem 1.125rem; background: var(--card-bg-raised);
}
.set2-modal-ft .set2-modal-end { margin-left: auto; display: flex; gap: 0.5rem; }

/* Upload: what the file must contain sits on the LEFT with the template button
   under it, and the dropzone on the right. The template is a thing you read
   before you drop, not an action on what you dropped. */
.set2-up { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.set2-up-req { font-size: var(--ty-micro); color: var(--ink-quiet); line-height: var(--lh-base); }
.set2-up-req li { margin-bottom: 0.375rem; }
.set2-up-req code { font-family: var(--font-sans); color: var(--ink-primary); }
.set2-drop {
  display: grid; place-items: center; gap: 0.5rem;
  min-height: 9rem; padding: 1rem; text-align: center;
  border: 0.0938rem dashed var(--card-border-hover);
  border-radius: var(--r-lg);
  background: var(--card-bg);
  font-size: var(--ty-micro); color: var(--ink-quiet);
  cursor: pointer;
}
.set2-drop:hover, .set2-drop.is-over { border-color: var(--brand); color: var(--ink-secondary); }
.set2-drop svg { width: 1.25rem; height: 1.25rem; }

@media (max-width: 52rem) {  .set2-col { padding: 1.5rem 1.25rem 5rem; }
  .set2-up { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {.set2-lad-ch,
.set2-exp,
.set2-ck,
.set2-row {
    transition-duration: 0.01ms !important;
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE CONNECTION SCOPE

   One control where the console had three: a product picker in the page
   chrome, a CRM picker in a section header, and a disabled third copy of the
   product inside the sync form. It sits in the module bar, so Config and Sync
   are visibly describing the same pair.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-scope-pick {
  display: inline-flex; align-items: center; gap: 0.375rem;
  appearance: none; border: 0.0625rem solid var(--hairline);
  background: var(--card-bg); cursor: pointer;
  padding: 0.25rem 0.5rem; border-radius: var(--r-sm);
  font: inherit; font-size: var(--ty-micro); color: var(--ink-quiet);
  transition: border-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.set2-scope-pick b { color: var(--ink-primary); font-weight: var(--fw-semibold); }
.set2-scope-pick:hover { border-color: var(--ink-rule); color: var(--ink-primary); }
.set2-scope-pick:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }
.set2-scope-pick .set2-lad-ch { width: 0.75rem; height: 0.75rem; opacity: 0.7; }

/* The picker's own rows are names, not paths, so they take the sans face back
   off `.set2-pop-i`, which is mono for the segment pickers.

   Two LINES, not two columns. Side by side, `.set2-pop-p` took its width first
   (nowrap, margin-left:auto) and left the name to wrap under it, so the row
   that mattered least set the row that mattered most. */
.set2-pop-i:has(.set2-pop-n) { flex-direction: column; align-items: flex-start; gap: 0.0625rem; }
.set2-pop-i .set2-pop-n {
  font-family: var(--font-sans); font-size: var(--ty-meta);
  font-weight: var(--fw-semibold); color: var(--ink-primary);
  flex: 0 0 auto; white-space: nowrap;
}
.set2-pop-i .set2-pop-p {
  font-family: var(--font-sans); font-size: var(--ty-micro);
  color: var(--ink-quiet); white-space: nowrap;
}
/* ── THE CURRENT ROW WAS NEVER MARKED ──
   This tinted with `--card-bg-raised`, which is the popover's OWN background
   in both themes — so every "you are here" in every one of these menus, the
   client picker and the role picker included, has been painting a row the
   exact colour it already was. Measured at #1c2630 on #1c2630 dark and
   #f7f9fc on #f7f9fc light.

   A brand tint instead, which no other state here uses: hover is
   `--card-bg`, so selected and hovered stay tellable apart on the row under
   the cursor — which was the second half of the same problem. */
.set2-pop-i.is-on { background: var(--brand-dim); }
.set2-pop-i.is-on .set2-pop-n { color: var(--ink-action); }

/* ══════════════════════════════════════════════════════════════════════════
   FAILURES

   Not a table. A failed run has to answer four things — what broke, when, why,
   and what it cost — and a row of cells makes the reason the narrowest column
   on the page when it is the only one anybody reads.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── The failure card, now anchored rather than listed ──

   It rendered in a `.set2-fails` column in a section of its own. That section
   is gone — a run's failure belongs on the run — so the card is what the
   popout holds, one at a time. The rules below are unchanged; the two that
   follow the block strip the chrome it no longer needs, because inside a
   popover the panel already IS the bordered surface and drawing a second
   border inside the first is just a line. */
.set2-fail {
  border: 0.0625rem solid var(--err-line);
  background: var(--err-bg);
  border-radius: var(--r-lg);
  padding: 0.75rem 0.875rem;
}
.set2-fail-hd { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.set2-fail-w { font-size: var(--ty-meta); font-weight: var(--fw-semibold); color: var(--ink-primary); }
.set2-fail-when { margin-left: auto; }
.set2-fail-why {
  margin: 0.375rem 0 0; font-size: var(--ty-meta); line-height: var(--lh-snug);
  color: var(--ink-secondary); max-width: 52ch;
}
.set2-fail-ft {
  display: flex; align-items: center; gap: 0.625rem; flex-wrap: wrap;
  margin-top: 0.625rem; padding-top: 0.625rem;
  border-top: 0.0625rem solid var(--hairline);
}
.set2-fail-n { font-size: var(--ty-micro); color: var(--ink-err); font-weight: var(--fw-semibold); }
.set2-fail-do { margin-left: auto; display: flex; align-items: center; gap: 0.625rem; }
/* Why THIS button and not another one — the difference between an action and
   a guess. Its own line: it is a sentence, and `.set2-from` is nowrap, so
   sharing the footer with the button ran it underneath. */
.set2-fail-note {
  margin: 0.25rem 0 0; font-size: var(--ty-micro); color: var(--ink-quiet);
  line-height: var(--lh-snug); max-width: 52ch;
}
/* Inside the popout the card is the content, not a surface of its own: the
   popover already drew the border, the radius and — importantly — the opaque
   ground. The card keeps none of the three.

   It kept the error TINT for one build, and the tint was `--err-bg`, which is
   a 12% red. Setting it as the panel's background replaced an opaque
   `--card-bg-raised` with something 88% see-through, so the rows underneath
   read straight through the explanation sitting on top of them. A panel is a
   surface. The red pill and the red records-lost line carry the state, which
   is what "one red per failed row" asks for anyway. */
.set2-pop .set2-fail { border: 0; background: none; border-radius: 0; }
/* The code, under the header rather than jammed into it. In the row it led the
   line and pushed the first readable word 22 characters in; here it is a label
   on the panel that names it, which is the one place somebody copies it from. */
.set2-pop .set2-fail > .set2-pill { margin-top: 0.5rem; }

/* ══════════════════════════════════════════════════════════════════════════
   PEOPLE, ANCHORED TO A SCOPE

   Rows in groups, not a grid of cards. The card grid's height was the roster;
   this page's height is whoever reaches one node, which does not grow with
   the company. The groups are the answer — granted here, inherited from
   above — so they carry the weight and the rows inside them stay quiet.

   Deliberately NOT a table: no column headers, no shared track widths between
   groups. Every row is a flex line that sizes to its own content, which is
   what keeps a person with three roles from setting a column width for a
   person with one.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── People's scope lives in the HEADER slot, in the Connections component ──
   It had a bar of its own sitting 40px under the one the header already keeps
   for exactly this, and a treatment of its own inside that bar — plain crumbs
   for the ancestors, a control only on the node you stood on. Both went. The
   bar is `.set2-scope` and every level is `.set2-scope-pick`, which is the
   control Dynamic fields has always used, so it needs no new rules at all.

   One addition stood here: `.is-muted`, which faded the whole scope line to
   55% while a search was running, on the reasoning that the search stops
   reading from the scope so the line should recede. It made a breadcrumb you
   still need — it is how you get back — the least readable thing on the page
   at exactly the moment you are lost in it. The search field having a value in
   it is the signal that a search is running. */

/* ── The scope menu row ──
   Two lines and a tick. `.set2-pop-i:has(.set2-pop-n)` turns every two-line
   row into a column, which would stack the tick under the caption, so the
   text gets a wrapper of its own and the row goes back to being a row. The
   selector carries one class more than that rule so it wins on specificity
   rather than on source order — two rules at 0,2,0 deciding a layout by
   which one was pasted second is not a cascade anybody can maintain. */
.set2-pop-i.is-scope:has(.set2-pop-n),
.set2-pop-i.is-role:has(.set2-pop-n),
.set2-pop-i.is-val:has(.set2-pop-n) { flex-direction: row; align-items: center; gap: 0.5rem; }
.set2-pop-i.is-val .set2-pop-n { flex: 1 1 auto; min-width: 0; }
.set2-pop-tx {
  display: flex; flex-direction: column; gap: 0.0625rem;
  flex: 1 1 auto; min-width: 0;
}
.set2-pop-tx .set2-pop-n,
.set2-pop-tx .set2-pop-p { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* THE TRIGGER MENU DOES NOT TRUNCATE.
   Everywhere else the caption is a detail beside a name you already know, so
   an ellipsis costs nothing. Here the caption IS the choice: the three names
   are `Always`, `Automatic` and `On demand`, and the second and third are
   indistinguishable from their names alone — the sentence saying which one
   fires when is the only thing separating them. "Fires when the description
   ma…" is the menu withholding the answer to the question it just asked.
   The copy is cut to the bone AND the rule is lifted; one without the other
   still truncates on a narrow panel. Three classes deep to outrank the rule
   above on specificity rather than on source order. */
.set2-pop.is-trig { width: 17rem; }
.set2-pop.is-trig .set2-pop-tx .set2-pop-p {
  white-space: normal; overflow: visible; text-overflow: clip;
  line-height: var(--lh-snug);
}
/* Reserved, not conditional: a tick that only occupies space on the current
   row shifts every caption sideways as the selection moves. */
.set2-pop-k {
  width: 0.75rem; height: 0.75rem; flex-shrink: 0;
  color: var(--ink-action); opacity: 0;
}
.set2-pop-i.is-scope.is-on .set2-pop-k,
.set2-pop-i.is-role.is-on .set2-pop-k,
.set2-pop-i.is-val.is-on .set2-pop-k { opacity: 1; }

/* ── The role menu ──
   Six sentences need more than the 15rem a list of names does, and they need
   to WRAP: `.set2-pop-s` truncates by default, which turned every description
   into "Everything, on every scope it is gra…" — the half that says what
   the role does, cut. Two lines, then ellipsis, so the panel still cannot run
   away down the screen. */
.set2-pop.is-roles { width: 20rem; }
.set2-pop-i.is-role .set2-pop-s {
  white-space: normal; line-height: var(--lh-snug);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.set2-pop-k svg { width: 100%; height: 100%; display: block; }


/* ── A group ──
   The label is the answer to "how does this person reach here", so it is set
   as a statement rather than as a quiet section marker, and it takes the
   status colour of what it means. */
.set2-sp-g + .set2-sp-g { margin-top: 1.5rem; }
.set2-sp-gt {
  display: flex; align-items: center; gap: 0.5rem;
  padding-bottom: 0.4375rem; margin-bottom: 0.375rem;
  border-bottom: 0.0625rem solid var(--hairline);
  font-size: var(--ty-micro); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--ink-quiet);
}
.set2-sp-gt.is-ok   { color: var(--ink-ok); }
.set2-sp-gt.is-warn { color: var(--ink-warn); }
.set2-sp-gt.is-err  { color: var(--ink-err); }
.set2-sp-gt .set2-num {
  color: var(--ink-quiet); font-weight: var(--fw-medium); letter-spacing: normal;
}
.set2-sp-ge {
  margin-left: auto; text-transform: none; letter-spacing: normal;
  font-weight: var(--fw-medium); color: var(--ink-quiet);
}

/* ── A row ── */
.set2-sp-row {
  display: flex; align-items: center; gap: 0.625rem;
  padding: 0.5rem 0.5rem; border-radius: var(--r-md);
  min-height: 3rem;
}
.set2-sp-row:hover { background: var(--card-bg); }
.set2-sp-row.is-picked { background: var(--brand-dim); }
/* Somebody holding nothing is still a person in the directory. Quiet, not
   hidden — an account you cannot see is one you will re-invite by accident. */
.set2-sp-row.is-none .set2-av { background: var(--card-bg-raised); color: var(--ink-quiet); }

.set2-sp-lines { display: flex; flex-direction: column; min-width: 0; flex: 0 1 15rem; }
.set2-sp-n2 {
  display: flex; align-items: center; gap: 0.375rem; min-width: 0;
  font-size: var(--ty-meta); font-weight: var(--fw-semibold); color: var(--ink-primary);
}
.set2-sp-m {
  font-size: var(--ty-micro); color: var(--ink-quiet);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The reason they are in this group, in their own words: the roles, and where
   each was written. Wraps rather than truncates — this is the content of the
   row, not a trailing detail. */
.set2-sp-why {
  display: flex; align-items: center; gap: 0.3125rem; flex-wrap: wrap;
  flex: 1 1 auto; min-width: 0;
  font-size: var(--ty-micro); color: var(--ink-quiet);
}
/* ── A LINE PER GRANT ──
   Stacked, and the role chip takes a fixed track so the client chips start at
   one x down the column — which is what makes "what does this person hold"
   readable vertically instead of parsed out of a run of chips. */
.set2-sp-why:has(.set2-sp-grants),
.set2-sp-why:has(.set2-sp-toggle) { flex-direction: column; align-items: flex-start; gap: 0.25rem; }
.set2-sp-grants { display: flex; flex-direction: column; gap: 0.25rem; width: 100%; }
.set2-sp-grant { display: flex; align-items: center; gap: 0.375rem; flex-wrap: wrap; }
.set2-sp-grant > .set2-chip.is-role { flex: 0 0 auto; min-width: 6.5rem; justify-content: center; }
.set2-sp-on { display: flex; align-items: center; gap: 0.3125rem; flex-wrap: wrap; min-width: 0; }
/* Collapsed still names the roles — only the clients are behind the press. */
.set2-sp-sum { display: flex; align-items: center; gap: 0.3125rem; flex-wrap: wrap; }
.set2-sp-toggle {
  display: inline-flex; align-items: center; gap: 0.25rem;
  appearance: none; border: 0; background: none; cursor: pointer; padding: 0;
  font: inherit; font-size: var(--ty-micro); font-weight: var(--fw-semibold);
  color: var(--ink-action);
}
.set2-sp-toggle:hover { text-decoration: underline; }
.set2-sp-toggle:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; border-radius: var(--r-xs); }
.set2-sp-toggle .set2-exp { width: 0.625rem; height: 0.625rem; transition: transform var(--t-fast) var(--ease-out); }
.set2-sp-toggle[aria-expanded="true"] .set2-exp { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .set2-sp-toggle .set2-exp { transition: none; } }
.set2-sp-note { color: var(--ink-quiet); }
.set2-sp-act { flex-shrink: 0; margin-left: auto; }

/* ── SELLING ──
   The line that says why the page is here: it belongs to AiMY Sales, and a
   reader who does not use that product is told so before anything else. In
   the prose ink rather than the quiet one, because it is the page's first
   sentence, not a caption on something.

   Two things were making it a paragraph to scroll past rather than a line to
   glance at: it was two sentences, and it rendered at the size of the prose it
   introduces. The words are one sentence now, and the step below follows.

   It belongs to the TITLE, so it is rendered with the title (see PAGE_LEDE)
   and spaced against it. Left in the content it sat under an empty scope bar
   and read as the page's first paragraph instead of as the title's second
   line.

   ── NOT `.set2-lede`, AND THE NAME MATTERS ──

   That class was taken: it is the count line under a filter row, 900px down
   this file, in `--ink-quiet` with a `b.is-warn` variant. Borrowing it put two
   unrelated components on one selector, and the OTHER one won on source order
   — which is why the top margin here read 14px when this rule plainly said
   -4. Nothing looked broken; the number was just somebody else's.

   `--ty-micro` — which is 14 here, not 12, because `.set2-col` shifts the
   scale up a step. That IS the surface's floor, so this needs no exception to
   it, and `--ink-secondary` keeps the contrast the small step spends nothing
   on.

   40rem, not 34: at 14px the one sentence measures ~570px, so a tighter
   measure buys a second line back after the words have just paid for one. */
.set2-page-lede {
  margin: 0.5rem 0 0; max-width: 40rem;
  font-size: var(--ty-micro); line-height: 1.5; color: var(--ink-secondary);
}
.set2-page-lede b { color: var(--ink-primary); font-weight: var(--fw-semibold); }

/* 46rem is this surface's own narrow rung — the one `.set2-col` and the title
   row already turn on — not a limit measured off this sentence. The gap opens
   rather than closes on the way down: the title and the line stop sharing a
   row with anything else, so they need their own separation instead of
   borrowing the page's. */
@media (max-width: 46rem) {
  .set2-page-lede { margin-top: 0.75rem; }
}
/* A row is the thing and what it is: the name, then its kind and the line
   that says what it does. The line takes the row's width — it is the only
   long text here — and Remove sits at the far edge. */
.set2-sell-row { min-height: 3.25rem; }
.set2-sell-lines { flex: 1 1 auto; gap: 0.125rem; }
.set2-sell-d { font-size: var(--ty-micro); line-height: var(--lh-snug); color: var(--ink-secondary); }
.set2-sell-none { padding: 0.5rem; font-size: var(--ty-meta); color: var(--ink-secondary); }
/* The title with its page's one action at the right edge. Centred on the
   title's line, because the button is shorter than the heading's line box. */
.set2-title-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.set2-title-row .set2-title { min-width: 0; }
.set2-title-row .btn { flex: none; }
/* The page's verb, and under it the way out to the thing the verb cannot
   reach. Right-aligned with the button rather than centred under it, so the
   two read as one control and its footnote rather than as a stack. */
.set2-act-stack {
  display: flex; flex-direction: column; align-items: flex-end;
  gap: 0.25rem; flex: none;
}
.set2-act-sub { font-size: var(--ty-micro); }

/* ── THE MENU ──
   Wider than the default panel so no name wraps. Capped in height and it
   scrolls: the catalogue in production is far longer than these eight, and
   the search is how you get through it. Rows are one line, so
   they take a 36px target — a list you pick several from is pressed several
   times, and the 32px default is sized for a menu you press once. The search
   field is the panel's own control, sized to its rows rather than to the
   page's inputs. */
.set2-pop.is-sell { width: 22rem; max-height: 30rem; padding: 0.5rem; }
.set2-pop.is-sell .set2-pop-hd { padding: 0.25rem 0.5rem 0.625rem; margin-bottom: 0.5rem; }
.set2-pop.is-sell .set2-pop-f {
  height: 2.25rem; margin: 0 0 0.375rem; width: 100%;
  padding: 0 0.75rem; font-size: var(--ty-meta);
}
.set2-pop.is-sell .set2-pop-bd { padding: 0; }
.set2-pop.is-sell .set2-pop-t { padding: 0.625rem 0.625rem 0.25rem; }
.set2-pop.is-sell .set2-pop-t:first-child { padding-top: 0.25rem; }
.set2-pop.is-sell .set2-pop-i { min-height: 2.25rem; padding: 0.375rem 0.625rem; }
.set2-pop.is-sell .set2-pop-i .set2-pop-n { font-weight: var(--fw-medium); }
/* Air between rows, because a row here is picked several times over: two
   ticked rows flush against each other read as one block of highlight. */
.set2-pop.is-sell .set2-pop-i + .set2-pop-i { margin-top: 0.25rem; }

/* ── THE DESCRIPTION WRAPS HERE, AND DOES NOT TRUNCATE ──

   Same call the trigger menu makes a few hundred lines up, for a weaker
   version of the same reason. Elsewhere a caption is a detail beside a name
   you already know and an ellipsis costs nothing; here half these rows are
   AiMY products and half are services the company runs for you, and *AiMY QA*
   against *QA and test automation* is exactly the pair a reader cannot tell
   apart from the names.

   Wrapping does not make the list ragged, which is the usual price: measured,
   the eight descriptions run 43–52 characters, so every one of them takes the
   same two lines at this width and the rhythm survives. If a much shorter or
   much longer one is ever added, THAT is when this needs looking at again.

   The tick goes to `flex-start` so it sits on the NAME. Centred against a
   two-line block it floats between the lines, pointing at neither. */
.set2-pop.is-sell .set2-pop-i { align-items: flex-start; }
.set2-pop.is-sell .set2-pop-p {
  white-space: normal; overflow: visible; text-overflow: clip;
  line-height: var(--lh-snug); color: var(--ink-quiet);
}
.set2-pop.is-sell .set2-pop-k { margin-top: 0.1875rem; flex: 0 0 auto; }

.set2-chip.is-role {
  background: var(--brand-dim); color: var(--ink-primary);
  font-weight: var(--fw-semibold);
}
/* A scope you can go to. It is a chip because it names a scope, and a button
   because "granted on Upland" is only useful if Upland is one press away. */
.set2-chip.is-go { cursor: pointer; appearance: none; font: inherit; }
.set2-chip.is-go:hover { border-color: var(--brand); color: var(--ink-action); }
.set2-chip.is-go:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }

.set2-av {
  width: 2rem; height: 2rem; flex-shrink: 0; border-radius: var(--r-pill);
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-dim); color: var(--ink-action);
  font-size: var(--ty-micro); font-weight: var(--fw-bold); letter-spacing: 0.02em;
}

/* Below the point where the reason and the row stop fitting on one line, the
   reason drops under the name rather than squeezing both. */
@media (max-width: 58rem) {
  .set2-sp-row { flex-wrap: wrap; }
  .set2-sp-lines { flex: 1 1 12rem; }
  .set2-sp-why { flex: 1 1 100%; padding-left: 3.5rem; }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE FILTER BAR

   A search that narrows and three dropdowns that narrow, all of them writing
   to the URL under one `f` key. Native `<select>` on purpose: it is the only
   control in this file a phone renders as a native wheel, and a filter is the
   last place to spend a custom popover.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── THE FILTER ROW IS A BOUNDARY ──
   It sat flush against the first group heading beneath it, so the controls
   that NARROW the list and the heading that counts what survived read as one
   block — and on a page where the row above is chrome and everything below it
   is the answer, that is the one gap that has to be legible. The gap is bigger
   than the one between the controls inside the row, which is what makes the
   row read as a unit rather than as the first line of the results. */
.set2-fbar {
  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  margin-bottom: 1.5rem;
}
/* The search is not a third filter, so it does not sit against them. `auto`
   opens the gap that says so, and the field keeps the width `.k-search` gives
   it rather than growing to fill what the gap leaves — a box that changes size
   with the viewport reads as layout filler, not as a control with a size. */
.set2-fbar-q { margin-left: auto; flex: 0 1 auto; }
.set2-fbar-end { display: flex; align-items: center; gap: 0.375rem; flex-wrap: wrap; margin-left: auto; }

/* ── THE FILTERS ARE `.v2-dropdown` NOW ──
   Everything that stood here styled a native `<select>` into looking like a
   control it was not — appearance stripped, a chevron absolutely positioned
   over it, `:valid` standing in for "something is chosen". None of it reached
   the LIST, which the OS drew in its own colours and its own theme.

   The design system's dropdown draws both halves, so the rules it needs are
   the two that place it in this row. */
.set2-fsel { min-width: 8rem; }
.set2-fsel .v2-dropdown-btn { width: 100%; justify-content: space-between; }

/* ══════════════════════════════════════════════════════════════════════════
   ROLE GROUPS

   A role, the KIND of scope it is granted on, and a chip per scope it reaches.
   Collapsed by default — the header alone answers "what can this person do and
   over what", and the chips are the detail behind that.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-tr { width: 0.875rem; height: 0.875rem; display: block; }

.set2-chip.is-type {
  background: var(--accent-dim); color: var(--ink-action);
  border-color: transparent; font-weight: var(--fw-semibold);
}
.set2-chip.is-scope { display: inline-flex; align-items: center; gap: 0.25rem; padding-right: 0.25rem; }
.set2-chip-x {
  appearance: none; border: 0; background: none; cursor: pointer;
  padding: 0; display: inline-flex; color: var(--ink-quiet); line-height: 0;
}
.set2-chip-x svg { width: 0.625rem; height: 0.625rem; }
.set2-chip-x:hover { color: var(--ink-err); }
.set2-chip-x:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.0625rem; }

.set2-kebab {
  margin-left: auto; flex-shrink: 0; appearance: none; border: 0; background: none;
  cursor: pointer; padding: 0.25rem 0.375rem; border-radius: var(--r-xs);
  display: flex; flex-direction: column; gap: 0.125rem; align-items: center;
}
.set2-kebab span { width: 0.1875rem; height: 0.1875rem; border-radius: 50%; background: var(--ink-faint); }
.set2-kebab:hover span { background: var(--ink-primary); }
.set2-kebab:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.0625rem; }

/* ── The picker's own back row and search ── */
.set2-pop-back {
  display: flex; align-items: center; gap: 0.375rem; width: 100%;
  appearance: none; border: 0; background: none; cursor: pointer;
  padding: 0.375rem 0.5rem; margin-bottom: 0.125rem;
  border-bottom: 0.0625rem solid var(--hairline);
  font: inherit; font-size: var(--ty-micro); font-weight: var(--fw-semibold);
  color: var(--ink-quiet); text-align: left;
}
.set2-pop-back:hover { color: var(--ink-primary); }
.set2-pop-back .set2-lad-ch { width: 0.75rem; height: 0.75rem; }
.set2-pop-f {
  width: 100%; appearance: none; border: 0; border-bottom: 0.0625rem solid var(--hairline);
  background: none; padding: 0.5rem; font: inherit; font-size: var(--ty-meta);
  color: var(--ink-primary); margin-bottom: 0.125rem;
}
.set2-pop-f:focus { outline: none; border-bottom-color: var(--brand); }
.set2-pop-i.is-err .set2-pop-n { color: var(--ink-err); }
.set2-pop-i .set2-pop-p svg { width: 0.75rem; height: 0.75rem; color: var(--ink-action); }

/* ══════════════════════════════════════════════════════════════════════════
   THE DATE RANGE, AND THE RETENTION ROWS
   ══════════════════════════════════════════════════════════════════════════ */
.set2-range { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.set2-range-d { flex: 0 0 auto; width: 10rem; font-variant-numeric: tabular-nums; }
/* ══════════════════════════════════════════════════════════════════════════
   SKILLS — THE PRODUCTION TABLE AND CLAUDE'S DOCUMENT

   The two-pane library that stood here is gone: a persistent left column of
   skills with their parts nested under them, which cost two clicks to reach
   three facts and used the same shape Claude and MagicPath already use for a
   different job.

   What replaces it is the list production actually ships — Name, Description,
   Status, all sortable, under the two filters and the search — and the detail
   Claude ships: a back link, the name over its author, tabs, and the file.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── THE LIST IS ROWS, NOT A TABLE ──

   The note here used to say "grid rather than <table> so the row can be one
   <button>: the whole row is the destination". Both halves have changed.

   The COLUMNS went because the description was a cell: clamped to two lines
   and sharing its width with a name that had to fit on one. Under the name
   it has the row, and the row is as tall as what is in it.

   The BUTTON went because the row carries a switch, and a control cannot be
   nested inside a button — invalid markup, and the outer button would take
   every click aimed at the switch. The whole row is still the destination:
   .set2-sk-go::after is stretched over it and the end cap sits above it on
   z-index, which is the one arrangement that gives a full-row target AND a
   switch that works. */
.set2-sk-sort {
  display: flex; align-items: center; gap: 0.5rem;
  margin-top: 1.25rem; padding: 0 0.5rem 0.5rem;
  border-bottom: 0.0625rem solid var(--hairline);
}
.set2-sk-sort-l { font-size: var(--ty-micro); color: var(--ink-faint); }

.set2-sk-r {
  position: relative;
  display: flex; align-items: flex-start; gap: 0.5rem;
  padding: 0.75rem 0.5rem;
  border-bottom: 0.0625rem solid var(--hairline);
}
/* Keyed to the LINK, not to the row: passing over the switch should not light
   the row and promise that pressing it opens something. */
.set2-sk-r:has(.set2-sk-go:hover) { background: var(--card-bg); }
.set2-sk-ico { flex: none; display: flex; margin-top: 0.125rem; color: var(--ink-quiet); }
.set2-sk-main {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem;
}
/* The name shrinks and ellipses; the tag beside it never does. min-width: 0
   on both the row and the name, or a long name pushes the tag off the end
   instead of truncating. */
.set2-sk-top {
  display: flex; align-items: center; gap: 0.5rem;
  min-width: 0; max-width: 100%;
}
.set2-sk-top .set2-pill { flex: none; }

/* The stretched link. `inset: 0` on the PSEUDO-element, not on the button, so
   the words keep their own box and stay selectable while the row is the hit
   area. z-index 0 puts it under .set2-sk-end without leaving the stack. */
.set2-sk-go {
  appearance: none; border: 0; background: none; padding: 0; margin: 0;
  font-family: var(--font-sans); font-size: var(--ty-meta);
  font-weight: var(--fw-semibold); color: var(--ink-primary);
  text-align: left; cursor: pointer; min-width: 0; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.set2-sk-go::after { content: ""; position: absolute; inset: 0; z-index: 0; }
.set2-sk-go:focus-visible {
  outline: 0.125rem solid var(--brand); outline-offset: 0.125rem;
  border-radius: var(--r-xs);
}
.set2-sk-d {
  font-size: var(--ty-micro); color: var(--ink-quiet);
  line-height: var(--lh-snug); max-width: 62ch;
}

/* Above the stretched link, so the switch takes its own clicks. Centred on
   the ROW rather than on the first line: it is a control for the whole row,
   not a mark on the name. */
.set2-sk-end {
  position: relative; z-index: 1;
  flex: none; align-self: center;
  display: flex; align-items: center; gap: 0.625rem;
}
.set2-sk-end > svg { color: var(--ink-faint); flex: none; }

/* A disabled or overridden skill keeps its row at FULL STRENGTH: one you
   cannot see is one you re-create by accident, and one you can barely read is
   one you misread. The switch says which it is, and where the switch cannot —
   Overridden, No agent, No product — the pill is still there to. */

/* ── A SWITCH THAT IS NOT YOURS TO THROW ──

   aimy-ds.css ships .toggle with no disabled state, so a locked one would be
   indistinguishable from a live one — the worst possible reading, since the
   difference is whether pressing does anything.

   It stays legible rather than dimmed: you still need to see whether the org
   left the skill on. What changes is that it reads as inert — the thumb drops
   to the ramp instead of white, the lit track loses half its saturation, and
   the cursor says so. Stated in color-mix against --ink-primary so it holds in
   both themes; the library’s own track is a flat rgba(255,255,255,…) and works
   in dark alone. */
.toggle.is-locked { cursor: not-allowed; }
.toggle.is-locked .toggle-track {
  background: color-mix(in srgb, var(--ink-primary) 12%, transparent);
}
.toggle.is-locked input:checked ~ .toggle-track {
  background: color-mix(in srgb, var(--brand) 45%, transparent);
}
.toggle.is-locked .toggle-thumb { background: var(--d300); box-shadow: none; }

.set2-th {
  display: inline-flex; align-items: center; gap: 0.3125rem;
  appearance: none; border: 0; background: none; cursor: pointer; padding: 0;
  font: inherit; font-size: var(--ty-micro); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--ink-quiet);
}
.set2-th:hover { color: var(--ink-primary); }
.set2-th:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }
/* The arrow is always drawn, so pressing a header is never a guess about
   whether it does anything. It lights only for the column doing the work. */
.set2-th-a { color: var(--ink-rule); display: inline-flex; align-items: center; }
/* An <svg> with a viewBox and no size fills its container, so the sort mark
   carries its own box rather than inheriting a font size it no longer uses. */
.set2-th-a svg { width: 0.75rem; height: 0.75rem; display: block; }
.set2-th-a.is-on { color: var(--ink-action); }

/* The narrow-width block that stood here hid the header and re-flowed the
   three columns into two. There are no columns now and the row stacks by
   construction, so it had nothing left to do. */

/* ── The document ──
   Claude's header: the name with its author beneath, and the controls that act
   on the whole skill at the far right. */
.set2-doc { display: flex; flex-direction: column; }
.set2-doc-hd {
  display: flex; align-items: flex-start; gap: 0.75rem;
  margin-top: 0.875rem;
}
.set2-doc-id { display: flex; flex-direction: column; gap: 0.125rem; min-width: 0; }
.set2-doc-t {
  margin: 0; font-family: var(--font-display);
  font-size: var(--ty-title); font-weight: var(--fw-bold);
  color: var(--ink-primary); letter-spacing: var(--ls-tight);
}
.set2-doc-by { font-size: var(--ty-micro); color: var(--ink-quiet); }
.set2-doc-end { margin-left: auto; display: flex; align-items: center; gap: 0.625rem; }
/* ══ AND THE CONTROLS DROP BELOW THE NAME ══════════════════════
   `.set2-doc-t` is the display face at --ty-title, so a single word of it is
   wider than the cell this row leaves on a phone — and a word cannot wrap
   inside itself. "Professional" ran out of its box and painted across the
   Enabled pill and the toggle beside it.

   The row wraps instead: back and name keep the first line, the pill, the
   toggle and the overflow take the second. `margin-left: 0` goes with it — an
   auto margin applies per flex LINE, so left alone it strands the three at the
   right end of a line they are alone on, which this file has now corrected
   five times. */
@media (max-width: 735.98px) {
  .set2-doc-hd { flex-wrap: wrap; row-gap: 0.5rem; }
  .set2-doc-end { margin-left: 0; width: 100%; }
}

/* ── Tabs ──
   Underlined rather than pilled: they switch what the page SHOWS, and a pill
   would put them in the same visual family as the filters above, which change
   what it contains. */
.set2-tabs {
  display: flex; align-items: center; gap: 0.25rem;
  margin: 1.25rem 0 0;
  border-bottom: 0.0625rem solid var(--hairline);
}

/* ══ AND BELOW 703 THE ACTIONS TAKE THEIR OWN LINE ═══════════════════════
   This row is two tabs and `.set2-tabs-end`, which is Download example and
   New skill — 256px of buttons that do not shrink, held on one line by the
   default `nowrap`. Measured on Skills, the row against the column it has:

       768   column 704, row wants 639   fits
       560   column 496, row wants 639   143px out
       480   column 416                  223px out
       360   column 291                  348px out

   Every one of those pushed `#pageScroll` sideways, which is why the page
   arrived scrolled right with its own title cut off at the left edge.

   Wrapped, the tabs keep the first line and the two buttons take the second.
   `margin-left: auto` goes with them — an auto margin applies per flex LINE,
   so left alone it strands the pair at the right end of a line they are alone
   on, which is the defect this file has now fixed four times. `width: 100%`
   is what forces the break rather than leaving them to land beside a tab.

   703 IS MEASURED: the row needs 639px of column, and the column is the
   viewport less this page's own 64px of padding — so they stop sharing a line
   just under 703. Checked at 768, where it fits, and 700, where it does not. */
@media (max-width: 702.98px) {
  .set2-tabs { flex-wrap: wrap; }
  .set2-tabs-end { margin-left: 0; width: 100%; padding-top: 0.125rem; }
}
.set2-tab {
  position: relative;
  appearance: none; border: 0; background: none; cursor: pointer;
  padding: 0.5rem 0.75rem; margin-bottom: -0.0625rem;
  font: inherit; font-size: var(--ty-meta); color: var(--ink-quiet);
  border-bottom: 0.125rem solid transparent;
  display: inline-flex; align-items: center; gap: 0.375rem;
}
.set2-tab:hover { color: var(--ink-primary); }
.set2-tab:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; border-radius: var(--r-xs); }
.set2-tab.is-on { color: var(--ink-primary); font-weight: var(--fw-semibold); border-bottom-color: var(--brand); }
/* `.set2-tab-dot` stood here. It marked the Precedence tab when a name was
   contested — a signal that had to exist because the answer was behind a tab.
   The answer is on Overview now, so the signal is the section itself. */

/* ── Overview ──
   Description left, and on the right the card Claude uses for the trigger —
   which is where "how and where does this fire" belongs. */
.set2-ov {
  display: grid; grid-template-columns: minmax(0, 1fr) 17rem;
  gap: 1.5rem; margin-top: 1.25rem; align-items: start;
}
.set2-ov-main { min-width: 0; }
.set2-ov-d {
  margin: 0.375rem 0 0; max-width: 40rem;
  font-size: var(--ty-meta); color: var(--ink-secondary); line-height: var(--lh-base);
}
.set2-ov-side { display: flex; flex-direction: column; gap: 0.5rem; }
.set2-ov-card {
  background: var(--card-bg); border-radius: var(--r-lg);
  padding: 0.625rem 0.75rem;
}
/* A card whose list is empty is a defect, not an empty state — the skill
   cannot run. It says so on its own edge. */
.set2-ov-card.is-err { box-shadow: inset 0 0 0 0.0625rem var(--err-line); }
.set2-ov-k {
  margin: 0; font-size: var(--ty-micro); color: var(--ink-quiet);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
}
.set2-ov-v { margin: 0.25rem 0 0; font-size: var(--ty-meta); color: var(--ink-primary); }
.set2-ov-chips {
  display: flex; align-items: center; gap: 0.3125rem; flex-wrap: wrap;
  margin-top: 0.4375rem;
}

/* ── Contents ──
   The file, with the rendered/source toggle in its own corner rather than in a
   bar above it: the toggle changes how THIS card reads, so it belongs to the
   card. */
.set2-file { margin-top: 1.25rem; }
.set2-file .set2-body {
  background: var(--card-bg); border-radius: var(--r-lg);
  padding: 1.25rem 1.5rem;
}


@media (max-width: 62rem) {
  .set2-ov { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .set2-ov-side { flex-direction: row; flex-wrap: wrap; }
  .set2-ov-card { flex: 1 1 12rem; }
}

/* ══════════════════════════════════════════════════════════════════════════
   SELECTION, AND THE BAR THAT ANSWERS IT

   The bar only exists while a selection does, and it offers only what applies
   to what is ticked — so it can never present an action that would do nothing
   to the people selected. Docked to the foot of the column rather than
   floating over the middle of the page: it belongs to the list above it.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-ck2 {
  width: 1.0625rem; height: 1.0625rem; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  appearance: none; cursor: pointer;
  border: 0.0625rem solid var(--ink-rule); border-radius: var(--r-xs);
  background: none; color: transparent; padding: 0;
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.set2-ck2:hover { border-color: var(--brand); }
.set2-ck2:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }
.set2-ck2[aria-checked="true"] { background: var(--brand); border-color: var(--brand); color: #fff; }
.set2-ck2 svg { width: 0.6875rem; height: 0.6875rem; }

.set2-bulk {
  position: sticky; bottom: 0; z-index: 6;
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  margin-top: 1.25rem; padding: 0.6875rem 0.875rem;
  border-radius: var(--r-lg);
  background: var(--card-bg-raised);
  border: 0.0625rem solid var(--hairline);
  box-shadow: 0 0.5rem 1.5rem rgba(0,0,0,0.18);
}
.set2-bulk-n { font-size: var(--ty-meta); color: var(--ink-secondary); }
.set2-bulk-n b { color: var(--ink-primary); }
.set2-bulk-end { margin-left: auto; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
body.is-settings .set2-bulk { bottom: 4.75rem; }

/* A word that acts. Not a button — it sits inside a sentence. */
.set2-lnk {
  appearance: none; border: 0; background: none; cursor: pointer; padding: 0;
  font: inherit; font-size: var(--ty-micro); color: var(--ink-action);
}
.set2-lnk:hover { text-decoration: underline; }
.set2-lnk:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }

/* ── The wide column ──
   46rem is the widest a settings ROW stays legible. A card grid capped at
   46rem gives two cramped columns, which is what made this surface feel
   condensed in the first place. Per-module, not global. */
.set2-col.is-wide { max-width: 72rem; }
.set2-lbl {
  display: flex;
  font-size: var(--ty-micro);
  font-weight: var(--fw-semibold);
  color: var(--ink-quiet);
  margin-bottom: 0.375rem;
  align-items: baseline;
  gap: 0.375rem;
}
/* A select styled as a field, for the places a native menu is the easiest
   control there is: few options, no search, and a phone renders it natively. */
.set2-fld-s {
  appearance: none; -webkit-appearance: none; cursor: pointer;
  padding-right: 1.75rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 0.875rem) 55%, calc(100% - 0.625rem) 55%;
  background-size: 0.25rem 0.25rem, 0.25rem 0.25rem;
  background-repeat: no-repeat;
}


/* ── The address box ──
   A field that grows chips. The input is the last child and takes the rest of
   the row, so typing always happens where the caret already is rather than in
   a separate box below the ones already added. */
.set2-hint .is-err { color: var(--ink-err); }


/* ══════════════════════════════════════════════════════════════════════════
   CRAFT PASS — TARGET SIZE

   Audited at 1536 and nearly every small control failed: the selection
   checkbox at 17x17, remove-a-scope at 10x10, delete-a-mapping at 18x18, the
   kebab at 15x21, "Advanced" at 18 tall. WCAG 2.5.8 sets the floor at 24x24
   CSS px; Apple asks 44pt and Material 48dp. Under the floor is not a taste
   question, it is a control people miss.

   THE GLYPH DOES NOT GROW. A 10px x is the right size for an x — what was
   wrong was the box around it. Every one of these keeps its drawn size and
   gains hit area, so the page looks the same and stops being a dexterity
   test. 1.75rem (28px) clears the floor with room for the focus ring.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-x,
.set2-chip-x,
.set2-kebab,
.set2-ck2,
.set2-modal-x {
  min-width: 1.75rem; min-height: 1.75rem;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
/* The chip's own x sat inside the chip's padding, so growing it would have
   grown the chip. Negative margin gives the hit area back without moving a
   pixel of what is drawn. */
.set2-chip-x { margin: -0.5rem -0.375rem -0.5rem -0.25rem; }
/* Text-shaped controls get their height from line-box padding rather than a
   min-height, so they stay on the baseline they share with their row. */
.set2-adv,
.set2-lnk {
  min-height: 1.75rem;
  display: inline-flex; align-items: center;
}
/* `.set2-map-add` and `.set2-map-new` stood here — the "Add subfield to X"
   rail and the "Add field mapping" link under the table. Both are gone: the
   subfield action is on the parent's row, and the primary action is a button
   in the section head. */

/* One focus ring for everything in this file. It was written eleven times
   with three different offsets, which is how a keyboard pass ends up looking
   like three different products. */
.set2-col :focus-visible,
.set2-modal :focus-visible,
.set2-pop :focus-visible {
  outline: 0.125rem solid var(--brand);
  outline-offset: 0.125rem;
  border-radius: var(--r-xs);
}

/* ── The save bar stops covering the last row ──
   It is sticky at the foot of the scroll port, so the column has to end above
   it. It was overlapping content on EVERY module — the audit caught it over
   person cards, mapping rows and settings rows alike. The reserve is
   unconditional: a little empty space at the bottom of a clean page costs
   nothing, and content hidden under a bar costs the edit somebody was making. */
/* 10rem of dead foot existed only so the last row could clear a bar that
   floated over it. Nothing floats now, so the page ends where it ends. */
body.is-settings .set2-col { padding-bottom: 5rem; }
.set2-map-mid {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1875rem;
  overflow: hidden;
}
.set2-sample-n { font-style: normal; color: var(--ink-rule); flex-shrink: 0; }
.set2-key {
  display: inline-flex;
  align-items: baseline;
  gap: 0.0625rem;
  appearance: none;
  border: 0.0625rem solid transparent;
  background: none;
  padding: 0.1875rem 0.4375rem;
  border-radius: var(--r-sm);
  font: inherit;
  font-family: var(--font-sans);
  font-size: var(--ty-meta);
  color: var(--ink-primary);
  cursor: pointer;
  text-align: left;
  transition: border-color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out);
  line-height: var(--lh-snug);
  /* The segments are flex items, so `overflow-wrap: anywhere` broke them from
     the inside — "requester.organizati / on.domai / n". A path wraps BETWEEN
     its segments or not at all: wrap the line, and let no segment shrink. */
  flex-wrap: wrap;
  overflow-wrap: normal;
  gap: 0;
}
.set2-key > * { flex: 0 0 auto; }
.set2-key-s {
  color: var(--ink-primary);
}
/* ── THE SEPARATOR HAS TO BE SEEN ──
   In JetBrains Mono the period occupied a full advance — a wide, mostly empty
   cell — so the 2px of margin on each side of it was three helpings of the
   same space and `assignee.name` came out as `assignee . name`.

   Urbanist gives it a 2.7px advance and nothing else, so with the margin
   removed the path went the other way and read as `assignee name`: the dot was
   there and too small to register between two words set tight against it.

   1px each side is the number. At 2px the separator measured 6.7px end to end
   — wider than Urbanist's own word space, so the path read as separate words
   with a speck between them, which is the same failure as the mono version at
   half the size. A step up in ink does the rest: the dot has to be SEEN, not
   given room. */
.set2-key-dot { color: var(--ink-quiet); margin: 0 0.0625rem; }
.set2-sample {
  display: flex;
  gap: 0.5rem;
  margin-top: 0;
  font-family: var(--font-sans);
  font-size: var(--ty-micro);
  color: var(--ink-quiet);
  min-width: 0;
  overflow: hidden;
  opacity: 0.85;
}
.set2-map-end {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  justify-content: flex-end;
  flex-shrink: 0;
}
.set2-adv {
  display: inline-flex;
  align-items: center;
  gap: 0.1875rem;
  appearance: none;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 0.125rem 0.25rem;
  margin-left: 0;
  font: inherit;
  font-size: var(--ty-micro);
  color: var(--ink-quiet);
}
.set2-ck2.is-void { border: 0; background: none; cursor: default; }

/* The count line under the filter row. One sentence, and the number that
   matters most in it is the one nobody wants to be non-zero. */
.set2-lede {
  margin: 0.875rem 0 0; font-size: var(--ty-micro); color: var(--ink-quiet);
}
.set2-lede b.is-warn { color: var(--ink-warn); }

@media (max-width: 62rem) {}
.set2-ck {
  appearance: none;
  border: 0.0938rem solid var(--ink-rule);
  background: transparent;
  width: 0.875rem;
  height: 0.875rem;
  border-radius: 0.1875rem;
  flex-shrink: 0;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  place-items: center;
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
  min-width: 1.5rem;
  min-height: 1.5rem;
  align-items: center;
  justify-content: center;
}
.set2-node .set2-ck { margin: -0.25rem 0; }
.set2-chip.is-act,
.set2-add,
.set2-chip button {
  min-height: 1.5rem;
  display: inline-flex; align-items: center; justify-content: center;
}
.set2-chip button { min-width: 1.5rem; margin: -0.375rem -0.25rem -0.375rem -0.125rem; }
/* The library ships `.seg-btn` at 20px tall. Raised only inside settings so
   the change cannot reach the filter row on the corpus, which is not ours. */
.set2-col .seg-btn { min-height: 1.75rem; padding-inline: 0.625rem; }
/* -- THE CARD SIZES TO ITS CONTENT --
   A fixed 15.5rem kept the grid even, and at 12px it fitted. At 14px it does
   not: the densest record needs 127px of grants port and has 108, so two of
   Alex Smith's roles were behind a scrollbar in a card whose whole job is
   showing what somebody can reach.
   A floor instead of a ceiling. Cards in one grid ROW already stretch to the
   tallest of them, so the rhythm survives without hiding anything -- the
   evenness came from the grid, not from the magic number. */

/* A count line under the filter row, replacing the tally that used to fight
   the section heading from the right-hand side. */
.set2-lede b { color: var(--ink-primary); font-weight: var(--fw-semibold); }




/* The scope pickers name what they choose. Two adjacent dropdowns showing only
   their values is a puzzle — "CXS" and "Knowledge Search" do not say which is
   the client and which is the product until you already know the answer. */
.set2-scope-k {
  font-size: var(--ty-micro); color: var(--ink-quiet);
  font-weight: var(--fw-medium); margin-right: 0.3125rem;
}


/* ── THE SPINE'S GUTTER, LEFT EMPTY ──
   `.set2-stage`, `.set2-spine*` and their responsive fallback have gone with
   the component. The map they drew now lives in the rail, beside the other
   list of destinations it was duplicating.

   Nothing took the space. The 11.25rem track plus its 2rem gap was dead gutter
   before the spine existed, so `.set2-col` keeps its own auto margins and its
   left edge is back where it was on a module that never had a spine — which
   was the same place, which is why the column does not move. */


@media (prefers-reduced-motion: reduce) {
  .rail-grp-x svg, .rail-fix-go { transition-duration: 0.01ms !important; }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE UNSAVED BLOCK

   Two shapes, one component. `is-stack` is the narrow column at the foot of
   the spine — sticky with it, in the gutter, covering nothing. `is-flat` is
   the wide row under a header, for the pages short enough to have no spine,
   where the header never leaves view anyway.

   Neither is fixed to the window and neither has a shadow, because neither is
   over anything now.
   ══════════════════════════════════════════════════════════════════════════ */
/* `display: flex` from an author sheet outranks the UA's `[hidden]`, so the
   attribute has to be honoured explicitly or a clean page shows an empty box. */

/* ── Under a header, on a page with no spine ── */

/* A text button, so the two reversible exits do not read as three equal
   choices beside the one that publishes. */
.set2-lnk.is-err { color: var(--ink-err); }

/* A section with no head of its own starts flush with where a head would
   have put its first row, so removing the heading does not also remove the
   space the page was breathing with. */
.set2-sec.is-headless { margin-top: 1.5rem; }

/* ══════════════════════════════════════════════════════════════════════════
   ONE RULE FOR THE 12px TIER

   Measured across a module, SIX of the twelve type roles resolved to the same
   12px / 400 / rgb(139,154,170): row help, section notes, tallies, spine
   notes, scope labels, row descriptions. The scale offers nothing between
   --ty-micro and --ty-meta, so size cannot separate them — and it should not,
   because they are not different in importance. They are different in KIND.

   PROSE is written to be read: help text, descriptions, the sentence under a
   heading. It reads at --ink-quiet.
   DATA is written to be scanned: counts, tallies, states. It stays at
   --ink-faint, one step back, and its figures are tabular.

   Contrast was never the problem — every one of them passes AA at 6.08:1.
   Telling them apart was.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-tally, .rail-pg-s, .set2-from { font-variant-numeric: tabular-nums; }


/* ══════════════════════════════════════════════════════════════════════════
   THE SPINE MARKS WHAT YOU CHANGED

   Reading position and unsaved state are different facts, so they get
   different marks. `is-here` raises the name to full contrast — where you are.
   `is-dirty` rings the dot in brand and leaves the fill alone — what you
   touched. A section can be both, and reads as both.
   ══════════════════════════════════════════════════════════════════════════ */
/* Both at once: the ring of the current stage, plus the hollow brand fill of
   an edited one. Neither cancels the other. */



/* ══════════════════════════════════════════════════════════════════════════
   THE FIELD TABLE, READ AT A GLANCE

   Measured before this: the AiMY field and the CRM key were both 13px and both
   rgb(238,242,246) — the same size and the same colour, separated only by
   weight. Two columns shouting equally, so nothing led and the eye had nowhere
   to land. The key looked LARGER because monospace is wider at equal px, which
   put the loudest voice on the value rather than on the thing it belongs to.

   And the nesting step was 22px. At 13px semibold that is not a step, it is a
   nudge — "Email domain" sat almost flush with the top-level rows it hangs
   under, so a tree rendered as a flat list.

   Three moves, in order of what the eye needs:
     1. The AiMY FIELD is the row's identity and keeps full contrast.
     2. The KEY is what the field is set to. Only its LAST segment identifies
        it — `requester.organization.domain` is "domain, from an organisation,
        from a requester" — so the ancestors step back and the leaf stays lit.
     3. The SAMPLES are evidence. They go quietest of all, and they stop
        clipping: a truncated value proves nothing.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 2. The path reads to its leaf ── */
/* ── WEIGHT, NOT A SECOND FACE ──
   These paths were set in JetBrains Mono. A monospace face says "this is a
   machine string" — true, and it cost a whole second typeface on a surface
   whose other 95% is Urbanist, at a size the file had already had to bump one
   step because mono runs small. Two faces disagreeing down one column is a
   louder signal than the one it was carrying.

   The distinction it was making is still worth making, and the type scale can
   make it: the leading segments are the ROUTE and recede, the last segment is
   the VALUE the field actually reads and takes the weight. That is the same
   ladder the rest of the surface uses, in the same face. */
.set2-key-s { color: var(--ink-quiet); font-weight: var(--fw-medium); }
.set2-key-s:last-of-type { color: var(--ink-primary); font-weight: var(--fw-bold); }
.set2-key-dot { color: var(--ink-quiet); margin: 0 0.0625rem; }
.set2-map-row.is-broken .set2-key-s:last-of-type { color: var(--ink-err); }

/* ── 3. Evidence, not content — but still content ──
   These were set to `--ink-rule`, which is the SEPARATOR token: rgb(107,123,139)
   at 4.02:1 on this background, under the 4.5 floor. A hairline colour is fine
   for a hairline and wrong for a word, and four text roles were wearing it —
   samples, Advanced, Add subfield, the +N count. They read at `--ink-faint`
   now: still the quietest text on the row, at 6.08:1. */
.set2-sample { color: var(--ink-quiet); }
.set2-sample i {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ══════════════════════════════════════════════════════════════════════════
   THE ROW IS A GRID AGAIN

   `.set2-map-row` had no `display` at all — it was a block, so its three cells
   stacked. The column head above it went on declaring `9rem 1fr auto`, which
   meant TEAMSUPPORT KEY was a heading over empty space and every key sat under its
   own field name instead of beside it. The actions cell, being a full-width
   block with `justify-content: flex-end`, became a line of its own with
   `Advanced` marooned at the right margin — which is why it read as a separate
   row, and why the plus and the bin in it were never found.

   Three columns, matching the head. Wider first column: eleven characters of
   9rem was ellipsising "Email address" at the second level of indent.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-map-row, .set2-adv-bd, .set2-map-ctx { --step: 1.75rem; }
.set2-map-row {
  display: grid;
  grid-template-columns: 12rem minmax(0, 1fr) auto;
  gap: 0.75rem;
  align-items: center;
  padding-left: 0.25rem;
}
/* ── THE INDENT MOVED INTO THE NAME CELL ──
   It was on the row, so a subfield pushed its KEY right as well as its name.
   Reading down the TeamSupport column — which is the column you scan when you are
   checking a mapping — every nested key started at a different x, and the
   deeper the field the further it drifted from the one above it. The tree is a
   fact about the NAME; the key column stays a column. */
.set2-map-ctx { padding-left: calc(var(--d, 0) * var(--step)); }
.set2-adv-bd  { padding-left: calc(0.25rem + (var(--d, 0) + 1) * var(--step)); }

/* ══ THE MAP KEEPS ITS KEY COLUMN, AND BELOW 46rem THAT MEANS SCROLLING ══
   The middle track is `minmax(0, 1fr)`, so it is the one that absorbs every
   pixel the other two do not want — and the other two do not shrink at all:
   the name column is a flat `12rem` and `.set2-map-end` is `flex-shrink: 0`
   around two text-labelled buttons and a bin. Measured, the three tracks and
   what the key column was left with:

       viewport   name   KEY   actions
       1536       192    243     209
        768       192    235     209
        560       192     22     209
        480       192      0     209     + 1px past the viewport
        400       192      0     209     + 81px past the viewport

   Two defects, and the smaller one is the overflow. At every width from 520
   down the TEAMSUPPORT KEY column is ZERO PIXELS WIDE — a column head standing
   over nothing, on the surface whose entire job is checking which source key a
   field is mapped to. The row then cannot honour its own third track either,
   so the actions run out past the right edge with no scrollbar to reach them:
   at 400 the bin sits 81px off-screen and cannot be pressed at all.

   A TABLE THAT GENUINELY NEEDS THE WIDTH SCROLLS; it does not delete a column
   and pretend. 35rem is the floor the three tracks add up to with a key column
   worth reading — 0.25 padding + 12 name + 0.75 gap + ~8 key + 0.75 gap + 13
   actions — and it is set on the HEAD AND THE ROWS TOGETHER, inside one
   scroller, so the heading never parts company with the column it names.

   46rem is settings.css's own narrow rung rather than a number invented for
   this: above it the key column already clears 190px and nothing here applies.
   `overflow-y: hidden` because a box that scrolls on one axis is given `auto`
   on the other by default, and an `.set2-adv-bd` opening inside would then
   grow a second scrollbar around a panel that is simply taller. */
@media (max-width: 46rem) {
  .set2-map { overflow-x: auto; overflow-y: hidden; }
  .set2-map-hd,
  .set2-map-row,
  .set2-adv-bd { min-width: 35rem; }
  /* The scrollbar is chrome on a table you are reading across; the fade at the
     edge is what says the row continues. Both are this build's own devices —
     the gate's chip carousel states the argument. */
  .set2-map { scrollbar-width: none; -ms-overflow-style: none; }
  .set2-map::-webkit-scrollbar { display: none; }
}

/* ── The actions stop competing with the data ──
   Four identical "Advanced" links down the column read as content. They step
   back to the level of a caption and come forward on approach — the row is
   still one tab stop away, so nothing is hidden from the keyboard. */
.set2-adv { color: var(--ink-quiet); }
.set2-map-row:hover .set2-adv,
.set2-map-row:focus-within .set2-adv { color: var(--ink-primary); }



/* ══════════════════════════════════════════════════════════════════════════
   VALUE MAPPING

   The panel rendered `<b></b>` over values seeded with an empty name, so every
   row read "Refund not received reads as" and stopped. There was no input.
   The code is fixed — it is what the connector sent, and editing it would
   break the match it exists to perform. The NAME is the field.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-pop.is-wide { width: 21rem; }
.set2-vm { display: flex; flex-direction: column; gap: 0.375rem; }
.set2-vm-row {
  display: grid;
  grid-template-columns: minmax(3.5rem, auto) 0.75rem minmax(0, 1fr) 1.75rem;
  align-items: center; gap: 0.375rem;
}
.set2-vm-k {
  font-family: var(--font-sans); font-size: var(--ty-micro);
  color: var(--ink-quiet); background: var(--card-bg);
  padding: 0.25rem 0.375rem; border-radius: var(--r-xs);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.set2-vm-ar { color: var(--ink-rule); text-align: center; font-size: var(--ty-micro); }
.set2-vm-v { min-width: 0; }
.set2-vm-none { font-size: var(--ty-micro); color: var(--ink-quiet); }

/* ══════════════════════════════════════════════════════════════════════════
   THE SETTINGS LAYER SITS ABOVE THE PAGE CHROME

   `.set2-pop` was z-index 80 and `.set2-scrim` 90, both UNDER `.app-topnav`
   at 100 — so a popover anchored high on the page had the AiMY logo painted
   through it, and a modal's backdrop dimmed everything except the one bar it
   most needed to cover.

   There is no z-index scale in this project (31 ad hoc values from -1 to 9999,
   zero tokens), so these two say what band they belong to rather than adding
   two more numbers: above the topnav, below the app-level chat surfaces
   (`.aimy-float-wrap` 550, `.aimy-overlay` 600) which own the whole window.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-pop   { z-index: 120; }
.set2-scrim { z-index: 130; }


/* The "+2" is a count, not a separator — same floor as the value it follows. */
.set2-sample-n { color: var(--ink-quiet); }
.set2-adv-none { font-size: var(--ty-micro); color: var(--ink-quiet); }

/* "e.g." marks the values as examples rather than as the field's content. It
   is the quietest thing on the line because it is a label, not a value. */
.set2-sample-l { color: var(--ink-rule); font-style: italic; flex-shrink: 0; }

/* The middot separates VALUES from each other. It has no business after the
   "e.g." label or before "and 2 more", which are prose, not list items. */
.set2-sample .set2-sample-l + i::before,
.set2-sample .set2-sample-n::before { content: none; }
.set2-sample-n { font-style: italic; }

/* The draft name sits exactly where the committed name will, so the row does
   not jump when it settles. */
.set2-map-name {
  font-size: var(--ty-meta); font-weight: var(--fw-semibold);
  padding: 0.25rem 0.5rem; width: 100%; min-width: 0;
}


/* ══════════════════════════════════════════════════════════════════════════
   A SUBFIELD LOOKS LIKE A SUBFIELD

   This was a tinted band per level — `--card-bg` at depth 1, `--card-bg-raised`
   at depth 2 — on the argument that two signals agreeing beat one signal drawn
   carefully. It read as a stack of filled cards inside a table of unfilled
   rows: the subfields looked like a different KIND of object rather than the
   same object one level down, and the fill made them the heaviest thing in a
   section where the parent rows are the ones you scan.

   It also spent the surface step this file reserves elsewhere for state.
   Nothing on the row could go `--card-bg` to say "suggested" or "broken" while
   two of every three rows were already tinted, which is the exact objection an
   earlier pass in this same file recorded before the bands came back.

   So: indent and space, and a guide so the step reads as descent rather than
   as a margin somebody chose. One signal, drawn where it means something.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── NO GUIDE ──
   There was a hairline down the parent's gutter, on the reasoning that a step
   needs a mark or it reads as a margin somebody chose. It does not: the rows
   already have a hairline between every one of them, so a second hairline at
   right angles to those was one more line in a table made of lines, and the
   only thing it added was a fragment hanging beside each "Add subfield" that
   attached to nothing.

   28px of indent against a 9rem label column is unambiguous on its own. The
   step and the space around the group carry it. */

/* ── SPACE IS THE OTHER HALF ──
   With the fill gone the run needs air around it, or a group and the row after
   it are one undifferentiated column of hairlines. A subfield sits tight to
   its parent; the next top-level field gets a clear gap. */
.set2-map-row.is-sub { min-height: 2.75rem; padding-top: 0.5rem; padding-bottom: 0.5rem; }
/* Air on both sides of the line that starts a group, so the separator reads as
   a break between two things rather than as a lid on the row beneath it. */
.set2-map-row:not(.is-sub) { margin-top: 0.375rem; padding-top: 1rem; }
.set2-map-hd + .set2-map-row { margin-top: 0; padding-top: 0.6875rem; }

/* A subfield's own name still reads at full contrast — it is a field, not a
   detail of the one above it. */
.set2-map-row.is-sub .set2-map-ctx { color: var(--ink-primary); }

/* ══════════════════════════════════════════════════════════════════════════
   THE CRM DROPDOWN

   It was a 150x24 run of text at the right edge of the section head, reading
   "Fields from TeamSupport" and sitting between the heading and a count — a
   control dressed as a caption, which is why it could not be found. The design
   has it as a select: bordered, its own height, unmistakably something you
   press. It scopes this section only, which the column head beneath it already
   states.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-crm-pick {
  display: inline-flex; align-items: center; gap: 0.375rem;
  min-height: 2rem; padding: 0.25rem 0.625rem;
  appearance: none; cursor: pointer; font: inherit;
  font-size: var(--ty-micro);
  color: var(--ink-primary);
  background: var(--card-bg-raised);
  border: 0.0625rem solid var(--card-border);
  border-radius: var(--r-md);
  transition: border-color var(--t-fast) var(--ease-out);
}
.set2-crm-pick b { font-weight: var(--fw-semibold); }
.set2-crm-pick svg { width: 0.75rem; height: 0.75rem; color: var(--ink-quiet); }
.set2-crm-pick:hover { border-color: var(--ink-rule); }
.set2-crm-pick:focus-visible {
  outline: 0.125rem solid var(--brand); outline-offset: 0.0625rem;
}

/* The relevance control carries its number and its unit together — "30 Days"
   is one choice, not a figure plus a label to read separately. A select
   because the ranges that matter are a short list, and a free number box
   invites 1,247 days. */
.set2-fld-sel {
  appearance: none;
  /* Sized to its content, not to the row. The control cell stretches its
     children, which turned a 110px choice into a 260px box reaching for the
     page edge. */
  width: auto; flex: 0 0 auto;
  padding-right: 2rem; min-width: 7.5rem; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-faint) 50%),
                    linear-gradient(135deg, var(--ink-faint) 50%, transparent 50%);
  background-position: calc(100% - 1.0625rem) 55%, calc(100% - 0.75rem) 55%;
  background-size: 0.3125rem 0.3125rem, 0.3125rem 0.3125rem;
  background-repeat: no-repeat;
}


/* ══════════════════════════════════════════════════════════════════════════
   TRIGGER SYNC + SYNC HISTORY, to the frame
   ══════════════════════════════════════════════════════════════════════════ */
.set2-card {
  background: var(--card-bg); border: 0.0625rem solid var(--hairline);
  border-radius: var(--r-lg); padding: 0.875rem;
}
.set2-card-t {
  font-size: var(--ty-micro); font-weight: var(--fw-semibold);
  color: var(--ink-quiet); margin-bottom: 0.625rem;
}

/* The typeahead. The list hangs under the field rather than in a popover: it
   belongs to this box, and a floating layer for eight rows is a layer to
   dismiss for no reason. */
.set2-ta { position: relative; }
.set2-ta-f { width: 100%; }
.set2-ta-list {
  position: absolute; left: 0; right: 0; top: calc(100% + 0.25rem); z-index: 4;
  display: flex; flex-direction: column;
  max-height: 13rem; overflow-y: auto;
  background: var(--card-bg-raised);
  border: 0.0625rem solid var(--card-border);
  border-radius: var(--r-md);
  box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.22);
}
.set2-ta-list[hidden] { display: none; }
.set2-ta-i {
  display: flex; align-items: baseline; gap: 0.375rem;
  padding: 0.4375rem 0.625rem; min-height: 1.75rem;
  appearance: none; border: 0; background: none; cursor: pointer;
  text-align: left; font: inherit; font-size: var(--ty-micro);
  color: var(--ink-primary);
}
.set2-ta-i:hover { background: var(--card-bg); }
.set2-ta-i:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; }
.set2-ta-k { color: var(--ink-quiet); }
.set2-ta-none { padding: 0.5rem 0.625rem; font-size: var(--ty-micro); color: var(--ink-quiet); }

/* Product and date range share a row: they scope the same run. */
.set2-up { display: grid; grid-template-columns: 1fr 1.4fr; gap: 0.875rem; }

/* The history table gets a head, because three columns of mixed content need
   naming — a date, a set of chips and a state do not announce themselves. */
.set2-run-hd {
  background: none; font-size: var(--ty-micro); color: var(--ink-quiet);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
  /* The head is a label for the column, not the first row of it. At 6px it was
     closer to the run below than that run was to its own contents. */
  padding: 0 1rem 0.625rem;
  /* Same track as the rows it labels. */
}
/* No row tint. The frame keeps a failed run on the same ground as every other
   and lets the pill carry the state -- and washing the row red dropped the
   chips sitting on it below the contrast floor, which is the failure state
   costing legibility exactly where it is needed. */
/* Was `.set2-retry`, which was underlined in error red and sat beside the
   pill. Two red things touching read as one alarm; this is the quiet half of
   the pair — the pill carries the state, and this only offers to explain it. */
.set2-why {
  appearance: none; border: 0; background: none; cursor: pointer; font: inherit;
  font-size: var(--ty-micro); color: var(--ink-quiet); white-space: nowrap;
  text-decoration: underline; text-decoration-color: var(--ink-rule);
  text-underline-offset: 0.125rem;
  padding: 0;
}
.set2-why:hover { color: var(--ink-primary); text-decoration-color: currentColor; }
.set2-why:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; border-radius: var(--r-xs); }


/* ══════════════════════════════════════════════════════════════════════════
   TRIGGER SYNC — UI FIXES

   Measured: the two field labels sat 16px apart (Product y=1501, Date range
   y=1517) because only one of the two fields carried a label of its own
   height; the read-only Product input was pixel-identical to the editable one,
   same background and same ink, so nothing said it was fixed; and the section
   subtitle wore `.set2-note`, which carries a 2px left rule and reads as a
   pulled-out callout rather than as the sentence under a heading.
   ══════════════════════════════════════════════════════════════════════════ */

/* A subtitle is prose under a heading. No rule, no indent, no quoting. */
.set2-sub {
  font-size: var(--ty-micro); color: var(--ink-quiet);
  line-height: var(--lh-snug); margin-bottom: 0.875rem;
  max-width: 60ch;
}

/* Both columns start at the same line, and the label reserves the same height
   whatever sits under it. */
.set2-up { align-items: start; }
/* `.set2-field` carries a top margin so stacked fields breathe. Inside this
   two-column grid the second one inherited it and started 16px below the
   first, so two labels on one row sat on different lines. The grid's own gap
   is the spacing here. */
.set2-up > .set2-field {
  display: flex; flex-direction: column; gap: 0.375rem;
  margin-top: 0;
}
.set2-up .set2-lbl { margin: 0; line-height: 1.2; }

/* ── A FIXED VALUE IS NOT AN EMPTY FIELD ──
   It is here because the run belongs to this product, and offering it again
   would be a second place to set the same thing. So it reads as a statement:
   flat, quieter ink, no caret, out of the tab order. */
.set2-fld.is-fixed {
  background: transparent;
  border-color: transparent;
  border-bottom-color: var(--hairline);
  border-radius: 0;
  color: var(--ink-quiet);
  padding-left: 0; padding-right: 0;
  cursor: default;
}
.set2-fld.is-fixed:focus { outline: none; }

/* The hint belongs to the dates above it. */
.set2-up .set2-hint { margin-top: 0.0625rem; }

/* The count and its two buttons get room from the row above rather than
   sitting 16px under a caption. */
.set2-blast { margin-top: 1.25rem; }


/* ══════════════════════════════════════════════════════════════════════════
   THE DATE RANGE

   A native date input prints "mm/dd/yyyy" when it is empty, in the same ink as
   a real value — so an unset range read as though it held something, and the
   something it appeared to hold was a US format string. It cannot be given a
   placeholder, so the empty state is dimmed to placeholder weight and the
   filled state comes back to full contrast.

   The picker button is the browser's, and it arrives as a grey square that
   matches nothing. It is tinted to the surface instead.
   ══════════════════════════════════════════════════════════════════════════ */
/* Every `-webkit-datetime-edit` and `-webkit-calendar-picker-indicator` rule
   that used to live here is gone, along with the `--cal-invert` variable that
   existed to flip a black icon the browser ships and this file cannot replace.
   The ends are buttons opening our own calendar; there is no vendor shadow DOM
   left to reach into. */
.set2-range-d { min-width: 0; }

/* One field for one value. It was two boxes with an arrow between them, sized
   to match each other; there is nothing left to match. `relative`, because the
   calendar hangs off it. */
.set2-range { display: flex; align-items: center; gap: 0.5rem; position: relative; }
.set2-range-d { flex: 1 1 auto; max-width: 20rem; }

/* ── THE TRIGGER ──
   It wears `.set2-fld`, so it is the same box as every other field on the
   page — this only adds what a button needs that an input does not, and the
   glyph that says what pressing it does. */
.set2-range-d {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  cursor: pointer; text-align: left;
  font-family: var(--font-sans); font-size: var(--ty-micro);
  color: var(--ink-primary);
}
.set2-range-d.is-empty { color: var(--ink-rule); }
.set2-range-d .set2-cal-i { flex-shrink: 0; color: var(--ink-quiet); }
.set2-range-d:hover .set2-cal-i { color: var(--ink-primary); }
.set2-range-d.is-open { border-color: var(--brand); }

/* ── THE PANEL ──
   Anchored to the pair rather than to one end, so opening from either box puts
   the calendar in the same place — the month you are reading does not jump
   sideways depending on which date you pressed.

   Below the topnav and below the chat surfaces, which is the band this file's
   z-index note puts settings popovers in. It opens UPWARD: the sync form sits
   low enough on its page that a 300px panel dropping from it would land in the
   float bar, and unlike the day picker there is only ever one of these, in one
   place, so the direction can simply be the right one. */
.set2-cal {
  position: absolute; z-index: 120;
  left: 0; bottom: calc(100% + 0.5rem);
  width: 17rem;
}
.set2-cal .cal-day { font-size: var(--ty-micro); }
.set2-cal .cal-dow { font-size: var(--ty-meta); }
.set2-cal .cal-hint b { color: var(--ink-quiet); font-weight: var(--fw-semibold); }
/* `.set2-range-a` — the arrow between the two date boxes — went with the
   second box. One field needs nothing pointing at anything. */

/* A run in flight is not a finished run. The pill says so, and the row it sits
   on says nothing else different — a sync that is working is the normal case,
   not an alert. */
.set2-run .set2-pill.is-info { font-variant-numeric: tabular-nums; }

/* The dry run shows the filter it used, above the rows it produced. */
.set2-pv-crit {
  display: flex; flex-wrap: wrap; gap: 0.375rem; align-items: center;
  margin-bottom: 0.875rem;
}


/* ══════════════════════════════════════════════════════════════════════════
   TRIGGER DELETE

   To the frame: a banner that states the irreversibility once, then a card of
   one row per CRM — name, threshold, days, and the red trash that starts it.
   The row's own figure sits in the gap the frame leaves between the name and
   the control, because a threshold that cannot state what it would destroy is
   a threshold nobody can judge.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-danger {
  display: flex; align-items: flex-start; gap: 0.5rem;
  padding: 0.6875rem 0.875rem; margin-bottom: 0.875rem;
  border: 0.0625rem solid var(--err-line);
  border-radius: var(--r-md);
  background: var(--err-bg);
  font-size: var(--ty-micro); color: var(--ink-err);
}
.set2-danger-i { flex-shrink: 0; display: flex; }
.set2-danger-i svg { width: 0.9375rem; height: 0.9375rem; }

/* ── NOT `overflow: hidden` ──
   It was, to clip row fills to the rounded corners. The rows carry no fill —
   the card's border and the hairlines between rows do all of it — so the only
   thing that clipping reached was the one element that has to escape: the day
   picker's panel opens downward out of the last row and was being cut off at
   the card's edge, which is a dropdown you cannot read the bottom of.

   The corners still round because the border rounds; there is nothing inside
   painting over them. */
.set2-ret-card {
  border: 0.0625rem solid var(--hairline);
  border-radius: var(--r-lg);
}
.set2-ret-row {
  display: grid;
  grid-template-columns: minmax(7rem, auto) minmax(0, 1fr) auto 2rem;
  align-items: center; gap: 0.875rem;
  padding: 0.875rem 1rem;
}
/* `minmax(7rem, …)` is a floor the row cannot pay for on a phone: measured at
   390 the four tracks wanted 13px more than the column. The name gives up its
   floor and ellipsises — it is the one cell still legible cut short, because
   the threshold beside it is the number being set. */
@media (max-width: 735.98px) {
  .set2-ret-row { grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto 2rem; gap: 0.5rem; }
}
/* Four tracks and three gaps still wanted 10px more than a 360 column had —
   measured, the sentence and the bin both ending past the edge. The sentence
   drops under the name and threshold rather than competing with them for a
   width none of the three can spare. */
@media (max-width: 400px) {
  .set2-ret-row { grid-template-columns: minmax(0, 1fr) auto 2rem;
                  gap: 0.375rem 0.25rem; }
  .set2-ret-says { grid-column: 1 / -1; }
  /* The threshold is a number and its unit, and its intrinsic 117.6px was
     the last thing forcing the row wide: three tracks and two 8px gaps
     wanted 236px of a 218px row, and the bin on the end was the part that
     fell outside it — clipped by the card rather than scrolled to, so it
     could not be pressed at all. */
  .set2-ret-c { min-width: 0; }
  .set2-ret-c .set2-fld { min-width: 0; }
}
/* AND THE SENTENCE HAS TO BE ALLOWED TO WRAP. Spanning every track does not
   make an item free of them — its own min-content is distributed back across
   the tracks it covers, so a one-line sentence was widening the name column it
   sits under: measured at 360, the first track resolved to 99px on the rows
   with the longest sentence and 71px on the rest, and the row ran 30px past
   the column.  lets it break instead, which is what a sentence
   is for. The threshold control keeps its width; it is a number and a unit and
   there is nothing in it to wrap. */
@media (max-width: 735.98px) {
  .set2-ret-says { min-width: 0; overflow-wrap: anywhere; }
}
.set2-ret-row + .set2-ret-row { border-top: 0.0625rem solid var(--hairline); }
.set2-ret-n {
  font-size: var(--ty-meta); font-weight: var(--fw-semibold); color: var(--ink-primary);
}
/* The consequence, in the frame's empty middle. Quiet until it is alarming. */
.set2-ret-says { font-size: var(--ty-micro); color: var(--ink-quiet); }
.set2-ret-c { display: flex; align-items: center; gap: 0.5rem; }

/* The trash is the destructive act and the only red control on the row. It
   still opens the typed confirmation — the icon starts the ladder, it does not
   skip it. */
.set2-ret-del {
  appearance: none; border: 0; background: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 2rem; min-height: 2rem; border-radius: var(--r-sm);
  color: var(--ink-err);
}
.set2-ret-del svg { width: 1rem; height: 1rem; }
.set2-ret-del:hover { background: var(--err-bg); }
.set2-ret-del:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; }

@media (max-width: 46rem) {
  .set2-ret-row { grid-template-columns: 1fr auto 2rem; }
  .set2-ret-says { grid-column: 1 / -1; order: 3; }
}


/* ══════════════════════════════════════════════════════════════════════════
   KNOWLEDGE ENABLEMENT WEBHOOK SETTINGS

   To the frame: a blue note naming who calls these, then a labelled row per
   credential — the field with a help affordance inside it, a copy button, and
   its own Save.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-info {
  display: flex; align-items: flex-start; gap: 0.5rem;
  padding: 0.6875rem 0.875rem; margin-bottom: 1rem;
  border: 0.0625rem solid color-mix(in srgb, var(--ink-info) 28%, transparent);
  border-radius: var(--r-md);
  background: var(--info-bg);
  font-size: var(--ty-micro); color: var(--ink-info);
}
.set2-info-i { flex-shrink: 0; display: flex; }
.set2-info-i svg { width: 0.9375rem; height: 0.9375rem; }

.set2-wh { display: flex; flex-direction: column; gap: 1.125rem; }
/* Label left, control right, and the note under the control it belongs to —
   not under the whole row, where it would sit below the label instead. */
.set2-wh-row {
  display: grid;
  grid-template-columns: 8rem minmax(0, 1fr);
  align-items: center; gap: 0.375rem 1rem;
}
.set2-wh-l {
  font-size: var(--ty-meta); font-weight: var(--fw-semibold); color: var(--ink-primary);
}
.set2-wh-c { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.set2-wh-note {
  grid-column: 2; font-size: var(--ty-micro); color: var(--ink-quiet);
  display: flex; align-items: baseline; gap: 0.5rem;
}

/* The help sits INSIDE the field, as the frame has it, so the field keeps its
   full width and the affordance does not become a fourth control in the row. */
.set2-wh-f { position: relative; flex: 1 1 auto; min-width: 0; display: flex; }
.set2-wh-f .set2-fld { width: 100%; padding-right: 2.25rem; }
.set2-wh-help {
  position: absolute; right: 0.25rem; top: 50%; transform: translateY(-50%);
  appearance: none; border: 0; background: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.75rem; min-height: 1.75rem; border-radius: var(--r-sm);
  color: var(--ink-quiet);
}
.set2-wh-help svg { width: 0.875rem; height: 0.875rem; }
.set2-wh-help:hover { color: var(--ink-primary); }
.set2-wh-help:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; }

.set2-wh-copy {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 2rem; min-height: 2rem; flex-shrink: 0;
  border: 0.0625rem solid var(--card-border); border-radius: var(--r-md);
  background: var(--card-bg-raised); color: var(--ink-quiet);
}
.set2-wh-copy svg { width: 0.875rem; height: 0.875rem; }
.set2-wh-copy:hover { color: var(--ink-primary); border-color: var(--ink-rule); }
.set2-wh-copy:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.0625rem; }

@media (max-width: 46rem) {
  .set2-wh-row { grid-template-columns: minmax(0, 1fr); }
  .set2-wh-note { grid-column: 1; }
}


/* ══════════════════════════════════════════════════════════════════════════
   THE FLOOR IS 14px, IN SETTINGS

   The console's scale runs down to a 12px micro, and its rail drops --ty-body
   to 14 while holding micro at 12 -- "a floor that bends is not a floor," as
   the comment there says. That floor is right for a corpus view read at a
   glance and wrong for a settings surface read closely, where micro was
   carrying help text, counts, notes and every table caption.

   So the scale is re-pointed for this surface only, on `body.is-settings`.
   Nothing outside settings moves: the grid and document views keep the scale
   they were measured at, which is what the 1536 invariant protects.

   The steps stay four wide and keep their ratios; the whole scale shifts up.
   Recession that used to come from size now comes from colour, which the
   prose/data split already established.
   ══════════════════════════════════════════════════════════════════════════ */
body.is-settings .set2-col,
body.is-settings .set2-scrim,
body.is-settings .set2-pop {
  /* 15 stood here, and it was the last odd step in the product: one pixel off
     the floor above it and one pixel off the body below it, which is the exact
     thing the console's scale comment calls a level a reader cannot see. Three
     steps inside two pixels is not a hierarchy, it is a blur.

     A 14 floor on an even scale leaves 14 · 16 · 20, so the row name joins the
     body step and separates from prose the way the rail already does — weight
     and ink, not size. Nothing moves DOWN: the floor is still 14. */
  --ty-micro:   0.875rem;   /* 14 - FLOOR: counts, captions, help */
  --ty-meta:    1rem;       /* 16 - label, row name               */
  --ty-body:    1rem;       /* 16 - prose, a value you read for    */
  --ty-title:   1.25rem;    /* 20 - section head                   */
}

/* ── THE COMPENSATION OUTLIVED WHAT IT COMPENSATED FOR ──

   This lifted .btn-sm to --ty-meta because "`.btn-sm` ships at 10.5px from
   the shared chrome and settings uses it for Test sync, Run sync, Save,
   Confirm and Delete — the most consequential controls on the surface were
   the smallest text on it".

   It does not ship at 10.5px any more. knowledge.css puts .btn-sm on the meta
   step, so it is 14 everywhere else in the product — and because THIS surface
   re-points --ty-meta to 16, the old compensation was lifting it a second
   time. "Download example" and "New skill" rendered 16/600, the body step, on
   a row of 14px controls.

   --ty-micro is 14 here, which is this surface’s floor and the product’s meta
   step at once: the same size .btn-sm takes on every other screen. */
body.is-settings .set2-col .btn-sm,
body.is-settings .set2-scrim .btn-sm,
body.is-settings .set2-pop .btn-sm { font-size: var(--ty-micro); }

/* This compensated for mono running visually small. Nothing here uses mono, so
   it went with it — everything sits on the one scale now. */

/* ── A WORD, NOT A GLYPH ──
   It was a bare "+" at `--ink-rule`, invisible until the row was hovered. Two
   things wrong with that and only one of them is the contrast: a plus on a row
   in a table of fields could as easily mean "add a field here" as "add a field
   UNDER this", and the word is the only thing that says which. It reads at the
   weight of the other row actions and comes forward on approach like them. */
.set2-map-plus {
  appearance: none; border: 0; background: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 0.25rem;
  min-height: 1.75rem; padding: 0.125rem 0.375rem; border-radius: var(--r-sm);
  font: inherit; font-size: var(--ty-micro); font-weight: var(--fw-semibold);
  white-space: nowrap; color: var(--ink-quiet);
}
.set2-map-plus svg { width: 0.625rem; height: 0.625rem; }
.set2-map-plus:hover, .set2-map-plus:focus-visible { color: var(--ink-action); background: var(--card-bg); }
.set2-map-plus:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; }


/* ══════════════════════════════════════════════════════════════════════════
   INVITE, INLINE

   The frame's shape: one field across the top of the list, a Send beside it.
   It reuses the address box the modal had -- chips grow inside the field and
   the caret stays where typing happens -- and the button stretches to the
   field's height so the two read as one control, not a box with a button
   parked next to it.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 46rem) {
    }

/* ══════════════════════════════════════════════════════════════════════════
   ROW ACTIONS COME FORWARD ON APPROACH

   Every mapping row carried a plus, an Advanced and a bin at full time -- on
   eight rows, twenty-four controls standing in a column beside the data they
   act on, so the actions read as a second table. The two that CHANGE the
   structure step out of sight until the row is approached by pointer or by
   keyboard; Advanced, which only discloses, stays where it was. Nothing is
   removed from the tab order, and a touch screen, which has no hover to
   approach with, keeps them visible.
   ══════════════════════════════════════════════════════════════════════════ */
/* Only the BIN hides now. Adding a subfield is the act this section exists to
   let you perform and it was the one control you had to find by accident;
   deleting a mapping is the one you should have to reach for. Hiding both put
   the constructive act and the destructive one behind the same gesture. */
.set2-map-row .set2-x {
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out),
              background var(--t-fast) var(--ease-out);
}
.set2-map-row:hover .set2-x, .set2-map-row:focus-within .set2-x { opacity: 1; }
/* ══ A TARGET BIG ENOUGH TO HIT ════════════════════════════════════
   The settings modules' own two undersized controls, on the pattern
   knowledge.css states in full at §A TARGET BIG ENOUGH TO HIT: the box stays
   where the layout put it and a transparent `::after` carries a 44px hit area,
   so the map rows keep their rhythm and nothing reflows. Measured at 390px —
   `.set2-tip-b` is 18x18 and `.set2-x` is 28x28, and both have room to spare
   on every side. `pointer: coarse` only, so a mouse never sees it. */
@media (pointer: coarse) {
  .set2-tip-b, .set2-x { position: relative; }
  .set2-tip-b::after, .set2-x::after {
    content: '';
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: max(100%, 2.75rem);
    height: max(100%, 2.75rem);
  }
}

@media (hover: none) {
  .set2-map-row .set2-x { opacity: 1; }
}

/* ══════════════════════════════════════════════════════════════════════════
   ONE RED PER FAILED ROW

   A failed run painted its code, its reason, its pill and its Retry all in the
   error colour -- four alarms on one row, three rows deep, so the history read
   as an incident board rather than a log with three failures in it. Only the
   pill is red now, because it is the only one of the four still on the row:
   the code and the reason moved into the panel, and the link that opens it is
   deliberately the quiet half of the pair.
   ══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════
   WEBHOOKS: ONE SECTION, A GROUP PER CONNECTOR, A SAVE THAT SLEEPS
   ══════════════════════════════════════════════════════════════════════════ */
.set2-wh-grp + .set2-wh-grp {
  margin-top: 1.5rem; padding-top: 1.375rem;
  border-top: 0.0625rem solid var(--hairline);
}
/* ══ AND IT WRAPS, BECAUSE THE BUTTON DOES NOT SHRINK ════════════════════
   The row is the connector's name, its status pill and Test connection. That
   last is 136px and `nowrap` held all three on one line whatever the width, so
   on a phone the button left the page: measured at 390, the row wanted 372px
   of a 282px column and the button ended 70px past the page's own edge, with
   `#pageScroll` growing a sideways scroll to reach it.

   `wrap` is the whole fix. Above the width where all three fit it changes
   nothing — there is no second line to make — and below it the button takes
   one of its own, still at the right edge because `.set2-wh-test` keeps its
   `margin-left: auto`. */
.set2-wh-hd { display: flex; align-items: center; flex-wrap: wrap; gap: 0.625rem; margin-bottom: 0.875rem; }

/* ══ AND WHAT DROPS IS THE TAG, NOT THE ACTION ═══════════════════════════
   Wrapping alone put "Test connection" on the second line and left the name
   sitting beside its status pill — which reads as the pill being the point of
   the row and the button an afterthought. It is the other way round: the name
   and the thing you can DO to it belong together, and the status is what the
   pair is about.

   `order: 1` moves the pill behind the button in flow, so the line fills with
   the name and the action and the tag takes the next one. The button keeps its
   `margin-left: auto`, so it stays at the right edge of the name's line rather
   than sitting against it.

   AND THE NAME GIVES WAY, not the button. At 320 the widest name and the
   button want 259px of a 251px row, so without this the BUTTON would wrap and
   the reorder would buy nothing — three lines instead of two. The name
   ellipsises instead: it is the one part of the row that is still legible cut
   short, because the pill under it and the fields below it say which endpoint
   this is.

   494 IS MEASURED. The three want 385px in a row that is the viewport less
   109, so they stop sharing a line just under a 494 viewport — checked at 480,
   where the button had already dropped 33px, and at 520, where it had not. The
   `.98` clears a fractional viewport, as everywhere else in this build. */
@media (max-width: 493.98px) {
  .set2-wh-hd { row-gap: 0.375rem; }
  .set2-wh-hd .set2-pill { order: 1; }
  .set2-wh-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .set2-wh-test { flex-shrink: 0; }
}
/* Right edge, away from the name and the pill it would otherwise read as part
   of — those two say what the endpoint IS, and this does something to it. */
.set2-wh-test { margin-left: auto; }
.set2-wh-t {
  margin: 0; font-family: var(--font-display);
  font-size: var(--ty-meta); font-weight: var(--fw-bold);
  color: var(--ink-primary); letter-spacing: var(--ls-tight);
}
/* Asleep: legible, plainly inert. The library's 0.38 made "Saved" unreadable
   in the second it is shown. */
.set2-col .set2-wh-save:disabled { opacity: 0.55; cursor: default; }
/* Awake: the one primary action for the field that changed, and only that
   field. */
.set2-col .set2-wh-save.is-dirty {
  background: var(--brand); color: #fff; border-color: var(--brand); opacity: 1;
}
.set2-col .set2-wh-save.is-dirty:hover { background: #4d7fff; border-color: #4d7fff; color: #fff; }
.set2-wh-help.is-on { color: var(--ink-primary); }
.set2-wh-note .set2-lnk {
  color: var(--ink-quiet);
  text-decoration: underline; text-underline-offset: 0.125rem;
  text-decoration-color: var(--ink-rule);
}
.set2-wh-note .set2-lnk:hover { color: var(--ink-err); text-decoration-color: currentColor; }

/* ══════════════════════════════════════════════════════════════════════════
   PRESS FEEDBACK

   knowledge.css owns the press, and console.html loads it: `--t-press` (90ms,
   deliberately below the scale's smallest step) with
   `.btn:active { transform: scale(0.97) }` already reaches every button on
   this page.

   Two selectors used to stand below pressing those same buttons 1px DOWN
   instead - a second press system on one control, and the more specific of the
   two, so it would have won. It never ran. The list ended in a comma, so the
   parser read the @media that follows as its third selector, found it invalid,
   and discarded everything up to the closing brace: the press never existed,
   and neither did the guard it swallowed.

   The guard is kept and now parses. It is redundant with the global
   reduced-motion reset in aimy-ds.css and harmless - it states the intent for
   these two controls where a reader of this file will look for it.
   ══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .set2-map-row .set2-map-plus, .set2-map-row .set2-x { transition-duration: 0.01ms !important; }
}

/* ── WHERE THE QUICK ACTION LANDED ──
   A fix from the rail navigates and then has to say WHICH row it meant. The
   mark is a state of the arrival, not of the row: it fades on its own and does
   not survive the next thing you do, because "this is the one" stops being
   true the moment you have seen it.

   A ring rather than a fill. The rows it lands on already carry their own
   colour — a broken mapping is red, a failed run is red — and filling them
   again would say the same thing twice while hiding the state underneath. */
.is-found {
  animation: setFound 2.4s var(--ease-out) forwards;
  border-radius: var(--r-sm);
}
@keyframes setFound {
  0%   { box-shadow: 0 0 0 0.1875rem color-mix(in srgb, var(--brand) 55%, transparent); }
  70%  { box-shadow: 0 0 0 0.1875rem color-mix(in srgb, var(--brand) 55%, transparent); }
  100% { box-shadow: 0 0 0 0.1875rem transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .is-found { animation: none; box-shadow: 0 0 0 0.1875rem color-mix(in srgb, var(--brand) 55%, transparent); }
}

/* ── A ONE-SECTION PAGE HAS ALREADY BEEN NAMED ──
   See pageBody. The head's RULE stays, and so does whatever sits on its right
   — the connector picker and the tallies are not the title and are not
   repetition. Only the words that the page title just said come off. */
.set2-solo .set2-sec:first-child > .set2-sec-h > .set2-sec-t { display: none; }
.set2-solo .set2-sec:first-child { margin-top: 1.25rem; }
/* With the title gone the row is whatever the right-hand side needs, and on a
   page carrying nothing there it is a hairline under the header — which is the
   separator the title used to sit on anyway. */
.set2-solo .set2-sec:first-child > .set2-sec-h { min-height: 1.5rem; align-items: center; }


/* ══════════════════════════════════════════════════════════════════════════
   DATA RELEVANCE RANGE

   Almost nothing of its own. The section is `secRetention`'s card and rows
   with a different control in the slot, which is the point: two sections
   asking "how much history" answer in one shape, so a reader learns the row
   once. `.set2-ret-card`, `.set2-ret-row`, `.set2-ret-n`, `.set2-ret-says` and
   `.set2-ret-c` all do their usual jobs here.

   What is left is the one difference — no trash column, so the grid is three
   tracks rather than four.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-win-row { grid-template-columns: minmax(7rem, auto) minmax(0, 1fr) auto; }
/* The range is chosen from a list, so the slot holds a select where Retention
   holds a number input. Same width, so the two stacks line up down the page. */
.set2-win-row .set2-fld-sel { width: 5.25rem; }

/* ── The caveat, as a caveat ──
   "Reading only. Nothing is removed here" was the row's DESCRIPTION, sitting
   where a control explains itself — so the one control on the section was
   introduced by a sentence about what it does not do. It is a note about the
   whole section and it reads as one now, under the card rather than inside it. */
.set2-fine {
  margin: 0.75rem 0 0; max-width: 46rem;
  font-size: var(--ty-micro); color: var(--ink-quiet); line-height: 1.5;
}
.set2-fine b { color: var(--ink-quiet); font-weight: var(--fw-semibold); }

@media (max-width: 46rem) {
  /* Same fold as the retention rows: the consequence drops to its own line. */
  .set2-win-row { grid-template-columns: 1fr auto; }
  .set2-win-row .set2-ret-says { grid-column: 1 / -1; order: 3; }
}



/* ══════════════════════════════════════════════════════════════════════════
   A MASKED TOKEN KEEPS ITS SIZE

   The auth token is a password field, so what you see is the browser's mask
   glyph set in whatever face the input carries — and that is the one thing the
   move off JetBrains Mono broke rather than improved.

   Measured: ten mask bullets run 90px in JetBrains Mono at 15px and 28px in
   Urbanist at the same size. Urbanist draws a small bullet on a tight advance,
   which is correct for a bullet in running prose and wrong for a row of them
   standing in for a 28-character secret: the field went from reading as a
   redacted value to reading as a dotted line.

   So the mask is sized deliberately rather than inheriting. The bullet comes
   up so it is a mark you can count, and letter-spacing gives it back the
   advance the monospace cell used to provide. `[type="password"]` scopes it to
   the masked state — press the eye and the field is ordinary text at the
   ordinary size, which is when you actually want to read the characters.
   ══════════════════════════════════════════════════════════════════════════ */
input.set2-fld[type="password"] {
  font-size: 1.375rem;
  letter-spacing: 0.3125rem;
  /* The type is 22px in a field that has to stay the height of the URL field
     stacked above it, so the box is pinned rather than left to the font's own
     leading — 22px of Urbanist leading pushed the row to 42px against the
     URL's 36. The bullets sit mid-x-height, so nothing clips. */
  line-height: 1;
  height: 2.125rem;
  padding-top: 0; padding-bottom: 0;
}


/* ══════════════════════════════════════════════════════════════════════════
   THE DAY PICKER

   `.v2-dropdown` from the design system, unmodified in behaviour — this is
   only the sizing that puts it in a settings row, plus one state it should not
   express here.

   `.active-filter` is the library marking "not the default", where the default
   is the FIRST option. That is right for a filter chip, whose first option is
   "All". These options are a scale of days: 7 is not a default and 90 is not a
   deviation from it, so every row but one would have worn the accent border
   for no reason a reader could name.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── SIZED FOR A ROW, NOT FOR THE CHROME ──
   The library sets the trigger at 11px, which is right where it lives: a
   filter chip in the workbench's top bar, among other 11px chrome. In a
   settings row it sits beside a 15px name and a 14px unit and reads as the
   smallest thing on the line — which is the defect this file already recorded
   against `.btn-sm`, whose 10.5px made "Run sync" and "Delete" the quietest
   text on the surface. Same fix, same reasoning: the control a row exists for
   is not where the type scale bottoms out. */
.set2-dd .v2-dropdown-btn {
  min-width: 4.75rem; justify-content: space-between;
  font-size: var(--ty-micro); font-weight: var(--fw-semibold);
  padding: 0.375rem 0.5rem 0.375rem 0.6875rem;
  min-height: 2.125rem;
  color: var(--ink-primary);
}
.set2-dd .v2-dropdown-btn svg { width: 0.6875rem; height: 0.6875rem; }
.set2-dd .dd-label-text { font-variant-numeric: tabular-nums; max-width: none; }
.set2-dd .v2-dropdown-option { font-size: var(--ty-micro); padding: 0.4375rem 0.625rem; }
.set2-dd .v2-dropdown-btn.active-filter {
  border-color: rgba(255,255,255,0.12);
  background: rgba(255,255,255,0.04);
  color: inherit;
}
:root[data-theme="light"] .set2-dd .v2-dropdown-btn.active-filter {
  border-color: rgba(16,24,40,0.14);
  background: rgba(16,24,40,0.03);
}
/* The panel is a column of two-digit numbers, so it needs none of the width a
   label list does — and it is right-aligned under a right-aligned control. */
.set2-dd .v2-dropdown-panel { min-width: 100%; left: auto; right: 0; }
.set2-dd .v2-dropdown-option { font-variant-numeric: tabular-nums; }


/* The flipped panel. `bottom` replaces `top` rather than being added to it, or
   the library's `top: calc(100% + 4px)` would keep it pinned below and stretch
   the box between the two edges. See placeDD. */
.set2-dd.is-up .v2-dropdown-panel {
  top: auto;
  bottom: calc(100% + 0.25rem);
}


/* The panel's foot: what to do next on the left, the way out on the right.
   `.cal-hint` brought its own top rule from the corpus calendar, so the row
   sits on it rather than adding a second. */
.set2-cal .cal-foot {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem;
  margin-top: 0.5rem; padding-top: 0.5rem;
  border-top: 0.0625rem solid var(--hairline);
}
.set2-cal .cal-hint { margin: 0; padding: 0; border-top: 0; }
.set2-cal .cal-foot .set2-lnk { font-size: var(--ty-micro); }

/* ══ SCOPES — THE MAP ═════════════════════════════════════════════════════
   DOM nodes over one SVG wire layer, not a drawing. The boxes are real
   buttons, so they focus, tab, take a hit area and read their type out to a
   screen reader; the SVG draws the elbows and nothing else. A canvas that is
   one <svg> is a picture of a hierarchy — this one is the hierarchy.

   Panning is SCROLLING. The container scrolls both ways, which is free, native
   and keyboard-reachable, and it is what "drag to pan" reimplements badly.
   ══════════════════════════════════════════════════════════════════════════ */
.set2-sc {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 16rem;
  gap: 1rem;
  margin-top: 0.875rem;
  align-items: start;
}
/* ══ THE SEARCH TAKES THE ROOM THE CONTROLS LEAVE ════════════════════════
   `.set2-fld` is `width: 100%`, which as a flex item means a basis of `auto`
   resolving to the whole bar — so the field asked for everything, the depth
   picker and Fit asked for 238px beside it, and the field lost: measured at
   1536, 900, 500 and 390, it rendered ZERO PIXELS WIDE at every one. What was
   on screen was an unlabelled dark sliver, which is not a search box that is
   too small, it is a search box nobody can find.

   `flex: 1 1 10rem` replaces the 100% basis with a real one and lets it take
   what the cluster leaves, and `min-width: 0` lets it shrink to that basis
   rather than to its intrinsic size. `max-width` keeps it from running the
   whole width of a 1088px bar, where a search the length of the page is a
   field pretending the tenancy is bigger than it is. */
.set2-sc-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; }
.set2-sc-q { flex: 1 1 10rem; min-width: 0; max-width: 20rem; }
.set2-sc-bar-end { margin-left: auto; display: flex; align-items: center; gap: 0.5rem; }

/* ══ AND BELOW 560 THEY TAKE A LINE EACH ═════════════════════════════════
   Measured: the cluster is 238px and the field's basis is 160, so the two want
   406px of bar before the gap. The bar is the content column, which is the
   viewport less the page's own 64px of padding and whatever the rail is
   holding — at a 560 viewport it is 491 and they fit; at 480 it is 411 and
   they do not, and the field is squeezed under its own basis.

   Wrapped, the field takes the first line whole and the controls the second.
   `margin-left: auto` goes with it: an auto margin applies per flex LINE, so
   left alone it would push the pair to the right end of a line they are alone
   on — the defect this file has now fixed three times. At the left they share
   an edge with the title, the canvas and the panel beneath. */
@media (max-width: 559.98px) {
  .set2-sc-q { flex-basis: 100%; max-width: none; }
  .set2-sc-bar-end { margin-left: 0; }
}

/* ── A BUTTON BESIDE A DROPDOWN TAKES THE DROPDOWN'S BOX ──
   `.btn-sm` is 27px at an 8px radius; `.v2-dropdown-btn` is 34px at 6px. Side
   by side in a toolbar that is a 7px step and two different corners on two
   controls doing the same kind of job, which reads as one of them being an
   afterthought. The dropdown is the design system's own select and cannot
   move, so the button matches it. */
.set2-fbar > .btn,
.set2-sc-bar-end > .btn,
.set2-tabs-end > .btn { min-height: 2.125rem; border-radius: var(--r-sm); }

.set2-sc-canvas {
  overflow: auto;
  background: var(--card-bg);
  border-radius: var(--r-lg);
  padding: 0.5rem;
  max-height: 34rem;
  scrollbar-width: thin;
}
/* Fit scales the whole layer rather than re-laying it out, so the geometry is
   computed once and zoom cannot change what is drawn — only how big it is.
   `min()` with 1 stops a small tenancy being blown up past actual size, which
   would make four boxes look like a diagram of something important.

   ── THE SCALE HAS TO REACH THE BOX, NOT ONLY THE PAINT ──
   `transform` scales what is DRAWN and leaves the layout box alone, so fitting
   a six-level tenancy drew it at 68% and still reserved the full height: 174px
   of dead scroll under a canvas whose whole promise is that everything is on
   screen. One factor, `--sc-k`, now drives the transform AND the reserved
   width and height, so the space kept is the space used.

   The wires take their size from the same two numbers rather than from 100%
   of a parent that is now shorter than its own viewBox — a percentage there
   scaled the elbows a second time and walked them off the nodes. */
.set2-sc-canvas { container-type: inline-size; --sc-k: 1; }
/* ══ FIT FITS THE WIDTH. THE HEIGHT STILL HAS TO BE REACHABLE ═════════════
   `--sc-k` divides the container's WIDTH by the map's, so a wide-and-short map
   is fitted and a TALL one is not: the box keeps its full height, `max-height:
   34rem` above cuts it off, and `overflow: hidden` meant the rest could not be
   scrolled to. Measured on the shipped tree, which is 658 units tall:

       1536   box 658, canvas 544 — 130px unreachable
        900   box 628, canvas 544 — 100px unreachable

   So this is not a narrow-screen defect; the bottom of the scopes map was
   missing at the reference width too, which is where it was noticed. Fitted on
   one axis and clipped on the other is the worst of both.

   `overflow-y` becomes `auto`, so the axis that was never fitted scrolls like
   any other overflow. Where the map is shorter than 34rem — every collapsed
   depth — there is nothing to scroll and no scrollbar appears.

   ══ AND THE SCALE HAS A FLOOR, WHICH IS WHAT MAKES IT PAN ════════════════
   Unfloored, `min()` shrinks the map to whatever the column is: measured at a
   390 viewport it resolved to **k = 0.33**, a 217px-tall thumbnail of a
   six-level tree with every label a third of its size. Nothing scrolled there,
   and correctly — the whole map fitted. That is the defect: there was nothing
   to scroll BECAUSE there was nothing readable left.

   A 770px tree does not go into a 271px column. So below the floor it stops
   shrinking and starts PANNING, which is the only honest way to read a map
   that large on a phone, and `overflow` goes to `auto` on both axes because a
   floored scale is exactly the case where the width is no longer fitted.

   0.85 is the number this ecosystem already uses for the point where type
   rather than layout is what breaks — AiMY Sales states it at UI_MIN_SCALE.
   Above it nothing changes: `max()` is inert wherever the fit already clears
   the floor, which is every width from about 700 up, the 1536 reference
   included, where k stays exactly 1. */
.set2-sc-canvas.is-fit {
  overflow: auto;
  --sc-k: max(0.85, min(1, calc((100cqw - 1rem) / (var(--sc-w) * 0.0625rem))));
}
/* Two elements because one cannot do both jobs: a transform scales what is
   PAINTED and leaves the layout box alone, so putting the scale and the
   reduced size on the same element scaled it twice — the box came out at
   `k²` and the wires drew past their own container. The outer box reserves
   `size × k` and the inner layer stays at full size and is painted down into
   it. At `--sc-k: 1` the two are identical and the transform is a no-op. */
.set2-sc-box {
  /* 0.0625rem is one pixel at the root this geometry was drawn against, so
     --sc-w and --sc-h stay the plain numbers scLayout produces while the box
     they describe scales with the page. See SC_U in settings.js. */
  width: calc(var(--sc-w) * 0.0625rem * var(--sc-k));
  height: calc(var(--sc-h) * 0.0625rem * var(--sc-k));
}
.set2-sc-layer {
  position: relative;
  width: calc(var(--sc-w) * 0.0625rem);
  height: calc(var(--sc-h) * 0.0625rem);
  transform: scale(var(--sc-k));
  transform-origin: 0 0;
}

/* ══ THE TREE DRAWS ITSELF ═════════════════════════════════
   Arriving, and whenever a branch opens, the wires trace themselves outward
   and the nodes land along them. Every elbow carries `pathLength="1"`, so a
   dash array of 1 is its whole length whatever its real geometry - no
   measuring, no JavaScript, and the far branches take exactly as long as the
   near ones, which is what makes it read as growth.

   `stroke-dashoffset` IS a paint property, and this is the one place in the
   build that gets to use it. There is no compositor equivalent for drawing a
   stroke, it runs once per shape change rather than continuously, and it is
   bounded by the sibling cap. seatMap keeps it off every paint where the tree
   did not actually change - which is all of them, while somebody is typing.
═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: no-preference) {
  /* THE RESTING STATE IS THE DRAWN STATE, and that ordering is the whole
     safety property. Declaring `stroke-dashoffset: 1` on the rule and
     animating TO zero means a wire is hidden until its animation completes -
     so a clock that never starts, or freezes mid-delay, leaves the tree
     invisible for as long as the page is open. A backgrounded tab does
     exactly that, and this repo's review pane never starts one at all.

     Offset zero is the default, so a path here is fully drawn unless an
     animation is actively taking it apart. `backwards` rather than `forwards`
     for the same reason: it hides the wire only while its own animation is
     genuinely pending, and an animation that is cancelled or never begins
     leaves the element at its own values. seatMap then strips the class on a
     timer, so even a frozen clock resolves to a drawn tree. */
  .set2-sc-wires.is-drawing path {
    stroke-dasharray: 1;
    animation: set2-wire-draw var(--t-slow) var(--ease-out) backwards;
    animation-delay: calc(min(var(--i, 0), 12) * var(--t-stagger));
  }
  @keyframes set2-wire-draw {
    from { stroke-dashoffset: 1; }
    to   { stroke-dashoffset: 0; }
  }

  /* A node that was not on the map a moment ago, and the whole map on the
     paint it arrives. Scale and opacity, from just under full size - the node
     resolves into place rather than being dropped onto it. */
  /* `backwards`, never `both` - see the wires above. A node waiting its turn
     is hidden; a node whose animation was cancelled or never ran is not. */
  .set2-sc-w.is-new,
  .set2-sc-layer.is-arriving .set2-sc-w {
    animation: set2-node-in var(--t-base) var(--ease-out) backwards;
    animation-delay: calc(min(var(--i, 0), 8) * var(--t-stagger));
  }
  @keyframes set2-node-in {
    from { opacity: 0; transform: scale(0.94); }
  }
}
.set2-sc-wires {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  color: var(--ink-rule); pointer-events: none;
}

.set2-sc-w { position: absolute; }
.set2-sc-n {
  display: flex; align-items: center; width: 100%; height: 100%;
  appearance: none; cursor: pointer; font: inherit; text-align: left;
  padding: 0 0.5rem;
  border: 0.0625rem solid var(--card-border); border-radius: var(--r-sm);
  background: var(--card-bg-raised); color: var(--ink-secondary);
}
.set2-sc-n:hover { border-color: var(--ink-rule); color: var(--ink-primary); }
.set2-sc-n:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }
.set2-sc-nn {
  font-size: var(--ty-micro); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* `.set2-sc-roll` is the "+N" pill, absolutely positioned over the node's right
   edge. At the wide geometry a 148-unit node has room to spare under it; at the
   tight one it is 104 and the name ran straight beneath the pill — measured on
   Scopes at 390, "Knowledge Ops" and "Support Enablement" both. The name stops
   short of it instead. */
/* `.set2-sc-w` is the wrapper and `.set2-sc-roll` is its child, beside the
   `.set2-sc-n` button rather than inside it — so a `:has()` hung on the button
   matches nothing, which is why the first attempt at this changed no pixels. */
@media (max-width: 735.98px) {
  .set2-sc-w:has(.set2-sc-roll) .set2-sc-nn { padding-right: 1.25rem; }
}
.set2-sc-n.is-on {
  border-color: var(--brand); color: var(--ink-primary);
  box-shadow: inset 0 0 0 0.0625rem var(--brand);
}
/* Dashed is the tenancy STOPPING, not a branch being closed. A closed branch
   carries a count you can press; this carries nothing, because there is
   nothing under it. */
.set2-sc-n.is-empty {
  border-style: dashed; border-color: var(--ink-rule);
  background: none; color: var(--ink-quiet);
}
/* Ours. Pinned last in the fixture and never scoped away, so it is marked
   rather than left to be inferred from its position. */
.set2-sc-n.is-aimy { border-color: rgba(var(--accent-rgb), 0.4); background: var(--accent-dim); }
/* Search dims rather than hides: a match with its containment removed tells
   you nothing about which Tier 1 you found. 0.28 before, which was a node you
   could see the shape of and not read — the map went illegible everywhere
   except the hits. It steps back rather than disappearing now: still clearly
   not what you searched for, still a name you can read to decide the search
   was wrong. */
.set2-sc-n.is-dim { opacity: 0.62; }
.set2-sc-n.is-more,
.set2-sc-n.is-more:hover {
  justify-content: center;
  border-style: dashed; background: none;
  color: var(--ink-action); font-size: var(--ty-micro);
}

/* The rolled-up count, sitting on the node's edge where the branch would
   continue — so what it opens is where it points. */
.set2-sc-roll {
  position: absolute; right: -0.5rem; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; gap: 0.125rem;
  min-width: 1.25rem; height: 1.25rem; padding: 0 0.25rem;
  appearance: none; cursor: pointer; font: inherit;
  font-size: var(--ty-meta); font-variant-numeric: tabular-nums;
  border: 0.0625rem solid var(--card-border); border-radius: var(--r-pill);
  background: var(--card-bg); color: var(--ink-quiet);
}
.set2-sc-roll:hover { border-color: var(--brand); color: var(--ink-action); }
.set2-sc-roll:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; }
.set2-sc-roll .set2-exp { width: 0.5rem; height: 0.5rem; }
.set2-sc-roll.is-open .set2-exp { transform: rotate(180deg); }

/* ── The inspector ──
   Counts and one way in per count. It does not list the people: People
   answers that properly, split by whether a grant can be revoked where you
   are standing, and two surfaces answering it differently is how they start
   disagreeing. */
.set2-sc-insp {
  background: var(--card-bg); border-radius: var(--r-lg);
  padding: 0.875rem 0.875rem 0.75rem;
  position: sticky; top: 1rem;
}
.set2-sc-path {
  display: flex; align-items: center; gap: 0.25rem; flex-wrap: wrap;
  margin: 0 0 0.375rem; font-size: var(--ty-micro); color: var(--ink-quiet);
}
.set2-sc-crumb {
  appearance: none; border: 0; background: none; cursor: pointer; padding: 0;
  font: inherit; font-size: var(--ty-micro); color: var(--ink-quiet);
}
.set2-sc-crumb:hover { color: var(--ink-primary); text-decoration: underline; }
.set2-sc-t {
  font-family: var(--font-display); font-size: var(--ty-body);
  font-weight: var(--fw-bold); color: var(--ink-primary); margin: 0;
}
.set2-sc-type {
  margin: 0.125rem 0 0; font-size: var(--ty-micro);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--ink-quiet);
}
.set2-sc-sub { margin: 0.5rem 0 0; font-size: var(--ty-micro); color: var(--ink-quiet); }
.set2-sc-sub.is-empty { color: var(--ink-warn); }

.set2-sc-facts { margin-top: 0.875rem; display: flex; flex-direction: column; }
.set2-sc-fact {
  display: flex; align-items: center; gap: 0.5rem; width: 100%;
  appearance: none; border: 0; background: none; cursor: pointer;
  text-align: left; font: inherit;
  min-height: 2rem; padding: 0.25rem 0.375rem;
  border-radius: var(--r-sm); color: var(--ink-secondary);
  border-top: 0.0625rem solid var(--hairline);
}
.set2-sc-fact:first-child { border-top: 0; }
.set2-sc-fact:hover { background: var(--card-bg-raised); }
.set2-sc-fact:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; }
.set2-sc-fact.is-flat { cursor: default; }
.set2-sc-fact.is-flat:hover { background: none; }
.set2-sc-fn { flex: 1 1 auto; font-size: var(--ty-micro); }
.set2-sc-fv { font-size: var(--ty-meta); font-weight: var(--fw-semibold); color: var(--ink-primary); }
.set2-sc-fv.is-zero { color: var(--ink-quiet); font-weight: var(--fw-medium); }
.set2-sc-fact .set2-chev { width: 0.75rem; height: 0.75rem; }
.set2-sc-fact.is-flat .set2-chev { display: none; }

.set2-sc-inh { margin-top: 0.875rem; padding-top: 0.625rem; border-top: 0.0625rem solid var(--hairline); }
.set2-sc-ih {
  font-size: var(--ty-micro); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--ink-quiet);
  margin-bottom: 0.375rem;
}
.set2-sc-ir { display: flex; align-items: baseline; gap: 0.375rem; padding: 0.1875rem 0; }
.set2-sc-in { font-size: var(--ty-micro); color: var(--ink-secondary); }
/* `--ink-faint`, not `--ink-rule`. The rule value is for LINES; used as text
   it measures about 4:1 and this file has already been caught putting a
   sentence there once. It is the quietest thing on the panel and it still has
   to be readable — it is where "nothing here edits the hierarchy" is said. */
.set2-sc-ft {
  margin: 0.875rem 0 0; padding-top: 0.625rem;
  border-top: 0.0625rem solid var(--hairline);
  font-size: var(--ty-micro); color: var(--ink-quiet); line-height: var(--lh-snug);
}

/* Under the point where a 16rem panel beside a canvas leaves neither enough
   room, the inspector goes below and the canvas keeps the width — the map is
   the thing you came for. */
@media (max-width: 62rem) {
  .set2-sc { grid-template-columns: minmax(0, 1fr); }
  .set2-sc-insp { position: static; }
}


/* ══════════════════════════════════════════════════════════════════════════
   OWNERSHIP — WHOSE SKILL IT IS, AND WHAT THAT DECIDES

   A skill belongs to the organization or to you, and two answering to one
   name is the whole of precedence. So ownership is not a badge dropped beside
   a name: it heads the two halves of the list, it sits against the title, and
   it labels each side of the resolution.
   ══════════════════════════════════════════════════════════════════════════ */

/* The tag. Two owners, two readings, and neither of them is a status — so it
   borrows nothing from the pills, which mean "is this working". Uppercase and
   small, the same register the card keys use, because it names a category. */
.set2-own {
  display: inline-flex; align-items: center; flex-shrink: 0;
  height: 1.125rem; padding: 0 0.375rem;
  border-radius: var(--r-xs);
  font-size: var(--ty-micro); font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
  white-space: nowrap;
}
/* The same pairing `.set2-chip.is-role` already uses — brand tint under
   primary ink — rather than `--ink-action`, which is purple and was sitting on
   a blue fill. */
.set2-own.is-org { background: var(--brand-dim); color: var(--ink-primary); }
/* Yours is neutral, and in the light theme `--card-bg-raised` is within a
   couple of values of a white card: the fill alone left no tag at all on the
   precedence cards. The hairline is what makes it a tag on any ground. */
.set2-own.is-you {
  background: var(--card-bg-raised); color: var(--ink-quiet);
  box-shadow: inset 0 0 0 0.0625rem var(--hairline);
}

/* The address, wherever it is printed. Mono because it is a token you type,
   not a phrase you read — the same reason the frontmatter is mono. */
.set2-slug {
  font-family: var(--font-mono); font-size: var(--ty-meta);
  color: var(--ink-quiet); word-break: break-all;
}

/* ── The list's two halves ──
   TWO GROUPS UNDER ONE HEADER ROW WAS THE FIRST ANSWER, AND IT DID NOT SCALE.
   Both halves visible at once put the org skill and the personal one it
   overrides three rows apart, which was the argument for it — but an
   organization ships tens of skills and a person writes two, so reaching your
   own meant scrolling past every one of theirs. That is the O(records) rule
   this project has held on every other page.

   Tabs instead. Yours is one press away whatever the org's count is, the
   filters and the sort belong to the half you are in, and the collision stays
   legible where it actually matters: the row keeps its Overridden pill, and
   the skill's own page names the version that beat it with a way to open it.

   They reuse the document's tab rules — same component, and the two are never
   on screen together. */
.set2-tab-n {
  margin-left: 0.375rem;
  min-width: 1.125rem; height: 1.125rem; padding: 0 0.3125rem;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-pill); background: var(--card-bg-raised);
  font-size: var(--ty-meta); font-weight: var(--fw-semibold); color: var(--ink-quiet);
}
.set2-tab.is-on .set2-tab-n { background: var(--brand-dim); color: var(--ink-primary); }

/* The tab and its help glyph travel together, and the underline belongs to the
   BUTTON so the glyph sits outside it — pressing the tab and asking what the
   tab means are two acts, and only one of them changes the list. */
.set2-tab-w { display: inline-flex; align-items: center; }

/* NO RULE UNDER THE OWNER TABS.
   `.set2-tabs` carries a full-width hairline because on the DOCUMENT it is the
   only thing separating the tabs from the panel they switch. On the list it
   was the second of three stacked horizontal lines in 100px — tab rule, then
   the filter row, then the table's own top border — and the one carrying the
   least meaning. The active tab's own underline stays: that is the selection,
   not a separator. */
.set2-tabs.is-bare { border-bottom: 0; }
.set2-tabs.is-bare .set2-tab { margin-bottom: 0; }
.set2-tabs.is-bare .set2-tabs-end { padding-bottom: 0; }

/* Make a skill, or fetch a template to make one from. Neither narrows the set,
   so neither belongs in the row of things that do. */
.set2-tabs-end {
  margin-left: auto; display: flex; align-items: center; gap: 0.375rem;
  padding-bottom: 0.375rem;
}

/* `.set2-own-note` stood here — a sentence under the tab row saying who
   overrides whom. True, and not news after the first read. It is the tab's
   tooltip now.

   It was also, accidentally, the gap: the filter row sat under the note, and
   removing the note left the dropdowns flush against the tab row's underline,
   touching the boundary they are supposed to sit below. The space has to be
   declared rather than inherited from a paragraph that is no longer there. */
.set2-tabs + .set2-fbar { margin-top: 1.125rem; }

/* ── The document's title row ──
   Owner beside the name, not under it. Under it is the byline, which is who
   wrote the thing; beside it is whose it is, and only the second one decides
   whether the page describes something that runs. */
.set2-doc-tr { display: flex; align-items: center; gap: 0.5rem; min-width: 0; flex-wrap: wrap; }

/* The name, edited in place. It has to be the SAME text at the same size in
   the same face as the heading it replaces — a rename that changes how big the
   title is reads as the page jumping rather than as a field opening. So it
   takes the display face and `--ty-title` from `.set2-doc-t`, and only the box
   around it says it is now editable. */
.set2-doc-ti {
  min-width: 12rem; max-width: 100%; flex: 1 1 20rem;
  margin: -0.25rem -0.5rem; padding: 0.25rem 0.5rem;
  appearance: none;
  border: 0.0625rem solid var(--brand); border-radius: var(--r-sm);
  background: var(--card-bg);
  font-family: var(--font-display); font-size: var(--ty-title);
  font-weight: var(--fw-bold); letter-spacing: var(--ls-tight);
  color: var(--ink-primary);
}
.set2-doc-ti:focus,
.set2-doc-ti:focus-visible { outline: none; box-shadow: 0 0 0 0.1875rem var(--brand-dim); }

/* ── The editable cards ──
   Label left, action right, on the card's own head. `Edit` used to sit at the
   end of the chip flow wearing a class that had no CSS anywhere in the build,
   so it rendered as the word Edit in body text, in line with the chips it
   edits — indistinguishable from one of them. */
.set2-ov-h {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  min-height: 1.5rem;
}
.set2-ov-h .btn { flex-shrink: 0; }
.set2-ov-s {
  margin: 0.1875rem 0 0;
  font-size: var(--ty-micro); color: var(--ink-quiet); line-height: var(--lh-snug);
}

/* ── Editing the file ──
   The textarea IS the card: same fill, same radius, same padding as the
   rendered body it replaces, so pressing Edit changes what you can do to the
   text and not where the text is. Mono at the source's size, because what you
   are editing is the file and half of it is YAML. */
.set2-file.is-edit { display: flex; flex-direction: column; gap: 0.625rem; }
.set2-ed {
  width: 100%; box-sizing: border-box;
  min-height: 22rem; resize: vertical;
  padding: 1.25rem 1.5rem;
  border: 0; border-radius: var(--r-lg);
  background: var(--card-bg); color: var(--ink-secondary);
  font-family: var(--font-mono); font-size: var(--ty-micro); line-height: 1.7;
  tab-size: 2;
  scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
}
.set2-ed:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: -0.125rem; }
.set2-ed::-webkit-scrollbar { width: 0.3125rem; height: 0.3125rem; }
.set2-ed::-webkit-scrollbar-track { background: transparent; }
.set2-ed::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.16); border-radius: var(--r-pill); }
.set2-ed::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.24); }
.set2-ed-bar { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.set2-ed-h {
  flex: 1 1 16rem; min-width: 0;
  font-size: var(--ty-micro); color: var(--ink-quiet); line-height: var(--lh-snug);
}

/* THE CORNER STOPPED WORKING WHEN A SECOND CONTROL ARRIVED.
   The view toggle sat absolutely in the card's top-right, over the padding,
   which held while it was one small pill. Add `Edit` beside it and the pair
   is wide enough to land on the first line of the instruction — the word
   `Edit` printed straight through the prose, in both themes, at every width.

   So they come out of the corner and become a bar above the card, right
   aligned and tight to it. Nothing overlaps, the buttons keep the same
   grouping, and the text starts where the card starts. */
.set2-file-bar {
  display: flex; align-items: center; justify-content: flex-end; gap: 0.375rem;
  margin-bottom: 0.5rem;
}

/* ── Precedence ──
   Two cards, stacked, the winner first. Not side by side: they are not being
   compared feature by feature, they are being RANKED, and a rank reads down. */
/* Precedence lives inside Overview now, under the description, so it opens
   with the section label the description already uses rather than with the
   page-level top margin a tab panel needed. */
.set2-prec { margin-top: 1.75rem; display: flex; flex-direction: column; gap: 0.625rem; }
.set2-prec > .set2-lbl { margin-bottom: 0; }
.set2-prec > .set2-note { margin: 0; }
.set2-pr-c {
  background: var(--card-bg); border-radius: var(--r-lg);
  padding: 0.875rem 1rem;
}
/* No edge on the winner. It carried an inset brand stripe, which put a third
   marker on a card that already says `Applies` in a pill and holds the only
   un-muted body text of the two. The pill is the statement; the ink weight is
   the reinforcement; a coloured rail was decoration reading as a state. */
.set2-pr-h { display: flex; align-items: center; gap: 0.625rem; flex-wrap: wrap; }
.set2-pr-p { margin-left: auto; }
.set2-pr-b {
  margin: 0.5rem 0 0;
  font-size: var(--ty-meta); color: var(--ink-secondary); line-height: var(--lh-base);
}
.set2-pr-w {
  margin: 0.4375rem 0 0;
  font-size: var(--ty-micro); color: var(--ink-quiet);
}
/* The container is now just a stack: the insight inside it carries the fill,
   and a card holding a card is two borders saying one thing. `.set2-pr-none`
   went with it — the line under an uncontested skill saying its name is
   unshared, which is the absence of a finding rather than one. */
/* Just a stack around the insight now. `.set2-pr-row` and `.set2-pr-hint` went
   with the second rename field: the address follows the skill's name, so there
   is one editor for it and it is at the top of the page. */
.set2-pr-act { display: flex; flex-direction: column; gap: 0.625rem; }

/* ── AN AIMY INSIGHT, IN SETTINGS ──
   A name collision is something AiMY NOTICED — a fact about two objects that
   neither object states on its own — so it is said in AiMY's voice and wears
   AiMY's treatment. The values are lifted from `.tc-foot .tc-ins` in
   knowledge.css deliberately, not approximated: the band on the results page,
   the note on a type card, the line in the rail and this have to read as one
   voice in four places. */
.set2-ins {
  display: flex; align-items: flex-start; flex-wrap: wrap; gap: 0.5rem;
  margin: 0; padding: 0.625rem 0.75rem;
  border-radius: var(--r-md);
  background: linear-gradient(120deg, rgba(var(--accent-rgb), 0.10), rgba(0, 102, 255, 0.04));
  border: 0.0625rem solid rgba(var(--accent-rgb), 0.18);
  font-size: var(--ty-meta); line-height: var(--lh-snug); color: var(--ink-secondary);
}
.set2-ins svg { flex-shrink: 0; margin-top: 0.125rem; }
.set2-ins-t { flex: 1 1 14rem; min-width: 0; }
/* A text action, not a filled bar — the same call `.ins-act` makes. The insight
   is a reading, and a primary button on it would make it a form. */
.set2-ins-a {
  flex-shrink: 0; appearance: none; border: 0; background: none; cursor: pointer;
  padding: 0; font: inherit; font-size: var(--ty-meta); font-weight: var(--fw-semibold);
  color: var(--ink-action);
}
.set2-ins-a:hover { opacity: 0.75; }
.set2-ins-a:focus-visible { outline: 0.125rem solid var(--brand); outline-offset: 0.125rem; border-radius: var(--r-xs); }
@media (max-width: 52rem) {
  .set2-pr-p { margin-left: 0; }
}
