/* Nucleus has one visual language and every page an employee can reach uses it.
   There is no supplied Fuse brand, so this was settled by review rather than
   discovered: the operator opened the visual proposal, chose the dark direction,
   and accepted it. That proposal was a process artifact and was deleted once the
   look was applied -- git history holds it, and this file is the only place the
   visual language is defined. The decisions are recorded as custom properties,
   because the previous stylesheet held the same colours as repeated literals and
   that is how the pages drifted apart in the first place.

   Both appearances live here. Dark is what a reader gets until they choose,
   and `theme.js` sets the attribute the light block below hangs off.

   The shape of it: Nucleus is a data core, so the geometry is square, the rules
   are hairlines, nothing floats on a shadow, and anything that is an identifier
   or a control is set in monospace -- a page should read as an instrument. */

:root {
  /* Surfaces, back to front. */
  --page: #0c1116;
  --card: #121920;
  --sunk: #0a0e13;
  --grid: #151d25;

  /* Ink, loudest to quietest. */
  --ink: #e6edf3;
  --muted: #90a1b2;
  /* 4.6:1 on a card. It was #64748b, 3.7:1, below the 4.5:1 small text needs
     (operator, 2026-10-30). */
  --faint: #748397;

  /* Hairlines. --line separates, --rule divides a page. */
  --line: #222c36;
  --rule: #2c3844;

  /* One accent. It marks the live thing on a page and nothing else. */
  --accent: #35d6f5;
  --accent-ink: #06121a;

  /* States. Warning is the wildcard-origin consequence; danger is a refusal. */
  --warning: #f0b429;
  --warning-sunk: #2a2113;
  --danger: #ff8a8a;
  --danger-sunk: #2b1618;

  /* What a modal dims behind it. A token rather than a literal because it is
     the one colour that has to change with the appearance and is not on any
     surface: the same near-black over a light page reads as a fault. */
  --backdrop: rgb(3 6 9 / .72);

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --radius: 3px;

  /* The scales. Every size, space and weight below this line is one of these
     steps. They are short on purpose: the stylesheet they replaced carried
     eighteen font sizes -- twelve of them between .70rem and .95rem -- and
     twenty-eight spacing values, and that is how two pages written months
     apart stopped agreeing. A component that wants a value this scale does not
     carry is a change to the scale, made once, and not a number chosen here.
     `web/styles.test.js` refuses a literal that is not a step. */

  /* Space, on a 4px base. --space-4 is the resting gap between related things,
     --space-5 the padding inside a region, --space-6 the page gutter. */
  --space-1: .25rem;
  --space-2: .5rem;
  --space-3: .75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;

  /* Type. --text-md is body copy; everything smaller is a label, a hint or an
     instrument reading, and everything larger is a heading. */
  --text-2xs: .6875rem;
  --text-xs: .75rem;
  --text-sm: .875rem;
  --text-md: 1rem;
  --text-lg: 1.25rem;
  --text-xl: 1.625rem;
  --text-2xl: 2rem;

  /* Two weights. The sheet this replaces used 600, 620, 640 and 650, which no
     reader can tell apart and every author had to guess between. */
  --weight-normal: 400;
  --weight-strong: 640;

  /* How wide a line of prose is allowed to get. The integration guide set 41
     paragraphs at a median 138 characters a line, which is roughly twice a
     readable measure; a wide page is fine, a wide paragraph is not. `ch` is
     the width of a zero, which in this face is narrower than the average
     letter, so the step is smaller than the character count it buys: 62
     lands real prose around 72 characters. */
  --measure: 62ch;

  /* How wide the label column of a description list is. Each list sized its own
     column to its longest label, so one app's page drew its values starting at
     four different distances from the left -- 286, 353, 359 and 463 pixels --
     and a reader moving down the page had to find the column again in every
     region. One column for every list lines them up. It is set by the labels
     the registration carries: every one of them fits on a line at this width
     except "Connection completion return URLs", which wraps to two rather than
     pushing every other list sixty pixels wider for its sake. */
  --label-column: 14rem;

  /* Retained because it is the historical name for the resting gap and is
     still referenced where a grid's gap is the same decision as a stack's. */
  --gap: var(--space-4);

  /* The copy icon, for the one place a script cannot draw it: the renderer's own
     copy buttons, which are drawn with a mask. A mask reads only coverage, so
     the stroke named here is not a colour anyone sees. */
  /* The arrow on a select, drawn in the muted ink so it follows the appearance.
     Two half-filled squares make the chevron: no image, and no colour of its own. */
  --select-arrow: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);

  --copy-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5.5' y='5.5' width='8' height='8' rx='1.5'/%3E%3Cpath d='M10.5 3.5v-.5a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 3v5A1.5 1.5 0 0 0 4 9.5h.5'/%3E%3C/svg%3E");

  color-scheme: dark;
  font-family: ui-sans-serif, system-ui, sans-serif;
  color: var(--ink);
  background: var(--page);
}

/* The light counterpart. Same system, same geometry, same tokens -- only the
   surface and ink swap, so nothing below this block knows which one it is
   drawing. `theme.js` sets the attribute before the first paint.

   These are the values from the light half of the visual review, with two
   deliberate departures. `--faint` is darker than the reviewed #8a97a4, which
   falls to 2.8:1 on the light page and is used for the eyebrow, the section
   heading and the optional marker -- small text that has to stay readable, and
   the appearance rules say so in as many words. The four state colours had no
   light half to recover: warning and danger were added to the dark palette
   after the review, so these are derived and have not been seen by the
   operator. */
:root[data-theme="light"] {
  --page: #f7f9fb;
  --card: #ffffff;
  --sunk: #f0f3f7;
  --grid: #e3e9f0;

  --ink: #0f1720;
  --muted: #5b6a79;
  --faint: #64748b;

  --line: #dde3ea;
  --rule: #c8d2dc;

  --accent: #0b5cff;
  --accent-ink: #ffffff;

  --warning: #8a5700;
  --warning-sunk: #fdf3e0;
  --danger: #b3261e;
  --danger-sunk: #fdecea;

  --backdrop: rgb(15 23 32 / .4);

  color-scheme: light;
}

/* ---------------------------------------------------------------------------
   Base.

   Three rules here are the foundation the rest rests on, and each was added
   because its absence had already broken a page.

   `box-sizing` is declared for everything. Without it a width and a padding
   add up, which is how the front door -- the one page every employee sees
   first -- overflowed a 360px phone: `.door-card` asked for `width: 100%` and
   then added `2rem` of padding outside it. That fault had already been found
   and patched on the credential page alone, which is the shape of every defect
   in this stylesheet's history: fixed where it was noticed, left everywhere
   else.

   Text wraps at word boundaries. The sheet this replaces set
   `overflow-wrap: anywhere` on `body`, so every element's minimum content
   width became one character -- and a flex or grid item is allowed to shrink
   to its minimum. That is why the administrator queue's own heading drew 11.7
   pixels wide and 181 tall, one letter per line, beside its filter. Long
   identifiers still have to break, so the rule is applied to the things that
   carry them rather than to the document.

   Prose is set to a measure. A container may be as wide as the screen; a line
   of text may not. */
*, *::before, *::after { box-sizing: border-box; }

body { margin: 0; }
/* The UA sheet's [hidden] { display: none } is user-agent origin, so any author
   `display` below beats it -- `form` and `label` both set one. Without this a
   hidden element stays on screen, which is how an employee who owned no app was
   still shown the app list. */
[hidden] { display: none !important; }

/* What may break mid-word, because it is an identifier, an address or a value
   a reader will copy: breaking it is worse than overflowing. */
code, dd, .value, .chosen-name, .request-example,
.operation-address, .app-owner, .queue-subject { overflow-wrap: anywhere; }

h1, h2, h3, h4, p { margin-top: 0; }
h1, h2, h3, h4 { letter-spacing: -.018em; line-height: 1.18; font-weight: var(--weight-strong); }
h1 { font-size: var(--text-xl); }
h2 { font-size: var(--text-lg); }
h3 { font-size: var(--text-md); }
h4 { font-size: var(--text-sm); }
p { line-height: 1.6; }
a { color: var(--accent); text-underline-offset: .18em; }
code { font-family: var(--mono); font-size: .9em; color: var(--muted); }

/* The measure. It is set on the text itself rather than on a wrapper so that a
   page cannot opt out of it by forgetting a class, and lifted on the few
   elements whose job is to fill a cell or a row. */
p, li { max-width: var(--measure); }
dd p, dd li, td p, th p, .value-row p,
.impact p, dialog p, .badge, .callout p:only-child { max-width: none; }

/* ---------------------------------------------------------------------------
   Layout primitives. Four, and every page is built out of them.

   `.page-width` is the one measurement that says how wide Nucleus is.
   `.stack` gives a run of blocks one rhythm instead of each one carrying its
   own margin. `.cluster` is a row of things that wraps. `.rail` is an indented
   block hanging off a hairline. */
.page-width, header, main, .topbar, .mainnav, .wayfinding {
  margin: 0 auto; max-width: 72rem; width: 100%;
}
header, main { padding: var(--space-6); }
header {
  display: flex; justify-content: space-between; gap: var(--space-6);
  align-items: start; border-bottom: 1px solid var(--rule);
}
/* The intro block beside the header's own actions. It holds the heading, one
   sentence and nothing else -- the guide that put five paragraphs here is why
   the rule exists rather than the habit. */
header > div { min-width: 0; }

.stack { display: grid; gap: var(--space-4); }
.stack > * { margin-bottom: 0; }
.cluster { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; }
/* A row of buttons. One name; the six that said the same thing in slightly
   different numbers are kept as selectors so existing markup still lands here,
   and no new page needs them. */
.actions, .registration-actions, .impact-actions,
.environment-action, .integration-actions, .page-actions {
  display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center;
  margin: var(--space-4) 0 0;
}
.page-actions { margin-top: 0; }
/* An indented block hanging off a hairline: an environment under its source, a
   condition under its filter, a credential under its role, one need under the
   request that carries it. The need takes the same rail rather than a treatment
   of its own, because the questions inside it are the same questions the rest
   of the request asks and a border of their own would say otherwise. */
.rail, .environment-row, .filter-condition, .need {
  border-left: 2px solid var(--line); padding-left: var(--space-3);
}
.environment-row { margin-top: var(--space-4); }
/* A need's own press, under the questions it acts on rather than against the
   last of them. Nothing here is a form child, so the form's gap does not reach
   inside a need. */
.need > button { margin-top: var(--space-4); }
/* One need from the next. The needs are drawn into a wrapper, so the form's gap
   falls either side of the wrapper and never between them -- which left one
   need's Remove sitting against the next need's first question, and two needs
   reading as one. More than the gap between questions, because the boundary
   being drawn is a larger one: these are separate asks, not two parts of one. */
.need + .need { margin-top: var(--space-5); }

/* ---------------------------------------------------------------------------
   The shared shell: who you are across the top, where you can go under it.

   The bar and the row of destinations read as one block, and the hairline
   under the nav divides that block from the page. At a phone's width the
   identity row wraps under the brand and the destinations wrap to three rows,
   so the two need a boundary of their own -- without one the sign-out button
   sits directly on the first destination and the whole chrome reads as one
   undifferentiated mass. */
.topbar {
  align-items: center; display: flex; flex-wrap: wrap;
  gap: var(--space-3) var(--space-4); justify-content: space-between;
  padding: var(--space-4) var(--space-6) var(--space-3);
  /* The bar is a `<header>`, so the shared rule above hands it a bottom
     border. It is meant to read as one block with the destinations under it,
     and the comment here has claimed so since the sheet was written -- but
     the border was there the whole time, which left the row of destinations
     in a thin sliver between two rules. One rule, under the nav. The narrow
     width puts it back, where the bar really is a separate band. */
  border-bottom: 0;
}
.brand {
  color: var(--ink); font-family: var(--mono); font-size: var(--text-xs);
  font-weight: var(--weight-strong); letter-spacing: .2em; text-decoration: none;
  text-transform: uppercase;
}
.brand:hover, .brand:focus-visible { color: var(--accent); }
.topbar .subject:empty, .topbar .employee-name:empty { display: none; }
/* A subject is an instrument wherever it is drawn, not only in the bar. The
   credential page draws one where the directory named nobody, and a hexadecimal
   string set in the page's prose face reads as a word somebody meant. */
