:root {
  --dash-bg: #ffffff;
  --dash-border: #dddddd;
  --dash-text: #1a1a1a;
  --dash-muted: #6b7280;
  --dash-hover-border: #999999;
  --dash-note-bg: #fdf6d8;
  --dash-note-border: #e6d98a;
  --dash-note-focus: #d8c65a;
  --dash-note-text: #3a3420;
}

[data-bs-theme="dark"] {
  --dash-bg: #23262b;
  --dash-border: #3a3f47;
  --dash-text: #e8e8e8;
  --dash-muted: #9aa0a6;
  --dash-hover-border: #6b7280;
  --dash-note-bg: #3a341a;
  --dash-note-border: #7a6a2a;
  --dash-note-focus: #b8a23a;
  --dash-note-text: #f1e9c8;
}

.dashboard-grid {
  display: grid;
  /* Matches blueprints.yaml's `grid_columns` default (12). On this
     plugin's own /dashboard page, dashboard.html.twig renders an inline
     `grid-template-columns` computed from the actual configured value,
     which overrides this regardless of source order - so this 12 is only
     a fallback there. It's NOT a fallback everywhere else `.dashboard-grid`
     is reused, though: the adminlte theme's settings.html.twig/
     signup.html.twig/forgot.html.twig build their own card layouts
     directly on this same class, entirely outside this plugin's Twig
     template, so for them this rule is the only thing setting the column
     count - see .dashboard-widget--xs etc.'s own comment below for the
     matching situation on widget spans. */
  grid-template-columns: repeat(12, 1fr);
  gap: 1rem;
  margin-top: 1rem;
}

.dashboard-widget {
  /* Fallback only - a widget with a real size class (the normal case,
     see .dashboard-widget--xs etc. below) overrides this immediately;
     matches --xs's own span so an unclassed widget still looks
     reasonable rather than a sliver. On this plugin's own /dashboard
     page, an inline `grid-column` style (see .dashboard-widget--xs
     etc.'s own comment below) additionally overrides *that*, so an
     admin-reconfigured size_presets value still wins there. */
  grid-column: span 2;
  border: 1px solid var(--dash-border);
  border-radius: 8px;
  padding: 1rem;
  background: var(--dash-bg);
  color: var(--dash-text);
  cursor: grab;
  /* .dashboard-grid's items stretch to the tallest sibling in their row
     (CSS Grid's default align-items:stretch), so this card's own box
     does get taller than its content on a short row - but without being
     a flex column itself, .dashboard-widget__body (its only sizeable
     child) had no way to grow into that extra height, leaving unused
     space below any widget shorter than its row's tallest neighbor.
     Flexing it here, plus .dashboard-widget__body below, is what lets a
     content-driven-height widget (like a chart) actually fill it. */
  display: flex;
  flex-direction: column;
  /* A grid item's default min-width is `auto`, which resolves to its
     content's min-content size, not 0 - so a widget with something wide
     and non-wrapping inside (a data table with nowrap cells, e.g.
     sensor-widgets' Log/Device Inspector) was forcing this item's own
     GRID TRACK to grow past its `1fr` share to fit that content,
     dragging every sibling column wider right along with it (the exact
     "one widget's table stretches the whole row" bug). min-width:0 lets
     the item actually shrink to its track's real size, so overflow stays
     internal to whatever the widget itself scrolls (see e.g.
     .sw-log__table-wrap/.sw-inspector__table-wrap in sensor-widgets.css,
     which need the same fix one level further in) instead of blowing out
     the grid. Same fix already applied to .dashboard-widget__title below
     for the same reason, on a different axis (text truncation). */
  min-width: 0;
}

