/* auth-admin dashboard — Iniciador design system.
   Near-black canvas (never pure black), depth by surface luminosity, white
   primary action, indigo reserved for the focus ring. Geist, headings <= 500. */
:root{
  /* surfaces: depth is luminosity, never shadow */
  --s-deep: hsl(0 0% 2%); --s0: hsl(0 0% 4%); --s1: hsl(0 0% 6%);
  --s2: hsl(0 0% 7%); --s3: hsl(0 0% 9%); --s4: hsl(0 0% 12%);
  /* borders: white at low alpha */
  --border-subtle: hsl(0 0% 100% / .06); --border: hsl(0 0% 100% / .10);
  --border-strong: hsl(0 0% 100% / .16);
  /* text: cool gray on a coherent 216 hue */
  --text: #FAFAFA; --text-soft: #D4D8DE; --text-muted: #8B9098; --text-dim: #61656B;
  /* accents: positive/negative for data, indigo only for focus */
  --positive: hsl(150 60% 56%); --negative: hsl(8 60% 73%); --accent: hsl(225 88% 72%);
  /* radii: --radius-lg is the outer surface (panel, card, table, section),
     --radius the box nested one level in, --radius-sm the control inside that.
     Every value already existed as a literal; naming them is what makes a
     nested surface read as concentric instead of accidentally equal. */
  --radius-lg: 12px; --radius: 0.625rem; --radius-sm: 8px;
}
*{ box-sizing: border-box; }
body{
  background: var(--s0); color: var(--text);
  font-family: 'Geist', system-ui, sans-serif; font-size: 14px; margin: 0;
  -webkit-font-smoothing: antialiased;
}
h1,h2,h3{ font-weight: 500; text-wrap: balance; }
h1{ font-size: 22px; letter-spacing: -.02em; margin: 0 0 20px; }
h2{ font-size: 15px; letter-spacing: -.01em; margin: 32px 0 10px; }
p{ line-height: 1.6; color: var(--text-soft); max-width: 72ch; }
code{ font-family: 'Geist Mono', ui-monospace, monospace; font-size: 11.5px; font-weight: 400; }
:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }

/* shell */
#app{ display: grid; grid-template-columns: 270px minmax(0,1fr); min-height: 100vh; }
.sidebar{
  background: var(--s-deep); border-right: 1px solid var(--border-subtle);
  padding: 16px 10px; position: sticky; top: 0; align-self: start;
  height: 100vh; overflow-y: auto;
}
#app > main{ min-width: 0; }
.brand{
  font-family: 'Geist Mono', ui-monospace, monospace; font-weight: 600;
  font-size: 12.5px; color: var(--text); padding: 4px 10px 20px;
}
.nav-label{
  padding: 18px 10px 6px; font-size: 10.5px; letter-spacing: 1.4px;
  text-transform: uppercase; color: var(--text-muted); font-weight: 500;
}
.nav-item{
  position: relative; display: flex; align-items: center; min-height: 40px;
  padding: 7px 12px; border-radius: var(--radius-sm);
  color: var(--text-muted); text-decoration: none; font-size: 13px;
  transition: background-color .12s ease, color .12s ease;
}
.nav-item:hover{ background: var(--s1); color: var(--text-soft); }
/* The current page's sidebar link is marked with the class Unpoly itself
   maintains inside the [up-nav] container (up-current, assets/signin.js); the
   server renders that same class before the bundle runs. The legacy active
   class is styled identically so any pre-existing markup highlights the same. */
.nav-item.active, .nav-item.up-current{ background: var(--s1); color: var(--text); }
.nav-item.active::before, .nav-item.up-current::before{
  content: ""; position: absolute; left: 0; top: 7px; bottom: 7px;
  width: 2px; border-radius: 1px; background: var(--text);
}
.topbar{
  display: flex; align-items: center; gap: 12px;
  padding: 14px 28px; border-bottom: 1px solid var(--border-subtle);
}
.who{ font-weight: 500; font-size: 13px; color: var(--text-soft); }
.topbar #logout-form{ margin-left: auto; }
#main{ padding: 28px 28px 72px; max-width: 1360px; margin: 0 auto; }