.subject { font-family: var(--mono); }
.identity {
  display: flex; gap: var(--space-4); align-items: center; color: var(--muted);
  font-family: var(--mono); font-size: var(--text-sm); flex-wrap: wrap;
}
/* A name is prose and a subject is an instrument, so the name takes the ink and
   the page's own face while .identity keeps a subject quiet and monospaced. The
   bar draws one or the other: the name where the directory gave one, and the
   subject only where it did not, so a nameless reader never looks named. */
.identity .employee-name {
  /* Named, not inherited: .identity above sets the monospace face, so inherit
     would take that and draw the name in the same face as the subject -- the
     one difference this rule exists to make. */
  color: var(--ink); font-family: ui-sans-serif, system-ui, sans-serif;
}

/* The destinations, in the order an arriving employee needs them. The current
   one is not marked by colour alone: it is the only one with the accent rule
   under it, the same way the trail marks its last step. */
/* The row of destinations is its own band inside the block, so it needs air
   above as well as below. With no top padding its text began on the exact
   pixel the bar's box ended -- nothing above it and thirteen pixels below --
   and the brand read as sitting on top of the destinations. The bottom is
   one step shorter than the top because each entry already reserves room
   under itself for the rule that marks the current one. */
.mainnav {
  border-bottom: 1px solid var(--rule); display: flex; flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  padding: var(--space-3) var(--space-6) var(--space-2);
}
.mainnav a {
  color: var(--muted); font-family: var(--mono); font-size: var(--text-xs);
  font-weight: var(--weight-strong); letter-spacing: .12em;
  padding-bottom: var(--space-1); text-decoration: none; text-transform: uppercase;
}
.mainnav a:hover, .mainnav a:focus-visible { color: var(--accent); }
.mainnav a[aria-current] { border-bottom: 2px solid var(--accent); color: var(--ink); }

/* The appearance choice. It is a preference, not a feature, so it sits in the
   corner at the size of one: an icon naming where it goes -- a sun in dark,
   a moon in light -- with its name carried in text for anyone who cannot see
   which icon is drawn. */
.appearance-choice {
  background: transparent; border: 0; border-radius: var(--radius);
  color: var(--faint); cursor: pointer; display: inline-flex;
  padding: var(--space-1); width: auto;
}
.appearance-choice:hover, .appearance-choice:focus-visible { color: var(--accent); }
.appearance-choice svg {
  fill: none; height: 1.05rem; stroke: currentColor; stroke-linecap: round;
  stroke-width: 1.4; width: 1.05rem;
}
.appearance-choice .icon-moon { display: none; }
:root[data-theme="light"] .appearance-choice .icon-sun { display: none; }
:root[data-theme="light"] .appearance-choice .icon-moon { display: block; }
/* The front door and the credential page have no bar to put the appearance
   control in, so there it stands under the card's last line. */
.appearance { margin: var(--space-5) 0 0; }

/* Where you are within a destination. The bar above says which destination
   that is, so only the pages nested under one carry a trail. */
.wayfinding {
  padding: var(--space-5) var(--space-6) 0; display: flex; flex-wrap: wrap;
  gap: var(--space-2); align-items: baseline; font-family: var(--mono);
  font-size: var(--text-xs); font-weight: var(--weight-strong);
  letter-spacing: .12em; text-transform: uppercase;
}
.wayfinding a { color: var(--faint); text-decoration: none; }
.wayfinding a:hover, .wayfinding a:focus-visible { color: var(--accent); }
/* The step the reader is on is not a link, and does not rely on colour to say
   so: it is the only one with the accent rule under it. */
.wayfinding [aria-current="page"] {
  color: var(--ink); border-bottom: 2px solid var(--accent); padding-bottom: .15rem;
}
.wayfinding a + a::before, .wayfinding a + [aria-current="page"]::before {
  color: var(--faint); content: "/\00a0"; font-weight: var(--weight-normal);
}

/* The small label above a heading. Only the front door and the RentalWorks
   credential page carry one: they have no bar above them, so it is the only
   thing naming the product. */
.eyebrow {
  color: var(--faint); font-family: var(--mono); font-size: var(--text-xs);
  font-weight: var(--weight-strong); letter-spacing: .2em; text-transform: uppercase;
}

/* ---------------------------------------------------------------------------
   Regions and cards.

   A card marks a thing -- one registration in a list, one confirmation, one
   callout. A region is part of a page, and is marked by a rule and by space.
   Drawing every region as a card is what made a page of five identical boxes
   in which nothing was more important than anything else; the look the
   operator accepted is retained, its repetition is not. */
.section { margin-bottom: 0; }
/* A region that follows a wrapper a script fills is not a sibling of the
   regions inside it, so it missed this spacing entirely and sat flush against
   the last row's own border -- a heading hard up against a hairline, where
   every other region on the page had a gap. `:not(:empty)` because the wrapper
   holds nothing until its reading arrives, and the gap would then be under the
   page header with nothing above it. */
.section ~ .section, #sources:not(:empty) ~ .section {
  margin-top: var(--space-6); padding-top: var(--space-5);
}
/* The rule itself is only for a region following a region. A region following
   the wrapper needs none: a list of rows already ends in the row's own border,
   so drawing one here puts two lines a gap apart at the same seam. */
.section ~ .section { border-top: 1px solid var(--line); }
.section > :last-child { margin-bottom: 0; }
/* A subheading inside a region needs distance above it, or it reads as a
   caption for what it follows rather than a heading for what comes next. The
   group heading sets its own and keeps it. */
.section > h3:not(.section-heading) { margin-top: var(--space-5); }
/* A label is a grid and carries no margin of its own, so two stacked pickers
   sat flush against each other and a search box sat flush against the button
   above it. Anything following something else in a region keeps the resting
   gap. */
.section > * + label { margin-top: var(--space-4); }

.panel {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); margin-bottom: var(--space-5);
  padding: var(--space-5);
}
.panel > :last-child { margin-bottom: 0; }
/* A heading and whatever sits opposite it -- a state badge, a filter. Both
   items may shrink, but neither below its own content, which is what stops a
   heading collapsing into a column of letters beside a wide control. */
.app-heading {
  display: flex; justify-content: space-between; gap: var(--space-4);
  flex-wrap: wrap; align-items: baseline;
}
.app-heading > * { min-width: 0; }
.app-heading h2, .app-heading h3 { margin-bottom: 0; }
/* The wrapper takes the heading's own distance from what follows. The rule
   above removes the heading's margin so it lines up with what sits opposite it,
   and nothing gave that distance back -- so on the audit log and the evidence
   page, where a region's heading is wrapped in case something sits beside it,
   "Narrow it" and "Requests" sat directly on the first field's label, while
   the unwrapped "Time window" above them kept its gap. */
.section > .app-heading { margin-bottom: var(--space-4); }

/* ---------------------------------------------------------------------------
   Callout: one component, three states. It was five near-copies differing only
   in padding, one of them missing its corner. */
.callout, .error, .sign-in-refusal, .revocation-run {
  border-left: 2px solid var(--line); border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--sunk); padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
}
.callout > :last-child,
.sign-in-refusal > :last-child, .revocation-run > :last-child { margin-bottom: 0; }
/* Something is wrong and the reader can fix it. The app's sign-in needs
   changing and the callout says how. */
.callout--warning, .sign-in-refusal {
  background: var(--warning-sunk); border-left-color: var(--warning);
}
/* Something is wrong and the reader cannot fix it, or a value was refused. */
.callout--danger, .error, .revocation-run {
  background: var(--danger-sunk); border-left-color: var(--danger);
}
/* A stop that has been lifted is history rather than a warning. The panel
   stays -- the run is the whole of what an owner is ever told -- but an app
   that is working again does not lead its card in the colour reserved for one
   that is not. */
.callout--quiet, .revocation-run.lifted {
  background: var(--sunk); border-left-color: var(--line);
}
.error { color: var(--danger); font-weight: var(--weight-strong); }
.sign-in-refusal h3, .revocation-run h3 { margin-top: 0; }
.revocation-entries {
  display: grid; gap: var(--space-3); list-style: none; margin: var(--space-4) 0 0; padding: 0;
}
.revocation-entry { border-top: 1px solid var(--line); padding-top: var(--space-3); }
.revocation-heading { font-weight: var(--weight-strong); }
.revocation-when { color: var(--muted); font-size: var(--text-sm); }

/* ---------------------------------------------------------------------------
   Forms. Every question is the same wrapper, so no question can acquire a
   border, a grouping or a spacing its neighbours do not have. */
form { display: grid; gap: var(--space-4); }
/* A label is a block, not a grid. As a grid every child became its own row,
   so the required marker sat on a line of its own and stretched the full
   column -- a chip the width of the page saying "REQUIRED". Inline flow keeps
   the marker beside the name it qualifies. */
label { display: block; font-weight: var(--weight-strong); }
label > input, label > select, label > textarea { margin-top: var(--space-1); }
/* A control that lives inside its own label takes the line under its name,
   whatever the control is. This comment used to say that happened by itself
   because a control is block-level, and it is not: a control is inline-block,
   so one that fits beside its own name sits there. An input is `width: 100%`
   and usually did not fit; a select sized to its options, or an input capped
   at the measure on a wide screen, usually did. So one row of filters on the
   audit log drew "App", "Employee" and "Changed by" over their boxes and
   "Kind [Anything]" beside its box, and no two fields in the row began at the
   same height. A checkbox is the exception because its name follows it on the
   same line, which is what a toggle is. */
label > input:not([type="checkbox"], [type="radio"]), label > select, label > textarea { display: block; }
/* A label's click target is its words. A label that names a control sitting
   outside it is still a block, so its box ran the full width of the form and
   a click on empty space level with "A screenshot or an example file" -- two
   hundred pixels right of the last word -- opened a file dialog. A label that
   contains its own control keeps the full width, because the control inside
   it is what fills that width. */
label:not(:has(input, select, textarea)) { width: fit-content; }

/* One right edge. The control was bounded by the measure and the hint beside
   it was not, so every question ended in two different places and the column
   read as ragged. Bounding the question instead lets the control fill it
   exactly and wraps the hint at the same edge. */
.question { display: grid; gap: var(--space-1); max-width: var(--measure); }
/* A question answered by choosing rather than typing: the choices are a group,
   so the group is a fieldset and its name is the legend, drawn as the label
   every other question has. The fieldset's own frame is the browser's, not
   ours, and would make one question look unlike its neighbours. */
fieldset.question { border: 0; margin: 0; min-width: 0; padding: 0; }
.question > legend { font-weight: var(--weight-strong); margin-bottom: var(--space-1); padding: 0; }
/* The segmented choice answering a question rather than switching a reading:
   sized to its choices instead of stretched across the column, and as tall as
   the fields around it so each choice is a full target. Four choices fit a
   phone's dialog on one line at this padding; on anything narrower they wrap
   rather than carry the page sideways. */
.question > .log-view { flex-wrap: wrap; justify-self: start; }
.question > .log-view label { padding: var(--space-2); white-space: nowrap; }
.question input, .question select, .question textarea { max-width: none; }
/* The explorer's own controls, as a strip rather than a column. Stacked full
   width they pushed the operations -- the reason anyone opens the page -- most
   of a screen down, and a source picker holding the word "RentalWorks" has no
   use for six hundred pixels. */
.explorer-selection {
  align-items: end; display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-5);
}
.explorer-selection > label, .explorer-selection > div { margin: 0; }
/* A strip is a block of controls, and what follows it -- a second strip, the
   table it filters -- is a different block. Nothing separated them, so the
   evidence page's row of group-by toggles sat directly on the field names of
   the filters under it, and each Find button sat on the heading row of the
   table it fills. */
