/*
 * Sensortech V2 — design tokens + components.
 * Lifted from claude/design_handoff_meter_overview/design/styles.css
 * (cool greys, brand blue, Inter / JetBrains Mono). The .st-* class names
 * are the canonical primitives. A short alias section at the bottom keeps
 * older class names (.card, .kpi, .tabs, .severity, .tag-chip) working
 * until every view has been migrated.
 */

@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap");

:root {
    --st-bg: #fafaf9;
    --st-card: #ffffff;
    --st-topbar: #dad7d1;
    --st-card-soft: #f3f2ee;
    --st-border: #e6e3dd;
    --st-border-strong: #dbd6ca;
    --st-text: #1f2630;
    --st-text-muted: #6b7480;
    --st-text-soft: #8a8f99;
    --st-accent: #2c5fb3;
    --st-accent-soft: #bad0ee;
    --st-accent-bg: #eaf1fb;
    --st-good: #1f7a4d;
    --st-good-bg: #e6f3ec;
    --st-warn: #9a7b00;
    --st-warn-bg: #fbf1c2;
    --st-bad: #b3261e;
    --st-bad-bg: #fbe5e3;
    /* Interaction shades + on-color text. Never invent a one-off hex —
       hover/active states and text-on-color all come from these. */
    --st-on-accent: #ffffff;
    --st-accent-hover: #234a8c;
    --st-accent-active: #244e94;
    --st-bad-active: #951e18;
    --st-accent-on-dark: #d6e4f7;
    /* Beige "paper" gradient stops (map placeholder card). */
    --st-paper: #f3efe6;
    --st-paper-deep: #ece6d6;
    /* Chart accent: hue-rotated equivalents of --st-accent / --st-accent-soft
       for paired series (e.g. humidity bars next to temperature bars). Same
       saturation + lightness as the corresponding blue tokens so they read
       at the same visual weight. */
    --st-warm: #b3782c;
    --st-cold: #3b6ea5;
    --st-warm-soft: #eed8b9;
    --st-shadow-sm: 0 1px 2px rgba(20, 25, 35, 0.04);
    --st-shadow-md:
        0 1px 3px rgba(20, 25, 35, 0.05), 0 8px 24px rgba(20, 25, 35, 0.04);
    --st-radius: 10px;
    --st-radius-lg: 14px;
    --st-font:
        "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui,
        sans-serif;
    --st-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
}

* {
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
    font-family: var(--st-font);
    font-size: 14px;
    line-height: 1.45;
    color: var(--st-text);
    background: var(--st-bg);
    -webkit-font-smoothing: antialiased;
}

a {
    color: var(--st-accent);
    text-decoration: none;
}
a:hover {
    text-decoration: underline;
}

code,
pre {
    font-family: var(--st-mono);
}
code {
    font-size: 0.92em;
    color: var(--st-text);
    background: var(--st-card-soft);
    padding: 1px 5px;
    border-radius: 3px;
}

h1 {
    font-size: 24px;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 0 0 4px;
}
h2 {
    font-size: 16px;
    font-weight: 600;
    margin: 24px 0 10px;
}
h3 {
    font-size: 13px;
    font-weight: 600;
    margin: 20px 0 8px;
    color: var(--st-text);
}

.st-mono {
    font-family: var(--st-mono);
}
.st-muted {
    color: var(--st-text-muted);
}
.st-soft {
    color: var(--st-text-soft);
}

/* ── Topbar ── */
.st-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 24px;
    background: var(--st-topbar);
    box-shadow: 0 1px 2px rgba(20, 25, 35, 0.06);
    position: sticky;
    top: 0;
    /* Above Leaflet's default control z-index (.leaflet-control is 800) so
     the dropdown menu doesn't get covered by map tiles/controls. */
    z-index: 1000;
}
.st-logo {
    display: flex;
    align-items: center;
    color: var(--st-text);
}
.st-logo:hover {
    text-decoration: none;
}
.st-logo-mark {
    height: 28px;
    width: auto;
    flex: 0 0 auto;
    display: block;
}
.st-topbar-right {
    display: flex;
    align-items: center;
    gap: 12px;
}
/* Topbar search — rounded pill (white card, soft border) with the
   search icon and the input inside it. position: relative also anchors
   the dropdown panel below. */
.st-search {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    border: 1px solid var(--st-border-strong);
    border-radius: 999px;
    background: var(--st-card);
    color: var(--st-text-muted);
    font-size: 13px;
    min-width: 220px;
    margin: 0;
}
.st-search input,
.st-search input[type="text"],
.st-search input[type="search"] {
    /* Strip the browser's native search-field decoration (inset rounded
       pill, built-in padding, cancel button) so the input sits flush
       inside our pill instead of double-bordering it. */
    -webkit-appearance: none;
    appearance: none;
    border: none;
    outline: none;
    background: transparent;
    box-shadow: none;
    font: inherit;
    color: var(--st-text);
    flex: 1;
    min-width: 0;
    padding: 0;
    margin: 0;
    border-radius: 0;
    height: auto;
}
.st-search input[type="search"]::-webkit-search-decoration,
.st-search input[type="search"]::-webkit-search-cancel-button,
.st-search input[type="search"]::-webkit-search-results-button,
.st-search input[type="search"]::-webkit-search-results-decoration {
    -webkit-appearance: none;
    appearance: none;
}
.st-search input:focus {
    box-shadow: none;
    outline: none;
}
.st-search input::placeholder {
    color: var(--st-text-soft);
}

/* Mobile search: an icon button replaces the inline pill (which otherwise
   widens the topbar). Tapping it opens the input as an overlay below the bar
   — JS toggles `.search-open` on .st-topbar. Hidden on desktop. */
.st-search-toggle {
    display: none;
}
@media (max-width: 600px) {
    .st-search-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 36px;
        height: 36px;
        padding: 0;
        border: 1px solid var(--st-border-strong);
        border-radius: 8px;
        background: var(--st-card);
        color: var(--st-text-muted);
        cursor: pointer;
    }
    .st-search {
        display: none;
    }
    .st-topbar.search-open .st-search {
        display: flex;
        position: absolute;
        top: 100%;
        left: 12px;
        right: 12px;
        margin-top: 8px;
        min-width: 0;
        z-index: 40;
        box-shadow: var(--st-shadow-sm);
    }
    /* ≥16px so iOS Safari doesn't auto-zoom the page when the input focuses.
       Must match the [type="search"] selector below — the base rule sets
       `font: inherit` (13px) at higher specificity and would otherwise win. */
    .st-search input,
    .st-search input[type="text"],
    .st-search input[type="search"] {
        font-size: 16px;
    }
}

/* Dropdown panel below the topbar input. Light-themed (white card) to
   distinguish from the dark .st-menu-panel — system controls are dark,
   content surfaces are light. The dropdown HTML is server-rendered by
   /search/quick and swapped in by app/javascript/search.js. */
.st-search-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow: var(--st-shadow-md);
    z-index: 30; /* above .st-menu-panel's 20 if both ever open together */
    max-height: 70vh;
    overflow-y: auto;
    padding: 6px;
    color: var(--st-text);
    font-size: 13px;
}
.st-search-dropdown[hidden] {
    display: none;
}
.st-search-section {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--st-text-muted);
    padding: 8px 10px 4px;
}
.st-search-result {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 7px 10px;
    border-radius: 6px;
    color: var(--st-text);
    line-height: 1.3;
}
.st-search-result:hover,
.st-search-result:focus-visible {
    background: var(--st-card-soft);
    color: var(--st-text);
    text-decoration: none;
    outline: none;
}
.st-search-result-title {
    font-weight: 500;
}
.st-search-result-meta {
    color: var(--st-text-muted);
    font-size: 12px;
}
.st-search-empty {
    padding: 16px 12px;
    color: var(--st-text-muted);
    text-align: center;
}
.st-search-foot {
    border-top: 1px solid var(--st-border);
    margin-top: 6px;
    padding: 8px 10px 4px;
    text-align: center;
}
.st-search-foot a {
    font-size: 12px;
    color: var(--st-accent);
}

/* On the full results page, each card holds a vertical list of rows. */
.st-search-result-list {
    display: flex;
    flex-direction: column;
}
.st-search-result-list .st-search-result {
    border-bottom: 1px solid var(--st-border);
    border-radius: 0;
}
.st-search-result-list .st-search-result:last-child {
    border-bottom: none;
}
.st-iconbtn {
    width: 34px;
    height: 34px;
    border: 1px solid var(--st-border-strong);
    border-radius: 8px;
    background: var(--st-card);
    color: var(--st-text-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    font: inherit;
    cursor: pointer;
    padding: 0;
}
.st-iconbtn:hover {
    color: var(--st-text);
    border-color: var(--st-text-muted);
}

/* Inline row-edit pen — borderless, faded, sized in em so it never grows the
   table row height. */
.st-rowedit {
    display: inline-flex;
    line-height: 0;
    color: var(--st-text-muted);
    opacity: 0.5;
}
.st-rowedit:hover {
    color: var(--st-accent);
    opacity: 1;
}
.st-rowedit svg {
    width: 1em;
    height: 1em;
}

/* Topbar menu (details/summary popover) */
.st-menu {
    position: relative;
}
.st-menu > summary {
    list-style: none;
    width: 34px;
    height: 34px;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--st-text-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.st-menu > summary::-webkit-details-marker {
    display: none;
}
.st-menu > summary:hover {
    color: var(--st-text);
}
.st-menu[open] > summary {
    background: transparent;
    color: var(--st-text);
}
.st-menu-panel {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    background: #1f2630;
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: var(--st-radius);
    box-shadow:
        0 4px 6px rgba(0, 0, 0, 0.2),
        0 12px 32px rgba(0, 0, 0, 0.25);
    min-width: 240px;
    padding: 6px;
    z-index: 20;
    color: #f3f2ee;
}
.st-menu-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 7px 10px;
    border-radius: 6px;
    color: #f3f2ee;
    font-size: 13px;
}
.st-menu-item:hover {
    background: rgba(255, 255, 255, 0.07);
    color: var(--st-on-accent);
    text-decoration: none;
}
.st-menu-item.active {
    background: rgba(44, 95, 179, 0.32);
    color: var(--st-accent-on-dark);
    font-weight: 500;
}
.st-menu-item .badge {
    font-family: var(--st-mono);
    font-size: 11px;
    padding: 1px 7px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.08);
    color: rgba(243, 242, 238, 0.75);
}
.st-menu-item.active .badge {
    background: rgba(44, 95, 179, 0.45);
    color: var(--st-on-accent);
}
.st-menu-separator {
    height: 2px;
    background: rgba(255, 255, 255, 0.06);
    margin: 8px 4px;
    border-radius: 1px;
}
/* "Log ud" — a button_to wrapped form whose <button> we restyle to match
   the surrounding menu items. The selector is scoped tightly enough to
   outrank the generic `form.button_to button` rule (~1690) without
   resorting to !important. */
.st-menu-panel form.button_to {
    margin: 0;
    padding: 0;
}
.st-menu-panel form.button_to button.st-menu-item-button {
    display: flex;
    align-items: center;
    justify-content: flex-end; /* right-align the label */
    width: 100%;
    padding: 7px 10px;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: #f3f2ee;
    font: inherit;
    font-size: 13px;
    font-weight: 400;
    cursor: pointer;
}
.st-menu-panel form.button_to button.st-menu-item-button:hover {
    background: rgba(255, 255, 255, 0.07);
    color: var(--st-on-accent);
}

/* ── Page heading ── */
.st-page {
    padding: 24px;
}
.st-page-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    margin-bottom: 20px;
    gap: 16px;
    flex-wrap: wrap;
}
.st-h1 {
    font-size: 24px;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 0 0 4px;
}
.st-h1.big {
    font-size: 32px;
}
.st-sub {
    color: var(--st-text-muted);
    font-size: 14px;
    margin: 0;
}