/* panels */
.panel, .card, dl, .form{
  background: var(--s1); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 18px 20px;
}
dl{
  display: grid; grid-template-columns: max-content minmax(0,1fr);
  gap: 9px 22px; font-size: 12.5px; margin: 0 0 20px; max-width: 820px;
}
dt{ color: var(--text-muted); }
dd{ margin: 0; color: var(--text-soft); overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
dd code{ color: var(--text); }
/* Narrow viewports: the label column of a definition list is sized by its
   longest label, which on a phone leaves the value a few characters wide. The
   label goes above its own value instead, and the pair keeps a smaller gap
   than the gap between pairs so it still reads as one item. */
@media (max-width: 640px){
  dl{ grid-template-columns: minmax(0,1fr); gap: 2px; }
  dt{ margin-top: 12px; }
  dt:first-of-type{ margin-top: 0; }
}

/* tables: dense, horizontally scrollable, never reflowed into cards */
.table-wrap{
  overflow-x: auto; background: var(--s1); border: 1px solid var(--border);
  border-radius: var(--radius-lg); margin: 0 0 20px;
}
table{ width: 100%; border-collapse: collapse; font-size: 12.5px; min-width: 720px; }
th{
  text-align: left; font-size: 10.5px; font-weight: 500; letter-spacing: 1.4px;
  text-transform: uppercase; color: var(--text-muted); white-space: nowrap;
  padding: 11px 14px; border-bottom: 1px solid var(--border-subtle);
}
/* The row-action columns have no header text; letting them hug their link keeps
   the free width on the data columns instead of stranding the action far right. */
th:empty{ width: 1px; }
td a.detail{ white-space: nowrap; }
td{
  padding: 10px 14px; border-bottom: 1px solid var(--border-subtle);
  color: var(--text-soft); font-variant-numeric: tabular-nums; vertical-align: middle;
}
tbody tr{ transition: background-color .12s ease; }
tbody tr:hover{ background: var(--s2); }
tbody tr:last-child td{ border-bottom: 0; }
td code{ color: var(--text); }

/* form controls */
input,textarea,select{
  background: var(--s2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text); font: inherit; font-size: 13px; padding: 8px 11px;
  transition: border-color .12s ease;
}
/* Text inputs and selects are 40px tall so a pointer target never falls below
   the minimum. Checkboxes and radios are excluded: they are their own small
   glyph, sized by the user agent, and their label is the target. */
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]), select{
  min-height: 40px;
}
input::placeholder,textarea::placeholder{ color: var(--text-dim); }
input:hover,textarea:hover,select:hover{ border-color: var(--border-strong); }
input:focus,textarea:focus,select:focus{ border-color: var(--border-strong); }
input:user-invalid,textarea:user-invalid{ border-color: var(--negative); }
input:disabled,textarea:disabled,select:disabled{ opacity: .5; cursor: not-allowed; }
label{ font-size: 12.5px; color: var(--text-muted); }

/* actions: exactly one white primary per context; indigo is never a button */
button.primary, a.primary{
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--text); color: hsl(0 0% 6%); border: 1px solid transparent;
  border-radius: 999px; font: inherit; font-size: 13px; font-weight: 600;
  padding: 8px 18px; min-height: 40px; cursor: pointer; text-decoration: none;
  white-space: nowrap; transition: opacity .12s ease;
}
button.primary:hover, a.primary:hover{ opacity: .9; }
button.primary:active, a.primary:active{ opacity: .8; }
button.ghost, a.ghost{
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--s2); color: var(--text-soft); border: 1px solid var(--border);
  border-radius: 999px; font: inherit; font-size: 13px; padding: 7px 15px;
  min-height: 40px; cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: border-color .12s ease, color .12s ease;
}
button.ghost:hover, a.ghost:hover{ border-color: var(--border-strong); color: var(--text); }
button.danger{ background: transparent; border-color: hsl(8 60% 73% / .28); color: var(--negative); }
button.danger:hover{ border-color: hsl(8 60% 73% / .5); color: var(--negative); }
button:disabled{ opacity: .45; cursor: not-allowed; }
button:disabled:hover{ border-color: var(--border); color: var(--text-soft); }
/* Row controls inside a table are the one place a 40px-tall button would
   double the height of every line. The drawn control stays dense and the hit
   area is expanded to 40px with a transparent overlay instead — the target is
   full size, only the paint is small. The horizontal expansion stays at 2px so
   two adjacent row actions never cover each other. */
td .primary, td .ghost{
  position: relative; min-height: 28px; padding-top: 4px; padding-bottom: 4px;
}
td .primary::after, td .ghost::after{
  content: ""; position: absolute; inset-block: -6px; inset-inline: -2px;
}
td a.detail{
  position: relative; display: inline-block; padding: 3px 0;
}
td a.detail::after{
  content: ""; position: absolute; inset-block: -8px; inset-inline: -2px;
}
/* Unpoly marks the submitting form's button and the loading fragment while a
   request is in flight (assets/signin.js); both read as busy, not broken. */
button.up-active, a.up-active, input.up-active{ opacity: .6; cursor: progress; }
#main{ transition: opacity .12s ease; }
#main.up-loading{ opacity: .55; }

/* Unpoly appends a bare <up-progress-bar> element to the body once a request
   passes its late threshold, sets an inline width in vw as it advances, and
   removes it on recovery (up.ProgressBar, assets/signin.js). Only its width and
   its width transition are inline: the bundle carries no stylesheet, so without
   the rules below the element is an unstyled inline box and nothing is visible
   during the 2-8s control-plane requests. The pulse keeps the bar alive while
   the inline width sits near its ceiling and barely moves. */
up-progress-bar{
  position: fixed; top: 0; left: 0; height: 2px;
  background: hsl(0 0% 100% / .9); z-index: 1000;
  animation: up-progress-pulse 1.1s ease-in-out infinite;
}
@keyframes up-progress-pulse{ 50%{ opacity: .5; } }