.explorer-selection + .explorer-selection, .explorer-selection + .table-scroll { margin-top: var(--space-4); }
.explorer-selection select { max-width: none; width: auto; }
.explorer-selection .app-toggle { align-items: center; display: flex; gap: var(--space-2); }
.explorer-selection .app-toggle input { width: auto; }
.explorer-selection #app-toggle-hint { flex-basis: 100%; margin: 0; }
/* The search takes what is left of the strip once the pickers have their
   width, so a long placeholder does not push the row to a second line. */
.explorer-selection #operation-search { min-width: 12rem; }
.explorer-selection label:has(#operation-search) { flex: 1 1 12rem; }

/* What this selection can and cannot do, on one line under the controls. It
   was four stacked paragraphs saying the connection, the app, the next step
   and a link to go and do it. */
.explorer-state {
  align-items: baseline; display: flex; flex-wrap: wrap;
  gap: var(--space-2) var(--space-4); margin-top: var(--space-4);
}
.explorer-state > * { margin: 0; max-width: none; }
.explorer-state > :empty { display: none; }
#app-state, #setup-status { color: var(--muted); font-size: var(--text-sm); }
#setup-link { font-size: var(--text-sm); }

/* The address a reader copies, on one line with its own control. */
#root-row {
  align-items: baseline; display: flex; flex-wrap: wrap;
  gap: var(--space-2) var(--space-3); margin-top: var(--space-4);
}
#root-row > span { color: var(--muted); font-size: var(--text-sm); }

input, select, textarea, button { box-sizing: border-box; font: inherit; }
input, select, textarea {
  background: var(--sunk); border: 1px solid var(--rule); border-radius: var(--radius);
  color: var(--ink); min-width: 0; padding: var(--space-2) var(--space-3); width: 100%;
  /* An explicit line height, so a select and an input beside it are the same
     height. Left at `normal` each resolves it from its own internals -- the
     select came out two pixels taller -- and two controls on one row that
     differ by two pixels read as a mistake, which is what they are. */
  line-height: 1.25;
  /* What a reader typed is body text. `font: inherit` above takes the weight
     along with the face, and a label is strong -- so every control that lives
     inside its own label drew its value in the heading weight: the audit log's
     dates, the explorer's "RentalWorks" and "Training", the evidence page's
     "Any" and "100", all as loud as the names above them. */
  font-weight: var(--weight-normal);
  /* A control is read the way a line of prose is read, so it stops where one
     would. Without this a source picker on a wide screen drew a 1500px
     dropdown holding the word "RentalWorks". A control that fills a cell of a
     grid is exempt below, because there the cell is the decision. */
  max-width: var(--measure);
}
.value-row input, .value-row select, .value-row textarea,
.filter-property input, .filter-property select { max-width: none; }
/* A select drawn by the stylesheet rather than by the platform. Safari keeps its
   own native select unless told not to, and ignores the padding and border every
   control is given -- so on the operator's own screen the audit log's Kind and
   Outcome drew shorter than the text boxes beside them, and their names sat lower
   than every other name in the row. The arrow the platform drew goes with it, so
   one is drawn here. */
select {
  -webkit-appearance: none; appearance: none;
  background-image: var(--select-arrow);
  background-position: calc(100% - 1.05rem) 52%, calc(100% - .75rem) 52%;
  background-repeat: no-repeat; background-size: .3rem .3rem, .3rem .3rem;
  padding-right: var(--space-6);
}
input:focus-visible, select:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
textarea { resize: vertical; }
/* A field a policy has made unavailable has to look unavailable. Left alone, a
   disabled control keeps full-contrast text and reads as broken rather than as
   deliberately switched off. */
input:disabled, select:disabled, textarea:disabled {
  background: transparent; border-style: dashed; color: var(--faint); cursor: not-allowed;
}
.field-unavailable, .field-help, .withheld-reason {
  color: var(--muted); font-size: var(--text-sm);
  font-weight: var(--weight-normal); line-height: 1.5;
}
/* A withheld act and the condition that withholds it, drawn as one thing.
   Every row of actions in this file is a flex row, so a reason left beside the
   control becomes another item in that row and drifts away from what it
   explains at the first width that wraps. This is the column that keeps them
   together; `form-ui.js` builds it and `aria-describedby` carries the same
   attachment to a reader who is not looking at the row. */
.withheld {
  align-items: flex-start; display: flex; flex-direction: column;
  gap: var(--space-1);
}
/* A block rather than shrink-to-fit, because a field inside it is `width: 100%`
   and would have nothing to be a hundred percent of. In a row of actions it is
   a flex item and sizes to its button either way, so one declaration serves
   both places the component appears. */
.withheld .withheld-reason { max-width: var(--measure); }
/* A withheld field keeps the width of the row it sits in, the way it had before
   anything wrapped it. `.value-line` is a flex row, so the wrapper lands there
   as an item and shrinks to its contents -- which drew the exact-origin field
   at 552px beside a 795px field that was merely not withheld, so switching the
   origin policy made the field jump narrower as it went unavailable. It takes
   the row the way the bare control did, and stretches its own children back to
   full width inside it. */
.value-row .withheld { align-items: stretch; flex: 1; min-width: 0; }
.field-error {
  color: var(--danger); font-size: var(--text-sm);
  font-weight: var(--weight-strong); line-height: 1.5; margin: 0;
}
.field-error::before { content: "\26A0\00a0"; }
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--danger); box-shadow: inset 0 0 0 1px var(--danger);
}
.required-note { color: var(--muted); }

/* The files a request will carry, each with its own way off the list. Empty is
   nothing at all rather than a gap under the control. */
.chosen-files { display: grid; gap: var(--space-2); list-style: none; margin: var(--space-1) 0 0; padding: 0; }
.chosen-files:empty { display: none; }
.chosen-files li {
  align-items: center; background: var(--sunk); border: 1px solid var(--line);
  border-radius: var(--radius); display: flex; gap: var(--space-3);
  justify-content: space-between; padding: var(--space-2) var(--space-3);
  max-width: none;
}
.chosen-name { font-size: var(--text-sm); }
.chosen-files button { font-size: var(--text-2xs); padding: var(--space-1) var(--space-2); }

/* ---------------------------------------------------------------------------
   Buttons. One definition, three variants. `.button` is the same control drawn
   on an anchor, which is what the two unstyled names -- `.action` on My apps
   and `.secondary-action` on app creation -- should have been: the primary
   action of a page drew as a plain link, and its cancel drew as an accent one,
   because neither name had a rule anywhere. */
button, .button {
  background: var(--accent); border: 1px solid var(--accent); border-radius: var(--radius);
  color: var(--accent-ink); cursor: pointer; display: inline-block;
  font-family: var(--mono); font-size: var(--text-xs); font-weight: var(--weight-strong);
  letter-spacing: .08em; padding: var(--space-2) var(--space-3);
  text-align: center; text-decoration: none; text-transform: uppercase; width: fit-content;
}
/* The front door has one action and nothing else on the page, so there the
   button is the size of a call to action rather than of a control. */
.door-action .button { font-size: var(--text-sm); padding: var(--space-3) var(--space-5); }
/* A secondary control is the quieter of two, not a disabled one. Drawn on the
   hairline that separates things and in the ink used for supporting prose, it
   read as unavailable. It takes the rule that divides a page and the ink of
   ordinary text: still plainly not the accent, still plainly pressable. */
button.secondary, .button.secondary {
  background: transparent; border-color: var(--rule); color: var(--ink);
}
button.secondary:hover, .button.secondary:hover { border-color: var(--accent); color: var(--accent); }
/* A control that destroys something is not the accent. The accent marks the
   live thing on a page, and removing a registration is not what the page is
   for. `.danger` was a second drawing of this same idea. */
button.destructive, .button.destructive, button.danger {
  background: transparent; border-color: var(--danger); color: var(--danger);
}
/* After the variants, so a destructive control that is unavailable reads as
   unavailable rather than as danger. A page disables a button for two reasons
   -- the act is not offered, and the act is in flight -- and this says the
   first. */
/* Dashed, because reduced contrast is what §spec:shared-ui-conventions already
   refused and a cursor is not a signal -- it is invisible until the reader is
   already pointing at the control, and absent on a phone. An unavailable field
   had this right from the start; this is the same vocabulary, so a reader
   learns "dashed means switched off" once. It is what tells a withheld control
   from `.secondary`, which is solid. */
button:disabled, button[aria-disabled="true"] {
  border-color: var(--line); background: transparent; color: var(--faint);
  border-style: dashed; cursor: not-allowed;
}
/* A button that is working, which is not a button switched off. It is disabled
   while it waits -- that is what stops a second press sending a second request
   -- but it is doing what the reader asked, so it keeps the accent and its
   label says what is happening. `aria-busy` carries the same fact to a screen
   reader.
   The second selector is the whole point and is not a duplicate. `setBusy`
   always switches the control off, so a working control matches
   `button:disabled` and `select:disabled` too -- and both of those are more
   specific than a bare attribute, whatever order they are written in. Written
   with only the first selector this rule lost every cascade it was written for
   and a working control drew identically to an unavailable one on every page,
   which is the one pair of states the console must never conflate. It looked
   right in the specimen only because the example there is not switched off. */
[aria-busy="true"], [aria-busy="true"]:disabled {
  background: transparent; border-color: var(--accent); border-style: dashed;
  color: var(--accent); cursor: progress;
}

/* A shortcut drawn as an icon. The operator decided on 2026-09-14 that shortcut
   actions -- copying a value is the commonest -- are small icons, and that a
   button spends words only where an icon could not say it. A copy control used
   to say "Copy" in words, on the reasoning that every copy control on a page
   should say what it does; four of them beside four values were most of a
   column, and what one does is still said, as its accessible name and its
   tooltip. The drawing is 14px and the target 24px, so it stays easy to press.
   Quiet until it is pointed at or focused, and a check for a moment once it has
   done its work. */
button.icon-button {
  align-items: center; background: transparent; border: 1px solid transparent;
  color: var(--faint); display: inline-flex; height: 1.5rem; justify-content: center;
  letter-spacing: normal; padding: 0; text-transform: none; width: 1.5rem;
}
button.icon-button:hover, button.icon-button:focus-visible { color: var(--accent); }
.icon-button svg {
  fill: none; height: .875rem; stroke: currentColor; stroke-linecap: round;
  stroke-linejoin: round; stroke-width: 1.5; width: .875rem;
}
.icon-button .icon-copied { display: none; }
button.icon-button.copied { color: var(--accent); }
.icon-button.copied .icon-copy { display: none; }
.icon-button.copied .icon-copied { display: block; }

/* ---------------------------------------------------------------------------
   Badge: a short state or condition, set as an instrument. One component;
   `.app-status`, `.requirement`, `.environment-condition` and `.code-status`
   were four drawings of it. */
.badge, .app-status, .requirement {
  background: var(--sunk); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--muted); display: inline-block; font-family: var(--mono);
  font-size: var(--text-2xs); font-weight: var(--weight-strong); letter-spacing: .1em;
  padding: var(--space-1) var(--space-2); text-transform: uppercase; white-space: nowrap;
}
/* A badge that opens the thing it marks -- the key behind a call in the audit
   log. It stays a badge rather than becoming a link in the prose sense, so it
   takes no underline; the thing it names is in its tooltip. */
a.badge { text-decoration: none; }
a.badge:hover { border-color: var(--rule); color: var(--ink); }
.badge--bare, .requirement.optional, .environment-condition, .code-status {
  background: transparent; border-color: transparent; color: var(--faint);
  font-family: var(--mono); font-size: var(--text-xs); font-weight: var(--weight-normal);
  letter-spacing: .1em; padding: 0; text-transform: uppercase;
}

/* ---------------------------------------------------------------------------
   Description lists: a label and its value. The label treatment is the one
   used for every small uppercase reading on a page. */
/* The label column is `--label-column` wide, not as wide as this list's own
   longest label, so every list on a page starts its values in one place. A
   label longer than the column wraps inside it rather than widening it --
   `min-content` is only the floor, the width of its longest word. */
dl { display: grid; grid-template-columns: minmax(min-content, var(--label-column)) 1fr; gap: var(--space-2) var(--space-4); margin-bottom: var(--space-4); }
dt {
  color: var(--muted); font-family: var(--mono); font-size: var(--text-xs);
  font-weight: var(--weight-strong); letter-spacing: .1em; text-transform: uppercase;
}
dd { margin: 0; }

