@charset "UTF-8";
/*!
 * Aderis restyle for the Flask UI
 * Copyright 2026 Aderis Energy, LLC All Rights Reserved (https://aderisenergy.com)
 *
 * Maps Bootstrap 4.1.3 components onto the Aderis design system's tokens. Dark only: the
 * Aderis system has no light variant and Acuity ships no light theme to users. Do not add
 * one, and do not add theming hooks -- that work belongs to the Blazor app, later.
 *
 * WHY EXPLICIT OVERRIDES: Bootstrap 4.1.3 compiles literal hex and exposes no custom
 * properties, so there is no variable to retarget. Each component needs its own rule.
 *
 * RULES FOR EDITING, both enforced by Acuity.Host.Tests/AderisStyleGuardTests.cs:
 *   1. Colour comes from --aderis-* tokens. A hex literal is allowed ONLY as a var()
 *      fallback -- var(--aderis-orange, #f78a1e) -- which keeps the UI correct when
 *      Acuity.Host is bypassed and /_content/.../aderis-theme.css therefore 404s.
 *   2. Every selector starts with .aderis.
 *
 * ABOUT .aderis: it is a specificity anchor, not a theming mechanism. `.aderis .btn-primary`
 * is (0,2,0) and beats Bootstrap's (0,1,0) with no !important. The class now sits on <body> in
 * the four base templates, so it is on every page; it is no longer a staging gate. Do not build
 * per-page opt-in on top of it, and do not assume (0,2,0) is enough -- several Bootstrap rules
 * are (0,3,0) or (0,4,0) (`.close:not(:disabled):not(.disabled):hover`,
 * `.nav-tabs .nav-link.active`, `.btn-primary:not(:disabled):not(.disabled).active`). Where
 * that happens, mirror Bootstrap's own selector shape to land one class above it.
 *
 * No !important, ever. If a rule is not winning the cause is paint order or a competing
 * unlayered rule. Note acuity.css's fg- and bg- colour utilities DO carry !important and
 * will still win inside this scope; that is intended, since they resolve through tokens.
 *
 * Never write the sequence star-slash inside these comments, not even in a class glob like
 * fg-* / bg-*: it closes the comment early and silently turns the remaining prose into
 * invalid CSS. The scope test catches it; this note is so it does not have to.
 *
 * Verify every change at /dev/aderis-gallery, which extends base.html and therefore loads
 * the same stylesheet stack as a real page.
 */

/* ── Page surface ─────────────────────────────────────────────────────────────
   Bootstrap's reboot sets `body { background-color: #fff; color: #212529 }`, which is why
   Acuity pages are white today: nothing in acuity.css sets a body background, and .sos-body
   is used in zero templates. Overriding it here is what makes the app dark.

   `color-scheme: dark` additionally fixes what CSS cannot reach -- the native dropdown list
   of a <select>, autofill backgrounds, and scrollbars.

   `accent-color` is the other half of that: color-scheme darkens a native checkbox's BOX but
   leaves its checked fill at the UA default, which is Chrome's #0075ff and appears in no
   Aderis palette. It is the last blue left in the app's chrome, on every bare
   <input type=checkbox|radio|range> -- the ones Bootstrap 4's .custom-control never replaced,
   such as the Idle Mode column on /statusindicators. One declaration reaches all of them;
   .custom-control-input paints its own box and is unaffected.                            */