/* badges and lifecycle state */
.badge{
  display: inline-block; font-size: 10.5px; font-weight: 500; letter-spacing: .06em;
  text-transform: uppercase; padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--s1); color: var(--text-muted);
}
.state{
  display: inline-block; font-size: 10.5px; letter-spacing: 1px; text-transform: uppercase;
  padding: 2px 8px; border-radius: 999px; white-space: nowrap;
  border: 1px solid var(--border-subtle); background: var(--s2); color: var(--text-muted);
}
.state.active{ color: var(--positive); border-color: hsl(150 60% 56% / .25); background: hsl(150 60% 56% / .08); }
.state.failed{ color: var(--negative); border-color: hsl(8 60% 73% / .25); background: hsl(8 60% 73% / .08); }
/* Two more semantic states the pages needed and were spelling as prose: a
   pending/attention state and a retired one. Colour never carries the meaning
   on its own — the badge always contains its own word. */
.state.warning{ color: hsl(38 80% 72%); border-color: hsl(38 80% 72% / .25); background: hsl(38 80% 72% / .08); }
.state.inactive{ color: var(--text-dim); border-color: var(--border-subtle); background: var(--s1); }

/* banners: the pre-existing .flash and .err hooks carry the alert styling */
.err{ color: var(--negative); font-size: 12.5px; }
.flash, p.err, p.flash, p.notice{
  border: 1px solid; border-radius: var(--radius); padding: 10px 13px;
  font-size: 12.5px; line-height: 1.5; margin: 0 0 16px; max-width: 72ch;
}
.flash, p.flash{
  background: hsl(150 60% 56% / .08); border-color: hsl(150 60% 56% / .22);
  color: hsl(150 45% 80%);
}
p.err{ background: hsl(8 60% 73% / .08); border-color: hsl(8 60% 73% / .24); color: var(--negative); }
/* A statement of fact, neither success nor failure: an expired session is
   normal, so the box borrows the banner shape without borrowing a tone that
   would read as either. */
p.notice{ background: var(--s2); border-color: var(--border); color: var(--text-soft); }
.muted{ color: var(--text-muted); font-size: 12.5px; }

/* components */
.searchbar{ display: flex; gap: 8px; margin-bottom: 18px; max-width: 620px; }
.searchbar input{ flex: 1; min-width: 0; }
/* Searchable comboboxes are native selects until the bundle attaches. The
   enhanced field then owns submission and the fallback is disabled, so a form
   can never send both values. */
.combobox{ position: relative; display: grid; gap: 5px; }
.combobox [hidden]{ display: none; }
.combobox-enhanced{ position: relative; }
.combobox-input{ width: 100%; }
.combobox-list{
  position: absolute; z-index: 20; inset: calc(100% + 4px) 0 auto;
  max-height: 280px; overflow-y: auto; margin: 0; padding: 4px;
  list-style: none; background: var(--s3); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: 0 12px 28px hsl(0 0% 0% / .4);
}
.combobox-option{
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 12px;
  align-items: center; min-height: 40px; padding: 7px 9px;
  border-radius: var(--radius-sm); color: var(--text-soft); cursor: pointer;
}
.combobox-option:hover, .combobox-option[data-active]{ background: var(--s4); color: var(--text); }
.combobox-option[aria-selected="true"]{ color: var(--text); }
.combobox-option-label{ min-width: 0; font-size: 13px; font-weight: 500; }
.combobox-option-description{
  grid-column: 1; min-width: 0; color: var(--text-muted); font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.combobox-option-id{
  grid-column: 2; grid-row: 1 / span 2; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 18ch;
}
.combobox-empty{ margin: 6px 0 0; color: var(--text-muted); font-size: 12.5px; }
@media (max-width: 640px){
  .combobox-option{ grid-template-columns: minmax(0, 1fr); }
  .combobox-option-id{ grid-column: 1; grid-row: auto; max-width: 100%; }
}
/* One action row, two names: .row-actions is what the existing pages already
   carry and .action-bar is what new markup calls it. They are the same layout,
   declared once, so the two spellings can never drift apart. .bar-end pushes
   what follows it to the far edge, which is how an action that is not part of
   the common group is separated without a second container. */
.row-actions, .action-bar{
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 0 0 20px;
}
.row-actions:empty, .action-bar:empty{ display: none; margin: 0; }
.bar-end{ margin-left: auto; }
/* The cursor pagination navigation is the same horizontal row of controls as
   an action bar, except it follows the list instead of preceding it, so the
   spacing sits above it. No class exists for that, so it is declared here. */
nav.pagination{
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 20px 0 0;
}
@media (max-width: 640px){
  /* Stacked, but each control keeps a comfortable width instead of shrinking
     to its label. Order is preserved: the primary action stays first. */
  .row-actions > *, .action-bar > *{ flex: 1 1 100%; }
  .row-actions .primary, .row-actions .ghost,
  .action-bar .primary, .action-bar .ghost{ width: 100%; }
  .bar-end{ margin-left: 0; }
}
ul.choices{ list-style: none; padding: 0; margin: 16px 0 20px; display: grid; gap: 3px; max-width: 720px; }
/* The whole row is the target: the link's overlay covers its own list item, so
   a 18px-tall text link becomes a 40px-tall row. Nothing else in the row is
   interactive, so the overlay cannot swallow another control's click. */
.choices li{
  position: relative;
  display: flex; gap: 14px; align-items: baseline; justify-content: space-between;
  padding: 11px 13px; background: var(--s1); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm); transition: border-color .12s ease;
}
.choices a::after{ content: ""; position: absolute; inset: 0; border-radius: inherit; }
.choices li:hover{ border-color: var(--border); }
.choices a{ color: var(--text); text-decoration: none; }
.choices a:hover{ text-decoration: underline; }
.choices code{ color: var(--text-dim); }
/* The two page-level navigation links are small text. They keep the 40px
   target through their own height, and their box hugs the label so the target
   is the words rather than the whole content width. */
