/* The base palette moved to tokens.css, which index.html loads first. One source,
   because two files defining --fg is the drift UI.md exists to prevent. The rule it
   carries came from here and is worth restating: if a variable is a colour, it is
   defined TWICE — light and dark. The dark block here once overrode only
   --fg/--muted/--line/--bg/--warnbg, so --ok, --bad and --warn kept light-theme
   values chosen against white and were rendered on near-black; "active" and
   "Remove…" were legible in one theme only.

   Every colour in this file is now a token, including the health map's --h-* set.
   See ../UI.md §2. */
*{box-sizing:border-box}
/* `hidden` is an ATTRIBUTE, and the UA implements it as display:none — which any
   author `display` rule outranks. `nav{display:flex}` therefore defeated
   `<nav hidden>` silently, and a signed-out visitor was shown the whole list of
   surfaces: System Health, Ingestion queue, Identity, Configuration, Usage,
   Audit. That is the enumeration CON-AUT-007 forbids, and it was invisible in
   review because the markup says `hidden` and means it.
   One rule, once, so no component can reintroduce it. */
[hidden]{display:none !important}
body{margin:0;padding:var(--s-5);
  font:var(--text-base)/var(--leading) var(--font);color:var(--fg);background:var(--bg)}

/* CON-NAV-013 / NFR-USA-003. One rule, every interactive element, before any
   component gets a chance to forget. `:focus-visible` rather than `:focus` so a
   pointer click does not ring, but every keyboard arrival does — and the ring is
   drawn OUTSIDE the element (`outline-offset`) so a dense table row cannot clip it.
   Removing an outline without replacing it is the most common regression when a
   screen is generated; this makes the default correct rather than the exception. */
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,
textarea:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:var(--focus-width) solid var(--focus);outline-offset:var(--focus-offset);
  border-radius:var(--radius)}
/* align-items was `baseline` while the first item was type. IT IS NOW A MARK,
   and an SVG has no text baseline — the browser falls back to its bottom margin
   edge, which drops the mark below the nav labels. `center` is the honest fix
   once nothing in this row is type that has to line up with the rest. nav and
   .who already centre their own children, so this changes only how the three
   groups sit against each other. */
header{display:flex;align-items:center;gap:1.5rem;border-bottom:1px solid var(--line);padding-bottom:.75rem;margin-bottom:1.25rem}
h1{font-size:1.05rem;margin:0;font-weight:600;line-height:1}
.muted{color:var(--muted);font-weight:400}
a{color:var(--link)}
/* Pills, matching the reference (UI.md §1). Zitadel renders an inactive item as
   plain text and the active one as a filled accent pill — the fill is the only
   thing carrying "you are here", which is why the accent appears nowhere else in
   the bar. A bottom-border underline was doing that job before and read as a tab
   strip rather than as navigation.
   Colour is not the only signal: aria-current on the active link is what a screen
   reader announces, and it is set in app.js alongside the class. */
nav a{color:var(--fg);text-decoration:none;font-size:var(--text-sm);
  padding:var(--s-1) var(--s-3);border-radius:999px;white-space:nowrap;
  transition:background-color .12s,color .12s}
nav a:hover{background:var(--surface-2)}
nav a.active{background:var(--accent);color:var(--accent-fg);font-weight:var(--weight-bold)}
nav a.active:hover{background:var(--accent)}
/* ── The ingestion queue. THE EXEMPLAR (UI.md §8) ─────────────────────────────
   Every value below is a token. When adding a screen, copy this block's discipline
   rather than the prose in UI.md: spacing from --s-*, size from --text-*, radius
   from --radius, and no literal anywhere. The rest of this file predates the
   tokens and is normalised view by view, not in one sweep — the diff that touches
   every view at once is unreviewable and lands its regressions everywhere. */
.flags{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-bottom:var(--s-4)}
.flag{font-size:var(--text-xs);padding:var(--s-1) var(--s-3);
  border:1px solid var(--line);border-radius:999px}
.flag.off{background:var(--warnbg);border-color:var(--warn);color:var(--warn);
  font-weight:var(--weight-bold)}
.backlog{display:flex;gap:var(--s-6);padding:var(--s-3) var(--s-4);
  border:1px solid var(--line);border-radius:var(--radius);margin-bottom:var(--s-5)}
.backlog.alert{border-color:var(--warn);background:var(--warnbg)}
.backlog div{display:flex;flex-direction:column}
.backlog b{font-size:var(--text-lg);font-weight:var(--weight-bold);
  font-variant-numeric:tabular-nums}
.backlog span{font-size:var(--text-xs);color:var(--muted);
  text-transform:uppercase;letter-spacing:.04em}
.filters{display:flex;gap:var(--s-4);margin-bottom:var(--s-3);font-size:var(--text-sm)}
select{font:inherit;padding:var(--s-1)}
table{width:100%;border-collapse:collapse;font-size:var(--text-sm)}
th{text-align:left;font-weight:var(--weight-bold);color:var(--muted);
  font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.04em;
  padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--line)}
td{padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--line)}
/* Status as a TINTED PILL, not coloured text — the reference's treatment. A pill
   reads as a value at a glance down a column; coloured text reads as emphasis. */
.state{font-size:var(--text-xs);padding:var(--s-1) var(--s-2);
  border-radius:999px;border:1px solid transparent;
  background:color-mix(in srgb,var(--muted) 14%,transparent);color:var(--muted)}
.state.quarantined,.state.failed{
  background:color-mix(in srgb,var(--bad) 16%,transparent);color:var(--bad)}
.state.drafted{background:color-mix(in srgb,var(--ok) 16%,transparent);color:var(--ok)}
/* The three states that are not "populated" (UI.md §5). */
.empty{color:var(--muted);font-style:italic;padding:var(--s-4) 0;max-width:70ch}
.loading{color:var(--muted);padding:var(--s-4) 0}
.errstate{border:1px solid var(--bad);border-left:3px solid var(--bad);
  border-radius:0 var(--radius) var(--radius) 0;padding:var(--s-3) var(--s-4);
  margin:var(--s-4) 0;max-width:70ch}