/* ── Cards ── */
.st-card {
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow: var(--st-shadow-sm);
}
.st-card.soft {
    background: var(--st-card-soft);
}
.st-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 18px;
    border-bottom: 1px solid var(--st-border);
    gap: 12px;
    /* Touch: on narrow screens the card itself is a horizontal scroller
       (.st-card { overflow-x: auto } in the mobile media query). A drag that
       starts on the header must scroll the PAGE, never pan the card — so only
       vertical panning is allowed from here. */
    touch-action: pan-y;
}
.st-card-title {
    font-size: 1em;
    font-weight: 600;
    color: var(--st-text);
    margin: 0;
    display: flex;
    align-items: center;
    gap: 8px;
}
.st-card-body {
    padding: 18px;
}
.st-card-body.flush {
    padding: 0;
}

/* ── KPI tile ── */
.st-kpi-strip {
    margin-bottom: 18px;
}
/* Widget-config sensor picker: click-to-toggle checklist. Specificity must
   beat the drawer's generic `.w-field label` (block/uppercase) and
   `.w-field input` (full-width, 38px) rules, hence the .w-field prefix. */
.w-field .w-sensor-picker {
    max-height: 40vh;
    overflow: auto;
    border: 1px solid var(--st-border-strong);
    border-radius: 9px;
    padding: 4px;
    background: var(--st-card);
}
.w-field label.w-sensor-opt {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    padding: 6px 8px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
    color: var(--st-text);
}
.w-field .w-sensor-opt input[type="checkbox"] {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    padding: 0;
    margin: 0;
    accent-color: var(--st-accent);
}
.w-field .w-sensor-opt:hover {
    background: var(--st-card-soft);
}
/* ── Property-design board widgets (kpi_strip / comparison / sensor_table) ── */
.w-card-kpis {
    container-type: inline-size;
}
.w-card-kpis .w-kpis .st-kpi-strip {
    margin-bottom: 0;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
}
@container (min-width: 700px) {
    .w-card-kpis .w-kpis .st-kpi-strip { grid-template-columns: repeat(3, 1fr); }
}
@container (min-width: 1000px) {
    .w-card-kpis .w-kpis .st-kpi-strip { grid-template-columns: repeat(6, 1fr); }
}
.w-card-kpis .st-kpi { padding: 12px 14px; gap: 4px; }
.w-card-kpis .st-kpi-value { font-size: 22px; }
.w-card-kpis .st-kpi-label { font-size: 9.5px; letter-spacing: 0.06em; }
.w-card-sensors,
.w-card-comparison {
    display: flex;
    flex-direction: column;
    min-height: 0;
}
.w-sensor-scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;
}
/* 1x1 comparison widget: no room for the period picker — the chart follows
   the page range anyway. */
@container (max-width: 260px) {
    .w-card-comparison .dr { display: none; }
}
.w-sensors-all {
    margin-top: 8px;
    align-self: flex-end;
    flex-shrink: 0; /* the scroll area shrinks, the link never clips away */
}
/* Full-row widgets (width unit 6): span every column regardless of how many
   the auto-fill grid produced. */
.w-slot[data-w-full] {
    grid-column: 1 / -1;
}
/* Alarm-sidens værktøjsrække: statustabs + søgning på én linje, pænt ombrudt
   på smalle skærme. */
.st-alarm-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 14px;
    margin-bottom: 14px;
}
.st-pagination {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 14px;
    margin-top: 12px;
    font-size: 13px;
    color: var(--st-text-muted);
}
a.st-kpi {
    color: inherit;
    text-decoration: none;
}
a.st-kpi:hover {
    border-color: var(--st-text-muted);
}
.st-kpi {
    position: relative; /* anchor for .st-kpi-icon */
    background: var(--st-card-soft);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.st-kpi-icon {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 28px; /* 22px × 1.25 */
    height: 28px;
    stroke-width: 2.1; /* CSS wins over the SVG presentation attribute */
    pointer-events: none;
}
/* Type-tinted icons. Water + sensor + humidity share the accent blue;
   alarm + thermometer get warmer tones so they stand apart from the
   consumption tiles at a glance. */
.st-kpi-icon-sensor {
    color: var(--st-accent);
}
.st-kpi-icon-water {
    color: var(--st-accent);
}
.st-kpi-icon-humidity {
    color: var(--st-accent);
}
.st-kpi-icon-alarm {
    color: var(--st-warn);
}
.st-kpi-icon-thermometer {
    color: var(--st-warm);
}
.st-kpi-label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--st-text-muted);
}
.st-kpi-value {
    font-size: 30px;
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--st-text);
    line-height: 1.1;
}
.st-kpi-value.huge {
    font-size: 44px;
}
.st-kpi-value.bad {
    color: var(--st-bad);
}
.st-kpi-unit {
    font-size: 18px;
    color: var(--st-text-muted);
    margin-left: 4px;
    font-weight: 400;
}
.st-kpi-foot {
    font-size: 12px;
    color: var(--st-text-muted);
}
.st-kpi-foot.oneline {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.st-kpi-foot .good,
.st-kpi-foot.good {
    color: var(--st-good);
}
.st-kpi-foot .warn,
.st-kpi-foot.warn {
    color: var(--st-warn);
}
.st-kpi-foot.bad {
    color: var(--st-bad);
}

/* ── Tabs (segmented) ── */
.st-tabs {
    display: inline-flex;
    background: var(--st-card-soft);
    border: 1px solid var(--st-border);
    border-radius: 8px;
    padding: 3px;
    gap: 2px;
}
.st-tab {
    padding: 5px 12px;
    font-size: 12px;
    font-weight: 500;
    border-radius: 6px;
    border: none;
    background: transparent;
    color: var(--st-text-muted);
    font: inherit;
    cursor: pointer;
    text-decoration: none;
}
.st-tab:hover {
    color: var(--st-text);
    text-decoration: none;
}
.st-tab.active {
    background: var(--st-text);
    color: var(--st-on-accent);
}
.st-tab.active:hover {
    color: var(--st-on-accent);
}
/* Period tabs ship both a long and a short label; CSS picks one. Default
   = long, mobile = short (see <540px media block). */
.st-tab-long {
    display: inline;
}
.st-tab-short {
    display: none;
}
/* Same trick for table column headers (and a few cell values like
   timestamps) where the long form is too wide on phones. */
.st-th-long {
    display: inline;
}
.st-th-short {
    display: none;
}

/* ── Date-range picker (.dr) ──
   Canonical period control for chart cards (replaces chart_period_tabs).
   Trigger pill shows the selected preset name in bold; once the user steps
   with ← →, the label switches to a monospace date range. UX reference:
   claude/design_charts/08_date_picker.html. See docs/charts.md. */
.dr {
    position: relative;
    display: inline-flex;
    align-items: stretch;
    background: var(--st-card-soft);
    border: 1px solid var(--st-border);
    border-radius: 8px;
    padding: 0;
    gap: 0;
}
.dr-step {
    width: 32px;
    min-height: 32px;
    background: transparent;
    border: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--st-text-muted);
    font: inherit;
    padding: 0;
    border-radius: 7px;
    transition:
        background 120ms ease,
        color 120ms ease;
}
.dr-step:hover:not(:disabled) {
    color: var(--st-text);
    background: rgba(20, 25, 35, 0.04);
}
.dr-step:disabled {
    color: var(--st-text-soft);
    opacity: 0.45;
    cursor: not-allowed;
}
.dr-step:focus-visible {
    outline: 2px solid var(--st-accent);
    outline-offset: 2px;
}
.dr-step svg {
    width: 14px;
    height: 14px;
    display: block;
}
.dr-trigger {
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 6px 12px;
    font: inherit;
    color: var(--st-text);
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 32px;
    border-radius: 7px;
    justify-content: center;
    transition: background 120ms ease;
}
.dr-trigger:hover {
    background: rgba(20, 25, 35, 0.04);
}
.dr-trigger:focus-visible {
    outline: 2px solid var(--st-accent);
    outline-offset: 2px;
}
/* Calendar glyph in the trigger — hidden on desktop (text label shows there),
   revealed on mobile where the trigger collapses to an icon. */
.dr-cal {
    display: none;
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    color: var(--st-text-muted);
}
.dr-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--st-text);
    letter-spacing: -0.005em;
}
.dr-label.range {
    font-family: var(--st-mono);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0;
}
.dr-chevron {
    width: 12px;
    height: 12px;
    flex: 0 0 auto;
    color: var(--st-text-muted);
    transition: transform 160ms ease;
}
.dr.open .dr-chevron {
    transform: rotate(180deg);
}

/* Popover */
.dr .pop {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    min-width: 260px;
    max-width: 320px;
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow:
        0 8px 28px rgba(20, 25, 35, 0.12),
        0 2px 6px rgba(20, 25, 35, 0.06);
    padding: 6px;
    opacity: 0;
    transform: translateY(-4px) scale(0.99);
    pointer-events: none;
    transition:
        opacity 140ms ease,
        transform 140ms ease;
    z-index: 1100; /* above topbar (1000) so it doesn't get covered */
}
.dr.open .pop {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
}
.pop-label {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--st-text-muted);
    padding: 8px 10px 4px;
}
.pop-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    padding: 7px 10px;
    border: none;
    background: transparent;
    cursor: pointer;
    font: inherit;
    font-size: 13px;
    color: var(--st-text);
    border-radius: 6px;
    text-align: left;
    transition: background 100ms ease;
}
.pop-item:hover {
    background: var(--st-card-soft);
}
.pop-item:focus-visible {
    outline: 2px solid var(--st-accent);
    outline-offset: -2px;
}
.pop-item.selected {
    background: var(--st-accent-bg);
    color: var(--st-accent);
    font-weight: 600;
}
.pop-item-check {
    width: 14px;
    height: 14px;
    flex: 0 0 auto;
    opacity: 0;
    color: var(--st-accent);
}
.pop-item.selected .pop-item-check {
    opacity: 1;
}
.pop-divider {
    height: 1px;
    background: var(--st-border);
    margin: 4px 6px;
}

