:root {
  --bg: #0f1419;
  --bg-2: #161d26;
  --card: #1b2430;
  --card-2: #212c3a;
  --border: #2a3848;
  --text: #e6edf3;
  --muted: #8c9bab;
  --accent: #4ea1ff;
  --good: #3fb950;
  --warn: #d29922;
  --bad: #f85149;
  --grid: #58a6ff;
  --pv: #f2cc60;
  --ess: #56d364;
  --load: #db61a2;
  --flow: #2dd4bf;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
}

.device-select { color: var(--muted); font-size: 12px; display: flex; flex-direction: column; gap: 3px; }
.device-select[hidden] { display: none; }
select {
  background: var(--card); color: var(--text); border: 1px solid var(--border);
  border-radius: 7px; padding: 6px 10px; font-size: 14px; min-width: 160px;
}
.conn { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--muted); }
.dot { width: 10px; height: 10px; border-radius: 50%; background: var(--muted); }
.dot.ok { background: var(--good); box-shadow: 0 0 8px var(--good); }
.dot.bad { background: var(--bad); box-shadow: 0 0 8px var(--bad); }

/* Circle-based navigation. The overview is the home menu (utility circles in
   .ov-nav); every other screen shows the shared back circle in #page-nav. */
.ov-nav { display: flex; gap: 22px; justify-content: center; flex-wrap: wrap; margin-top: 40px; }
#page-nav { margin-bottom: 14px; }
#page-nav[hidden] { display: none; }
.nav-circle {
  display: inline-flex; flex-direction: column; align-items: center; gap: 7px;
  background: none; border: none; cursor: pointer; padding: 0;
  color: var(--muted); font-size: 12px; font-weight: 500;
}
.nav-ico { position: relative; display: inline-flex; }
.nav-ico svg {
  width: 56px; height: 56px; padding: 15px; box-sizing: border-box;
  background: var(--card); border: 1.5px solid var(--border); border-radius: 50%;
  color: var(--text); transition: border-color .15s, color .15s;
}
.nav-circle:hover { color: var(--text); }
.nav-circle:hover .nav-ico svg { border-color: var(--accent); color: var(--accent); }
.nav-circle.back .nav-ico svg { width: 46px; height: 46px; padding: 12px; }
.badge {
  position: absolute; top: -3px; right: -3px;
  background: var(--bad); color: #fff; border-radius: 10px; padding: 1px 7px; font-size: 11px;
  font-weight: 600; line-height: 1.5;
}

main { padding: 20px; max-width: 1200px; margin: 0 auto; }

.tiles {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  margin-bottom: 20px;
}
.tile {
  background: var(--card); border: 1px solid var(--border); border-radius: 12px;
  padding: 16px; display: flex; flex-direction: column; gap: 6px;
}
.tile .label { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .4px; }
.tile .value { font-size: 26px; font-weight: 600; font-variant-numeric: tabular-nums; }
.tile .value .unit { font-size: 14px; color: var(--muted); margin-left: 4px; font-weight: 400; }
.tile.status .value { font-size: 18px; }
.tile .value.pos { color: var(--good); }
.tile .value.neg { color: var(--accent); }

.charts { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.charts.single { grid-template-columns: 1fr; }
.chart-card.wide { grid-column: 1 / -1; }
.dash-blank {
  display: flex; align-items: center; justify-content: center;
  min-height: 200px; border: 1px dashed var(--border); border-radius: 12px;
}
.temp-card { margin-top: 14px; }
.temp-card .card-sub { color: var(--muted); font-weight: 400; font-size: 12px; }
.dash-toolbar {
  display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap;
  gap: 16px; margin-bottom: 14px;
}
@media (max-width: 800px) { .charts { grid-template-columns: 1fr; } }
.card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 16px; }
.card h3 { margin: 0 0 12px; font-size: 14px; font-weight: 600; color: var(--muted); }
.chart-box { position: relative; height: 260px; }
/* The Solar dashboard's Plotly charts set their own height on the inner .plot
   (and need room below for the legend), so let the box wrap the plot instead of
   clipping it to 260px — otherwise the legend overflows onto the next card. */
#tab-solar .chart-box { height: auto; }

/* Bottom-align so the device select, filter and checkbox line up in a tidy row
   despite the "Device" label sitting above the select. */
.toolbar { display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 16px; }
/* Every toolbar control fills its label wrapper at a matching height, so the row
   lines up cleanly. Label wrappers size themselves; Filter grows to fill. */
.toolbar .device-select select,
.toolbar #reg-filter,
.toolbar .reg-time-lbl input {
  width: 100%; background: var(--card); border: 1px solid var(--border); color: var(--text);
  border-radius: 8px; padding: 9px 12px; font-size: 14px; min-width: 0; line-height: 1.35;
}
/* Native selects ignore vertical padding (shorter than the inputs); drop the
   native chrome and add a caret so their height matches Filter/History. */
.toolbar .device-select select {
  appearance: none; -webkit-appearance: none; padding-right: 30px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%238c9bab' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center;
}
.toolbar .device-select { flex: 0 0 auto; min-width: 170px; }
.toolbar .reg-filter-lbl { flex: 1 1 220px; }
.reg-time-lbl input[type="datetime-local"] {
  background: var(--card); border: 1px solid var(--border); color: var(--text);
  border-radius: 8px; padding: 8px 10px; font-size: 13px;
}
/* Historic-snapshot banner: makes it obvious the table is pinned to a past time. */
.reg-hist-banner {
  color: var(--warn); background: rgba(210, 153, 34, .08);
  border: 1px solid rgba(210, 153, 34, .4); border-radius: 8px;
  padding: 8px 12px; margin: 0 0 16px;
}
.reg-hist-banner[hidden] { display: none; }
#reg-live { color: var(--good); border-color: rgba(63, 185, 80, .45); }

/* Register rows are clickable to open a per-register history chart. */
.reg-group tbody tr[data-key] { cursor: pointer; }
.reg-group tbody tr[data-key]:hover { background: var(--card-2); }