.errstate p{margin:var(--s-2) 0;color:var(--muted)}
.errstate button{font:inherit;font-size:var(--text-sm);padding:var(--s-1) var(--s-3);
  border-radius:var(--radius)}
footer{margin-top:var(--s-8);font-size:var(--text-xs)}
code{font-size:.85em}

/* The health map's --h-* set moved to tokens.css with the rest of the palette.
   It stays a distinct vocabulary from --ok/--warn/--bad — one view's semantics,
   not the platform's — but a colour is a token wherever it lives (UI.md §2). */
nav{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
/* CON-NAV-010: the plane boundary is structural, not decorative. With the visible
   labels removed the rule is the only thing carrying it on screen, so it is a real
   divider rather than extra gap — and the group keeps its accessible name in the
   markup, which is what a screen reader announces. */
nav .navgrp{display:flex;align-items:center;gap:var(--s-2)}
nav .navgrp:not(:first-child){margin-left:var(--s-2);padding-left:var(--s-4);
  border-left:1px solid var(--line)}

/* Submenus. A disclosure, not a hover menu: it opens on click and stays open
   until dismissed, because a menu that opens on hover cannot be operated by
   keyboard or touch without a second mechanism (NFR-USA-003, CON-NAV-013). The
   button carries aria-expanded and the panel is `hidden` rather than moved
   offscreen, so a screen reader is told the same thing the eye is. */
nav .navmenu{position:relative;display:flex;align-items:center}
nav .navtop{font:inherit;font-size:var(--text-sm);color:var(--fg);background:none;
  border:0;cursor:pointer;padding:var(--s-1) var(--s-3);border-radius:999px;
  display:flex;align-items:center;gap:.35rem;white-space:nowrap;
  transition:background-color .12s,color .12s}
nav .navtop::after{content:"";width:.32rem;height:.32rem;border-right:1.5px solid currentColor;
  border-bottom:1.5px solid currentColor;transform:rotate(45deg) translateY(-.1rem);opacity:.7}
nav .navtop[aria-expanded="true"]::after{transform:rotate(-135deg) translateY(.05rem)}
nav .navtop:hover,nav .navtop:focus-visible{background:var(--surface-2)}
/* `.hasactive` keeps the parent lit while a child view is showing, so the trail
   from menu to open view is visible when the panel is closed. */
nav .navtop.hasactive{background:var(--accent);color:var(--accent-fg);
  font-weight:var(--weight-bold)}
nav .navsub{position:absolute;top:calc(100% + var(--s-1));left:0;z-index:40;
  display:flex;flex-direction:column;min-width:11rem;padding:var(--s-1);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-lg);
  box-shadow:0 6px 18px rgba(0,0,0,.28)}
nav .navsub[hidden]{display:none}
nav .navsub a{display:block;white-space:nowrap}
.hhead{display:flex;gap:2rem;align-items:flex-start;margin-bottom:1.25rem;flex-wrap:wrap}
.hhead h2{font-size:1rem;margin:0 0 .3rem}
.hhead p{margin:0;font-size:.8rem;max-width:52ch}
.hsum{margin-left:auto;display:flex;gap:.9rem;font-size:.78rem;flex-wrap:wrap}
.hsum .k{display:flex;align-items:center;gap:.35rem;white-space:nowrap}
.hsum i{width:.6rem;height:.6rem;border-radius:50%;background:var(--h-off)}
.hsum .healthy i{background:var(--h-ok)}.hsum .degraded i{background:var(--h-warn)}
.hsum .fault i{background:var(--h-bad)}

.hmap{position:relative;overflow-x:auto;padding:1.4rem 0 .5rem}
.hwires{position:absolute;inset:0;pointer-events:none;overflow:visible}
.wire{fill:none;stroke:var(--h-off);stroke-width:1.5;opacity:.5}
.wire.healthy{stroke:var(--h-ok);opacity:.85}.wire.degraded{stroke:var(--h-warn);opacity:.85}
.wire.fault{stroke:var(--h-bad);opacity:.85}
.wire.unknown{stroke-dasharray:4 4}
.wire.back{stroke-dasharray:2 5}
/* A gateway route is not traffic — it is reachability, so it is drawn lighter
   than a data-path edge and never competes with one for attention. */
.wire.route{stroke-width:1;stroke-dasharray:1 4;opacity:.45}
.hband{display:grid;grid-template-columns:repeat(6,minmax(150px,1fr));gap:.9rem;min-width:960px}
.col{display:flex;flex-direction:column;gap:.7rem}
/* min-height reserves the optional note line in every column, so the first box of
   a column with no note still lines up with its neighbours. */
.hband h3,.hplat h3{font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);margin:0;font-weight:600;display:flex;flex-direction:column;gap:.15rem}
.hband h3{min-height:2.4rem}
.hband h3 span,.hplat h3 span{text-transform:none;letter-spacing:0;font-weight:400;font-size:.72rem;opacity:.8}
.hplat{margin-top:1.6rem;padding-top:1.1rem;border-top:1px dashed var(--line);min-width:960px}
/* auto-fit, not a fixed count. It was repeat(5,...) and a sixth platform
   service (party-svc) wrapped onto a row of its own at the bottom left —
   present, green and easy to miss, which reads as a rendering fault rather
   than as a service. The count of platform services is not a layout constant;
   the next one added would have done the same thing. */
.hplat .row{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:var(--s-3);margin-top:var(--s-2)}

/* The box. `position:relative` and a background are required: the connectors are
   drawn underneath, and a line must not run through the text it points at. */
.node{position:relative;background:var(--bg);border:1px solid var(--line);
  border-left:3px solid var(--h-off);border-radius:5px;padding:.5rem .6rem;font-size:.8rem}