a.back, a.next{
  display: inline-flex; align-items: center; min-height: 40px;
  color: var(--text-muted); text-decoration: none; font-size: 12.5px;
}
/* The page-level back link closes the page: it gets its own line even when the
   inline action forms above it are laid out inline. */
#main > a.back{ display: flex; width: max-content; margin-top: 4px; }
/* Every disclosure control is at least 40px. The three named ones below set
   their own box; this covers the plain ones, and keeps display:list-item so the
   user agent still draws the triangle that says the section can open. */
summary{ padding: 11px 0; cursor: pointer; }
a.back:hover, a.next:hover{ color: var(--text); text-decoration: underline; }
a.detail{ color: var(--text-soft); text-decoration: none; }
a.detail:hover{ color: var(--text); text-decoration: underline; }
.mfa-account, .mfa-state{ font-size: 13px; color: var(--text-soft); }
.mfa-reconfigure-card{
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  max-width: 820px; padding: 14px 16px; background: var(--s1);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.mfa-reconfigure-copy{
  flex: 1 1 auto; margin: 0; font-size: 12.5px; line-height: 1.5;
  color: var(--text-muted); text-wrap: pretty;
}
.mfa-reconfigure-card form[action*="/mfa-reset"]{ flex: 0 0 auto; margin: 0; }
.mfa-reconfigure-card .primary{ white-space: normal; }
@media (max-width: 640px){
  .mfa-reconfigure-card{ flex-direction: column; align-items: flex-start; }
  .mfa-reconfigure-card .primary{ min-height: 40px; }
}
.claims-unavailable{ margin: 0 0 20px; }

/* Write forms (.form): one column, every label directly above its own input,
   never side by side. The measure is capped near 600px because these are short
   labelled fields, not prose: a full-width row would drag the eye across the
   viewport between a label and the box it names. The single-button action forms
   inside .row-actions carry class="inline" and are not .form, so they stay in
   that row. */
.form{ margin: 0 0 20px; max-width: 600px; }
/* The sections are plain block boxes, not grids: a <legend> is rendered
   specially inside its fieldset, and making the fieldset a grid container is
   the one thing that makes that rendering differ between browsers. Spacing
   comes from the margins below instead. */
/* No rule on the section box itself: a <legend> punches a hole in its
   fieldset's border and sits in it, which draws a line that starts halfway
   across the form. The uppercase legend plus the gap below separates the
   sections on their own. */
.form fieldset{ border: 0; margin: 0; padding: 0; }
.form fieldset + fieldset{ margin-top: 26px; }
.form legend{
  padding: 0; margin: 0 0 12px; font-size: 10.5px; letter-spacing: 1.4px;
  text-transform: uppercase; color: var(--text-muted); font-weight: 500;
}
.form .field{ display: grid; gap: 5px; }
.form .field + .field{ margin-top: 14px; }
.form label{ font-size: 12.5px; color: var(--text-soft); }
/* Selects are included: a select sizes itself to its widest option, and the
   product-role catalog has option labels long enough to push the control past a
   phone's viewport.

   Checkboxes and radios are NOT: they are a fixed-size glyph, and a full-width
   one pushed its own caption onto the next line — the box ended up alone on one
   row with "Domínio ativo" beside empty space. */
.form input:not([type="checkbox"]):not([type="radio"]), .form textarea, .form select{
  width: 100%;
}
.form input[type="checkbox"], .form input[type="radio"]{ width: auto; margin: 0; }
/* A label that contains its own checkbox is the control's target, so it gets the
   40px height and puts the box beside its caption instead of above it. */
.form label:has(> input[type="checkbox"]), .form label:has(> input[type="radio"]){
  display: flex; align-items: center; gap: 9px; min-height: 40px;
}
.form textarea{
  font-family: 'Geist Mono', ui-monospace, monospace; font-size: 12px; resize: vertical;
}
.form .muted{ margin: 0 0 12px; }
.form > .err{ margin-bottom: 16px; }
/* Inline validation: the message sits under the input it names, so the
   correction is read where it has to be made. */
.field-error{
  margin: 0; font-size: 12px; line-height: 1.4; color: var(--negative);
}
/* Actions read left to right in the order they are taken: the primary first,
   the way out beside it. */
.form-actions{
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin: 22px 0 0; padding-top: 18px; border-top: 1px solid var(--border-subtle);
}
form[action*="/revoke"], form[action*="/mfa-reset"]{ display: inline-block; margin: 0 10px 20px 0; }

/* Tenant context: the breadcrumb says which tenant the page belongs to, the tab
   strip moves between that tenant's sections. Both live inside #main and are
   re-rendered by the server on every swap. */
.breadcrumb{
  display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  font-size: 12.5px; margin: 0 0 12px;
}
/* The crumb links are small text, so their hit area is expanded vertically
   rather than by growing the trail into a band. The horizontal expansion stays
   at 3px so a crumb never covers its separator or its neighbour. */
.breadcrumb a{ position: relative; color: var(--text-muted); text-decoration: none; }
.breadcrumb a::after{ content: ""; position: absolute; inset-block: -13px; inset-inline: -3px; }
.breadcrumb a:hover{ color: var(--text); text-decoration: underline; }
.crumb-sep{ color: var(--text-dim); }
.crumb-current{ color: var(--text-soft); }
.tabs{
  display: flex; flex-wrap: wrap; gap: 4px;
  border-bottom: 1px solid var(--border-subtle); margin: 0 0 22px;
}
.tab{
  position: relative; display: inline-flex; align-items: center; min-height: 40px;
  padding: 8px 12px 10px; margin-bottom: -1px;
  color: var(--text-muted); text-decoration: none; font-size: 13px;
  border-bottom: 2px solid transparent;
  transition: color .12s ease, border-color .12s ease;
}
.tab:hover{ color: var(--text-soft); }
.tab.up-current{ color: var(--text); border-bottom-color: var(--text); }

/* sign-in */
.signin{ display: grid; place-items: center; min-height: 100vh; padding: 24px; }
.signin-card{
  background: var(--s1); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 32px; width: 100%; max-width: 420px; display: grid; gap: 12px;
}
.signin-card h1{ font-size: 20px; margin: 0; }
.signin-card .brand{ padding: 0; }
.signin-card p{ margin: 0; }
.signin-card input, .signin-card button{ width: 100%; justify-content: center; }
/* The card is a 12px-gap grid, and .field is otherwise only styled inside
   .form. Without this the label and the input it names would either become two
   separate card rows 12px apart or sit flush against each other. */
.signin-card .field{ display: grid; gap: 6px; }
/* :not([hidden]) is required, not decoration: a bare display rule would beat the
   user agent's [hidden] rule and reveal the second-factor step on first load. */
#signin-mfa:not([hidden]){ display: grid; gap: 12px; border-top: 1px solid var(--border-subtle); padding-top: 16px; }

/* Narrow viewports: the sidebar becomes a wrapping strip above the content.
   The nav labels stay readable (the entries are text links, not icons), and
   tables keep their own horizontal scroll instead of reflowing into cards. */
@media (max-width: 1024px){
  /* align-content: start keeps the two stacked rows at their own height: the
     grid default stretches them to fill min-height:100vh and would open a gap
     between the navigation strip and the content. */
  #app{ grid-template-columns: minmax(0,1fr); align-content: start; }
  .sidebar{
    position: static; height: auto; display: flex; flex-wrap: wrap; align-items: center;
    gap: 4px; padding: 10px 16px; border-right: 0; border-bottom: 1px solid var(--border-subtle);
  }
  .brand{ flex: 1 0 100%; padding: 2px 0 10px; }
  /* Each group label starts its own line: inline with the entries it names, the
     strip read as one undifferentiated run of words. */
  .nav-label{ flex: 1 0 100%; padding: 10px 0 2px; }
  .nav-item{ padding: 6px 10px; }
  .nav-item.active::before, .nav-item.up-current::before{ display: none; }
  .topbar{ padding: 12px 16px; }
  #main{ padding: 20px 16px 56px; }
}
@media (prefers-reduced-motion: reduce){
  *{ transition-duration: .01ms !important; }
  up-progress-bar{ animation: none; }
}

/* Grants editor (#grants-editor): the numeric authorization SKU catalog, one
   disclosure per module so 300-odd checkboxes are not all on screen at once.
   The catalog is long and repetitive, so the code carries the weight — it is
   the value the operator actually reasons about — and the description beside it
   is dimmed. */
#grants-editor .hint, #roles-editor .hint{
  margin: 0 0 16px; font-size: 12px; line-height: 1.5; color: var(--text-muted);
}
/* The product-role editor has the same per-module disclosure shape as the SKU
   catalog, so it gets the same rule instead of a second, slightly different one. */