/* Data access: one source, its environments beneath it. The nesting is the
   point -- app access and the acting owner's credential intersect at the
   environment, and separate cards hid that they were describing one source. */
.data-source > h3 { border-bottom: 1px solid var(--line); padding-bottom: var(--space-2); }
.environment-row h4 { margin: 0; }
/* The environment, its state beside it, and what to do about it at the end of
   the line. Spread evenly across the row, the state landed wherever the gap
   between a short name and a long link put it -- 400 pixels from Training on
   one row and 535 from Production on the next, owned by neither -- so a column
   of rows did not read down, and "Not ready" could be taken for a note about
   the link beside it. */
.environment-row .app-heading { align-items: baseline; gap: var(--space-3) var(--space-4); justify-content: flex-start; }
.environment-row .environment-action { margin: 0 0 0 auto; }

/* One registration, drawn the same for its owner and for an administrator. The
   row is the grid cell, so a value and the control that replaces it in edit
   occupy the same place -- the reader keeps the arrangement they just used to
   read the values, and reflow is identical either way. */
.registration-values { display: grid; grid-template-columns: minmax(min-content, var(--label-column)) 1fr; gap: var(--space-2) var(--space-4); }
.value-row { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; align-items: start; }
.value-row dd { display: grid; gap: var(--space-1); }
/* A list is held one address per line and is written into the value that way,
   so it is drawn that way. Collapsed as ordinary text, two browser origins drew
   as one line with a space in it -- which reads as a single address, and is
   exactly the value an owner copies out to somebody else. */
.value-row .value { overflow-wrap: anywhere; white-space: pre-line; }
/* An identifier in a registration is the value the row exists to show, not a
   name mentioned in passing, so it keeps the ink rather than the quiet colour
   `code` takes inside prose. */
.value-row code.value { color: var(--ink); }
.value-line { align-items: baseline; display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
.value-line .value { min-width: 0; }
.value-row .copy { flex: none; }
.value-row input, .value-row select, .value-row textarea { width: 100%; }
/* What a browser that will not copy is given instead: the exact message, on
   the page, selected. It is only ever there after a refusal, so it is styled
   as the thing to take rather than as part of the record above it. */
.copy-fallback {
  background: var(--sunk); border: 1px solid var(--line); border-radius: var(--radius);
  font-family: var(--mono); font-size: var(--text-sm); margin: var(--space-4) 0 0;
  overflow-wrap: anywhere; padding: var(--space-3); white-space: pre-wrap;
}
/* A secret shown once, at the moment it is issued. It is the one thing the
   reader came to take, so it sits in its own sunk box like the fallback above,
   with its copy control beside it rather than wrapped under a value too long
   for any row. */
.secret-once {
  align-items: start; background: var(--sunk); border: 1px solid var(--line);
  border-radius: var(--radius); display: grid; gap: var(--space-3);
  grid-template-columns: minmax(0, 1fr) auto; padding: var(--space-3);
}
.secret-once code { color: var(--ink); font-size: var(--text-sm); overflow-wrap: anywhere; }

/* ---------------------------------------------------------------------------
   Tables. A summary exists because five facts about one request line up with
   the same five about the next; anything that reflows per row hides exactly
   the comparison an administrator opened the page to make. So the table keeps
   its shape and scrolls inside its own region instead of carrying the page
   sideways with it. */
/* `position` is load-bearing. The last column's heading is a
   `.visually-hidden` span, which is `position: absolute` -- and an absolutely
   positioned element is clipped by a scroll container only when that container
   is its containing block. Without this the span resolved against the viewport
   instead, landed at the table's full width, and carried the whole document
   sideways while the table beside it scrolled correctly. */
.table-scroll { overflow-x: auto; position: relative; }
.queue { border-collapse: collapse; font-size: var(--text-sm); width: 100%; min-width: 34rem; }
.queue th, .queue td {
  border-bottom: 1px solid var(--line); padding: var(--space-3) var(--space-3) var(--space-3) 0;
  text-align: left; vertical-align: top;
}
.queue th {
  color: var(--muted); font-family: var(--mono); font-size: var(--text-xs);
  font-weight: var(--weight-strong); letter-spacing: .1em; text-transform: uppercase;
}
.queue tr:last-child td { border-bottom: 0; }
.queue-subject, .queue-asked { color: var(--muted); font-family: var(--mono); font-size: var(--text-sm); white-space: nowrap; }
.queue-needs { color: var(--muted); }
/* An identifier under the name it belongs to, in a cell that leads with the
   name: the same division the bar and the registered-apps list draw between a
   person and their subject. */
.queue td > .queue-subject { display: block; }
.queue-state { white-space: nowrap; }
.queue-filter { align-items: center; display: flex; gap: var(--space-3); font-weight: var(--weight-normal); }
.queue-filter select { width: auto; }

/* ---------------------------------------------------------------------------
   The audit log's filters. Every narrowing on one row, each a name over its
   control, and no buttons: a choice applies when it is made and a search as it
   is typed. It replaced three sections of the audit log and three more of the
   Evidence page, each with its own Apply, Find or Clear, which the operator
   folded into one log on 2026-09-14. */
.log-filters { align-items: end; display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-4); margin: 0; }
.log-filters > label, .log-filters > .check-menu { flex: 0 1 auto; margin: 0; }
.log-filters input[type="search"] { width: 12rem; }
.log-filters select { width: auto; }
.log-range { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-4); }
/* The filters are one region and the entries another, so a rule divides them. */
.log-filters-foot {
  align-items: baseline; border-bottom: 1px solid var(--line); display: flex; flex-wrap: wrap;
  gap: var(--space-2) var(--space-5); justify-content: space-between;
  margin: var(--space-3) 0 var(--space-5); padding-bottom: var(--space-4);
}
.log-filters-foot > * { margin: 0; }
/* The audit log has no line under its filters -- its reference opens from the
   list's heading -- so the rule sits on the list itself. */
.log-filters + .section { border-top: 1px solid var(--line); margin-top: var(--space-5); padding-top: var(--space-5); }
/* Source, Operation and Method, folded under More filters: stacked in its
   panel, the source menu opening in place inside it. */
.log-more { gap: var(--space-3); }
.log-more > label { margin: 0; }
.log-more input[type="search"], .log-more select { width: 100%; }
.log-more .check-menu-list { margin-top: var(--space-2); position: static; }
/* A reference is a different question, so it is a link beside the list's
   heading that opens to its box, across the heading's width, rather than a
   field in the row. */
.log-reference { border: 0; }
.log-reference[open] { flex-basis: 100%; order: 1; }
.app-heading:has(.log-reference[open]) > .log-heading-controls { flex-basis: 100%; }
.log-reference .disclosure-body input { max-width: 24rem; }
.log-reference > .disclosure-summary { color: var(--accent); font-size: var(--text-sm); padding: 0; }
.log-reference .disclosure-body { padding: var(--space-3) 0 0; }
.log-reference .disclosure-body > :first-child { margin-top: 0; }
.log-count { color: var(--muted); font-size: var(--text-sm); }
.log-heading-controls { align-items: center; display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-4); }
.log-heading-controls > a { font-size: var(--text-sm); }
/* The list's heading with the control that explains its kinds beside it. */
.log-title { align-items: center; display: flex; gap: var(--space-2); }
.icon-button .icon-help { height: 1rem; width: 1rem; }

/* List or totals: one choice between two readings of the same reads. The chosen
   one is filled in solid ink with the page's colour for its words, so it is told
   from the other at a glance and without colour -- the two differ in fill,
   contrast and weight. A faint tint and a bolder word was not enough to tell
   which was chosen. The accent stays for the live act. */
.log-view { border: 1px solid var(--rule); border-radius: var(--radius); display: inline-flex; }
.log-view label {
  color: var(--muted); cursor: pointer; font-size: var(--text-sm); font-weight: var(--weight-normal);
  padding: var(--space-1) var(--space-3); position: relative;
}
.log-view label + label { border-left: 1px solid var(--rule); }
.log-view input { height: 1px; margin: 0; opacity: 0; position: absolute; width: 1px; }
.log-view label:has(input:checked) { background: var(--ink); color: var(--page); font-weight: var(--weight-strong); }
.log-view label:not(:has(input:checked)):hover { color: var(--ink); }
.log-view label:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 1px; }

/* A checkbox menu: any combination of a short, grouped list, drawn like the
   selects beside it and opened in place. Show is a kind and its outcomes, so
   "refused sign-ins and failed reads" is two ticks; Source is a service and its
   environments. Every tick applies at once, behind the open menu. The closed
   control says the ticks in words, and stops at a width rather than pushing the
   row about as the reader ticks; the whole of it is the tooltip and the name. */
.check-menu { display: block; position: relative; }
.check-menu-label { display: block; font-weight: var(--weight-strong); }
.check-menu-control > summary {
  background-color: var(--sunk); background-image: var(--select-arrow);
  background-position: calc(100% - 1.05rem) 52%, calc(100% - .75rem) 52%;
  background-repeat: no-repeat; background-size: .3rem .3rem, .3rem .3rem;
  border: 1px solid var(--rule); border-radius: var(--radius); color: var(--ink); cursor: pointer;
  line-height: 1.25; list-style: none; margin-top: var(--space-1); max-width: 17rem; min-width: 12rem;
  overflow: hidden; padding: var(--space-2) var(--space-6) var(--space-2) var(--space-3);
  text-overflow: ellipsis; white-space: nowrap;
}
.check-menu-control > summary::-webkit-details-marker { display: none; }
.check-menu-control > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.check-menu-control[open] > summary { border-color: var(--accent); }
.check-menu-list {
  background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius); display: grid;
  gap: var(--space-4); left: 0; min-width: 16rem; padding: var(--space-4); position: absolute;
  top: calc(100% + var(--space-1)); z-index: 20;
}
.check-group { border: 0; display: grid; gap: var(--space-2); margin: 0; min-width: 0; padding: 0; }
.check-group label { align-items: center; cursor: pointer; display: flex; font-weight: var(--weight-normal); gap: var(--space-2); }
.check-group .check-group-all { font-weight: var(--weight-strong); }
.check-group .check-leaf { padding-left: var(--space-5); }
.check-menu input[type="checkbox"], .tick input[type="checkbox"] { accent-color: var(--accent); flex: none; height: 1rem; margin: 0; padding: 0; width: 1rem; }
/* A tick box with its words, for choosing rows to act on together -- the
   allowlist's operations to copy. The box is the checkbox menu's box, so the
   console has one; the words follow it on the line, as a toggle's do, and
   read as body text rather than as a field's name. Added for the environment
   copy (#86) and listed for the allowlist's design pass. */
.tick { align-items: center; cursor: pointer; display: inline-flex; font-weight: var(--weight-normal); gap: var(--space-2); }
/* A tick whose thing needs saying what it is -- a linked key in a confirmation,
   named and then described. The name keeps the line beside the box and the
   description sits under it, so a list of them reads down the names. */
.tick--described { align-items: baseline; }
.tick--described > span { display: grid; gap: var(--space-1); }

/* One line per entry with every column in view: what a line is leads its cell
   and its detail sits under it, and names and times do not wrap. */
.log-table { min-width: 0; }
.log-table td.log-when, .log-table td.log-who { white-space: nowrap; }
.log-kind { display: block; }
.log-call .log-kind { font-family: var(--mono); white-space: nowrap; }
.log-sub { color: var(--muted); display: block; font-size: var(--text-xs); }
/* An app in a row is its name, linked to the log narrowed to it, with the copy
   icon beside it. The identifier printed under every name was the widest thing
   in the table and was only ever wanted to paste somewhere else. */
.log-app { display: block; }
.log-app-line { align-items: center; display: flex; gap: var(--space-1); white-space: nowrap; }
/* What a browser that will not copy is given: the identifier on its own line,
   selected, where it cannot widen the column the name sits in. */
.log-app .copy-fallback-id { display: block; overflow-wrap: anywhere; white-space: normal; }
/* A detail can hold an address with nothing to break at, which held its column
   open and carried the table sideways. It breaks inside the address instead. */