.pop-custom {
    padding: 8px 10px 6px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.pop-custom-row {
    display: flex;
    gap: 6px;
    align-items: center;
}
.pop-custom-row input {
    flex: 1;
    font: inherit;
    font-size: 12px;
    font-family: var(--st-mono);
    padding: 6px 8px;
    border: 1px solid var(--st-border-strong);
    border-radius: 6px;
    background: var(--st-card);
    color: var(--st-text);
    min-width: 0;
}
.pop-custom-row input:focus {
    outline: 2px solid var(--st-accent);
    outline-offset: -1px;
    border-color: var(--st-accent);
}
.pop-custom-row .dash {
    color: var(--st-text-muted);
    font-size: 12px;
}
.pop-custom-apply {
    align-self: flex-end;
    font: inherit;
    font-size: 12px;
    font-weight: 600;
    padding: 6px 12px;
    border-radius: 6px;
    border: none;
    cursor: pointer;
    background: var(--st-accent);
    color: var(--st-on-accent);
    transition: background 120ms ease;
}
.pop-custom-apply:hover {
    background: var(--st-accent-hover);
}

/* Muted total appended after a chart-card title:
 *   <h2 class="st-card-title">Vandforbrug <span class="st-card-title-sub">5,1 m³</span></h2>
 * Same font-size as the heading, regular weight, muted color. */
.st-card-title-sub {
    font-weight: 400;
    color: var(--st-text-muted);
    margin-left: 6px;
    font-variant-numeric: tabular-nums;
}

/* Chart-card legend that sits under the comparison line chart. */
.st-chart-legend {
    display: flex;
    gap: 18px;
    align-items: center;
    flex-wrap: wrap;
    margin-top: 10px;
    font-size: 12px;
    color: var(--st-text-muted);
}
.st-chart-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
/* Comparison-chart legend: filled box for the current-period bars, dotted
 * line for the previous-period overlay drawn behind them. */
.st-chart-legend-swatch {
    display: inline-block;
    width: 14px;
    height: 10px;
    border-radius: 2px;
    background: var(--st-accent-soft);
}
.st-chart-legend-swatch.dotted {
    width: 22px;
    height: 0;
    background: transparent;
    border-radius: 0;
    border-top: 2px dotted rgba(138, 143, 153, 0.85);
    align-self: center;
}

@media (max-width: 600px) {
    /* Picker stays compact and right-aligned: steppers kept, trigger shows just
       the calendar icon (label + chevron hidden). flex:0 0 auto + margin-left
       guard against the box stretching to full header width. */
    .dr {
        flex: 0 0 auto;
        margin-left: auto;
        align-self: center;
    }
    .dr-cal {
        display: inline-block;
    }
    .dr-trigger .dr-label,
    .dr-trigger .dr-chevron {
        display: none;
    }
    .dr-trigger {
        flex: 0 0 auto;
        padding: 7px 9px;
    }
    /* Popover: a usable fixed-width panel anchored to the trigger's right edge,
       capped to the viewport — so the date inputs never spill outside it. */
    .dr .pop {
        right: 0;
        left: auto;
        min-width: 240px;
        max-width: calc(100vw - 24px);
    }
    /* Stack the from/to date inputs so they can't overflow the popover. */
    .pop-custom-row {
        flex-direction: column;
        align-items: stretch;
    }
    .pop-custom-row .dash {
        display: none;
    }
    .pop-custom-row input {
        font-size: 16px; /* avoid iOS focus-zoom on the date fields */
    }
    .st-chart-legend {
        gap: 12px;
        font-size: 11px;
    }
    /* Chart card header stays one line (title left, compact picker right);
       shorten the plot so it fits a phone. */
    .st-card-header {
        padding: 12px 14px;
    }
    .st-chart-wrap {
        height: 240px;
    }
    /* Forms: tighter card + comfortable but not oversized fields on phones. */
    .st-form {
        padding: 14px 16px;
    }
    .st-form > div {
        margin-bottom: 12px;
    }
}
@media (prefers-reduced-motion: reduce) {
    .dr-step,
    .dr-trigger,
    .dr-chevron,
    .dr .pop,
    .pop-item,
    .pop-custom-apply {
        transition: none !important;
    }
}

/* ── Pills / badges ── */
.st-pill {
    display: inline-flex;
    align-items: center;
    white-space: nowrap; /* "-95 dBm" must never split across lines in narrow cells */
    gap: 4px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.st-pill.good {
    background: var(--st-good-bg);
    color: var(--st-good);
}
.st-pill.warn {
    background: var(--st-warn-bg);
    color: var(--st-warn);
}
.st-pill.bad {
    background: var(--st-bad-bg);
    color: var(--st-bad);
}
.st-pill.neutral {
    background: var(--st-card-soft);
    color: var(--st-text-muted);
}
.st-pill.accent {
    background: var(--st-accent-bg);
    color: var(--st-accent);
}

.st-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
    flex: 0 0 auto;
}
.st-dot.good {
    background: var(--st-good);
}
.st-dot.warn {
    background: var(--st-warn);
}
.st-dot.bad {
    background: var(--st-bad);
}
.st-dot.neutral {
    background: var(--st-text-soft);
}

/* ── Buttons ── */
.st-btn {
    display: inline-block;
    padding: 8px 14px;
    border-radius: 8px;
    border: 1px solid var(--st-border-strong);
    background: var(--st-card);
    color: var(--st-text);
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
}
.st-btn:hover {
    text-decoration: none;
    border-color: var(--st-text-muted);
}
.st-btn.primary {
    background: var(--st-accent);
    color: var(--st-on-accent);
    border-color: var(--st-accent);
}
.st-btn.primary:hover {
    background: var(--st-accent-active);
    border-color: var(--st-accent-active);
    color: var(--st-on-accent);
}
.st-btn.danger {
    background: var(--st-bad);
    color: var(--st-on-accent);
    border-color: var(--st-bad);
}
.st-btn.danger:hover {
    background: var(--st-bad-active);
    border-color: var(--st-bad-active);
    color: var(--st-on-accent);
}
.st-btn.large {
    padding: 14px 22px;
    font-size: 16px;
}
.st-btn.small {
    padding: 4px 10px;
    font-size: 12px;
}

/* ── Tables ── */
.st-table {
    width: 100%;
    border-collapse: collapse;
}
.st-table th,
.st-table td {
    text-align: left;
    padding: 12px 16px;
    font-size: 13px;
    border-bottom: 1px solid var(--st-border);
    vertical-align: middle;
}
.st-table th {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--st-text-muted);
    font-weight: 600;
    background: transparent;
}
.st-table tr:last-child td {
    border-bottom: none;
}
.st-table tbody tr:hover td {
    background: rgba(0, 0, 0, 0.015);
}
/* Sortable columns: clickable headers with an asc/desc affordance. JS
   (sortable_table.js) toggles aria-sort; everything visual keys off it. */
.st-table.st-sortable th[data-sort-col] {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}
.st-table.st-sortable th[data-sort-col]::after {
    content: "↕";
    margin-left: 5px;
    opacity: 0.3;
    font-size: 0.9em;
}
.st-table.st-sortable th[aria-sort="ascending"]::after {
    content: "↑";
    opacity: 1;
}
.st-table.st-sortable th[aria-sort="descending"]::after {
    content: "↓";
    opacity: 1;
}
.st-table.st-sortable th[data-sort-col]:hover {
    color: var(--st-text);
}
.st-table.st-sortable th[data-sort-col]:focus-visible {
    outline: 2px solid var(--st-accent);
    outline-offset: -2px;
}
.st-link {
    color: var(--st-accent);
    font-weight: 500;
}

/* Widget-lup: diskret forstørrelsesglas øverst til højre i metrik-widgets,
   og graf-dialogen den åbner. */