/* These six rules match blueprints.yaml's own `size_presets` DEFAULT
   values (2/3/4/6/8/12 out of a 12-column grid) - kept here as a fallback
   for two different situations, not just one: (1) the adminlte theme's
   settings.html.twig/signup.html.twig/forgot.html.twig reuse these exact
   .dashboard-widget--<key> classes directly for their own, unrelated
   card layouts, entirely outside this plugin's own /dashboard page and
   its Twig template - they never get an inline `grid-column` style at
   all, so for them these rules are the ONLY thing setting a span, not a
   fallback; (2) on this plugin's own /dashboard page, an inline
   `grid-column: span N` computed from the *actual* configured
   size_presets (see dashboard.html.twig / user-dashboard.php's
   sizePresets()) is rendered per widget and overrides these regardless
   of source order (inline beats a class selector), so an admin who
   reconfigures size_presets away from these defaults only affects that
   page, not the theme's other card layouts above. If you ever change
   size_presets' own default `columns` values, update the numbers below
   to match, or the theme's other pages will silently disagree with this
   plugin's own dashboard. */
.dashboard-widget--xs  { grid-column: span 2; }
.dashboard-widget--sm  { grid-column: span 3; }
.dashboard-widget--md  { grid-column: span 4; }
.dashboard-widget--lg  { grid-column: span 6; }
.dashboard-widget--xl  { grid-column: span 8; }
.dashboard-widget--xxl { grid-column: span 12; }

/* Collapsed widgets (see the 'collapse' data-action and 'collapsible' on
   a widget's catalog entry) hide their body and shrink to just their
   header - align-self:start opts this one item out of the grid's default
   row-stretch (see .dashboard-widget's own comment above) so it doesn't
   stay artificially tall just because a taller, non-collapsed sibling
   shares its row. */
.dashboard-widget--collapsed {
  align-self: start;
}

.dashboard-widget--collapsed .dashboard-widget__body {
  display: none;
}

.dashboard-widget__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: .5rem;
  font-weight: 600;
  color: var(--dash-text);
}

.dashboard-widget__title {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  /* Lets a widget truncate its own title text (e.g. with an ellipsis on
     a long value) instead of forcing the header to wrap/overflow - a
     flex item's default min-width:auto blocks shrinking below its
     content's natural size, which would otherwise defeat any
     text-overflow rule a widget sets on a child of this element. */
  min-width: 0;
  overflow: hidden;
}

/* Signals that clicking the title itself (not just the chevron button)
   toggles collapse too - see the delegated click handler in
   dashboard.js. :has() scopes this to a header that actually has a
   collapse button at all, i.e. a widget whose catalog entry set
   `collapsible: true` - a non-collapsible widget's title stays
   non-interactive. */
.dashboard-widget__header:has(button[data-action="collapse"]) .dashboard-widget__title {
  cursor: pointer;
}

.dashboard-widget__controls { display: flex; gap: .4rem; flex-shrink: 0; }

/* A compact_controls widget's own settings-toggle button (and, for
   sensor-widgets' multi-instance widgets, the follow-indicator icon
   right before it - see .sw-widget__follow-indicator's own comment in
   sensor-widgets.css) gets physically moved in here on init (see
   README section 13 / SensorWidgets.moveIntoHeaderSlot()). Needs its
   OWN `display: flex` - this plain `<span>` has none of its own
   otherwise, so two moved-in children (both `display: flex` themselves,
   which blockifies each to its own full-width box) would stack
   vertically instead of sitting side by side the way Hide/the rest of
   .dashboard-widget__controls already does; row/center/gap here keeps
   any number of moved-in children inline regardless of their own
   individual display value. */
.dashboard-widget__widget-slot {
  display: flex;
  align-items: center;
  gap: .4rem;
}

/* The one child that should actually grow into whatever extra height
   .dashboard-widget picked up from row-stretch (see its own comment
   above) - min-height:0 is needed alongside flex:1 1 auto for a widget
   with its own internal flex/percentage-height chain (e.g. sensor-widgets'
   chart) to correctly measure a real, non-auto height to fill instead of
   collapsing to content size. min-width:0 is this same rule's
   cross-axis twin (.dashboard-widget is a COLUMN flex container, so
   width is body's cross axis, still governed by the same flex-item
   auto-minimum-size default) - without it, a wide table several levels
   further down this widget's own markup could stop this item shrinking
   to the card's actual width, same failure mode as .dashboard-widget's
   own min-width:0 above but one level in. */
.dashboard-widget__body {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
}

