/* styles/admin_sections.css — Administration additions: section rail, bands, switches. Order in VulnerabilityCore.html is the cascade order. */

/* ===================================================== Administration additions
   New classes introduced by the Administration pass. Appended rather than filed
   beside their neighbours so that where two rules tie on specificity, the newer
   intent wins — every one of these was written against the rules above it. */

/* ---- Admin page: the section rail ------------------------------------------ */
/* Administration is the only screen in the app that does not fit on one, and until
   now nothing told you the bands below the fold existed or got you between them.
   This is the page's contents bar.

   AN ANCHOR RAIL, NOT TABS. Tabs would unmount whichever band you are not looking
   at, and three of them hold state that would go with it - the Jira form (whose
   API token is never read back, so a half-typed one cannot be restored), the
   taxonomy row loaded into an editor, and every inline save note. Anchors leave
   the page whole, so a jump costs nothing and Ctrl-F still finds everything.

   It sticks rather than riding in .page-head-aside, where the other five screens
   park their two or three controls: an aside of buttons may scroll away, the only
   navigation on a page this tall may not.

   The bar borrows the topbar's own translucent-plus-blur recipe instead of a flat
   fill. In dark mode the canvas is not --bg but --bg under a radial accent wash
   (.app[data-theme="dark"]), so any opaque colour cuts a visible rectangle out of
   it near the top of the page - which is exactly where this bar lives. Sharing the
   recipe is also the only way the two genuinely read as one band. */
.admin-nav {
  /* The rail is the only child of .page on this screen that is CHROME rather
     than content, but it was spaced exactly like a fifth card: .page's 20px gap
     above it and the same 20px below, around a 50px bar. Measured, that is 90px
     between the description and the first card with 40px of it literally empty,
     and the rail sat equidistant from both so it read as belonging to neither.
     These two numbers pull it to ~14px under the description it indexes and
     ~16px above the band it points into, so it attaches upward to the page head.
     Done here rather than by touching .page { gap }, which is the scaffold for
     six other screens. */
  margin-top: -6px;
  margin-bottom: -4px;
  position: sticky;
  top: var(--topbar-h);
  /* Under the topbar's 15 and over the cards: a card slides beneath the rail
     rather than through it. Modal scrims are at 300, so they still cover it. */
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  background: color-mix(in oklab, var(--bg-panel) 82%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
  padding: 9px 0 10px;
}
.admin-nav-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 12px;
  border: 1px solid transparent;
  border-radius: 20px;
  background: none;
  color: var(--text-dim);
  font: 600 12.5px/1 var(--font);
  cursor: pointer;
  transition: 0.12s;
}
.admin-nav-btn .ic {
  color: var(--text-faint);
}
.admin-nav-btn:hover {
  color: var(--text);
  background: var(--surface-2);
  border-color: var(--line-2);
}
/* "You are here" is carried by the EDGE, not the fill. Measured on the real
   tokens, --accent-soft over --bg is 1.10:1 in dark and 1.25:1 in light on the
   default accent, and no better on the other two the theme panel offers; the
   --accent-line edge is 1.28:1 / 1.96:1, and --text-dim to --text is 1.21:1 in
   dark. Painted that way the selected chip simply is not there. Mixing the accent
   60% toward --text gives an edge that clears 3:1 in both themes for all three
   accents (4.8:1 at worst) and still moves when the user changes the accent, which
   a fixed --accent-2 would not. The soft fill stays, demoted to a hint. The label
   goes to --text and not --accent, which is 1.75:1 on that fill in dark. */
.admin-nav-btn.is-on {
  color: var(--text);
  background: var(--accent-soft);
  border-color: var(--focus);
}
.admin-nav-btn.is-on .ic {
  color: currentColor;
}
/* --accent-line is what the rest of the app reaches for here, and on this canvas
   it is a 1.28:1 focus ring in dark - i.e. none. Same mix as the selected edge. */