.w-slot .w-zoom {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 3;
    color: var(--st-text-muted);
    opacity: 0.55;
    padding: 4px;
    line-height: 0;
}
.w-slot .w-zoom:hover { color: var(--st-text); opacity: 1; }
.w-zoom-dialog {
    border: 1px solid var(--st-border);
    border-radius: 12px;
    padding: 18px;
    width: min(720px, 92vw);
    background: var(--st-card);
    color: var(--st-text);
    /* Ingen indre scroll (UA-defaulten er overflow: auto) — titel/vælger/X
       skal blive stående, og touch må ikke panorere indholdet væk. */
    overflow: hidden;
}
.w-zoom-dialog::backdrop { background: rgb(15 18 20 / 0.45); }
/* X-luk på linje med titel + periode-vælger; section-head reserverer pladsen. */
.w-zoom-close {
    position: absolute;
    top: 16px;
    right: 14px;
    width: 34px;
    height: 34px;
    border: 1px solid var(--st-border-strong);
    border-radius: 8px;
    background: var(--st-card);
    color: var(--st-text-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 0;
}
.w-zoom-close:hover { color: var(--st-text); border-color: var(--st-text-muted); }
.w-zoom-dialog .aq-section-head { padding-right: 46px; }

/* Capped card tables (unit alarm cards): ~5 rows tall, inline scroll, header
   stays visible while scrolling. */
.st-table-scroll {
    max-height: 262px;
    overflow-y: auto;
}
.st-table-scroll .st-table thead th {
    position: sticky;
    top: 0;
    background: var(--st-card);
    z-index: 1;
}

.st-empty {
    padding: 32px 18px;
    text-align: center;
    color: var(--st-text-muted);
    font-size: 13px;
}

/* Card title — h2-sized variant used when the title should command more
   weight (e.g. the unit dashboard's map/foto card). Matches .st-h1's
   structure at a notch smaller font-size. */
.st-card-title-lg {
    font-size: 20px;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 0;
    color: var(--st-text);
}

/* ── Photo gallery (unit dashboard map/foto toggle) ── */
.st-photo-frame {
    position: relative;
    width: 100%;
    /* Matches .st-leaflet height so toggling Kort/Foto doesn't reflow
       the card and the dashboard layout stays stable. */
    height: 360px;
    background: var(--st-card-soft);
    border-radius: var(--st-radius);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
.st-photo {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.st-photo-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 36px;
    height: 36px;
    border-radius: 999px;
    background: rgba(20, 25, 35, 0.55);
    color: var(--st-on-accent);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    line-height: 1;
    text-decoration: none;
}
.st-photo-arrow:hover {
    background: rgba(20, 25, 35, 0.78);
    text-decoration: none;
    color: var(--st-on-accent);
}
.st-photo-arrow-prev {
    left: 10px;
}
.st-photo-arrow-next {
    right: 10px;
}
.st-photo-counter {
    position: absolute;
    bottom: 10px;
    right: 10px;
    background: rgba(20, 25, 35, 0.55);
    color: var(--st-on-accent);
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-family: var(--st-mono);
}
.st-photo-upload {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 12px;
    padding: 8px 10px;
    border: 1px dashed var(--st-border-strong);
    border-radius: var(--st-radius);
    background: var(--st-card-soft);
    flex-wrap: wrap;
}
.st-photo-upload-label {
    font-size: 12px;
    color: var(--st-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
}
.st-photo-upload input[type="file"] {
    flex: 1;
    min-width: 200px;
    font-size: 13px;
}

/* ── Chart wrappers ── */
/* Slim positioning container for Chart.js canvases. Chart.js draws into the
   canvas and handles all rendering; this wrap just gives it a sized
   parent so `responsive: true + maintainAspectRatio: false` can work. */
.st-chart-wrap {
    position: relative;
    width: 100%;
    height: 360px; /* matches .st-leaflet so chart card + map card line up */
}
.st-chart-wrap-combo {
    /* height set inline by the helper — varies per chart */
}
.st-chart-wrap > canvas {
    max-width: 100%;
}

/* ── Map placeholder ── */
.st-map {
    height: 100%;
    min-height: 240px;
    background:
        repeating-linear-gradient(
            135deg,
            rgba(60, 90, 140, 0.04) 0 8px,
            transparent 8px 18px
        ),
        linear-gradient(180deg, var(--st-paper) 0%, var(--st-paper-deep) 100%);
    border-radius: 8px;
    position: relative;
    overflow: hidden;
}
.st-map.mini {
    min-height: 160px;
}
/* Sensor-type icon left of the name in the dashboard sensor tables (replaces
   the old Type column). Muted so it reads as a quiet qualifier, not a link. */
.st-sensor-name {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    white-space: nowrap;
}
.st-sensor-icon {
    flex: 0 0 auto;
    color: var(--st-text);
}
/* Type column now carries the kind icon next to its short label. */
.st-type-cell { display: inline-flex; align-items: center; gap: 6px; }

/* Past-alarm resolution timeline, right-aligned in the alarm detail header. */
.alarm-resolution { display: flex; gap: 22px; text-align: right; flex-wrap: wrap; justify-content: flex-end; }
.alarm-resolution-item { display: flex; flex-direction: column; gap: 2px; font-size: 13px; }

/* Collapsible unit sensors (property page): the expand arrow sits in the icon
   slot (bare chevron, no pill), and the extra sensors are hidden sub-rows. */
.st-row-expand {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    padding: 0;
    border: none;
    background: none;
    color: var(--st-text-soft);
    cursor: pointer;
}
.st-row-lead { flex: 0 0 16px; display: inline-flex; align-items: center; justify-content: center; }
.st-row-expand:hover { color: var(--st-text); }
.st-row-expand svg { width: 16px; height: 16px; transition: transform 0.15s ease; }
.st-row-expand[aria-expanded="true"] svg { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .st-row-expand svg { transition: none; } }

/* Folded-out sub-rows sit flush (no distinct fill); the name is indented just
   past the owner row's name (which is offset by the 16px expand-arrow slot +
   7px gap) so the sub-sensors read as nested under it. */
.st-subrow-name { padding-left: 30px; }

.st-map-pin {
    position: absolute;
    width: 22px;
    height: 22px;
    margin-left: -11px;
    margin-top: -11px;
    border-radius: 50%;
    background: var(--st-accent);
    border: 3px solid var(--st-on-accent);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
    font-size: 10px;
    font-weight: 700;
    color: var(--st-on-accent);
    display: flex;
    align-items: center;
    justify-content: center;
}
.st-map-pin.good {
    background: var(--st-good);
}
.st-map-pin.warn {
    background: var(--st-warn);
}
.st-map-pin.bad {
    background: var(--st-bad);
}
.st-map-attribution {
    position: absolute;
    bottom: 4px;
    right: 8px;
    font-size: 9px;
    color: var(--st-text-soft);
    background: rgba(255, 255, 255, 0.7);
    padding: 1px 5px;
    border-radius: 3px;
}

/* Leaflet container — same chrome as the placeholder map.
   isolation:isolate creates a fresh stacking context so Leaflet's internal
   z-index ladder (panes 200–700, controls up to 1000) can never escape
   above the topbar / dropdown menu. */
.st-leaflet {
    height: 360px;
    border-radius: 8px;
    overflow: hidden;
    position: relative;
    isolation: isolate;
    z-index: 0;
}
/* Expandable dashboard maps: a background click opens the fullscreen modal —
 * hint it with a zoom-in cursor on the (non-dragging) grab layer. */
.st-leaflet--expandable .leaflet-grab {
    cursor: zoom-in;
}

/* Expand + layer-toggle buttons — appended into the zoom bar, so they inherit
   leaflet's .leaflet-bar a sizing/border. We only size + center the icon. */
.st-map-expand-btn svg,
.st-map-layer-btn svg {
    width: 16px;
    height: 16px;
    vertical-align: middle;
}
.st-map-expand-btn:hover {
    color: var(--st-accent);
}
/* Layer toggle is gray-toned so it reads quieter than the +/- controls.
   (extra .leaflet-bar specificity to beat leaflet's `.leaflet-bar a` color) */
.leaflet-bar a.st-map-layer-btn {
    color: var(--st-text-soft);
}
.leaflet-bar a.st-map-layer-btn:hover,
.leaflet-bar a.st-map-layer-btn.is-active {
    color: var(--st-accent); /* satellite active → tint so state is legible */
}

/* ── Fullscreen map modal ─────────────────────────────────────────────── */
.st-map-modal {
    position: fixed;
    inset: 0;
    z-index: 2000; /* above the sticky topbar (z-index 1000) */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3vmin;
    background: rgba(20, 25, 35, 0.55);
    backdrop-filter: blur(2px);
    animation: st-map-modal-in 140ms ease-out;
}
.st-map-modal-inner {
    position: relative;
    width: 94vw;
    height: 92vh;
    background: var(--st-card);
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 20px 60px rgba(20, 25, 35, 0.35);
}
.st-map-modal-map {
    width: 100%;
    height: 100%;
}
/* Controls now live top-right, where the modal's close button also sits —
   push the top-right Leaflet bar down so it clears the X. */
.st-map-modal .leaflet-top.leaflet-right {
    margin-top: 52px;
}
.st-map-modal-close {
    position: absolute;
    top: 12px;
    right: 12px;
    z-index: 1200; /* above Leaflet controls/panes inside the modal */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border: none;
    border-radius: 50%;
    background: var(--st-card);
    color: var(--st-text);
    box-shadow: 0 2px 8px rgba(20, 25, 35, 0.25);
    cursor: pointer;
}
.st-map-modal-close:hover {
    background: var(--st-bg, #f4f5f7);
}
/* Lock background scroll while the modal is open. */
.st-modal-open {
    overflow: hidden;
}
@keyframes st-map-modal-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
@media (prefers-reduced-motion: reduce) {
    .st-map-modal {
        animation: none;
    }
}
/* Desaturated tile variant — used on the signal-diagnostics map so colored
 * status pins and RSSI link polylines pop against a quiet basemap. Filter
 * lives on .leaflet-tile (the individual tile <img>) instead of the pane
 * so it survives Leaflet's pane re-creation on zoom. */
.st-leaflet--muted .leaflet-tile {
    filter: grayscale(1) brightness(1.04) contrast(0.9);
}
.st-map-legend {
    display: flex;
    gap: 18px;
    flex-wrap: wrap;
    margin-top: 10px;
    padding: 0 4px;
    font-size: 12px;
    color: var(--st-text-muted);
}
.st-map-legend > span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.st-leaflet-pin-wrap {
    background: transparent;
    border: none;
}
.st-leaflet-pin {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: 2px solid var(--st-on-accent);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
    font-size: 10px;
    font-weight: 700;
    color: var(--st-on-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--st-mono);
}

/* ── Signal-diagnostics: map + node detail pane ── */
.sd-map-wrap { display: flex; gap: 14px; align-items: stretch; }
.sd-map { flex: 1 1 100%; min-width: 0; transition: flex-basis 0.24s ease; }
.sd-open .sd-map { flex-basis: 50%; }
.sd-detail {
    flex: 1 1 50%;
    min-width: 0;
    overflow-y: auto;
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: 8px;
}
.sd-detail[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .sd-map { transition: none; } }
@media (max-width: 760px) {
    .sd-map-wrap { flex-direction: column; }
    .sd-open .sd-map { flex-basis: auto; }
    .sd-detail { flex-basis: auto; max-height: 60vh; }
}

.sd-detail-inner { padding: 14px 16px; }
.sd-detail-loading { padding: 28px 16px; text-align: center; color: var(--st-text-soft); font-size: 13px; }
.sd-detail-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; margin-bottom: 12px; }
.sd-detail-title { font-size: 16px; font-weight: 650; margin: 0; }
.sd-detail-sub { font-size: 12px; color: var(--st-text-soft); font-family: var(--st-mono); margin: 2px 0 0; word-break: break-all; }
.sd-detail-close { background: none; border: none; color: var(--st-text-soft); cursor: pointer; padding: 4px; border-radius: 6px; line-height: 0; }
.sd-detail-close:hover { background: var(--st-card-soft); color: var(--st-text); }

.sd-chips { display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 10px; margin-bottom: 14px; }
.sd-chip { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.sd-chip-label { font-size: 11px; color: var(--st-text-soft); text-transform: uppercase; letter-spacing: 0.04em; }
.sd-chip-value { font-size: 14px; font-weight: 600; }
.sd-chip .st-pill { align-self: flex-start; }

.sd-stat-row { display: flex; gap: 16px; padding: 10px 0; border-top: 1px solid var(--st-border); border-bottom: 1px solid var(--st-border); margin-bottom: 14px; flex-wrap: wrap; }
.sd-stat { display: flex; flex-direction: column; }
.sd-stat-num { font-size: 16px; font-weight: 650; font-variant-numeric: tabular-nums; }
.sd-stat-label { font-size: 11px; color: var(--st-text-soft); }

.sd-section-title { font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--st-text-soft); margin: 0 0 8px; }
.sd-packets { width: 100%; border-collapse: collapse; font-size: 12px; }
.sd-packets th { text-align: left; font-weight: 600; color: var(--st-text-soft); padding: 4px 6px; border-bottom: 1px solid var(--st-border); }
.sd-packets td { padding: 4px 6px; border-bottom: 1px solid var(--st-border); }
.sd-packets .sd-num { text-align: right; font-variant-numeric: tabular-nums; }
.sd-detail-link { display: inline-block; margin-top: 12px; font-size: 13px; }

/* ── Forms ── */
.st-form {
    background: var(--st-card);
    padding: 18px 20px;
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow: var(--st-shadow-sm);
    max-width: 560px;
}
.st-form > div {
    margin-bottom: 14px;
}
.st-form label {
    display: block;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--st-text-muted);
    margin-bottom: 6px;
}
.st-form input[type="text"],
.st-form input[type="email"],
.st-form input[type="password"],
.st-form input[type="number"],
.st-form input[type="search"],
.st-form select,
.st-form textarea {
    width: 100%;
    padding: 8px 12px;
    font: inherit;
    font-size: 14px;
    color: var(--st-text);
    border: 1px solid var(--st-border-strong);
    border-radius: 8px;
    background: var(--st-card);
}
.st-form input:focus,
.st-form select:focus,
.st-form textarea:focus {
    outline: none;
    border-color: var(--st-accent);
    box-shadow: 0 0 0 3px var(--st-accent-bg);
}
.st-form textarea {
    min-height: 100px;
    resize: vertical;
}
.st-form .st-form-help {
    font-size: 12px;
    color: var(--st-text-muted);
    margin-top: 4px;
}

/* ── Devise unauthenticated screens ──
   The Devise views use .st-form like everything else; these classes
   replace the inline style attributes that previously decorated the
   logo, the heading, the remember-me checkbox row, and the full-width
   submit button. Class-based so a future CSP (`style-src 'self'`)
   doesn't break the screens. */
.st-devise-brand {
    text-align: center;
    margin-bottom: 24px;
}
.st-devise-logo {
    height: 32px;
    width: auto;
}
.st-form-heading {
    font-size: 18px;
    font-weight: 600;
    margin: 0 0 18px;
    letter-spacing: -0.01em;
}
/* When a help paragraph follows the heading, tighten the gap and
   leave the standard 18px before the first field. */
.st-form-heading + .st-form-help {
    margin-top: -12px;
    margin-bottom: 18px;
}
.st-form-checkbox-row {
    display: flex;
    align-items: center;
    gap: 8px;
}
.st-form-checkbox-row input[type="checkbox"] {
    width: auto;
    margin: 0;
}
.st-form-checkbox-row label {
    margin: 0;
    font-size: 13px;
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    color: var(--st-text);
}
.st-form-actions {
    margin-top: 18px;
    margin-bottom: 0;
}
.st-btn-block {
    width: 100%;
}
.st-devise-links {
    margin-top: 18px;
    text-align: center;
    font-size: 13px;
    color: var(--st-text-muted);
}

/* ── Top navigation progress bar ── */
/* Driven by app/javascript/nav_progress.js. The element is appended on
   link click and removed when the next page replaces the document, so
   the animation always reads as "started, doing work, gone" regardless
   of how long the request actually takes. */
#st-nav-progress {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: var(--st-accent);
    transform: scaleX(0);
    transform-origin: left center;
    z-index: 9999;
    pointer-events: none;
    box-shadow: 0 0 8px rgba(44, 95, 179, 0.5);
}
#st-nav-progress.active {
    animation: st-nav-progress 12s cubic-bezier(0.1, 0.9, 0.3, 1) forwards;
}
@keyframes st-nav-progress {
    0% {
        transform: scaleX(0);
    }
    20% {
        transform: scaleX(0.4);
    }
    50% {
        transform: scaleX(0.75);
    }
    80% {
        transform: scaleX(0.92);
    }
    100% {
        transform: scaleX(0.97);
    }
}
@media (prefers-reduced-motion: reduce) {
    #st-nav-progress {
        display: none;
    }
}

/* ── Flash ── */
.st-flash {
    padding: 10px 14px;
    border-radius: var(--st-radius);
    border: 1px solid transparent;
    font-size: 13px;
    margin: 0 0 16px;
}
.st-flash.notice {
    background: var(--st-good-bg);
    border-color: var(--st-good-bg);
    color: var(--st-good);
}
.st-flash.alert {
    background: var(--st-bad-bg);
    border-color: var(--st-bad-bg);
    color: var(--st-bad);
}
.st-errors {
    background: var(--st-bad-bg);
    border: 1px solid var(--st-bad-bg);
    padding: 10px 14px;
    border-radius: var(--st-radius);
    color: var(--st-bad);
}
.st-errors li {
    margin-left: 16px;
}

/* ── Definition lists (kv pairs on detail pages) ── */
.st-kv {
    display: grid;
    grid-template-columns: 160px 1fr;
    gap: 8px 16px;
    margin: 0 0 16px;
}
.st-kv dt {
    color: var(--st-text-muted);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
}
.st-kv dd {
    margin: 0;
    font-size: 14px;
}

/* ── Heatmap (legacy chart helper renders cells) ── */
.heatmap-grid {
    display: grid;
    grid-template-columns: 32px repeat(24, 1fr);
    gap: 2px;
    font-family: var(--st-mono);
    font-size: 10px;
}
.heatmap-grid .hm-row-label,
.heatmap-grid .hm-col-label {
    color: var(--st-text-muted);
    padding: 2px 4px;
}
.heatmap-grid .hm-cell {
    height: 22px;
    border-radius: 3px;
    background: var(--st-accent-soft);
}

/* ── Sparkline / inline chart ── */
.spark {
    display: block;
    width: 100%;
    height: 36px;
}