.dashboard-widget__controls button {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  opacity: .7;
  padding: .2rem .3rem;
  color: var(--dash-text);
}
.dashboard-widget__controls button:hover { opacity: 1; }

/* Per-widget size dropdown - direct-pick rather than the old single
   button that cycled one size per click (see dashboard.js's
   size-toggle/size-select handling). `.dashboard-size` is the
   positioning anchor sitting inline among the header's other control
   buttons; `position: relative` lets its own menu (absolutely
   positioned) anchor to IT specifically rather than the whole card, and
   `display: inline-flex` keeps it from stretching to fill the row the
   way a bare inline element sized by its toggle button's own box model
   quirks sometimes does. Reused as-is (same classes, same JS) by
   sensor-widgets' own multi-instance widgets, which render this inside
   their own settings panel instead of this generic header - see their
   own templates' "Widget" field. */
.dashboard-size {
  position: relative;
  display: inline-flex;
}

/* The toggle button's own look - a standalone class (not scoped under
   .dashboard-widget__controls the way this used to be written) so it
   renders identically wherever `.dashboard-size` appears, including
   inside sensor-widgets' own settings panels (a completely different
   ancestor, `.sw-*__widget-controls`) - that ancestor-scoping gap is
   why the multi-instance widgets' own toggle used to fall back to a
   plain Bootstrap .btn.btn-outline-secondary look instead of matching
   every other widget's size button; see those templates' own comments
   for why they carry this class directly on the button now instead.
   `button.` in the selector (not just `.dashboard-size__toggle`) is
   needed specifically for the GENERIC header case: `.dashboard-widget__controls
   button`'s own font-size/padding/border:none (further up this file)
   has the higher class+type specificity of the two, so without this a
   plain `.dashboard-size__toggle` rule would silently lose there - same
   fix, same reasoning as `button.dashboard-icon-btn` earlier in this
   file for the toolbar's own icon buttons. Inside sensor-widgets'
   settings panels there's no such competing rule at all, which is
   exactly why that context "worked" even before this fix and the
   generic header didn't - confirmed by hand, not just reasoned about. */
button.dashboard-size__toggle {
  display: inline-flex;
  align-items: center;
  background: none;
  cursor: pointer;
  line-height: 1;
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  border: 1px solid var(--dash-border);
  border-radius: 4px;
  padding: .15rem .4rem;
  color: var(--dash-text);
  opacity: .7;
}
button.dashboard-size__toggle:hover { opacity: 1; }

.dashboard-size__menu {
  position: absolute;
  top: calc(100% + .3rem);
  /* Right-aligned, opening leftward - same reasoning as the toolbar's
     own dropdowns (.dashboard-add__menu): the size control usually sits
     toward the right end of a card's header controls row, so opening
     rightward risks running past the viewport or even the dashboard
     grid's own right edge on a narrow card. */
  right: 0;
  min-width: 90px;
  background: var(--dash-bg);
  border: 1px solid var(--dash-border);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .15);
  padding: .3rem;
  display: flex;
  flex-direction: column;
  gap: .15rem;
  z-index: 20;
}
.dashboard-size__menu[hidden] { display: none; }

/* `button.` on every rule below (not just `.dashboard-size__item`/
   `--active`) - same reason as `button.dashboard-size__toggle` above:
   in the generic header, these ARE `.dashboard-widget__controls
   button`, so without the type qualifier that ancestor rule's own
   color/opacity (higher class+type specificity) would silently win
   over these, exactly the way the toggle button's own style did before
   that fix - confirmed by hand, this menu's items sit inside
   `.dashboard-widget__controls` there the same way the toggle does.
   Inside sensor-widgets' own settings panels there's no such ancestor
   at all, so this bug (before the fix) only ever showed up in the
   generic header specifically, not there. */
button.dashboard-size__item {
  border: none;
  background: none;
  color: var(--dash-text);
  text-align: left;
  padding: .3rem .5rem;
  border-radius: 4px;
  cursor: pointer;
  font-size: .8rem;
  text-transform: uppercase;
  font-weight: 600;
}
button.dashboard-size__item:hover { background: var(--dash-border); }

