* {
  box-sizing: border-box;
}

/* This page toggles a plain .hidden class (via classList) on several
   elements (weather/room-temp widgets, same-time-yesterday, historical
   comparison, room-temps-section, now-section, etc.) but had no generic
   rule backing it - only 3 elements had their own one-off ".x.hidden"
   override. Elements that only ever go hidden->shown once happened to
   look fine regardless, but anything that can toggle back to hidden after
   being shown (e.g. actron-target-status when the mode changes to one
   with no single setpoint) would have stayed visibly stuck. Added here,
   low in the cascade, so any more specific existing rule still wins.  */
.hidden {
  display: none;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* Widened from 640px (2026-07-20) so the Quick View grid's 6 desktop
     columns (see .pinned-grid) get enough room per card - at 640px every
     card's name truncated to "Downstairs…" almost immediately. */
  max-width: 1000px;
  margin: 0 auto;
  padding: 16px 20px 40px;
  color: #1b1b1b;
  font-size: 14px;
}

a {
  color: #6b2c81;
}

.page-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.page-header-widgets {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 6px 10px;
}

.weather-widget {
  font-size: 14px;
  font-weight: 600;
  color: #555;
  white-space: nowrap;
}

/* Standalone/home-screen-installed PWAs have no browser chrome (no address
   bar reload icon, and pull-to-refresh doesn't reliably work in standalone
   mode on iOS) - an explicit in-page refresh button gives an obvious way
   out of a stuck/stale view instead of relying on a gesture that may not
   be there. Shown in every context (not just standalone), same button
   works fine as a plain reload in a normal browser tab too. */
.page-refresh-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 1px solid #e6d9ec;
  border-radius: 50%;
  background: #f7f2f9;
  color: #6b2c81;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.page-refresh-btn:active {
  background: #ece0f0;
}

.page-refresh-btn.is-spinning {
  animation: page-refresh-spin 0.6s linear infinite;
}

@keyframes page-refresh-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.today-cost {
  font-size: 20px;
  font-weight: 700;
  color: #1b1b1b;
  margin: 4px 0 2px;
  font-variant-numeric: tabular-nums;
}

.stats-today-label {
  font-size: 12px;
  font-weight: 400;
  color: #888;
}

.same-time-yesterday {
  margin: 4px 0 10px;
  padding: 8px 12px;
  background: #f7f2f9;
  border: 1px solid #e6d9ec;
  border-radius: 8px;
  font-size: 12px;
  color: #555;
}

.same-time-yesterday strong {
  color: #1b1b1b;
}

.historical-comparison {
  margin: 0 0 10px;
}

.historical-comparison summary {
  cursor: pointer;
  color: #6b2c81;
  font-size: 12px;
  font-weight: 600;
  list-style: none;
  user-select: none;
}

.historical-comparison summary::-webkit-details-marker {
  display: none;
}

.historical-comparison summary::before {
  content: "▸ ";
  font-size: 10px;
}

.historical-comparison[open] summary::before {
  content: "▾ ";
}

.historical-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

.historical-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border-radius: 12px;
  background: #f7f2f9;
  border: 1px solid #e6d9ec;
  font-size: 11px;
  color: #555;
  white-space: nowrap;
}

.amount-negative {
  color: #c0392b;
}

.amount-positive {
  color: #22875a;
}

.rate-hint {
  font-size: 12px;
  color: #888;
  margin: 0 0 16px;
}

.next-sync-countdown {
  font-size: 11px;
  color: #aaa;
  font-variant-numeric: tabular-nums;
  margin: -12px 0 16px;
}

.button-grid {
  display: flex;
  flex-direction: column;
}

/* ---------- Shared "device row" design system ----------
   Every device on this page - Actron zones, manual tap-to-log appliances,
   two-way controlled appliances, Tuya status rows - renders with the same
   shape: a dot + name on the left, a value/pill on the right. One visual
   language instead of some devices being card-tiles and others plain list
   lines. */

.device-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.device-row {
  display: flex;
  flex-direction: column;
  padding: 9px 10px;
  margin: 0 0 6px;
  background: #faf8fc;
  border: 1px solid #ece1f2;
  border-radius: 10px;
  box-shadow: 0 1px 2px rgba(107, 44, 129, 0.05);
}

.device-list li.device-row:last-child {
  margin-bottom: 0;
}

.device-row-main {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 4px 8px;
  font-size: 13px;
}