.admin-nav-btn:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
/* Anchor target. The offset is the topbar plus the rail's own box (9 + 28.5 + 10
   + 1 = 48.5), rounded up so a jumped-to band lands just clear of the rail instead
   of behind it. It lives here, beside the rail's padding, because the two numbers
   only ever move together. */
.admin-sec {
  scroll-margin-top: calc(var(--topbar-h) + 52px);
}
/* Bands sit further apart than the cards inside one: .page already contributes
   20px of gap, so this makes it 36. The eye then groups Companies with Users before
   it groups them with Jira. */
.admin-sec + .admin-sec {
  margin-top: 16px;
}
/* Wrapped onto two rows the rail would take a third of a phone screen and pin it
   there for the whole scroll. On narrow screens it stays at the top of the page
   and works as a plain index instead.
   560, not 760: measured, the four chips do not wrap until ~540px, so the old
   breakpoint surrendered stickiness across 200px of viewport where the rail still
   fitted on one row — and that is the range where it is worth most, because the
   page is ~3100px tall there. 560 is also an existing rung on this product's
   ladder rather than a fifth number. The blur and translucency go with it: they
   exist so the bar can sit over the canvas's radial wash, and a static bar
   overlaps nothing. */
@media (max-width: 560px) {
  .admin-nav {
    position: static;
    background: none;
    backdrop-filter: none;
    margin-top: 0;
    margin-bottom: 0;
  }
  .admin-sec {
    scroll-margin-top: calc(var(--topbar-h) + 12px);
  }
}

/* ---- the card head's own action ------------------------------------------
   Both roster cards now open with their list rather than with a 270px create
   form, and the form comes back from a button up here. */
.admin-head-act {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Quieter than the form's own .btn-accent on purpose — a card must not grow a
   second primary action. .btn-ghost is not available for the job: the note in
   admin.css records that it resolves to a solid --btn-secondary, near-black in
   the light theme. This is .jira-load's recipe, this file's established "small
   outlined action", minus the negative margin that tucks it into a .field-wrap.
   The open state borrows .admin-nav-btn.is-on's three declarations verbatim
   rather than re-deriving them: that rule's comment carries the measurements for
   why the EDGE and not the fill has to carry state on this canvas. */
.admin-head-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 11px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font: 600 12px/1 var(--font);
  cursor: pointer;
  transition: 0.12s;
}
.admin-head-btn:hover {
  color: var(--text);
  border-color: var(--focus);
}
.admin-head-btn.is-on {
  color: var(--text);
  background: var(--accent-soft);
  border-color: var(--focus);
}

/* ---- the roster filter bar ------------------------------------------------
   Rendered only once a list is long enough to need it. Everything it does is
   CLIENT-SIDE over the arrays the page already holds: core/api.js has no search
   endpoint and its only server-side narrowing is adminUsers(companyId), so there
   is nothing for a future "move this to the server" edit to land on without a
   new route. .toolbar and .search are the ledger's, reused verbatim — .search
   already carries the --focus ring this product documents as the fix for
   --accent being 1.5-1.8:1 on the dark canvas, so no new focus decision is made
   here. */
.admin-listbar {
  margin-top: -2px;
}
.admin-listbar .search {
  height: 36px;
  min-width: 160px;
  flex: 1 1 200px;
}
.admin-listbar .field-wrap {
  height: 36px;
}
.admin-listbar .field {
  flex: 0 1 200px;
  min-width: 0;
}
/* Same pill as the card head's action, carrying a count instead of a verb. The
   two of them are the same two numbers the stat strip reports, which is what
   lets a reader move from "3 awaiting OTP" to the three rows. */
.admin-filter {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 13px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font: 600 12px/1 var(--font);
  cursor: pointer;
  white-space: nowrap;
  transition: 0.12s;
}
.admin-filter:hover {
  color: var(--text);
  border-color: var(--focus);
}
.admin-filter.is-on {
  color: var(--text);
  background: var(--accent-soft);
  border-color: var(--focus);
}
.admin-filter-n {
  font-variant-numeric: tabular-nums;
  font-family: var(--font-mono);
  font-size: 11px;
}