.node.healthy{border-left-color:var(--h-ok);background:var(--h-okbg)}
.node.degraded{border-left-color:var(--h-warn);background:var(--h-warnbg)}
.node.fault{border-left-color:var(--h-bad);background:var(--h-badbg)}
.node.unknown{border-style:dashed;border-left-style:solid;opacity:.75}
.node.external{background:transparent;border-left-width:1px}
.node-h{display:flex;align-items:center;gap:.4rem}
.node-h b{font-weight:600;font-size:.82rem}
.dot{width:.5rem;height:.5rem;border-radius:50%;background:var(--h-off);flex:none}
.healthy .dot{background:var(--h-ok)}.degraded .dot{background:var(--h-warn)}
.fault .dot{background:var(--h-bad)}
.node-d{color:var(--muted);font-size:.72rem;margin-top:.1rem}
.node-s{font-size:.72rem;margin-top:.3rem;font-weight:600}
.healthy .node-s{color:var(--h-ok)}.degraded .node-s{color:var(--h-warn)}
.fault .node-s{color:var(--h-bad)}.unknown .node-s{color:var(--h-off)}
.node-s .why{font-weight:400;color:var(--muted)}
/* Where the statistics will go. Empty renders as nothing rather than as a gap. */
.node-stats{display:flex;flex-wrap:wrap;gap:.6rem;margin-top:.35rem}
.node-stats:empty{display:none}
.node-stats span{display:flex;flex-direction:column;font-size:.62rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:.03em}
.node-stats b{font-size:.9rem;color:var(--fg);font-variant-numeric:tabular-nums}

.who{margin-left:auto;font-size:.85rem;display:flex;align-items:center;gap:.6rem}
.who button{font:inherit;padding:var(--s-1) var(--s-3)}
.who .note{color:var(--muted);font-size:.75rem}

/* The warn colour marks the block; it does not set the body text. --warn on
   --warnbg is 4.84:1 in light theme — over the AA line but thin for a dense
   explanatory paragraph, and these blocks carry the longest prose on the page.
   The border and the bold lead carry the semantic, the sentences use --fg
   (17.8:1 light, 12.9:1 dark), which is what the reader is actually here for. */
.ierr{background:var(--warnbg);border:1px solid var(--warn);border-left:3px solid var(--warn);
  color:var(--fg);padding:.5rem .75rem;border-radius:4px}
.ierr>b:first-child{color:var(--warn)}
/* The signed-out gate. Deliberately sparse: it is the whole page for anyone
   without a session, and everything it does not say is the point (CON-AUT-007). */
.gate{max-width:44rem;margin:3rem auto;padding:1.5rem;border:1px solid var(--line);border-radius:8px}
.gate h2{font-size:1.05rem;margin:0 0 .5rem}
.gate p{font-size:.88rem;margin:.4rem 0}
.gate button{font:inherit;padding:.35rem .9rem;margin:.3rem .3rem 0 0;
  background:var(--btn-bg);color:var(--btn-fg)}
.row{display:flex;gap:.5rem;flex-wrap:wrap;margin:.6rem 0}
.row input{font:inherit;padding:.35rem .5rem;border:1px solid var(--line);border-radius:4px;background:transparent;color:inherit}
.row button,.act button{font:inherit;font-size:.85rem;padding:.25rem .6rem;background:var(--btn-bg);color:var(--btn-fg)}
.act button.danger{border-color:var(--bad);color:var(--bad)}
.act{white-space:nowrap;display:flex;gap:.35rem;align-items:center}
.act select{font:inherit;font-size:.85rem}
.role{font-size:.8rem;border:1px solid var(--line);border-radius:999px;padding:.1rem .5rem}
.role a{text-decoration:none;color:var(--muted)}
tr.sel{outline:2px solid var(--ok);outline-offset:-2px}
.state.active{color:var(--ok);border-color:currentColor}
.state.suspended{color:var(--warn);border-color:currentColor}
h3{font-size:.95rem;margin:1.2rem 0 .4rem}
h3 .muted{font-size:.75rem;font-weight:400}
.small{font-size:.78rem}
button[disabled]{opacity:.45;cursor:not-allowed}
.state.removed{color:var(--muted);border-style:dashed}

/* Identity administration — CON-IDN-013..024.
   Native <dialog>: showModal() gives the focus trap, the Escape handler and the
   focus restore that CON-IDN-024 wants, without a hand-rolled version to drift. */
.dlg{border:1px solid var(--line);border-radius:8px;background:var(--bg);color:var(--fg);
  padding:1.1rem 1.25rem;max-width:46rem;width:calc(100% - 2rem);font:inherit}
.dlg::backdrop{background:var(--backdrop)}
.dlg h3{margin:0 0 .5rem}
.dlg h4{font-size:.85rem;margin:.6rem 0 .3rem}
.dlg p{margin:.5rem 0;font-size:.85rem}
.dlg ul{margin:.3rem 0 .3rem 1.1rem;padding:0;font-size:.82rem}
.dlg label{display:block;margin:.7rem 0;font-size:.85rem}
/* type=email and type=url are listed explicitly: `input:not([type])` does not
   match them, so a typed input silently fell out of the block layout and sat
   inline beside its label while every other field stacked. */
.dlg input[type=text],.dlg input[type=email],.dlg input[type=url],
.dlg input:not([type]),.dlg textarea,.dlg select{
  display:block;width:100%;margin-top:.25rem;font:inherit;padding:.4rem .5rem;
  border:1px solid var(--line);border-radius:4px;background:transparent;color:inherit}
.dlg input[readonly]{color:var(--muted);background:color-mix(in srgb,var(--fg) 5%,transparent)}
.dlg menu{display:flex;gap:.5rem;justify-content:flex-end;margin:1rem 0 0;padding:0}
.dlg menu button{font:inherit;padding:.35rem .9rem;
  background:var(--btn-bg);color:var(--btn-fg)}
.dlg menu button.danger{border-color:var(--bad);color:var(--bad)}
.dlg menu button:focus-visible{outline:2px solid currentColor;outline-offset:2px}
/* The consequence of an irreversible or asymmetrical action, not styled as an
   aside — CON-IDN-014's whole point is that it must not read as a detail. */
.warnp{background:var(--warnbg);border-left:3px solid var(--warn);color:var(--warn);
  padding:.45rem .7rem;border-radius:0 4px 4px 0;font-size:.82rem}