.appliance-row {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.appliance-row:active {
  background: #f0e4f5;
}

.appliance-row-controlled {
  /* Whole row is the tap target now too (switches power on/off), same as
     .appliance-row - the temp stepper/mode/fan-speed <select> and Details
     toggle nested inside remain individually interactive on top of that,
     handled by their own click/change branches before this row's own. */
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.appliance-row-controlled:active {
  background: #f0e4f5;
}

.device-row.is-readonly {
  cursor: default;
}

.device-row.tuya-gang-group-toggle {
  cursor: pointer;
}

.device-row.tuya-gang-group-toggle:active {
  background: #f0e4f5;
}

.device-row.is-pending {
  opacity: 0.6;
  cursor: default;
  pointer-events: none;
}

/* A whole-row highlight, not just the dot, so an active/on device is
   obvious at a glance while scanning the list. */
.device-row.is-on {
  background: #fdf0dc;
}

.item-name {
  display: inline-flex;
  align-items: center;
  color: #333;
  min-width: 0;
}

.item-amount {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: #555;
  white-space: nowrap;
}

.device-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-right: 8px;
  background: #ccc;
  flex-shrink: 0;
}

.device-dot.is-on {
  background: #eb5757;
  box-shadow: 0 0 0 3px rgba(235, 87, 87, 0.25);
}

.device-pill {
  display: inline-block;
  min-width: 34px;
  padding: 2px 8px;
  border: 1px solid #e6d9ec;
  border-radius: 10px;
  background: #f7f2f9;
  color: #888;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  text-align: center;
}

.device-pill.is-on {
  background: #eb5757;
  border-color: #eb5757;
  color: #fff;
}

.device-badge {
  display: inline-block;
  font-size: 9px;
  font-weight: 700;
  color: #fff;
  background: #eb5757;
  border-radius: 8px;
  padding: 1px 6px;
  margin-left: 6px;
  letter-spacing: 0.03em;
}

.device-badge-stale {
  background: #d99a1a;
}

/* ---------- Collapsible room/category groupings ----------
   <details>/<summary> - native disclosure behavior, no extra JS needed for
   the expand/collapse itself (see the "toggle" listener further down for
   just persisting the choice across re-renders). Room is the bold, high-
   contrast tier (a solid color bar - hard to miss while scanning);
   category nests underneath as a smaller, muted label, matching the
   existing two-tier relationship. */
.room-heading,
.category-label {
  display: flex;
  align-items: center;
  gap: 6px;
  list-style: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.room-heading::-webkit-details-marker,
.category-label::-webkit-details-marker {
  display: none;
}

.room-heading::before,
.category-label::before {
  content: "";
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 4px 0 4px 6px;
  border-color: transparent transparent transparent currentColor;
  flex-shrink: 0;
  transition: transform 0.15s ease;
}

.room-group[open] > .room-heading::before,
.category-group[open] > .category-label::before {
  transform: rotate(90deg);
}

.room-heading {
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  background: #6b2c81;
  padding: 8px 12px;
  border-radius: 8px;
  margin: 12px 0 4px;
}

.room-group:first-child .room-heading {
  margin-top: 0;
}

.category-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #888;
  padding: 8px 4px 4px;
}

/* ---------- Appliance-specific inline pieces ---------- */

/* A small colored dot instead of a spelled-out "HIGH LOAD"/"LOW LOAD" pill
   - the text version was wide enough to overlap the appliance name once
   rows moved from a card-tile layout to a single-line row (see the
   redesign in appliance-log.js). Same info still reachable via the title
   tooltip, just not spelled out inline anymore. */
.load-tag {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.load-tag-high {
  background: #c0392b;
}

.load-tag-low {
  background: #22875a;
}

.appliance-icon {
  font-size: 15px;
  line-height: 1;
  margin-right: 4px;
}

.control-badge {
  font-size: 10px;
  line-height: 1;
  margin-left: 2px;
  vertical-align: super;
}

.mini-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  font-size: 11px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.08);
  color: #555;
  cursor: pointer;
  margin-right: 6px;
  flex-shrink: 0;
}

.sync-now-btn {
  display: inline-block;
  padding: 3px 10px;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  background: #d68a00;
  border-radius: 10px;
  cursor: pointer;
}

.sync-now-btn:active {
  background: #b57400;
}

.sync-now-btn-pending {
  cursor: default;
  background: #c9c9c9;
}

#actron-sync-now-btn {
  margin-left: 4px;
  vertical-align: middle;
}

.appliance-status {
  font-size: 11px;
  color: #888;
  font-variant-numeric: tabular-nums;
}