/* ── Page-level utilities ── */
.st-row {
    display: flex;
    gap: 16px;
}
.st-col {
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.st-grow {
    flex: 1;
    min-width: 0;
}
.st-grid-2 {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 14px;
}
.st-grid-4 {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 14px;
}
/* Six-tile strips (property/unit/tenant: +temp/humidity): one row when it
   fits, balanced 3+3 below that, 2 columns on phones — never a ragged 4+2. */
@media (min-width: 1281px) {
    .st-grid-4.st-grid-six {
        grid-template-columns: repeat(6, 1fr);
    }
}
@media (max-width: 1280px) {
    .st-grid-4.st-grid-six {
        grid-template-columns: repeat(3, 1fr);
    }
}
@media (max-width: 540px) {
    .st-grid-4.st-grid-six {
        grid-template-columns: 1fr 1fr;
    }
}
.st-divider {
    height: 1px;
    background: var(--st-border);
    margin: 12px 0;
}
.st-stack-sm > * + * {
    margin-top: 8px;
}
.st-stack-md > * + * {
    margin-top: 14px;
}
.st-stack-lg > * + * {
    margin-top: 18px;
}

/* Stack 2-col rows on tablets and small laptops up to ~1200px. iPad
   landscape (1024–1194) was getting a cramped 2fr/1fr split where a chart
   + map shared a row; full-width cards read better at that range. KPI
   strip stays 4-across down to 900px since the cards are tiny. */
@media (max-width: 1200px) {
    .st-grid-2 {
        grid-template-columns: 1fr;
    }
}
@media (max-width: 900px) {
    /* Tablet keeps all four KPI tiles on one row (compacted) instead of the
       2+2 mid-break; the 540px block below takes over on phones. */
    .st-grid-4 {
        grid-template-columns: repeat(4, 1fr);
        gap: 10px;
    }
    .st-grid-4 .st-kpi {
        padding: 12px 14px;
        gap: 4px;
    }
    .st-grid-4 .st-kpi-label {
        font-size: 9.5px;
        letter-spacing: 0.06em;
    }
    .st-grid-4 .st-kpi-value {
        font-size: 24px;
    }
    .st-page {
        padding: 16px;
        padding-bottom: calc(80px + env(safe-area-inset-bottom));
    }
    /* Tables: allow horizontal scroll inside cards on narrow screens. */
    .st-card {
        overflow-x: auto;
    }
    /* …but not on graph cards (for nu): charts fit their container, so the
       inner scroller only stole touch-gestures fra side-scroll. */
    .st-card:has(.st-chart-wrap) {
        overflow-x: visible;
    }
    .st-card .st-table {
        min-width: 480px;
    }
    /* Compact tables (3 short columns: timestamp + value + delta) fit on a
     phone without scrolling — the 480px floor only stretches them. */
    .st-card .st-table.compact {
        min-width: 0;
    }
    /* Touch targets — bump tab height on mobile for easier tapping. */
    .st-tab {
        padding: 9px 12px;
        min-height: 44px;
    }
    /* Hide page-header action buttons (e.g. "Rediger") on tablet/phone — frees
     up horizontal room for the entity name (h1). The action is reachable
     from the burger menu / direct URL. */
    .st-page-header .st-btn {
        display: none;
    }

    /* …except the dashboard editor's toolbar: Gem / Tilføj widget / Annullér
     have no other path, so hiding them makes the editor unsaveable on
     phone and iPad portrait. */
    .st-page-header .wc-toolbar .st-btn {
        display: inline-block;
    }
}
@media (max-width: 540px) {
    .st-page {
        padding: 12px;
        padding-bottom: calc(80px + env(safe-area-inset-bottom));
    }
    .st-logo-mark {
        height: 22px;
    }
    .st-topbar {
        padding: 12px 14px;
    }
    .st-grid-4 {
        grid-template-columns: 1fr 1fr;
        gap: 10px;
    }
    .st-kpi {
        padding: 12px 14px;
        gap: 4px;
    }
    .st-kpi-label {
        font-size: 9.5px;
        letter-spacing: 0.06em;
    }
    .st-kpi-value {
        font-size: 24px;
    }
    .st-kpi-value.huge {
        font-size: 32px;
    }
    .st-kpi-unit {
        font-size: 14px;
    }
    .st-h1 {
        font-size: 22px;
    }
    .st-page-header {
        gap: 10px;
        margin-bottom: 14px;
    }
    /* Visually-collapse the page-header text block on phones (h1 +
     breadcrumb sub take vertical room above the KPI strip), but keep it
     in the accessibility tree so screen-reader users still hear the
     entity name. `display: none` would have removed it from AT entirely.
     The sr-only-style clip leaves zero visual footprint while preserving
     announcement on focus / page-load. */
    .st-page-header > div:first-child {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }
    .st-card-header {
        padding: 12px 14px;
    }
    .st-card-body {
        padding: 14px;
    }
    /* Period tabs swap to compact labels on phones so "1 dag / 7 dage /
     30 dage / 1 år" doesn't wrap onto two lines. */
    .st-tab-long {
        display: none;
    }
    .st-tab-short {
        display: inline;
    }
    /* Same swap for table headers / timestamp cells. */
    .st-th-long {
        display: none;
    }
    .st-th-short {
        display: inline;
    }
    /* Drop the "↑ 25% vs. i går" sub-line under usage cells on phones —
     too narrow to render usefully and clutters the row height. */
    .st-compare-line {
        display: none;
    }
}

/* ── Bottom tab bar (mobile only) ── */
.st-bottom-tabs {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    display: none;
    background: rgba(255, 255, 255, 0.92);
    backdrop-filter: saturate(180%) blur(14px);
    -webkit-backdrop-filter: saturate(180%) blur(14px);
    border-top: 1px solid var(--st-border);
    padding-bottom: env(safe-area-inset-bottom);
    z-index: 30;
}
.st-bottom-tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 10px 4px 8px;
    min-height: 56px;
    color: var(--st-text-muted);
    font-size: 10px;
    font-weight: 500;
    text-decoration: none;
}
.st-bottom-tab:hover {
    text-decoration: none;
    color: var(--st-text);
}
.st-bottom-tab.active {
    color: var(--st-accent);
}
.st-bottom-tab svg {
    width: 22px;
    height: 22px;
}

@media (max-width: 900px) {
    .st-bottom-tabs {
        display: flex;
    }
}

/* ──────────────────────────────────────────────────────────────────────
   Legacy aliases — keep older view markup readable while pages migrate.
   Each rule below maps an old class name onto the equivalent st-* primitive.
   Remove once every view uses the st-* names directly.
   ────────────────────────────────────────────────────────────────────── */

.card {
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow: var(--st-shadow-sm);
    padding: 18px;
}
.kpi {
    background: var(--st-card-soft);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.kpi-label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--st-text-muted);
    display: flex;
    align-items: center;
    gap: 8px;
}
.kpi-label::before {
    content: "";
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: var(--st-accent);
}
.kpi-label.water::before {
    background: var(--st-accent);
}
.kpi-label.elec::before {
    background: var(--st-warn);
}
.kpi-label.heat::before {
    background: var(--st-bad);
}
.kpi-label.health::before {
    background: var(--st-good);
}
.kpi-value {
    font-size: 30px;
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--st-text);
    line-height: 1.1;
    margin-top: 4px;
}
.kpi-unit {
    color: var(--st-text-muted);
    font-size: 18px;
    margin-left: 4px;
    font-weight: 400;
}
.kpi-delta {
    font-family: var(--st-mono);
    font-size: 12px;
}
.kpi-delta.up {
    color: var(--st-bad);
}
.kpi-delta.down {
    color: var(--st-good);
}
.kpi-delta.flat {
    color: var(--st-text-muted);
}
.kpi-foot {
    font-size: 12px;
    color: var(--st-text-muted);
}
.kpi-spark {
    margin-top: auto;
    padding-top: 6px;
}

.tabs {
    display: flex;
    gap: 18px;
    padding: 0;
    border-bottom: 1px solid var(--st-border);
    margin: 6px 0 18px;
}
.tabs a {
    padding: 8px 0;
    color: var(--st-text-muted);
    font-size: 13px;
    border-bottom: 2px solid transparent;
}
.tabs a:hover {
    color: var(--st-text);
    text-decoration: none;
}
.tabs a.active {
    color: var(--st-text);
    border-bottom-color: var(--st-accent);
    font-weight: 500;
}
.tabs .tab-count {
    color: var(--st-text-muted);
    margin-left: 4px;
}

.eyebrow {
    font-family: var(--st-mono);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--st-text-muted);
    margin-bottom: 6px;
}
.meta-line {
    color: var(--st-text-muted);
    font-size: 13px;
}
.meta-line span + span::before {
    content: " · ";
    color: var(--st-text-muted);
}
.mono {
    font-family: var(--st-mono);
}

table:not(.st-table) {
    width: 100%;
    border-collapse: collapse;
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    overflow: hidden;
    box-shadow: var(--st-shadow-sm);
}
table:not(.st-table) th,
table:not(.st-table) td {
    padding: 12px 16px;
    font-size: 13px;
    text-align: left;
    vertical-align: middle;
}
table:not(.st-table) thead th {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--st-text-muted);
    border-bottom: 1px solid var(--st-border);
    font-weight: 600;
    background: transparent;
}
table:not(.st-table) tbody tr + tr td {
    border-top: 1px solid var(--st-border);
}
table:not(.st-table) tbody tr:hover td {
    background: rgba(0, 0, 0, 0.015);
}

form:not(.st-form):not(.button_to):not(.st-search-host):not(.st-filter-form):not(.st-cell-form) {
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow: var(--st-shadow-sm);
    max-width: 560px;
    padding: 24px;
}
form:not(.st-form):not(.button_to):not(.st-search-host) > div {
    margin-bottom: 14px;
}
form:not(.st-form):not(.button_to):not(.st-search-host) label {
    display: block;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--st-text-muted);
    margin-bottom: 6px;
}
form:not(.st-form):not(.button_to):not(.st-search-host) input[type="text"],
form:not(.st-form):not(.button_to):not(.st-search-host) input[type="email"],
form:not(.st-form):not(.button_to):not(.st-search-host) input[type="password"],
form:not(.st-form):not(.button_to):not(.st-search-host) input[type="number"],
form:not(.st-form):not(.button_to):not(.st-search-host) input[type="search"],
form:not(.st-form):not(.button_to):not(.st-search-host):not(.st-filter-form)
    select,
form:not(.st-form):not(.button_to):not(.st-search-host):not(.st-filter-form)
    textarea {
    width: 100%;
    padding: 8px 12px;
    font: inherit;
    font-size: 14px;
    color: var(--st-text);
    border: 1px solid var(--st-border-strong);
    border-radius: 8px;
    background: var(--st-card);
}

/* Filter dropdown styled to match the date-range picker pill (.dr): same
   soft-grey fill, light border, radius and height so the two controls read as
   a set. Used by the Signal-diagnostik gateway filter. */