.dlg fieldset{border:1px solid var(--line);border-radius:6px;padding:.6rem .8rem;margin:.8rem 0}
.dlg legend{font-size:.72rem;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}
.rolechk{display:flex;gap:.55rem;align-items:flex-start;margin:.5rem 0}
.rolechk input{margin-top:.25rem;flex:none}
.rolechk span{display:flex;flex-direction:column}
.rolechk em{font-style:normal;color:var(--muted);font-size:.78rem}
.eff{border-top:1px dashed var(--line);padding-top:.5rem}
.idty div{display:flex;gap:.5rem;align-items:baseline;font-size:.85rem}
.idty span{color:var(--muted);font-size:.75rem;text-transform:uppercase;letter-spacing:.04em}

/* CON-NAV-011: persistent, non-dismissible, and naming the tenant, the elevation
   identifier and the remaining window. It is not possible to operate inside an
   elevated session without this visible, so it sits above the content it guards. */
.elev{position:sticky;top:0;z-index:5;display:flex;gap:.5rem;align-items:center;
  flex-wrap:wrap;background:var(--warnbg);border:1px solid var(--warn);color:var(--warn);
  padding:.45rem .75rem;border-radius:4px;font-size:.82rem;margin:.8rem 0}
.elev code{background:transparent}
.elev button{margin-left:auto;font:inherit;font-size:.8rem;padding:.15rem .6rem;
  border:1px solid currentColor;background:var(--btn-bg);color:var(--btn-fg)}
#igate{border:1px solid var(--line);border-left:3px solid var(--warn);
  border-radius:0 6px 6px 0;padding:.4rem 1rem 1rem;margin:1rem 0}
#igate .gatep{font-size:.85rem;max-width:70ch}
#igate button{font:inherit;padding:var(--s-2) var(--s-4)}

/* System Health card navigation — CON-SYS-002..010.
   A linked card is an <a>; an unlinked one is an <article>, so the pointer
   cursor and hover lift exist only where there is somewhere to go (CON-SYS-008).
   The affordance IS the difference. */
a.node.link{display:block;text-decoration:none;color:inherit;cursor:pointer}
a.node.link:hover{border-color:var(--h-ok);
  box-shadow:var(--shadow-overlay);transform:translateY(-1px)}
a.node.link:focus-visible{outline:2px solid var(--h-ok);outline-offset:2px}
a.node.link .go{position:absolute;top:.45rem;right:.5rem;
  font-size:.8rem;color:var(--muted);opacity:.55;transition:opacity .12s}
a.node.link:hover .go{opacity:1}
/* CON-SYS-010: leaving the console is marked before the click, not after. */
a.node.leaves .go.ext{color:var(--h-warn);opacity:.75}
a.node.leaves:hover{border-color:var(--h-warn)}
/* CON-SYS-008: say why there is nowhere to go, rather than staying silent. */
.node-nolink{font-size:.68rem;color:var(--h-off);margin-top:.3rem;font-style:italic}

/* A button that reads as a link — it changes what is shown, not what exists,
   so it should not carry the weight of an action button. */
.linkish{background:none;border:0;padding:0 0 0 var(--s-3);font-size:var(--text-xs);
  /* --muted, and it passes: 4.83:1 on --bg in light, 7.6:1 in dark. An earlier
     edit moved this to --link on a reading of 4.34:1 that was a MEASUREMENT bug —
     the checker compared the text against `rgba(0,0,0,0)`, which parses as black,
     rather than against the page background behind it. Reverted. */
  color:var(--muted);text-decoration:underline;font-weight:var(--weight-normal)}
.linkish:hover{color:var(--fg)}


/* ── Buttons: filled, dark text (UI.md §4) ────────────────────────────────────
   Two tiers and no more: blue fill means pressable, red fill means destructive.
   An outlined button on a neutral background reads as disabled, which is what
   the previous treatment looked like in dark.
   The fills live in tokens.css and are identical in both themes — see the note
   there about why dark text forces a light fill. */
button{border-radius:var(--radius);
  background:var(--btn-bg);color:var(--btn-fg);
  font-weight:var(--weight-bold)}
button:hover:not([disabled]){filter:brightness(1.08)}
button[disabled]{opacity:.45;cursor:not-allowed}

button.danger,.act button.danger,.dlg menu button.danger,
button[value=remove],#itenants button[data-tdel],[data-mdel]{background:var(--btn-danger-bg);color:var(--btn-danger-fg);border-color:transparent}

/* The focus ring is drawn outside the fill, so it needs the darker --accent to
   stay visible against a light button. */
button:focus-visible{outline-color:var(--accent)}

/* ── The fan ──────────────────────────────────────────────────────────────────
   Tenant detail opens as a row inside the tenants table, directly beneath the
   tenant it belongs to, rather than as a section further down the page. The
   inset rule and the tinted ground are what say "this belongs to the row above";
   without them a full-width detail block reads as a sibling of the table. */
tr.fanrow > td{padding:0;border-bottom:1px solid var(--line);
  background:var(--surface);border-left:3px solid var(--accent)}
tr.fanrow #idetail{padding:var(--s-4) var(--s-5) var(--s-5)}
/* The row above is the fan's handle, so it loses its own separator — the two
   read as one object rather than as two rows that happen to touch. */
tr.sel > td{border-bottom:0}

/* The way out of the tenant plane. Sits above the tenant-scoped sections so it
   is reachable without scrolling past the data it dismisses. */
.planeout{margin:.2rem 0 .8rem}

/* ── Audit records (AUD-QRY-001) ──────────────────────────────────────────────
   Copies the queue exemplar: tokens only, dense rows, tinted status pills. */

/* AUD-QRY-007's completeness statement. Deliberately NOT muted and NOT a footer
   note — it is the difference between "nothing happened" and "you were not shown
   everything", and a reader who misses it draws the wrong conclusion from a
   screen that otherwise looks authoritative. Warning treatment, above the table. */
.partial{background:var(--warnbg);border:1px solid var(--warn);color:var(--warn);
  border-left:3px solid var(--warn);border-radius:0 var(--radius) var(--radius) 0;
  padding:var(--s-2) var(--s-3);margin:var(--s-3) 0;font-size:var(--text-sm);max-width:100ch}
.partial b{font-weight:var(--weight-bold)}