/* Two-way controlled appliances' real interactive controls (toggle
   switch, temperature stepper, mode/fan <select>s) - a plain wrapping
   flex row underneath the main dot/name/pill row, always visible
   regardless of on/off state (no more separate compact-vs-running
   layouts now that every row gets the full list width anyway). */
.appliance-controls-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 4px 0 2px;
}

.temp-stepper-value {
  font-size: 13px;
  font-weight: 700;
  min-width: 30px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.appliance-mode-select {
  font-size: 12px;
  padding: 5px 8px;
  border-radius: 6px;
  border: 1px solid #d9c3e0;
  background: #fff;
  color: #6b2c81;
}

/* Deliberately flat/muted, no border/background matching the interactive
   <select> above - a zone's mode/fan-speed readout (see zoneTileHtml) is
   informational only, not a per-zone control, and shouldn't look tappable. */
.zone-readout-pill {
  font-size: 12px;
  padding: 4px 8px;
  border-radius: 6px;
  background: #efe8f2;
  color: #6b6473;
}

.toast {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  max-width: calc(100% - 40px);
  background: #1b1b1b;
  color: #fff;
  font-size: 13px;
  padding: 10px 16px;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
  text-align: center;
}

.toast.hidden {
  display: none;
}

.timer-picker {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.timer-picker.hidden {
  display: none;
}

.timer-picker-inner {
  background: #fff;
  border-radius: 14px;
  padding: 20px;
  max-width: 320px;
  width: 100%;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}

.timer-picker-title {
  font-size: 14px;
  font-weight: 700;
  color: #1b1b1b;
  margin-bottom: 14px;
  text-align: center;
}

.timer-picker-options {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 14px;
}

.timer-option {
  padding: 10px 4px;
  background: #f7f2f9;
  border: 1px solid #e6d9ec;
  border-radius: 10px;
  font-size: 14px;
  font-weight: 700;
  color: #6b2c81;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.timer-option:active {
  background: #ece0f0;
}

.timer-picker-custom-row {
  display: flex;
  gap: 8px;
  margin-bottom: 14px;
}

.timer-picker-custom-select {
  flex: 1;
  min-width: 0;
  padding: 10px 8px;
  background: #f7f2f9;
  border: 1px solid #e6d9ec;
  border-radius: 10px;
  font-size: 13px;
  color: #1b1b1b;
}

.timer-picker-custom-confirm {
  padding: 10px 16px;
  background: #6b2c81;
  border: none;
  border-radius: 10px;
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}

.timer-picker-custom-confirm:disabled {
  background: #c9b6d1;
  cursor: default;
}

.timer-picker-cancel {
  width: 100%;
  padding: 10px;
  background: none;
  border: none;
  color: #888;
  font-size: 13px;
  cursor: pointer;
}

.muted {
  font-size: 12px;
  color: #888;
}

#room-temps-section {
  margin-top: 20px;
}

/* Same two-tier thresholds as the Sentinel dashboard - <=5% critical (red),
   6-30% a heads-up (amber, reusing .sync-now-btn's existing attention
   color), otherwise no color at all. */
.tuya-battery-critical {
  color: #c62828;
  font-weight: 700;
}

.tuya-battery-warning {
  color: #d68a00;
}

/* Checkbox rendered as its accompanying 🔔 emoji at full opacity when on,
   faded when off, rather than showing the native checkbox control itself -
   consistent with the rest of this page's tap-target styling. */
.tuya-notify-toggle {
  display: inline-flex;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  opacity: 0.35;
  font-size: 13px;
}

.tuya-notify-toggle:has(input:checked) {
  opacity: 1;
}

.tuya-notify-toggle input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}

.tuya-critical-open-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding-top: 2px;
  padding-bottom: 6px;
  font-size: 11.5px;
  color: #9c93a4;
}

/* First image-bearing row type on this page (camera snapshot - shared by
   every camera vendor, Ring and Reolink alike) - kept small/constrained so
   it doesn't blow out the otherwise compact, text-only device-row rhythm
   every other category uses. */
.camera-snapshot-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 4px;
  padding-bottom: 6px;
  cursor: pointer;
}

.camera-snapshot-row.is-collapsed::before {
  content: "Tap to view snapshot";
  font-size: 11.5px;
  color: #9c93a4;
  text-decoration: underline;
}

.camera-snapshot-thumb {
  width: 120px;
  max-height: 90px;
  border-radius: 6px;
  object-fit: cover;
  background: #ece4ef;
  flex-shrink: 0;
}

.camera-last-event {
  font-size: 11.5px;
  color: #9c93a4;
}