.st-filter-form {
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.st-filter-select {
    height: 34px;
    padding: 0 12px;
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    color: var(--st-text);
    background: var(--st-card-soft);
    border: 1px solid var(--st-border);
    border-radius: 8px;
    cursor: pointer;
}
.st-filter-select:hover {
    border-color: var(--st-border-strong);
}
.st-filter-select:focus-visible {
    outline: 2px solid var(--st-accent);
    outline-offset: -1px;
}

/* Device multi-select on the signal-diagnostics filter bar. A <details>
   disclosure styled like .st-filter-select, opening a light checklist panel. */
.st-ms {
    position: relative;
}
.st-ms-summary {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    list-style: none; /* drop the default disclosure triangle */
}
.st-ms-summary::-webkit-details-marker {
    display: none;
}
.st-ms-chevron {
    width: 14px;
    height: 14px;
    color: var(--st-text-soft);
    transition: transform 0.15s ease;
}
.st-ms[open] .st-ms-chevron {
    transform: rotate(180deg);
}
.st-ms-panel {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 30;
    width: 280px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px;
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow: var(--st-shadow-sm), 0 12px 32px rgba(20, 25, 35, 0.18);
}
.st-ms-search {
    height: 32px;
    padding: 0 10px;
    font: inherit;
    font-size: 13px;
    color: var(--st-text);
    background: var(--st-card-soft);
    border: 1px solid var(--st-border);
    border-radius: 8px;
}
.st-ms-tools {
    display: flex;
    gap: 14px;
    font-size: 12px;
}
.st-ms-tools .st-link {
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
}
.st-ms-list {
    max-height: 260px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.st-ms-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 6px;
    border-radius: 6px;
    font-size: 13px;
    cursor: pointer;
}
.st-ms-item:hover {
    background: var(--st-card-soft);
}
.st-ms-item input {
    flex: 0 0 auto;
}
.st-ms-item-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.st-ms-apply {
    width: 100%;
    justify-content: center;
}
@media (prefers-reduced-motion: reduce) {
    .st-ms-chevron {
        transition: none;
    }
}

form:not(.st-form):not(.button_to):not(.st-search-host) input:focus,
form:not(.st-form):not(.button_to):not(.st-search-host) select:focus,
form:not(.st-form):not(.button_to):not(.st-search-host) textarea:focus {
    outline: none;
    border-color: var(--st-accent);
    box-shadow: 0 0 0 3px var(--st-accent-bg);
}
form:not(.st-form):not(.button_to):not(.st-search-host) input[type="submit"],
form:not(.st-form):not(.button_to):not(.st-search-host)
    button:not(.st-iconbtn):not(.st-btn) {
    display: inline-block;
    padding: 8px 14px;
    border-radius: 8px;
    border: 1px solid var(--st-accent);
    background: var(--st-accent);
    color: var(--st-on-accent);
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}
form.button_to {
    display: inline-block;
    padding: 0;
    border: none;
    background: transparent;
    max-width: none;
}
form.button_to button {
    display: inline-block;
    padding: 6px 12px;
    border-radius: 8px;
    border: 1px solid var(--st-border-strong);
    background: var(--st-card);
    color: var(--st-text);
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}

.btn {
    display: inline-block;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 500;
    background: var(--st-accent);
    color: var(--st-on-accent);
    border: 1px solid var(--st-accent);
    border-radius: 8px;
    font-family: inherit;
    cursor: pointer;
    text-decoration: none;
}
.btn:hover {
    background: var(--st-accent-active);
    border-color: var(--st-accent-active);
    color: var(--st-on-accent);
    text-decoration: none;
}
.btn-soft {
    background: var(--st-card);
    color: var(--st-text);
    border-color: var(--st-border-strong);
}
.btn-soft:hover {
    background: var(--st-card-soft);
    color: var(--st-text);
    border-color: var(--st-text-muted);
}

.flash {
    padding: 10px 14px;
    border-radius: var(--st-radius);
    border: 1px solid transparent;
    font-size: 13px;
    margin-bottom: 16px;
}
.flash-notice {
    background: var(--st-good-bg);
    border-color: var(--st-good-bg);
    color: var(--st-good);
}
.flash-alert {
    background: var(--st-bad-bg);
    border-color: var(--st-bad-bg);
    color: var(--st-bad);
}
.errors {
    background: var(--st-bad-bg);
    border: 1px solid var(--st-bad-bg);
    padding: 10px 14px;
    border-radius: var(--st-radius);
    color: var(--st-bad);
}
.errors li {
    margin-left: 16px;
}

dl:not(.st-kv) {
    display: grid;
    grid-template-columns: 160px 1fr;
    gap: 8px 16px;
    margin: 0 0 16px;
}
dl:not(.st-kv) dt {
    color: var(--st-text-muted);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
}
dl:not(.st-kv) dd {
    margin: 0;
}

.severity {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.severity-info {
    background: var(--st-accent-bg);
    color: var(--st-accent);
}
.severity-warning {
    background: var(--st-warn-bg);
    color: var(--st-warn);
}
.severity-critical {
    background: var(--st-bad-bg);
    color: var(--st-bad);
}
.has-open-alarms {
    color: var(--st-bad);
    font-weight: 600;
}

.tag-chip {
    display: inline-block;
    font-family: var(--st-mono);
    font-size: 11px;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--st-card-soft);
    color: var(--st-text-muted);
    margin-right: 4px;
}
.tag-chip.tag-water {
    background: var(--st-accent-bg);
    color: var(--st-accent);
}
.tag-chip.tag-elec {
    background: var(--st-warn-bg);
    color: var(--st-warn);
}
.tag-chip.tag-heat {
    background: var(--st-bad-bg);
    color: var(--st-bad);
}
.tag-chip.tag-temp {
    background: var(--st-card-soft);
    color: var(--st-text-soft);
}
.tag-chip.tag-ok {
    background: var(--st-good-bg);
    color: var(--st-good);
}
.tag-chip.tag-warn {
    background: var(--st-warn-bg);
    color: var(--st-warn);
}
.tag-chip.tag-crit {
    background: var(--st-bad-bg);
    color: var(--st-bad);
}
.tag-chip.tag-info {
    background: var(--st-accent-bg);
    color: var(--st-accent);
}

.alerts-panel {
    display: flex;
    flex-direction: column;
    gap: 0;
}
.alerts-panel-row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 12px 0;
    border-top: 1px solid var(--st-border);
}
.alerts-panel-row:first-child {
    border-top: 0;
    padding-top: 0;
}
.alerts-panel-row .dot {
    width: 8px;
    height: 8px;
    border-radius: 999px;
    margin-top: 6px;
    flex: 0 0 auto;
}
.alerts-panel-row .dot.critical {
    background: var(--st-bad);
}
.alerts-panel-row .dot.warning {
    background: var(--st-warn);
}
.alerts-panel-row .dot.info {
    background: var(--st-accent);
}
.alerts-panel-row .body {
    flex: 1;
    min-width: 0;
}
.alerts-panel-row .body .title {
    font-size: 14px;
    color: var(--st-text);
    font-weight: 500;
}
.alerts-panel-row .body .meta {
    font-family: var(--st-mono);
    font-size: 11px;
    color: var(--st-text-muted);
}

.kpi-health .kpi-label.health::before {
    background: var(--st-warn);
}
.kpi-health-line {
    font-size: 22px;
    font-weight: 500;
    color: var(--st-text);
    letter-spacing: -0.01em;
    margin: 6px 0 8px;
}
.kpi-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 8px;
}
.chip {
    display: inline-flex;
    align-items: center;
    font-family: var(--st-mono);
    font-size: 11px;
    padding: 2px 8px;
    border-radius: 4px;
}
.chip-crit {
    background: var(--st-bad-bg);
    color: var(--st-bad);
}
.chip-warn {
    background: var(--st-warn-bg);
    color: var(--st-warn);
}
.chip-ok {
    background: var(--st-good-bg);
    color: var(--st-good);
}
.chip-info {
    background: var(--st-accent-bg);
    color: var(--st-accent);
}

figure.chart {
    margin: 0;
    padding: 0;
    background: transparent;
    border: 0;
}
figure.chart figcaption {
    font-size: 13px;
    color: var(--st-text);
    margin-bottom: 8px;
    display: flex;
    align-items: baseline;
    gap: 8px;
}
figure.chart figcaption .chart-meta {
    font-family: var(--st-mono);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--st-text-muted);
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
.st-skip-link {
    position: absolute;
    left: -9999px;
    top: 8px;
    padding: 8px 12px;
    background: var(--st-accent);
    color: var(--st-on-accent);
    font-weight: 500;
    border-radius: 6px;
    z-index: 1000;
}
.st-skip-link:focus {
    left: 8px;
}

/* ── Field-assignment ("Felt-montering") — mobile, big touch targets ── */
.st-field-search {
    width: 100%;
    padding: 14px 16px;
    margin-bottom: 14px;
    font: inherit;
    font-size: 16px; /* ≥16px so iOS doesn't zoom the viewport on focus */
    color: var(--st-text);
    border: 1px solid var(--st-border-strong);
    border-radius: var(--st-radius);
    background: var(--st-card);
}
.st-field-search:focus {
    outline: none;
    border-color: var(--st-accent);
    box-shadow: 0 0 0 3px var(--st-accent-bg);
}

.st-tap-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.st-tap-link {
    display: block;
    min-height: 56px;
    padding: 14px 16px;
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow: var(--st-shadow-sm);
    text-decoration: none;
    color: var(--st-text);
}
.st-tap-link:hover {
    border-color: var(--st-accent-soft);
    text-decoration: none;
}
/* Same box as .st-tap-link but static — the device identity card atop the
   assign panel (no tap target, no hover). */
.st-tap-static {
    padding: 14px 16px;
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow: var(--st-shadow-sm);
}
.st-tap-main {
    display: block;
    font-size: 18px;
    font-weight: 600;
}
.st-tap-sub {
    display: block;
    font-size: 14px;
    color: var(--st-text);
    margin-top: 2px;
}
.st-tap-meta {
    display: block;
    font-size: 12px;
    color: var(--st-text-muted);
    margin-top: 4px;
}

/* Panel labels: section-level, not the uppercase .st-form label treatment. */
.st-field-form .st-field-label {
    display: block;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    color: var(--st-text);
    margin-bottom: 8px;
}
.st-field-select {
    width: 100%;
}
.st-field-select option {
    padding: 10px 8px;
    font-size: 15px;
}

/* Grouped sub-sections on the field form. The left-border colour distinguishes
   the meter coming down (amber/warn) from the one going up (green/good). */
.st-field-group {
    margin: 0 0 14px;
    padding: 14px 16px 4px;
    border: 1px solid var(--st-border);
    border-left-width: 4px;
    border-radius: var(--st-radius);
}
.st-field-group > legend {
    padding: 0 6px;
    margin-left: -6px;
}
.st-field-group > div {
    margin-bottom: 14px;
}
.st-field-group--old {
    border-left-color: var(--st-warn);
    background: var(--st-warn-bg);
}
.st-field-group--new {
    border-left-color: var(--st-good);
    background: var(--st-good-bg);
}

/* Filterable unit picker (replaces the native <select> — iOS can't hide
   options). A scrollable list of full-width tappable rows. */
.st-unit-picker {
    margin-top: 8px;
    max-height: 320px;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}
.st-unit-option {
    display: block;
    width: 100%;
    text-align: left;
    min-height: 56px;
    padding: 12px 16px;
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    box-shadow: var(--st-shadow-sm);
    color: var(--st-text);
    font: inherit;
    cursor: pointer;
}
.st-unit-option[hidden] { display: none; }
.st-unit-option:hover { border-color: var(--st-accent-soft); }
.st-unit-option.is-selected {
    border-color: var(--st-accent);
    box-shadow: 0 0 0 2px var(--st-accent-soft);
}
.st-tap-empty {
    padding: 14px 16px;
    color: var(--st-text-muted);
    font-size: 14px;
}

.st-kind-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.st-kind-button {
    /* Reset the inherited .st-form label styling for these chips. */
    display: inline-flex;
    align-items: center;
    margin: 0;
    padding: 0;
    text-transform: none;
    letter-spacing: 0;
}
.st-kind-button input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.st-kind-button span {
    display: inline-block;
    padding: 12px 18px;
    font-size: 16px;
    font-weight: 600;
    border: 1px solid var(--st-border-strong);
    border-radius: var(--st-radius);
    background: var(--st-card);
    color: var(--st-text);
    cursor: pointer;
}
.st-kind-button input:checked + span {
    background: var(--st-accent);
    color: var(--st-on-accent);
    border-color: var(--st-accent);
}
.st-kind-button input:focus-visible + span {
    outline: 2px solid var(--st-accent);
    outline-offset: 2px;
}