.grants-module, .roles-module{
  border-top: 1px solid var(--border-subtle); padding: 8px 0;
}
.grants-module > summary, .roles-module > summary{
  cursor: pointer; display: flex; align-items: center; min-height: 40px;
  font-size: 12.5px; color: var(--text-soft); padding: 4px 0;
  transition: color .12s ease;
}
.grants-module > summary:hover, .roles-module > summary:hover{ color: var(--text); }
.grants-module .hint, .roles-module .hint{ margin: 6px 0 0 2px; }
/* One row per code: the box, the six-digit code, then what it names.

   The checkbox glyph is 13px, so the ROW is the target: the whole label is
   clickable, it is given a comfortable height, and it highlights on hover so
   the operator can see the extent of what they are about to hit. Expanding the
   rows past their own box is not an option here — three hundred of them sit
   directly below one another, and overlapping targets would toggle a neighbour. */
label.grant{
  display: flex; align-items: center; gap: 8px; padding: 6px 8px;
  min-height: 32px; border-radius: var(--radius-sm);
  font-size: 12.5px; color: var(--text-soft);
  transition: background-color .12s ease;
}
label.grant:hover{ background: var(--s3); }
label.grant input{ width: auto; min-height: 0; margin: 0; }
label.grant code{ color: var(--text); font-size: 12px; }
label.grant span{ color: var(--text-muted); }
/* The module wildcard sits above the codes it covers and reads as the heading
   of that block, not as one more code in the list. */