.camera-viewer-img {
  width: 100%;
  border-radius: 10px;
  background: #ece4ef;
  margin-top: 8px;
}

.camera-viewer-hint {
  font-size: 11.5px;
  color: #9c93a4;
  text-align: center;
  margin: 8px 0 0;
}

.tuya-critical-open-label {
  white-space: nowrap;
}

.tuya-critical-open-input {
  width: 44px;
  padding: 3px 5px;
  border: 1px solid #d9c3e0;
  border-radius: 6px;
  font-size: 12px;
  text-align: center;
  color: #1b1b1b;
  background: #fff;
}

.tuya-critical-open-suffix {
  white-space: nowrap;
}

/* "min"/"max" placeholders (and typed decimals like "-3.5") need more room
   than the 44px default sized for a short plain integer. */
.tuya-temp-range-input {
  width: 56px;
}

/* HH:MM plus the native time input's own icon/controls needs more room
   than a plain number - 74px was still clipping the text under the icon. */
.tuya-litter-reminder-time {
  width: 96px;
}

/* Same width as the interval-days input above it - both are small plain
   whole numbers. */
.tuya-litter-reminder-since-last {
  width: 44px;
}

/* Two explicit lines (interval/time/save; days-since-last/due-in) rather
   than leaving it to flex-wrap's own judgment - with 4 inputs plus a
   button, wrap was splitting mid-line unpredictably depending on device
   name/label width, sometimes leaving the save button stranded alone on a
   third line. Splitting deliberately keeps the save button anchored to the
   end of the first, most-often-edited line (interval/time). */
.tuya-litter-reminder-row {
  flex-direction: column;
  align-items: stretch;
  flex-wrap: nowrap;
}

.tuya-litter-reminder-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

/* Explicit save button for the temp-range/litter-reminder rows (2026-07-21)
   - small icon-only tap target rather than a full labeled button, to match
   the row's own compact/muted footprint. */
.tuya-row-save-btn {
  border: 1px solid #d9c3e0;
  border-radius: 6px;
  background: #fff;
  font-size: 12px;
  line-height: 1;
  padding: 3px 6px;
  cursor: pointer;
}

.tuya-row-save-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

/* A multi-gang switch (e.g. "Living Room Lights") keeps its combined-status
   main row as a glance-and-go summary, and gets one of these per real gang
   below it instead - smaller/muted/indented to read as a sub-item, not a
   peer device. */
.tuya-gang-row {
  padding-left: 16px !important;
}

.tuya-gang-row .device-row-main {
  font-size: 12px;
  color: #999;
}

.tuya-gang-row .item-name {
  color: #999;
}

.tuya-pairing-select {
  font-size: 11px;
  padding: 3px 4px;
  border-radius: 6px;
  border: 1px solid #ddd;
  background: #fff;
  color: #555;
  max-width: 120px;
}

/* Read-only - the actual wattage threshold comes from the paired
   appliance's own Notion profile, not a value entered here. */
.tuya-pairing-hint {
  font-size: 10.5px;
  color: #999;
  white-space: nowrap;
}