/* `denied` is the platform working, not a fault. It must not share red with a
   failure, or an auditor scanning for problems stops on every enforced rule. */
.state.denied{background:color-mix(in srgb,var(--warn) 16%,transparent);color:var(--warn)}

#view-audit table{table-layout:auto}
#view-audit td{vertical-align:top}
#view-audit .filters{flex-wrap:wrap;align-items:end}
#view-audit .filters input[type=search]{font:inherit;font-size:var(--text-sm);
  padding:var(--s-1) var(--s-2);border:1px solid var(--line);
  border-radius:var(--radius);background:var(--bg);color:var(--fg)}

/* ── Configuration, Usage, and the audit registry ─────────────────────────────
   Tokens only (UI.md §2). The three views share one vocabulary deliberately:
   a revision state, a meter row and an event type are all "a fact with a
   qualifier", and giving each its own treatment would be three dialects. */

/* Revision state. `draft` and `validated` are NOT styled as success — neither is
   in effect, and CON-CFG-003 exists because a saved draft that looks like a
   published one is the whole problem. Only `published` gets the ok tint. */
.state.cfg-draft{background:color-mix(in srgb,var(--muted) 14%,transparent);color:var(--muted)}
.state.cfg-validated{background:color-mix(in srgb,var(--warn) 16%,transparent);color:var(--warn)}
.state.cfg-published{background:color-mix(in srgb,var(--ok) 16%,transparent);color:var(--ok)}
.state.cfg-superseded,.state.cfg-rolled_back{
  background:transparent;border-color:var(--line);color:var(--muted)}

#view-config section,#view-usage section{margin:var(--s-6) 0}
#view-config h3,#view-usage h3{display:flex;align-items:baseline;gap:var(--s-3);flex-wrap:wrap}
#view-config textarea{font:var(--text-sm)/1.4 ui-monospace,monospace;width:100%;
  padding:var(--s-2);border:1px solid var(--line);border-radius:var(--radius);
  background:transparent;color:inherit}
#view-config label{display:block;margin:var(--s-3) 0;font-size:var(--text-sm)}
#view-config input[type=text],#view-config input:not([type]),#cnewrev input{
  font:inherit;padding:var(--s-1) var(--s-2);border:1px solid var(--line);
  border-radius:var(--radius);background:transparent;color:inherit}
#cnewrev button,#crefresh{font:inherit;font-size:var(--text-sm);
  padding:var(--s-1) var(--s-3);border:1px solid var(--line);
  border-radius:var(--radius);background:transparent;color:inherit;cursor:pointer}
.checklist{list-style:none;margin:var(--s-3) 0;padding:0;max-height:16rem;overflow-y:auto}
.checklist li{padding:var(--s-1) 0}
.checklist label{display:flex;gap:var(--s-2);align-items:baseline}

/* A figure that is NOT known. CON-MTR-009: unknown and zero are different facts,
   so they get different treatments and the unknown one carries its reason. It is
   deliberately not styled as an error — nothing is broken, the number is simply
   not available, and dressing it in red would send someone looking for a fault. */
.figs{display:flex;gap:var(--s-5);flex-wrap:wrap;margin:var(--s-4) 0}
.fig{display:flex;flex-direction:column;min-width:11rem;padding:var(--s-3) var(--s-4);
  border:1px solid var(--line);border-radius:var(--radius)}
.fig b{font-size:var(--text-lg);font-weight:var(--weight-bold);font-variant-numeric:tabular-nums}
.fig b i{font-style:normal;font-size:var(--text-sm);color:var(--muted);margin-left:.2em}
.fig span{font-size:var(--text-xs);color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
/* Deliberately NOT .errstate. Nothing is broken when a figure is unavailable —
   the number simply is not known — and painting it red sends an operator looking
   for a fault that does not exist. Dashed and muted says "absent"; red says
   "wrong", and they are different claims. */
.notice{border:1px dashed var(--line);border-left:3px solid var(--muted);
  border-radius:0 var(--radius) var(--radius) 0;padding:var(--s-3) var(--s-4);
  margin:var(--s-4) 0;max-width:70ch}
.notice p{margin:var(--s-2) 0;color:var(--muted)}
.fig.unavail{border-style:dashed}
.fig.unavail b{font-size:var(--text-base);color:var(--muted);font-style:italic}
.fig .why{font-size:var(--text-xs);color:var(--muted);margin-top:var(--s-1);
  font-style:normal;max-width:22ch}

/* "This part is real." The console mixes live services with stubs, and a reader
   cannot tell by looking — so the live parts say so rather than leaving the
   stubbed parts to be discovered. */
.livechip{font-size:var(--text-xs);padding:.1rem var(--s-2);border-radius:999px;
  background:color-mix(in srgb,var(--ok) 14%,transparent);color:var(--ok);
  text-transform:none;letter-spacing:0;font-weight:var(--weight-bold)}

.evts{display:flex;flex-wrap:wrap;gap:var(--s-2);margin:var(--s-3) 0}
.evt{font-size:var(--text-xs);padding:.15rem var(--s-2);border-radius:var(--radius);
  border:1px solid var(--line);font-family:ui-monospace,monospace}
/* Critical is a stronger claim than "important": AUD-CAP-004 forbids the action
   from completing until the event is durably persisted. */
.evt.crit{border-color:var(--warn);color:var(--warn);font-weight:var(--weight-bold)}
#aregbox{margin:var(--s-4) 0;padding:var(--s-3) var(--s-4);border:1px solid var(--line);
  border-radius:var(--radius)}
#aregbox summary{cursor:pointer;font-weight:var(--weight-bold);font-size:var(--text-sm)}

/* The two irreversible party actions (PTY-CON-004, PTY-CON-012).
   Set apart deliberately: archiving is one row above, fully reversible, and a
   click away. Making these look the same as Save is how the wrong one gets
   pressed — so they get a border, a warning ground and their own heading. */
.danger-zone{margin-top:var(--s-6);padding:var(--s-3) var(--s-4);
  border:1px solid var(--bad);border-left:3px solid var(--bad);
  border-radius:0 var(--radius) var(--radius) 0;
  background:color-mix(in srgb,var(--bad) 6%,transparent);max-width:70ch}