label.grant-wildcard{ margin: 6px 0 4px; }
label.grant-wildcard code, label.grant-wildcard span{ color: var(--text-soft); }
/* A code granted by a wildcard is shown, not editable: the wildcard is what an
   operator changes. */
.grant-derived{ color: var(--text-dim); font-size: 11.5px; }

/* ==========================================================================
   Composition patterns shared by every administrative page.

   These are not a second design system: they are the small set of shapes the
   pages were each inventing locally out of bare h1/h2/div/dl/table, expressed
   once. Nothing here introduces a colour, a font or a scale that was not
   already declared above.
   ========================================================================== */

/* Skip link: the sidebar is a long list of entries, and a keyboard operator
   should be able to reach the page body without walking all of them. It is
   only painted while focused. */
.skip-link{
  position: absolute; left: -9999px; top: 0; z-index: 1001;
  background: var(--s3); color: var(--text); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); padding: 10px 14px; font-size: 13px;
  text-decoration: none;
}
.skip-link:focus-visible{ left: 12px; top: 12px; }

/* --- page header ---------------------------------------------------------
   Context, then title, then one short description, then the page's actions.
   The text block and the action block are siblings so they sit side by side on
   a wide viewport and stack in reading order on a narrow one. */
.page-head{
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px 24px;
  margin: 0 0 24px;
}
.page-head-text{ flex: 1 1 340px; min-width: 0; }
/* The heading owns no bottom margin here: the description below it belongs to
   the same group and is placed by this container, not by the h1 rule. */
