/* Owner-console chrome.
 *
 * ⚠️ **DELIBERATELY SMALL.** Everything the console renders — tables, buttons,
 * badges, tabs, breadcrumbs, forms, cards — comes from `screens.css`. This file
 * holds only what is genuinely console-specific: the top bar, the tier mark and
 * the theme control. If a rule here starts to look like a component, it belongs
 * in `screens.css` where both surfaces get it.
 *
 * ⚠️ **PALETTE TOKENS ONLY, NEVER LITERAL HEX.** `tokens.css` defines every
 * colour for both themes; a hard-coded value looks right in whichever theme it
 * was authored in and fails silently in the other. That is not hypothetical —
 * it happened in this codebase on 2026-09-16, caught by
 * `tests/ui/test_css_custom_properties_resolve.py`.
 */

.owner-body {
  margin: 0;
  background: var(--bg);
  color: var(--text-primary);

  /* ⚠️ **ONE NUMBER, TWO CONSUMERS — AND THEY MUST NOT DRIFT APART.** The
   * measure a form panel is capped at, and the width its tab strip underlines.
   * When these were separate literals the strip ran a thousand pixels past the
   * panel. Declared here so the relationship is enforced rather than
   * remembered. Lists and tables deliberately ignore it: they get the full
   * width of the pane. */
  --form-measure: 640px;
}

/* ── Typography ─────────────────────────────────────────────────────────────
 *
 * ⚠️ **15px/1.55, NOT THE PRODUCT'S 16px, AND THAT IS A DELIBERATE DIVERGENCE.**
 * Taken from VermontiV2, which Marcus rated better on 2026-09-16. An operator
 * console is a dense information surface — tables of tenants, identity
 * configuration, group mappings — and the extra pixel costs a row per screen
 * without buying legibility at these line lengths.
 *
 * ⚠️ **SCOPED TO `.owner-body`, SO THE TENANT APP IS UNTOUCHED.** Changing
 * `--text-base` in `tokens.css` would reflow every clinical screen in the
 * product, which is not a change to make as a side effect of console feedback.
 * If the denser setting is wanted everywhere, it is a one-line change to the
 * token and its own careful pass.
 */
.owner-body {
  font-size: 15px;
  line-height: 1.55;
}

.owner-body .page-title {
  letter-spacing: -0.01em;
}

/* Section headings read as labels rather than headlines — the console
 * convention Vermonti uses, and the reason its cards scan quickly. */
.owner-body .card__title {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-secondary);
}

/* ── Title links ────────────────────────────────────────────────────────────
 *
 * ⚠️ **A TITLE IS NOT A LINK-COLOURED LINK.** Marcus, 2026-09-16: the Vermonti
 * titles *"are bold white and turn blue with underscore on hover"*. The
 * product-wide `a:where(:link, :visited)` rule paints every anchor with
 * `--accent`, which is right for prose but wrong for the primary cell of a
 * table: a column of forty accent-coloured names is a wall of blue, and the
 * colour stops meaning "this is actionable" because everything has it.
 *
 * So the name carries the body colour and its WEIGHT signals importance;
 * `--accent` is held back for the hover, where it means "you are about to
 * follow this". The underline appears with it, so the affordance is not colour
 * alone — which also keeps it legible for anyone who cannot separate the two
 * hues.
 *
 * Specificity note: `.cell--primary a` is 0,1,1 and beats the 0,0,1 base rule
 * without needing `!important`.
 */
/* ⚠️ **WIDENED FROM `.cell--primary a` TO EVERY LINK IN A TABLE CELL**, because
 * the argument above was never about the primary cell — it is about columns. The
 * Customer column added in MIG-341 is a link in every row, and it rendered as a
 * second wall of blue beside the first. Marcus, 2026-09-17: *"make sure the
 * Customer link is not blue."*
 *
 * ⚠️ **SCOPED WITH `:where()` SO IT STAYS AT 0,0,1**, which is the whole lesson
 * of `layout.css`'s base link rule: a table-scoped rule at 0,2,1 would beat
 * `.btn--secondary` (0,1,0) and repaint every row-action button — the exact
 * failure that produced an unreadable "+ Add tenant" caption. `:where()` on the
 * ancestors keeps this the weakest possible rule; it wins over the base link
 * rule only by being later in the cascade, and ANY component class still
 * outranks it.
 *
 * ⚠️ And `:not(.btn)` is NOT the fix here, for the reason this file already
 * records: per-component exclusions are the wrong shape, and the second broken
 * component arrived while patching the first with one. */