.queue td.queue-needs { min-width: 12rem; overflow-wrap: anywhere; }
/* A totals row names its group and opens the reads behind it, so the name is
   the control: drawn as the link it behaves like. */
button.link-button {
  background: transparent; border: 0; color: var(--accent); font-family: inherit; font-size: inherit;
  font-weight: var(--weight-normal); letter-spacing: normal; padding: 0; text-align: left;
  text-decoration: underline; text-transform: none; text-underline-offset: .18em; width: auto;
}

/* The body a call sent, opened under the call in the audit log. It is what an
   app sent, exactly, so it keeps its own line breaks and spacing and breaks
   anywhere rather than widening the table; a long one scrolls in its own box
   instead of pushing the rest of the log off the page. */
.recorded-body {
  background: var(--sunk); border: 1px solid var(--line); border-radius: var(--radius);
  font-family: var(--mono); font-size: var(--text-sm); margin: 0; max-height: 24rem;
  overflow: auto; overflow-wrap: anywhere; padding: var(--space-3); white-space: pre-wrap;
}

/* ---------------------------------------------------------------------------
   Documents. The integration guide and the error reference are read rather
   than operated, so they carry the furniture a document needs: headings with
   real hierarchy, quoted blocks, code set apart from prose, and tips. They
   were written with that markup already -- blockquote, pre, code, table --
   and almost none of it was drawn, so a message the reader was meant to copy
   and send looked exactly like the paragraph above it.

   Everything here stays on the same scales as the rest of the product. A
   document is not a second visual language. */
.integration-guide table { width: 100%; border-collapse: collapse; margin-bottom: var(--space-4); }
.integration-guide th, .integration-guide td {
  padding: var(--space-3); border-bottom: 1px solid var(--line); text-align: left; vertical-align: top;
}
.integration-guide th {
  color: var(--muted); font-family: var(--mono); font-size: var(--text-xs);
  font-weight: var(--weight-strong); letter-spacing: .1em; text-transform: uppercase;
}

/* A step's own heading, and the sub-headings inside it. A document needs more
   distance above a heading than below it -- that gap is what groups a heading
   with the text it introduces rather than with the text it follows. */
.integration-guide h3 { margin: var(--space-6) 0 var(--space-2); }
.integration-guide h4 { color: var(--muted); margin: var(--space-5) 0 var(--space-2); }
.integration-guide > section > :first-child { margin-top: 0; }

/* Something to copy and send: the message to IT, the grant request. It is not
   a quotation, it is the payload, so it is set apart from the prose and left
   selectable as a block. The accent edge marks it as the thing to take. */
.integration-guide blockquote {
  background: var(--sunk); border-left: 2px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  margin: var(--space-4) 0; padding: var(--space-4);
}
.integration-guide blockquote > :last-child { margin-bottom: 0; }
/* A block the reader is told to send or run carries its own control, in the
   corner, out of the text's way. */
.integration-guide blockquote, .integration-guide pre { position: relative; }
.copy-block { position: absolute; right: var(--space-2); top: var(--space-2); }
.integration-guide blockquote p, .integration-guide blockquote li { max-width: none; }

/* A request, a response, a fragment of configuration. It keeps its own line
   breaks and scrolls inside its box rather than widening the page. */
.integration-guide pre {
  background: var(--sunk); border: 1px solid var(--line); border-radius: var(--radius);
  font-size: var(--text-sm); margin: var(--space-4) 0; max-width: none;
  overflow-x: auto; padding: var(--space-4); white-space: pre-wrap; overflow-wrap: anywhere;
}
/* After the shorthands above, not before: a `padding` shorthand resets every
   side, so room made for the copy control ahead of one is room taken straight
   back and a long line runs under the control. */
.integration-guide blockquote, .integration-guide pre { padding-right: var(--space-7); }
.integration-guide pre code { background: none; border: 0; color: var(--ink); font-size: inherit; padding: 0; }
/* Inline code is a name, not a sentence, so it reads as an object in the
   prose rather than as emphasis. */
.integration-guide :not(pre) > code {
  background: var(--sunk); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--ink); padding: 0 var(--space-1);
}
.integration-guide li { margin-bottom: var(--space-2); }
/* A placeholder the reader still has to replace, and a value the guide filled
   in for them. The difference is carried by weight and by the ink, not by
   colour alone -- and a filled one is set as the identifier it now is. */
.fillable { color: var(--muted); }
.fillable.filled { color: var(--ink); font-weight: var(--weight-strong); }

/* ---------------------------------------------------------------------------
   The error reference. A reader arrives holding one code, so each one is its
   own block with a visible edge: they scan for the code, stop, and read only
   that block. The status sits beside the code because the two are read
   together -- branching on either alone is what the reference exists to stop. */
.recovery-family, .error-code { border-top: 1px solid var(--line); padding-top: var(--space-3); margin-top: var(--space-3); }
.recovery-family:first-child, .error-code:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
.error-reading + .error-reading { border-top: 1px dashed var(--line); margin-top: var(--space-3); padding-top: var(--space-3); }
.reading-when { font-size: var(--text-sm); margin: 0 0 var(--space-1); }

/* ---------------------------------------------------------------------------
   A dialog: something answered over the page rather than beside it -- a
   confirmation against a report of what it reaches, or a short form the reader
   should not leave their page for. It is calm by default. The danger edge
   belonged to every dialog, so Reinstate, which hands access back, was drawn
   as a destruction; §spec:shared-ui-conventions says an act a single action
   reverses shares neither the confirmation's look nor its colour. Only a
   confirmation whose answer destroys something carries the edge now, and it
   is read off the answer itself, so the two cannot disagree. */
dialog {
  background: var(--card); border: 1px solid var(--rule);
  border-radius: var(--radius); color: var(--ink); max-width: 34rem;
  padding: var(--space-5); width: calc(100% - var(--space-6));
}
dialog.impact:has(button.destructive) { border-top: 2px solid var(--danger); }
dialog::backdrop { background: var(--backdrop); }
/* A dialog opened in place rather than modally sits in the flow of the page.
   Nucleus opens every dialog modally; the specimen draws them in place, so
   one of each can be read beside the rest of the system. */
dialog[open]:not(:modal) { margin: 0 0 var(--space-5); position: static; }
dialog dl { margin-bottom: var(--space-5); }
dialog h2 { font-size: var(--text-lg); margin-bottom: var(--space-4); }
dialog form { gap: var(--space-4); }
dialog.impact dl { margin-bottom: 0; }
/* The report form's actions sit under its fields like any form's, and its
   closing line once it is sent sits where they did. */
dialog .actions { margin-top: 0; }

/* Withdrawing a registration sits apart from the settings above it, because it
   is not another setting. */
.withdraw { border-top: 1px solid var(--line); padding-top: var(--space-4); }

/* The acts on a whole registration, one to a line: the control, and beside it
   what pressing it changes. It replaced a wrapping row of four buttons, three
   of them switched off with a reason hanging under each -- where a sentence
   landed depended on the window width, the one act that worked sat among three
   that did not, and nothing said what any of them did. Now only the acts the
   registration's state allows are drawn, so the line beside each control can
   be about its effect.

   The controls share a column, so they are one width and every sentence starts
   in the same place. At a phone's width the sentence goes under its control,
   and the control is only as wide as its words. */
.acts {
  border-top: 1px solid var(--line); column-gap: var(--space-4); display: grid;
  grid-template-columns: max-content minmax(0, 1fr); margin: var(--space-4) 0;
}
.act {
  align-items: baseline; border-bottom: 1px solid var(--line); display: grid;
  grid-column: 1 / -1; grid-template-columns: subgrid; padding: var(--space-3) 0;
}
.act > button { width: 100%; }
.act > p { margin: 0; max-width: var(--measure); }
.act-says { color: var(--muted); font-size: var(--text-sm); line-height: 1.5; }

/* ---------------------------------------------------------------------------
   Lists of things.

   A row: one thing of a kind, read across -- what it is, what state it is
   in, what you can do about it. Rows rather than cards, because a card gives
   each entry a weight it has not earned: two of them on Credentials put a
   pair of identical accent buttons above the name of the source they belonged
   to, so the page read from the action backwards. */
/* A switch: one control that both says a state and changes it.
   §spec:shared-ui-conventions had two acts that undo each other share one
   control whose verb follows the state -- Grant/Withdraw, Open/Close -- and the
   operator reversed that for settings on 2026-09-15. A verb button says what
   pressing it would do and leaves the state to a sentence beside it, so a list
   of them is read twice: once for the state, once for the act. A switch says
   the state in the same place it takes the act, which is what lets the sentence
   go and the row become one line.

   It is square, not a pill. The geometry the operator accepted is square and
   this is not the component to make an exception of; a rounded track here would
   be the only curve on the page.

   On is NOT the accent. "One accent, marking the live thing on a page and
   nothing else" is the rule, and a list where every open pair is accented
   spends it on state -- so the track fills with the ordinary foreground and the
   accent stays available for whatever is actually happening.

   Colour is not the only carrier either: the knob is at the other end, which is
   a position rather than a hue, and `aria-checked` carries it to a screen
   reader. A withheld switch needs no rule of its own -- it is a button, so it
   takes the dashed treatment every withheld control in the console shares. */
.switch {
  background: transparent; border: 1px solid var(--line); border-radius: var(--radius);
  display: inline-flex; align-items: center; flex: none;
  height: 1.125rem; width: 2rem; padding: 0; position: relative;
  /* Its own ink, and the knob is drawn in it. A button's colour is the ink for
     text on an accent fill, and this one has no fill -- taken as it comes, the
     knob was near-black on a transparent track in the dark appearance and
     near-white on one in the light, which is to say invisible in both. Found by
     opening the page; no check reads a computed colour against the surface
     behind it. Naming it here is also what makes the withheld and working
     states work for free: both shared rules set `color`, and the knob follows
     it. */
  color: var(--muted);
}
/* The knob says the state twice over and in neither case by hue: it is at one
   end or the other, and it is filled or hollow. So the switch survives being
   withheld, being busy and being read by somebody who cannot see either colour,
   and it needs no rule of its own for any of those -- it takes its colour from
   whatever the shared button rules set, which is the accent while it works and
   the faint ink while it is withheld. A rule here that coloured the knob per
   state would be the one thing this component must not do. */
.switch::after {
  background: transparent; border: 1px solid currentColor; border-radius: 1px;
  content: ""; height: .625rem; width: .625rem; position: absolute; left: 2px;
}
.switch[aria-checked="true"]::after { background: currentColor; left: auto; right: 2px; }

.rows { border-top: 1px solid var(--line); list-style: none; margin: 0; padding: 0; }
.row {
  align-items: baseline; border-bottom: 1px solid var(--line); display: flex;
  flex-wrap: wrap; gap: var(--space-2) var(--space-5); justify-content: space-between;
  max-width: none; padding: var(--space-4) 0;
}
.row-main { min-width: 0; }
.row-main > :last-child { margin-bottom: 0; }
.row-name { font-size: var(--text-md); margin: 0 0 var(--space-1); }
/* A row whose name is an act rather than a destination is drawn as the link
   beside it, which takes the heading's weight; the link button's own reset
   would draw it lighter than every name above it. */
.row-name button.link-button { font-weight: inherit; }
.row-state { color: var(--muted); margin-bottom: 0; }
.row-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; }
/* A row whose state is its control. With no sentence under the name there is
   one line to pad, and the resting gap around a two-line stack leaves a list of
   them looking half empty. A modifier on the shared row rather than a second
   row component, because it is the same arrangement at a different density. */
.row--compact { align-items: center; padding: var(--space-3) 0; }
.row--compact .row-name { margin-bottom: 0; }

/* A heading that names a group rather than a section of the page. It is the
   label treatment, so it must sit on the heading level its content occupies
   and not be borrowed to make an h3 look like an h2. */
.section-heading {
  border-bottom: 1px solid var(--line); color: var(--faint); font-family: var(--mono);
  font-size: var(--text-xs); font-weight: var(--weight-strong); letter-spacing: .2em;
  margin: var(--space-6) 0 var(--space-3); padding-bottom: var(--space-2); text-transform: uppercase;
}

