/* ================= THE ADMIN CONSOLE, IN THE SUITE'S CLOTHES =================
   One file per page layout, the pattern alt-tasks/alt-notes/alt-calendar/alt-team/alt-okrs
   already follow. Read alt-tokens.css and alt-components.css first — a new primitive here is
   likely a duplicate of one already there.
   Not `.pane-grid` any more: the classic console sized its columns from the WINDOW, but a
   240px section list down the left makes the pane's own width the real question — a container
   query, the same mechanism the email bench already used.
   Nothing here hides a control, moves one off the page, or changes what one does. */

/* ---------- the console frame ----------
   240px, not the suite's 320 — nine section labels need less than a task list's note groups
   and board names. Deliberately outranks alt-responsive.css's `--a-left` steps (300/280px):
   those claw width back from a 320px rail, and 240 is already narrower than either. */
.alt-app.a-admin{ --a-left:240px; }

/* THE CONTEXT STRIP IS A SIBLING OF .a-main, NEVER A CHILD — `altGridTracks()` counts
   .a-main's children to place the splitters, so a strip inside it is a phantom column.
   Stated in sys_design-system.md §12; repeated here because this page has no splitter and
   the reason would otherwise look like it did not apply. */
.a-admin-band{
  display:flex; align-items:baseline; gap:var(--a-3); flex-wrap:wrap;
  padding-bottom:var(--a-2);
}
.a-admin-band h1{
  margin:0; font-family:var(--a-display); font-size:18px; font-weight:700; color:var(--a-text);
}
/* THE TITLE AND ITS MANUAL, ONE GROUP — not a `.a-quick` strip of its own, which gave every
   screen a tall empty band before "Admin Console" ever appeared. Nesting the link here costs
   no extra height: centred rather than baseline, one item in the band's own flex row. */
.a-admin-title{ display:flex; align-items:center; gap:var(--a-2); flex:0 0 auto; }
/* One faint sentence, never a banner — an administrator reads it once per area and it must
   not cost a band of height for the rest of their working life. `role="status"` announces the
   scope change. 11px is the floor for --a-faint (sys_design-system.md §17). */
.a-admin-scope{
  margin:0; font-size:11px; line-height:1.5; color:var(--a-faint);
  max-width:96ch; flex:1 1 320px; min-width:0;
}

/* ---------- the section list ----------
   A list of destinations, not a tab strip — nine sections wrapped the classic header's strip
   to two rows at 500px and took the page's scrollbar with it. Down the left, one column
   scrolls independently and the pane keeps its full height. */
.a-admin-nav{
  gap:2px; overflow-y:auto; min-height:0;
  /* Sticks to the top of its own column rather than the document: each column scrolls
     itself and the page does not (sys_design-system.md §12). */
  align-content:start;
}
.a-admin-nav-h{ margin:0 0 var(--a-1); padding:0 var(--a-2); }
/* THE ROW. A real <button>, so it is focusable, answers Enter and Space for free, and
   reports which one is current — a div with a click handler is a destination a keyboard
   cannot reach. */
.a-admin-nav button{
  display:flex; align-items:center; gap:var(--a-2); width:100%; min-width:0;
  height:var(--a-row); padding:0 var(--a-2); border:1px solid transparent;
  border-radius:var(--a-r-sm); background:transparent; color:var(--a-dim);
  font-family:var(--a-body); font-size:13px; font-weight:600; text-align:left;
  cursor:pointer; white-space:nowrap;
}
.a-admin-nav button:hover{ background:var(--a-raised); color:var(--a-text); }
.a-admin-nav button:focus-visible{ outline:2px solid var(--a-ring); outline-offset:-2px; }
.a-admin-nav button svg{ width:16px; height:16px; flex:0 0 auto; }
.a-admin-nav .lb{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }
/* THE CURRENT SECTION, MARKED THREE WAYS — a wash, an accent edge and the accent text —
   because colour alone is not a state signal (§20). `aria-current="page"` is what the
   selector keys on, so the attribute and the appearance cannot drift apart: there is no
   `.on` class to forget to remove. */
.a-admin-nav button[aria-current="page"]{
  background:var(--a-blue-soft); border-color:var(--a-blue); color:var(--a-blue-text);
}
/* A COUNT IS A FACT ABOUT THE SECTION, not a badge for attention. Tabular so a column of
   them does not jitter, and pushed to the right edge so they line up down the list however
   long the labels. */
.a-admin-nav .n{
  flex:0 0 auto; font-size:11px; font-weight:700; font-variant-numeric:tabular-nums;
  color:var(--a-faint);
}
.a-admin-nav button[aria-current="page"] .n{ color:var(--a-blue-text); }
/* A SECTION WHOSE COUNT IS NOT IN YET SAYS NOTHING, rather than saying nought. A zero that
   means "not loaded" is a lie the reader cannot tell from a zero that means "none". */
.a-admin-nav .n:empty{ display:none; }

/* THE PANE-LOCAL BLOCK GRID USED TO LIVE HERE, AND IT IS GONE ON PURPOSE. It was a second
   implementation of admin.html's own `.pane-grid`, used only by #overviewPane, which the owner
   asked to match "the same design style as the rest of the admin panels" — one question, one
   implementation.
   What was lost, said plainly: this grid sized columns from a CONTAINER query on the pane,
   while `.pane-grid` asks a MEDIA query about the window — so every `.pane-grid` pane now
   reaches two/three columns a little before it has the room (the defect `.bench-questions`
   already hit). The fix is to move `.pane-grid` itself onto a container query once, for all
   its panes — not done here, since it changes Security and SMTP's breakpoints too and needs
   browser measurement. `.a-admin-sec` below is unrelated and stays: #auditPane still speaks
   that vocabulary. */

/* ---------- a block ---------- */
.a-admin-sec{ min-width:0; }
.a-admin-sec > h2{
  margin:0 0 var(--a-1); font-family:var(--a-display); font-size:14px; font-weight:700;
  color:var(--a-text);
}
.a-admin-sec > h3{
  margin:var(--a-4) 0 var(--a-1); font-family:var(--a-display); font-size:13px;
  font-weight:700; color:var(--a-text);
}
/* THE PROSE KEEPS A MEASURE; THE PANE DOES NOT. Cap the TEXT, never the page — an
   application surface has no page-level max-width, and the tables want every pixel. */
.a-admin-p{
  margin:0 0 var(--a-3); font-size:12px; line-height:1.6; color:var(--a-dim); max-width:78ch;
}
.a-admin-p:last-child{ margin-bottom:0; }

/* ---------- the bulk bar ----------

   THE COUNT IS THE AFFORDANCE. A destructive action states what it would act on and is
   DISABLED at nought rather than hidden — `Delete 0 teams` disabled says what the control
   is for; a control that vanishes teaches nothing (sys_design-system.md §9). */
.a-admin-bulk{
  display:flex; align-items:center; gap:var(--a-2); flex-wrap:wrap; min-width:0;
  padding-bottom:var(--a-2); margin-bottom:var(--a-2);
  border-bottom:1px solid var(--a-line-soft);
}
.a-admin-bulk .ct{ margin-right:auto; font-size:11px; color:var(--a-faint); white-space:nowrap; }

/* A DETAIL ROW: a name, then quiet metadata, then whatever acts on it. Unscoped inside the
   detail on purpose — the account dialog builds the same rows, and a `.a-admin-detail .drow`
   selector is why every row in its Teams, Boards and Groups sections once rendered as
   unstyled block children running together ("Billing Team 1team admin"). A browser has
   nothing to say about a selector that never matches, which is how that survived review. */