/* ---- the stat strip -------------------------------------------------------
   The Platform screen's own .plat-strip, reused rather than re-invented:
   platform.css states that that screen is built from Administration's parts on
   purpose, so borrowing back is the house move. Every number in it is already in
   scope on this page — no new fetch. It also puts something real in the band
   that used to be 90px of near-empty canvas above the first card.
   The strip is not interactive: the filter pills on each card do the narrowing,
   and wrapping .plat-stat in a <button> would have to fight the UA reset for its
   own border-left separator. */
/* TWO classes, and it has to be two: platform.css loads AFTER this file, so its
   single-class `.plat-strip { margin-bottom: 18px }` ties this rule on
   specificity and wins on source order. At (0,0,2,0) this one wins outright. */
.plat-strip.admin-strip {
  /* .plat-strip ships an 18px bottom margin for its own page; here .page's gap
     owns the spacing. The negative pair is the same move the rail makes and for
     the same reason: head, strip and rail are three things that belong to the
     top of the page, and at a full 20px gap each they read as three separated
     bands instead of one header block. Measured, this brings the description to
     the first card from 172px to ~146px, of which 104px is now the strip and the
     rail themselves rather than void. */
  margin-top: -6px;
  margin-bottom: -10px;
}

/* The two list cards fall back to the shared .empty, which is sized for a whole
   page: 60px of padding and a 24px icon, landing directly under .admin-form — a
   bordered inset panel. The result is a card that reads as broken rather than as
   one with nothing in it yet.
   Scoped to .admin-card, not changed at source: .empty is the dashboard, ledger,
   browse and components empty state too, and the full-page version is right there.
   Here it takes the shape .admintbl-wrap will take once there is a row — same
   fill, same radius — with a dashed line, which says "the list goes here" without
   claiming a list is here. */
.admin-card .empty {
  padding: 26px 20px;
  gap: 8px;
  background: var(--bg-panel);
  border: 1px dashed var(--line-2);
  border-radius: var(--radius-sm);
}
.admin-card .empty p {
  font-size: 14px;
}
.admin-card .empty .empty-sub {
  margin-top: 0;
}

/* "Load projects" / "Load issue types", moved out of the action bar and onto the two
   fields they fill -- the same shape as the login card's Show/Hide inside its own
   .field-wrap. They were the only controls on this screen whose effect landed off
   screen: press a button at the bottom of a wide card and an <input> two hundred
   pixels up silently becomes a <select>.
   Outlined and transparent rather than filled, because .field-wrap changes its own
   background on :focus-within and any fill token would collide with one of the two
   states it takes. */
.jira-load {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 28px;
  margin-right: -4px;
  padding: 0 10px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font: 600 11px/1 var(--font);
  cursor: pointer;
  transition: 0.12s;
}
.jira-load:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--text);
}
.jira-load:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Disabled is the state these two spend most of their life in -- they read through
   the SAVED connection, so on a fresh tenant they are inert until the first save.
   It has to look inert, and the title attribute carries the reason. */
.jira-load:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
/* The reason these are inert has to be reachable, and a title on a DISABLED button
   is never shown: browsers suppress pointer events on disabled form controls, so the
   tooltip never fires. The wrapper carries it instead. Not interactive content, so
   the <label> still forwards a stray click to the field's own control. */
.jira-load-wrap {
  flex: none;
  display: inline-flex;
  margin-right: -4px;
}
.jira-load { margin-right: 0; }
.jira-load-spin { animation: spin 1.1s linear infinite; }

/* Two settings whose consequences are the whole point of them, drawn until now as
   native checkboxes with a 12px label in the same 16px stack as the text inputs --
   so a switch that widens who can read a comment thread carried the weight of a
   field, and its five-line warning floated after it as an unattached paragraph.
   Each becomes a row that owns its own consequence line. Same --bg-panel inset,
   --line and --radius-sm as .admin-form and .admintbl-wrap, the two blocks this page
   already stacks, so it is another one of those rather than a new surface.
   The privacy row is bordered in accent, not in a warning colour: turning it on is a
   legitimate choice, and colouring it as an error is how people learn to skip the one
   paragraph here that has to be read. */