/* One need's decision, beside the need it is about: the state it moves to, the
   reason a closing one records, and the press that records both. A row of
   controls rather than a form, because this is an act taken on a row rather
   than an answer being composed -- the questions stack, an act sits on one
   line. The reason gets room: it is a sentence. */
.need-decision {
  display: flex; flex-wrap: wrap; gap: var(--space-3); margin: 0; align-items: end;
}
.need-decision label { font-size: var(--text-sm); font-weight: var(--weight-normal); }
.need-decision input { width: 11rem; }

/* An attachment is shown at the size it was taken, up to the width of the
   card. A screenshot scaled to a thumbnail is a screenshot nobody can read,
   which would put the administrator back in the terminal this page replaced. */
.attachments { display: grid; gap: var(--space-5); list-style: none; margin: 0; padding: 0; }
.attachments img { background: var(--sunk); border: 1px solid var(--line); border-radius: var(--radius); display: block; height: auto; max-width: 100%; }
.attachments p { margin-bottom: var(--space-2); }
.attachments li { max-width: none; }

/* Accumulated demand. One block per provider, most asked first, because the
   question this answers is "what do we keep being asked for" and that is an
   ordering rather than a search. */
.demand { display: grid; gap: var(--space-5); list-style: none; margin: 0; padding: 0; }
.demand > li { max-width: none; }
.demand h3 { margin-bottom: var(--space-1); }
.demand > li > p { margin-bottom: var(--space-2); }
.demand ul { color: var(--muted); font-size: var(--text-sm); margin: 0; padding-left: var(--space-4); }
.demand ul li { margin-bottom: var(--space-1); }

/* Off the screen, still in the accessibility tree: a column of buttons needs
   no visible heading, and a heading nobody can reach is not a table. */
.visually-hidden { clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }

/* ---------------------------------------------------------------------------
   The front door and the credential page. Both stand alone on the grid -- the
   data core showing through -- and both are one card and nothing else. */
.front-door, .connection-page {
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 3.5rem 3.5rem;
  display: grid; place-items: center; min-height: 100vh; padding: var(--space-7) var(--space-5);
  /* The credential page is a `<main>`, so the shared rule above capped it at
     the width of a page of content and centred it -- and the grid, which is
     this element's own background, stopped at that cap with bare page either
     side of it. The grid is the backdrop of the whole viewport here, so this
     one is not a page of content. */
  max-width: none; width: 100%;
}
/* Two forms, because Connect and Cancel post to different addresses. Nothing
   separated them, so the way out sat against the action it undoes. */
.connection-page form + form { margin-top: var(--space-4); }
.door-card, .connection-page .panel {
  border-top: 2px solid var(--accent); margin: 0; max-width: 29rem;
  padding: var(--space-6); width: 100%; min-width: 0;
}
/* A card is narrower than a page, so a label column beside the values left
   them a third of it: a Key ID broke across lines with its copy control under
   it. Inside a card each label sits over its value, as every page does at
   phone width. */
.connection-page .panel .registration-values { grid-template-columns: 1fr; }
.door-card { background: var(--card); border: 1px solid var(--line); border-top: 2px solid var(--accent); border-radius: var(--radius); }
.door-card h1 { font-size: var(--text-2xl); margin-bottom: var(--space-3); }
.door-card .lede { font-size: var(--text-md); }
.door-card .door-note { color: var(--muted); font-size: var(--text-sm); margin-bottom: 0; }
.door-action { margin: var(--space-5) 0 var(--space-3); }

/* ---------------------------------------------------------------------------
   Cards for apps, and the messages a card carries. */
.app-card { margin-bottom: var(--space-5); }

/* ---------------------------------------------------------------------------
   Disclosure: one line, opened in place. Three surfaces want it and each had
   begun to grow its own -- the administrator's registrations, the read
   allowlist's paths, and the explorer's credential reference. A reader arrives
   at each looking for one entry among many, and a page that draws every field
   of every entry makes finding it a scroll.

   The line carries what you scan by. Everything else waits to be asked for. */
.disclosure { border-bottom: 1px solid var(--line); }
.disclosure-summary {
  align-items: baseline; cursor: pointer; display: flex; flex-wrap: wrap;
  gap: var(--space-2) var(--space-4); list-style: none; padding: var(--space-3) 0;
}
.disclosure-summary::-webkit-details-marker { display: none; }
/* The marker is drawn rather than taken from the user agent, which puts its
   own outside the flex row. */
.disclosure-summary::before {
  color: var(--faint); content: "\203A"; display: inline-block;
  flex: none; font-family: var(--mono); width: var(--space-3);
}
[open] > .disclosure-summary::before { transform: rotate(90deg); }
.disclosure-summary:hover, .disclosure-summary:focus-visible { color: var(--accent); }
.disclosure-summary h2, .disclosure-summary h3 { font-size: var(--text-md); margin: 0; }
/* The state sits at the end of the line, away from the name, so a column of
   them reads down. */
.disclosure-summary .app-status { margin-left: auto; }
.disclosure-body { padding: 0 0 var(--space-5) var(--space-5); }
.disclosure-body > :last-child { margin-bottom: 0; }
/* An opened card is read in regions -- name and purpose, data access,
   secrets -- and a region's heading needs distance from the list the region
   before it ended on, or it reads as that list's last row. */
.disclosure-body > section + section { margin-top: var(--space-5); }
#app-list { border-top: 1px solid var(--line); }
details.app-card { background: none; border: 0; border-radius: 0; margin: 0; padding: 0; }
/* The owner's name, or their subject in .app-owner below where Nucleus has no
   name for them. Same division as the bar's: a name is prose in ink, a subject
   a quiet monospaced identifier, so a reader can tell which they are reading. */
.disclosure-summary .owner-name { color: var(--ink); }
.disclosure-summary .owner-name:empty, .disclosure-summary .app-owner:empty { display: none; }
.disclosure-summary .app-owner {
  color: var(--muted); font-family: var(--mono); font-size: var(--text-sm);
}
.app-message:empty { display: none; }
.app-message { margin: var(--space-3) 0 0; }
.activation-guidance, .repair-explains, .removal-note, .revocation-note,
.naming-status, .settings-status, .withdraw-status, .refusal-provenance,
.refusal-time, .revocation-lead { color: var(--muted); font-size: var(--text-sm); line-height: 1.5; }
.app-conditions { margin-bottom: 0; }

/* A refused entry, said in both places the reader might be looking: beside the
   field, and in a summary at the top of the form that links to it. Nothing
   here is carried by colour alone -- the summary is a heading in words, the
   field's own message is text behind a marker, and the control carries
   `aria-invalid` and a doubled border rather than only a red one. */
.refusal-reading + .refusal-reading { border-top: 1px dashed var(--line); margin-top: var(--space-3); padding-top: var(--space-3); }
.refusal-heading { font-size: var(--text-md); margin-bottom: var(--space-1); }
.refusal-remedy { font-weight: var(--weight-strong); }

/* ---------------------------------------------------------------------------
   At a phone's width a label sits above its value rather than beside it, and
   nothing scrolls sideways to be read. */
@media (max-width: 44rem) {
  /* The log's entries stack: when and how it ended on the first line, then
     each other cell on its own, so nothing is out of view. The headings stay
     for screen readers. */
  .log-table thead { clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }
  .log-table, .log-table tbody { display: block; }
  .log-table tr {
    border-bottom: 1px solid var(--line); display: grid; gap: var(--space-1) var(--space-3);
    grid-template-columns: 1fr auto; padding: var(--space-3) 0;
  }
  .log-table tr:last-child { border-bottom: 0; }
  .log-table td { border: 0; padding: 0; }
  .log-table td.log-when { grid-column: 1; grid-row: 1; }
  .log-table td.log-outcome { grid-column: 2; grid-row: 1; text-align: right; }
  .log-table td:not(.log-when):not(.log-outcome) { grid-column: 1 / -1; }
  .log-table td.log-took, .log-table td.log-reference-cell { color: var(--muted); }
  .log-call .log-kind { white-space: normal; overflow-wrap: anywhere; }
  header { display: block; }
  header > div + * { margin-top: var(--space-4); }
  dl, .registration-values, .value-row { grid-template-columns: 1fr; }
  /* The action has wrapped onto a line of its own here, where the end of the
     line is a thumb's width from everything it belongs to. */
  .environment-row .environment-action { margin-left: 0; }
  .log-filters input[type="search"], .log-filters select { width: 100%; }
  /* `min-width: 0`, or a menu whose closed line lists several ticks keeps
     that whole line as its floor and carries the page sideways. */
  .log-filters > label, .log-filters > .check-menu { flex: 1 1 100%; min-width: 0; }
  .check-menu-control > summary { max-width: none; width: 100%; }
  .check-menu-list { right: 0; }
  .acts, .act { grid-template-columns: minmax(0, 1fr); }
  .act { row-gap: var(--space-2); }
  .act > button { width: fit-content; }
  header, main { padding: var(--space-5) var(--space-4); }
  /* A dialog takes the phone's gutter rather than the desktop's, the same step
     the page around it takes here. At the desktop's it left the report form's
     four kinds a few pixels short of one line. */
  dialog { padding: var(--space-4); width: calc(100% - var(--space-5)); }
  /* At this width the identity row has wrapped under the brand and the
     destinations have wrapped to three rows, so the two stop reading as one
     block and start reading as one mass -- the sign-out button sits directly
     on the first destination. A hairline lighter than the one under the nav
     divides them without claiming to be the same kind of boundary. Above this
     width the bar deliberately carries no rule. */
  .topbar {
    padding: var(--space-4) var(--space-4) var(--space-3);
    border-bottom: 1px solid var(--line);
  }
  .mainnav { padding: var(--space-3) var(--space-4) var(--space-2); }
  .wayfinding { padding: var(--space-4) var(--space-4) 0; }
  .front-door, .connection-page { padding: var(--space-5) var(--space-4); }
}

/* ---------------------------------------------------------------------------
   The interactive contract. The renderer draws the operations and the answer
   they return; what is styled here is the filter's rows of conditions, which
   the renderer's own parameter presentation could not carry. */