.aderis {
  background-color: var(--aderis-bg, #030303);
  color: var(--aderis-text, #f5f5f5);
  color-scheme: dark;
  accent-color: var(--aderis-primary, #00b176);
}

/* acuity.css has `h1 { color: var(--darker-gray) !important }`, marked DEPRECATED in that
   file itself. On a dark surface it renders mid-grey on near-black. */
.aderis h1,
.aderis h2,
.aderis h3,
.aderis h4,
.aderis h5,
.aderis h6 {
  color: var(--aderis-text, #f5f5f5);
}

.aderis a {
  color: var(--aderis-link, #f78a1e);
}

.aderis hr {
  border-top-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
}

.aderis code {
  color: var(--aderis-clean-green, #00b176);
}

.aderis .text-muted {
  color: var(--aderis-text-muted, #b3b3b3);
}

/* ── Buttons ──────────────────────────────────────────────────────────────────
   8px radius matches the Aderis component set.

   INK PAIRING, the thing that goes wrong: --aderis-on-accent (white) belongs on the
   mid-dark fills (green, crimson, blue); --aderis-on-accent-strong (near-black) belongs on
   the bright ones (orange, yellow, lemon). White on bright yellow is unreadable.        */
/* `filter` is in the transition list because the primary/success hover below animates
   brightness, not a colour. Without it the hover snaps instead of easing, which reads as a
   different interaction from every other button in the file. */
.aderis .btn {
  border-radius: 8px;
  border-width: 1px;
  transition: background-color 0.15s ease, border-color 0.15s ease, filter 0.15s ease;
}

/* Bootstrap 4 gives .btn:focus its own blue halo (box-shadow with rgba(0,123,255,.25)), and
   each btn-* variant adds another. Suppressing it and supplying the Aderis outline separately
   is what stops a stray blue ring appearing on a green button. */
.aderis .btn:focus,
.aderis .btn.focus {
  box-shadow: none;
}

/* :focus-visible, not :focus -- matching AderisButton.razor.css:27-30. The ring shows for
   keyboard navigation and not for mouse clicks, which is the behaviour the reference
   component ships. */
.aderis .btn:focus-visible {
  outline: 2px solid var(--aderis-primary, #00b176);
  outline-offset: 2px;
}

.aderis .btn-primary,
.aderis .btn-success {
  background-color: var(--aderis-primary-fill, #007850);
  border-color: var(--aderis-primary-fill, #007850);
  color: var(--aderis-on-accent, #ffffff);
}

/* Lighten on hover, matching the direction the reference component takes. `filter` rather
   than a second colour: it avoids inventing an intermediate literal, which the guard test
   forbids and which would need re-deriving by hand if --aderis-primary is ever re-pointed.
   White text stays white under brightness > 1 while the fill gets lighter, so contrast
   DROPS across the hover: 5.52:1 at rest becomes about 4.61:1 hovered. That still clears
   AA for body text (4.5:1), which is why 1.12 is the ceiling -- the token comment upstream
   derives 32% black on the same budget, so raising this brightness breaks that derivation. */
.aderis .btn-primary:hover,
.aderis .btn-success:hover {
  filter: brightness(1.12);
}

.aderis .btn-secondary {
  background-color: var(--aderis-surface-neutral, #1e1e1e);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text, #f5f5f5);
}

.aderis .btn-secondary:hover {
  background-color: var(--aderis-surface-neutral-hover, #2a2a2a);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text, #f5f5f5);
}

.aderis .btn-danger {
  background-color: var(--aderis-danger, #ee4a63);
  border-color: var(--aderis-danger, #ee4a63);
  color: var(--aderis-on-accent, #ffffff);
}

.aderis .btn-warning {
  background-color: var(--aderis-warning, #ffe14d);
  border-color: var(--aderis-warning, #ffe14d);
  color: var(--aderis-on-accent-strong, #1a1200);
}

/* Bootstrap's .btn-light is #f8f9fa and .btn-dark #343a40 -- both literal, neither reachable
   from a variable, and NEITHER was overridden until now. .btn-light is why the Reports page
   renders a stark white pill for the DISABLED api button:
   `{{ 'btn-primary' if report_enabled else 'btn-light' }}`. On a #141414 card a near-white
   fill is the loudest element on the page, and it means "off".

   Both map onto the neutral raised surface, i.e. .btn-secondary's treatment: in a dark-only
   UI "light" and "dark" are not meaningful brightness directions, and what every call site
   actually wants is a button carrying no semantic colour. 15.29:1 for --aderis-text on it. */
.aderis .btn-light,
.aderis .btn-dark {
  background-color: var(--aderis-surface-neutral, #1e1e1e);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text, #f5f5f5);
}

.aderis .btn-light:hover,
.aderis .btn-dark:hover {
  background-color: var(--aderis-surface-neutral-hover, #2a2a2a);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text, #f5f5f5);
}

/* White on this blue is 3.45:1 -- marginal, and the same class of issue as .btn-danger's
   3.62:1. Left consistent with the INK PAIRING note above rather than fixed here: there are
   3 call sites, and diverging from .btn-danger would need a reason this design cannot give. */
.aderis .btn-info {
  background-color: var(--aderis-info, #1e8bf7);
  border-color: var(--aderis-info, #1e8bf7);
  color: var(--aderis-on-accent, #ffffff);
}

.aderis .btn-link {
  color: var(--aderis-link, #f78a1e);
}

.aderis .btn-outline-primary {
  background-color: transparent;
  border-color: var(--aderis-primary, #00b176);
  color: var(--aderis-primary, #00b176);
}

/* --aderis-primary-fill, not the raw --aderis-primary. Same owner ruling as .btn-primary and
   .badge-primary: the bright accent is for outlines, focus rings, tabs and spinners, never a
   filled surface carrying text -- white on #00b176 is 2.78:1. The fill gives 5.52:1.

   `filter: none` is not redundant. Templates in this app write
   class="btn btn-primary btn-outline-primary", so BOTH .btn-primary:hover and this rule match
   the same element and the brightness(1.12) above would lighten this fill too -- landing at
   2.23:1, worse than the raw accent it replaces. This rule must therefore stay AFTER
   .btn-primary:hover: both are (0,2,0), so paint order is what decides the filter. */
.aderis .btn-outline-primary:hover {
  background-color: var(--aderis-primary-fill, #007850);
  border-color: var(--aderis-primary-fill, #007850);
  color: var(--aderis-on-accent, #ffffff);
  filter: none;
}

.aderis .btn.disabled,
.aderis .btn:disabled {
  opacity: 0.45;
}

/* ── Pressed / selected buttons ───────────────────────────────────────────────
   Bootstrap declares these as
     .btn-primary:not(:disabled):not(.disabled).active,
     .btn-primary:not(:disabled):not(.disabled):active,
     .show>.btn-primary.dropdown-toggle { background-color: #0062cc }
   at (0,4,0) and (0,3,0), both of which beat our plain (0,2,0) variant rules. Untouched, every
   button flashes Bootstrap blue or grey on mouse-down, and an OPEN dropdown toggle -- the
   navbar's Reports / Config / System menus, written class="btn btn-secondary dropdown-toggle" --
   sits in Bootstrap grey for as long as the menu is up. Matching Bootstrap's own selector shape
   puts us one class ahead of it, so these win by specificity rather than paint order.

   .active is a SELECTED state here, not just a mouse-down flash: devices_tree_2, data_query and
   the device pages toggle `active` on btn-primary/btn-success to mark the chosen item. So the
   pressed shade has to be distinguishable from the resting fill, which rules out simply
   restating the variant's own colour.

   The shade is a deeper tint of the variant's OWN -rgb token rather than a new literal or a
   brightness filter. `filter` is wrong here specifically: unlike the hover, which lightens and
   leaves white text clamped at white, darkening with brightness() < 1 would dim the label along
   with the fill. Composing the alpha instead leaves the text untouched, and it is the same
   idiom .alert-* and .card-footer already use. Measured over --aderis-card: primary/success
   6.62:1, danger 6.03:1, warning 8.38:1 -- each ABOVE its own resting fill, so pressing never
   costs legibility.

   `filter: none` cancels the hover's brightness(1.12), which still matches while the button is
   held down; without it the deliberate pressed shade would be lightened back toward the hover. */
.aderis .btn-primary:not(:disabled):not(.disabled):active,
.aderis .btn-primary:not(:disabled):not(.disabled).active,
.aderis .btn-success:not(:disabled):not(.disabled):active,
.aderis .btn-success:not(:disabled):not(.disabled).active,
.aderis .show > .btn-primary.dropdown-toggle,
.aderis .show > .btn-success.dropdown-toggle {
  background-color: rgba(var(--aderis-primary-rgb, 0, 177, 118), 0.55);
  border-color: var(--aderis-primary-fill, #007850);
  color: var(--aderis-on-accent, #ffffff);
  filter: none;
}

/* Secondary is the exception to "pressed is darker": its resting fill is already a near-black
   neutral, so darkening it further would sink the button into the page instead of reading as
   pressed. It takes the raised neutral instead -- lighter, which is how elevation reads in this
   system, and the same shade its own hover uses. */
.aderis .btn-secondary:not(:disabled):not(.disabled):active,
.aderis .btn-secondary:not(:disabled):not(.disabled).active,
.aderis .show > .btn-secondary.dropdown-toggle {
  background-color: var(--aderis-surface-neutral-hover, #2a2a2a);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text, #f5f5f5);
  filter: none;
}

.aderis .btn-danger:not(:disabled):not(.disabled):active,
.aderis .btn-danger:not(:disabled):not(.disabled).active,
.aderis .show > .btn-danger.dropdown-toggle {
  background-color: rgba(var(--aderis-danger-rgb, 238, 74, 99), 0.7);
  border-color: var(--aderis-danger, #ee4a63);
  color: var(--aderis-on-accent, #ffffff);
  filter: none;
}

/* Bright fill, so the near-black ink -- Task 3's pairing rule, not Bootstrap's #212529. */
.aderis .btn-warning:not(:disabled):not(.disabled):active,
.aderis .btn-warning:not(:disabled):not(.disabled).active,
.aderis .show > .btn-warning.dropdown-toggle {
  background-color: rgba(var(--aderis-warning-rgb, 255, 225, 77), 0.75);
  border-color: var(--aderis-warning, #ffe14d);
  color: var(--aderis-on-accent-strong, #1a1200);
  filter: none;
}

/* Neutral pressed shade comes from --aderis-on-surface-rgb, the lightening ink, because
   --aderis-surface-neutral has no -rgb triple to tint the way the variants above do. */
.aderis .btn-light:not(:disabled):not(.disabled):active,
.aderis .btn-light:not(:disabled):not(.disabled).active,
.aderis .btn-dark:not(:disabled):not(.disabled):active,
.aderis .btn-dark:not(:disabled):not(.disabled).active,
.aderis .show > .btn-light.dropdown-toggle,
.aderis .show > .btn-dark.dropdown-toggle {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.12);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text, #f5f5f5);
}

.aderis .btn-info:not(:disabled):not(.disabled):active,
.aderis .btn-info:not(:disabled):not(.disabled).active,
.aderis .show > .btn-info.dropdown-toggle {
  background-color: rgba(var(--aderis-info-rgb, 30, 139, 247), 0.75);
  border-color: var(--aderis-info, #1e8bf7);
}

/* ── Row-action icon buttons ───────────────────────────────────────────────────
   WHY A NEW FAMILY: btn-warning was doing two jobs. 78 of its 101 uses were the EDIT
   affordance; 9 were genuine cautions. One class cannot carry both, and the yellow is
   load-bearing for the cautions -- aderis-theme.css holds --aderis-lemon at ~50deg
   specifically so it clears the orange at 30deg, and says so in a comment.

   NOT btn-outline-warning / btn-outline-danger: Bootstrap 4.1.3 ships rules for those,
   including (0,3,0) and (0,4,0) pressed variants that would each need a mirrored selector
   to beat. `.btn-edit` and `.btn-delete` are names Bootstrap has never heard of, so a plain
   (0,2,0) rule wins against nothing and needs no pressed-state counter-rule at all.

   TONAL, NOT FILLED: these sit 20-30 to a table. A solid fill at that density competes with
   the page's one primary CTA, and orange is already the link and nav-rail colour. The
   0.12 fill / 0.35 border is the same recipe the .alert-* rules use, so the two families
   stay in visual agreement. */
.aderis .btn-edit {
  background-color: rgba(var(--aderis-orange-rgb, 247, 138, 30), 0.12);
  border-color: rgba(var(--aderis-orange-rgb, 247, 138, 30), 0.35);
  color: var(--aderis-orange, #f78a1e);
}

/* Fills SOLID on hover rather than deepening the tint: the tint is what makes these quiet at
   rest, so the hover has to read as a state change at a glance in a dense table. Contrast
   RISES across it, 6.33:1 to 7.62:1 -- the opposite of .btn-primary, and the reason this is a
   colour swap and not that family's brightness() filter, which would only wash the tint out. */
.aderis .btn-edit:hover {
  background-color: var(--aderis-orange, #f78a1e);
  border-color: var(--aderis-orange, #f78a1e);
  color: var(--aderis-on-accent-strong, #1a1200);
}

.aderis .btn-delete {
  background-color: rgba(var(--aderis-danger-rgb, 238, 74, 99), 0.12);
  border-color: rgba(var(--aderis-danger-rgb, 238, 74, 99), 0.35);
  color: var(--aderis-danger, #ee4a63);
}

/* --aderis-on-accent (white), matching .btn-danger, so a tonal delete and a solid Delete All
   share one ink once hovered. White on #ee4a63 is 3.62:1 where near-black would be 5.13:1 --
   acceptable only because these are ICON-ONLY, making the glyph a graphical object at WCAG
   1.4.11's 3:1 threshold. A LABELLED .btn-delete would need 4.5:1 and therefore
   --aderis-on-accent-strong here, AND a rethink of the 4.52:1 resting state. */
.aderis .btn-delete:hover {
  background-color: var(--aderis-danger, #ee4a63);
  border-color: var(--aderis-danger, #ee4a63);
  color: var(--aderis-on-accent, #ffffff);
}

/* The VIEW/info affordance, third member of the family. It was riding on btn-primary, which
   made every config row carry a filled primary button -- the exact competition-with-the-CTA
   problem the family exists to solve, and worse than the edit case because primary IS the CTA
   colour. Same 0.12/0.35 recipe; --aderis-primary stays its green, which is the point: the
   affordance keeps its colour and loses its weight. */
.aderis .btn-view {
  background-color: rgba(var(--aderis-primary-rgb, 0, 177, 118), 0.12);
  border-color: rgba(var(--aderis-primary-rgb, 0, 177, 118), 0.35);
  color: var(--aderis-primary, #00b176);
}

/* --aderis-primary-fill and --aderis-on-accent, i.e. .btn-primary's exact paint, for the same
   reason .btn-delete:hover matches .btn-danger: a tonal view and a solid primary button share
   one ink once hovered. White on #007850 is 5.52:1, the figure the .btn-primary comment above
   already carries. NOT the brightness() filter that family hovers with -- on a 0.12 tint the
   filter would only wash the tint out instead of reading as a state change. */
.aderis .btn-view:hover {
  background-color: var(--aderis-primary-fill, #007850);
  border-color: var(--aderis-primary-fill, #007850);
  color: var(--aderis-on-accent, #ffffff);
}

/* ── Icon-button glyphs ────────────────────────────────────────────────────────
   acuity.css:488 draws these with `content: url(/static/icons/mypv_edit.svg)`, and both SVG
   files carry `style="fill:#fff"` INLINE. An external SVG is a separate document, unreachable
   from page CSS, so `color` never reached the glyph and every one of these rendered white --
   white on the lemon fill is 1.30:1, the exact pairing the INK PAIRING note warns against.
   It shipped, and is legible only because the pencil is a thick glyph.

   mask-image fixes it structurally instead of by editing the SVGs: the file becomes an alpha
   stencil and the visible pixels come from `background-color: currentColor`, so the glyph now
   follows its button's own `color` in every state and for every variant. The SVGs are left
   alone, which matters because the Angular tiles share them.

   WIDTH AND HEIGHT ARE NOW OVERRIDDEN TOO, on the icon-only rules below, not just `content`
   and the paint. Both SVGs are 24x24 Material icons with ~3 units of padding, so the visible
   ink is ~17.7x18 units. `content: url()` used to render them at their intrinsic 24x24 --
   ignoring acuity.css:490's 15px box entirely -- which read as ~18px of ink. Switching to
   `mask-size: 15px 15px` shrank that ink to ~11px, visibly smaller than the 14px FontAwesome
   sprites next to them (.mypv-icon is 1em, and .btn-sm sets font-size: 14px). Raising
   mask-size alone does not fix this: mask-clip defaults to border-box, and the box is still
   15px (acuity.css:490), so anything the mask paints past that gets clipped. The box has to
   grow with it, hence `width: 20px; height: 20px` alongside `mask-size: 20px 20px`, which
   lands the ink near 14px and matches the sprites again. This also reaches the WEG and
   dead-file call sites that the retag deliberately skips, which is why their retained yellow
   buttons get fixed for free (near-black on lemon, 14.27:1).

   GEOMETRY IS NOW OVERRIDDEN AS WELL, on the icon-only rules below: `display: block;
   float: none; margin: 0 auto` replaces acuity.css's `float: left; margin-right: 10px;
   margin-top: -2px`.

   Those three declarations position a glyph BEFORE A LABEL, and .mypv_edit_btn /
   .mypv_delete_btn never have one -- all 77 and 27 live uses are icon-only, which
   IconGlyphButtonsAreIconOnly in AderisTemplateConventionTests now holds to, and
   .mypv_view_btn / .mypv_download_btn (4 and 1 uses) are under the same contract in the same
   test, so all four rules share one geometry and the buttons come out the same size when they
   sit in the same row -- which on /device_types all four do. So the 10px was
   dead space INSIDE the button, making it 46px wide to hold a 20px glyph, and the float pushed
   that space entirely to one side: every edit and delete button in the app was visibly
   lopsided, worst where one sits alone in a card header. An earlier pass left these three
   alone on the theory that they carried button-to-button spacing; for these two classes they
   do not. Now 36px -- btn-sm's own 8px of side padding around the glyph -- and centred.

   `.mypv_edit_btn_text::before` (acuity.css:416) is the LABELLED sibling of `.mypv_edit_btn`
   and has its own, different geometry: 15x15 with a 15px margin-right, not the icon-only
   variant's 15x22 with a 10px margin-right. Its acuity.css box is already 15px square, so it
   is left at ONLY `content` and the paint overridden, same as every rule here was before the
   resize above -- no width/height override, `mask-size: 15px 15px` fits its box with nothing
   to clip. There is no `mypv_delete_btn_text` in use anywhere in this app, so it gets no
   counterpart rule.

   The `-webkit-mask` shorthand is written out as longhands below, not the
   `url() center / size no-repeat` compound form: an older WebKit that rejects that compound
   form drops the WHOLE `-webkit-mask` declaration, and `content: ""` plus
   `background-color: currentColor` would then paint a solid currentColor RECTANGLE -- not the
   invisible glyph a dropped mask would suggest. Longhands mean a single unsupported property
   (most likely `-webkit-mask-position`) can drop alone without taking the rest down with it.
   The unprefixed `mask` shorthand is unaffected and stays as one declaration.

   `currentColor` is exempt from the colour-literal guard by that test's own design. */
.aderis .mypv_edit_btn::before {
  content: "";
  background-color: currentColor;
  display: block;
  float: none;
  width: 20px;
  height: 20px;
  margin: 0 auto;
  -webkit-mask-image: url(/static/icons/mypv_edit.svg);
  -webkit-mask-position: center;
  -webkit-mask-size: 20px 20px;
  -webkit-mask-repeat: no-repeat;
          mask: url(/static/icons/mypv_edit.svg) center / 20px 20px no-repeat;
}

.aderis .mypv_delete_btn::before {
  content: "";
  background-color: currentColor;
  display: block;
  float: none;
  width: 20px;
  height: 20px;
  margin: 0 auto;
  -webkit-mask-image: url(/static/icons/mypv_delete.svg);
  -webkit-mask-position: center;
  -webkit-mask-size: 20px 20px;
  -webkit-mask-repeat: no-repeat;
          mask: url(/static/icons/mypv_delete.svg) center / 20px 20px no-repeat;
}

.aderis .mypv_view_btn::before {
  content: "";
  background-color: currentColor;
  display: block;
  float: none;
  width: 20px;
  height: 20px;
  margin: 0 auto;
  -webkit-mask-image: url(/static/icons/mypv_view.svg);
  -webkit-mask-position: center;
  -webkit-mask-size: 20px 20px;
  -webkit-mask-repeat: no-repeat;
          mask: url(/static/icons/mypv_view.svg) center / 20px 20px no-repeat;
}

.aderis .mypv_download_btn::before {
  content: "";
  background-color: currentColor;
  display: block;
  float: none;
  width: 20px;
  height: 20px;
  margin: 0 auto;
  -webkit-mask-image: url(/static/icons/mypv_download_cloud.svg);
  -webkit-mask-position: center;
  -webkit-mask-size: 20px 20px;
  -webkit-mask-repeat: no-repeat;
          mask: url(/static/icons/mypv_download_cloud.svg) center / 20px 20px no-repeat;
}

.aderis .mypv_edit_btn_text::before {
  content: "";
  background-color: currentColor;
  -webkit-mask-image: url(/static/icons/mypv_edit.svg);
  -webkit-mask-position: center;
  -webkit-mask-size: 15px 15px;
  -webkit-mask-repeat: no-repeat;
          mask: url(/static/icons/mypv_edit.svg) center / 15px 15px no-repeat;

  /* Geometry after all, contrary to the note above -- acuity.css:416-423 floats this glyph
     left with `margin-right: 15px; margin-top: -2px`, which top-aligns a 15px box in
     btn-sm's 21px line box and then lifts it 2px further. The label is centred by
     `.btn { text-align: center }` in the float-shortened line box, so the icon sat high and
     the padding read lopsided -- the "Edit Variables button alignment issue".

     This never showed on dev because mypv_edit.svg has `fill: #fff` baked in, so the old
     `content: url(...)` painted a white pencil on btn-warning yellow at roughly 1.6:1 --
     invisible. Repainting it to currentColor is what made the pre-existing misalignment
     appear. Inline-block with a normal margin and an optical baseline nudge instead. */
  display: inline-block;
  float: none;
  margin: 0 6px 0 0;
  vertical-align: -0.125em;
}

/* The blue halo again, one level deeper. Our `.aderis .btn:focus { box-shadow: none }` is
   (0,3,0), but Bootstrap re-adds the ring at
   `.btn-primary:not(:disabled):not(.disabled):active:focus` (0,5,0) -- so a button that is
   focused AND pressed still flashes rgba(0,123,255,.5). Matching the shape puts us at (0,6,0).
   The Aderis focus ring is supplied by :focus-visible above and is unaffected. */
.aderis .btn:not(:disabled):not(.disabled):active:focus,
.aderis .btn:not(:disabled):not(.disabled).active:focus,
.aderis .show > .btn.dropdown-toggle:focus {
  box-shadow: none;
}

/* ── Form controls ───────────────────────────────────────────────────────────
   Mirrors the .form-control block in aderis-theme.css, from an unlayered file so it wins.

   `color-scheme: dark` on .aderis is what fixes the parts CSS cannot reach: the native
   dropdown list of a <select>, autofill backgrounds, scrollbars.                        */
.aderis .form-control {
  background-color: var(--aderis-ash, #262626);
  border: 1px solid var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  border-radius: 8px;
  color: var(--aderis-text, #f5f5f5);
}

.aderis .form-control::placeholder {
  color: var(--aderis-text-muted, #b3b3b3);
  opacity: 1;
}

.aderis .form-control:focus {
  background-color: var(--aderis-ash, #262626);
  border-color: var(--aderis-primary, #00b176);
  box-shadow: 0 0 0 3px rgba(var(--aderis-primary-rgb, 0, 177, 118), 0.15);
  color: var(--aderis-text, #f5f5f5);
}

/* Bootstrap gives disabled inputs `background-color: #e9ecef`, which on a dark surface reads
   as a BRIGHTER box -- the opposite of the intended affordance. */
.aderis .form-control:disabled {
  background-color: var(--aderis-surface-neutral, #1e1e1e);
  color: var(--aderis-text-muted, #b3b3b3);
  opacity: 0.45;
}

/* [readonly] is deliberately NOT the disabled treatment, and the two must not be merged back
   together. Bootstrap gives both the same pale box, but they mean different things: a disabled
   field's value is inert and may be dimmed, whereas a readonly field exists precisely to be
   READ -- the dashboard's change-confirmation modal shows the original and the new value in
   readonly inputs, and 33 templates use the attribute. Under the disabled opacity of 0.6 that
   text composites to 3.72:1, below AA for body text; at full strength it is 15.29:1. So the
   surface keeps the "not editable" cue and the ink stays at full strength.

   `:not(:disabled)` because a control can carry BOTH attributes, and then it is disabled --
   readonly is the weaker claim and must not cancel the dim. That combination is not written in
   any template; it is produced at runtime, which is why it went unnoticed. flatpickr sets
   `readonly` on its own input whenever allowInput is false, i.e. on all 18 templates that use
   it, and a page that additionally toggles `disabled` (data_toolbox is one of 48 such places)
   then hit these two rules at the same (0,2,1) specificity with this one later in the file --
   so the disabled state won nothing and the field read exactly like a live one. */
.aderis .form-control[readonly]:not(:disabled) {
  background-color: var(--aderis-surface-neutral, #1e1e1e);
  color: var(--aderis-text, #f5f5f5);
  opacity: 1;
}

/* ── Two leaks out of layer(aderis) ──────────────────────────────────────────
   aderis-bridge.css imports aderis-theme.css into layer(aderis) on the premise that every
   token flows through while every RULE stays suppressed, because unlayered declarations beat
   layered ones. That premise only holds per PROPERTY: a property the layer declares and no
   unlayered rule does applies uncontested. Two such properties ride in on .form-control --
   `appearance: none` and `min-height: 38px` -- and nothing in Bootstrap, acuity.css,
   mypv_config_style.css or this file declares either. Both fixes below are unlayered, so they
   win outright; both are narrow, so the intended .form-control treatment above is untouched.

   The general lesson: any future property the Blazor theme adds to a shared class will leak
   the same way. The bridge's comment overstates the isolation. */

/* Leak 1 -- the select caret. `appearance: none` strips the native dropdown chrome and the
   layer supplies no replacement: no background-image, no caret gutter. Every template uses
   `<select class="form-control">`; .custom-select is handled separately below. currentColor
   rather than a literal: it resolves to the `color` already set above, so the caret is
   token-driven and introduces no new hex. Guards for <select multiple> and <select size>,
   which are list boxes and must not get a caret.

   `appearance: none` is declared HERE as well, not only inherited from the layer: the layer
   only loads when pages are served through Acuity.Host (/_content/**), and Caddy proxies
   straight to gunicorn :8081, so on the Caddy path the theme 404s -- by design, tokens fall
   back to literals -- and the select kept its NATIVE arrow while this rule drew the chevron
   next to it: two carets on every dropdown. The rule that draws a replacement caret must be
   the rule that removes the native one, so it renders one caret on every serving path. */
.aderis select.form-control:not([multiple]):not([size]) {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2rem;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 1.05rem center, right 0.75rem center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat, no-repeat;
}

/* Leak 2 -- the oversized checkbox halo. 41 templates put .form-control on a checkbox or
   radio. That was harmless while the inputs were native, because a native checkbox discards
   background-color, border, border-radius and min-height. Once `appearance: none` and
   `min-height: 38px` land, the glyph becomes a styleable box -- 15px wide from
   .mypv-small-check, 38px tall from the layer -- which the .form-control block above then
   fills, borders and, on focus, wraps in a 3px ring. That is the reported "checkbox selection
   highlight has padding": a halo far larger than and offset from the glyph. Hand the native
   control back rather than reworking 41 templates. */
.aderis input[type="checkbox"].form-control,
.aderis input[type="radio"].form-control {
  appearance: auto;
  -webkit-appearance: auto;
  min-height: 0;
  height: auto;
  width: auto;
  padding: 0;
  background-color: transparent;
  border: 0;
  border-radius: 0;
  vertical-align: middle;
}

.aderis input[type="checkbox"].form-control:focus,
.aderis input[type="radio"].form-control:focus {
  background-color: transparent;
  border: 0;
  box-shadow: none;
}

/* Bootstrap 4.1.3's .custom-select hardcodes `background: #fff` plus a dark chevron SVG, so
   the 9 native .custom-select controls in the app (alarms.html's add-a-custom-alarm picker,
   datapi/device_measures, wire_tap/monitor_v1) rendered as white boxes beside fully themed
   selectpickers -- the "dropdown to add a custom alarm is a different style" report.
   color-scheme cannot reach a background-image, so the caret is redrawn here the same way as
   for select.form-control above. */
.aderis .custom-select {
  /* Same self-sufficiency as select.form-control above: draw the caret, strip the native
     one, regardless of whether the layered theme loaded on this serving path. */
  appearance: none;
  -webkit-appearance: none;
  background-color: var(--aderis-ash, #262626);
  border: 1px solid var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  border-radius: 8px;
  color: var(--aderis-text, #f5f5f5);
  padding-right: 2rem;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 1.05rem center, right 0.75rem center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat, no-repeat;
}

.aderis .custom-select:focus {
  border-color: var(--aderis-primary, #00b176);
  box-shadow: 0 0 0 3px rgba(var(--aderis-primary-rgb, 0, 177, 118), 0.15);
}

.aderis label {
  color: var(--aderis-text, #f5f5f5);
}

.aderis .form-text {
  color: var(--aderis-text-muted, #b3b3b3);
}

.aderis .input-group-text {
  background-color: var(--aderis-surface-neutral, #1e1e1e);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text-muted, #b3b3b3);
}

.aderis .custom-control-label {
  color: var(--aderis-text, #f5f5f5);
}

/* ── Cards ────────────────────────────────────────────────────────────────────
   Mirrors Card/AderisCard.razor.css and its header/footer partials.

   --aderis-card (#141414) is lighter than --aderis-bg (#030303): in this system elevation
   reads as lighter, AND it carries a drop shadow -- the reference uses both, so both are
   here. The 24px radius is the component's own value; 8px would visibly disagree with every
   Razor card on screen.

   NO overflow: hidden here, and it must not come back. It was doing the corner masking that
   the explicit inner radii below now do, and it SWALLOWED every absolutely positioned child
   that escapes the card box: bootstrap-select menus, .dropdown-menu, flatpickr calendars,
   tooltips and popovers. 16 templates put a .selectpicker inside a .card, and on the short
   cards (Alarms -> Profiles, Alarms -> Device) the open menu is taller than the card, so it
   was cut off at the card's bottom edge. That is the "dropdowns getting swallowed by their
   containers" report, and it was site-wide.

   Deliberately NOT mirrored: the reference's padding, flex layout, uppercase transform,
   letter-spacing and muted header colour. Those style the Blazor component's own markup;
   imposing them on Bootstrap's .card-header would restyle content, not surfaces, across 67
   templates. Surfaces and borders are mirrored; typography and layout stay Bootstrap's. */
.aderis .card {
  background-color: var(--aderis-card, #141414);
  border: 1px solid var(--aderis-border, rgba(255, 255, 255, 0.08));
  border-radius: 24px;
  box-shadow: 0 18px 30px rgba(var(--aderis-shadow-rgb, 0, 0, 0), 0.35);
  color: var(--aderis-text, #f5f5f5);
}

/* What overflow: hidden used to do, without the clipping. Bootstrap 4.1.3 already rounds
   these corners -- `.card-header:first-child { border-radius: calc(.25rem - 1px) ... }` --
   it just uses its own 4px card radius, and that mismatch against 24px is the square-corner
   bleed the overflow was hiding. 23px is the 24px radius minus the 1px border, mirroring
   Bootstrap's own calc() pattern. (0,3,0) beats Bootstrap's (0,2,0), so no !important. */
.aderis .card-header:first-child {
  border-top-left-radius: 23px;
  border-top-right-radius: 23px;
}

.aderis .card-footer:last-child {
  border-bottom-left-radius: 23px;
  border-bottom-right-radius: 23px;
}

/* --aderis-border-strong, not --aderis-border: the header/body seam is meant to be the
   stronger line. Both header partials in the reference use the strong token, and this file
   already uses it correctly for .table thead th. */
.aderis .card-header {
  background-color: var(--aderis-card-header, rgba(255, 255, 255, 0.06));
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text, #f5f5f5);
}

/* The footer's tint is fainter than the header's -- 0.03 against the header's 0.06 -- which
   the reference composes separately rather than reusing --aderis-card-header. */
.aderis .card-footer {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.03);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text, #f5f5f5);
}

.aderis .card-title {
  color: var(--aderis-text, #f5f5f5);
}

/* ── Tables ───────────────────────────────────────────────────────────────────
   Bootstrap 4's .table sets `color: #212529`, and .table-striped tints odd rows with
   `rgba(0,0,0,.05)` -- a BLACK tint, invisible on a dark surface. Striping has to invert to
   a white tint to read at all. 80 templates contain tables, so this is one of the
   highest-visibility conversions in the plan.                                            */
.aderis .table {
  color: var(--aderis-text, #f5f5f5);
}

.aderis .table th,
.aderis .table td {
  border-top-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
}

.aderis .table thead th {
  border-bottom-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text-muted, #b3b3b3);
}

/* Bootstrap's emphasised header strip: `.table .thead-light th { background-color: #e9ecef;
   color: #495057 }` at (0,2,1) -- a near-white band that survived the restyle, 14 uses across
   /devices/all and the welcome page. Mirroring its selector shape lands (0,3,1), which also
   beats our own plain thead rule above, so the strip keeps the muted ink AND gains the
   card-header tint that makes it read as a band. .thead-dark is its #212529 sibling -- unused
   today, but retagged identically for the .btn-light/.btn-dark reason: in a dark-only UI
   neither name is a brightness direction, and both mean "emphasised header". */
.aderis .table .thead-light th,
.aderis .table .thead-dark th {
  background-color: var(--aderis-card-header, rgba(255, 255, 255, 0.06));
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text-muted, #b3b3b3);
}

.aderis .table-striped tbody tr:nth-of-type(odd) {
  background-color: var(--aderis-surface-2, rgba(255, 255, 255, 0.04));
}

.aderis .table-hover tbody tr:hover {
  background-color: var(--aderis-surface-2, rgba(255, 255, 255, 0.04));
  color: var(--aderis-text, #f5f5f5);
}

.aderis .table-bordered,
.aderis .table-bordered th,
.aderis .table-bordered td {
  border-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
}

/* ── Contextual table rows ────────────────────────────────────────────────────
   THESE CARRY STATUS MEANING, so a wrong colour here is a correctness bug, not a cosmetic
   one: a row tinted "danger" is telling an operator something is wrong with that device or
   process, and a reader who cannot see the tint -- or who sees the wrong one -- loses that
   signal entirely.

   Bootstrap sets background ONLY (`.table-primary{background-color:#b8daff}`) and leaves the
   text to .table's `color: #212529`. Our .table rule replaces that ink with the light body
   colour, so every one of these pale fills ends up light-on-pale: measured 1.06:1
   (.table-warning) to 1.54:1 (.table-dark). Re-tinting them as dark-surface tints -- the same
   move .alert-* already makes -- puts the same rows at 12.11:1 to 15.03:1.

   Bootstrap's selector is `.table-primary, .table-primary>td, .table-primary>th` at (0,1,1),
   so the child selectors must be repeated here or the td/th rules would still paint the pale
   fill over the row. With .aderis prepended these are (0,2,1) and win.

   Only background-color is set: the inherited light ink is already correct, and restating it
   would fight .table-dark's own `color` handling for no gain.

   Semantic mapping, deliberately through the ROLE tokens so a design-system remap follows:
   primary and info both to --aderis-info-rgb (Bootstrap's primary IS informational blue here;
   the Aderis primary is green, and a green "primary" row would be indistinguishable from a
   success row); success/danger/warning to their own roles; secondary, light, dark and active
   to the neutral lightening ink, since none of them carries a hue. .table-light and
   .table-dark here are Bootstrap's near-white and light-grey CONTEXTUAL row variants (#fdfdfe
   and #c6c8ca); on a dark surface both collapse to the same neutral tint. Note .table-dark is
   also Bootstrap's dark TABLE skin (`.table-dark{background-color:#212529}` at (0,1,0)), which
   our (0,2,0) rule likewise re-tints -- acceptable, since a dark skin on a dark page has
   nothing left to express, and no template in this app uses either meaning today. */
.aderis .table-primary,
.aderis .table-primary > td,
.aderis .table-primary > th,
.aderis .table-info,
.aderis .table-info > td,
.aderis .table-info > th {
  background-color: rgba(var(--aderis-info-rgb, 30, 139, 247), 0.12);
}

.aderis .table-success,
.aderis .table-success > td,
.aderis .table-success > th {
  background-color: rgba(var(--aderis-success-rgb, 0, 177, 118), 0.12);
}

.aderis .table-danger,
.aderis .table-danger > td,
.aderis .table-danger > th {
  background-color: rgba(var(--aderis-danger-rgb, 238, 74, 99), 0.12);
}

.aderis .table-warning,
.aderis .table-warning > td,
.aderis .table-warning > th {
  background-color: rgba(var(--aderis-warning-rgb, 255, 225, 77), 0.12);
}

.aderis .table-secondary,
.aderis .table-secondary > td,
.aderis .table-secondary > th,
.aderis .table-light,
.aderis .table-light > td,
.aderis .table-light > th,
.aderis .table-dark,
.aderis .table-dark > td,
.aderis .table-dark > th,
.aderis .table-active,
.aderis .table-active > td,
.aderis .table-active > th {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.12);
}

/* .table-hover re-states each contextual fill on :hover at (0,2,1) -- e.g.
   `.table-hover .table-primary:hover>td` -- which would reinstate the pale colour the moment
   the pointer crosses the row. (0,3,1) with .aderis, so these win. A hover on an already
   tinted row lifts it one step rather than changing its meaning. */
.aderis .table-hover .table-primary:hover,
.aderis .table-hover .table-primary:hover > td,
.aderis .table-hover .table-primary:hover > th,
.aderis .table-hover .table-info:hover,
.aderis .table-hover .table-info:hover > td,
.aderis .table-hover .table-info:hover > th {
  background-color: rgba(var(--aderis-info-rgb, 30, 139, 247), 0.2);
}

.aderis .table-hover .table-success:hover,
.aderis .table-hover .table-success:hover > td,
.aderis .table-hover .table-success:hover > th {
  background-color: rgba(var(--aderis-success-rgb, 0, 177, 118), 0.2);
}

.aderis .table-hover .table-danger:hover,
.aderis .table-hover .table-danger:hover > td,
.aderis .table-hover .table-danger:hover > th {
  background-color: rgba(var(--aderis-danger-rgb, 238, 74, 99), 0.2);
}

.aderis .table-hover .table-warning:hover,
.aderis .table-hover .table-warning:hover > td,
.aderis .table-hover .table-warning:hover > th {
  background-color: rgba(var(--aderis-warning-rgb, 255, 225, 77), 0.2);
}

.aderis .table-hover .table-secondary:hover,
.aderis .table-hover .table-secondary:hover > td,
.aderis .table-hover .table-secondary:hover > th,
.aderis .table-hover .table-light:hover,
.aderis .table-hover .table-light:hover > td,
.aderis .table-hover .table-light:hover > th,
.aderis .table-hover .table-dark:hover,
.aderis .table-hover .table-dark:hover > td,
.aderis .table-hover .table-dark:hover > th,
.aderis .table-hover .table-active:hover,
.aderis .table-hover .table-active:hover > td,
.aderis .table-hover .table-active:hover > th {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.2);
}

/* ── Alerts ───────────────────────────────────────────────────────────────────
   Bootstrap 4 alerts are pale tinted blocks with dark text -- unreadable on a dark surface.
   The dark-surface equivalent is a transparent fill tinted by the semantic colour, a solid
   border in that colour, and the colour itself as the text. The -rgb tokens exist precisely
   so a colour can be used at partial alpha without hardcoding a second value.

   These use the SEMANTIC tokens (--aderis-success, --aderis-danger) rather than the raw
   palette (--aderis-clean-green, --aderis-red). If the design system remaps what "danger"
   means, these follow; that indirection is the point.                                    */
/* Mirrors Alert/AderisAlert.razor.css: a 12% tint of the semantic colour, a 30%-opacity
   border of the same, 12px radius, and text that BLENDS the semantic colour with body text
   rather than using it raw. The blend is the important part -- pure green on a dark green
   tint is low contrast, whereas 55% green plus 45% body text stays legible and still reads
   as "success".

   Warning blends at 60%, not 55%: bright yellow needs more of itself to hold its identity
   against the text colour. That asymmetry is the reference's, not a typo.

   If color-mix is unsupported the whole `color` declaration is dropped and the text inherits
   the page's light colour on a tinted background -- still legible, so the degradation is
   acceptable and needs no @supports guard.

   Deliberately NOT mirrored: the reference's flex layout, gap, padding and icon/close-button
   structure. Those belong to its own markup; Bootstrap's .alert is a plain block. */
.aderis .alert {
  border: 1px solid transparent;
  border-radius: 12px;
  font-size: 0.9rem;
}

.aderis .alert-success {
  background-color: rgba(var(--aderis-success-rgb, 0, 177, 118), 0.12);
  border-color: rgba(var(--aderis-success-rgb, 0, 177, 118), 0.3);
  color: color-mix(in srgb, var(--aderis-success, #00b176) 55%, var(--aderis-text, #f5f5f5));
}

.aderis .alert-danger {
  background-color: rgba(var(--aderis-danger-rgb, 238, 74, 99), 0.12);
  border-color: rgba(var(--aderis-danger-rgb, 238, 74, 99), 0.3);
  color: color-mix(in srgb, var(--aderis-danger, #ee4a63) 55%, var(--aderis-text, #f5f5f5));
}

.aderis .alert-warning {
  background-color: rgba(var(--aderis-warning-rgb, 255, 225, 77), 0.12);
  border-color: rgba(var(--aderis-warning-rgb, 255, 225, 77), 0.3);
  color: color-mix(in srgb, var(--aderis-warning, #ffe14d) 60%, var(--aderis-text, #f5f5f5));
}

.aderis .alert-info {
  background-color: rgba(var(--aderis-info-rgb, 30, 139, 247), 0.12);
  border-color: rgba(var(--aderis-info-rgb, 30, 139, 247), 0.3);
  color: color-mix(in srgb, var(--aderis-info, #1e8bf7) 55%, var(--aderis-text, #f5f5f5));
}

/* ── Badges ───────────────────────────────────────────────────────────────────
   Mirrors the base `.badge` in Badge/AderisStatusBadge.razor.css: a full pill (999px),
   uppercase, letter-spaced, 0.72rem at weight 600.

   The uppercase treatment IS mirrored here, unlike on card headers. A badge holds one short
   label, so changing its case is a style change; a card header holds arbitrary content --
   titles, buttons, forms -- where forcing uppercase would restyle content instead. Badges
   appear in 6 templates, so the blast radius is small either way.

   Fills carry text, so Task 3's ink pairing applies again.                               */
.aderis .badge {
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  padding: 0.3em 0.7em;
  text-transform: uppercase;
}

/* --aderis-primary-fill, not the raw bright --aderis-success: white on #00b176 measures
   2.78:1, and badge text is SMALL, so it needs 4.5:1 rather than the 3:1 UI floor. The fill
   gives 5.52:1. Same owner ruling that applies to .btn-primary/.btn-success in Task 3 --
   bright accents are for outlines, focus rings and tabs, never for a filled surface that
   carries text. */
.aderis .badge-primary,
.aderis .badge-success {
  background-color: var(--aderis-primary-fill, #007850);
  color: var(--aderis-on-accent, #ffffff);
}

.aderis .badge-danger {
  background-color: var(--aderis-danger, #ee4a63);
  color: var(--aderis-on-accent, #ffffff);
}

.aderis .badge-warning {
  background-color: var(--aderis-warning, #ffe14d);
  color: var(--aderis-on-accent-strong, #1a1200);
}

.aderis .badge-secondary {
  background-color: var(--aderis-surface-neutral, #1e1e1e);
  color: var(--aderis-text, #f5f5f5);
}

/* ── Modals ───────────────────────────────────────────────────────────────────
   THE TRAP: Bootstrap's .close is `color: #000` with `text-shadow: 0 1px 0 #fff` and
   `opacity: .5`. On a dark dialog that is an effectively invisible dismiss button, and it
   appears in 56 templates. The text-shadow has to go too, or a white ghost outline remains.

   The backdrop is appended to <body>, so it falls inside .aderis and is reachable. Bootstrap
   gives it `background-color: #000` plus `.show { opacity: .5 }`; --aderis-scrim already
   carries its own alpha, so the opacity is forced to 1 to avoid multiplying the two.       */
/* Mirrors Dialogs/Modal.razor.css. Note the header/footer seams use the WEAK --aderis-border
   while the dialog's outer edge uses --aderis-border-strong -- the opposite emphasis from
   cards, and it is the reference's choice, not an inconsistency to normalise.

   Deliberately NOT mirrored: the reference's .aderis-modal-body sets `color:
   --aderis-text-muted`. Bootstrap's .modal-body holds arbitrary content -- forms, tables,
   labels -- and muting all of it would wash out form text across 76 templates. Surfaces are
   mirrored; body text stays at full strength. */
.aderis .modal-content {
  background-color: var(--aderis-dialog, #141414);
  border: 1px solid var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  border-radius: 20px;
  box-shadow: 0 18px 36px rgba(var(--aderis-shadow-rgb, 0, 0, 0), 0.5);
  color: var(--aderis-text, #f5f5f5);
}

/* Same reasoning as .card above: explicit inner radii instead of overflow: hidden, so a
   select or datepicker inside a dialog is not clipped by the dialog's own rounding.
   19px = the 20px radius minus the 1px border. */
.aderis .modal-header:first-child {
  border-top-left-radius: 19px;
  border-top-right-radius: 19px;
}

.aderis .modal-footer:last-child {
  border-bottom-left-radius: 19px;
  border-bottom-right-radius: 19px;
}

.aderis .modal-header {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.04);
  border-bottom-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
}

.aderis .modal-footer {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.02);
  border-top-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
}

.aderis .modal-title {
  color: var(--aderis-text, #f5f5f5);
  font-size: 1rem;
  font-weight: 600;
}

/* Muted until hovered, matching .aderis-modal-close. Bootstrap's .close is `color: #000`
   with `text-shadow: 0 1px 0 #fff` and `opacity: .5` -- on a dark dialog that is an
   invisible dismiss button with a white ghost outline, and it appears in 56 templates.
   The text-shadow must go too, or the ghost outline survives the colour change. */
.aderis .close {
  color: var(--aderis-text-muted, #b3b3b3);
  opacity: 1;
  text-shadow: none;
}

/* Bootstrap's rule is .close:not(:disabled):not(.disabled):hover at (0,4,0) -- there is no
   plain .close:hover in 4.1.3. Matching its shape puts us at (0,5,0) and wins. Do not
   simplify these pseudo-classes away: without them Bootstrap reasserts #000 and the dismiss
   button goes invisible on a dark dialog. */
.aderis .close:not(:disabled):not(.disabled):hover,
.aderis .close:not(:disabled):not(.disabled):focus {
  color: var(--aderis-text, #f5f5f5);
  opacity: 1;
}

/* --aderis-scrim already carries its own alpha, so opacity is forced to 1 rather than
   multiplying it by Bootstrap's `.modal-backdrop.show { opacity: .5 }`. blur(3px) is the
   reference's; it degrades to a plain scrim where backdrop-filter is unsupported. */
.aderis .modal-backdrop,
.aderis .modal-backdrop.show {
  backdrop-filter: blur(3px);
  background-color: var(--aderis-scrim, rgba(0, 0, 0, 0.72));
  opacity: 1;
}

/* ── Dropdowns ────────────────────────────────────────────────────────────────
   Reaches the shared navbar's Reports / Config / System menus as well as page-level
   dropdowns, so this is the most-seen rule in the file. .dropdown-submenu (navbar.partial's
   own nested-menu pattern) inherits from .dropdown-menu and needs nothing extra.          */
/* Mirrors the menu panel and option rows in Dropdown/AderisDropdownShell.razor.css: 10px
   radius, strong border, 0.3rem of inner padding so rows do not touch the panel edge, and a
   drop shadow. Row hover is 0.07, noticeably stronger than the 0.04 used for table striping
   -- a menu row needs to read as actively targeted, not merely banded. */
.aderis .dropdown-menu {
  background-color: var(--aderis-card, #141414);
  border: 1px solid var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  border-radius: 10px;
  box-shadow: 0 8px 28px rgba(var(--aderis-shadow-rgb, 0, 0, 0), 0.5);
  padding: 0.3rem;
}

.aderis .dropdown-item {
  border-radius: 6px;
  color: var(--aderis-text, #f5f5f5);
}

.aderis .dropdown-item:hover,
.aderis .dropdown-item:focus {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.07);
  color: var(--aderis-text, #f5f5f5);
}

.aderis .dropdown-divider {
  border-top-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
}

/* ── List groups ──────────────────────────────────────────────────────────── */
.aderis .list-group-item {
  background-color: var(--aderis-card, #141414);
  border-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
  color: var(--aderis-text, #f5f5f5);
}

/* Bootstrap's `.list-group-item.active { background-color: #007bff }` is (0,2,0) -- the SAME
   specificity as the rule above, and ours is later in paint order, so without this the
   selected row is silently repainted --aderis-card and becomes indistinguishable from every
   unselected row. Losing the highlight is worse than the wrong colour: the list stops showing
   what is selected at all. (0,3,0) here so the highlight survives.

   A 16% primary tint rather than the solid fill: this is a selection band behind arbitrary row
   content, not a button, so it keeps the light body ink (13.52:1) and gains the strong border
   to mark the edge. */
.aderis .list-group-item.active {
  background-color: rgba(var(--aderis-primary-rgb, 0, 177, 118), 0.16);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  color: var(--aderis-text, #f5f5f5);
}

/* .list-group-item-action is the interactive variant (an <a> or <button> row). Bootstrap gives
   it `color: #495057` and a #f8f9fa hover, both light-theme values. 0.07 is the same hover step
   .dropdown-item uses -- these are the same gesture on the same kind of target, so they should
   not disagree. */
.aderis .list-group-item-action {
  color: var(--aderis-text, #f5f5f5);
}

.aderis .list-group-item-action:hover,
.aderis .list-group-item-action:focus {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.07);
  color: var(--aderis-text, #f5f5f5);
}

/* ── Nav tabs ─────────────────────────────────────────────────────────────────
   `.nav-tabs .nav-link.active { color:#495057; background-color:#fff; border-color:#dee2e6
   #dee2e6 #fff }` is (0,3,0), so the active tab renders as a white pill sitting on a
   near-black page -- the single most conspicuous light-theme leak left in the navigation.
   (0,4,0) here to take it.

   The bottom border deliberately matches the card background rather than being removed:
   that is how Bootstrap makes the active tab read as JOINED to the panel below it, by painting
   over the container's own bottom rule. Setting it to `transparent` instead would let the
   container's line show through and the tab would look detached. */
.aderis .nav-tabs {
  border-bottom-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
}

.aderis .nav-tabs .nav-link {
  color: var(--aderis-text-muted, #b3b3b3);
}

.aderis .nav-tabs .nav-link:hover,
.aderis .nav-tabs .nav-link:focus {
  border-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
  color: var(--aderis-text, #f5f5f5);
}

.aderis .nav-tabs .nav-item.show .nav-link,
.aderis .nav-tabs .nav-link.active {
  background-color: var(--aderis-card, #141414);
  border-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18))
    var(--aderis-border-strong, rgba(255, 255, 255, 0.18))
    var(--aderis-card, #141414);
  color: var(--aderis-text, #f5f5f5);
}

/* ── Popovers ─────────────────────────────────────────────────────────────────
   Two separate failures, both bad. `.popover-header { color: inherit;
   background-color: #f7f7f7 }` inherits our light body ink onto a near-white strip: 1.02:1,
   an effectively blank title bar. And popover `data-content` in this app carries
   `<table class='table'>` markup, which our own `.aderis .table` rule paints with light ink --
   on Bootstrap's white `.popover` background that is 1.09:1. The second one is self-inflicted,
   which is why the panel has to be converted rather than left alone.

   popover-header and popover-body both set `color` EXPLICITLY. Leaving the header at `inherit`
   is exactly the bug above: it would resolve against whatever ink the trigger's context
   happens to carry, which on a light-inked ancestor puts dark text back on the dark panel. */
.aderis .popover {
  background-color: var(--aderis-card, #141414);
  border: 1px solid var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
  border-radius: 10px;
  box-shadow: 0 8px 28px rgba(var(--aderis-shadow-rgb, 0, 0, 0), 0.5);
  color: var(--aderis-text, #f5f5f5);
}

.aderis .popover-header {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.04);
  border-bottom-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
  color: var(--aderis-text, #f5f5f5);
}

.aderis .popover-body {
  color: var(--aderis-text, #f5f5f5);
}

/* The bottom-placed popover draws a second seam UNDER the arrow with
   `.bs-popover-bottom .popover-header::before { border-bottom: 1px solid #f7f7f7 }` -- the old
   header colour, which would show as a white hairline across the notch. */
.aderis .bs-popover-auto[x-placement^="bottom"] .popover-header::before,
.aderis .bs-popover-bottom .popover-header::before {
  border-bottom-color: var(--aderis-border, rgba(255, 255, 255, 0.08));
}

/* The arrow is TWO stacked triangles: ::before is the border and ::after the fill, offset 1px.
   Bootstrap hardcodes `border-<side>-color: #fff` on the fill and rgba(0,0,0,.25) on the
   border, per placement. Miss these and a white spike stays pinned to an otherwise dark panel.
   Both the explicit .bs-popover-<side> classes and the [x-placement^=...] attribute forms are
   needed: Popper.js sets the attribute form for `placement: auto`, which is Bootstrap's
   default, so the class-only version would leave auto-placed popovers unfixed. */
.aderis .bs-popover-auto[x-placement^="top"] .arrow::before,
.aderis .bs-popover-top .arrow::before {
  border-top-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
}

.aderis .bs-popover-auto[x-placement^="top"] .arrow::after,
.aderis .bs-popover-top .arrow::after {
  border-top-color: var(--aderis-card, #141414);
}

.aderis .bs-popover-auto[x-placement^="right"] .arrow::before,
.aderis .bs-popover-right .arrow::before {
  border-right-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
}

.aderis .bs-popover-auto[x-placement^="right"] .arrow::after,
.aderis .bs-popover-right .arrow::after {
  border-right-color: var(--aderis-card, #141414);
}

.aderis .bs-popover-auto[x-placement^="bottom"] .arrow::before,
.aderis .bs-popover-bottom .arrow::before {
  border-bottom-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
}

.aderis .bs-popover-auto[x-placement^="bottom"] .arrow::after,
.aderis .bs-popover-bottom .arrow::after {
  border-bottom-color: var(--aderis-card, #141414);
}

.aderis .bs-popover-auto[x-placement^="left"] .arrow::before,
.aderis .bs-popover-left .arrow::before {
  border-left-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
}

.aderis .bs-popover-auto[x-placement^="left"] .arrow::after,
.aderis .bs-popover-left .arrow::after {
  border-left-color: var(--aderis-card, #141414);
}

/* ── Page subnav ──────────────────────────────────────────────────────────────
   The ~40px title band under the navbar. One shared construct --
   `div.d-flex.acu-subnav > div.acu-subnav-header` plus links from
   templates/subnav/_macros.html -- emitted by 10 partials in templates/subnav/ and rendered
   by about 44 templates. It is an Acuity-custom class, not a Bootstrap component, which is
   why the original restyle scope missed it: mypv_config_style.css:31 paints it #f7f7f7 and
   it then took our light body ink, leaving an effectively blank title bar.

   COLOUR ONLY. margin-top / min-height / padding / text-decoration stay in the two legacy
   copies (mypv_config_style.css:27-58 and the inline block in templates/base.html) -- those
   are the sole source of this band's geometry, and base.html's copy is load-bearing for the
   two pages that render a subnav from it. Only border-bottom-COLOR is set, leaving width and
   style to the legacy shorthand, the same way .aderis .nav-tabs and .aderis hr are written.

   --aderis-card, not --aderis-ash or --aderis-surface-neutral. On dark, lighter reads as
   nearer, and the navbar above is #1b1f22 (acuity.css's .bg-darkest-blue !important,
   deliberately untouched) while the page below is #030303. A band sitting UNDER the navbar's
   orange stripe is subordinate to it and must therefore be darker. Relative to the navbar's
   luminance: card 0.53x (subordinate, correct), surface-neutral 0.98x (merges into the
   navbar), ash 1.46x and the old #353841 2.99x (both read as MORE elevated than their own
   parent chrome). Card is also what .aderis .nav-tabs .nav-link.active uses, and this band is
   functionally a tab strip.

   THE ACTIVE CLASS IS `mypv_subnav_link_active`, NOT `active`.
   static/js/templates/templates.js:20 adds it to `#dt_<segment>_subnav`, which is exactly the
   id templates/subnav/_macros.html:3 generates. Nothing in the app ever adds `active` here;
   `.active` is carried below only because the declaration being replaced names it. The third
   selector part reaches the legacy .mypv_subnav bars (alarms_oem, data_download,
   revenue_models, plus two orphan partials), which share that same `color: black` and
   otherwise render black text directly on #030303.

   SPECIFICITY. mypv_config_style.css loads AFTER this file in every base that links it, and
   base.html's <style> is later still, so a tie LOSES -- each rule must strictly out-specify:
     .aderis .acu-subnav                        (0,2,0) beats .acu-subnav              (0,1,0)
     .aderis .acu-subnav-header                 (0,2,0) beats .acu-subnav-header        (0,1,0)
                                                    and .aderis a                      (0,1,1)
     .aderis .acu-subnav-link                   (0,2,0) beats .acu-subnav-link          (0,1,0)
     .aderis .acu-subnav-link:hover             (0,3,0) beats a:hover                   (0,1,1)
     .aderis .acu-subnav .acu-subnav-link
        .mypv_subnav_link_active                (0,4,0) beats .mypv_subnav_link_active  (0,1,0)
                                                    and .acu-subnav-link.active         (0,2,0)
                                                    and the :hover rule above           (0,3,0)
     .aderis .mypv_subnav_link_active           (0,2,0) beats .mypv_subnav_link_active  (0,1,0)
   The (0,4,0) form clears hover on SPECIFICITY rather than source order, so the current page
   stays marked while the pointer is over it.

   Contrast on #141414: header 16.90:1, links 8.79:1, hover 16.90:1, active green 6.62:1
   (passes AA at every size; misses AAA-normal by 0.38). Replaces measured failures of 1.02:1,
   1.42:1, 1.79:1 and 1.02:1.                                                              */
.aderis .acu-subnav {
  background-color: var(--aderis-card, #141414);
  border-bottom-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
}

.aderis .acu-subnav-header {
  color: var(--aderis-text, #f5f5f5);
}

.aderis .acu-subnav-link {
  color: var(--aderis-text-muted, #b3b3b3);
}

.aderis .acu-subnav-link:hover,
.aderis .acu-subnav-link:focus {
  color: var(--aderis-text, #f5f5f5);
}

.aderis .acu-subnav .acu-subnav-link.active,
.aderis .acu-subnav .acu-subnav-link.mypv_subnav_link_active,
.aderis .mypv_subnav_link_active {
  color: var(--aderis-primary, #00b176);
}

/* ── Legacy config-table headers ──────────────────────────────────────────────
   .table_header / .table_header_border are Acuity-custom classes on the section and column
   header rows of ~29 config templates -- the device info partials, users, alarms, KPI config,
   and every protocol map list. mypv_config_style.css:125 paints the border variant
   `background: white; color: black` at (0,1,0), and device_info_partial.html's non-portable
   branch repeated the same inline: on the dark card that is the loudest element on any config
   page, and the "super-white header" seen all over the site. (0,2,0) here wins both.

   Card-header tint and full-strength ink: these bands hold section titles, the same job
   .card-header does, so they take the same surface. Column headers carrying these classes
   inside a <thead> still take the muted ink from `.aderis .table thead th` (0,3,1), which
   out-specifies this rule -- section bands full, column labels muted, no extra rule needed.

   border-bottom-COLOR only, the .nav-tabs idiom: the 2px width and style stay in
   mypv_config_style.css's shorthand. Its !important flags came off in the same change --
   they existed to beat Bootstrap's `.table th` border-top at (0,1,1), which our
   `.aderis .table th` border-top-color already re-inks dark, and with them in place this
   rule could never take the seam. */
.aderis .table_header,
.aderis .table_header_border {
  background-color: var(--aderis-card-header, rgba(255, 255, 255, 0.06));
  color: var(--aderis-text, #f5f5f5);
}

.aderis .table_header_border {
  border-bottom-color: var(--aderis-border-strong, rgba(255, 255, 255, 0.18));
}

/* mypv_config_style.css also carries `.live_data_td { color: black }` -- the live values on
   the device partial's Status and Data tab, invisible on the dark card -- and
   `.mypv-partial-settings-spacer-row { background: white }`, the blank divider rows between
   the partial's sections, which rendered as white bands. The spacer takes the opaque card
   colour rather than transparent so table striping cannot band the gap. */
.aderis .live_data_td {
  color: var(--aderis-text, #f5f5f5);
}

.aderis .mypv-partial-settings-spacer-row {
  background-color: var(--aderis-card, #141414);
}

/* ── jsTree (Devices tree, OPCUA node pickers) ────────────────────────────────
   The tree ships its own theme (static/jsTree/themes/default/style.min.css) linked INSIDE the
   page body, i.e. after this file, so a tie loses on paint order and every rule here must
   strictly out-specify. Its interaction states are light-theme washes at (0,2,0):
     .jstree-default .jstree-hovered { background: #e7f4f9; box-shadow: inset 0 0 1px #ccc }
     .jstree-default .jstree-clicked { background: #beebff; box-shadow: inset 0 0 1px #999 }
   Under the page's light node text, hovering greys a node out and SELECTING one erases it --
   white on #beebff is 1.26:1. (0,3,0) here takes both.

   Selection is the .list-group-item.active recipe (16% primary tint) -- it is the same
   semantic, a selection band behind row content, so it gets the same look. Hover is the
   .dropdown-item step (0.07): a row-target on a panel. The inset grey hairline goes entirely;
   the tint is the affordance, and a light hairline on dark reads as dirt.

   Clicked is written AFTER hovered deliberately: jsTree applies both classes to a selected
   node under the pointer, both rules are (0,3,0), and source order is what keeps that node
   selected-green instead of flipping to the hover neutral -- the dashboard layout chips make
   the same move. */
.aderis .jstree-default .jstree-hovered {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.07);
  box-shadow: none;
}

.aderis .jstree-default .jstree-clicked {
  background-color: rgba(var(--aderis-primary-rgb, 0, 177, 118), 0.16);
  box-shadow: none;
}

/* ── Modbus map call-group and orphan bands ──────────────────────────────────
   These two rows were `<tr style="background: #cbcdd2;">` and `<tr style="background:
   #ffecb6;">` -- inline, so (1,0,0,0) and unreachable from here without !important, which this
   file forbids. Against `.aderis .table`'s near-white ink that measured about 1.5:1 and 1.1:1,
   i.e. the band text was effectively gone. The attributes are now the two classes below.

   The call-group band borrows the card-header wash: it is the same semantic, a grouping header
   inside a panel. The orphan band keeps a caution reading, because "Orphaned Tags" IS the
   warning -- lemon at 12% carries that without competing with a real alert. Both are tints of
   tokens rather than opaque fills, so the row keeps the table's own surface underneath. */
.aderis .modbus-call-group-row {
  background-color: rgba(var(--aderis-on-surface-rgb, 255, 255, 255), 0.06);
}

.aderis .modbus-orphan-row {
  background-color: rgba(var(--aderis-lemon-rgb, 255, 225, 77), 0.12);
}