.jira-switches {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.jira-switch {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr);
  align-items: start;
  gap: 11px;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-panel);
  transition: border-color 0.12s;
}
.jira-switch:hover { border-color: var(--line-2); }
/* The .field-wrap focus treatment, so a keyboard user gets the same accent rail here
   as on every input above. */
.jira-switch:focus-within {
  border-color: transparent;
  box-shadow: inset 3px 0 0 var(--accent), 0 0 0 1px var(--accent-line);
}
.jira-switch input {
  width: 16px;
  height: 16px;
  margin: 1px 0 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.jira-switch-txt {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
.jira-sw-lab {
  cursor: pointer;
  display: block;
}
.jira-sw-lab > b {
  font: 600 13px/1.2 var(--font);
  color: var(--text);
}
/* Every word of the help text is still here, folded away behind a native <details>
   rather than a React toggle: the open state is the browser's to keep and the
   keyboard contract comes free. The summary is deliberately quieter than the title
   it sits under — it is an offer to explain, not a second heading. The old pair of
   `.jira-switch-txt > span` rules that styled this paragraph in the flat layout is
   gone: the later, winning copy of them lives on as `.jira-why > span` below. */
.jira-why > summary {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  width: fit-content;
  padding: 2px 0;
  font: 600 10.5px/1 var(--font);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  /* --text-dim, matching .field-lab (admin.css), not --text-faint: this is the same
     600/10.5px uppercase treatment as a field label, and the faint token measures
     3.31:1 at 12px in the light theme — worse again at 10.5. */
  color: var(--text-dim);
  cursor: pointer;
  list-style: none;
}
.jira-why > summary::-webkit-details-marker { display: none; }
/* A chevron built from two borders, so it needs no icon component inside a <summary>. */
.jira-why > summary::after {
  content: "";
  width: 5px;
  height: 5px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-1px) rotate(45deg);
  transition: transform 0.12s;
}
.jira-why[open] > summary::after { transform: translateY(1px) rotate(-135deg); }
.jira-why > summary:hover { color: var(--text); }
.jira-why > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}
.jira-switch.is-privacy {
  border-color: var(--accent-line);
  background: color-mix(in oklab, var(--accent) 6%, var(--bg-panel));
}

/* Status map editor — sits directly under the "Move the Jira issue" switch it belongs
   to, indented to that switch's text column so it reads as the switch's own detail
   rather than a fifth control. Each row: triage status -> the Jira status it moves to. */
.jira-map {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: -2px 0 0 42px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-panel);
}
.jira-map-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 26px;
}
.jira-map-title {
  font: 12px/1.4 var(--font);
  color: var(--text-dim);
}
.jira-map-title b { color: var(--text); font-weight: 600; }
.jira-map-note {
  margin: 0;
  font: 12px/1.5 var(--font);
  color: var(--text-dim);
}
.jira-map-note b { color: var(--text); font-weight: 600; }
.jira-map-row {
  display: grid;
  grid-template-columns: minmax(120px, 190px) 14px minmax(0, 260px);
  align-items: center;
  gap: 12px;
}
.jira-map-from {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font: 500 13px/1.2 var(--font);
  color: var(--text);
  min-width: 0;
}
.jira-map-arrow { color: var(--text-faint); }
.jira-map-to.field-wrap { min-height: 34px; padding-block: 0; }
/* One column at phone width: the arrow has nothing to point across. */
@media (max-width: 560px) {
  .jira-map { margin-left: 0; }
  .jira-map-row { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .jira-map-arrow { display: none; }
}
.jira-why > span {
  display: block;
  margin-top: 6px;
  font: 12px/1.55 var(--font);
  /* --text-dim, not --text-faint: on the privacy row's 6% accent fill the faint token
     measures 2.97:1 in light -- worse than the 3.31:1 this paragraph already had as a
     floating .jira-hint. Making the one warning on this card less readable than it was
     is the opposite of why it moved here. */
  color: var(--text-dim);
  max-width: 78ch;
}
/* The audience clause, not the whole paragraph: one lift so the sentence that changes
   who can read a comment is the part that survives a skim. */
.jira-why > span b {
  color: var(--text);
  font-weight: 600;
}

/* On the superadmin page this names WHOSE tracker gets a token: the subject of the whole
   card, so it has a strip of its own rather than the weight of one more field.
   Same --bg-panel inset, --line and --radius-sm as .admin-form and .admintbl-wrap, so
   this is another of the page's existing block types rather than a new surface.
   Not rendered at all for a company admin, whose company is fixed. */
.jira-tenant {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px 20px;
  flex-wrap: wrap;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-panel);
}
.jira-tenant-name {
  flex: 0 1 320px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: min(260px, 100%);
  font: 600 14px/1.3 var(--font);
  color: var(--text);
  overflow-wrap: anywhere;
}
.jira-tenant-note {
  flex: 1 1 260px;
  margin: 0;
  font: 12px/1.5 var(--font);
  color: var(--text-faint);
  text-align: right;
}
@media (max-width: 720px) {
  /* Right-aligned prose under a wrapped name reads as detached; once the two are
     stacked they are a label and its caption again. */
  .jira-tenant-note { text-align: left; }
}