.danger-zone h4{margin:0 0 var(--s-1);color:var(--bad);font-size:var(--text-sm)}
.danger-zone p{margin:0 0 var(--s-3)}
.danger-zone button{margin-right:var(--s-2)}

/* Collapsible sections (<details>). The marker and the heading sit on one line —
   an <h3> inside a <summary> is block-level and would otherwise wrap under the
   triangle. `list-style` is the Firefox spelling, `::-webkit-details-marker` the
   Safari one; both are needed and neither is sufficient. */
.fold{margin:var(--s-4) 0}
.fold > summary{cursor:pointer;list-style-position:outside}
.fold > summary > h3{display:inline-block;margin:0 0 0 var(--s-1);vertical-align:middle}
.fold > summary:hover > h3{color:var(--link)}
.fold > summary:focus-visible{outline:var(--focus-width) solid var(--focus);
  outline-offset:var(--focus-offset);border-radius:var(--radius)}
.fold[open] > summary{margin-bottom:var(--s-2)}

/* Message preview. A dialog rather than a page: previewing is a glance at one
   row, and routing to it would lose the queue's filter and scroll position. */
#qpreview{max-width:56rem;width:92vw;border:1px solid var(--line);border-radius:var(--r-1);
  background:var(--surface);color:var(--fg);padding:0}
#qpreview::backdrop{background:rgba(0,0,0,.55)}
#qpreview article{padding:var(--s-4);max-height:82vh;overflow-y:auto}
#qpreview h3{margin:0 0 .3rem;font-size:1rem;overflow-wrap:anywhere}
#qpreview h4{margin:1.2rem 0 .4rem;font-size:.72rem;text-transform:uppercase;
  letter-spacing:.06em;opacity:.75}
.chainmsg{border-left:2px solid var(--line);padding-left:var(--s-3);margin-bottom:var(--s-3)}
/* pre-wrap, not pre: a quoted chain has 80-column hard wraps AND long unbroken
   URLs, and only one of those should force a horizontal scrollbar. */
.pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:.78rem;margin:0;
  background:var(--surface-2);padding:var(--s-2);border-radius:var(--r-1)}

/* Health-card badges. Type on the left, UI on the right of it.
   Outlined rather than filled: the card's own fill already carries STATE, which
   is the thing an operator scans for first, and a second filled chip would
   compete with it. These are attributes of the component, not its health. */
.node-badges{display:flex;flex-wrap:wrap;gap:.25rem;margin-top:.25rem}
.node-badges:empty{display:none}
.badge{font-size:.6rem;line-height:1.5;padding:0 .38rem;border-radius:999px;
  border:1px solid currentColor;text-transform:uppercase;letter-spacing:.05em;
  font-weight:600;opacity:.85;white-space:nowrap}
/* One hue per answer to "what do I do about it", and none of them reuse the
   health palette — a badge that borrowed --bad would read as an alert. */
.badge.t-native{color:var(--accent)}
.badge.t-container{color:var(--muted)}
.badge.t-external{color:var(--muted);border-style:dashed}
.badge.t-planned{color:var(--muted);opacity:.6;border-style:dotted}
/* The UI badge is the one that says "you can go there", so it matches the link
   affordance the card already uses rather than introducing a third signal. */
.badge.b-ui{color:var(--link)}

/* The tenant, named, in the chrome. It was `tenant c565529b` in the muted note
   style; the tenant is the single most important piece of context on the page
   for anyone who administers more than one, so it reads as a value. */
.who .tenantname{font-weight:var(--weight-bold);padding:var(--s-1) var(--s-3);
  border-radius:999px;background:var(--surface-2);white-space:nowrap}

/* Platform authority, marked across the page rather than in a corner.
   Deliberately NOT --bad: holding platform authority is not a fault and must not
   read as one, or it becomes the banner everybody learns to ignore. */
.planebanner{display:flex;gap:.4rem;flex-wrap:wrap;align-items:baseline;
  font-size:var(--text-sm);padding:var(--s-2) var(--s-4);margin:0 0 var(--s-4);
  border:1px solid var(--accent);border-left:3px solid var(--accent);
  border-radius:0 var(--radius) var(--radius) 0;
  background:color-mix(in srgb,var(--accent) 8%,transparent)}
.planebanner b{color:var(--accent)}
/* A second, quieter signal so the state survives scrolling past the banner. */
body.has-platform-authority header{border-bottom-color:var(--accent)}

/* The principal, by email. Regular weight — the tenant is the pill because the
   tenant is what changes and what a mistake lands in; who you are does not. */
.who .principal{color:var(--fg)}

/* The tenant chip as the elevation control.
   A button only for a principal who can use one — CON-NAV-003's omit-don't-disable
   applies to affordances too, and a greyed control would disclose that a platform
   plane exists to someone who holds none of it. */
.tenantname.switchable{font:inherit;color:inherit;background:none;cursor:pointer;
  border:1px solid var(--line);border-radius:var(--r-1);padding:.15rem .5rem;
  display:inline-flex;align-items:center;gap:.35rem}
.tenantname.switchable::after{content:"";width:.3rem;height:.3rem;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:rotate(45deg) translateY(-.08rem);opacity:.7}
.tenantname.switchable:hover,.tenantname.switchable:focus-visible{background:var(--surface-2)}

/* An ACTIVE elevation is not the same state as merely holding platform authority,
   and the page says so rather than leaving the band one colour for both. This is
   the one moment an operator is acting inside a tenant that is not theirs. */