.filter-condition {
  align-items: end; display: grid; gap: var(--space-4);
  grid-template-columns: 1fr max-content; margin-bottom: var(--space-3);
}
.filter-properties { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
/* One condition property: its label and control, and beneath them whatever the
   contract says about it. The note sits outside the label so it describes the
   box rather than becoming part of the box's name. */
.filter-property { align-content: start; display: grid; gap: var(--space-1); }

/* An absolute address is long and must not be truncated: a reader copying a
   shortened address gets a shortened address. It wraps instead. */
.operation-substitutions { margin-top: var(--space-1); }

/* The request is copied whole, so it keeps its line breaks and does not
   scroll: a reader has to be able to see what they are about to take. */
.request-example { font-family: var(--mono); font-size: var(--text-sm); margin: 0; white-space: pre-wrap; }

/* What Nucleus adds inside a rendered operation. */
.response-record { border-top: 1px solid var(--line); margin-top: var(--space-3); padding-top: var(--space-3); }
.response-record-name { font-family: var(--mono); font-size: var(--text-sm); margin-bottom: var(--space-1); }
.response-record-fields { color: var(--muted); font-size: var(--text-sm); margin: 0; padding-left: var(--space-4); }
.response-fields h4 { margin-bottom: var(--space-2); }
.suggested-names, .property-state { color: var(--muted); font-size: var(--text-sm); }

/* ---------------------------------------------------------------------------
   The vendored renderer, in Nucleus's appearance.

   Swagger UI ships one light theme and no way to ask for another, so the whole
   of both appearances is here, written against the same tokens as the rest of
   the product. Nothing below names a colour: the light and dark blocks at the
   top of this file are still the only place a colour is chosen, which is what
   keeps the explorer from being the one page that stops following an explicit
   appearance choice.

   Scoped to `.swagger-ui` so it reaches the renderer and nothing else. */
.swagger-ui { color: var(--ink); font-family: inherit; }
/* The renderer draws no document heading and no server list -- both are
   suppressed at the component -- but the empty band that held them is still
   drawn around them. */
.swagger-ui .scheme-container { display: none; }
/* The renderer's tag heading is not drawn. Every operation on the page belongs
   to the one selected destination, and the heading above them already names it
   -- with its environment, which the tag does not carry. Two headings saying
   the same thing, one of them less completely, is one heading too many. */
.swagger-ui .opblock-tag { display: none; }
/* The renderer draws its own markup inside Nucleus's, so every bare element
   rule of ours reaches whatever it draws. `<main>` was the one that showed:
   the operations took a second page gutter and a second width cap, sitting
   32px inside the section heading above them and stopping 32px short of its
   rule. `<header>` had not shown yet, which is worse rather than better -- it
   would have arrived as a centred, capped, bottom-ruled band the day the
   renderer drew one. Both are answered here, and `web/styles.test.js` refuses
   a page-layout element that is not. */
.swagger-ui main, .swagger-ui header {
  border: 0; display: block; margin: 0; max-width: none; padding: 0; width: auto;
}
.swagger-ui .wrapper { max-width: none; padding: 0; }

/* Nucleus draws every `<button>` on a page as a control of its own, and the
   renderer has a great many that carry none of its own classes: the schema
   expanders, the tag and operation controls, "Expand all". They came out as
   accent slabs with their labels set in the accent's own ink -- which in the
   light appearance is blue lettering on a blue block, and unreadable. Inside
   the renderer, a button the renderer has not claimed is the renderer's to
   draw; the ones it has claimed keep the appearance given them below. */
.swagger-ui button:not(.btn) {
  background: none; border: 0; border-radius: 0; color: inherit;
  font-family: inherit; font-size: inherit; font-weight: inherit;
  letter-spacing: normal; padding: 0; text-transform: none; width: auto;
}
.swagger-ui .model-box-control, .swagger-ui .models-control, .swagger-ui .opblock-summary-control { text-align: left; }
.swagger-ui .model-title, .swagger-ui .models-control span { color: var(--ink); }

/* An operation is a panel, like every other panel on the page. */
.swagger-ui .opblock { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: none; margin: 0 0 var(--gap); }
.swagger-ui .opblock.opblock-get { background: var(--card); border-color: var(--line); }
.swagger-ui .opblock .opblock-summary { border-bottom: 1px solid var(--line); }
.swagger-ui .opblock.opblock-get .opblock-summary { border-color: var(--line); }
.swagger-ui .opblock-summary-control:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.swagger-ui .opblock .opblock-summary-method { background: var(--accent); color: var(--accent-ink); border-radius: var(--radius); font-family: var(--mono); text-shadow: none; }
.swagger-ui .opblock .opblock-summary-path, .swagger-ui .opblock .opblock-summary-path__deprecated { color: var(--ink); font-family: var(--mono); }
.swagger-ui .opblock .opblock-summary-path a { color: inherit; }
.swagger-ui .opblock .opblock-summary-description { color: var(--muted); font-family: inherit; }

/* The bars that separate parameters from responses. */
.swagger-ui .opblock .opblock-section-header { background: var(--grid); border-bottom: 1px solid var(--line); box-shadow: none; }
.swagger-ui .opblock .opblock-section-header h4, .swagger-ui .opblock .opblock-section-header > label { color: var(--ink); font-family: inherit; }
.swagger-ui .opblock-title_normal, .swagger-ui .opblock-description-wrapper, .swagger-ui .opblock-external-docs-wrapper { color: var(--ink); font-family: inherit; }
.swagger-ui .opblock-description-wrapper p, .swagger-ui .renderedMarkdown p, .swagger-ui .markdown p { color: var(--muted); font-family: inherit; font-size: var(--text-sm); }

/* Tables: the parameter list and the response list. */
.swagger-ui table thead tr td, .swagger-ui table thead tr th { border-bottom: 1px solid var(--rule); color: var(--muted); font-family: inherit; font-size: var(--text-xs); letter-spacing: .04em; text-transform: uppercase; }
.swagger-ui table tbody tr td { border-top: 1px solid var(--line); color: var(--ink); padding: var(--space-3) var(--space-2); }
.swagger-ui .parameter__name { color: var(--ink); font-family: var(--mono); font-weight: var(--weight-strong); }
.swagger-ui .parameter__name.required::after { color: var(--danger); }
.swagger-ui .parameter__type, .swagger-ui .parameter__deprecated, .swagger-ui .parameter__in { color: var(--faint); font-family: var(--mono); }
.swagger-ui .response-col_status { color: var(--ink); font-family: var(--mono); }
.swagger-ui .response-col_links, .swagger-ui .response-col_description { color: var(--muted); }

/* Controls, matching the page's own. */
.swagger-ui select, .swagger-ui input[type="text"], .swagger-ui input[type="password"], .swagger-ui input[type="search"], .swagger-ui input[type="email"], .swagger-ui input[type="file"], .swagger-ui textarea {
  background: var(--sunk); border: 1px solid var(--rule); border-radius: var(--radius); box-shadow: none; color: var(--ink); font-family: var(--mono);
}
/* The shorthand above takes the renderer's own arrow away, so its selects take
   the one every other select draws. */
.swagger-ui select {
  background-image: var(--select-arrow);
  background-position: calc(100% - 1.05rem) 52%, calc(100% - .75rem) 52%;
  background-repeat: no-repeat; background-size: .3rem .3rem, .3rem .3rem;
  padding-right: var(--space-6);
}
.swagger-ui input::placeholder, .swagger-ui textarea::placeholder { color: var(--faint); }
.swagger-ui .btn { background: transparent; border: 1px solid var(--rule); border-radius: var(--radius); box-shadow: none; color: var(--ink); font-family: inherit; }
.swagger-ui .btn:hover { box-shadow: none; border-color: var(--accent); }
.swagger-ui .btn.execute { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
/* A control that will not run a read says why, and stays readable while it says
   it: a reason nobody can read is the same as no reason. */
/* The renderer draws its own buttons, so the rule above reaches them where no
   diff shows it. The answer is that it should: a control the explorer will not
   run is withheld in the same sense, and its label already says what is
   missing. It takes the dashed border deliberately rather than by inheritance,
   and keeps the readable ink -- a reason nobody can read is the same as no
   reason. */
.swagger-ui .btn.execute[disabled], .swagger-ui .btn[disabled] {
  background: transparent; border-color: var(--line); border-style: dashed;
  color: var(--muted); opacity: 1;
}
.swagger-ui .btn.cancel { border-color: var(--rule); color: var(--muted); }
.swagger-ui .btn:focus-visible, .swagger-ui a:focus-visible, .swagger-ui button:focus-visible, .swagger-ui input:focus-visible, .swagger-ui select:focus-visible, .swagger-ui textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.swagger-ui .try-out__btn { border-color: var(--rule); color: var(--ink); }

/* The renderer highlights every code block -- example values, schemas, the
   command it sent, the body it got back -- by writing a colour into a `style`
   attribute on each token. An attribute written by script is not something an
   author stylesheet can reach by being more specific, which is why this is the
   one place in the sheet that says `!important`: there is no other way to say
   it.
   
   The colours were chosen for the renderer's own light theme and fail on both
   of ours -- a pale green on a pale grey box, and the same green on a near
   black one. The tokens take the page's ink, which leaves them louder than the
   punctuation and keys around them: a value is the part a reader came for, so
   being the loudest thing in the block is right. */
.swagger-ui .microlight span[style], .swagger-ui .highlight-code span[style],
.swagger-ui .curl-command span[style], .swagger-ui .curl span[style],
.swagger-ui .response-col_description span[style], .swagger-ui .model-example span[style] {
  color: var(--ink) !important;
}

/* Code: the request, the answer and the schema, all set as instruments. */
.swagger-ui .microlight, .swagger-ui .highlight-code > .microlight { background: var(--sunk) !important; color: var(--ink) !important; font-family: var(--mono); }
.swagger-ui .highlight-code { background: var(--sunk); }
/* A wide result scrolls inside its own region rather than widening the page and
   pushing the controls beside it out of reach. */
.swagger-ui .highlight-code > .microlight { display: block; max-height: 28rem; overflow: auto; }
/* The control that opens an operation. The renderer sets `color: #000` on it,
   which on a dark page is very nearly the page itself -- an affordance drawn
   in the background colour. The whole summary is the click target, so the
   arrow answers to a hover anywhere along the row. */
.swagger-ui .opblock-control-arrow { color: var(--muted); }
.swagger-ui .opblock-control-arrow svg, .swagger-ui svg.arrow { fill: currentColor; }
.swagger-ui .opblock-summary:hover .opblock-control-arrow,
.swagger-ui .opblock-summary-control:focus-visible .opblock-control-arrow { color: var(--accent); }
/* The renderer's own copy control is not drawn. It copies the operation's
   path; Nucleus draws a Copy beside the absolute address inside the operation,
   which is the thing a reader actually pastes, and two controls copying two
   different strings from one row is worse than one copying the right one.

   It was also a control nobody could use on purpose: the renderer gives it no
   width until the row is hovered, so it appeared under the pointer on top of
   the arrow that opens the operation, and its icon carries `fill="#ffffff"` on
   the path itself -- white, and invisible on the light page. */
.swagger-ui .opblock .opblock-summary .view-line-link.copy-to-clipboard,
.swagger-ui .opblock .opblock-summary:hover .view-line-link.copy-to-clipboard { display: none; }
/* Every copy control the renderer draws is the same thing: an empty button
   whose picture is a background image its own theme supplies, which on either
   of Nucleus's surfaces comes out as a blank grey square. They draw the same
   copy icon every other copy control in the console draws, from a mask so the
   icon takes the ink of the control and follows the appearance. */
.swagger-ui .copy-to-clipboard, .swagger-ui .curl-command .copy-to-clipboard {
  background: none; border: 0; height: auto; width: auto;
}
.swagger-ui .copy-to-clipboard button, .swagger-ui .curl-command .copy-to-clipboard button {
  align-items: center; background: transparent; border: 0; border-radius: var(--radius);
  color: var(--faint); cursor: pointer; display: inline-flex; height: 1.5rem;
  justify-content: center; padding: 0; width: 1.5rem;
}
.swagger-ui .copy-to-clipboard button::after {
  background: currentColor; content: ""; display: block; height: .875rem; width: .875rem;
  -webkit-mask: var(--copy-icon) center / contain no-repeat; mask: var(--copy-icon) center / contain no-repeat;
}
.swagger-ui .copy-to-clipboard button:hover, .swagger-ui .copy-to-clipboard button:focus-visible { color: var(--accent); }
/* Nucleus's own icon buttons drawn beside an operation -- its address and its
   request -- are inside the renderer, so the rule above that hands every
   unclaimed button back to the renderer reaches them too and takes their size
   with it: they drew fourteen pixels wide, narrower than the icon's own target.
   They keep the size every icon button has. */
.swagger-ui button.icon-button:not(.btn) { color: var(--faint); height: 1.5rem; width: 1.5rem; }
.swagger-ui button.icon-button:not(.btn):hover, .swagger-ui button.icon-button:not(.btn):focus-visible,
.swagger-ui button.icon-button.copied:not(.btn) { color: var(--accent); }
/* The command the renderer sent is a block, so its control rides the top right
   of the box, clear of the code. The renderer puts it on the bottom edge, where
   it sat half outside the box with its label cut off. */
.swagger-ui .curl-command .copy-to-clipboard { bottom: auto; right: var(--space-2); top: var(--space-2); }
/* The code wraps clear of the control rather than behind it. */
.swagger-ui .curl-command pre, .swagger-ui .curl pre, .swagger-ui .highlight-code > .microlight { padding-right: var(--space-7); }
.swagger-ui .download-contents { background: var(--grid); color: var(--ink); }

/* Models and the response schema. */
.swagger-ui .model, .swagger-ui .model-title, .swagger-ui .prop-type, .swagger-ui .prop-format { color: var(--ink); font-family: var(--mono); }
.swagger-ui .model-toggle::after { background: none; }
.swagger-ui section.models, .swagger-ui .model-box { background: var(--grid); border-color: var(--line); }
.swagger-ui section.models h4 { color: var(--ink); }

/* The renderer lays its response table out wider than a phone, and the box it
   sits in does not scroll, so on a narrow screen the table carried the whole
   page sideways with it. It scrolls in its own region instead, which is what
   a two-dimensional result is allowed to do. */
.swagger-ui .responses-inner { overflow-x: auto; }

/* Tabs and the live response. */
.swagger-ui .tab li { color: var(--muted); font-family: inherit; }
.swagger-ui .tab li.active { color: var(--ink); }
.swagger-ui .responses-inner h4, .swagger-ui .responses-inner h5 { color: var(--ink); font-family: inherit; }
.swagger-ui .request-url pre { background: var(--sunk); color: var(--ink); }
/* The renderer's record of the request it actually sent, drawn after a read.
   Three things were wrong with it and all three come from its own theme, which
   was written for a light page: the code sat flush against the edge of its box
   with no padding at all, it was set in syntax colours -- green keywords and
   green strings -- that have no contrast on either of Nucleus's surfaces, and
   the control that copies it was an unreadable grey square.

   The colours go rather than get corrected. This is one command on one line
   wrapped; there is nothing here that syntax colour helps a reader tell apart,
   and two greens that both fail on the background are not worth keeping for
   the sake of having had them. */
.swagger-ui .curl-command, .swagger-ui .curl {
  background: var(--sunk); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--space-4); position: relative;
}
.swagger-ui .curl-command pre, .swagger-ui .curl pre { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.swagger-ui .curl-command, .swagger-ui .curl { font-family: var(--mono); }
/* A copy control is never given a line of its own. It sits beside what it
   copies when that is one line, and in the corner when that is a block -- the
   address and the request examples are the second kind, so the control rides
   the top right of the box rather than sitting under it waiting to be mistaken
   for the next thing to read. */
.operation-extras .value-row {
  display: grid; grid-template-columns: 1fr max-content;
  gap: var(--space-1) var(--space-3); align-items: start; position: relative;
}
.operation-extras .value-row > span:first-child {
  color: var(--muted); font-size: var(--text-sm); grid-column: 1 / -1;
}
.operation-extras .value-row .value { grid-column: 1; min-width: 0; }
.operation-extras .value-row .copy { grid-column: 2; grid-row: 2; }

/* What an operation takes, inside the operation, above its parameters. It sits
   on the renderer's own band so it reads as part of the opened operation
   rather than as something pasted into it. */
.opblock-body > .operation-credential {
  background: var(--grid); border-bottom: 1px solid var(--line);
  margin: 0; padding: var(--space-3) var(--space-4);
}

/* Nucleus's own additions to an operation: the absolute address, the copyable
   request, this read's own refusal and what the source last said it returns.
   The renderer owns the operation's own subtree, so these are drawn beside it
   rather than within -- which meant they stayed on screen when the operation
   was shut, and an operation that was collapsed still filled a screen.

   They belong to the operation, so they appear exactly when it is open. A
   collapsed operation has no body element at all; this asks for one. */
.operation-extras { display: none; }
.operation:has(.opblock-body) .operation-extras {
  border-top: 1px solid var(--line); display: grid; gap: var(--space-3); padding: var(--space-4);
}

/* ---------------------------------------------------------------------------
   A table read across and opened in place. The allowlist's operations are read
   down a column -- which writes are open to agents, which carry a ceiling --
   and a list of disclosures hid every one of those until its line was opened,
   so the settings could not be scanned at all. This is the queue's table with
   a toggle on the name: the line carries the settings, and what changes them
   opens in a row of its own under it, in place, so the reader keeps the list.
   Added for the allowlist redesign. */
.queue--open { min-width: 0; }
.queue--open td { vertical-align: baseline; }
.queue--open .queue-tick { width: var(--space-5); }
/* A bare box is 16 pixels; its label is the target, so it is padded out to the
   24 a finger or a shaky hand needs. */
.queue--open .tick { min-height: var(--space-5); min-width: var(--space-5); }
.queue--open button.link-button { padding: var(--space-1) 0; }
/* The name is the control, drawn as the disclosure's line is: the marker turns
   when the row is open, and `aria-expanded` says the same to a reader. */
button.row-toggle {
  background: transparent; border: 0; color: var(--ink); font-family: var(--mono);
  font-size: var(--text-sm); font-weight: var(--weight-normal); letter-spacing: normal;
  padding: var(--space-1) 0; text-align: left; text-transform: none; overflow-wrap: break-word; width: auto;
}
button.row-toggle::before {
  color: var(--faint); content: "\203A"; display: inline-block; width: var(--space-4);
}
button.row-toggle[aria-expanded="true"]::before { transform: rotate(90deg); }
button.row-toggle:hover, button.row-toggle:focus-visible { color: var(--accent); }
.row-toggle-note { display: block; margin-top: var(--space-1); padding-left: var(--space-4); }
/* The opened row. It spans the table and indents under the name it belongs to. */
.queue--open .row-detail > td { padding: 0 0 var(--space-5) var(--space-6); }
.queue--open tr:has(+ .row-detail:not([hidden])) > td { border-bottom: 0; }
.row-detail .rows { margin-top: var(--space-4); max-width: var(--measure); }
.row-detail h4 { margin: var(--space-5) 0 var(--space-2); }
.row-detail > td > :first-child { margin-top: 0; }
/* A value that is not the ordinary one is said in ink; the ordinary one is
   quiet, so an unusual row reads out of the column without a colour of its own. */
.cell-quiet { color: var(--muted); }
/* A table that is also its own filter: under each column's name, the control
   that narrows by it. The heading treatment is for the names; a control in the
   row below takes the page's own face, or a search box would be typed in small
   capitals. */
.queue--grid .filter-row th {
  font-family: inherit; font-size: var(--text-sm); font-weight: var(--weight-normal);
  letter-spacing: normal; padding-top: 0; text-transform: none;
}
.queue--grid .filter-row input { max-width: none; width: 100%; }
/* A select as wide as its longest choice, so "Allowed (25)" is never cut to
   "Al" in the narrow column it heads. */
.queue--grid .filter-row select { max-width: none; width: auto; }
.queue--grid .column-names th { border-bottom: 0; }
/* A row nobody has allowed yet. Its name is quieter than an allowed one's, so
   a mixed list still reads as the allowed rows first. */
.row--muted .row-toggle { color: var(--muted); }
/* A row chosen by hand for an act on several at once. The box says it; the
   card surface lets a reader see the chosen rows down a long table. */
.queue--grid > tbody > tr:has(> .queue-tick input:checked) > td { background: var(--card); }
/* One of a few exclusive states on a row -- off, reads, writes; environment,
   apps -- drawn as the segmented choice at a row's density, so a column reads
   down by which segment is filled. */
.log-view--row label { font-size: var(--text-xs); padding: var(--space-1) var(--space-2); }
/* The chosen segment filled quietly, for a page that draws many choices at
   once: a column of ink-filled segments pulled the eye off the operation names
   on the dark page (operator, 2026-09-30). The rule tone rather than ink; the
   choice is carried by the fill, the ink and the weight together. A row's
   choice is always quiet; a larger one opts in. */
.log-view--row label:has(input:checked),
.log-view--quiet label:has(input:checked) { background: var(--rule); color: var(--ink); }
/* The settings to apply to every chosen row, opened above the bar's own line. */
.selection-bar .bulk-edit { border-bottom: 1px solid var(--line); flex-basis: 100%; padding-bottom: var(--space-3); }
/* A number entered in a cell or beside its own button -- a ceiling. A field
   takes its row's width everywhere else, which in a table cell pushed the
   button onto a line of its own. */
input.number-field { flex: none; width: 8rem; }
/* A comparison cell: one value per environment, the environment named, and a
   difference said in words and weight rather than by hue. */
.pair { display: grid; gap: var(--space-1); }
.pair-side { color: var(--muted); font-size: var(--text-xs); }
/* A comparison line that is also the reason beside a control drawn read only
   -- the copy review's "Already there" line, which its target's values are
   described by. It keeps the class that says what it is, and the line of the
   comparison it sits in: the unavailable-field line height would make each
   one taller than the approved review (operator, 2026-09-30). */
.pair-side.field-unavailable { line-height: inherit; }
.differs { font-weight: var(--weight-strong); }
/* A setting an act is about to change, drawn on the control that sets it: the
   chosen option, the tick or the field turns amber. A two-way setting says what
   it replaces by elimination; a number says it in a `.pair-side` line under the
   field. Either way it is also in the control's spoken name. The same amber
   marks a change on the page that did not save: it is drawn as tried, differs
   from what is in force, and the row says why with a retry. The warning pair,
   because a change to another environment is the thing on a review to look at
   twice. */
.log-view--change { border-color: var(--warning); }
.log-view--change label:has(input:checked) { background: var(--warning); color: var(--page); }
.tick--change input[type="checkbox"] { accent-color: var(--warning); outline: 2px solid var(--warning); outline-offset: 1px; }
input.number-field--change { border-color: var(--warning); box-shadow: inset 0 0 0 1px var(--warning); }
/* An operation named in a cell of its own, not as a row's toggle. */
.queue--open .queue-name code { color: var(--ink); font-size: var(--text-sm); }

/* The rows chosen to act on together, and the act. It holds to the bottom of
   the window while anything is chosen, because the rows are ticked down a long
   list and the act was below the last of them -- #86 found the copy's review
   drawn out of sight. */
.selection-bar {
  align-items: center; background: var(--card); border-top: 1px solid var(--rule);
  bottom: 0; display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-5);
  justify-content: space-between; margin-top: var(--space-5);
  padding: var(--space-3) var(--space-4); position: sticky; z-index: 10;
}
.selection-bar > * { margin: 0; }
.selection-count { font-weight: var(--weight-strong); }

/* A step that takes the list's place -- a review, then its result. Its heading
   takes focus when it opens, so the page moves to it rather than drawing it
   below everything. */
.step-heading { margin-bottom: var(--space-2); }
.step-heading:focus { outline: none; }

@media (max-width: 44rem) {
  /* On a phone the settings stack under the name, each labelled, rather than
     scroll a table sideways. Child combinators throughout, because an opened
     row holds a table of its own that should stay a table. */
  /* The column names go; the filters stay, as a stack of labelled fields. */
  .queue--open > thead { display: none; }
  .queue--grid > thead { display: block; margin-bottom: var(--space-3); }
  .queue--grid .column-names { display: none; }
  .queue--grid .filter-row { display: grid; gap: var(--space-3); }
  .queue--grid .filter-row th { display: block; padding: 0; }
  .queue--grid .filter-row th[data-label]::before {
    content: attr(data-label); display: block; font-weight: var(--weight-strong); margin-bottom: var(--space-1);
  }
  .queue--open, .queue--open > tbody, .queue--open > tbody > tr, .queue--open > tbody > tr > td { display: block; width: 100%; }
  .queue--open > tbody > tr:not(.row-detail) {
    border-bottom: 1px solid var(--line); display: grid; gap: var(--space-1) var(--space-3);
    grid-template-columns: auto 1fr; padding: var(--space-3) 0;
  }
  .queue--open > tbody > tr:has(+ .row-detail:not([hidden])) { border-bottom: 0; }
  .queue--open > tbody > tr > td { border: 0; padding: 0; }
  .queue--open > tbody > tr:not(.row-detail) > td:not(.queue-tick) { grid-column: 2; }
  .queue--open .queue-tick { grid-row: 1 / span 6; width: auto; }
  .queue--open > tbody > tr > td[data-label]::before {
    color: var(--muted); content: attr(data-label) " "; font-family: var(--mono);
    font-size: var(--text-2xs); letter-spacing: .1em; text-transform: uppercase;
  }
  /* A cell with nothing in it -- a setting an unallowed operation does not
     have yet -- draws no label either. */
  .queue--open > tbody > tr > td:empty { display: none; }
  .queue--open > tbody > .row-detail > td { border-bottom: 1px solid var(--line); padding: 0 0 var(--space-4) var(--space-4); }
  .selection-bar { padding: var(--space-3) 0; }
}