/* --text-dim, not --text-faint: the faint token measures 3.31:1 at 12px in the light
   theme, and this is the sentence carrying the two facts the strip exists to state. */
.jira-tenant-note {
  flex: 1 1 260px;
  margin: 0;
  font: 12px/1.5 var(--font);
  color: var(--text-dim);
  text-align: right;
}

/* ---- the destructive dialog ----
   DeleteConfirm and the report-export dialog were the same object: .usm chrome, an
   accent-purple eyebrow, one red button in the footer. That put "delete this tenant and
   everything filed under it" at the same visual weight as "pick a logo", and put the
   only red BELOW the consequence list rather than above it.

   One derived red for the whole dialog, declared here so the eyebrow, the head glyph and
   the consequence rail cannot drift apart. Mixed toward --text for the same reason
   .admin-verify's label is: raw --sev-critical measures 2.75:1 on the head band below in
   dark, which is not a colour an 11px uppercase eyebrow can be. Measured after mixing:
   5.46:1 dark / 8.62:1 light. The CONFIRM BUTTON keeps the raw token — white on solid red
   is already legible, and the thing you actually click should be the product's real red,
   not a derivative of it.

   Selector is .usm.usm-danger, not .usm-danger: the app sheets load before wizard.css, so a
   single-class rule setting border-color would lose to wizard.css's .usm{border:...}
   shorthand at equal specificity. */
.usm.usm-danger {
  --danger: color-mix(in oklab, var(--sev-critical) 68%, var(--text));
  border-color: color-mix(in oklab, var(--sev-critical) 34%, var(--line-2));
}
.usm-danger .usm-head {
  position: relative;
  align-items: center;
  /* The head stops being a title bar and starts being the warning. 9% is the most tint
     that still leaves --text-dim readable on it in light (4.35:1 for the title's sibling
     text); above that the light theme starts looking like a validation error. */
  background: color-mix(in oklab, var(--sev-critical) 9%, transparent);
  border-bottom-color: color-mix(in oklab, var(--sev-critical) 22%, var(--line));
  /* .usm-head is space-between for two children; the glyph makes three, and
     space-between would strand the title in the middle. */
  justify-content: flex-start;
}
.usm-danger .usm-head .iconbtn {
  margin-left: auto;
}
.usm-danger .eyebrow {
  color: var(--danger);
}
/* The glyph is what carries "this one is different" before a single word is read —
   the one job a footer button cannot do, because nobody looks at the footer first. */
.usm-danger-ic {
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  color: var(--danger);
  background: color-mix(in oklab, var(--sev-critical) 16%, transparent);
  border: 1px solid color-mix(in oklab, var(--sev-critical) 30%, transparent);
}