.actron-target-status {
  margin: 0 0 6px;
  font-size: 11px;
  font-weight: 600;
  color: #6b2c81;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}

.temp-stepper {
  display: inline-flex;
  gap: 4px;
  margin-left: 4px;
}

.actron-controls {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin: 0 0 8px;
}

.mode-selector-row {
  display: flex;
  gap: 4px;
}

.mode-option-btn {
  flex: 1;
  min-width: 0;
  padding: 5px 2px;
  border: 1px solid #e6d9ec;
  border-radius: 8px;
  background: #f7f2f9;
  color: #6b2c81;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.mode-option-btn.mode-option-selected {
  background: #6b2c81;
  border-color: #6b2c81;
  color: #fff;
}

.mode-option-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.temp-step-btn {
  width: 20px;
  height: 20px;
  line-height: 18px;
  padding: 0;
  border: 1px solid #d9c3e0;
  border-radius: 50%;
  background: #fff;
  color: #6b2c81;
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.temp-step-btn:active {
  background: #f0e4f5;
}

.temp-step-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.room-temps-status {
  margin: 0 0 8px;
  padding: 8px 12px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  background: #f7f2f9;
  color: #888;
}

.room-temps-status.room-temps-on {
  background: #fdf0dc;
  color: #8a5a00;
  box-shadow: 0 0 0 1px rgba(235, 87, 87, 0.25);
}

/* A zone can be "on" (damper open, fan running) while the compressor
   itself is idle - satisfied setpoint and cycling, or not kicked in yet -
   so this gets its own cooler/muted look distinct from actively heating,
   not just different text. */
.room-temps-status.room-temps-idle {
  background: #eef1f5;
  color: #5c6b7a;
  box-shadow: 0 0 0 1px rgba(92, 107, 122, 0.2);
}

.room-temps-icon {
  display: inline-block;
  font-size: 15px;
  vertical-align: -2px;
}

.room-temps-toggle-btn {
  display: inline-block;
  margin-left: 6px;
  padding: 2px 8px;
  border: 1px solid #d9c3e0;
  border-radius: 999px;
  background: #fff;
  color: #6b2c81;
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.room-temps-toggle-btn:active {
  background: #f0e4f5;
}

.appliance-rooms-toolbar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 8px;
}

.rooms-toggle-all-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 72px;
  padding: 4px 10px;
  border: 1px solid #d9c3e0;
  border-radius: 999px;
  background: #fff;
  color: #6b2c81;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.rooms-toggle-all-btn:active {
  background: #f0e4f5;
}

.page-footer {
  margin-top: 24px;
  text-align: right;
}

.page-footer a {
  margin-right: 12px;
}

.app-version {
  color: #bbb;
  cursor: default;
}

/* ================= Redesign additions (2026-07-18) ================= */

.page-clock {
  font-size: 12px;
  color: #888;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.page-tabs {
  display: flex;
  gap: 4px;
  background: #f7f2f9;
  border-radius: 10px;
  padding: 4px;
  margin: 14px 0 16px;
}

.page-tab-btn {
  flex: 1;
  border: none;
  background: none;
  padding: 9px 0;
  border-radius: 8px;
  font-size: 13.5px;
  font-weight: 600;
  color: #6b6470;
  cursor: pointer;
}

.page-tab-btn.is-active {
  background: #6b2c81;
  color: #fff;
}

.page-tab {
  display: none;
}

.page-tab.is-shown {
  display: block;
}

/* ---- Sticky Quick View header ---- */
/* Stays pinned to the viewport top while the rooms/devices list below it
   scrolls - background needed since it's stacked above #button-grid, not
   the page background showing through would defeat the point. .is-compact
   (toggled by scroll position - see updateStickyHeaderCompactState in
   appliance-log.js) collapses it down once the user has actually started
   scrolling, so it doesn't sit there at full height (Quick View grid +
   stats card + hints) eating most of a phone screen for the entire scroll -
   the Quick View cards stay visible but shrink to a single scrollable row
   of icon+value only (name/badge dropped, see below), rather than
   disappearing outright, so at-a-glance temps/status are still there while
   scrolling. */
.sticky-header {
  position: sticky;
  top: 30px;
  z-index: 20;
  background: #fff;
  padding-top: 8px;
  margin-top: -8px;
  transition: box-shadow 0.2s ease;
}

.sticky-header.is-compact {
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
}

.sticky-header.is-compact #same-time-yesterday,
.sticky-header.is-compact #historical-comparison,
.sticky-header.is-compact .rate-hint,
.sticky-header.is-compact .next-sync-countdown {
  display: none;
}

.sticky-header.is-compact .pinned-section {
  margin-bottom: 8px;
}

/* The "QUICK VIEW"/Edit heading row - not worth the vertical space once
   compact; Edit mode isn't meaningful mid-scroll anyway. */
.sticky-header.is-compact .pinned-header {
  display: none;
}

/* Single horizontally-scrollable row instead of the 2/6-column grid - a
   fixed card count per row would either overflow or force everything
   tiny/wrapped depending on how many cards are pinned, where scrolling
   sideways through a short strip reads better at this reduced height. */
.sticky-header.is-compact .pinned-grid {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  gap: 6px;
}

.sticky-header.is-compact .pin-card {
  flex: 0 0 auto;
  flex-direction: row;
  align-items: center;
  min-height: 0;
  padding: 5px 9px;
  gap: 6px;
  white-space: nowrap;
}

/* Status badge and edit-mode controls dropped, but the name stays (just
   width-capped + truncated with ellipsis, same as the full grid already
   does) - a bare temperature with no room name wasn't enough to tell which
   sensor was which at a glance in the strip, and the room is exactly
   what's in the name (e.g. "Bedroom 2 TH Display"). Row layout puts
   icon → name → value left to right. */
.sticky-header.is-compact .pin-card-badge,
.sticky-header.is-compact .pin-card-remove,
.sticky-header.is-compact .pin-card-add {
  display: none;
}

.sticky-header.is-compact .pin-card-icon {
  font-size: 13px;
}

.sticky-header.is-compact .pin-card-name {
  max-width: 88px;
  font-size: 10.5px;
}

.sticky-header.is-compact .pin-card-value {
  font-size: 12px;
}

.sticky-header.is-compact .stats-card {
  padding: 6px 16px;
  margin-bottom: 0;
  border-color: transparent;
}

.sticky-header.is-compact .today-cost {
  font-size: 15px;
  margin: 2px 0;
}

/* ---- Quick view pinned mini-card grid ---- */
.pinned-section {
  margin-bottom: 16px;
}

.pinned-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

.pinned-heading {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #6b2c81;
}

.pinned-edit-btn {
  border: 1px solid #d9c3e0;
  background: #fff;
  color: #6b2c81;
  font-size: 12px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: 999px;
  cursor: pointer;
}

.pinned-edit-btn.is-active {
  background: #6b2c81;
  color: #fff;
}

.pinned-empty-hint {
  font-size: 12.5px;
  color: #9c93a4;
  margin: 0;
}

/* 2 columns on phone width, 6 once there's room for a proper 6x2 grid -
   same 12 slots either way, just reflowed by column count (see
   renderPinnedGrid's own comment for what fills them: auto "running" cards
   first, then user-pinned sensor cards, then the edit-mode "+" tile).
   minmax(0, 1fr), not plain 1fr - a plain 1fr track's *minimum* size still
   defaults to its content's min-content width, and .pin-card-name/-value
   below are white-space: nowrap, so without this a long sensor name (e.g.
   "Downstairs Study TH sensor") forces its whole column wider than 1/6 (or
   1/2) of the grid, pushing the grid's total width past the page's normal
   content column entirely (confirmed live 2026-07-20, both mobile and
   desktop) instead of letting the name's own overflow:ellipsis truncate it.
   minmax(0, 1fr) gives the track an explicit 0 floor, so it actually
   shrinks to the 1fr share and hands truncation back to the ellipsis rule. */
.pinned-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}