:where(.owner-body .entity-table td) a:where(:link, :visited) {
  color: var(--text-primary);
  text-decoration: none;
}

:where(.owner-body .entity-table td) a:where(:hover, :focus-visible) {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 3px;
}

/* The primary cell keeps only what makes it primary. Its colour now comes from
 * the shared rule above, so there is one definition of "a link in a table reads
 * as text until you reach for it". */
.cell--primary a {
  font-weight: 600;
}

/* ── Top bar ────────────────────────────────────────────────────────────────
 *
 * ⚠️ **THE TIER MARK IS A SAFETY CONTROL, NOT DECORATION.** The console acts
 * across every tenant at once; an operator who believes they are inside one
 * trust can suspend or purge the wrong one. The accent bar and the "Owner" mark
 * are there so the surface is never mistakable for a tenant screen — which is
 * also why this console does NOT simply inherit the tenant top bar.
 */
.owner-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 22px;
  background: var(--surface);
  border-bottom: 1px solid var(--border-subtle);
  box-shadow: inset 0 3px 0 0 var(--accent);
}

.owner-brand {
  font-weight: 650;
  letter-spacing: 0.2px;
  color: var(--text-primary);
  text-decoration: none;
}

/* ⚠️ **AMBER, NOT THE ACCENT, AND IT IS A SIGNAL RATHER THAN A STYLE CHOICE.**
 * The original console marked the tier in orange and it was the right instinct:
 * this surface acts across every tenant at once, so an operator must never be
 * uncertain whether they are inside one trust or above all of them. Amber reads
 * as "elevated"; the accent reads as "ordinary link" because that is what it is
 * everywhere else.
 *
 * `--warning` rather than a literal, so it themes. Measured 2026-09-16 against
 * `--surface`: light #9A4A08 5.98:1 (AA), dark #F0C675 10.25:1 (AAA). */
.owner-brand__tier {
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: 99px;
  font-size: 12px;
  font-weight: 600;
  color: var(--warning);
  border: 1px solid var(--warning);
}

.owner-topbar__actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.owner-topbar__signout {
  margin: 0;
}

/* ── The content column: FLUSH LEFT, FULL WIDTH ─────────────────────────────
 *
 * ⚠️ **CENTRING A CAPPED COLUMN WAS THE WRONG FIX AND THIS REPLACES IT.** The
 * column was capped at 1080px and centred, which on any real monitor left a
 * wide empty gutter on both sides while the audit table — six columns, one of
 * them JSON — was squeezed into the middle and overflowed its own card.
 *
 * Centring only ever treated the symptom. The content looked "over to one
 * side" because it was capped inside a wider track; the cap is what was wrong,
 * not the alignment. An operator console is a DATA surface, not an article:
 * tables want every pixel, and a left edge that does not move between screens
 * is what makes a list scannable.
 *
 * So: no cap, no auto margins. Everything starts at the same left rule.
 *
 * ⚠️ The readable-measure argument is real, but it applies to PROSE, not to
 * lists — so it is answered where it arises (`.owner-main .card > p`, below)
 * rather than by shrinking the whole console.
 */
.owner-main {
  padding: 22px 28px 80px;
}

/* ⚠️ **PROSE IS CAPPED WHERE IT SITS, NOT BY CAPPING THE PAGE.** Explanatory
 * paragraphs set across a 2560px monitor are genuinely unreadable — the eye
 * loses the start of the next line. Capping the paragraph keeps sentences at a
 * sane measure while the table beside it still gets the full width.
 *
 * `ch` rather than px: the measure that matters is characters per line, and it
 * should follow the font, not a guess made at one font size. */
.owner-main .card > p,
.owner-main .page-subtitle {
  max-width: 82ch;
}