/* The size currently applied to this widget - the theme's own Bootstrap
   primary color (not a checkmark glyph, this menu has no room budgeted
   for one at this scale), both as the text color and a light background
   tint, so it reads as "you are here" clearly enough to survive at this
   small a font size regardless of exact hue - a color-only difference
   from the plain hover state above wouldn't. Explicit fallback values
   (not `inherit`, which would make the marking silently invisible
   against the item's own already-similar text color if either variable
   were ever unavailable for some reason) rather than trusting
   adminlte.css to always expose both - confirmed present there as of
   this writing, but this is the one place in this file a color falls
   back to a literal hardcoded value instead of one of dashboard.css's
   own --dash-* tokens, precisely because it needs to track the site
   theme's actual primary color, not this plugin's own dark-mode-only
   palette. */
button.dashboard-size__item--active {
  color: var(--bs-primary, #0d6efd);
  background: var(--bs-primary-bg-subtle, transparent);
  font-weight: 700;
}
button.dashboard-size__item--active:hover {
  background: var(--bs-primary-bg-subtle, var(--dash-border));
}

.dashboard-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  color: var(--dash-text);
}

.dashboard-toolbar__actions {
  display: flex;
  align-items: center;
  gap: .5rem;
}

.dashboard-toolbar h1 {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .6rem;
}

/* Which preset (if any) the current layout is based on - see
   activePresetInfo() in user-dashboard.php. Small (reads as a subtitle
   next to the page title, not a second headline) but full-brightness
   text by default - a preset that's an exact, currently-loaded match is
   worth reading clearly, not treated like disabled/secondary chrome. */
.dashboard-active-preset {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  font-size: .8rem;
  font-weight: 500;
  color: var(--dash-text);
}
.dashboard-active-preset i { font-size: .85rem; }

/* Once the user has changed anything since loading it (see
   dashboard.preset_dirty), the name itself greys out - this is the one
   state that actually gets the muted/dimmed treatment, plus an explicit
   "(modified)"-style suffix since color alone isn't a reliable enough
   signal on its own. */
.dashboard-active-preset--modified {
  color: var(--dash-muted);
  opacity: .7;
}
.dashboard-active-preset__modified-mark {
  font-style: italic;
}

.dashboard-toolbar button {
  border: 1px solid var(--dash-border);
  background: var(--dash-bg);
  color: var(--dash-text);
  /* Pill, matching the rest of the site's button language (adminlte theme's
     custom.css - .btn-primary and the outline/danger family both round to
     999px). This plugin's toolbar buttons are hand-rolled, not Bootstrap
     .btn, so they don't inherit that automatically - kept in sync by hand. */
  border-radius: 999px;
  padding: .4rem .9rem;
  cursor: pointer;
  font-size: .9rem;
}
.dashboard-toolbar button:hover { border-color: var(--dash-hover-border); }

/* Every top-level toolbar control (Add / Load preset / Save / Reset) is a
   compact icon+caption button - smaller/tighter than a normal
   .dashboard-toolbar button (see dashboard.html.twig) so the bar stays a
   fixed 4 buttons instead of growing by a whole button per
   save-permission a user holds, the way it used to. `button.` in the
   selector (not just `.dashboard-icon-btn`) matches .dashboard-toolbar
   button's own specificity (class+type vs class+type) so this - the
   later rule - reliably wins the padding/font-size it overrides, instead
   of the two rules fighting on source order alone. */
button.dashboard-icon-btn {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  padding: .4rem .8rem .4rem .65rem;
  font-size: .75rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}
.dashboard-icon-btn i { font-size: 1rem; }

.dashboard-add { position: relative; }

.dashboard-add__menu {
  position: absolute;
  top: calc(100% + .4rem);
  /* Right-aligned to its toggle button, opening leftward - all three of
     these dropdowns (Add / Load preset / Save) live at the right-hand
     end of the toolbar (.dashboard-toolbar's own space-between pushes
     .dashboard-toolbar__actions there), so opening rightward (the old
     `left: 0`) routinely ran the menu off the right edge of the
     viewport. */
  right: 0;
  /* Wide enough that a longer row (e.g. "Als eigene Vorlage speichern"
     plus its "Premium" badge, see --item--locked/.dashboard-preset__badge
     below) fits on one or two lines instead of wrapping to three -
     bumped up from an original 180px once the Save dropdown's rows
     started routinely needing more room than the shorter Add-widget/
     Load-preset rows ever did. */
  min-width: 260px;
  background: var(--dash-bg);
  border: 1px solid var(--dash-border);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .15);
  padding: .4rem;
  display: flex;
  flex-direction: column;
  gap: .2rem;
  z-index: 20;
}
.dashboard-add__menu[hidden] { display: none; }