@media (min-width: 640px) {
  .pinned-grid {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
}

.pin-card {
  position: relative;
  background: #fff;
  border: 1px solid #e6d9ec;
  border-radius: 10px;
  padding: 8px 10px;
  min-height: 68px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
}

.pin-card-auto {
  cursor: pointer;
  border-color: #d9c3e0;
}

.pin-card-nav {
  cursor: pointer;
  border-color: #d9c3e0;
}

.pin-card-icon-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.pin-card-icon {
  font-size: 15px;
}

.pin-card-badge {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(34, 135, 90, 0.12);
  color: #22875a;
}

.pin-card-badge-off {
  background: #f7f2f9;
  color: #888;
}

.pin-card-badge-stale {
  background: rgba(217, 154, 26, 0.15);
  color: #a06e0f;
}

/* Same red as .tuya-battery-critical, for a Quick View low-battery card
   at the <=5% tier specifically (6-30% reuses pin-card-badge-stale's
   amber, same two-tier split as tuyaBatteryHtml). */
.pin-card-badge-critical {
  background: rgba(198, 40, 40, 0.12);
  color: #c62828;
}

.pin-card-name {
  font-size: 11px;
  font-weight: 600;
  color: #3a2a40;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pin-card-value {
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pin-card-remove {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: none;
  background: #b02a37;
  color: #fff;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
}

.pin-card-add {
  align-items: center;
  justify-content: center;
  gap: 2px;
  border: 1px dashed #c9b6d1;
  background: transparent;
  color: #6b2c81;
  font-size: 20px;
  font-weight: 700;
  cursor: pointer;
}

.pin-card-add-label {
  font-size: 10px;
  font-weight: 600;
}

.pin-picker-group {
  margin-bottom: 14px;
}

.pin-picker-group-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: #9c93a4;
  margin-bottom: 6px;
}

.pin-picker-option {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  margin-bottom: 6px;
  border: 1px solid #e6d9ec;
  border-radius: 8px;
  background: #fff;
  font-size: 14px;
  cursor: pointer;
}

.pin-picker-empty {
  font-size: 13px;
  color: #9c93a4;
}

.now-controls {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-top: 12px;
  border-top: 1px solid #e6d9ec;
  flex-wrap: wrap;
}

.big-toggle {
  width: 52px;
  height: 30px;
  border-radius: 15px;
  background: #f0e9f3;
  border: 1px solid #d9c3e0;
  position: relative;
  cursor: pointer;
  flex-shrink: 0;
  -webkit-tap-highlight-color: transparent;
}

.big-toggle::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform 0.15s ease;
}