.a-admin-drow{
  display:flex; align-items:center; gap:9px; padding:5px 0; font-size:13px; flex-wrap:wrap;
}
.a-admin-drow + .a-admin-drow{ border-top:1px solid var(--a-line-soft); }
/* `min-width:0` LETS THE BOX SHRINK; IT DOES NOT MAKE THE TEXT INSIDE IT WRAP, and those are
   different things. A person with no display_name falls back to their EMAIL as the name
   (admin-team-detail.js: `u.display_name || u.email || …`), and an address like
   a long address with no spaces in it has no space in it — so the flex item duly shrank to
   nothing and the unbreakable string painted straight over the meta column beside it. The owner
   saw exactly that on 2026-09-18: "not styled, words are running into themselves", on the two
   rows in a team that had no display name, while the row above them with a real name looked
   perfect. That is the tell — the layout was never broken, only the rows whose content could not
   break were, which is why it survived every review of a team whose people all had names.

   `overflow-wrap:anywhere` is the fix, and it is the SAME one CSS/admin.css already applies to
   the identical situation (`.aea-attempt .dmeta`), so this is the house answer rather than a
   second one. `.dm` gets a basis and `min-width:0` too: without them it is `flex:0 1 auto` with
   `min-width:auto` and cannot shrink at all, which is the other half of the same collision. */
.a-admin-drow .dn{ font-weight:600; flex:1 1 120px; min-width:0; overflow-wrap:anywhere; }
.a-admin-drow .dm{ color:var(--a-faint); font-size:12px; flex:1 1 200px; min-width:0; overflow-wrap:anywhere; }
/* A row whose job is an action rather than a fact takes no separator above it. */
.a-admin-drow.act-row{ border-top:none; padding-top:8px; }
/* THE SENTENCE UNDER A CONFIGURATION ROW belongs to the row above it, so it carries no
   separator of its own and sits inside the row's rhythm rather than starting a new one. */
.a-admin-config-note{ margin:2px 0 var(--a-2); }

/* ---------- a data table ----------

   FOUR ANALYSES, ONE SHAPE — scenarios, composition, ages, maintenance answer four halves of
   one question, and four table treatments would read as four unrelated features. Wrapped in
   its own scroller: wide content scrolls INSIDE its container and the page never scrolls
   sideways. */
.a-admin-tscroll{ overflow-x:auto; min-width:0; }
.a-admin-table{ border-collapse:collapse; width:100%; font-size:12px; }
.a-admin-table th{
  text-align:left; padding:6px 12px 6px 0; white-space:nowrap;
  font-size:10px; font-weight:700; letter-spacing:.09em; text-transform:uppercase;
  color:var(--a-dim); border-bottom:1px solid var(--a-line-soft);
}
.a-admin-table td{
  padding:7px 12px 7px 0; border-bottom:1px solid var(--a-line-soft);
  color:var(--a-text); font-variant-numeric:tabular-nums; vertical-align:middle;
}
.a-admin-table tr:last-child td{ border-bottom:none; }
.a-admin-table td.num,.a-admin-table th.num{ text-align:right; padding-right:var(--a-3); }
/* THE RECOMMENDED ROW, marked with weight as well as a wash — a pick communicated by
   background alone is one a reader with any colour difficulty cannot see. */
.a-admin-table tr.is-pick td{ background:var(--a-blue-soft); font-weight:700; }
/* Open content is protected, and the row says so quietly rather than sitting beside the
   completed one as though it were the same kind of opportunity. */
.a-admin-table tr.is-protected td{ color:var(--a-dim); font-style:italic; }

/* ---------- figures ----------

   `.a-stats`/`.a-stat` come from alt-components.css. What this page adds is a wider strip:
   the console reports six to eight figures where a task band reports three, so the track
   count is measured rather than fixed at three. */
.a-admin-figures{ grid-template-columns:repeat(auto-fit, minmax(136px, 1fr)); }
/* A FIGURE THAT IS NOT A DESTINATION IS NOT A BUTTON. `.a-stat` is a control — it has
   `cursor:pointer` and a hover edge — so a read-only figure says so by being a div, and this
   takes the affordance back off it. Never a disabled button: disabled means "you could, but
   not now", and these were never going anywhere. */
.a-admin-figures > div.a-stat{ cursor:default; }
.a-admin-figures > div.a-stat:hover{ border-color:var(--a-line-soft); }

/* ---------- the activity feed ----------

   EVERY ROW IS A RECORD THAT EXISTS. This feed is composed from role changes, purge runs and
   the creation timestamps the tables already carry — there is no audit table in this
   database, and inventing a row would be inventing an event. Where a kind of change is not
   recorded anywhere, the feed says so rather than leaving the reader to assume it is quiet. */