.dashboard-add__item {
  border: none;
  background: none;
  color: var(--dash-text);
  text-align: left;
  padding: .4rem .6rem;
  border-radius: 4px;
  cursor: pointer;
  font-size: .9rem;
  display: flex;
  align-items: center;
  gap: .5rem;
}
.dashboard-add__item:hover { background: var(--dash-border); }

/* Admin-only marking (see CLAUDE.md), moved here from the old standalone
   "Save as default"/"Save as system preset" toolbar buttons now that
   they're rows in the merged Save dropdown - same var(--bs-danger) as
   every other admin-only control in this Bootstrap 5 theme, just text
   color (no border to color on a menu row). Only applied while the
   viewer actually holds the permission (see dashboard.html.twig) - a
   locked row gets --locked below instead, not this. */
.dashboard-add__item--danger { color: var(--bs-danger); }
.dashboard-add__item--danger:hover { background: var(--bs-danger-bg-subtle, var(--dash-border)); }

/* A Save-menu row the viewer lacks the permission for - `disabled`
   already makes it inert (no click, no hover background from the rule
   above matters since :hover can't apply). Muted color alone read too
   close to the normal text color at a glance (var(--dash-muted) is only
   a little dimmer than var(--dash-text) in dark mode), so this also
   knocks down the whole row's opacity - a guaranteed-visible "grayed
   out" regardless of the exact color values, same as how a disabled
   menu item looks in most native UI. Applies to the row's "Premium"
   badge too, which is fine/expected - the row being visibly disabled is
   the point, the badge doesn't need to out-pop that. */
.dashboard-add__item--locked {
  color: var(--dash-muted);
  cursor: not-allowed;
  opacity: .55;
}
.dashboard-add__item--locked:hover { background: none; }

/* A "My presets" row (see dashboard.html.twig) - unlike every other
   entry in either dropdown, this one is two separate buttons (load +
   delete) side by side rather than a single .dashboard-add__item
   covering the whole row, so the trash icon has its own click target
   that doesn't also trigger the load action underneath it. */
.dashboard-preset__row {
  display: flex;
  align-items: stretch;
  gap: 0;
}
.dashboard-preset__row-load {
  flex: 1;
  min-width: 0;
}

.dashboard-preset__delete {
  border: none;
  background: none;
  color: var(--dash-muted);
  cursor: pointer;
  padding: 0 .5rem;
  border-radius: 4px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  font-size: .85rem;
}
.dashboard-preset__delete:hover {
  color: var(--bs-danger);
  background: var(--bs-danger-bg-subtle, var(--dash-border));
}

.dashboard-add__empty {
  margin: 0;
  padding: .4rem .6rem;
  color: var(--dash-muted);
  font-size: .85rem;
}

/* "Load preset" dropdown - reuses .dashboard-add/.dashboard-add__menu/
   .dashboard-add__item above (see dashboard.html.twig), just adds a
   section heading and a small "System" marker of its own. */
.dashboard-preset__group-label {
  margin: .2rem .6rem 0;
  padding: 0;
  color: var(--dash-muted);
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .02em;
}
.dashboard-preset__group-label:first-child { margin-top: 0; }

.dashboard-preset__badge {
  display: inline-block;
  font-size: .7rem;
  text-transform: uppercase;
  border: 1px solid var(--dash-border);
  border-radius: 4px;
  padding: .05rem .35rem;
  color: var(--dash-muted);
}