.st-capture-btn {
    /* A big, friendly camera button (the real file input is .sr-only). */
    display: block;
    text-align: center;
    padding: 16px;
    font-size: 16px;
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
    color: var(--st-accent);
    background: var(--st-accent-bg);
    border: 1px dashed var(--st-accent-soft);
    border-radius: var(--st-radius);
    cursor: pointer;
}
/* Confirmed-capture state: solid green so a taken photo reads at a glance. */
.st-capture-btn.has-photo {
    color: var(--st-good);
    background: var(--st-good-bg);
    border-style: solid;
    border-color: var(--st-good);
}
.st-photo-preview {
    display: block;
    margin-bottom: 10px;
    width: 100%;
    max-height: 220px;
    object-fit: cover;
    border-radius: var(--st-radius);
    border: 1px solid var(--st-border);
}
/* An author display rule outranks the UA [hidden] rule, so restore it — else
   the empty <img> shows as a broken thumbnail before a photo is captured. */
.st-photo-preview[hidden] {
    display: none;
}
.st-photo-reset {
    display: inline-block;
    margin-top: 8px;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 600;
    color: var(--st-text-muted);
    background: transparent;
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    cursor: pointer;
}
/* Author display rule outranks the UA [hidden] rule — restore it so the reset
   button stays gone until a photo is captured. */
.st-photo-reset[hidden] {
    display: none;
}
.st-field-hint {
    display: block;
    margin-top: 8px;
    font-size: 13px;
    color: var(--st-text-muted);
}
/* Compact number/date inputs inside settlement tables (batch + inline edit).
   A fixed width — inside an auto-layout table `width: 100%` collapses a number
   input to its tiny intrinsic size. */
/* !important beats the global `form:not(.st-form) input[type=number] { width:
   100% }` rule (higher specificity), which otherwise collapses these inputs to
   nothing inside an auto-layout table. */
.st-inline-input {
    width: 140px !important;
    padding: 8px 10px;
    font-size: 16px;
    color: var(--st-text);
    background: var(--st-card);
    border: 1px solid var(--st-border-strong);
    border-radius: var(--st-radius);
}
.st-inline-input[type="date"] {
    width: 170px !important;
}
/* Discrete click-to-edit cell input: borderless (just an accent underline) so it
   reads as inline text, not a boxed form field. Save via the ✓ button / Enter;
   clicking away keeps the typed value (settlement_edit.js). */
.st-cell-input {
    width: 84px !important;
    /* !important beats the global form-input rule (border/padding/radius). */
    padding: 2px 2px !important;
    font: inherit;
    font-size: 14px;
    text-align: right;
    color: var(--st-text);
    background: transparent !important;
    border: none !important;
    border-bottom: 1px solid var(--st-accent) !important;
    border-radius: 0 !important;
    box-shadow: none !important;
}
.st-cell-input:focus {
    outline: none;
    border-bottom-width: 2px;
}
.st-cell-input[type="date"] {
    width: 120px !important;
    text-align: left;
}
.st-cell-save {
    border: none;
    background: transparent;
    color: var(--st-good);
    cursor: pointer;
    font-size: 15px;
    font-weight: 700;
    line-height: 1;
    padding: 0 2px;
}
.st-cell-unsaved {
    display: block;
    margin-top: 3px;
    font-size: 11px;
    color: var(--st-warn);
}
/* Settlement bolig timeline. */
.st-timeline {
    list-style: none;
    margin: 0;
    padding: 0;
}
.st-timeline-item {
    display: flex;
    gap: 16px;
    padding: 12px 18px;
    border-bottom: 1px solid var(--st-border);
}
.st-timeline-item:last-child {
    border-bottom: none;
}
.st-timeline-date {
    min-width: 120px;
    flex-shrink: 0;
    color: var(--st-text-muted);
    font-size: 12px;
    padding-top: 2px;
}
.st-inline-input:focus {
    outline: none;
    border-color: var(--st-accent);
    box-shadow: 0 0 0 3px var(--st-accent-bg);
}
.st-field-coords {
    display: flex;
    gap: 8px;
    margin-top: 10px;
}
.st-field-coords input {
    flex: 1;
    min-width: 0;
}
.st-field-actions {
    margin-top: 4px;
}
.st-btn-block {
    display: block;
    width: 100%;
    text-align: center;
}