.a-admin-feed{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.a-admin-fitem{
  display:grid; grid-template-columns:auto minmax(0,1fr) auto; gap:var(--a-1) var(--a-2);
  align-items:baseline; padding:8px var(--a-2); border-radius:var(--a-r-sm);
}
.a-admin-fitem:hover{ background:var(--a-raised); }
.a-admin-fitem .k{ align-self:center; color:var(--a-faint); display:flex; }
.a-admin-fitem .k svg{ width:15px; height:15px; }
.a-admin-fitem .tx{ font-size:12px; line-height:1.5; color:var(--a-text); min-width:0; }
.a-admin-fitem .tx b{ font-weight:600; }
.a-admin-fitem .ag{
  font-size:11px; color:var(--a-faint); white-space:nowrap; font-variant-numeric:tabular-nums;
}
.a-admin-fitem .wh{ grid-column:2; font-size:11px; color:var(--a-faint); }

/* ---------- account retention (People pane) ----------

   THE FOUR THRESHOLD FIELDS WRAP RATHER THAN SQUEEZE. `.control-row` already wraps above
   640px on its own (CSS/components.css), but its default gap is tuned for a single line of
   short controls; `.ar-fields` only widens the row gap once it has actually wrapped to two
   lines, so four captioned fields never sit closer together than the rest of this console's
   rows do. `.a-field` itself — the caption-over-input pair — is alt-components.css's, reused
   rather than restated. */
/* TWO BY TWO, AND THE COLUMNS LINE UP BECAUSE A GRID MAKES THEM — REQUESTED 2026-09-15.

   The four thresholds were one flex row, so their captions ran together into a single band of
   small caps ("FIRST NOTICE (DAYS) FINAL NOTICE (DAYS) DELETION GRACE (DAYS) KEEP DELETION LOG
   (MONTHS)") with no pause anywhere a reader could use. They are also two different questions:
   when somebody is TOLD, and how long the record is KEPT. One row per question.

   `max-content` IS WHAT ALIGNS THE SECOND ROW WITH THE FIRST, and it is why this is a grid
   rather than two flex rows. A grid column is sized to its widest member, so column one is as
   wide as the longer of "First notice" and "Deletion grace", and both fields under it start at
   the same x. Two independent flex rows would each pack from the left and the second row's
   second field would sit wherever its own first caption happened to end — the thing the owner
   asked to avoid.

   JUSTIFIED START, NOT STRETCHED. The columns take the width their captions need and stop;
   nothing is spread across the pane, so the pair still reads as a group rather than as two
   items pushed to opposite edges.

   AT PHONE WIDTH IT FOLDS TO ONE COLUMN, because two captions of this length side by side
   below ~520px would wrap mid-phrase, which is the defect this whole change is about. */
.alt-app .ar-fields{
  display:grid;
  grid-template-columns:max-content max-content;
  justify-content:start;
  align-items:start;
  column-gap:var(--a-6);
  row-gap:var(--a-4);
}
.alt-app .ar-fields .a-field{ flex:0 0 auto; }

/* THE PER-ROW EXEMPTION CONTROL. `.ar-exempt` is emitted by userRow() in
   JS/features/admin-users.js and is also the hook account-retention-people.test.js selects on,
   so it is load-bearing in two directions — but nothing styled it, and a class a page emits
   and never defines is the shape a check exists to catch.

   IT NEEDS A RULE ON ITS MERITS, not to satisfy one. Its label is "Exempt from deletion" or
   "Remove exemption" — the two longest strings any button in a people row carries — and in a
   row that already holds a name, an email, counts, badges and three other buttons it was the
   one that wrapped. `nowrap` and `flex:0 0 auto` keep it whole and stop it being squeezed by
   the metadata beside it. `margin-left:auto` is deliberately NOT set: it would push the
   control to the row's far edge, away from the dormancy badge it acts on. */
.alt-app .ar-exempt{ white-space:nowrap; flex:0 0 auto; }
@media (max-width:520px){
  .alt-app .ar-fields{ grid-template-columns:max-content; }
}

/* ---------- deleted accounts (People pane) ----------

   ONE ROW, TWO LINES: who and what happened, then when and what it cost. Denser than
   `.a-admin-fitem` above on purpose — that feed is one sentence per record, this is several
   facts about one account — so it earns its own class rather than a variant of that grid. */
.alt-app .ar-log-row{
  display:flex; flex-direction:column; gap:4px; padding:10px var(--a-2);
  border-radius:var(--a-r-sm); border-bottom:1px solid var(--a-line-soft);
}
.alt-app .ar-log-row:last-child{ border-bottom:0; }
.alt-app .ar-log-row:hover{ background:var(--a-raised); }
.alt-app .ar-log-head{
  display:flex; align-items:center; flex-wrap:wrap; gap:var(--a-2);
}
.alt-app .ar-log-name{ font-size:13px; font-weight:600; color:var(--a-text); }
.alt-app .ar-log-email{ font-size:12px; color:var(--a-dim); }
.alt-app .ar-log-meta{
  display:flex; flex-wrap:wrap; gap:var(--a-2) var(--a-3);
  font-size:11px; color:var(--a-faint); font-variant-numeric:tabular-nums;
}
.alt-app .ar-log-counts,
.alt-app .ar-log-transfers{ font-size:12px; line-height:1.5; color:var(--a-dim); }

/* ---------- narrow ----------

   THE SECTION LIST BECOMES A STRIP. Below 1024 the suite collapses .a-main to one column
   (alt-responsive.css), so a 240px sidebar would become a 240px-tall block of nine rows
   above every pane. As a horizontally-scrolling row of the same buttons it costs one line —
   the same thing the shell's own nav does at 820, so it is a mechanism this experience
   already has rather than a second answer.

   NO NEW BREAKPOINTS. 1023 and 639 are two of the suite's six; a seventh here would be a
   second responsive story for one page. */
@media (max-width: 1023px){
  .a-admin-nav{
    flex-direction:row; overflow-x:auto; overflow-y:hidden; gap:var(--a-1);
    padding-bottom:var(--a-1); scrollbar-width:none;
    border-bottom:1px solid var(--a-line-soft);
    /* A HORIZONTAL TAB STRIP IS ITS OWN HEIGHT AND NEVER ITS ROW'S.
       Reported 2026-09-20 by a wireframe-parity pass: on the Teams pane below 1024px the strip
       ballooned — measured 153px at 900px against 49px on every other pane, leaving a band of
       dead space above the Teams controls.

       ITS CHILDREN WERE INNOCENT, which is what made it look mysterious. Measured: every button
       44px tall at top:1 on BOTH panes, byte-identical markup, `flex-wrap: nowrap` — nothing
       was wrapping and nothing was taller. The box itself was being stretched.

       `.a-main` IS A GRID and its `align-items` computes to `normal`, i.e. stretch, so this nav
       takes the height of whatever row it lands in. In the stacked layout that height varies
       with the pane beside it, which is why ONE pane looked broken and the rest did not — the
       strip was reporting the grid's spare space, not its own content.

       `align-self:start` is the fix and it belongs in this media query alone: in the WIDE
       layout the nav is a real column and stretching to the row is exactly right. */
    align-self:start;
  }
  .a-admin-nav::-webkit-scrollbar{ display:none; }
  /* The heading names the strip for a screen reader and would otherwise take a column of the
     scroller for a word nobody needs at this width. */
  .a-admin-nav-h{ display:none; }
  .a-admin-nav button{ width:auto; flex:0 0 auto; }
  /* THE COUNT GOES, NOT THE LABEL. A strip of nine numbers is nine things to read past to
     find the word; the label is what the control is for. The counts are still on the section
     itself, at the head of its own pane. */
  .a-admin-nav .n{ display:none; }
}
@media (max-width: 639px){
  .a-admin-band h1{ font-size:16px; }
  /* EVERY TARGET IS AT LEAST 44px ON A PHONE (§20). --a-row is already 48 here, so the rows
     comply; this is the strip's own buttons, which size to their content. */
  .a-admin-nav button{ min-height:44px; }
}

/* ---------- one team, in depth ----------

   THE CARD IS `.a-modal` FROM alt-shell.css — fixed, centred, min(920px,92vw) by
   min(760px,88vh) — so this adds only what that does not say: the head row, and the one
   scroll region under the tabs. `.a-tabs` / `.a-tab` are the shared component, now in
   alt-components.css.

   A DETAIL SURFACE HAS ONE SCROLL REGION, and it is the body. The head and the tabs are
   pinned, because a card that scrolls entirely takes its own title and close button away
   with it — which is the defect the team board's task editor had. */
.a-admin-team-modal{ width:min(760px, 94vw); height:min(680px, 88vh); }
.a-admin-team-head{
  display:flex; align-items:center; gap:var(--a-2); flex:0 0 auto;
  padding:12px 14px; border-bottom:1px solid var(--a-line-soft); background:var(--a-float);
}
.a-admin-team-head h2{
  margin:0; min-width:0; font-family:var(--a-display); font-size:15px; font-weight:700;
  color:var(--a-text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* `auto` and never `scroll`: a permanent scrollbar on a short roster is a card that looks
   broken. `scrollbar-gutter` keeps the content from stepping sideways when one appears. */
.a-admin-team-body{
  flex:1 1 auto; min-height:0; overflow-y:auto; scrollbar-gutter:stable;
  padding:14px;
}
/* The tab strip is one line tall, and it does not wrap: four labels and their counts fit at
   every width this card has. */
.a-admin-team-modal .a-tabs{ flex:0 0 auto; flex-wrap:nowrap; padding:0 10px; }
@media (max-width: 639px){
  /* THE CARD IS THE WINDOW ON A PHONE. A centred 94vw card with 6vw of scrim around it wastes
     the only dimension a roster needs, and the scrim is not a control here — the close button
     and Escape both are. */
  .a-admin-team-modal{
    /* vh first: the fallback for an engine without dvh, not a second answer. */
    width:100vw; height:100vh; height:100dvh; max-width:none; border-radius:0; border:0;
    top:0; left:0; transform:none;
  }
}

/* ==========================================================================
   THE CONSOLE'S EXISTING VOCABULARY, IN THIS EXPERIENCE'S CLOTHES

   NOT A FORK, AND NOT A REWRITE OF NINE MODULES. The console's behaviour lives in
   JS/features/admin-*.js — nine files, forty-odd capabilities, a typed-confirmation purge
   chain, and a batch semantics that keeps going on a per-item refusal. Every one of their
   render functions emits the console's own classes: `.row`, `.badge`, `.empty`, `.detail`,
   `.drow`, `.sec-section`, `.bulk-bar`, `.storage-*`, `.email-*`, `.bench-*`, `.smtp-*`.

   REWRITING THAT MARKUP TO EMIT `.a-*` WOULD PUT EVERY ONE OF THOSE CAPABILITIES THROUGH A
   REGRESSION IN THE SAME CHANGE THAT RE-PRESENTS THE PAGE — and a defect in a destructive
   path would then be impossible to attribute to either half. So this is a PRESENTATION
   LAYER, in exactly the shape alt-shared-dialogs.css and alt-message-center.css already
   take for the same problem: a shared surface that arrives wearing the classic app's chrome
   and could not otherwise look like this experience drew it.

   SCOPED, AND WINNING ON SPECIFICITY RATHER THAN ON ORDER. Every selector here is prefixed
   `.alt-app`, which makes it (0,2,0) against the page's own (0,1,0) rules. That is what
   lets the page keep the inline <style> its modules and several mechanical checks still
   depend on, while this decides what a reader actually sees. Do NOT rely on source order:
   an inline <style> block sits after every <link> in the document.

   EVERY RULE HERE IS A SURFACE, A RADIUS, A TYPE SCALE, A COLOUR OR A FOCUS RING. None of
   them hides a control, moves one off the page, or changes what a button does. A rule in
   this file that makes something unreachable is a defect in this file.
   ========================================================================== */

/* ---------- controls ----------

   `.btn` IS THE CONSOLE'S BUTTON AND `.a-btn` IS THE SUITE'S. The modules emit the first, so
   the first is given the second's shape rather than the modules being edited. Height from
   --a-ctl, so a row of a select, a checkbox label and two buttons reads as one row — which
   is the whole reason the suite has one control height. */
.alt-app .btn{
  height:var(--a-ctl); padding:0 12px; border-radius:var(--a-r-sm);
  font-family:var(--a-body); font-size:13px; font-weight:600; line-height:1;
  background:var(--a-raised); color:var(--a-text); border:1px solid var(--a-line);
}
.alt-app .btn:hover:not(:disabled){ border-color:var(--a-line-strong); color:var(--a-text); }
.alt-app .btn.small{ height:28px; padding:0 10px; font-size:12px; }
.alt-app .btn.primary{
  background:var(--a-blue-fill); border-color:var(--a-blue); color:#fff;
}
.alt-app .btn.primary:hover:not(:disabled){ filter:brightness(1.08); color:#fff; }
/* DANGER IS AN OUTLINE, NOT A FILL. A console whose Delete button is solid red on every
   list reads as an alarm rather than as a control. */
.alt-app .btn.danger{
  background:var(--a-red-soft); border-color:var(--a-red); color:var(--a-red);
}
.alt-app .btn.danger:hover:not(:disabled){ border-color:var(--a-red); color:var(--a-red); }
.alt-app .btn.active{
  border-color:var(--a-blue); color:var(--a-blue-text); background:var(--a-blue-soft);
}
.alt-app .btn:focus-visible{ outline:2px solid var(--a-ring); outline-offset:2px; }

/* ---------- THE FOLD-ALL TOGGLE, FIVE INSTANCES, ONE MEASUREMENT ----------

   WHAT THIS FILE OWNS HERE IS THE FIT AND NOTHING ELSE. The control is the shared one —
   altFoldAllHtml()/altFoldAllEl() in JS/alt/alt-view-controls.js — and its LIT STATE is declared
   once for every host in CSS/alt/alt-components.css, keyed off `aria-pressed`. Five pages draw
   this button and one of them redefining "pressed" is five pages quietly disagreeing about what
   blue means, so nothing below touches colour.

   WHY IT NEEDS A RULE AT ALL. The console's small button is 28px (`.alt-app .btn.small` directly
   above) and the suite's is 26px (`.a-btn.sm` in alt-components.css). This control sits in a
   `.control-row` shoulder to shoulder with `.btn.small` neighbours — Clear filters, Select all, a
   sort `<select>` — and a row whose buttons differ by two pixels reads as a row that has come
   apart. So each instance is given the console's own height, which is the same reasoning that put
   `.alt-app .btn{height:var(--a-ctl)}` at the top of this block: the console has one control
   height and everything standing in a row obeys it.

   AND IT IS KEYED ON FIVE CLASSES OF THIS PANE'S OWN, NEVER ON THE SHARED `.a-fold-all`.
   That distinction is the whole reason the component takes a `cls`. A rule written as
   `.alt-app .a-btn.a-fold-all{height:28px}` reads identically and claims EVERY fold-all that ever
   lands on this page, including a `compact:true` one sized 18px or a dialog's own — which is how
   notes' 18px geometry nearly reached a labelled 26px control and clipped its text. admin.html
   hosts panes this lane does not own (the team pane and organization settings both draw folds of
   their own), so the leak here is not hypothetical. Each site names itself; a sixth instance gets
   a sixth class and a deliberate decision, not this rule by accident.

   THE FIVE SHARE ONE BODY because they are one control in one kind of row, measured once. They
   are listed separately rather than collapsed into a prefix selector so that a site which later
   needs to differ is a one-line change and shows up in a grep for its own name. */
.alt-app .a-btn.a-admin-fold-teams,
.alt-app .a-btn.a-admin-fold-boards,
.alt-app .a-btn.a-admin-fold-users,
.alt-app .a-btn.a-admin-fold-okrcleanup,
.alt-app .a-btn.a-admin-fold-storage{
  height:28px; padding:0 10px;
}

/* THE MOUNT THE MODULES FILL. `display:contents` so the BUTTON is the flex item of the control
   row, exactly as the two `.btn.small` it replaced were — a wrapper box of its own would sit
   between the row's `align-items:center` and the control it holds.

   IT ALSO ANSWERS THE EMPTY CASE, WHICH IS NOT A SIDE EFFECT BUT THE REASON. The component
   returns nothing when there is no group to fold, so the mount is genuinely empty then — and an
   element generating no box contributes nothing to `.control-row`'s 8px gap. A plain `<span>`
   left empty would hold that gap open and leave a hole in the row where a control used to be. */
.alt-app .a-admin-fold-mount{ display:contents; }

/* ---------- fields, and the exclusion that is the whole point of them ----------

   THIS BLOCK DRESSES THE CONSOLE'S CLASSIC FIELDS, NOT THE SUITE'S. The admin modules emit
   plain `<input>` and `<select>` — the people filter, the audit filter, the config rows — and
   this file gives them the suite's control language because those modules are not this file's
   to edit. A field that already carries `.a-input`, `.a-select` or `.a-textarea` is ALREADY
   suite vocabulary: alt-components.css is its canonical home and this file has nothing to add.

   WITHOUT `:not(.a-input)` IT DID NOT RESTATE THAT RULE — IT BEAT IT, AND THE SHELL'S GLOBAL
   SEARCH WAS THE CASUALTY. Reported as "the search text box at the top of the page seems to
   have two helper texts layered on each other". It has one. What sat on top of the placeholder
   was the magnifier at one end and the ⌘K keycap at the other, because this rule un-bought the
   space they stand in:

     .a-search .a-input     { width:100%; padding-left:30px }              (0,2,0)  components
     .a-top-search .a-input { height:var(--a-ctl-lg); padding-right:38px } (0,2,0)  shell
     .alt-app input[type=search] { height:var(--a-ctl); padding:0 10px }   (0,2,1)  HERE

   Both glyphs are absolutely positioned and `pointer-events:none`, so they occupy no layout and
   nothing pushes the text out of their way — the two component rules buy that room by hand, and
   alt-shell.css:104 says so in its own comment. A class plus a type plus an attribute is
   (0,2,1) and beats both from anywhere in the cascade, so on admin.html and nowhere else the
   placeholder started at x=10 UNDER the magnifier and ran to the right edge UNDER the keycap,
   with the control also dropped from --a-ctl-lg (36px) to --a-ctl (32px), a row out of line
   with every other suite page's header.

   THE EXCLUSION IS FREE, WHICH IS WHY IT IS THE RIGHT FIX. The eight declarations below are
   byte-for-byte the ones `.a-input,.a-select,.a-textarea` already makes in
   alt-components.css:103-107 (that rule adds only `min-width:0`). So handing the suite's own
   controls back to the suite changes nothing about how they look — it only stops this file
   outranking the two VARIANT rules layered on top of them.

   FIXED HERE, NOT IN THE COMPONENT. Raising `.a-search .a-input` to out-specify a page rule
   would make one page's accident the shared control's permanent shape and push the workaround
   onto the four suite pages that were always correct. CSS/okr-suite.css:234-253 is the
   post-mortem of this identical collision on okrs.html, states that escalation rule, and
   carries the identical `:not(.a-input)` form; this is the second site of the same defect,
   fixed the same way. _docs/tests/search-field-chrome.test.js now fails on either. */
.alt-app input[type=text]:not(.a-input),.alt-app input[type=email]:not(.a-input),
.alt-app input[type=password]:not(.a-input),.alt-app input[type=date]:not(.a-input),
.alt-app input[type=number]:not(.a-input),.alt-app input[type=tel]:not(.a-input),
.alt-app input[type=search]:not(.a-input),.alt-app input[type=url]:not(.a-input),
.alt-app select:not(.a-select),.alt-app textarea:not(.a-textarea){
  height:var(--a-ctl); padding:0 10px; border-radius:var(--a-r-sm);
  background:var(--a-bg); border:1px solid var(--a-line); color:var(--a-text);
  font-family:var(--a-body); font-size:13px;
}
.alt-app textarea:not(.a-textarea){ height:auto; padding:8px 10px; line-height:1.5; }
/* A multi-select with size>1 has to keep its rows; height is the single-row control's rule. */
.alt-app select[multiple][size]:not([size="1"]){ height:auto; padding:6px 8px; }
/* NO EXCLUSION HERE, DELIBERATELY, AND IT COSTS NOTHING: these declare the same values
   `.a-input:focus` does, and they are not geometry — nothing layered on the search field
   reserves a focus colour. Keeping them at (0,2,1) is also what stops a lighter rule further
   down freezing the border grey while somebody is typing. */
.alt-app input:focus,.alt-app select:focus,.alt-app textarea:focus{
  outline:none; border-color:var(--a-blue);
}
/* `opacity:1` IS THE HALF THAT MATTERS — sys_design-system.md §18. Firefox applies its own
   ~54% opacity to ::placeholder ON TOP of whatever colour is set, which drags --a-faint under
   the 3:1 floor; a colour alone does not defeat it. CSS/help.css:273 records the same trap and
   CSS/okr-suite.css:310 carries the same pair. This rule is (0,1,2) and outranks
   `.a-input::placeholder` (0,1,1), so on this page it is the one that decides. */
.alt-app input::placeholder{ color:var(--a-faint); opacity:1; }
.alt-app input[type=checkbox],.alt-app input[type=radio]{ accent-color:var(--a-blue); }

/* ---------- a record row ----------

   The modules' `.row` becomes the suite's row: the same panel, line and radius the section
   list and the new tables use, so a restyled list and a new one read as one console. */
.alt-app .row{
  gap:var(--a-3); padding:0 var(--a-3); min-height:var(--a-row); margin-bottom:4px;
  background:var(--a-panel); border:1px solid var(--a-line-soft);
  border-radius:var(--a-r-sm);
}
.alt-app .row:hover{ border-color:var(--a-line); }
.alt-app .row .pick{ accent-color:var(--a-blue); }
.alt-app .row .name{ font-size:13px; font-weight:600; color:var(--a-text); }
.alt-app .row .sub{ font-size:11px; color:var(--a-faint); margin-top:1px; }
.alt-app .row .meta{ font-size:11px; color:var(--a-faint); }
.alt-app .row .expand{ color:var(--a-faint); }
.alt-app .row .expand.open{ color:var(--a-blue-text); }

/* ---------- a status badge ----------

   ONE BADGE, THE SUITE'S. `.a-chip-st` is an OUTLINE whose colour sits on the border, which
   is what stops a column of them reading as a column of alerts. The console's three
   variants map onto three of the suite's status colours. */
.alt-app .badge{
  border-radius:var(--a-r-pill); padding:2px 8px; font-size:10px; font-weight:700;
  letter-spacing:.08em; text-transform:uppercase;
  background:transparent; border:1px solid var(--a-line-strong); color:var(--a-dim);
}
.alt-app .badge.admin{ border-color:var(--a-blue-text); color:var(--a-blue-text); }
.alt-app .badge.pending{ border-color:var(--a-amber); color:var(--a-amber); }

/* ---------- the shapes a pane is built from ---------- */
.alt-app .sec-section h3{
  font-family:var(--a-display); font-size:14px; font-weight:700; color:var(--a-text);
}
.alt-app .sec-sub{
  font-family:var(--a-display); font-size:13px; font-weight:700; color:var(--a-text);
}
.alt-app .sec-desc{ font-size:12px; line-height:1.6; color:var(--a-dim); }

/* ---------- A SECTION IS A PANEL ----------

   REQUESTED 2026-09-15, for the People pane only: "create section panels to clearly see these
   two portions of the people capabilities. Use the standard panel style to better pronounce
   these features."

   WIDENED 2026-09-16, by the same owner looking at the SMTP page: "inspect the People page and
   apply the same paneling for logical portions of the SMTP configuration page. Apply the same
   for System and Security page. Apply the same for Human verification and the audit pages. Use
   the SAME styling ... Do not wander stay true to the design system."

   THE OLD REASONING, AND WHY IT DID NOT SURVIVE. This rule read `#usersPane > .sec-section`,
   and the scope was the argument: every other pane is `.pane-grid`, where the grid's own gaps
   and columns already separate one block from the next, so a border there would state a
   boundary the layout was already making. The owner has now looked at those grid panes and
   asked for the border anyway. A gap is a pause; a border is a claim about where a thing ENDS,
   and on blocks that each own a switch, a saved value and a status line that claim was worth
   making twice. The superseded argument is kept here rather than deleted, because it is the one
   anyone narrowing this rule again will reach for, and it should be answered rather than
   rediscovered.

   WIDENING THE EXISTING RULE, NOT WRITING A SECOND VOCABULARY. "Do not wander" is satisfied by
   this rule changing its selector. There is no `.adm-panel`; the recipe below is unchanged.

   THE SCOPE IS A FACT, NOT A HOPE. `.sec-section` is hand-written markup in admin.html and
   NOTHING emits it — not one of the nine admin-*.js modules, not any other page, not any
   render function in this repository. Every instance of it lives in #usersPane, #securityPane,
   #smtpPane or #turnstilePane: People, plus exactly the three the owner named. So the bare
   class is PRECISE here rather than broad — it cannot reach #overviewPane, #teamsPane,
   #boardsPane, #okrCleanupPane, #storagePane or #trafficPane, because none of them contains
   one. admin-paneling.test.js asserts that property instead of trusting it, so a `.sec-section`
   added to an unnamed pane later fails a test rather than silently acquiring a border.

   IT ALSO CANNOT NEST. A panel inside a panel states one boundary twice, and no `.sec-section`
   is a descendant of another — #smtpPane's two deepest ones are children of `.sec-stack`, which
   is a layout wrapper and not a panel. That, too, is asserted rather than assumed.

   #auditPane IS NAMED, BECAUSE IT SPEAKS THE OTHER VOCABULARY. Its two blocks are already its
   logical portions — "What has changed", with the feed, its filters and its paging; and "What
   nothing records", with the gaps list — but they are `.a-admin-sec`, and their children are
   `.a-admin-p` and `.a-admin-feed`, because the audit feed IS the overview's activity feed.
   Rewriting the wrapper to `.sec-section` while everything inside it stayed `.a-admin-*` would
   be a fork rather than a unification, and would cost the `.a-admin-sec > h2` heading scale its
   only styling. So the PANE is named. `.a-admin-sec` is deliberately not panelled globally:
   #overviewPane's three blocks wear the same class, the owner did not ask about that page, and
   bordering it in passing is exactly the wandering the request forbids.

   THE STANDARD RECIPE, NOT A NEW ONE. --a-panel on --a-line-soft at --a-r-sm is what
   `.sec-level` above already uses and what every card in this console is built from; only the
   padding is stated here, because a section holds prose and controls rather than a single
   choice.

   THE GRID PANES PAY NO MARGIN, AND THAT IS CORRECT. #securityPane and #smtpPane are
   `.pane-grid`, and admin.html's own `.pane-grid .sec-section{margin-top:0 !important}`
   out-ranks the margin below. In a grid the gap is the spacing, and inside #smtpPane's
   `.sec-stack` the flex gap is; a margin as well would be the same statement made twice, which
   is the thing this whole rule is careful about. The margin below is for the single-column
   panes, which is where it was already doing its work.

   NO INLINE MARGINS ANYWHERE. Three `style="margin-top:26px"` came off the People sections when
   this rule was written — reaching for `!important` to out-rank an inline style written in the
   same change would have been fighting my own markup. Three more came off in the same spirit on
   2026-09-16, from #securityPane, #turnstilePane and #auditPane. Two of those were already
   inert, out-ranked by the grid's `!important` since those panes became grids; a declaration
   that does nothing still tells the next reader that spacing is decided in the markup. The
   spacing is the panel's job, and it is stated once, here. */
.alt-app .sec-section,
.alt-app #auditPane > .a-admin-sec{
  background:var(--a-panel);
  border:1px solid var(--a-line-soft);
  border-radius:var(--a-r-sm);
  padding:16px 18px;
  margin-top:18px;
  max-width:none;
}
.alt-app .sec-section:first-of-type,
.alt-app #auditPane > .a-admin-sec:first-of-type{ margin-top:0; }
/* CAP THE TEXT, NEVER THE PAGE — and this pane is why that rule had to be said again.

   `max-width:none` above is what makes a panel fill its pane instead of stopping short of its
   own border, and it is load-bearing: a 720px bordered block inside a 1145px pane reads as a
   layout that failed rather than as a decision. But #turnstilePane is the one pane where that
   cap was doing a SECOND job. It is single-column and almost entirely prose, so `.sec-section`'s
   720px was its reading measure as well as its width, and lifting it took the descriptions from
   720px to a measured 1107px — about 146 characters a line at 12px, nearly double the 78ch this
   console uses everywhere it has thought about it.

   MEASURED, NOT ASSUMED, and the fix is this console's existing answer rather than a new one:
   #storagePane already states exactly these two declarations for exactly this reason, and
   `.a-admin-p` carries the same 78ch — both measured at 590px on the same 1440px viewport where
   this pane measured 1107px. So the panel keeps the width and the sentence gets the measure.

   ONLY THIS PANE. #securityPane and #smtpPane are grid columns at 326px and never reach the
   cap; #auditPane's prose is `.a-admin-p` and was already capped; #storagePane states its own.
   #usersPane's descriptions run to 1107px too — but that is not this change's doing, it shipped
   on 2026-09-15 with the panels the owner approved, and its blocks hold a roster that wants
   every pixel. Widening this rule to "fix" a surface nobody asked about is the wandering the
   request warned against; it is reported to the owner instead. */
.alt-app #turnstilePane .sec-desc{ max-width:78ch; }
.alt-app #turnstilePane .sec-note{ max-width:78ch; }
.alt-app .sec-note{
  border-radius:var(--a-r-sm); font-size:12px; line-height:1.55;
  background:var(--a-amber-soft); border:1px solid var(--a-amber); color:var(--a-text);
}
/* AN EMPTY NOTE IS NOT A NOTE. `#storagePolicyNote` ships empty and is filled by
   renderStoragePolicy() once the policy row has been read, so before that — and on any path
   where that function returns early — the amber background and border painted a blank band
   across the retention panel, which reads as something failing to load rather than as nothing
   needing to be said. Reported 2026-09-07. `:empty` is exact here: it matches only an element
   with no children and no text, so a note with content is untouched, and it fixes every
   `.sec-note` rather than the one that happened to be noticed. */
.alt-app .sec-note:empty{ display:none; }
.alt-app .sec-level{
  background:var(--a-panel); border:1px solid var(--a-line-soft); border-radius:var(--a-r-sm);
}
.alt-app .sec-level:hover{ border-color:var(--a-line-strong); }
.alt-app .sec-level.on{ border-color:var(--a-blue); background:var(--a-blue-soft); }
.alt-app .sec-level .lv-name{ font-size:13px; font-weight:600; color:var(--a-text); }
.alt-app .sec-level .lv-hint{ font-size:12px; line-height:1.5; color:var(--a-dim); }
.alt-app .sec-level .lv-floor{ font-size:11px; color:var(--a-faint); }

.alt-app .bulk-bar{ gap:var(--a-2); border-bottom:1px solid var(--a-line-soft); }
.alt-app .bulk-count{ font-size:11px; color:var(--a-faint); }
.alt-app .empty{ color:var(--a-faint); font-size:12px; padding:26px; }
/* THE PAGE'S OWN STATUS LINE, where every RPC refusal is surfaced verbatim. Deliberately
   not the suite's toast: this one persists until the next action, because an administrator
   has to be able to READ a Postgres refusal rather than catch it going past. */
.alt-app .status{ border-radius:var(--a-r-sm); font-size:12px; }
.alt-app .status.err{ background:var(--a-red-soft); color:var(--a-text); border-color:var(--a-red); }
.alt-app .status.ok{ background:var(--a-green-soft); color:var(--a-text); border-color:var(--a-green); }
.alt-app .cfg-status{ font-size:12px; color:var(--a-dim); }
/* D1, STYLING AUDIT, 2026-09-30: `.cfg-status.err`/`.ok` (CSS/components.css) NEVER WON HERE.
   Both that rule and the bare `.alt-app .cfg-status` above it are (0,2,0) — two classes, no
   ID — so the LATER sheet decided it by source order alone, and admin.html loads this file
   after components.css. Measured with CDP `CSS.getMatchedStylesForNode`: `#niVerdict.cfg-status.err`
   computed `rgb(169,182,204)`, identical to the neutral state — `--red-text` (`--a-red` here)
   was never applied anywhere on this page. Pre-existing, not introduced by this feature; a
   narrower fix already exists for two specific modals
   (`.alt-app #taskModal .cfg-status.err`/`#adminModal`, CSS/alt/alt-team.css) and does nothing
   for a PANE, which is every other `.cfg-status` on this console — including this file's own
   `#niSettingsStatus`, the only feedback an administrator gets when a settings write is
   refused. Fixed ONCE, for every pane, at (0,3,0) — beats the bare rule above by specificity
   rather than by which file loads second, so this cannot regress the next time a sheet's load
   order changes. Not scoped to any one panel: a second modal-shaped patch would be the third
   statement of one intention. */
.alt-app .cfg-status.err{ color:var(--a-red); }
.alt-app .cfg-status.ok{ color:var(--a-green); }

/* ---------- disclosures and details ---------- */
.alt-app .group-head{
  font-size:12px; font-weight:700; letter-spacing:.06em; color:var(--a-text);
  background:var(--a-bg);
}
.alt-app .inactive-head{
  font-size:12px; font-weight:700; color:var(--a-faint);
  border-top:1px solid var(--a-line-soft);
}
.alt-app .inactive-head:hover,.alt-app .group-head:hover{ color:var(--a-text); }
.alt-app .detail{
  background:var(--a-raised); border:1px solid var(--a-line-soft);
  border-left:3px solid var(--a-blue); border-radius:0 var(--a-r-sm) var(--a-r-sm) 0;
}
.alt-app .drow + .drow{ border-top:1px solid var(--a-line-soft); }
.alt-app .dname{ font-weight:600; color:var(--a-text); }
.alt-app .dmeta{ color:var(--a-faint); font-size:12px; }

/* ---------- the account and temporary-password dialogs ----------

   THE SAME CHROME alt-shared-dialogs.css gives every other dialog in this experience, so
   the console's two do not arrive looking like a different application's. */
.alt-app .profile-overlay{ background:rgba(4, 10, 20, .58); }
.alt-app.theme-light .profile-overlay{ background:rgba(19, 32, 54, .34); }
.alt-app .profile-card{
  background:var(--a-panel); border:1px solid var(--a-line-strong);
  border-radius:var(--a-r-lg); box-shadow:var(--a-shadow-lg);
  font-family:var(--a-body); color:var(--a-text);
}
.alt-app .profile-head{ border-bottom:1px solid var(--a-line-soft); }
.alt-app .profile-head h3{ font-family:var(--a-display); font-size:15px; font-weight:700; }
.alt-app .profile-grid dt{ color:var(--a-dim); font-size:12px; }
.alt-app .profile-grid dd{ font-size:13px; }
.alt-app .profile-sec h4{
  font-size:10px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  color:var(--a-dim);
}
.alt-app .temp-pw-value{
  font-family:var(--a-mono); background:var(--a-bg); border:1px solid var(--a-line);
  border-radius:var(--a-r-sm); color:var(--a-text);
}
.alt-app .temp-pw-note{
  background:var(--a-amber-soft); border:1px solid var(--a-amber); color:var(--a-text);
  border-radius:var(--a-r-sm);
}
.alt-app .temp-pw-status{ color:var(--a-green); }

/* ---------- email, the bench and the team roster ---------- */
.alt-app .email-admin-state.on{ color:var(--a-green); }
.alt-app .email-admin-state.off{ color:var(--a-dim); }
.alt-app .email-stat{
  background:var(--a-raised); border:1px solid var(--a-line-soft); border-radius:var(--a-r-sm);
}
.alt-app .email-stat b{ font-family:var(--a-display); font-size:18px; color:var(--a-text); }
.alt-app .email-stat span{ font-size:10px; letter-spacing:.09em; color:var(--a-faint); }
.alt-app .smtp-team{
  background:var(--a-panel); border:1px solid var(--a-line-soft); border-radius:var(--a-r-sm);
}
.alt-app .smtp-team.is-on{ border-color:var(--a-line-strong); }
.alt-app .smtp-team-name{ font-size:13px; font-weight:600; color:var(--a-text); }
.alt-app .smtp-team-count{ font-size:11px; color:var(--a-faint); }
.alt-app .smtp-team-flag{
  font-size:11px; font-weight:700; letter-spacing:.05em;
  background:transparent; border:1px solid var(--a-amber); color:var(--a-amber);
  border-radius:var(--a-r-pill);
}
.alt-app .smtp-team-events{ border-top:1px solid var(--a-line-soft); }
.alt-app .email-event{ border-bottom:1px solid var(--a-line-soft); }
.alt-app .email-event .ee-name{ font-size:13px; font-weight:600; color:var(--a-text); }
.alt-app .email-event .ee-why{ font-size:12px; line-height:1.45; color:var(--a-dim); }
.alt-app .email-event.is-default-off .ee-name{ font-weight:500; color:var(--a-dim); }
.alt-app .bench-row label{ font-size:11px; color:var(--a-dim); }
.alt-app .bench-frame{ border:1px solid var(--a-line); border-radius:var(--a-r-sm); }
.alt-app .bench-verdict{
  border-radius:var(--a-r-sm); font-size:13px;
  background:var(--a-raised); border:1px solid var(--a-line-soft); color:var(--a-text);
}
.alt-app .bench-verdict.is-send{
  background:var(--a-green-soft); border-color:var(--a-green); color:var(--a-text);
}
/* NOT A WARNING WHEN IT STOPS. A gate refusing an email is the system working; amber says
   "here is why", where red would say "something is broken". */
.alt-app .bench-verdict.is-stop{
  background:var(--a-amber-soft); border-color:var(--a-amber); color:var(--a-text);
}
.alt-app .bench-gate{
  background:var(--a-panel); border:1px solid var(--a-line-soft);
  border-radius:var(--a-r-sm); font-size:13px;
}
.alt-app .bench-gate .bg-mark{ color:var(--a-faint); }
.alt-app .bench-gate.is-pass .bg-mark{ color:var(--a-green); }
.alt-app .bench-gate.is-stop .bg-mark{ color:var(--a-amber); }
.alt-app .bench-gate .bg-label{ color:var(--a-text); }
.alt-app .bench-gate .bg-state{ font-size:10px; letter-spacing:.09em; color:var(--a-faint); }
.alt-app .bench-gate.is-stop .bg-state{ color:var(--a-amber); }
.alt-app .bench-gate .bg-detail{ font-size:12px; line-height:1.5; color:var(--a-dim); }
.alt-app .bench-subject{ font-size:13px; font-weight:600; color:var(--a-text); }

/* ---------- storage and retention ----------

   The largest section, and the one whose figures matter most, so its surfaces get the same
   treatment rather than being left as the one place that still looks classic. */
.alt-app .storage-sort-label,.alt-app .switcher-label{
  font-size:10px; font-weight:700; letter-spacing:.09em; color:var(--a-dim);
}
.alt-app .storage-total,.alt-app .storage-hot,.alt-app .storage-row,.alt-app .storage-run{
  background:var(--a-panel); border:1px solid var(--a-line-soft); border-radius:var(--a-r-sm);
}
.alt-app .storage-total.action{ background:var(--a-amber-soft); border-color:var(--a-amber); }
.alt-app .storage-total.mismatch{ background:var(--a-red-soft); border-color:var(--a-red); }
.alt-app .storage-total-num{ font-family:var(--a-display); font-size:16px; color:var(--a-text); }
.alt-app .storage-total-lbl{ font-size:12px; color:var(--a-dim); }
.alt-app .storage-total-tag{ font-size:10px; letter-spacing:.09em; color:var(--a-faint); }
.alt-app .storage-hot-title{ font-size:10px; letter-spacing:.09em; color:var(--a-faint); }
.alt-app .storage-hot-value{ font-family:var(--a-display); font-size:18px; color:var(--a-text); }
.alt-app .storage-hot-team{ font-size:12px; color:var(--a-dim); }
.alt-app .storage-row.selected{ border-color:var(--a-blue); background:var(--a-blue-soft); }
.alt-app .storage-name{ font-size:13px; font-weight:600; color:var(--a-text); }
.alt-app .storage-figure{ font-size:12px; color:var(--a-text); }
.alt-app .storage-meta{ font-size:12px; color:var(--a-faint); }
.alt-app .storage-badge{
  border-radius:var(--a-r-pill); font-size:10px; font-weight:700; letter-spacing:.08em;
  background:transparent; border:1px solid var(--a-line-strong); color:var(--a-dim);
}
.alt-app .storage-badge.ok{ border-color:var(--a-green); color:var(--a-green); }
.alt-app .storage-badge.warn{ border-color:var(--a-amber); color:var(--a-amber); }
.alt-app .storage-badge.bad{ border-color:var(--a-red); color:var(--a-red); }
.alt-app .storage-scen th{
  font-size:10px; font-weight:700; letter-spacing:.09em; color:var(--a-dim);
  border-bottom:1px solid var(--a-line-soft);
}
.alt-app .storage-scen td{ border-bottom:1px solid var(--a-line-soft); color:var(--a-text); }
.alt-app .storage-scen tr.is-pick td{ background:var(--a-blue-soft); }
.alt-app .storage-scen tr.is-protected td{ color:var(--a-dim); }
.alt-app .storage-bar{ background:var(--a-line-soft); }
.alt-app .storage-bar > span,.alt-app .storage-bar i{ background:var(--a-blue); }
.alt-app .storage-advice{
  border:1px solid var(--a-blue); border-radius:var(--a-r); background:var(--a-blue-soft);
}
.alt-app .storage-advice.is-quiet{ border-color:var(--a-line-soft); background:var(--a-raised); }
.alt-app .storage-health.is-ok{ border-color:var(--a-green); background:var(--a-green-soft); }
.alt-app .storage-health.is-warn{ border-color:var(--a-amber); background:var(--a-amber-soft); }
.alt-app .storage-health.is-bad{ border-color:var(--a-red); background:var(--a-red-soft); }
.alt-app .storage-advice-head{ font-family:var(--a-display); font-size:16px; color:var(--a-text); }
.alt-app .storage-advice-why,.alt-app .storage-advice-figures{ font-size:12px; color:var(--a-dim); }
.alt-app .storage-loose-head{
  font-size:10px; font-weight:700; letter-spacing:.09em; color:var(--a-dim);
}
.alt-app .storage-loose-head.is-proven{ color:var(--a-red); }
.alt-app .storage-loose-head.is-possible{ color:var(--a-amber); }
.alt-app .storage-loose-head.is-pending{ color:var(--a-blue-text); }
.alt-app .storage-scope-note{
  background:var(--a-red-soft); border-color:var(--a-red); color:var(--a-text);
}
.alt-app .storage-preview{
  background:var(--a-panel); border:1px solid var(--a-blue); border-left-width:3px;
  border-radius:var(--a-r-sm);
}
.alt-app .storage-preview.danger{ border-color:var(--a-red); background:var(--a-red-soft); }

/* THE PREVIEW'S THREE VOICES, AND THE WHOLE JOB IS KEEPING THEM APART.

   BLUE SAYS "NOTHING HAS HAPPENED" — the banner. RED SAYS "THIS DELETES" — .storage-warn
   .severe, further down the same panel. GREEN SAYS "THIS IS KEPT" — the protected block. A
   reader scanning a grid of large numbers has to be able to tell which side of the button
   they are standing on, and hue is what answers that before any word is read.

   SO THE BANNER IS DELIBERATELY NOT RED. It is the calmest element here, not the loudest:
   an alarm-coloured "no data will be deleted" reads as a warning about deletion, which is
   the opposite of what it says. It must also survive `.storage-preview.danger`, whose
   ground turns red-soft when the plan includes open work — a blue card on a red ground
   stays legible and stays distinct, which a bordered-transparent one would not.

   AND EVERY RULE HERE CARRIES ITS OWN SPACING. `.storage-preview` sets no padding and
   `.storage-preview-grid` has no rule at all, so these five elements cannot inherit any
   geometry. Giving them their own means styling the new half of this panel without
   silently restyling the half that was already shipped. */
.alt-app .storage-preview-banner{
  display:block;
  margin:0 0 var(--a-3);
  padding:var(--a-2) var(--a-3);
  background:var(--a-blue-soft);
  border:1px solid var(--a-blue);
  border-radius:var(--a-r-sm);
  font-family:var(--a-display);
  font-size:12px;
  font-weight:700;
  letter-spacing:.06em;
  color:var(--a-blue-text);
}
/* Supporting detail, and it must never be mistaken for the headline count directly above
   it — hence a smaller, dimmer face and a hanging indent so a wrapped scope reads as one
   sentence rather than as a second line of figures. */
.alt-app .storage-preview-scope{
  margin:0 0 var(--a-3);
  font-size:12px;
  line-height:1.5;
  color:var(--a-dim);
  overflow-wrap:anywhere;
}
/* WHAT IS BEING KEPT, as its own half of the panel. Marked with a green edge rather than a
   filled green card: this is reassurance a reader should be able to find, not a third thing
   competing with the banner and the warning for their attention. */
.alt-app .storage-preview-protected{
  margin:var(--a-3) 0;
  padding:var(--a-2) var(--a-3);
  background:var(--a-raised);
  border:1px solid var(--a-line-soft);
  border-left:3px solid var(--a-green);
  border-radius:var(--a-r-sm);
}
.alt-app .storage-preview-protected-head{
  font-size:11px;
  font-weight:700;
  letter-spacing:.07em;
  text-transform:uppercase;
  color:var(--a-green);
}
/* A LIST THAT LOOKS LIKE A LIST, STATED RATHER THAN INHERITED. There is no global list
   reset in this codebase — components that want bare lists opt out one at a time
   (`.mc-list`, `.pw-reqs`) — so a bare <ul> here would in fact draw discs on its own. The
   marker and the indent are declared anyway, because "it happens to look right today" is
   how a list silently loses its markers when somebody later adds the reset that every other
   component is already working around. */
.alt-app .storage-preview-protected-list{
  margin:var(--a-2) 0 0;
  padding:0 0 0 18px;
  list-style:disc outside;
}
.alt-app .storage-preview-protected-list > li{
  margin:0 0 var(--a-1);
  font-size:12px;
  line-height:1.5;
  color:var(--a-text);
  overflow-wrap:anywhere;
}
.alt-app .storage-preview-protected-list > li::marker{ color:var(--a-green); }

.alt-app .storage-preview-head{ font-family:var(--a-display); font-size:14px; color:var(--a-text); }
.alt-app .storage-preview-cell{ background:var(--a-raised); border-radius:var(--a-r-sm); }
.alt-app .storage-preview-cell b{ font-size:14px; color:var(--a-text); }
.alt-app .storage-preview-cell span{ font-size:11px; color:var(--a-faint); }
.alt-app .storage-warn{
  background:var(--a-amber-soft); border:1px solid var(--a-amber); color:var(--a-text);
  border-radius:var(--a-r-sm);
}
.alt-app .storage-warn.severe{
  background:var(--a-red-soft); border-color:var(--a-red); font-weight:700;
}
.alt-app .storage-class + .storage-class{ border-top:1px solid var(--a-line-soft); }
.alt-app .storage-class-name{ font-weight:600; color:var(--a-text); }
.alt-app .storage-type{ font-size:12px; color:var(--a-dim); }
.alt-app .admin-filter-check{ font-size:12px; color:var(--a-dim); }
.alt-app .admin-filter-check:hover{ color:var(--a-text); }
.alt-app .admin-filter-count{ font-size:11px; color:var(--a-faint); }