/* Save-menu row the viewer lacks the permission for (see
   dashboard.html.twig's dashboard-add__item--locked rows) - same badge
   shape as --preset__badge above, amber/warning instead of neutral so it
   reads as "unlock this" rather than just "here's some metadata" like
   the System badge does. Trails the row's label text (unlike the System
   badge, which leads its preset name), so - unlike the base badge above -
   it gets pushed to the row's right edge via margin-left: auto on the
   flex row it sits in. */
.dashboard-preset__badge--premium {
  border-color: var(--bs-warning);
  color: var(--bs-warning);
  margin-left: auto;
}

@media (max-width: 700px) {
  /* !important on both: the grid and each widget now carry their "real"
     values as inline styles (grid_columns/size_presets, see the comments
     above) - an inline style otherwise beats any class selector
     regardless of source order, so without !important this breakpoint
     would stop collapsing to one column the moment those inline styles
     were introduced. */
  .dashboard-grid { grid-template-columns: 1fr !important; }
  .dashboard-widget { grid-column: span 1 !important; }

  /* The toolbar's 4 action buttons (Add / Load preset / Save / Reset,
     see dashboard.html.twig) are icon+caption pills at desktop width;
     4 of those plus the page title in one row don't fit a phone-width
     viewport. Rather than stacking them into separate full-width rows
     (tried first, but that wasted width and looked odd), just drop the
     caption and keep the icons side by side in the same row - the
     existing .dashboard-toolbar/.dashboard-toolbar__actions row layout
     is unchanged, shrinking each button down to icon size is what
     actually gets all 4 plus the title to fit. The caption's own
     `title` attribute is still there, so the label survives as a
     native tap-and-hold/hover tooltip rather than disappearing outright. */
  .dashboard-icon-btn__label { display: none; }
}

.dashboard-note {
  width: 100%;
  min-height: 120px;
  resize: vertical;
  border: 1px solid var(--dash-note-border);
  background: var(--dash-note-bg);
  color: var(--dash-note-text);
  border-radius: 6px;
  padding: .6rem;
  font-family: inherit;
  font-size: .9rem;
  box-sizing: border-box;
}
.dashboard-note::placeholder { color: var(--dash-muted); }
.dashboard-note:focus { outline: 2px solid var(--dash-note-focus); outline-offset: 1px; }

.dashboard-note__status {
  font-size: .75rem;
  color: var(--dash-muted);
  height: 1.2em;
  margin-top: .25rem;
  opacity: 0;
  transition: opacity .3s;
}
.dashboard-note__status.is-visible { opacity: 1; }

/* No positioning/opacity rules of its own - on init, dashboard.js
   physically moves this button out of the card body and into the shared
   .dashboard-widget__controls header row (see this widget's own
   compact_controls catalog entry and README section 13), where
   .dashboard-widget__controls button already styles it identically to
   the collapse/hide buttons next to it - same treatment sensor-widgets'
   own multi-instance widgets give their settings-toggle buttons. */
.dashboard-note__settings-toggle {
  display: flex;
  align-items: center;
}

/* .dashboard-note__settings is the outer Bootstrap `.modal` element
   itself (see this widget's own template) - no layout rules of its own,
   Bootstrap fully owns its display/position/backdrop toggling. Same
   shape as sensor-widgets' own *__settings-body/*__field/
   *__widget-controls - see e.g. the Chart widget's own CSS comment. */
.dashboard-note__settings-body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  text-align: left;
}
.dashboard-note__field {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.dashboard-note__field label {
  font-size: 0.75rem;
  color: var(--dash-muted);
}
.dashboard-note__widget-controls {
  display: flex;
  gap: 0.4rem;
}
.dashboard-note__add {
  align-self: flex-start;
}

.dashboard-info {
  white-space: pre-line;
  line-height: 1.5;
  color: var(--dash-text);
}

/* Same treatment as .dashboard-note__settings-toggle above - see that
   class's own comment. */
.dashboard-info__settings-toggle {
  display: flex;
  align-items: center;
}
.dashboard-info__settings-body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  text-align: left;
}
.dashboard-info__field {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.dashboard-info__field label {
  font-size: 0.75rem;
  color: var(--dash-muted);
}
.dashboard-info__widget-controls {
  display: flex;
  gap: 0.4rem;
}