.big-toggle.is-on {
  background: #22875a;
  border-color: #22875a;
}

.big-toggle.is-on::after {
  transform: translateX(22px);
}

.big-toggle[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

.big-stepper {
  display: flex;
  align-items: center;
  gap: 10px;
  background: #f7f2f9;
  border-radius: 10px;
  padding: 4px;
}

.big-step-btn {
  width: 34px;
  height: 34px;
  border-radius: 8px;
  border: none;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
  font-size: 17px;
  font-weight: 700;
  color: #6b2c81;
  cursor: pointer;
}

.big-step-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.big-step-value {
  font-size: 16px;
  font-weight: 700;
  min-width: 40px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.now-chip-row {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.now-chip {
  border: 1px solid #d9c3e0;
  background: #fff;
  border-radius: 999px;
  padding: 7px 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: #6b6470;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.now-chip.is-selected {
  background: #6b2c81;
  border-color: #6b2c81;
  color: #fff;
}

.now-chip:disabled {
  opacity: 0.5;
  cursor: default;
}

.now-detected-only {
  font-size: 12.5px;
  color: #9c93a4;
}

.now-details-toggle {
  display: block;
  margin: 10px 0 0;
  padding: 0;
  background: none;
  border: none;
  font-size: 12px;
  font-weight: 700;
  color: #6b2c81;
  cursor: pointer;
}

.now-details {
  display: none;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid #e6d9ec;
}

.now-details.is-open {
  display: block;
}

.now-detail-row {
  display: flex;
  justify-content: space-between;
  font-size: 12.5px;
  padding: 3px 0;
  color: #6b6470;
}

.now-detail-row b {
  color: #1b1b1b;
  font-variant-numeric: tabular-nums;
}

/* ---- Stats card (today vs history) ---- */
.stats-card {
  background: #fff;
  border: 1px solid #e6d9ec;
  border-radius: 12px;
  padding: 14px 16px;
  margin-bottom: 10px;
}

.compare-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 0;
}

.compare-label {
  font-size: 11.5px;
  color: #6b6470;
  width: 78px;
  flex-shrink: 0;
}

.compare-bar-track {
  flex: 1;
  height: 7px;
  border-radius: 4px;
  background: #f0e9f3;
  overflow: hidden;
}

.compare-bar-fill {
  height: 100%;
  border-radius: 4px;
  background: #b9a3c2;
}

.compare-value {
  font-size: 11.5px;
  font-weight: 700;
  width: 56px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ---- Automations ---- */
.rules-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 10px;
}

.rule-card {
  background: #fff;
  border: 1px solid #e6d9ec;
  border-radius: 12px;
  padding: 14px;
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.rule-card.is-disabled {
  opacity: 0.55;
}

.rule-icon {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: rgba(179, 105, 26, 0.12);
  color: #b3691a;
  display: grid;
  place-items: center;
  font-size: 16px;
  flex-shrink: 0;
}

.rule-body {
  flex: 1;
  min-width: 0;
}

.rule-name {
  font-size: 14px;
  font-weight: 700;
  color: #1b1b1b;
  margin-bottom: 4px;
}

.rule-name-edit-row {
  margin-bottom: 6px;
}

.rule-name-inline-input {
  width: 100%;
  border: 1px solid #d9c3e0;
  border-radius: 8px;
  padding: 7px 10px;
  font-size: 13.5px;
  font-weight: 700;
  color: #1b1b1b;
  background: #fff;
}

.rule-summary {
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.4;
}

.rule-meta {
  font-size: 11.5px;
  color: #9c93a4;
  margin-top: 3px;
}

.rule-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.rule-toggle {
  width: 38px;
  height: 22px;
  border-radius: 11px;
  background: #f0e9f3;
  border: 1px solid #d9c3e0;
  position: relative;
  cursor: pointer;
  flex-shrink: 0;
}

.rule-toggle::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  transition: transform 0.15s ease;
}

.rule-toggle.is-on {
  background: #b3691a;
  border-color: #b3691a;
}

.rule-toggle.is-on::after {
  transform: translateX(16px);
}

.rule-edit-btn,
.rule-delete-btn {
  background: none;
  border: none;
  color: #9c93a4;
  cursor: pointer;
  padding: 4px;
  font-size: 15px;
  line-height: 1;
}

.rule-edit-name-btn {
  background: none;
  border: none;
  color: #9c93a4;
  cursor: pointer;
  padding: 4px;
  font-size: 14px;
  line-height: 1;
}

.add-rule-btn {
  width: 100%;
  padding: 13px;
  border-radius: 12px;
  border: 1px dashed #d9c3e0;
  background: none;
  color: #6b2c81;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
}

.automation-note {
  font-size: 12px;
  color: #9c93a4;
  line-height: 1.5;
  margin-top: 14px;
}

/* ---- Rule builder sheet ---- */
.rule-sheet-veil {
  position: fixed;
  inset: 0;
  background: rgba(20, 12, 24, 0.4);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 40;
}

.rule-sheet-veil.hidden {
  display: none;
}

.rule-sheet {
  width: 100%;
  max-width: 480px;
  background: #fff;
  border-radius: 18px 18px 0 0;
  padding: 18px 18px calc(18px + env(safe-area-inset-bottom, 0px));
  max-height: 88vh;
  overflow-y: auto;
}

@media (min-width: 560px) {
  .rule-sheet-veil {
    align-items: center;
  }
  .rule-sheet {
    border-radius: 16px;
    max-height: 82vh;
  }
}

.rule-sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4px;
}

.rule-sheet-title {
  font-size: 16px;
  font-weight: 700;
  margin: 0;
}

.rule-sheet-close {
  background: none;
  border: none;
  color: #9c93a4;
  cursor: pointer;
  padding: 4px;
  font-size: 18px;
  line-height: 1;
}

.rule-sheet-step-label {
  font-size: 11.5px;
  color: #9c93a4;
  margin-bottom: 16px;
}

.rule-option-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
  margin-bottom: 6px;
}