.page-head h1{ margin: 0; }
.page-eyebrow{
  display: block; font-size: 10.5px; letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--text-muted); font-weight: 500; margin: 0 0 6px;
}
.page-desc{ margin: 6px 0 0; font-size: 12.5px; color: var(--text-muted); max-width: 72ch; }
.page-actions{ display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.page-actions:empty{ display: none; }

/* --- resource header ----------------------------------------------------
   The detail-page variant: the resource's own name, its state, the handful of
   identifiers an operator needs at all times, and the common actions. Anything
   consulted less often belongs in a section further down the page, not here. */
.resource-head{
  background: var(--s1); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 18px 20px; margin: 0 0 24px;
}
.resource-title-row{
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
}
.resource-title-row h1{ margin: 0; }
.resource-desc{ margin: 8px 0 0; font-size: 12.5px; color: var(--text-muted); max-width: 72ch; }
/* The header metadata is a definition list laid out as a grid, and the grid has
   FOUR tracks on purpose: label, value, label, value. A <dl> has flat dt/dd
   children, so an auto-fit track list would drop each label and each value into
   the next free cell independently and pair a label with somebody else's value.
   Four fixed tracks make the placement deterministic — two pairs per row, each
   label beside the value it names — with no wrapper element in the markup.

   It carries no surface of its own: it is already inside the header's surface. */
dl.resource-meta{
  background: none; border: 0; border-radius: 0; padding: 0;
  display: grid;
  grid-template-columns: max-content minmax(0,1fr) max-content minmax(0,1fr);
  gap: 11px 22px; margin: 18px 0 22px; max-width: none;
}
/* Inside the resource header the list is followed by the action row, which
   brings its own top margin and rule; a bottom margin here would double it. */
.resource-head dl.resource-meta{ margin-bottom: 0; }
dl.resource-meta dt{
  font-size: 10.5px; letter-spacing: 1.2px; text-transform: uppercase;
  color: var(--text-muted); margin: 0; padding-top: 1px;
}
dl.resource-meta dd{ font-size: 12.5px; color: var(--text); overflow-wrap: anywhere; }
/* One pair per row once two no longer fit: the value column would otherwise be
   narrower than the identifiers and timestamps it holds. */
@media (max-width: 1100px){
  dl.resource-meta{ grid-template-columns: max-content minmax(0,1fr); }
}
/* On a phone the label goes above its value, like every other definition list
   here. These rules restate the stacking because the class selector outranks
   the generic dl rule at every width. */
@media (max-width: 640px){
  dl.resource-meta{ grid-template-columns: minmax(0,1fr); gap: 2px; }
  dl.resource-meta dt{ margin-top: 12px; }
  dl.resource-meta dt:first-of-type{ margin-top: 0; }
}
.resource-actions{
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin: 18px 0 0; padding-top: 16px; border-top: 1px solid var(--border-subtle);
}
.resource-actions:empty{ display: none; margin: 0; padding: 0; border-top: 0; }

/* --- section ------------------------------------------------------------
   One shape for every block of a page: title, optional description, optional
   action, body. Sibling sections keep the same vertical interval, which is what
   makes a long detail page read as a list of groups instead of one scroll. */
.section{ margin: 0 0 28px; }
.section:last-child{ margin-bottom: 0; }
/* The head is a GRID with explicitly placed children, not a wrapping flex row.
   A flex item's line breaking is decided from its flex-basis clamped by its own
   max-width, so a description with `flex-basis: 100%` and a 72ch measure
   resolves to 72ch, fits beside the title and never wraps — the description
   ended up on the heading's line. Naming a row and a column for each part makes
   the placement independent of which parts a given section has. */
.section-head{
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: baseline; column-gap: 16px; row-gap: 6px;
  margin: 0 0 12px;
}
/* The section title is an h2 in the document outline; the margins come from
   this container so two adjacent sections cannot end up unevenly spaced. */
.section-head h2, h2.section-title{
  grid-column: 1; grid-row: 1; margin: 0; font-size: 15px; letter-spacing: -.01em;
}
.section-count{
  grid-column: 2; grid-row: 1; justify-self: start;
  font-size: 12.5px; color: var(--text-muted); font-variant-numeric: tabular-nums;
}
.section-desc{
  grid-column: 1 / -1; grid-row: 2;
  margin: 0; font-size: 12.5px; color: var(--text-muted); max-width: 72ch;
}
.section-actions{
  grid-column: 3; grid-row: 1; justify-self: end;
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
}
.section-actions:empty{ display: none; }
@media (max-width: 640px){
  /* The count stays beside the title; the actions drop to their own full-width
     row rather than being squeezed next to it. */
  .section-head{ grid-template-columns: auto 1fr; }
  .section-actions{ grid-column: 1 / -1; grid-row: auto; justify-self: stretch; }
}
/* A section that groups sibling sections — "Autorização" holding grants and
   product roles — is drawn as one surface so its members read as related. The
   inner radius is one step down from the outer, and the inner padding is wide
   enough for the two to look concentric. */
.section-group{
  background: var(--s1); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 20px; margin: 0 0 28px;
}
.section-group > .section-head{ margin-bottom: 16px; }
.section-group .section:last-child{ margin-bottom: 0; }
.section-group .panel, .section-group .card,
.section-group .form, .section-group .table-wrap,
.section-group dl:not(.resource-meta){ background: var(--s2); border-radius: var(--radius); }

/* --- copyable identifier ------------------------------------------------
   A UUID is shown abbreviated on a single line, never wrapped across a cell.
   The full value is in the element's title and in the copy button's payload,
   so nothing is truncated without a way to read it. */
.copy-id{ display: inline-flex; align-items: center; gap: 6px; max-width: 100%; min-width: 0; }
.copy-id-text{
  font-family: 'Geist Mono', ui-monospace, monospace; font-size: 11.5px;
  font-variant-numeric: tabular-nums; color: var(--text); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* The long-value variant (copyValue) holds its whole value and is cut at the
   end by this box instead of in the middle by the template: for a URL or an
   entity id the beginning is what identifies it. min-width:0 is what lets the
   flex item shrink at all, and the ceiling keeps one very long endpoint from
   pushing the copy control off the row. */
.copy-id-text.is-value{ min-width: 0; max-width: 46ch; }
/* The drawn button is small on purpose — it sits inside table cells and beside
   metadata — and its hit area is expanded to 40px with a transparent overlay. */
.copy-id-btn{
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0; flex: 0 0 auto;
  background: transparent; border: 1px solid transparent; border-radius: var(--radius-sm);
  color: var(--text-muted); cursor: pointer;
  transition: color .12s ease, border-color .12s ease;
}
.copy-id-btn::after{ content: ""; position: absolute; inset: -9px; }
.copy-id-btn:hover{ color: var(--text); border-color: var(--border); }
.copy-id-btn svg{ width: 13px; height: 13px; display: block; }
/* Copy confirmation is a state on the control, held for a moment by the
   document-level listener in the layout. It is not colour alone: the button's
   accessible name changes with it. */
.copy-id-btn[data-copied]{ color: var(--positive); border-color: hsl(150 60% 56% / .3); }

/* --- empty state --------------------------------------------------------
   What the list holds, why it is empty, and the next safe action. A bare
   "nenhum item" says none of those. */
.empty{
  background: var(--s1); border: 1px dashed var(--border);
  border-radius: var(--radius-lg); padding: 22px 20px; margin: 0 0 20px;
}
.empty-title{ margin: 0; font-size: 13px; font-weight: 500; color: var(--text); }
.empty-desc{ margin: 6px 0 0; font-size: 12.5px; color: var(--text-muted); max-width: 68ch; }
.empty-actions{ display: flex; flex-wrap: wrap; gap: 10px; margin: 16px 0 0; }
.empty-actions:empty{ display: none; margin: 0; }
/* An empty state inside a table's surface replaces the rows; it must not draw a
   second box around itself. */
.table-wrap > .empty, .section-group .empty{ background: none; border: 0; margin: 0; }

/* --- danger zone --------------------------------------------------------
   Permanent destructive actions, in their own block at the end of the page,
   each stating its consequence. The border is red; the buttons keep the
   existing .danger treatment. */
.danger-zone{
  border: 1px solid hsl(8 60% 73% / .28); border-radius: var(--radius-lg);
  background: hsl(8 60% 73% / .03); padding: 20px; margin: 36px 0 0;
}
.danger-zone h2{ margin: 0; font-size: 15px; color: var(--negative); }
.danger-zone .section-desc{ margin-top: 6px; }
/* One row per action: what it does on the left, the button on the right. */
.danger-action{
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px;
  padding: 14px 0 0; margin: 14px 0 0; border-top: 1px solid hsl(8 60% 73% / .18);
}
.danger-action-text{ flex: 1 1 280px; min-width: 0; }
.danger-action-title{ margin: 0; font-size: 13px; color: var(--text); }
.danger-action-desc{ margin: 4px 0 0; font-size: 12.5px; color: var(--text-muted); max-width: 68ch; }
.danger-action form{ margin: 0; }

/* --- editor disclosure --------------------------------------------------
   Grants and product roles are consulted far more often than they are edited.
   The read summary is always visible and the editor is behind an explicit
   disclosure, so a 300-checkbox form is not part of the identity's basic data.
   <details> keeps that entirely on the server: no script, no extra route. */
.editor-disclosure{
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--s2); margin: 0 0 16px;
}
.editor-disclosure > summary{
  cursor: pointer; list-style: none; padding: 11px 16px; min-height: 40px;
  display: flex; align-items: center; gap: 8px;
  font-size: 12.5px; color: var(--text-soft);
  transition: color .12s ease;
}
.editor-disclosure > summary::-webkit-details-marker{ display: none; }
.editor-disclosure > summary:hover{ color: var(--text); }
/* The marker is drawn in text, not by the user agent, so it says what the
   control does in both states. */
.editor-disclosure > summary::after{ content: "Abrir"; margin-left: auto; color: var(--text-muted); }
.editor-disclosure[open] > summary::after{ content: "Fechar"; }
.editor-disclosure[open] > summary{ border-bottom: 1px solid var(--border-subtle); }
.editor-disclosure > .form{
  background: none; border: 0; border-radius: 0; margin: 0; padding: 16px;
  max-width: none;
}

/* --- read-mode lists ----------------------------------------------------
   The compact state of an authorization vector: the codes an identity holds,
   as data. No form controls, no descriptions competing with the values. */
.chip-list{
  display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px; padding: 0; list-style: none;
}
.chip{
  display: inline-flex; align-items: baseline; gap: 6px;
  border: 1px solid var(--border-subtle); background: var(--s2);
  border-radius: var(--radius-sm); padding: 4px 9px; font-size: 12px;
  color: var(--text-soft);
}
.chip code{ color: var(--text); font-variant-numeric: tabular-nums; }
.chip span{ color: var(--text-muted); }

/* Timestamps and counts are read down a column, so they are tabular wherever
   they appear, not only inside tables and definition lists. */
.num{ font-variant-numeric: tabular-nums; }
/* family-head: multi-identity header, one card per family member */
.family-head{ padding: 18px 20px 10px; }
.family-head .hint{ margin: 0 0 14px; }
.family-head .resource-card{
  background: var(--s0); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); padding: 14px 16px; margin: 0 0 8px;
}
.family-head .resource-card .resource-title-row h1{ font-size: 14px; }
.family-head .resource-card dl.resource-meta{ margin: 10px 0 0; gap: 8px 16px; }
/* The identity the page is currently describing. The badge inside the card
   already names the state in words; this only makes the card findable at a
   glance in a stack of siblings that are otherwise identical. */
.family-head .resource-card.is-open{
  background: var(--s1); border-color: var(--border-strong);
}
/* The row holding a sibling card's "open this identity" link. It is the only
   control in the header, so it is spaced off the definition list rather than
   floated into it, and the link is padded to a 40px target instead of being
   the height of its own text. */
.family-head .resource-card .card-actions{ margin: 12px 0 0; }
.family-head .resource-card .card-actions > a{
  display: inline-flex; align-items: center; min-height: 40px; padding: 0 2px;
}

/* family-group: one person on the identities list — the family header card,
   then that person's identities in the table directly beneath it. The pair is
   one block, so the header loses the standalone bottom gap .resource-head
   carries and the table loses its own; only the group is spaced from the next
   person. The parent is a grouping record, never an account, so it borrows the
   header card the detail page already uses instead of any styling of its own. */
.family-group{ margin: 0 0 24px; }
.family-group > .family-head{ margin: 0 0 8px; }
.family-group > .family-head .resource-title-row h2{ margin: 0; font-size: 15px; }
.family-group > .table-wrap{ margin: 0; }