body.is-elevated .planebanner{background:var(--h-warn,#8d6e00);color:var(--h-warn-fg,#fff)}
body.is-elevated .tenantname.switchable{border-color:var(--h-warn,#8d6e00)}

/* NATIVE SELECT POPUPS DO NOT INHERIT THE PAGE'S BACKGROUND.
   `.dlg select` sets `background:transparent;color:inherit`, which is right for
   the closed control and wrong for the list it opens: the popup is painted by the
   platform on a system-white surface while the options keep the inherited colour.
   In dark mode that is light-grey text on white — unreadable, and invisible to
   anyone reviewing in one theme.
   Both properties are set explicitly on `option`, so the popup is legible in
   either theme rather than in whichever one was open at the time. */
.dlg select option,select option{background:var(--bg);color:var(--fg)}

/* An elevated chip is the tenant you are WORKING IN, borrowed rather than yours.
   Marked distinctly from the ordinary chip because it is the one element an
   operator glances at to answer "where am I", and the wrong answer there is the
   most expensive wrong answer on the page. */
.tenantname.elevated{border-color:var(--h-warn,#8d6e00);
  background:color-mix(in srgb,var(--h-warn,#8d6e00) 16%,transparent);
  font-weight:var(--weight-bold)}

/* `.linkish` is --muted, which is correct on the page background and nearly
   invisible on the elevated band's amber. The band sets `color:#fff`, so the
   controls inside it inherit that and keep the underline that marks them as
   controls. "End now" was there all along and could not be read. */
body.is-elevated .planebanner .linkish{color:var(--h-warn-fg,#fff);opacity:.95}
body.is-elevated .planebanner .linkish:hover{color:var(--h-warn-fg,#fff);opacity:1}

/* Publication pause. Loud when paused, because a paused tenant whose operator has
   forgotten is a queue that fills with work nobody is doing — and the whole point
   of ING-EVT-007 is that this state must not be mistaken for a broken relay. */
.pausebar{display:flex;align-items:center;gap:var(--s-3);margin-bottom:var(--s-4);
  padding:var(--s-2) var(--s-3);border:1px solid var(--line);border-radius:var(--radius);
  font-size:var(--text-sm)}
.pausebar.paused{border-color:var(--warn);background:var(--warnbg);color:var(--warn);
  font-weight:var(--weight-bold)}
/* Publication state could not be read. Deliberately NOT styled like "on":
   grey and muted, because the bar is reporting that it does not know rather
   than reporting good news (NFR-USA-004 — the text carries it either way). */
.pausebar.unknown{border-style:dashed;color:var(--muted)}
.pausebar button{font:inherit;font-size:var(--text-sm);padding:var(--s-1) var(--s-3);
  border-radius:var(--radius);margin-left:auto}

/* The audit spool tile on the Dashboard (AUD-CAP-005).
   Colour is never the only carrier: the count and its explanation are text
   (NFR-USA-004), and the grey "not observable" state reads as a sentence
   rather than as an absence of colour. */
.spool { border: 1px solid var(--line); border-left-width: 4px; border-radius: 6px;
         padding: .6rem .8rem; margin: 0 0 .8rem; background: var(--surface); }
.spool h4 { margin: 0 0 .2rem; font-size: .78rem; text-transform: uppercase;
            letter-spacing: .04em; color: var(--muted); }
.spool .srow { display: grid; grid-template-columns: 9rem 9.5rem 1fr; gap: .5rem;
               align-items: baseline; margin: .25rem 0; }
.spool .sname { font-size: .82rem; font-weight: 600; }
.spool .sval  { font-size: .9rem; font-weight: 600; }
.spool .sval.ok      { color: var(--ok); }
.spool .sval.bad     { color: var(--bad); }
.spool .sval.unknown { color: var(--muted); }
@media (max-width: 700px) { .spool .srow { grid-template-columns: 1fr; gap: .1rem; } }
.spool .why { margin: 0; font-size: .82rem; color: var(--muted); line-height: 1.45; }
.spool.healthy { border-left-color: var(--ok); }
.spool.fault   { border-left-color: var(--bad); }
.spool.unknown { border-left-color: var(--line); }

/* Import view (ING-FIL-001). Deliberately small: the view reuses .row, .muted
   and .small like every other view, and adds only what has no equivalent. */

/* One choice per line with its consequence beside it. The consequence is the
   point — "evaluation" means nothing on its own, and the sentence that follows
   it is what stops somebody picking backfill for a live mailbox. */
.radio{display:flex;gap:.5rem;align-items:flex-start;margin:.4rem 0;line-height:1.5}
.radio input{margin-top:.25rem;flex:none}
.radio em{color:var(--muted);font-style:normal}

/* The note explaining why live is absent. Bordered rather than muted-only: it
   is the reason an option somebody may go looking for is not there. */
.note{border-left:3px solid var(--line);padding:.4rem .7rem;margin:.7rem 0 0;
      color:var(--muted);font-size:.82rem;line-height:1.5}

/* Per-file outcome. Colour is never the only carrier (NFR-USA-004) — the word
   is the label and the title says what it means for the reader. */
.chip{display:inline-block;font-size:.7rem;line-height:1.6;padding:0 .45rem;
      border:1px solid var(--line);border-radius:999px;white-space:nowrap}
.chip.ok{color:var(--ok);border-color:var(--ok)}
.chip.warn{color:var(--warn);border-color:var(--warn)}
.chip.bad{color:var(--bad);border-color:var(--bad)}
.chip.muted{color:var(--muted)}

/* Wide tables scroll inside their own box rather than the page. */
.scroll{overflow-x:auto;max-width:100%}

/* Bulk selection on the ingestion queue. Appears only when something is
   selected — a permanently visible bar showing "0 selected" is furniture. */
.qbulk{display:flex;gap:.6rem;align-items:center;flex-wrap:wrap;
       padding:.5rem .7rem;margin:0 0 .6rem;border:1px solid var(--line);
       border-radius:6px;background:var(--surface)}
.qbulk #qbulk-count{font-weight:600}
th.sel,td.sel{width:1.6rem;text-align:center;padding-right:0}

/* The wordmark is a link to the principal's landing view. It must not read as
   body-copy link — it is a title that happens to navigate — so it inherits
   colour and gains its affordance on hover. */
header h1 a{color:inherit;text-decoration:none;display:inline-flex}
/* Underline was the hover affordance while this was a word; it does nothing to
   an SVG. Opacity rather than colour, because --link is the accent and the
   accent is spoken for in this bar. */
header h1 a:hover{opacity:.65}
header h1 a:focus-visible{outline:var(--focus-width) solid var(--focus);outline-offset:3px}

/* ── Tenant-plane dashboard ───────────────────────────────────────────────────
   Six rolled-up cards, reusing the health map's state colours and its `.hsum`
   summary strip rather than inventing a second status palette. That reuse is the
   point: two pages that disagree about what amber looks like is the drift UI.md
   opens with, and status colour is the worst place to have it.

   Where this deliberately differs from `.node`: these cards CONTAIN a list of
   members, so the state tint sits on the card and the member rows carry only a
   dot and a word. Tinting eleven rows inside six tinted cards would leave nothing
   for the eye to rank. Borders for structure, no shadow — UI.md §4 and §9. */
.tdgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
        gap:var(--s-4);margin:var(--s-5) 0}

.tdcard{border:1px solid var(--line);border-left-width:4px;
        border-left-color:var(--h-off);border-radius:var(--radius);
        padding:var(--s-4);background:var(--bg)}
.tdcard.healthy{border-left-color:var(--h-ok);background:var(--h-okbg)}
.tdcard.degraded{border-left-color:var(--h-warn);background:var(--h-warnbg)}
.tdcard.fault{border-left-color:var(--h-bad);background:var(--h-badbg)}
/* Dashed says "absent", the way it does on the health map. Red would send
   somebody looking for a fault that does not exist. */
.tdcard.unknown{border-style:dashed;border-left-style:solid}

.tdhead{display:flex;align-items:baseline;gap:var(--s-3);flex-wrap:wrap}
.tdhead h3{margin:0;font-size:var(--text-lg);font-weight:var(--weight-bold)}
/* The roll-up word, and it is a WORD as well as a colour — NFR-USA-004. */
.tdroll{margin-left:auto;display:flex;align-items:center;gap:var(--s-1);
        font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:lowercase;
        color:var(--h-off)}
.healthy>.tdhead .tdroll{color:var(--h-ok)}
.degraded>.tdhead .tdroll{color:var(--h-warn)}
.fault>.tdhead .tdroll{color:var(--h-bad)}
.tdlead{margin:var(--s-1) 0 0;font-size:var(--text-xs);color:var(--muted);max-width:44ch}

/* The figures. Tabular numerals so a count changing on the 15s tick does not
   shift the row it sits in — UI.md §4. */
.tdstats{display:flex;flex-wrap:wrap;gap:var(--s-5);margin-top:var(--s-3);
         padding-top:var(--s-3);border-top:1px solid var(--line)}
.tdstat{display:flex;flex-direction:column;gap:2px}
/* The SAME figure treatment as the queue's backlog tiles — --text-lg, bold,
   tabular, an uppercase label under it. Matched rather than re-chosen: a second
   big-number style is how "screen seven invents a spacing value" starts. */
.tdstat b{font-size:var(--text-lg);font-weight:var(--weight-bold);line-height:1.2;
          font-variant-numeric:tabular-nums;color:var(--fg)}
/* A figure that is NOT known. CON-MTR-009: unknown and zero are different facts,
   so they get different treatments and this one carries its reason. Muted, never
   red — "nobody could ask" is not a fault. */
.tdstat b.unk{color:var(--h-off);font-weight:400;cursor:help}
.tdstat span{font-size:var(--text-xs);color:var(--muted);
             text-transform:uppercase;letter-spacing:.04em}

/* The members behind the roll-up. Always rendered: a card that says "fault" over
   six services and does not say which one has withheld the only part of the
   answer that shortens the search. */
.tdmembers{margin-top:var(--s-3);padding-top:var(--s-3);border-top:1px solid var(--line)}
.tdrow{display:grid;grid-template-columns:.6rem 9.5rem auto;gap:var(--s-2);
       align-items:baseline;margin:0 0 var(--s-2);font-size:var(--text-xs)}
.tdrow:last-child{margin-bottom:0}
/* `.dot` and its state colours are already defined globally, and reusing them is
   the point — but the global rules are `.healthy .dot`, which match on any
   ANCESTOR carrying the state. Inside a green card that painted every member dot
   green, including the members that were grey, so the one row explaining why the
   card was not fully measured lost the only mark saying so. These re-state the
   member's own state at a specificity that beats the card's. */
.tdcard .tdrow .dot{background:var(--h-off);align-self:center}
.tdcard .tdrow.healthy .dot{background:var(--h-ok)}
.tdcard .tdrow.degraded .dot{background:var(--h-warn)}
.tdcard .tdrow.fault .dot{background:var(--h-bad)}
.tdname{font-weight:var(--weight-bold);color:var(--fg)}
.tdstate{font-weight:var(--weight-bold);color:var(--h-off)}
.tdcard .tdrow.healthy .tdstate{color:var(--h-ok)}
.tdcard .tdrow.degraded .tdstate{color:var(--h-warn)}
.tdcard .tdrow.fault .tdstate{color:var(--h-bad)}
/* The reason wraps onto its own line below ~34rem rather than being truncated:
   on this page the `why` IS the content, and "derived — a service that checks
   this reports ready" truncated to "derived —" says nothing at all. */
.tdrow .why{grid-column:3;font-weight:400;color:var(--muted)}
@media (max-width:34rem){
  .tdrow{grid-template-columns:.6rem auto}
  .tdrow .why{grid-column:1/-1;padding-left:var(--s-3)}
}

/* The counts banner. Two causes, two treatments, and neither is red: a capped
   projection and an unreadable one are both limits on what can be said, not
   faults in the platform. */
.tdnote{margin:0 0 var(--s-4);padding:var(--s-2) var(--s-3);border-radius:var(--radius);
        font-size:var(--text-xs);border:1px solid var(--line);color:var(--muted)}
.tdnote.capped{border-left:4px solid var(--h-warn);background:var(--h-warnbg);color:var(--fg)}
.tdnote.unknown{border-style:dashed;border-left:4px solid var(--h-off)}

/* Infrastructure consoles in the System menu. Marked as leaving, on the same
   reasoning as the health map's `.leaves` cards (CON-SYS-010): a new tab and a
   second sign-in are coming, and an operator should know that before clicking
   rather than after landing on someone else's login form. */
nav .navsub a.navext .ext{margin-left:.4em;color:var(--h-warn);opacity:.75;font-size:.9em}
nav .navsub a.navext:hover{border-color:var(--h-warn)}