/* Deleting a company cascades every SBOM, every match, the whole comment history and
   every evidence file, so it is not instant. While it runs, the consequence list has
   stopped being a choice and become a description of what is happening — so it recedes,
   and the head grows the only moving thing on screen. A disabled button with a 15px
   spinner in it is not enough signal for an operation of this size. */
.usm-danger.is-busy .usm-body {
  opacity: 0.55;
}
.usm-danger.is-busy .usm-head::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--danger), transparent);
  animation: usm-sweep 1.1s linear infinite;
}
@keyframes usm-sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(100%); }
}
/* Indeterminate motion is the one thing on this screen a vestibular-sensitive user
   cannot look away from, because it sits in a modal. Static bar, same meaning. */
@media (prefers-reduced-motion: reduce) {
  .usm-danger.is-busy .usm-head::after {
    animation: none;
    background: var(--danger);
    opacity: 0.5;
  }
}

/* Programmatic focus on the report dialog's shell (tabIndex -1, so it can receive focus
   without becoming a tab stop). Chrome paints no ring for a target that is not
   :focus-visible, but Safari and Firefox can — and a 780px accent rectangle appearing the
   instant the dialog opens reads as an error, not as focus. Keyboard focus that arrives
   later still shows, because :focus-visible is untouched. */
.usm[tabindex]:focus:not(:focus-visible) {
  outline: none;
}

/* ---- Administration: the text that is content, not chrome ----
   --text-faint is #8a8ab0, which is 3.31:1 on the light theme's white card. That
   is a fine token for an icon and the wrong one for three things here that someone
   has to read: the account's e-mail (the only way to tell two similar usernames
   apart), the paragraph saying which Atlassian scopes the token needs, and an
   outcome in the audit trail. Mixed 65% toward --text they reach 6.73:1 light /
   10.59:1 dark and are still visibly the quieter ink. Scoped to .admin-card rather
   than repairing --text-faint, which is doing honest decorative work on eight
   other screens. */
.admin-card .admintbl-sub,
.admin-card .jira-hint,
.admin-card .jira-log-ignored {
  color: color-mix(in oklab, var(--text-faint) 65%, var(--text));
}
/* "None yet" is a to-do for the platform admin — the comment above it says so —
   and the way it was made quiet, opacity .75 on --text-dim, took it to 3.09:1 in
   light. Contrast is the wrong axis for "this is unfinished"; the type carries it
   instead, for free. */
.admin-card .admin-noadmin {
  color: var(--text-dim);
  opacity: 1;
  font-style: italic;
}
/* The success line after creating a user or saving a switch. Raw --st-done on its
   own 12% tint is 2.88:1 in light. Same call .admin-verify makes, for the same
   stated reason: nobody asked for a specific green here, and a confirmation nobody
   can read is not one. 4.99:1 light, 7.13:1 dark, still obviously green. */
.admin-card .admin-ok {
  color: color-mix(in oklab, var(--st-done) 72%, var(--text));
}

/* The webhook box — the one place in the product that hands out a secret in a URL.
   Same inset surface as the map editors it sits among; the URL is a read-only input
   (selectable, copyable, never wrapped mid-token) rather than a paragraph. */
.jira-hook {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: -2px 0 0 42px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-panel);
}
.jira-hook-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.jira-hook-last {
  flex: none;
  font: 11.5px/1.4 var(--font);
  color: var(--text-faint);
}
.jira-hook-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.jira-hook-url {
  flex: 1 1 320px;
  min-width: 0;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text);
  font-size: 12px;
  text-overflow: ellipsis;
}
.jira-hook-url:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
/* ---- People (0025) --------------------------------------------------------
   The teammate <-> Jira account roster. Same box as the webhook, because it is the
   same kind of thing: a statement of what the site currently has, with the actions
   that change it directly underneath. */