/* ⚠️ **STOPS THE VIEWPORT SHIFTING SIDEWAYS.** A tall panel has a scrollbar and
 * a short one does not; without a reserved gutter every tab change nudges the
 * whole page horizontally by the scrollbar's width. Reserving it always costs
 * ~15px and removes the jump entirely.
 *
 * Scoped with `:has()` so only the console reserves the gutter, not the tenant
 * app. */
html:has(.owner-body) {
  scrollbar-gutter: stable;
}

/* ⚠️ THE `.theme-toggle` RULES MOVED TO `layout.css`, AND THEY HAD TO.
 * This sheet is loaded by the OWNER CONSOLE ONLY. The tenant UI loads
 * tokens/layout/sidebar/screens — so the same control rendered there was
 * completely unstyled. `layout.css` is loaded by BOTH apps and is the shell
 * sheet, which is where a piece of chrome belongs. Do not move them back. */


/* ── Destructive confirmation ───────────────────────────────────────────────
 *
 * ⚠️ The purge screen. Styled as a warning surface rather than an ordinary card
 * because the action is irreversible and synchronous: by the time the response
 * renders, the tenant's data is gone.
 */
.danger-zone {
  border: 1px solid var(--danger);
  border-radius: 10px;
  padding: 18px;
  margin: 18px 0;
}

.danger-zone__title {
  margin: 0 0 6px;
  color: var(--danger);
  font-size: 16px;
}

/* ── Console navigation ─────────────────────────────────────────────────────
 *
 * ⚠️ **NO BURGER, AND THAT IS THE FIX RATHER THAN A SIMPLIFICATION.** This was a
 * `<details>` disclosure so narrow screens got a toggle for nothing. But
 * `<details>` holds state: closed on a phone, then widened, it stayed closed
 * while the media query hid the `<summary>` that would reopen it — the
 * navigation disappeared with no way back. VermontiV2 has no toggle at all and
 * is the reason it behaves: the grid collapses to one column and the nav stacks
 * above the content, so there is no state to be left in the wrong position.
 *
 * *A control that can be hidden at some viewport widths must not also be the
 * only way to undo itself.*
 */
.owner-shell {
  display: grid;
  grid-template-columns: 1fr;
}

.owner-nav {
  background: var(--surface);
  border-bottom: 1px solid var(--border-subtle);
  padding: 8px 22px;
}

.owner-nav__list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.owner-nav__link {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  border-radius: var(--radius-md, 7px);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 14px;
  white-space: nowrap;
}

.owner-nav__link:hover {
  background: var(--surface-raised);
  color: var(--text-primary);
}

/* ⚠️ The active item carries WEIGHT AND A RULE as well as colour, so it stays
 * identifiable without colour perception. */
.owner-nav__link--active {
  color: var(--accent);
  font-weight: 600;
  background: var(--accent-soft);
}

/* ⚠️ `currentColor` rather than a token, so the glyph follows the link through
 * hover and the active state without a second set of rules to keep in step. */
.owner-nav__icon {
  flex: none;
  color: currentColor;
  opacity: 0.85;
}

.owner-nav__link--active .owner-nav__icon {
  opacity: 1;
}