/* Modal (per-register history chart). */
.modal {
  position: fixed; inset: 0; z-index: 50; background: rgba(0, 0, 0, .55);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.modal[hidden] { display: none; }
.modal-card {
  background: var(--card); border: 1px solid var(--border); border-radius: 12px;
  width: min(720px, 94vw); padding: 16px; box-shadow: 0 10px 40px rgba(0, 0, 0, .5);
}
.modal-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.modal-head #reg-chart-title, .modal-head #reg-write-title { font-weight: 600; flex: 1; }
.modal-chart { height: 300px; position: relative; }

/* Seasonal load-curves viewer: a wide scrollable modal with a 4-season grid. */
.curve-modal-card { width: min(1100px, 96vw); max-height: 90vh; overflow-y: auto; }
.curve-modal-card .modal-head > span:first-child { font-weight: 600; }
.curve-modal-card .modal-head .spacer { flex: 1; }
/* One row per season: a label column + weekday + weekend chart columns. */
.curve-grid { display: grid; grid-template-columns: max-content 1fr 1fr; gap: 12px 16px; align-items: center; }
.curve-season strong { display: block; font-size: 15px; color: var(--text); }
.curve-season .meta { font-size: 12px; color: var(--muted); }
.curve-cell { background: var(--bg-2); border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px 6px; }
.curve-cell-title { text-align: center; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
/* Completeness bar: fraction of the season's days that have recorded data. */
.curve-complete { height: 3px; border-radius: 2px; background: var(--border); margin: 0 0 6px; overflow: hidden; }
.curve-complete span { display: block; height: 100%; background: #ffb454; border-radius: 2px; }
.curve-canvas-wrap { height: 150px; position: relative; }
.curve-nodata {
  height: 168px; display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: 13px;
}
.curve-year { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); margin-right: 8px; }
.curve-year select { min-width: 84px; }
.curve-legend { display: flex; gap: 18px; margin: 2px 0 10px; font-size: 12px; color: var(--muted); }
.curve-key { display: inline-flex; align-items: center; gap: 6px; }
.curve-key .swatch { width: 18px; height: 0; border-top-width: 2px; border-top-style: solid; }
.curve-key .swatch.primary { border-top-color: #4ea1ff; }
.curve-key .swatch.recorded { border-top-style: dashed; border-top-color: #ffb454; }
@media (max-width: 700px) {
  .curve-grid { grid-template-columns: 1fr; }  /* stack label + both charts on phones */
  .curve-season { margin-top: 6px; }
}

/* Register edit affordance — only shown when register writes are enabled. */
/* Fixed-width slot before the value so the edit icons form a uniform column and
   values stay aligned (empty slot on read-only rows). Only shown when writes on. */
.reg-edit-slot { display: none; }
.can-write .reg-edit-slot { display: inline-block; width: 24px; margin-right: 6px; }
/* No editing while pinned to a historic snapshot — you'd be sending against old data. */
#register-groups.historic .reg-edit-slot { display: none; }
/* Retention/Poll/Store are live config, not part of a point-in-time snapshot. */
#register-groups.historic .col-retention { display: none; }
.reg-edit {
  padding: 0 5px; cursor: pointer; font-size: 13px;
  background: none; border: 1px solid var(--border); border-radius: 6px; color: var(--muted);
}
.reg-edit:hover { border-color: var(--accent); color: var(--accent); }
.reg-write-meta { color: var(--muted); font-size: 13px; margin: 0 0 12px; }
.reg-write-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; color: var(--muted); }
.reg-write-field input, .reg-write-field select {
  background: var(--card-2); border: 1px solid var(--border); color: var(--text);
  border-radius: 8px; padding: 9px 12px; font-size: 15px;
}
.reg-write-preview { font-size: 14px; color: var(--text); min-height: 20px; margin: 0 0 14px; }
.reg-write-hex { color: var(--muted); }
.reg-write-actions { display: flex; align-items: center; gap: 10px; justify-content: flex-end; }
.reg-write-warn { color: var(--warn); font-size: 13px; margin-right: auto; }

.reg-group { margin-bottom: 22px; }
.reg-group h3 {
  font-size: 13px; text-transform: uppercase; letter-spacing: .5px; color: var(--accent);
  margin: 0 0 8px; border-bottom: 1px solid var(--border); padding-bottom: 6px;
}
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 7px 10px; font-size: 13px; border-bottom: 1px solid var(--border); }
th { color: var(--muted); font-weight: 500; font-size: 11px; text-transform: uppercase; }
td.val { font-variant-numeric: tabular-nums; font-weight: 600; }
td.addr, td.access { color: var(--muted); font-variant-numeric: tabular-nums; }
td.comment { color: var(--muted); font-size: 12px; }
tr.has-alarm td.val { color: var(--bad); }

/* Retention cell: tier badge + Poll/Store toggles, in fixed-width slots so the
   badge, Poll and Store line up down the column. */
td.retention { white-space: nowrap; }
td.retention.ret-saving { opacity: .5; }
.ret-na { color: var(--muted); }
.ret-slot { display: inline-flex; align-items: center; vertical-align: middle; }
.ret-badge-slot { width: 92px; gap: 4px; }               /* badge + optional lock */
.ret-badge {
  display: inline-block; font-size: 10px; text-transform: uppercase; letter-spacing: .3px;
  padding: 1px 6px; border-radius: 8px; border: 1px solid var(--border); color: var(--muted);
}
.ret-badge.ret-costing { color: #ffb454; border-color: #ffb45455; }
.ret-badge.ret-analogue { color: #4ea1ff; border-color: #4ea1ff55; }
.ret-badge.ret-on_change { color: #66d9a6; border-color: #66d9a655; }
.ret-badge.ret-counter { color: #b48cff; border-color: #b48cff55; }
.ret-badge.ret-static { color: #8c9bab; }
.ret-badge.ret-ignore { color: var(--muted); }
.ret-lock { font-size: 10px; }
.ret-tog { width: 62px; gap: 4px; font-size: 11px; color: var(--muted); cursor: pointer; }
.ret-tog.locked { cursor: default; opacity: .6; }
.ret-tog input { cursor: inherit; margin: 0; }

.alarm-card {
  background: var(--card); border: 1px solid var(--border); border-left: 4px solid var(--warn);
  border-radius: 10px; padding: 14px 16px; margin-bottom: 12px;
}
.alarm-card.critical { border-left-color: var(--bad); }
.alarm-card h4 { margin: 0 0 8px; font-size: 14px; }
.alarm-card ul { margin: 0; padding-left: 18px; }
.alarm-card li { margin: 3px 0; }
.sev { font-size: 11px; padding: 1px 7px; border-radius: 8px; margin-left: 8px; }
.sev.Critical { background: rgba(248,81,73,.18); color: var(--bad); }
.sev.General { background: rgba(210,153,34,.18); color: var(--warn); }
.empty { color: var(--muted); padding: 30px; text-align: center; }

/* Alarm history */
.alarm-section {
  font-size: 13px; text-transform: uppercase; letter-spacing: .5px; color: var(--accent);
  margin: 24px 0 10px; border-bottom: 1px solid var(--border); padding-bottom: 6px;
}
.alarm-section:first-child { margin-top: 0; }
.alarm-section .hint { display: inline; margin: 0; text-transform: none; letter-spacing: 0; font-size: 11px; }
.alarm-event {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px;
  padding: 8px 12px; margin-bottom: 8px; font-size: 13px;
  border: 1px solid var(--border); border-left: 3px solid var(--border); border-radius: 8px;
}
.alarm-event.active { border-left-color: var(--bad); background: rgba(248, 81, 73, .06); }
.alarm-event .sev { margin-left: 0; }
.ae-desc { font-weight: 600; }
.ae-meta { color: var(--muted); font-size: 11px; }
.ae-time { margin-left: auto; color: var(--muted); font-size: 11px; white-space: nowrap; }

/* Fixed scroll areas so the layout doesn't jump as alarms come and go. */
.alarm-active-box { height: 132px; overflow-y: auto; }
.alarm-active-box .empty { padding: 44px 30px; }
.alarm-history-box { height: 392px; overflow-y: auto; }
.alarm-pager {
  display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin-top: 10px;
}
.alarm-pager .meta { color: var(--muted); font-size: 12px; }

.settings-sep { border: none; border-top: 1px solid var(--border); margin: 16px 0 4px; }

/* Database statistics panel (Settings > Database). */
.db-stats { margin: 4px 0 12px; }
.db-stats-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.db-stats-size { font-weight: 600; font-variant-numeric: tabular-nums; }
.db-stats-table { width: 100%; max-width: 340px; border-collapse: collapse; }
.db-stats-table td { padding: 3px 8px; font-size: 12px; border-bottom: 1px solid var(--border); }
.db-stats-table td.num { text-align: right; font-variant-numeric: tabular-nums; color: var(--muted); }

/* About card (Settings): app / protocol / access rows above the live connection
   dot + poll status (relocated from the old overview footer). */
.about-row { display: flex; justify-content: space-between; gap: 12px; padding: 3px 0; }
.about-lbl { color: var(--muted); }
.about-val { color: var(--text); font-weight: 600; }
.meta { color: var(--muted); font-size: 12px; margin-top: 14px; }

/* --- Settings --- */
/* Single column on every device, pinned to the 800px kiosk's content width
   (800 - 2*20 padding = 760px) so desktop matches the kiosk rather than
   spreading the cards into multiple columns. */
.settings-grid {
  display: grid; gap: 16px;
  grid-template-columns: 1fr;
  max-width: 760px;
  align-items: start;
}
.settings-toolbar { max-width: 760px; margin: 0 0 14px; display: flex; justify-content: flex-end; }
/* Accordion mode: collapse every card to its header; only the .open card's body shows,
   one at a time. */
.settings-grid.accordion .settings-card > h3 {
  cursor: pointer; display: flex; align-items: center; justify-content: space-between;
}
.settings-grid.accordion .settings-card > h3::after { content: "▸"; font-size: 11px; color: var(--muted); }
.settings-grid.accordion .settings-card.open > h3::after { content: "▾"; }
.settings-grid.accordion .settings-card:not(.open) > h3 { margin-bottom: 0; }
.settings-grid.accordion .settings-card:not(.open) > :not(h3) { display: none !important; }
.settings-card label {
  display: flex; flex-direction: column; gap: 4px; font-size: 12px;
  color: var(--muted); margin-bottom: 12px;
}
.settings-card label.switch, .settings-card label.chk {
  flex-direction: row; align-items: center; gap: 8px; font-size: 13px; color: var(--text);
}
/* Notification type toggles: a compact two-column grid of checkboxes. */
.alerts-types { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 16px; margin: 4px 0 4px; }
.alerts-types > .hint { grid-column: 1 / -1; margin: 0 0 2px; }
.alerts-types label.chk { margin-bottom: 4px; }
.alerts-actions { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
@media (max-width: 560px) { .alerts-types { grid-template-columns: 1fr; } }
/* Custom register-alert rows: an inline row of small inputs, not full-width. */
.alerts-registers { margin: 6px 0 2px; }
.reg-alert-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 6px; }
.reg-alert-row input, .reg-alert-row select { width: auto; padding: 6px 8px; font-size: 13px; }
.reg-alert-row .ra-key { flex: 1 1 150px; min-width: 120px; }
.reg-alert-row .ra-cond { flex: 0 0 auto; }
.reg-alert-row .ra-thr { flex: 0 0 80px; }
.reg-alert-row .ra-hyst { flex: 0 0 80px; }
.reg-alert-row .ra-label { flex: 1 1 110px; min-width: 90px; }
.reg-alert-row .ra-en { flex: 0 0 auto; font-size: 12px; color: var(--muted); }
.reg-alert-row .ra-remove { flex: 0 0 auto; }
/* A gated toggle whose prerequisite is off (e.g. register writes with Modbus
   controls off): greyed and non-interactive. */
.settings-card label.switch.is-disabled { opacity: .45; cursor: not-allowed; }
.settings-card input[type="text"], .settings-card input[type="number"], .settings-card input:not([type]) {
  background: var(--bg); border: 1px solid var(--border); color: var(--text);
  border-radius: 7px; padding: 8px 10px; font-size: 14px; width: 100%;
}
.settings-card h3 { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.hint { color: var(--muted); font-size: 12px; margin: 4px 0 10px; }
.muted { color: var(--muted); }
.bad-text { color: var(--bad); }
.groups { display: flex; flex-direction: column; gap: 6px; }

.conn-row { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; }
.conn-row .spacer { flex: 1; }

button.small {
  background: var(--card-2); color: var(--text); border: 1px solid var(--border);
  border-radius: 7px; padding: 6px 12px; font-size: 13px; cursor: pointer;
}
button.small:hover { border-color: var(--accent); }
button.small.ghost { background: transparent; }
button.small.danger { color: var(--bad); border-color: rgba(248, 81, 73, .45); background: transparent; }
button.small.danger:hover { border-color: var(--bad); background: rgba(248, 81, 73, .12); }
button.small:disabled { opacity: .5; cursor: default; }
.clear-confirm { margin-top: 4px; }
.clear-actions { display: flex; gap: 8px; margin-top: 8px; }
.hint.warn { color: var(--bad); }
button.primary {
  background: var(--accent); color: #08121f; border: none; font-weight: 600;
  border-radius: 8px; padding: 10px 20px; font-size: 14px; cursor: pointer;
}
button.primary:hover { filter: brightness(1.08); }

/* Devices and the map span the full settings grid for room. */
.devices-card, .location-card { grid-column: 1 / -1; }
.loc-map {
  height: 300px; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--border); margin: 6px 0 12px; background: var(--bg);
}
.loc-coords { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
.loc-coords label { margin-bottom: 0; flex: 0 1 180px; }
/* Leaflet tiles render dark-mode-neutral; keep its controls readable on our bg. */
.loc-map .leaflet-control-attribution { font-size: 10px; }
/* Each device: a main row (name/id/type/remove) with an optional DC-charger
   sub-line underneath, so rows stay aligned and the toggle isn't cramped. */
.pv-name-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; max-width: 600px; }
.pv-name-row .pv-tag {
  flex: 0 0 auto; min-width: 38px; font-size: 11px; font-weight: 600; color: var(--muted);
  text-align: center; padding: 4px 6px; border: 1px solid var(--border); border-radius: 6px;
}
.pv-name-row .pv-name { flex: 1 1 auto; min-width: 90px; }
/* Compact numeric forecast fields (kWp / tilt / azimuth) share the row. */
.pv-name-row .pv-kwp, .pv-name-row .pv-tilt, .pv-name-row .pv-az { flex: 0 0 64px; min-width: 0; }
.pv-name-row .pv-remove { flex: 0 0 auto; }
.solar-params { display: flex; gap: 12px; flex-wrap: wrap; max-width: 600px; }
.solar-params label { flex: 1 1 160px; }

.device-row { margin-bottom: 14px; max-width: 600px; }
.device-main { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.device-main .dev-name { flex: 1 1 150px; min-width: 130px; }
.device-main .dev-slave { flex: 0 0 64px; }
.device-main .dev-type { flex: 1 1 130px; }
.device-main .dev-remove { flex: 0 0 auto; margin-left: auto; }
.device-row .dev-dc {
  display: flex; flex-direction: row; align-items: center; gap: 7px; cursor: pointer;
  color: var(--muted); font-size: 12px; margin: 8px 0 0 2px;
}
.device-row .dev-dc input { width: auto; margin: 0; flex: 0 0 auto; }
.device-main .dev-name, .device-main .dev-slave, .device-main .dev-type {
  background: var(--bg); border: 1px solid var(--border); color: var(--text);
  border-radius: 7px; padding: 7px 8px; font-size: 13px; width: 100%;
}
.scan-line {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 13px;
}
#scan-result { margin-top: 10px; }
/* Scan range: which slave addresses to probe, with the time that costs. */
.scan-range {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 10px; font-size: 13px; color: var(--muted);
}
.scan-range .scan-num { width: 64px; padding: 4px 6px; font-size: 13px; }
.scan-range .hint { margin: 0 0 0 4px; }

.settings-actions { display: flex; align-items: center; gap: 14px; margin-top: 20px; }

/* --- Overview ("My Home") energy-flow diagram --- */
#tab-overview { max-width: 800px; margin: 0 auto; }
.ov-header { text-align: center; margin-bottom: 26px; }
.ov-header h2 { margin: 0; font-size: 18px; font-weight: 600; }
.ov-status { color: var(--flow); font-size: 12px; min-height: 16px; letter-spacing: .3px; }
/* The diagram is drawn at a fixed natural size (the viewBox) and scaled as one
   unit via a CSS transform on .ov-svg (see fit() in overview.js). Scaling the
   whole SVG — rather than letting it resize responsively — keeps the vector flow
   lines and the foreignObject cards locked together during a live window drag
   (Chromium throttles foreignObject re-layout, so responsive scaling desyncs them). */
.ov-diagram { width: 100%; position: relative; overflow: hidden; }
.ov-svg { position: absolute; top: 0; left: 0; width: 800px; height: 590px; transform-origin: top left; display: block; }

/* Animated power-flow connectors (drawn under the cards). */
.flow-base { stroke: var(--border); stroke-width: 2.5; fill: none; opacity: .45; }
.flow {
  stroke: var(--flow); stroke-width: 3; fill: none;
  stroke-dasharray: 6 8; stroke-linecap: round; opacity: 0; transition: opacity .4s;
}
.flow.on { opacity: 1; animation: ov-flow .9s linear infinite; }
.flow.on.rev { animation-direction: reverse; }
@keyframes ov-flow { to { stroke-dashoffset: -14; } }
/* Coloured by side of the inverter: DC side (PV/Solar/Battery/DC charger) green,
   AC side (Inverter→Gateway→Grid/Home/AC charger) blue like the Home icon. */
.flow.flow-dc { stroke: var(--ess); }
.flow.flow-ac { stroke: var(--accent); }
/* Battery <-> Optimiser control link: a solid muted line matching the other
   connectors' base track when inactive, turning solid yellow under live control
   (steady state — no dashes/animation). */
.sched-link { fill: none; stroke: var(--border); stroke-width: 2.5; opacity: .45; transition: opacity .3s, stroke .3s; }
.sched-link.on { stroke: #f0c674; opacity: .95; }

/* --- Node cards ----------------------------------------------------------- */
.ovc {
  width: 100%; height: 100%; box-sizing: border-box;
  display: flex; flex-direction: column; justify-content: space-between;
  position: relative; overflow: hidden;
  padding: 7px 10px; gap: 3px;
  background: var(--card); border: 1.5px solid var(--border); border-radius: 13px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
  transition: border-color .15s, box-shadow .15s;
}
.ov-click { cursor: pointer; }
.ovc.ov-click:hover { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.ovc.alarm { border-color: var(--bad); animation: ov-alarm 1.4s ease-in-out infinite; }
@keyframes ov-alarm {
  0%, 100% { box-shadow: 0 0 1px var(--bad); }
  50% { box-shadow: 0 0 7px var(--bad); }
}
.ovc-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
.ovc-ico { flex: 0 0 auto; display: inline-flex; color: var(--text); }
.ovc-svg { width: 19px; height: 19px; display: block; color: inherit; }
.ovc-title {
  font-size: 10px; font-weight: 600; letter-spacing: .5px; text-transform: uppercase;
  color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ovc-body { display: flex; align-items: baseline; justify-content: space-between; gap: 6px; }
.ovc-val { font-size: 20px; font-weight: 700; color: var(--text); line-height: 1.1; }
.ovc-extra-wrap { flex: 0 0 auto; }
.ovc-extra { font-size: 10px; font-weight: 600; color: var(--muted); white-space: nowrap; }
/* Reserve the status-pill height even when a card has no pill (e.g. Grid
   Contactor) so the value sits at the same y as every other card. */
.ovc-foot { min-width: 0; min-height: 16px; }
.ovc-sub {
  display: inline-block; max-width: 100%; box-sizing: border-box;
  font-size: 10px; font-weight: 600; color: var(--flow);
  background: var(--bg-2); border: 1px solid var(--border); border-radius: 999px;
  padding: 1px 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ovc-sub:empty { display: none; }
/* Per-node icon colours (inherited by the svg via currentColor). DC-side units
   (Solar/PV/Battery/DC charger) are green; the Cost coins are gold. */
.ico-solar, .ico-inverter { color: var(--pv); }
.ico-battery, .ico-pv, .ico-dc { color: var(--ess); }
.ico-gateway, .ico-home, .ico-ac, .ico-contactor { color: var(--accent); }
.ico-grid { color: #f0883e; }
.ico-optimiser { color: #f0883e; }
.ico-cost { color: var(--pv); }

/* Small PV-string cards feeding the Solar node. */
.ovc-pv {
  width: 100%; height: 100%; box-sizing: border-box; overflow: hidden;
  display: flex; flex-direction: column; gap: 1px; padding: 4px 7px;
  background: var(--card); border: 1.5px solid var(--border); border-radius: 10px;
}
.ovc-pv-head { display: flex; align-items: center; gap: 4px; min-width: 0; }
.ovc-pv-head .ovc-svg { width: 13px; height: 13px; }
.ovc-pv-title {
  font-size: 9px; font-weight: 600; letter-spacing: .3px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ovc-pv-val { font-size: 13px; font-weight: 700; color: var(--text); line-height: 1.05; }
.ovc-pv-sub { font-size: 9px; color: var(--muted); white-space: nowrap; }

/* Cost card: value stays white and the coins icon stays gold; the Earned/Lost/
   Even pill carries the daily outcome. */

/* Optimiser card: icon is a fixed orange in every state; name never fades. Live
   control (armed + sending) is shown only by the yellow card border, the solid
   link and the lit battery card — the value + pill keep their normal colours. */
.sched-node.is-off { border-color: var(--border); }
.sched-node.is-armed { border-color: #f0c674; }
/* Armed but deferred to the plant (inputs too poor to plan): a warning state. */
.sched-node.is-deferred { border-color: var(--bad); }
.sched-node.is-deferred .ovc-sub { color: var(--bad); border-color: var(--bad); }
.ovc.control-on { border-color: #f0c674; box-shadow: 0 0 0 1px #f0c674; }
/* Grid contactor: open (off grid) reads as a warning. */
.ovc-contactor.is-open { border-color: var(--warn); }
.ovc-contactor.is-open .ovc-sub { color: var(--warn); border-color: var(--warn); }
/* Gateway keeps its normal (teal) status pill on/off grid — the contactor
   already signals off-grid. */

/* --- Tariff settings --- */
.settings-card label.region-row {
  flex-direction: row; align-items: center; gap: 10px; font-size: 13px; color: var(--text);
}
.region-select { min-width: 220px; }
#tariff-browse-region { font-weight: 400; }
.tariff-role { font-size: 12px; color: var(--muted); font-weight: 600; }
.tariff-alloc { display: flex; flex-direction: column; gap: 12px; }
.t-role-head { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.t-warn { color: var(--warn); font-size: 11px; }
.t-alloc-row {
  display: flex; align-items: center; gap: 8px; padding: 4px 0 4px 8px;
  font-size: 13px; border-left: 2px solid var(--border);
}
.t-alloc-row.active { border-left-color: var(--good); }
.t-alloc-row .t-alloc-name { font-weight: 600; }
.t-alloc-row .t-alloc-from { color: var(--muted); font-size: 12px; }
.t-alloc-row .t-alloc-del { margin-left: auto; }

.tariff-browse-head { display: flex; align-items: center; gap: 8px; margin: 4px 0 8px; }
.tariff-browse-filter { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; }
.tariff-browse-filter input { flex: 1; width: auto; }
#tariff-status { margin: 0 0 8px; }
#tariff-status.busy::before { content: "⏳ "; }
.tariff-catalog { display: flex; flex-direction: column; gap: 8px; max-height: 360px; overflow-y: auto; }
.t-card { border: 1px solid var(--border); border-radius: 8px; padding: 10px; }
.t-card-main { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.t-card-name { font-weight: 600; }
.t-card-tags { display: flex; gap: 4px; }
.t-tag {
  font-size: 10px; padding: 1px 6px; border-radius: 8px;
  background: var(--card-2); color: var(--muted); border: 1px solid var(--border);
}
.t-tag-green { color: var(--good); }
.t-tag-expired { color: var(--bad); border-color: rgba(248, 81, 73, .4); }
.t-tag-manual { color: var(--accent); }
.t-card-rate { display: flex; align-items: center; gap: 12px; margin: 5px 0 3px; }
.t-card-rate.none { font-weight: 400; color: var(--muted); font-size: 12px; }
.t-rate-txt { font-size: 13px; font-weight: 600; color: var(--flow); white-space: nowrap; }
.t-spark { flex: 1; height: 24px; min-width: 0; color: var(--flow); opacity: .8; }
.t-rate-asof { font-weight: 400; color: var(--muted); font-size: 11px; }
.t-card-meta { margin: 4px 0 8px; }
.t-card-actions { display: flex; gap: 8px; }
.t-apply-form { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; margin-top: 8px; }
.t-apply-form label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.t-apply-form input, .t-apply-form select { width: auto; }

.tariff-form { display: flex; flex-direction: column; gap: 8px; max-width: 360px; }
.tariff-form > label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.tf-band { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.tf-band input { width: auto; }
.tf-band .b-rate { flex: 1 1 70px; min-width: 0; }
.tariff-form-actions { display: flex; gap: 8px; margin-top: 8px; }

/* --- Cost analysis tab --- */
#cost-status.health { color: var(--warn); }
.cost-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.cost-card .card-sub { color: var(--muted); font-weight: 400; font-size: 12px; }
/* Each block reads like a calculation: an operator column, a label, a value. */
.calc-sec { margin-bottom: 12px; }
.calc-head {
  font-size: 11px; text-transform: uppercase; letter-spacing: .4px;
  color: var(--accent); margin: 0 0 3px;
}
.calc-row, .calc-saving {
  display: grid; grid-template-columns: 16px 1fr auto; gap: 6px;
  align-items: baseline; padding: 3px 0; font-size: 13px;
}
.calc-op { color: var(--muted); text-align: center; font-weight: 600; }
.calc-label { color: var(--muted); }
.calc-val { color: var(--text); font-variant-numeric: tabular-nums; font-weight: 600; text-align: right; }
.calc-row.total { border-top: 1px solid var(--border); margin-top: 2px; padding-top: 5px; }
.calc-row.total .calc-label { color: var(--text); }
/* Headline saving at the foot of each block. */
.calc-saving {
  margin-top: 8px; padding-top: 9px; border-top: 2px solid var(--border);
}
.calc-saving .calc-label { color: var(--text); font-weight: 600; font-size: 14px; }
.calc-saving .calc-val { font-size: 18px; }
.calc-saving.earned .calc-val { color: var(--good); }
.calc-saving.lost .calc-val { color: var(--bad); }

/* --- Energy tab --- */
.energy-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 12px;
}
.day-nav { display: flex; align-items: center; gap: 8px; }
.day-nav input[type="date"] {
  background: var(--card); border: 1px solid var(--border); color: var(--text);
  border-radius: 7px; padding: 6px 10px; font-size: 13px;
}
.day-nav button:disabled { opacity: .4; cursor: default; }
.res-toggle { display: flex; gap: 4px; }
.res-toggle button.active { background: var(--accent); color: #08121f; border-color: var(--accent); font-weight: 600; }
.en-controls { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.en-auto { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.en-auto-ico { display: none; font-size: 15px; line-height: 1; }

/* Narrow panels (e.g. an 800px portrait kiosk): keep the date / range /
   resolution controls on a single row by collapsing the auto-refresh label to
   its ↻ icon and tightening the toggle padding + gaps. Desktop keeps the full
   "Auto-refresh" label. */
@media (max-width: 920px) {
  .energy-toolbar { gap: 10px; }
  .en-controls { gap: 10px; }
  .en-auto-txt { display: none; }
  .en-auto-ico { display: inline; }
  .res-toggle button.small { padding: 5px 9px; }
}
.en-auto select {
  background: var(--card); border: 1px solid var(--border); color: var(--text);
  border-radius: 7px; padding: 5px 8px; font-size: 13px; min-width: 60px;
}
#tab-energy .chart-card,
#tab-scheduler .chart-card { margin-bottom: 14px; padding: 8px; position: relative; }
/* The cost tab's distribution chart sits directly above the summary cards; it is
   excluded from the rule above, so give it the same 14px gap. Margin-bottom (not
   a top margin on the grid) so a hidden no-data chart adds no stray space. */
#cost-chart-card { margin-bottom: 14px; }

/* Name + value drawn inside (or beside) the Sankey node blocks. */
.sk-labels { position: absolute; inset: 0; pointer-events: none; }
.sk-label {
  position: absolute; color: #fff; font-size: 11px; line-height: 1.3;
  white-space: nowrap; text-shadow: 0 1px 2px rgba(0, 0, 0, .65);
  /* The layer ignores pointer events (so link hovers pass through the gaps), but
     the node blocks themselves are clickable to focus their flows. */
  pointer-events: auto; cursor: pointer; transition: opacity .15s ease;
}
/* Smooth the click-to-focus fade of the link ribbons, and suppress Plotly's
   on-hover outline (a 1px coloured stroke) so a faded link can't reappear on hover
   — links carry no intentional stroke, so forcing it off is safe. */
#en-chart-sankey .sankey-link { transition: fill-opacity .15s ease; stroke-width: 0 !important; }
.sk-label .skl-name { font-weight: 600; }
.sk-label .skl-val { opacity: .92; }
.sk-label.inside, .sk-label.box {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; overflow: hidden;
}
/* A synthesised node box for flows too thin to render one themselves. */
.sk-label.box { border-radius: 3px; border: 1px solid #0f1419; }

/* Link hover: hide Plotly's caret-pointer label, show a plain box at the cursor. */
#en-chart-sankey .hoverlayer .hovertext { display: none !important; }
.sk-hovertip {
  position: absolute; z-index: 7; pointer-events: none; display: none;
  background: rgba(20, 28, 38, .96); border: 1px solid var(--border); border-radius: 6px;
  padding: 5px 9px; font-size: 11px; color: var(--text); white-space: nowrap;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .45);
}
.sk-hovertip.show { display: block; }
.plot { width: 100%; height: 330px; }
/* The Solar dashboard's temperature chart sits below the generation chart and
   needs less height. */
.plot.plot-short { height: 200px; }
#en-chart-sankey { height: 300px; }
#en-status { min-height: 16px; }

/* Hide Plotly's native unified-hover box on the day charts — we render our own
   readout pinned to a corner so it never sits on top of the data. */
#en-chart-power .hoverlayer .legend,
#en-chart-battery .hoverlayer .legend,
#sched-chart-power .hoverlayer .legend,
#sched-chart-soc .hoverlayer .legend,
#solar-chart .hoverlayer .legend,
#solar-temp-chart .hoverlayer .legend { display: none !important; }
.plot-tip {
  position: absolute; top: 40px; z-index: 6; pointer-events: none; display: none;
  background: rgba(20, 28, 38, .94); border: 1px solid var(--border); border-radius: 8px;
  padding: 7px 9px; font-size: 11px; color: var(--text); line-height: 1.55;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .35); max-width: 50%;
}
.plot-tip.show { display: block; }
.plot-tip.left { left: 18px; }
.plot-tip.right { right: 18px; }
.plot-tip .pt-title { color: var(--muted); font-size: 10px; margin-bottom: 3px; }
.plot-tip .pt-row { display: flex; align-items: center; gap: 7px; white-space: nowrap; }
.plot-tip .pt-sw { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.plot-tip .pt-line { flex: none; display: block; }
.plot-tip .pt-val { margin-left: auto; font-variant-numeric: tabular-nums; }
/* Dashboard charts have an HTML <h3> title above the canvas, so the tip lives in
   the chart-box and sits a touch higher than the Plotly (in-plot title) charts. */
.chart-box { position: relative; }
.chart-box .plot-tip { top: 8px; }

/* --- Mobile / portrait (phones) --- */
@media (max-width: 560px) {
  .device-select { flex: 1; min-width: 0; }
  select { min-width: 0; }
  .conn { flex-shrink: 0; }

  /* Tighter utility circles so all four fit a phone width without wrapping. */
  .ov-nav { gap: 14px; }
  .nav-ico svg { width: 50px; height: 50px; padding: 13px; }
  main { padding: 14px; }
}

@media (max-width: 480px) {
  .device-row { max-width: none; }
  .charts { grid-template-columns: 1fr; }
}

/* Full-screen portrait kiosk (e.g. a wall-mounted 1280x800 panel rotated to
   800x1280). Pin the footer to the bottom, let the content area grow, and centre
   a full-width Overview diagram in the leftover height instead of crowding it at
   the top with dead space below. Scoped to portrait + tablet-width so phones and
   landscape desktops are unaffected. */
@media (orientation: portrait) and (min-width: 700px) {
  body { min-height: 100vh; display: flex; flex-direction: column; }
  /* width:100% because main's `margin:0 auto` (auto cross-axis margins) would
     otherwise disable flex stretch and collapse main to its content width. */
  main { flex: 1 0 auto; width: 100%; display: flex; flex-direction: column; }
  /* The Overview is the live dashboard — fill the width and centre vertically.
     `width: 100%` is required so the viewBox-only SVG has a definite basis to
     resolve its `width: 100%` against; without it the SVG collapses to 300px. */
  #tab-overview:not([hidden]) {
    width: 100%;
    max-width: none;
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}

/* --- Auth pages (login / register / setup) --- */
.auth-wrap {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 20px; box-sizing: border-box;
}
.auth-card { width: 100%; max-width: 360px; }
.auth-card h1 { font-size: 18px; margin: 0 0 4px; text-align: center; }
.auth-card .sub { color: var(--muted); font-size: 12px; text-align: center; margin: 0 0 18px; }
.auth-card label {
  display: flex; flex-direction: column; gap: 4px; font-size: 12px;
  color: var(--muted); margin-bottom: 12px;
}
.auth-card input, .auth-card textarea {
  background: var(--bg); border: 1px solid var(--border); color: var(--text);
  border-radius: 7px; padding: 9px 10px; font-size: 14px; width: 100%; box-sizing: border-box;
}
.auth-card button.primary { width: 100%; margin-top: 4px; }
.auth-msg { font-size: 13px; margin: 12px 0 0; }
.auth-msg.bad { color: var(--bad); }
.auth-msg.good { color: var(--good); }
.auth-foot { text-align: center; color: var(--muted); font-size: 12px; margin-top: 16px; }
.auth-foot a, .auth-card a { color: var(--accent); text-decoration: none; }

/* --- Auth-driven visibility --- */
/* The account/admin nav circle is shown to any signed-in user (admin or regular)
   and hidden when signed out or on a kiosk. Its label is set in auth.js. */
#nav-admin { display: none; }
body.role-admin #nav-admin,
body.role-user #nav-admin { display: inline-flex; }
/* The management cards on that screen are admin-only. */
body:not(.role-admin) .admin-manage { display: none; }
/* Battery-control surfaces: visible to admins when login is on, and to the single
   trusted user when login is off (no roles). The server still enforces admin on the
   control POST endpoints. :not([hidden]) so scheduler.js can still hide it when the
   scheduler is off. */
.control-only { display: none; }
body.role-admin .control-only:not([hidden]),
body:not(.auth-on) .control-only:not([hidden]) { display: block; }
/* Regular users and kiosks don't get Settings at all. */
body.role-user .ov-nav [data-nav="settings"],
body.role-kiosk .ov-nav [data-nav="settings"] { display: none; }
/* Defence in depth: if a user reaches Settings (e.g. a restored tab), hide it. */
body.role-user #tab-settings .settings-card,
body.role-user #tab-settings .settings-actions { display: none; }

/* --- Account card + admin rows --- */
.acc-pw label {
  display: flex; flex-direction: column; gap: 4px; font-size: 12px;
  color: var(--muted); margin: 10px 0 0;
}
.acc-pw input {
  background: var(--bg); border: 1px solid var(--border); color: var(--text);
  border-radius: 7px; padding: 8px 10px; font-size: 14px; width: 100%; box-sizing: border-box;
}
.acc-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* Divider + breathing room between the account card's sections (password / 2FA). */
.acc-tfa { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--border); }
.admin-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 0; border-bottom: 1px solid var(--border);
}
.admin-row:last-child { border-bottom: none; }
/* Account rows: fixed-width columns on the top line (name · role · status · 2FA)
   so they line up uniformly down the list, with actions stacked beneath. */
.admin-account { padding: 12px 0; border-bottom: 1px solid var(--border); }
.admin-account:last-child { border-bottom: none; }
.admin-account .admin-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.admin-account .admin-name {
  flex: 0 0 150px; width: 150px; font-weight: 600; font-size: 14px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.admin-account select.admin-role { flex: 0 0 88px; width: 88px; padding: 3px 8px; font-size: 12px; }
.admin-account .admin-top .badge-status {
  flex: 0 0 auto; width: 78px; text-align: center; box-sizing: border-box;
}
.admin-account .admin-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.admin-account .admin-actions button { padding: 4px 10px; font-size: 12px; }
/* Fixed-width name column so the role dropdown / actions line up across rows. */
.admin-name {
  font-weight: 600; flex: 0 0 auto; width: 140px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.admin-note { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.admin-row .spacer { flex: 1 1 auto; }
.badge-status { font-size: 11px; padding: 2px 8px; border-radius: 10px; border: 1px solid var(--border); }
.badge-status.ok { color: var(--good); border-color: rgba(63, 185, 80, .4); }
.badge-status.bad { color: var(--bad); border-color: rgba(248, 81, 73, .4); }
select.admin-role {
  background: var(--card-2); color: var(--text); border: 1px solid var(--border);
  border-radius: 7px; padding: 4px 8px; font-size: 13px;
}
.enroll-url {
  width: 100%; box-sizing: border-box; margin-top: 6px; font-family: monospace; font-size: 12px;
  background: var(--bg); border: 1px solid var(--border); color: var(--text);
  border-radius: 7px; padding: 8px 10px;
}

/* --- Battery scheduler ------------------------------------------------- */
.sched-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; font-size: 13px; color: var(--muted, #8c9bab); }
.sched-summary strong { color: var(--text); }
.sched-pill {
  display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 600; letter-spacing: .02em;
  background: var(--card-2); color: var(--muted, #8c9bab); border: 1px solid var(--border);
}
.sched-pill.off { color: var(--bad); border-color: rgba(248, 81, 73, .4); }
.sched-pill.warn { color: #f0c674; border-color: rgba(240, 198, 116, .45); }
/* Armed but deferred to the plant: a filled red pill so it reads as a warning. */
.sched-pill.bad { color: #fff; background: var(--bad); border-color: var(--bad); }

/* Live-control cockpit */
.sched-cockpit h3 { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; }
.ck-gates { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.ck-gate {
  display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 600; white-space: nowrap;
  background: var(--card-2); color: var(--muted); border: 1px solid var(--border);
}
.ck-gate.on { color: var(--good); border-color: rgba(63, 185, 80, .45); }
.ck-gate.off { color: var(--muted); }
.ck-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 10px; }
.ck-actions button.small { padding: 8px 18px; font-size: 14px; white-space: nowrap; }
.ck-state { color: var(--muted); font-size: 13px; line-height: 1.5; }
.ck-state .warn { color: #f0c674; }

/* Battery control tile (advisory command sequence) */
.sched-control h3 { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; }
.sc-now { border-left: 3px solid var(--border); border-radius: 8px; padding: 8px 12px;
  background: var(--card-2); margin-bottom: 12px; }
.sc-now.sc-charge { border-color: #2ecc71; }
.sc-now.sc-discharge { border-color: #a569bd; }
.sc-now.sc-idle { border-color: #5dade2; }
.sc-now-head { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted, #8c9bab); }
.sc-now-action { font-size: 15px; font-weight: 600; margin: 3px 0 5px; color: var(--text);
  display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
.sc-now-detail { font-size: 12px; color: var(--muted, #8c9bab); font-family: ui-monospace, monospace; }
.sc-up-title { display: flex; justify-content: space-between; font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted, #8c9bab); margin: 4px 0 5px; }
.sc-col-cost { width: 84px; text-align: right; }
.sc-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
.sc-list li { display: flex; gap: 12px; align-items: flex-start; font-size: 13px; line-height: 1.35; padding-left: 10px; border-left: 2px solid var(--border); }
.sc-list li.sc-charge { border-color: #2ecc71; }
.sc-list li.sc-discharge { border-color: #a569bd; }
.sc-list li.sc-idle { border-color: #5dade2; }
.sc-time { color: var(--muted, #8c9bab); font-variant-numeric: tabular-nums; white-space: nowrap; flex: none; width: 148px; }
.sc-desc { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.sc-en { color: var(--text); }
.sc-cmd { font-size: 11px; color: var(--muted, #8c9bab); font-family: ui-monospace, monospace; }
.sc-cost { flex: none; width: 84px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600;
  white-space: nowrap; color: var(--text); }
.sc-cost.earn { color: var(--good); }
.sc-cost.pay { color: var(--muted, #8c9bab); }
.sc-explain { margin-top: 10px; }

/* Focus highlight linking the control list and the charts. */
#sched-control [data-seg] { border-radius: 5px; }
.sc-list li.sc-hl, .sc-now.sc-hl { background: rgba(240, 198, 116, 0.16); box-shadow: inset 0 0 0 1px rgba(240, 198, 116, 0.35); }
.sc-list li { padding-top: 1px; padding-bottom: 1px; }

/* Consistent spacing between the scheduler cards (charts, control, cost). */
#tab-scheduler > .card { margin-bottom: 14px; }

/* Scheduler settings card */
.sched-params { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 14px; }
.sched-params label { display: flex; flex-direction: column; font-size: 12px; gap: 3px; }
/* One row per scheduler option: control on the left, description beside it. */
.sched-rows { display: flex; flex-direction: column; gap: 12px; }
.sched-row { display: grid; grid-template-columns: minmax(200px, 240px) 1fr; gap: 4px 18px; align-items: center; }
.sched-row > label { margin-bottom: 0; }
.sched-row .sched-desc { margin: 0; font-size: 12px; color: var(--muted); }
@media (max-width: 560px) {
  .sched-row { grid-template-columns: 1fr; align-items: start; }
}
.curve-coverage { margin: 8px 0; }
.curve-tbl { width: 100%; border-collapse: collapse; font-size: 12px; margin-top: 4px; }
.curve-tbl th, .curve-tbl td { text-align: right; padding: 3px 6px; border-bottom: 1px solid var(--border); }
.curve-tbl th:first-child, .curve-tbl td:first-child { text-align: left; text-transform: capitalize; color: var(--muted, #8c9bab); }
.curve-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 6px; }
/* The uploads are <label class="file-btn"> while View/Clear are <button class="small">
   — two different button styles, and the labels also inherit .settings-card label's
   form-field sizing (12px font + a stray bottom margin). Normalise all four to the
   standard small-button size so the row reads as one consistent set. */
.curve-actions .small { padding: 6px 12px; font-size: 13px; margin-bottom: 0; }
.file-btn {
  display: inline-flex; align-items: center; cursor: pointer;
  padding: 4px 10px; border-radius: 7px; font-size: 13px;
  background: var(--card-2); color: var(--text); border: 1px solid var(--border);
}
.file-btn:hover { border-color: var(--accent, #5dade2); }