.rule-option-card {
  border: 1px solid #d9c3e0;
  border-radius: 10px;
  padding: 14px 10px;
  text-align: center;
  background: #fff;
  cursor: pointer;
}

.rule-option-card.is-selected {
  border-color: #6b2c81;
  background: #f7f2f9;
}

.rule-option-icon {
  font-size: 20px;
  margin-bottom: 6px;
}

.rule-option-label {
  font-size: 12.5px;
  font-weight: 600;
}

.rule-field-block {
  margin-bottom: 16px;
}

.rule-field-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #9c93a4;
  font-weight: 700;
  margin-bottom: 8px;
  display: block;
}

.rule-field-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

.rule-field-select,
.rule-field-input {
  border: 1px solid #d9c3e0;
  border-radius: 8px;
  padding: 9px 10px;
  font-size: 13.5px;
  background: #fff;
  color: #1b1b1b;
}

.rule-field-input {
  width: 70px;
}

.rule-name-input {
  width: 100%;
}

.rule-segmented {
  display: flex;
  border: 1px solid #d9c3e0;
  border-radius: 9px;
  overflow: hidden;
}

.rule-segmented button {
  flex: 1;
  padding: 10px 0;
  background: #fff;
  border: none;
  border-right: 1px solid #d9c3e0;
  font-size: 12.5px;
  font-weight: 600;
  color: #6b6470;
  cursor: pointer;
}

.rule-segmented button:last-child {
  border-right: none;
}

.rule-segmented button.is-selected {
  background: #6b2c81;
  color: #fff;
}

/* Independently multi-selectable day pills - unlike .rule-segmented (a
   single joined strip where picking one deselects the rest), each day
   toggles on its own, so these are separate rounded pills with a gap
   between them rather than a joined segmented control. */
.rule-day-row {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.rule-day-toggle {
  padding: 7px 10px;
  border: 1px solid #d9c3e0;
  border-radius: 20px;
  background: #fff;
  font-size: 12px;
  font-weight: 600;
  color: #6b6470;
  cursor: pointer;
}

.rule-day-toggle.is-selected {
  background: #6b2c81;
  border-color: #6b2c81;
  color: #fff;
}

.rule-field-hint {
  font-size: 11px;
  color: #9c93a4;
  margin-top: 6px;
}

.rule-sheet-preview {
  background: #f7f2f9;
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 13.5px;
  line-height: 1.5;
  margin-bottom: 16px;
}

.rule-sheet-preview b {
  color: #6b2c81;
}

.rule-sheet-actions {
  display: flex;
  gap: 10px;
}

.rule-btn {
  flex: 1;
  padding: 12px 0;
  border-radius: 10px;
  border: 1px solid #d9c3e0;
  background: #fff;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  color: #1b1b1b;
}

.rule-btn-primary {
  background: #6b2c81;
  border-color: #6b2c81;
  color: #fff;
}