.jira-people {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: -2px 0 0 42px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-panel);
}
.jira-people .jira-switch { margin-left: 0; }
/* A detail box that LEADS a pane has no switch above it to be indented under. */
.jira-switches > .jira-hook:first-child,
.jira-switches > .jira-people:first-child {
  margin: 0;
}
.jira-people-list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--line);
}
.jira-person {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}
.jira-person-who {
  flex: 1 1 160px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  font: 12.5px/1.3 var(--font);
  color: var(--text);
}
.jira-person-mail {
  font: 10.5px/1.3 var(--font);
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.jira-person-state {
  flex: 1 1 200px;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
/* Nobody in Jira yet is a FACT, not a fault: stated quietly, never in a warning
   colour — most teams have people who simply do not use the tracker. */
.jira-person-none {
  font: 11.5px/1.4 var(--font);
  color: var(--text-faint);
}
.jira-person-tag {
  font: 600 10px/1 var(--font);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-faint);
  border: 1px solid var(--line-2);
  border-radius: 999px;
  padding: 3px 6px;
}
/* The one state that costs someone an action: linked, but Jira will not let them
   hold an assignment — which is exactly when the assignee sync silently does nothing.

   The BORDER may be --sev-high; the TEXT may not. Light's --sev-high (#ea580c) is
   3.56:1 on the white panel, which passes for a border (a UI component needs 3:1)
   and fails for 10px text (which needs 4.5:1). The two are therefore different
   colours on purpose — the amber cue is carried by the border, and the label is
   darkened to 7.3:1. Dark's #f97316 on #11112a is already 6.6:1, so it keeps the
   ramp colour for both. */
.jira-person-tag.is-warn {
  color: #9a3412;
  border-color: var(--sev-high, #d97706);
}
.app[data-theme="dark"] .jira-person-tag.is-warn { color: var(--sev-high, #f97316); }
.jira-person-note {
  flex: 1 1 100%;
  font: 11px/1.4 var(--font);
  color: var(--text-faint);
}
.jira-person-acts {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
/* An action that spends one of the customer's Jira licence seats. Outlined in the
   accent rather than filled: visible enough not to be clicked by accident, quiet
   enough not to read as the recommended next step. */
.jira-load.is-spend {
  border-color: var(--accent);
  color: var(--accent);
}
.jira-load.is-spend:hover:not(:disabled) {
  background: var(--accent);
  color: #fff;
}
.app[data-theme="dark"] .jira-load.is-spend { color: var(--accent-3); border-color: var(--accent-3); }
.app[data-theme="dark"] .jira-load.is-spend:hover:not(:disabled) { background: var(--accent-3); color: #000; }

/* The registration's state line: a tick when the site has it, a warning when it
   drifted or the token may not manage it, a plain link glyph before anything exists. */
.jira-hook-state {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font: 13px/1.5 var(--font);
  color: var(--text-dim);
}
.jira-hook-state .ic { flex: none; margin-top: 2px; }
.jira-hook-state.is-registered, .jira-hook-state.is-current { color: var(--text); }
.jira-hook-state.is-registered .ic, .jira-hook-state.is-current .ic { color: var(--st-done); }
.jira-hook-state.is-stale .ic, .jira-hook-state.is-not-admin .ic, .jira-hook-state.is-unsupported .ic, .jira-hook-state.is-failed .ic { color: var(--sev-high, #d97706); }
.jira-hook-manual {
  border: 0;
  background: none;
  padding: 0;
  font: 12px/1.4 var(--font);
  color: var(--text-faint);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.jira-hook-manual:hover { color: var(--text); }
.jira-hook-manual-box {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 6px;
  border-top: 1px dashed var(--line);
}
.jira-map-stale { color: var(--text-faint); font-weight: 400; }
@media (max-width: 560px) {
  .jira-hook { margin-left: 0; }
}

/* Sync log: which way the row went. Inbound rows are the new kind (0020), so they
   carry the accent; outbound stays in the quiet ink the column always had. */
.jira-log-dir {
  white-space: nowrap;
  font-size: 11.5px;
  color: var(--text-faint);
}
.jira-log-dir.is-in { color: var(--accent); font-weight: 600; }
.app[data-theme="dark"] .jira-log-dir.is-in { color: var(--accent-3); }