@media (min-width: 860px) {
  .owner-shell {
    grid-template-columns: 220px minmax(0, 1fr);
    align-items: start;
  }

  .owner-nav {
    border-bottom: 0;
    border-right: 1px solid var(--border-subtle);
    padding: 18px 12px;
    min-height: calc(100vh - 53px);
    position: sticky;
    top: 0;
  }

  .owner-nav__list {
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 2px;
  }

  .owner-nav__link--active {
    box-shadow: inset 2px 0 0 0 var(--accent);
  }

  /* ⚠️ **NO WIDTH OVERRIDE HERE ANY MORE.** This used to re-cap and re-centre
   * the column once the sidebar appeared. Both are gone: the base rule is now
   * full-width and flush left at every size, so the sidebar breakpoint changes
   * where the content STARTS and nothing else. One left rule, all widths. */

  /* ⚠️ **A SIGNED-OUT PAGE HAS NO NAV, AND THE GRID WENT ON RESERVING ITS
   * COLUMN.** `_nav.html` renders nothing without `signed_in_email`, so `<main>`
   * became the FIRST grid item and landed in the 220px nav track. The sign-in
   * form rendered in a 220px column.
   *
   * ⚠️ **THIS PRE-DATES THE FULL-BLEED CHANGE — measured, not assumed.** The
   * previous stylesheet put `<main>` in the same track; it merely measured
   * 301px instead of 220px, because a grid item's automatic minimum is its
   * content size and the inputs had no width rule to shrink below. Giving
   * `.form-field input` `width: 100%` removed that accidental prop and let the
   * column collapse to its real size. The bug was always there; it was being
   * held up by the absence of a rule. */
  .owner-shell--unauthenticated {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ⚠️ **THE ONE SCREEN WHERE CENTRING IS RIGHT, AND IT IS NOT A CONTRADICTION OF
 * THE RULE ABOVE.** Content is flush left because the console is a data surface
 * scanned down a stable left edge. A sign-in page has no navigation, no list and
 * no second thing to look at: there is exactly one target, and putting it in the
 * middle of the viewport is how every sign-in screen behaves for good reason.
 * "Left-align the content pane" was about lists, not about this. */
.owner-shell--unauthenticated .owner-main {
  max-width: 430px;
  margin-inline: auto;
  padding-top: 8vh;
}

/* Who you are signed in as. Plain text, not a link — the adjacent "My profile"
 * button is the thing that navigates. */
.owner-topbar__who {
  font-size: 13px;
  color: var(--text-secondary);
  white-space: nowrap;
}

@media (max-width: 719px) {
  /* The address is the first thing worth dropping on a narrow screen: the
   * profile button still gets you there, and the console has one operator
   * signed in at a time. */
  .owner-topbar__who { display: none; }
}

/* ── Purge challenge code ───────────────────────────────────────────────────
 *
 * ⚠️ Rendered large and monospaced because it must be READ ACCURATELY off the
 * screen and typed back — that transcription IS the control. A code shown in
 * body text invites copy-and-paste, which is the habit the challenge exists to
 * defeat.
 */
.purge-challenge {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0 0 18px;
}

.purge-challenge__label {
  font-size: 13px;
  color: var(--text-secondary);
}

.purge-challenge__code {
  font-family: var(--font-mono, ui-monospace, "Cascadia Mono", Consolas, monospace);
  font-size: 30px;
  font-weight: 700;
  letter-spacing: 0.22em;
  color: var(--danger);
  user-select: none;
}



/* ⚠️ **TABS SIT AT THEIR NATURAL WIDTH, CLOSE TOGETHER.** An earlier version
 * stretched them to equal width on a grid, which read as three large buttons
 * rather than a tab strip. What needed equalising was the FORM UNDER the tab,
 * not the tab itself — see `.tab-panels` below.
 */


/* ── Console form controls ──────────────────────────────────────────────────
 *
 * ⚠️ **NOTHING IN THE SHARED STYLESHEETS GIVES A TEXT INPUT A WIDTH.** The only
 * width rules for `<input>` cover checkboxes, search boxes and date filters, so
 * text, email and password fields render at the browser default of roughly
 * twenty characters. The console's old inline stylesheet had
 * `input, select, textarea { width: 100% }`; removing that stylesheet took the
 * rule with it, which is why the password form looked half the width of the
 * prose beside it.
 */
.owner-body .form-field input[type="text"],
.owner-body .form-field input[type="email"],
.owner-body .form-field input[type="password"],
.owner-body .form-field input[type="date"],
.owner-body .form-field select,
.owner-body .form-field textarea {
  width: 100%;
  box-sizing: border-box;
}

/* ── Tab panels ─────────────────────────────────────────────────────────────
 *
 * ⚠️ **THE JUMP IS HORIZONTAL, NOT VERTICAL, AND THAT DISTINCTION IS THE WHOLE
 * FIX.** An earlier version reserved `min-height: 460px` so short panels could
 * not collapse the page — which worked, and left a large empty void under the
 * Appearance panel. It traded one ugly thing for another.
 *
 * Tabs sit ABOVE their panel, so a panel growing or shrinking moves nothing the
 * eye is resting on: the heading, the tab strip and everything above stay
 * exactly where they were. Vertical variation is simply not the problem.
 *
 * What DOES move things is width — two sources, both closed here:
 *
 *   * the panels rendering at different widths, because their CONTENTS differ
 *     (a prose paragraph fills a card; a default-width password input does
 *     not). A single `max-width` makes every panel identical regardless;
 *   * the scrollbar appearing and vanishing between a tall panel and a short
 *     one, which shifts the entire viewport sideways by its width. That is
 *     handled by `scrollbar-gutter: stable` above.
 *
 * 640px is a deliberate measure rather than a round number: a text input
 * stretched across the full content width is unusable as well as ugly — the
 * eye loses the line between a label on the left and its value on the right.
 * This cap is now the ONLY thing bounding a form's width, since the content
 * column itself is full-bleed.
 */
.tab-panels {
  max-width: var(--form-measure);
}

/* ⚠️ **THE TAB STRIP HAS TO SHARE THE PANEL'S WIDTH, NOT THE PAGE'S.**
 * `.entity-tabs` carries a `border-bottom`, and once the content column went
 * full-bleed that rule measured 1629px above a 640px card — a line running a
 * thousand pixels past the thing it underlines, pointing at nothing. Tabs are
 * part of the panel they switch; they end where it ends.
 *
 * `:has(+ .tab-panels)` restricts this to tabs that actually govern a measured
 * panel, so the tenant and identity screens — where tabs sit above full-width
 * tables and the full-width rule is right — are untouched. */
.owner-body .entity-tabs:has(+ .tab-panels) {
  max-width: var(--form-measure);
}

/* ── Audit: the payload column ──────────────────────────────────────────────
 *
 * ⚠️ **THE TABLE REALLY WAS OUTSIDE ITS PANEL, AND THE CAP WAS THE CAUSE.**
 * Measured on the live page at 1920px: with the old capped-and-centred column,
 * the six-column table's right edge sat **58px past the card's content box**.
 * It needed ~1030px, the card offered less, and a table's `width: 100%` loses
 * to its own content minimum — so the table visibly burst out of the box drawn
 * around it. Full-bleed, that same table now measures **16px inside** the card.
 * The panel was never too small for the data; the column was.
 *
 * ⚠️ No page scrollbar ever appeared, which is why this presented as an
 * aesthetic complaint rather than a layout bug: the centred card had slack on
 * both sides for the overflow to spill into, so nothing looked broken — it just
 * looked wrong.
 *
 * ⚠️ **AND THE CELL CARRIED `.cell--muted`, WHICH IS `white-space: nowrap`.**
 * A JSON payload then set its own column width with no line breaks permitted —
 * measured at 490px in a 469px cell. `screens.css` documents this exact trap
 * directly above `.cell--prose` ("use this, NOT `.cell--muted`, for prose ...
 * that is exactly what the Skills catalogue did"), having been bitten by it
 * once already. The template now uses `.cell--prose`, which is the fix; what
 * remains here is only what is specific to a JSON payload.
 *
 * Bounded modestly, because the longest payload in this chain is 81 characters
 * and the mean is 23, across all 795 rows. (An earlier note here cited 21,861 —
 * that is `audit_events`, the TENANT chain, which this screen does not read.)
 */
.audit-payload {
  max-width: 44ch;
}

.audit-payload code {
  font-size: 11px;
  /* ⚠️ `anywhere`, not `.cell--prose`'s `break-word` — the difference is not
   * cosmetic. Only `anywhere` reduces the element's MIN-CONTENT width, and
   * min-content is precisely what the table layout algorithm uses to decide
   * this column's size. `break-word` wraps the text but still reserves the
   * unbroken token's full width, so the column would stay too wide. JSON
   * offers no spaces to break at, so this is the whole ballgame. */
  overflow-wrap: anywhere;
}

/* ── Audit: the filter bar ──────────────────────────────────────────────────
 *
 * Was three inline styles on the <form>, which was survivable with one control
 * and is not with three plus an export link.
 *
 * ⚠️ `align-items: end` so the button baselines sit on the SELECTS, not on the
 * labels above them — the two fields carry labels and the buttons do not, so
 * any other alignment leaves the buttons floating.
 */
.audit-filters {
  display: flex;
  gap: 10px;
  align-items: end;
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

.audit-filters .form-field {
  margin: 0;
}

/* ⚠️ Pushed to the far end because it is a different KIND of act. The other
 * controls narrow what is on screen and are reversible; this one takes the
 * record out of the installation, where it stops being append-only. Sitting it
 * next to "Clear" would invite it as the next click along. */
.audit-filters__export {
  margin-left: auto;
}

@media (max-width: 719px) {
  /* Once the bar wraps, `margin-left: auto` would strand the export alone on
   * its own line at the right — which reads as more prominent, not less. */
  .audit-filters__export { margin-left: 0; }
}

/* ⚠️ **THE ONE INHERITED RULE THAT STILL CENTRED SOMETHING.** `.empty-state`
 * is `text-align: center` in `screens.css`, which was invisible while the
 * column was capped and centred anyway. Full-bleed, it became a sentence
 * floating in the middle of a very wide card with no relationship to the
 * heading above it. Everything in this pane starts at the same left rule. */
.owner-body .empty-state {
  text-align: left;
}

/* A last-resort container so a table that genuinely cannot fit scrolls INSIDE
 * its card rather than out of it. With the payload column wrapping, the audit
 * table now fits with nothing to spare at 1920, 1440 and 1280 — so in practice
 * this never engages, and that is the point: it is the guard for a window
 * narrower than anything measured, or a future column.
 *
 * ⚠️ On the wrapper, not `overflow` on the card. Putting it on the card would
 * clip the row-menu popover — which is exactly why `.entity-table` declines
 * `overflow: hidden` itself, with a comment saying so.
 *
 * ⚠️ **NO `scrollbar-gutter` HERE, DELIBERATELY.** It would reserve a
 * permanent strip under every table on a platform with classic scrollbars, to
 * prevent a shift in the case that measurement says no longer happens. Paying
 * a visible cost on every screen to smooth a transition that does not occur is
 * the wrong trade. (`html` keeps its gutter: that one fires on every tab
 * change.) */
.table-scroll {
  overflow-x: auto;
}

/* Consistent rhythm between stacked cards, so a two-card tab and a one-card tab
 * start at the same place. */
.tab-panels > .card + .card {
  margin-top: var(--space-4);
}

/* ── Which installation am I on ─────────────────────────────────────────────
 *
 * ⚠️ **THE ONLY THING ON THE SCREEN THAT DISTINGUISHES TWO OTHERWISE IDENTICAL
 * CONSOLES.** An operator with production and pre-production both open has, at
 * present, nothing but the address bar to tell them apart — and the address bar
 * is the thing nobody reads when they are three clicks into a task. MIG-341's
 * Production badge marks a tenant ROW; this marks the whole WINDOW, which is the
 * mistake that badge cannot catch.
 *
 * ⚠️ **PRODUCTION AND UNLABELLED ARE LOUD; EVERYTHING ELSE IS QUIET.** The
 * colour is doing work, not decoration: it has to be noticeable in peripheral
 * vision while an operator is looking at a table. A uniformly-styled label
 * would be read once on the first visit and never again.
 */
.owner-installation {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  margin-left: 12px;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 12px;
  line-height: 1.4;
  white-space: nowrap;
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
}

.owner-installation strong {
  font-weight: 700;
  letter-spacing: 0.04em;
}

/* ⚠️ `--danger` on the border and text rather than a filled red pill: a solid
 * red block beside the brand reads as an ERROR on every page, and an alarm that
 * is always on stops being an alarm. A tinted outline is noticeable without
 * claiming something is wrong. */
.owner-installation--prod,
.owner-installation--unlabelled {
  border-color: var(--danger);
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 8%, transparent);
}

@media (max-width: 719px) {
  /* ⚠️ The LABEL drops on a narrow screen, never the environment: on a phone
   * the thing worth the pixels is "this is production", not which production. */
  .owner-installation { font-size: 11px; margin-left: 8px; }
}