/* ── Miljø section (environmental sensor tiles + sparklines) ── */
.aq-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(218px, 1fr));
    gap: 14px;
}
/* Phone: two columns (full tile design still fits comfortably at 2-up). */
@media (max-width: 600px) {
    .aq-card-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 10px;
    }
}
a.aq-metric {
    text-decoration: none;
    color: inherit;
}
.aq-metric {
    background: var(--st-card-soft);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius);
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    transition:
        border-color 0.12s ease,
        box-shadow 0.12s ease;
}
a.aq-metric:hover {
    border-color: var(--st-accent-soft);
    box-shadow: var(--st-shadow-sm);
}
a.aq-metric:focus-visible {
    outline: 2px solid var(--st-accent);
    outline-offset: 2px;
}
.aq-metric-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.aq-metric-label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--st-text-muted);
}
.aq-metric-value {
    font-size: 26px;
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1;
    color: var(--st-text);
}
.aq-metric-unit {
    font-size: 14px;
    color: var(--st-text-muted);
    margin-left: 3px;
    font-weight: 400;
}
.aq-spark {
    width: 100%;
    height: 44px;
    display: block;
}
.aq-metric-foot {
    font-size: 11px;
    color: var(--st-text-soft);
}
/* Header shown only in the inline-chart view (metric name + date picker). */
.aq-section-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}
@media (prefers-reduced-motion: reduce) {
    .aq-metric {
        transition: none;
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Custom-dashboard widgets + editor. Server-rendered widget cards on a
   column-span grid; the editor adds drag/resize/config chrome in edit mode. */
/* Fixed square-cell grid: one size unit = one CELL on BOTH axes, so a widget is
   always a whole number of identical squares. That is what makes a 1×1 an
   actual square and makes every row line up automatically (each card fills its
   cell span exactly). --wc-max-cols caps a widget's width to the columns that
   fit, so a wide widget never overflows on a narrow screen. */
.wc-grid {
    display: grid;
    --wc-cell: 168px;
    /* minmax: same column count as fixed cells, but tracks stretch to fill
       the row, so the board has no dead right margin. Rows stay fixed. */
    grid-template-columns: repeat(auto-fill, minmax(var(--wc-cell), 1fr));
    grid-auto-rows: var(--wc-cell);
    gap: 14px;
    justify-content: start; /* flugter med sidens overskrift — ikke centreret */
    align-items: stretch; /* each slot fills its cell span so all rows line up */
}
@media (max-width: 1100px) { .wc-grid { --wc-cell: 150px; --wc-max-cols: 5; } }
@media (max-width: 640px)  { .wc-grid { --wc-cell: 140px; --wc-max-cols: 2; } }

.w-slot {
    position: relative; min-width: 0;
    grid-column: span var(--w-cols, 2);
    grid-row: span var(--w-rows, 1);
    container-type: inline-size;
}
/* Small screens: cap a widget's width to the columns that fit so it can't
   overflow. min() confined to the media query keeps the base rule maximally
   compatible. */
@media (max-width: 1100px) { .w-slot { grid-column: span min(var(--w-cols, 2), var(--wc-max-cols, 5)); } }
.w-card > .aq-spark { flex: 1 1 auto; min-height: 34px; }
/* Touch på selve graffladen scroller siden (grafer panoreres ikke). */
.st-chart-wrap { touch-action: pan-y; }
/* Progressive density — narrow widgets keep the essentials (icon · value · graph). */
@container (max-width: 200px) {
    .w-head .st-pill { display: none; }
}
@container (max-width: 132px) {
    .w-name { display: none; }
    .w-card { padding: 9px; gap: 6px; }
    .w-value { font-size: 19px; }
    .w-icon { width: 26px; height: 26px; }
    .w-gauge-svg { max-width: 100%; height: auto; }
}
.w-trend-spark { flex: 1 1 auto; display: flex; }
.w-trend-spark .aq-spark { flex: 1; height: 100%; min-height: 56px; }

/* Editable dashboard name in the editor header. */
.w-name-input {
    font: inherit; font-size: 24px; font-weight: 650; letter-spacing: -0.02em;
    color: var(--st-text); background: transparent; border: none;
    border-bottom: 1.5px dashed var(--st-border-strong); padding: 2px; width: min(440px, 70vw);
}
.w-name-input:focus { outline: none; border-bottom-color: var(--st-accent); }

/* Per-dashboard card accent — a coloured top bar on each widget. */
.wc-grid[data-accent] .w-card { border-top: 3px solid var(--w-accent); }
/* Per-widget topbar-farve (binding "accent"): et tonet titel-bånd øverst i
   kortet (som gruppe-widgettens gamle grønne hoved), ikke en streg. Båndet
   trækkes ud til kortets kanter og runder topkanterne med. */
.w-slot[data-w-accent] .w-card > .w-head {
    background: color-mix(in srgb, var(--w-accent) 14%, transparent);
    margin: -14px -14px 0;
    padding: 12px 14px;
    border-radius: 11px 11px 0 0;
}
.w-slot[data-w-accent] .w-card-pad > .w-head {
    background: color-mix(in srgb, var(--w-accent) 14%, transparent);
    margin: -14px -14px 10px;
    padding: 12px 14px;
    border-radius: 11px 11px 0 0;
}
.w-slot[data-w-accent] .w-map-head {
    background: color-mix(in srgb, var(--w-accent) 14%, transparent);
}

/* Card-accent colour control in the editor header. */
.w-topbar-color {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 12px; color: var(--st-text-soft);
    border: 1px solid var(--st-border); border-radius: 8px; padding: 4px 8px;
}
.w-topbar-color input[type="color"] {
    width: 24px; height: 24px; padding: 0; border: none; background: none; cursor: pointer;
}
.w-topbar-reset {
    font: inherit; font-size: 11px; color: var(--st-text-soft);
    background: none; border: none; cursor: pointer; text-decoration: underline;
}
.w-topbar-reset:hover { color: var(--st-text); }

.w-card {
    background: var(--st-card);
    border: 1px solid var(--st-border);
    border-radius: 11px;
    box-shadow: var(--st-shadow-sm);
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    box-sizing: border-box;
    height: 100%;      /* fill the widget's square-cell span exactly */
    overflow: hidden;  /* content is sized to the cell; never spill past it */
}
.w-card-center { align-items: center; gap: 8px; }
.w-card-flush { padding: 0; overflow: hidden; gap: 0; }
.w-card-pad { padding: 14px; }

.w-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.w-head-l { display: flex; gap: 10px; align-items: center; min-width: 0; }
.w-head-r { text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.w-icon {
    width: 32px; height: 32px; border-radius: 9px; flex-shrink: 0;
    background: var(--st-card-soft);
    display: inline-flex; align-items: center; justify-content: center;
}
.w-icon-inline { display: inline-flex; align-items: center; }
.w-name { font-size: 14px; font-weight: 650; color: var(--st-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.w-sub { font-size: 12px; color: var(--st-text-soft); }
.w-alarm-link { margin-top: auto; padding-top: 8px; align-self: flex-start; font-size: 12px; }
.w-value { display: flex; align-items: baseline; gap: 4px; font-size: 26px; font-weight: 650; letter-spacing: -0.02em; line-height: 1; color: var(--st-text); }
.w-value-sm { font-size: 20px; }
.w-unit { font-size: 13px; color: var(--st-text-muted); font-weight: 500; }
.w-empty { font-size: 12px; color: var(--st-text-soft); padding: 6px 0; }

/* Gauge */
.w-gauge { position: relative; margin-top: 2px; }
.w-gauge-svg { display: block; }
/* Værdi og enhed stablet på hver sin linje, centreret — samme størrelser. */
.w-gauge-value { position: absolute; bottom: 2px; left: 0; right: 0; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 0; font-size: 22px; font-weight: 650; line-height: 1.1; text-align: center; }
.w-gauge-bounds { display: flex; justify-content: space-between; width: 100%; font-size: 11px; color: var(--st-text-soft); margin-top: -4px; }
/* The card already fills its cell; the gauge just lets the arc grow into the
   space left after the head and the min/max bounds (scaling via the SVG
   viewBox), so it looks right at 1×1 and at larger sizes alike. */
.w-card-gauge .w-gauge { flex: 1 1 auto; min-height: 0; width: 100%; display: flex; align-items: center; justify-content: center; }
.w-card-gauge .w-gauge-svg { width: 100%; height: 100%; }

/* Consumption comparison */
.w-cmp { display: flex; align-items: center; gap: 8px; }
/* The consumption card stacks head + value + bars + comparison; tighten the gap
   and let the bars flex small so it all fits a single (1-cell-tall) row without
   clipping the comparison chip. */
.w-card-consumption { gap: 8px; }
/* Forbrug: stor centreret værdi med perioden ("august 2026") nedenunder. */
/* Værdien centreres både vandret og lodret; enheden hænges på EFTER tallet
   (absolut ud fra værdien), så den ikke skubber centreringen. */
.w-consumption-center { flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; text-align: center; }
.w-value-num { position: relative; }
.w-consumption-period { text-align: center; }
.w-unit-after { position: absolute; left: 100%; bottom: 6px; margin-left: 5px; white-space: nowrap; }
.w-value-lg { font-size: 40px; line-height: 1.05; }
.w-card-consumption > .aq-spark { min-height: 22px; }
.w-cmp-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; }
.w-cmp-chip.up { background: var(--st-warn-bg); color: var(--st-warn); }
.w-cmp-chip.down { background: var(--st-good-bg); color: var(--st-good); }

/* Trend */
.w-trend-spark { margin-top: 2px; }
.w-trend-spark .aq-spark { height: 60px; }
.w-trend-axis { display: flex; justify-content: space-between; font-size: 11px; color: var(--st-text-soft); }

/* Group / verdict */
.w-group-head { display: flex; align-items: center; gap: 11px; padding: 14px 16px; }
.w-group-good { background: var(--st-good-bg); }
.w-group-warn { background: var(--st-warn-bg); }
.w-group-bad { background: var(--st-bad-bg); }
.w-group-neutral { background: var(--st-card-soft); }
.w-group-check { width: 34px; height: 34px; border-radius: 50%; background: #fff; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.w-group-cat { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--st-text-muted); }
.w-group-verdict { font-size: 15px; font-weight: 650; color: var(--st-text); }
/* Rows scroll inside the widget when the unit has more metrics than the
   chosen widget height fits (flex child needs min-height: 0 to shrink). */
.w-group-rows { padding: 0; flex: 1 1 auto; min-height: 0; overflow-y: auto; }
/* I flush-kort (liste-widgetten) bærer rækkerne selv side-padding. */
.w-card-flush .w-group-rows { padding: 4px 16px 8px; }
.w-group-row { display: flex; justify-content: space-between; align-items: center; padding: 9px 0; border-bottom: 1px solid var(--st-border); }
.w-group-row:last-child { border-bottom: none; }
.w-group-name { font-size: 14px; color: var(--st-text-muted); }
.w-group-val { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 600; }
.w-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }

/* Compact (XS) */
.w-compact { gap: 10px; }
.w-compact-top { display: flex; justify-content: space-between; align-items: center; }

/* Map widget */
.w-map { height: 100%; }
.w-map-head { padding: 12px 14px 6px; }
.w-map .st-leaflet { border-radius: 0; }

/* ── Editor chrome (only visible in .wc-grid.editing) ── */
/* Widget links are inert while editing so drag / select / resize never
   navigate away. */
.wc-grid.editing .w-card a { pointer-events: none; }
.wc-grid.editing .w-slot { outline: 1.5px dashed var(--st-border-strong); outline-offset: 6px; border-radius: 14px; }
.wc-grid.editing .w-slot:hover { outline-color: var(--st-accent-soft); }
.w-slot.dragging { opacity: 0.4; }
.w-slot.drop-before::before { content: ""; position: absolute; left: -10px; top: 0; bottom: 0; width: 3px; border-radius: 2px; background: var(--st-accent); }
.w-drag, .w-bar { display: none; }
.wc-grid.editing .w-drag {
    display: flex; position: absolute; top: -14px; left: 8px; z-index: 3;
    width: 30px; height: 30px; border-radius: 999px; background: var(--st-card);
    border: 1px solid var(--st-border); box-shadow: var(--st-shadow-sm);
    align-items: center; justify-content: center; cursor: grab; color: var(--st-text-soft);
}
.wc-grid.editing .w-bar {
    display: flex; position: absolute; top: -14px; right: 8px; z-index: 3; gap: 4px;
    background: var(--st-card); border: 1px solid var(--st-border); border-radius: 999px;
    padding: 3px; box-shadow: var(--st-shadow-sm);
}
.w-seg { display: inline-flex; background: var(--st-card-soft); border-radius: 999px; padding: 2px; }
.w-seg button { border: none; background: transparent; font: inherit; font-size: 11px; font-weight: 700; min-width: 24px; height: 24px; padding: 0 6px; border-radius: 999px; cursor: pointer; color: var(--st-text-soft); }
.w-seg button.on { background: var(--st-card); color: var(--st-accent); box-shadow: var(--st-shadow-sm); }
.w-ic { width: 30px; height: 30px; border: none; background: transparent; border-radius: 999px; cursor: pointer; color: var(--st-text-muted); display: inline-flex; align-items: center; justify-content: center; font-size: 14px; }
.w-ic:hover { background: var(--st-card-soft); color: var(--st-text); }
.w-ic.danger:hover { background: var(--st-bad-bg); color: var(--st-bad); }
/* Larger tap targets for editor chrome on touch screens. */
@media (max-width: 560px) {
    .wc-grid.editing .w-drag,
    .w-ic { width: 36px; height: 36px; }
    .w-seg button { min-width: 30px; height: 30px; }
}

.w-empty-canvas {
    border: 1.5px dashed var(--st-border-strong); border-radius: 12px;
    padding: 40px 20px; text-align: center; color: var(--st-text-muted);
    background: #fcfbf8; margin-top: 16px;
}
.w-drag { touch-action: none; }
/* Corner drag-resize grip (edit mode) — snaps width to columns, height is free. */
.w-resize { display: none; }
.wc-grid.editing .w-resize {
    display: flex; position: absolute; right: 3px; bottom: 3px; z-index: 3;
    width: 18px; height: 18px; align-items: center; justify-content: center;
    cursor: nwse-resize; touch-action: none; color: var(--st-text-soft);
    border-radius: 5px;
}
.wc-grid.editing .w-resize:hover { color: var(--st-accent); background: var(--st-card-soft); }
.w-slot.resizing { outline-color: var(--st-accent) !important; }

/* Inline link-style buttons (index row actions). */
.st-inline-form { display: inline; margin: 0; }
.st-linkbtn { background: none; border: none; padding: 0; font: inherit; color: var(--st-accent); cursor: pointer; }
.st-linkbtn:hover { text-decoration: underline; }
.st-linkbtn.danger { color: var(--st-bad); }

/* Catalog + config drawer (side sheet on desktop, bottom sheet on phones). */
.w-drawer { position: fixed; inset: 0; z-index: 60; background: rgba(28,32,40,0.35); display: flex; justify-content: flex-end; }
.w-drawer[hidden] { display: none; }
.w-drawer-panel {
    width: 360px; max-width: 92vw; height: 100%; background: var(--st-bg);
    box-shadow: -8px 0 32px rgba(20,25,35,0.2); display: flex; flex-direction: column;
    animation: w-slide-in 0.16s ease;
}
.w-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px; border-bottom: 1px solid var(--st-border); }
.w-drawer-head h2 { margin: 0; font-size: 16px; font-weight: 650; }
.w-drawer-body { padding: 16px 18px; overflow-y: auto; }
.w-drawer-body select[multiple] { max-height: 48vh; }
/* Sensor-links i widgets: diskrete — arver tekstfarven, viser sig ved hover. */
.w-sub-link { color: inherit; text-decoration: none; }
.w-sub-link:hover { color: var(--st-accent); text-decoration: underline; }
.w-drawer-body input[type="color"] { width: 44px; height: 28px; padding: 2px; border: 1px solid var(--st-border-strong); border-radius: 6px; background: var(--st-card); cursor: pointer; }
.w-cat-item {
    display: block; width: 100%; text-align: left; background: var(--st-card);
    border: 1px solid var(--st-border); border-radius: 12px; padding: 12px 14px;
    margin-bottom: 10px; cursor: pointer; font: inherit;
}
.w-cat-item:hover { border-color: var(--st-accent-soft); box-shadow: var(--st-shadow-sm); }
.w-cat-name { display: block; font-size: 14px; font-weight: 650; }
.w-cat-desc { display: block; font-size: 12px; color: var(--st-text-muted); margin-top: 2px; }
.w-field { margin-bottom: 15px; }
.w-field label { display: block; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--st-text-muted); margin-bottom: 6px; }
.w-field select, .w-field input { width: 100%; font: inherit; font-size: 14px; height: 38px; padding: 0 10px; border: 1px solid var(--st-border-strong); border-radius: 9px; background: var(--st-card); color: var(--st-text); }
@keyframes w-slide-in { from { transform: translateX(14px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (max-width: 560px) {
    .w-drawer { justify-content: stretch; align-items: flex-end; }
    .w-drawer-panel { width: 100%; max-width: 100%; height: auto; max-height: 88vh; border-radius: 16px 16px 0 0; animation: w-slide-up 0.18s ease; }
}
@keyframes w-slide-up { from { transform: translateY(20px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .w-drawer-panel { animation: none; } }

/* ── Liste widget ── */
.w-list-type {
    display: inline-block; margin-left: 6px; font-size: 11px;
    color: var(--st-text-soft); font-weight: 400;
}
@container (max-width: 240px) { .w-card .w-list-type { display: none; } }

/* ── Alarm widget: which sensors alarm ── */
.w-alarm-points {
    list-style: none; margin: 6px 0 0; padding: 0;
    font-size: 12px; color: var(--st-text-soft);
}
.w-alarm-points li { padding: 2px 0; border-top: 1px solid var(--st-border-soft, var(--st-border)); }
@container (max-width: 220px) { .w-card .w-alarm-points { display: none; } }

/* ── "Alle dashboards": preview cards instead of a list ── */
.wc-cards {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 14px;
}
.wc-card { padding: 0; overflow: hidden; }
.wc-mini-link { display: block; }
.wc-mini {
    display: grid; grid-template-columns: repeat(12, 1fr); gap: 3px;
    align-items: start; align-content: start;
    padding: 10px; height: 120px; overflow: hidden;
    background: var(--st-bg); border-bottom: 1px solid var(--st-border);
}
.wc-mini-block {
    display: block; border-radius: 3px;
    background: var(--st-card); border: 1px solid var(--st-border);
    border-top: 2px solid var(--wc-mini-accent, var(--st-accent));
}
.wc-mini-alarm { border-top-color: var(--st-bad); }
.wc-mini-map   { background: #eef2ec; }
.wc-mini-empty { grid-column: span 12; font-size: 12px; color: var(--st-text-soft); }
.wc-card-body { padding: 12px 14px; }
.wc-card-title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-weight: 600; }
.wc-card-actions { margin-top: 8px; font-size: 13px; }

/* ── Open-alarm dot in sensor lists ── */
.st-alarm-tri {
    margin-left: 6px; vertical-align: middle; fill: currentColor;
}
.st-alarm-tri.warn { color: var(--st-warn, #b3782c); }
.st-alarm-tri.bad  { color: var(--st-bad); }
.st-alarm-search { margin: 0 0 12px; max-width: 380px; }
.st-alarm-search input { width: 100%; }

/* ── Vandindsigt-widget (LLM-analyse) ─────────────────────────────────── */
.w-card-insight { display: flex; flex-direction: column; }
.w-insight-rows { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
.w-insight-rows li { display: flex; justify-content: space-between; gap: 10px; font-size: 14px; }
.w-insight-num { font-weight: 600; font-variant-numeric: tabular-nums; }
.w-insight-avg { font-weight: 400; color: var(--st-text-soft); }
.w-insight-notes { list-style: none; margin: 10px 0 0; padding: 8px 10px; border-radius: 8px;
                   background: var(--st-card-soft);
                   display: grid; gap: 4px; font-size: 12.5px; color: var(--st-text-soft); }
.w-insight-notes li.warn { color: var(--st-warn); }
.w-insight-foot { margin-top: auto; padding-top: 10px; }
