/* ============================================================================
   Stevens Energy web — layout + chrome (V5).
   Phone-first single column; ≥900px flows cards into 2 columns.
   ========================================================================== */

* { box-sizing: border-box; margin: 0; padding: 0; }

html { height: 100%; }

body {
  min-height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: 14px;
  line-height: 1.35;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: contain; /* our own pull-to-refresh owns the top overscroll */
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

.container {
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
}

[hidden] { display: none !important; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  clip-path: inset(50%);
  overflow: hidden;
}

svg.ic { display: block; flex: none; }

/* ---------------- App bar ---------------- */

.appbar {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--bg);
  padding-top: env(safe-area-inset-top);
}

.appbar-inner {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 62px;
  padding: 0 16px;
}

.appbar-title {
  font-size: 23px;
  font-weight: 800;
  color: var(--text);
  flex: 0 1 auto; /* v1.47.1: shrink-to-fit so the sync-dot sits right beside the text */
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.circle-btn {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
}
.circle-btn:active { background: var(--tile); }

/* ---------------- Panels + card grid ---------------- */

main { display: block; }

.panel { padding: 12px 16px calc(96px + env(safe-area-inset-bottom)); }

.page-head { display: grid; gap: 10px; margin-bottom: 12px; }

.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  align-items: start;
}

/* web view: aligned 12-col grid — cards share row lines and stretch to equal
   height within a row (dense flow fills holes), so everything scrolls in
   clean aligned bands instead of ragged masonry columns. Spans come from
   cw-* classes assigned per card key in ui.js; default is half width. */
@media (min-width: 900px) {
  .cards {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 16px;
    align-items: stretch;
    grid-auto-flow: row dense;
  }
  .cards > .cardwrap { grid-column: span 6; display: flex; flex-direction: column; }
  .cards > .cardwrap > .card, .cards > .cardwrap > .stack12 { flex: 1; }
  /* bare tile grids (e.g. solar stat tiles) stretch their rows to fill the band,
     with tile content centred so tall tiles don't top-hug */
  .cards > .cardwrap > .grid3, .cards > .cardwrap > .tile-grid { flex: 1; align-content: stretch; }
  .cards > .cardwrap > .grid3 .stat-tile {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
  }
  /* bare card stacks (e.g. weather "Today"/"What to wear") share the band too */
  .cards > .cardwrap > .stack10 { flex: 1; display: flex; flex-direction: column; gap: 10px; }
  .cards > .cardwrap > .stack10 > * + * { margin-top: 0; }
  .cards > .cardwrap > .stack10 > .wx-mini { flex: 1; align-items: center; }
  .cards > .cardwrap.cw-12 { grid-column: span 12; }
  .cards > .cardwrap.cw-4 { grid-column: span 4; }
  .cards > .cardwrap.cw-tall { grid-row: span 2; }
  .cards > .spin-wrap, .cards > .chart-empty { grid-column: 1 / -1; }
  .panel { padding-top: 16px; }
}

.cardwrap { min-width: 0; }

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 15px 16px;
}

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.stack12 > * + * { margin-top: 12px; }

/* muted helper / footnote text inside cards */
.foot { font-size: 10.5px; line-height: 1.25; color: var(--muted); }
.note-row { display: flex; gap: 5px; align-items: flex-start; margin-top: 7px; }
.note-row svg { margin-top: 1px; }

/* ---------------- Stat tiles (V5 compact, main.dart 1199-1234) ---------------- */

.grid3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.stat-tile {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  padding: 11px 12px;
  min-width: 0;
}
.stat-tile .lbl {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stat-tile .val {
  display: block;
  margin-top: 5px;
  font-size: 21px;
  white-space: nowrap;
}
.stat-tile .val .vu-unit { font-size: 11px; }

/* ---------------- Pills / chips ---------------- */

/* Tesla-style segmented control (v1.26) — one joined bar, square-ish, the
   active segment inverts to text-on-bg. Same DOM as the old round pills. */
.periodbar {
  display: flex;
  gap: 0;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  background: var(--card);
}
.pill {
  flex: 1;
  padding: 9px 0;
  text-align: center;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
  background: transparent;
  border: none;
  border-right: 1px solid var(--line);
  border-radius: 0;
}
.pill.on {
  color: var(--bg);
  background: var(--text);
  font-weight: 700;
}
.pill-icon {
  flex: none;
  width: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  background: transparent;
  border: none;
  border-radius: 0;
}
.pill-icon.on { color: var(--bg); background: var(--text); }

.chip {
  display: inline-block;
  padding: 5px 11px;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--muted);
  background: var(--tile);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
}
.chip--solid {
  color: var(--green-c);
  background: color-mix(in srgb, var(--green-c) 13%, transparent);
  border-color: color-mix(in srgb, var(--green-c) 55%, transparent);
}

/* ---------------- Section card rows (_row, main.dart 1023-1029) ---------------- */

.krow {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
}
.krow .k { color: var(--muted); }
.krow .v { font-weight: 500; color: var(--text); }

/* ---------------- Bottom nav ---------------- */

.bottomnav {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 30;
  background: var(--bg);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}
.bottomnav-inner {
  display: flex;
  height: 46px; /* compact icon-only by default; press expands to 60px + labels */
}
.navbtn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  color: var(--muted);
  font-size: 10.5px;
  font-weight: 500;
}
.navbtn span { /* label hidden while compact; revealed on .expanded */
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  transition: opacity 200ms var(--ease-out), max-height 220ms var(--ease-spring);
}
.navbtn.on { color: var(--text); font-weight: 700; } /* Tesla chrome: monochrome active, colour = data only */

/* ---------------- Digitised site plan (My System overview) ---------------- */

.sysov { display: flex; flex-direction: column; }
.sysov-info { flex: none; }
.sysov-hint { color: var(--muted); font-size: 11px; margin-top: 12px; }
.sysplan-host { position: relative; flex: 1; min-height: 340px; margin-top: 10px; }
.sysplan-host svg { position: absolute; inset: 0; width: 100%; height: 100%; }

/* desktop: the plan uses the free space beside the title, full card height */
@media (min-width: 1000px) {
  .sysov { flex-direction: row; gap: 16px; }
  .sysov-info { width: 180px; display: flex; flex-direction: column; }
  .sysov-hint { margin-top: auto; padding-top: 12px; }
  .sysplan-host { margin-top: 0; min-height: 470px; }
}

/* ---------------- Energy-flow hero card chrome (base = phone stack) -------- */

.fl-card { display: flex; flex-direction: column; padding: 16px 18px 10px; }
.fl-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.fl-headtxt { min-width: 0; }
.fl-title { font-size: 19px; font-weight: 800; }
.fl-sub { font-size: 13px; color: var(--muted); margin-top: 2px; }
.fl-pct-wrap { text-align: right; }
.fl-pct-n { font-size: 26px; }
/* v1.71 split self-powered: whole-home half, muted, beside the house figure */
.fl-pct-alt { font-size: 17px; font-weight: 600; opacity: .58; }
.fl-pct-alt[hidden] { display: none; }
.fl-pct-sub { opacity: .7; margin-top: 1px; }
.fl-pct-sub[hidden] { display: none; }
.fl-pct-sign { font-size: 14px; font-weight: 700; }

/* ---------------- Desktop console sidebar (≥1000px) ---------------- */

.sidenav { display: none; }

@media (min-width: 1000px) {
  .sidenav {
    position: fixed;
    inset: 0 auto 0 0;
    width: 200px;
    z-index: 35;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 18px 10px 14px;
    background: var(--bg);                                   /* same canvas — border does the separating */
    border-right: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  }

  .side-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 8px 16px;
  }
  .side-logo {
    width: 30px; height: 30px;
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--amber);
    background: color-mix(in srgb, var(--amber) 13%, transparent);
    border-radius: 10px;
  }
  .side-brand-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
  .side-name { font-size: 13.5px; font-weight: 800; letter-spacing: -0.2px; }
  .side-sub { font-size: 10px; color: var(--muted); }

  .side-tabs { display: grid; gap: 2px; }
  .snavbtn {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 36px;
    padding: 0 10px;
    border-radius: 9px;
    color: var(--muted);
    font-size: 13px;
    font-weight: 600;
    transition: background 140ms cubic-bezier(0.23, 1, 0.32, 1), color 140ms cubic-bezier(0.23, 1, 0.32, 1);
  }
  .snavbtn:hover { color: var(--text); background: color-mix(in srgb, var(--tile) 65%, transparent); }
  .snavbtn:active { transform: scale(0.98); }
  .snavbtn.on {
    color: var(--text);
    font-weight: 700;
    background: var(--tile);
  }

  /* live energy rail — the inverter's front panel, always on */
  .side-rail {
    margin-top: 18px;
    padding: 12px 2px 2px;
    border-top: 1px solid color-mix(in srgb, var(--line) 45%, transparent);
  }
  .srail-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 8px;
  }
  .srail-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
  .srail-k { flex: 1; font-size: 11.5px; color: var(--muted); }
  .srail-v { font-size: 12px; color: var(--text); }
  .srail-soc {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 8px 0;
    font-size: 11px;
    color: var(--muted);
  }
  .srail-track {
    flex: 1;
    height: 4px;
    border-radius: 2px;
    background: var(--tile);
    overflow: hidden;
  }
  .srail-track i {
    display: block;
    height: 100%;
    border-radius: 2px;
    background: var(--green-c);
    transition: width 400ms cubic-bezier(0.23, 1, 0.32, 1);
  }

  .side-foot {
    margin-top: auto;
    padding: 10px 8px 0;
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 10px;
    color: var(--muted);
    border-top: 1px solid color-mix(in srgb, var(--line) 45%, transparent);
  }
  .side-conn-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green-c); flex: none; }
  .side-conn-dot.off { background: var(--red); }

  /* Insights master-detail: selector list + one focused card */
  .cards > .ins-shell { grid-column: 1 / -1; }  /* the shell owns the whole row band */
  .ins-shell {
    display: grid;
    grid-template-columns: 252px minmax(0, 880px);
    gap: 26px;
    align-items: start;
  }
  .ins-list {
    position: sticky;
    top: 84px;
    display: grid;
    gap: 3px;
  }
  .ins-item {
    display: flex;
    align-items: center;
    text-align: left;
    min-height: 44px;
    padding: 8px 14px;
    border-radius: 10px;
    color: var(--muted);
    font-size: 14px;
    font-weight: 600;
    line-height: 1.3;
    transition: background 140ms cubic-bezier(0.23, 1, 0.32, 1), color 140ms cubic-bezier(0.23, 1, 0.32, 1);
  }
  .ins-item:hover { color: var(--text); background: color-mix(in srgb, var(--tile) 65%, transparent); }
  .ins-item.on {
    color: var(--green-c);
    font-weight: 700;
    background: color-mix(in srgb, var(--green-c) 11%, transparent);
  }
  .ins-stage { min-width: 0; }
  .ins-stage .chart-h205 { height: 320px; }              /* the focused card earns real chart room */
  .ins-stage .chart-h170 { height: 260px; }
  .ins-stage .chart-h130, .ins-stage .chart-h110 { height: 200px; }

  /* energy-flow hero band: headline column beside the scaled-up diagram */
  .fl-card { flex-direction: row; align-items: center; gap: 12px; padding: 20px 28px; }
  .fl-head { flex-direction: column; align-items: flex-start; gap: 18px; width: 225px; flex: none; }
  .fl-title { font-size: 24px; letter-spacing: -0.3px; }
  .fl-sub { font-size: 13.5px; margin-top: 4px; }
  .fl-pct-wrap { text-align: left; }
  .fl-pct-n { font-size: 46px; }
  .fl-pct-alt { font-size: 29px; }
  .fl-pct-sign { font-size: 20px; }
  .fl-host { margin-top: 0; flex: 1; min-width: 0; }

  /* full-width 7-day forecast strip: day tiles share the band equally */
  .cards > .cardwrap > .fc-strip { flex: 1; align-items: stretch; }
  .cardwrap.cw-12 .fc-day { flex: 1; width: auto; }

  /* chrome restructure: nav rail replaces the bottom bar; content shifts right.
     Both the appbar row and each panel cap at 1624px (1560 content + 32px side
     padding) and centre in the space right of the sidebar — same axis. */
  .bottomnav { display: none; }
  .appbar { border-bottom: 1px solid color-mix(in srgb, var(--line) 40%, transparent); }
  .appbar, main { padding-left: 200px; }
  main.container { max-width: none; }
  .appbar-inner { height: 68px; padding: 0 32px; max-width: 1624px; }
  .appbar-title { font-size: 24px; letter-spacing: -0.3px; }
  #btn-loc { display: none; }                                /* Radar lives in the sidebar */
  .panel { padding: 4px 32px 48px; max-width: 1624px; margin: 0 auto; }
}

/* ---------------- Login ---------------- */

.login-wrap {
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px;
}
.login-box {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.login-title { font-size: 30px; font-weight: 600; margin-top: 16px; }
.login-sub { color: var(--muted); margin-top: 6px; }
.login-box form { width: 100%; margin-top: 30px; display: grid; gap: 14px; }

.field {
  position: relative;
  display: flex;
  align-items: center;
}
.field svg {
  position: absolute;
  left: 14px;
  color: var(--muted);
  pointer-events: none;
}
.field input {
  width: 100%;
  height: 52px;
  padding: 0 14px 0 44px;
  font: inherit;
  font-size: 15px;
  color: var(--text);
  background: var(--tile);
  border: none;
  border-radius: var(--r-tile);
  outline: none;
}
.field input::placeholder { color: var(--muted); }
.field input:focus { box-shadow: 0 0 0 1.5px var(--line) inset; }

.login-err { color: var(--err); font-size: 13.5px; text-align: center; }

@media (min-width: 1000px) {
  .login-box {
    max-width: 400px;
    padding: 48px 42px;
    background: var(--card);
    border: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
    border-radius: var(--r-card);
  }
}

.btn-primary {
  height: 52px;
  border-radius: var(--r-tile);
  background: var(--text);
  color: var(--bg);
  font-size: 16px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 10px;
}
.btn-primary:disabled { opacity: 0.75; cursor: default; }

/* ---------------- Menu popover + bottom sheet ---------------- */

.scrim {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(0, 0, 0, 0.35);
}
.scrim.clear { background: transparent; }

.menu-pop {
  position: fixed;
  z-index: 41;
  min-width: 224px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 6px 0;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35);
}
.menu-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 11px 16px;
  font-size: 14.5px;
  color: var(--text);
  text-align: left;
}
.menu-item svg { color: var(--muted); }
.menu-item:active { background: var(--tile); }
.menu-div { height: 1px; margin: 6px 0; background: var(--line); }

.sheet {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 41;
  background: var(--card);
  border-radius: 20px 20px 0 0;
  padding: 18px 20px calc(14px + env(safe-area-inset-bottom));
  max-width: 560px;
  margin: 0 auto;
}
/* tall sheets (hot-water detail): scroll within the sheet */
.sheet--full { max-height: 92dvh; overflow-y: auto; overscroll-behavior: contain; }

/* desktop (user, v1.24): tall sheets become a CENTRED WINDOW, not a phone
   strip pinned to the bottom — ~940px dialog, cards in two columns */
@media (min-width: 1000px) {
  .sheet--full {
    left: 50%; right: auto; bottom: auto; top: 50%;
    transform: translate(-50%, -50%);
    width: min(940px, 92vw);
    max-width: none;
    max-height: 84vh;
    border-radius: 20px;
    padding: 22px 26px calc(18px + env(safe-area-inset-bottom));
  }
  .sheet--full .sheet-grab { display: none; }
  .sheet--dev .sheet-cards {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
    align-items: start;
  }
  /* desktop control row: the AI ticker takes the line at a readable width, the
     ultra-fast chip sits right beside it with its full label */
  .ctlrow { justify-content: flex-start; }
  .ctlrow #ai-ticker-host { flex: 0 1 560px; }
  .periodbar .pill { flex: 0 0 auto; padding: 9px 24px; }
}

/* device drill-down sheets: sticky chip-tab row + a scrollable stack of cards */
.sheet-chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--card);
  padding: 10px 0 8px;
  margin-top: 4px;
  scrollbar-width: none;
}
.sheet-chips::-webkit-scrollbar { display: none; }
.sheet-cards { display: flex; flex-direction: column; gap: 10px; }
.sheet-cards .card { border: 1px solid var(--line); border-radius: 16px; }

.sheet-item {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 12px 0;
  font-size: 14.5px;
  font-weight: 500;
  color: var(--muted);
  text-align: left;
}
.sheet-item.on { color: var(--text); font-weight: 700; }
.sheet-item.on svg:first-child, .sheet-item .check { color: var(--green-c); }
.sheet-item .check { margin-left: auto; }

/* ---------------- Toast ---------------- */

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(76px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 50;
  max-width: min(92vw, 480px);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 18px;
  background: var(--tile);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  color: var(--text);
  font-size: 13.5px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.3);
}
.toast button { color: var(--green-c); font-weight: 700; }

/* ---------------- Loading ---------------- */

.spin-wrap { display: flex; justify-content: center; padding: 64px 0; }
.spinner {
  width: 30px; height: 30px;
  border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--green-c) 25%, transparent);
  border-top-color: var(--green-c);
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.stub {
  padding: 48px 16px;
  text-align: center;
  color: var(--muted);
  font-size: 13.5px;
}

/* ---------------- Phase 2: solar-card components ---------------- */

.divider { height: 1px; background: var(--line); margin: 12px 0; }

/* progress bar track (LinearProgressIndicator) */
.bar {
  height: 7px;
  border-radius: 5px;
  background: var(--tile);
  overflow: hidden;
}
.bar i { display: block; height: 100%; border-radius: 5px; }

/* labelled split-bar (_splitBar) */
.splitbar { padding: 6px 0; }
.splitbar-row { display: flex; align-items: baseline; gap: 8px; margin-bottom: 5px; }
.splitbar-row .sb-label { flex: 1; min-width: 0; font-size: 13px; color: var(--muted); }
.splitbar-row .sb-val { font-size: 13px; font-weight: 500; }
.splitbar-row .sb-pct { font-size: 13px; font-weight: 600; }

/* stacked horizontal split (bill compare granny/house) */
.stackbar { display: flex; height: 22px; }
.stackbar i { display: block; }
.dotk {
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 50%;
  margin-right: 5px;
}

/* V5 legend: short rounded dashes */
.legend { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.legend-item { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--muted); }
.ldash { display: inline-block; width: 14px; height: 3.5px; border-radius: 2px; }

/* chart canvas wrappers (fixed heights; Chart.js responsive inside) */
.chart-h120, .chart-h150, .chart-h190 { position: relative; min-width: 0; }
.chart-h120 { height: 120px; }
.chart-h150 { height: 150px; }
.chart-h190 { height: 190px; }
.chart-h120 canvas, .chart-h150 canvas, .chart-h190 canvas { width: 100% !important; height: 100% !important; }
.chart-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 12px;
  height: 100%;
}

/* icon box (demand guardian) */
.icon-box {
  width: 40px; height: 40px;
  flex: none;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* smart-energy tile banner */
.card--tile {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: var(--tile);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  padding: 11px 14px;
}

/* week-forecast bars */
.wk-row { display: flex; gap: 4px; }
.wk-col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
}
.wk-barwrap { height: 64px; display: flex; align-items: flex-end; }
.wk-bar { width: 13px; border-radius: 3px; background: var(--amber); }

/* date selector pill (intraday graphs pager) */
.datesel { display: flex; align-items: center; gap: 8px; min-width: 0; }
.datesel-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 6px 7px 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
}
.datesel-pill svg:first-child { color: var(--muted); }
.datesel-pill svg:last-child { color: var(--muted); margin: 0 -2px; }

/* bill-compare period chips */
.bc-chips {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: 2px;
}
.bc-chips::-webkit-scrollbar { display: none; }
.bc-chip {
  flex: none;
  padding: 7px 12px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 9px;
  white-space: nowrap;
}
.bc-chip.on {
  color: var(--green);
  background: color-mix(in srgb, var(--green) 16%, transparent);
  border-color: var(--green);
}

/* solar controls row (v1.22): period pills + the ultra-fast CHIP on one line
   (the old full-width .uf-bar is retired) */
.ctlrow { display: flex; gap: 8px; align-items: center; }
.ctlrow #ai-ticker-host { flex: 1; min-width: 0; }

/* --- AI stat ticker (v1.47): rolls 10 offline-AI insights every 10s in the
   header slot the period pills held; tap advances. Slim card, two-line text. */
.ai-ticker {
  display: flex; align-items: center; gap: 9px;
  min-height: 40px; padding: 5px 11px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent;
  overflow: hidden;
}
.ait-ic { flex: none; display: inline-flex; color: var(--amber); }
.ait-tx { flex: 1; min-width: 0; }
.ait-h {
  font-size: 12.5px; font-weight: 700; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ait-d {
  font-size: 10.5px; color: var(--muted); line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* the empty #uf-bar div must not cost a flex gap (mobile hosts the UF chip in the
   glance strip instead) — v1.48 alignment fix: ticker now ends flush with the cards */
.ctlrow #uf-bar:empty { display: none; }
@keyframes ait-swap {
  from { opacity: 0; transform: translateY(5px); }
  to { opacity: 1; transform: none; }
}
.ai-ticker.ait-swap .ait-ic,
.ai-ticker.ait-swap .ait-tx { animation: ait-swap 0.38s ease; }
@media (prefers-reduced-motion: reduce) {
  .ai-ticker.ait-swap .ait-ic, .ai-ticker.ait-swap .ait-tx { animation: none; }
}
/* --- AI button + briefing pop-out (v1.79): the ai_briefing card retired into
   this. Small pill button at the ctlrow's end; the pop-out drops below the row
   and reuses the card's aib-* styles. */
.ai-btn {
  flex: none; display: inline-flex; align-items: center; gap: 4px;
  min-height: 40px; padding: 5px 12px;
  background: var(--card); color: var(--purple, var(--purple-c));
  border: 1px solid var(--line); border-radius: 8px;
  font-size: 12px; font-weight: 700; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ai-btn.on {
  color: var(--text);
  border-color: color-mix(in srgb, var(--purple-c) 55%, transparent);
}
#ai-pop { margin-top: 8px; }
#ai-pop[hidden] { display: none; }

/* the period pills as a full-width card row below the diagram (v1.47) */
.periodbar--card .pill { flex: 1 1 0; }
.ufchip {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; flex: none;
  padding: 9px 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px; /* standalone square button — sits outside the segmented bar */
}
/* data-health dot: green = live polls landing, amber = delayed, red = stale,
   grey = connecting (tap it for a plain-words status toast) */
.sync-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; background: var(--green-c); transition: background 0.3s; cursor: pointer; }
/* in the appbar (v1.47.1) it hugs the heading; auto-margin keeps the buttons right */
.appbar-inner .sync-dot { margin-right: auto; }
.sync-dot.warn { background: var(--amber); }
.sync-dot.bad { background: var(--red); }
.sync-dot.boot { background: var(--muted); opacity: 0.5; }
.ufchip svg { flex: none; }
.ufchip.on {
  color: var(--green-c);
  border-color: color-mix(in srgb, var(--green-c) 55%, transparent);
  background: color-mix(in srgb, var(--green-c) 10%, transparent);
}
.ufchip[disabled] { opacity: 0.55; }
.ufchip .uft { font-variant-numeric: tabular-nums; }

/* switch (Material-ish toggle) */
.switch {
  position: relative;
  flex: none;
  width: 46px; height: 26px;
  border-radius: var(--r-pill);
  background: var(--tile);
  border: 1px solid var(--line);
  transition: background 0.15s;
}
.switch span {
  position: absolute;
  top: 3px; left: 3px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--muted);
  transition: transform 0.15s, background 0.15s;
}
.switch.on { background: color-mix(in srgb, var(--green-c) 30%, transparent); border-color: var(--green-c); }
.switch.on span { transform: translateX(20px); background: var(--green-c); }
.switch:disabled { opacity: 0.5; }

.spinner--sm { width: 18px; height: 18px; border-width: 2px; }

/* flow hero labels (SVG text) */
.fl-val { font-family: 'Space Grotesk', sans-serif; font-size: 16px; font-weight: 700; }
.fl-cap { font-size: 11px; font-weight: 700; letter-spacing: 0.9px; }
/* solid switchboard discs (v1.24.1) — full accent fill in both themes; the
   old 8-10% tinted rings washed every hue into pastel (rust read as pink) */
.fl-disc { fill-opacity: 0.96; }
.fl-shadow1 { fill: #000; fill-opacity: 0.16; }
.fl-shadow2 { fill: #000; fill-opacity: 0.22; }
[data-theme="light"] .fl-shadow1 { fill-opacity: 0.06; }
[data-theme="light"] .fl-shadow2 { fill-opacity: 0.08; }
.fl-host { margin-top: 6px; }

/* input dialog */
.dialog {
  position: fixed;
  left: 50%; top: 38%;
  transform: translate(-50%, -50%);
  z-index: 42;
  width: min(88vw, 340px);
  /* long content (e.g. a big What's-new list) must scroll INSIDE the dialog — without
     this the action button lands off-screen and the dialog can't be dismissed. 72vh
     keeps the whole box on-screen even centred at top:38%. (fix 2026-07-11) */
  max-height: 72vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 20px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
}
.dialog-title { font-size: 17px; font-weight: 700; }
.dialog .field input { height: 46px; }
.dialog-prefix {
  position: absolute;
  left: 14px;
  color: var(--muted);
  font-size: 15px;
  pointer-events: none;
}
.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 18px;
  margin-top: 18px;
  align-items: center;
}
.dialog-save {
  padding: 9px 18px;
  border-radius: var(--r-tile);
  background: var(--text);
  color: var(--bg);
  font-weight: 600;
}

/* ============================ Weather tab (Phase 3) ======================= */

.stack10 > * + * { margin-top: 10px; }

/* location chips (port of the chips ListView, main.dart 6680-6717) */
.loc-chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 2px 0 6px;
}
.loc-chips::-webkit-scrollbar { display: none; }
.loc-chip {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 8px 14px;
  border-radius: 20px;
  border: 1px solid var(--line);
  background: var(--tile);
  color: var(--text);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}
.loc-x {
  display: inline-flex;
  margin-left: 2px;
  opacity: 0.7;
}
.loc-x:hover { opacity: 1; }
.loc-result {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 10px;
  border-radius: 12px;
  color: var(--text);
}
.loc-result:hover { background: var(--tile); }

/* hero (Davis + open-meteo variants) */
.wx-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 6px 0 10px;
}
.wx-hero svg.ic { display: inline-block; vertical-align: middle; }
.wx-hero-main { display: flex; flex-direction: column; align-items: center; }
.wx-hero-ic { display: block; margin-bottom: 2px; }
.wx-hero-temp { font-size: 64px; font-weight: 600; line-height: 1.05; }
.wx-hero-meta { display: flex; flex-direction: column; align-items: center; gap: 3px; margin-top: 4px; }
.wx-hero-greet { color: var(--muted); font-size: 15px; font-weight: 500; }
.wx-hero-line { color: var(--muted); font-size: 14.5px; }

/* desktop: the hero becomes a left-aligned banner instead of a floating stack */
@media (min-width: 1000px) {
  .wx-hero {
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 26px;
    text-align: left;
    padding: 10px 6px 18px;
  }
  .wx-hero-main { flex-direction: row; align-items: center; gap: 16px; }
  .wx-hero-ic { margin-bottom: 0; }
  .wx-hero-temp { font-size: 72px; }
  .wx-hero-meta { align-items: flex-start; gap: 4px; margin-top: 0; }
  .wx-hero-greet { font-size: 16px; }
  .wx-sunrow { margin-top: 4px; justify-content: flex-start; }
}
.wx-sunrow {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 5px;
  margin-top: 12px;
  color: var(--muted);
  font-size: 13px;
}

/* "Today" / "What to wear" advice pair */
.wx-mini { display: flex; gap: 12px; align-items: flex-start; padding: 13px 16px; }
.wx-mini-ic {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, currentColor 12%, transparent);
}

/* 7-day forecast strip */
.fc-strip {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: 4px;
}
.fc-strip::-webkit-scrollbar { display: none; }
.fc-day {
  flex: none;
  width: 78px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 12px 0;
  border-radius: var(--r-card);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--text);
  cursor: pointer;
}
.fc-rain { display: inline-flex; align-items: center; gap: 2px; font-size: 11px; }

/* hourly strip (in-card + day-detail sheet) */
.hr-strip {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  scrollbar-width: none;
}
.hr-strip::-webkit-scrollbar { display: none; }
.hr-hour {
  flex: none;
  width: 56px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
}

/* UV / air-quality columns */
.wx-sub-head { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.wx-big { display: flex; align-items: baseline; gap: 8px; margin: 8px 0 6px; }

/* tides rows */
.tide-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; }

/* weather tiles: 2-across grid, icon+label head */
.tile-grid--2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
.wx-tile { min-width: 0; }
.wx-tile-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
.wx-tile .val { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* bottom-sheet grab handle (day-detail) */
.sheet-grab {
  width: 38px;
  height: 4px;
  border-radius: 2px;
  background: var(--line);
  margin: 0 auto 16px;
}

/* ============================ System tab (Phase 4a) ======================= */

.mini-stat {
  flex: 1;
  min-width: 0;
  padding: 12px 14px;
  border-radius: var(--r-tile);
  border: 1px solid;
}
.roof-grid { display: grid; gap: 6px; }
.roof-grid i {
  display: block;
  height: 30px;
  border-radius: 8px;
}
.chart-h130 { position: relative; min-width: 0; height: 130px; }
.chart-h130 canvas { width: 100% !important; height: 100% !important; }

/* ============================ Insights tab (Phase 4b) ===================== */

.ins-pills { display: flex; gap: 8px; }
.ins-pill {
  flex: 1;
  padding: 9px 0;
  text-align: center;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--muted);
  font-weight: 700;
  font-size: 12.5px;
  cursor: pointer;
}
.ins-pill.on {
  color: var(--green-c);
  border-color: var(--green-c);
  background: color-mix(in srgb, var(--green-c) 16%, transparent);
}

/* Hot-water circuit selector. The FILLED pill is the circuit the tank is on
   right now — not an action — so the colours are the diagram's, not a button
   palette: coral = off-peak circuit, blue = diverted onto the mains. The other
   pill stays an outline, which is the only thing that looks pressable. */
.hw-pill.on { color: var(--bg); border-color: transparent; }
.hw-pill.on.hw-op { background: var(--coral); }
.hw-pill.on.hw-mains { background: var(--blue-c); }

/* Smart-divert changeover checklist (hot-water sheet). One glanceable column:
   the box is the whole signal — filled green tick = met, hollow = outstanding —
   so you read the state down the left edge without parsing any of the text.
   Labels are kept short enough to hold one line; only the budget row carries a
   third `more` line, so the rhythm of the column stays even. */
.hw-smart-head,
.hw-check-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
}
.hw-check-head { margin-top: 14px; }
.hw-check-count {
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  font-weight: 800;
  color: var(--muted);
  background: var(--tile);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 2px 8px;
  line-height: 1.5;
}
.hw-check-count.ok {
  color: var(--green-c);
  border-color: color-mix(in srgb, var(--green-c) 55%, transparent);
  background: color-mix(in srgb, var(--green-c) 13%, transparent);
}
/* Surplus strip — the engine's ONE working number (spare solar after battery +
   house) plus a split bar: blue = the slice of today's water solar pays for,
   coral track = what the off-peak circuit finishes tonight (colour law). */
.hw-surplus {
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--tile);
}
.hw-surplus-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
}
.hw-surplus-kwh {
  font-size: 17px;
  font-weight: 800;
  color: var(--muted);
}
.hw-surplus-kwh.pos { color: var(--blue-c); }
.hw-bar {
  margin-top: 8px;
  height: 8px;
  border-radius: 99px;
  background: color-mix(in srgb, var(--coral) 30%, transparent);
  overflow: hidden;
}
.hw-bar i {
  display: block;
  height: 100%;
  border-radius: 99px;
  background: var(--blue-c);
  transition: width 0.4s ease;
}
.hw-surplus-sub {
  margin-top: 7px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted);
}
.hw-surplus-sub b {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.hw-checks {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: grid;
  gap: 6px;
}
/* Each requirement is a quiet tile row — the box carries the state, the tile
   edge separates rows so the small detail text stops running together. */
.hw-check {
  display: grid;
  grid-template-columns: 18px 1fr;
  gap: 10px;
  align-items: start;
  padding: 8px 10px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--tile);
}
.hw-check.ok {
  border-color: color-mix(in srgb, var(--green-c) 26%, var(--line));
}
.hw-check-box {
  width: 18px;
  height: 18px;
  border-radius: 6px;
  border: 1.5px solid var(--line);
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 900;
  line-height: 1;
  color: transparent;
  margin-top: 1px;
}
.hw-check.ok .hw-check-box {
  background: var(--green-c);
  border-color: var(--green-c);
  color: var(--bg);
}
/* Ride-out: the check "passes" only because the 20-min dip timer is still
   running — amber ! instead of a green tick so it can't read as all-clear. */
.hw-check.hold {
  border-color: color-mix(in srgb, var(--amber) 34%, var(--line));
}
.hw-check.hold .hw-check-box {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--bg);
}
.hw-check.hold .hw-check-lbl { color: var(--text); }
.hw-check-verdict.hold {
  color: var(--amber);
  border-color: color-mix(in srgb, var(--amber) 45%, transparent);
  background: color-mix(in srgb, var(--amber) 12%, transparent);
}
.hw-check-txt { display: block; min-width: 0; }
.hw-check-lbl {
  display: block;
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
  line-height: 1.3;
}
.hw-check.ok .hw-check-lbl { color: var(--text); }
.hw-check-sub {
  display: block;
  font-size: 12px;
  color: var(--muted);
  margin-top: 2.5px;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
}
.hw-check-more {
  display: block;
  font-size: 11px;
  color: var(--muted);
  opacity: 0.78;
  margin-top: 2px;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
}
/* The verdict is the one line that says what happens NEXT, so it gets a tinted
   band rather than another run of muted body text. */
.hw-check-verdict {
  margin-top: 10px;
  padding: 7px 10px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--tile);
  font-size: 11.5px;
  font-weight: 700;
  color: var(--muted);
}
.hw-check-verdict.ok {
  color: var(--green-c);
  border-color: color-mix(in srgb, var(--green-c) 45%, transparent);
  background: color-mix(in srgb, var(--green-c) 12%, transparent);
}

.pa-pill {
  padding: 6px 13px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--tile);
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.pa-pill.on {
  color: var(--green-c);
  border-color: var(--green-c);
  background: color-mix(in srgb, var(--green-c) 16%, transparent);
}

.chart-h110, .chart-h170, .chart-h205 { position: relative; min-width: 0; }
.chart-h110 { height: 110px; }
.chart-h170 { height: 170px; }
.chart-h205 { height: 205px; }
.chart-h110 canvas, .chart-h170 canvas, .chart-h205 canvas { width: 100% !important; height: 100% !important; }

/* web view: give charts room to breathe on wide screens */
@media (min-width: 900px) {
  .chart-h110 { height: 150px; }
  .chart-h120 { height: 160px; }
  .chart-h130 { height: 170px; }
  .chart-h150 { height: 195px; }
  .chart-h170 { height: 220px; }
  .chart-h190 { height: 240px; }
  .chart-h205 { height: 280px; }
}

.gsplit-bar { display: flex; height: 7px; border-radius: 4px; overflow: hidden; }
.gsplit-bar i { display: block; }

.rd-row { display: flex; align-items: center; gap: 4px; }
.rd-h { flex: 1; text-align: right; color: var(--muted); font-size: 11px; font-weight: 600; }
.rd-c { flex: 1; text-align: right; font-size: 12.5px; font-weight: 600; }

/* ============================ Phase 5: Radar / Customise / PWA ============ */

.radar-wrap { display: flex; flex-direction: column; }
.radar-frame {
  position: relative;
  height: calc(100dvh - 210px);
  min-height: 320px;
  border-radius: var(--r-card);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--card);
}
.radar-frame iframe { width: 100%; height: 100%; border: 0; }
.radar-pill {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 13px 5px 11px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  font-size: 12px;
  font-weight: 600;
  backdrop-filter: blur(4px);
}
.radar-pill svg { color: var(--green-c); }

/* customise screen (full-screen layer) */
#customise {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--bg);
  overflow-y: auto;
}
.cz-bar {
  position: sticky;
  top: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: var(--bg);
  z-index: 1;
}
.cz-body { padding: 4px 20px 20px; max-width: 720px; margin: 0 auto; }
.cz-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  font-size: 14.5px;
}
.cz-row--order { padding: 7px 0; border-bottom: 1px solid color-mix(in srgb, var(--line) 45%, transparent); }
.cz-arrow {
  width: 34px;
  height: 30px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--tile);
  color: var(--muted);
  font-size: 11px;
  cursor: pointer;
}
.cz-arrow:disabled { opacity: 0.3; cursor: default; }
.cz-reset {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding: 9px 14px;
  border-radius: var(--r-tile);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--text);
  font-size: 13.5px;
  cursor: pointer;
}

/* offline banner */
#offline-banner {
  position: fixed;
  left: 50%;
  bottom: 74px;
  transform: translateX(-50%);
  z-index: 70;
  padding: 8px 16px;
  border-radius: var(--r-pill);
  border: 1px solid color-mix(in srgb, var(--amber) 50%, transparent);
  background: color-mix(in srgb, var(--amber) 16%, var(--card));
  color: var(--text);
  font-size: 12.5px;
  font-weight: 600;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35);
}

/* Phase 6: notifications sheet */
.nf-hour {
  background: var(--tile);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 6px 8px;
  font: inherit;
  font-size: 13px;
}
.switch:disabled { opacity: 0.35; }

/* ================= Mobile UX (sticky live strip, PTR, insights chips,
   glance hero, tighter chrome). Desktop (>=1000px) keeps the sidebar rail
   + one-at-a-time insights, so most of this is scoped to phones. ============ */

/* --- sticky live strip: always-visible Solar/Home/Battery/Grid on mobile --- */
.livestrip {
  display: flex;
  align-items: stretch;
  gap: 0;
  padding: 6px 12px 9px;
  overflow-x: auto;
  scrollbar-width: none;
  border-bottom: 1px solid var(--line);
}
.livestrip::-webkit-scrollbar { display: none; }
.ls-item {
  flex: 1 1 0;
  min-width: 58px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 2px 8px;
  border-radius: 8px;
  overflow: hidden; /* a too-wide value must clip in ITS cell, not paint over the next divider */
}
.ls-item:active { background: var(--tile); }
.ls-item + .ls-item { border-left: 1px solid var(--line); }
.ls-k { font-size: 9.5px; color: var(--muted); display: flex; align-items: center; gap: 4px; white-space: nowrap; }
.ls-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.ls-v { font-size: 14.5px; font-weight: 700; font-family: "Space Grotesk", sans-serif; white-space: nowrap; }
.ls-soc { min-width: 70px; }
/* narrow phones (Fold cover screen): the strip's five cells can't fit 14.5px
   values — shrink type instead of letting "3.83 kW" overlay the next column
   (user, 2026-08-31: "text overlaying background in battery section") */
@media (max-width: 370px) {
  .ls-item { padding: 2px 4px; min-width: 50px; }
  .ls-v { font-size: 11.5px; }
  .ls-k { font-size: 8.5px; }
  .ls-soc { min-width: 58px; }
}
.ls-soctrack { height: 4px; border-radius: 3px; background: var(--tile); margin-top: 5px; overflow: hidden; }
.ls-soctrack > i { display: block; height: 100%; background: var(--soc); border-radius: 3px; }
@media (min-width: 1000px) { .livestrip { display: none !important; } }

/* --- pull-to-refresh indicator (mobile) --- */
.ptr {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 6px);
  left: 50%;
  z-index: 45;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  transform: translate(-50%, -160%);
  opacity: 0;
  transition: transform .16s ease, opacity .16s ease;
  pointer-events: none;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28);
}
.ptr.show { opacity: 1; }
.ptr-spin {
  width: 16px; height: 16px;
  border-radius: 50%;
  border: 2px solid var(--line);
  border-top-color: var(--green-c);
}
.ptr.spin .ptr-spin { animation: ptrspin .7s linear infinite; }
@keyframes ptrspin { to { transform: rotate(360deg); } }
@media (min-width: 1000px) { .ptr { display: none; } }

/* --- insights: horizontal chip selector (mobile one-at-a-time) --- */
/* v1.80: chips WRAP instead of hiding in a horizontal scroll — all 16 insights
   are visible and one tap away (the single scroll row was hard to drive) */
.ins-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: -2px 0 12px;
  padding: 2px 0;
}
.ins-chips::-webkit-scrollbar { display: none; }
.ins-chip {
  flex: none;
  white-space: nowrap;
  padding: 7px 11px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--tile);
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  scroll-snap-align: start;
}
.ins-chip.on {
  color: var(--bg);
  border-color: var(--text);
  background: var(--text);
}

/* v1.80.2 insights nav: slim underlined category tabs + a one-line title pager
   (‹ chart name ▾ n/m ›) — no chip rows, the chart starts almost at the top */
.ins-cats2 {
  display: flex;
  gap: 2px;
  align-items: center;
  border-bottom: 1px solid var(--line);
  margin-bottom: 10px;
}
.ins-tab {
  flex: none;
  padding: 9px 9px 10px;
  margin-bottom: -1px;            /* underline sits ON the hairline */
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  -webkit-tap-highlight-color: transparent;
}
.ins-tab.on {
  color: var(--text);
  border-bottom-color: var(--text);
}
.ins-aibtn { min-height: 32px; padding: 3px 10px; border-radius: 10px; margin-bottom: 4px; }
.ins-aibtn:disabled { opacity: 0.55; }
.ins-pager {
  display: flex;
  gap: 8px;
  align-items: stretch;
  margin-bottom: 12px;
}
.ins-pgbtn {
  flex: none;
  width: 38px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  background: var(--tile);
  color: var(--muted);
  -webkit-tap-highlight-color: transparent;
}
.ins-title {
  flex: 1; min-width: 0;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 10px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  background: var(--tile);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 700;
  -webkit-tap-highlight-color: transparent;
}
.ins-title-tx { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ins-title svg { flex: none; color: var(--muted); }
.ins-count {
  flex: none;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.ins-menu {
  margin: -6px 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  background: var(--card);
  overflow: hidden;
}
.ins-menu[hidden] { display: none; }
.ins-mitem {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%;
  padding: 12px 14px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.ins-mitem:last-child { border-bottom: 0; }
.ins-mitem.on { color: var(--text); font-weight: 700; }
.ins-mitem svg { color: var(--green); }
/* the ask answer reuses .aib-answer (AI pop-out look) */
.ins-ask-out { margin-top: 10px; }

/* --- "Today at a glance" hero (mobile, top of Solar tab) --- */
.glance { display: flex; gap: 10px; }
/* v1.22 slim strip: one compact line — value + label per cell, no tall boxes */
.glance--slim {
  gap: 0;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 9px 12px;
  justify-content: space-between;
  align-items: center;
}
.gs-item { display: flex; flex-direction: column; align-items: center; gap: 1px; min-width: 0; }
/* ultra-fast chip riding in the glance strip: sized to the row */
.glance--slim .ufchip { padding: 7px 11px; font-size: 11.5px; }
.gs-v { font-size: 16px; font-weight: 700; font-family: "Space Grotesk", sans-serif; white-space: nowrap; }
.gs-v small { font-size: 10.5px; font-weight: 600; color: var(--muted); }
.gs-k { font-size: 9.5px; color: var(--muted); white-space: nowrap; }
/* v1.71: second key line for the split self-powered figure ("house / whole home") */
.gs-k2 { display: block; font-size: 8.5px; color: var(--muted); opacity: .7; white-space: nowrap; margin-top: 1px; }
.glance-cell {
  flex: 1 1 0;
  min-width: 0;
  background: var(--tile);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  padding: 11px 12px;
}
.glance-k { font-size: 10.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.glance-v { font-size: 23px; font-weight: 700; font-family: "Space Grotesk", sans-serif; margin-top: 4px; white-space: nowrap; }
.glance-v small { font-size: 12px; font-weight: 600; color: var(--muted); }
.glance-sub { font-size: 11px; font-weight: 600; color: var(--green-c); margin-top: 2px; white-space: nowrap; }
/* v1.19: the glance rides as a slim strip right under the diagram */
.glance-cell { padding: 9px 11px; }
.glance-v { font-size: 19px; margin-top: 2px; }

/* --- phone-only tightening: reclaim scroll space, bump chart/number size --- */
@media (max-width: 899px) {
  .panel { padding: 10px 13px calc(94px + env(safe-area-inset-bottom)); }
  .card { padding: 13px 14px; }
  .cards { gap: 11px; }
  .stat-tile .val { font-size: 22px; }
  /* taller charts read better on a phone */
  .chart-h110 { height: 128px; }
  .chart-h130 { height: 150px; }
  .chart-h170 { height: 188px; }
  .chart-h205 { height: 226px; }
}

/* ---- hold-to-wobble card reorder (v1.30) --------------------------------- */
@keyframes cardwobble {
  0% { transform: rotate(-0.45deg); }
  50% { transform: rotate(0.5deg); }
  100% { transform: rotate(-0.45deg); }
}
.wobble .cardwrap {
  animation: cardwobble 0.34s ease-in-out infinite;
  touch-action: none;          /* the drag owns the pointer — no scroll fights */
  cursor: grab;
  user-select: none;
}
.wobble .cardwrap:nth-child(2n) { animation-duration: 0.39s; animation-delay: 0.07s; }
.wobble .cardwrap.dragging {
  animation: none;
  position: relative;
  z-index: 30;
  cursor: grabbing;
  box-shadow: 0 10px 32px rgba(0, 0, 0, 0.35);
}
.reorder-bar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(76px + env(safe-area-inset-bottom));
  z-index: 45;
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 8px 12px;
  font-size: 12px;
  color: var(--muted);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.3);
}
.reorder-bar button {
  background: var(--tile);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
}
.reorder-bar .ro-done { background: var(--text); color: var(--bg); border-color: var(--text); }

/* ---- tap-a-graph fullscreen zoom (v1.30) --------------------------------- */
.sheet--chart [class*="chart-h"] { height: min(62vh, 520px); }   /* every chart-h* size */
.sheet--chart .cardwrap .card { border: none; padding: 6px 2px; }

/* v1.32: in the phone's fullscreen LANDSCAPE the 62vh cap left a half-height
   strip (vh shrinks to ~390px on rotate — the "not full view" bug). Landscape
   zoom now takes the whole screen; max-height guard keeps desktop untouched. */
@media (orientation: landscape) and (max-height: 500px) {
  .sheet--chart {
    top: 0; bottom: 0; left: 0; right: 0;
    max-width: none; max-height: none;
    border-radius: 0;
    padding: 8px 18px 8px;
  }
  .sheet--chart [class*="chart-h"] { height: calc(100dvh - 128px); }
}

/* ==== motion layer (v1.31) ================================================
   Entrance animations, added by js/motion.js on first appearance only (it
   gates by a session "seen" set so 10s live ticks / 5-min refreshes don't
   re-fire them). GPU-composited transforms + opacity; no layout properties. */

/* staggered card fade-in-up on first tab entry. fill-mode:both holds the 0%
   frame through --reveal-delay so a card mounted at full opacity never flashes
   before the keyframe grabs it (mount is synchronous, pre-paint). */
@keyframes card-reveal {
  from { opacity: 0; transform: translate3d(0, 10px, 0) scale(0.985); }
  to   { opacity: 1; transform: translate3d(0, 0, 0) scale(1); }
}
.cardwrap.reveal {
  animation: card-reveal var(--dur-slow) var(--ease-out) both;
  animation-delay: var(--reveal-delay, 0ms);
  will-change: transform, opacity;
}

/* progress bars fill up to their value while their card reveals (v1.33) —
   scoped under .reveal so 10s live ticks / background refreshes never refill */
@keyframes bar-fill { from { width: 0; } }
.cardwrap.reveal .bar > i {
  animation: bar-fill 900ms var(--ease-out) both;
  animation-delay: calc(var(--reveal-delay, 0ms) + 160ms);
}

/* tab crossfade — one-shot class added in setTab(); user-driven, so it's fine
   for it to replay on every tab switch. Not fired on data re-renders. */
@keyframes panel-enter {
  from { opacity: 0; transform: translate3d(0, 6px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}
.panel.panel-enter {
  animation: panel-enter var(--dur-med) var(--ease-out) both;
  will-change: transform, opacity;
}

/* flow-diagram entrance (v1.31) — device plates pop in staggered (inverter
   first, spreading outward), the dotted wire tracks fade up after them, and
   the travelling pulses ramp in via st.entered in the flow.js tick loop.
   The .flow-enter class is added once per session (Motion gate) in mount(). */
.flow-enter .flow-wire {
  opacity: 0;
  animation: flow-wire-fade 420ms var(--ease-out) forwards;
  animation-delay: var(--wire-delay, 260ms);
}
@keyframes flow-wire-fade { to { opacity: 1; } }
.flow-enter .flow-node {
  opacity: 0;
  transform: scale(0.9);
  transform-box: fill-box;
  transform-origin: center;
  animation: flow-node-pop 380ms var(--ease-out) forwards;
  animation-delay: var(--node-delay, 0ms);
}
@keyframes flow-node-pop { to { opacity: 1; transform: scale(1); } }

/* ==== motion pass (v1.32): overlays + touch feedback =======================
   Sheets, menus and toasts are recreated per open (innerHTML), so a plain CSS
   animation on the base class replays naturally — all user-driven, no gate.
   Each keyframe COMPOSES the element's own positioning transform (toast/menu
   translateX, desktop sheet window translate(-50%,-50%)) — a bare transform
   keyframe would rip them out of position mid-animation. */
@keyframes scrim-in { from { opacity: 0; } to { opacity: 1; } }
.scrim { animation: scrim-in 200ms ease-out both; }

@keyframes sheet-in {                    /* phone bottom sheet: slide up */
  from { opacity: 0; transform: translateY(28px); }
  to   { opacity: 1; transform: translateY(0); }
}
.sheet { animation: sheet-in 320ms var(--ease-spring) both; }

@keyframes menupop-in {
  from { opacity: 0; transform: translateY(-6px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.menu-pop { animation: menupop-in var(--dur-med) var(--ease-spring) both; }

@keyframes toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(14px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.toast { animation: toast-in var(--dur-med) var(--ease-spring) both; }

@keyframes dialog-in {   /* .dialog centres via translate(-50%,-50%) — compose it */
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.96); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.dialog { animation: dialog-in var(--dur-med) var(--ease-spring) both; }

/* desktop centred sheet window: scale in around its own centring transform */
@media (min-width: 1000px) {
  @keyframes sheetwin-in {
    from { opacity: 0; transform: translate(-50%, -50%) scale(0.965); }
    to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  }
  .sheet--full { animation-name: sheetwin-in; }
}

/* touch feedback: every tappable control gives a squeeze. background/color
   ride along so this doesn't clobber existing hover fades (.ins-item). */
.pill, .pill-icon, .chip, .circle-btn, .dialog-save, .ins-chip, .ins-item,
.bc-chip, .navbtn, .ufchip, .reorder-bar button, .btn-primary {
  transition: transform var(--dur-fast) var(--ease-out),
              background 140ms var(--ease-out), color 140ms var(--ease-out);
}
.pill:active, .pill-icon:active, .chip:active, .circle-btn:active,
.dialog-save:active, .ins-chip:active, .ins-item:active, .bc-chip:active,
.navbtn:active, .ufchip:active, .reorder-bar button:active, .btn-primary:active {
  transform: scale(0.955);
}

/* WebGL particle overlay on the flow hero (v1.32) — sits exactly over the SVG,
   never intercepts the node taps. v1.33: svg display:block + the canvas gets
   its EXACT pixel size inline (was 100% of the host, which is ~5px taller than
   an inline svg via the baseline gap — stretched the particle space so streams
   floated below the wires, worst at the bottom of the diagram). */
.fl-host svg { display: block; }
.fl-gl {
  position: absolute;
  top: 0; left: 0;
  pointer-events: none;
}

/* ==== v1.34: living details ================================================ */

/* sun rays stay static (v1.41: user removed the spin) */
.fl-rays {
  transform-box: fill-box;
  transform-origin: center;
}

/* v1.42: smooth crossfade for changing diagram numbers + captions (replaces
   the v1.35 glow/blip the user found choppy) — ease opacity down, swap, back up */
.fl-val, .fl-cap, .fl-sub { transition: opacity 220ms ease; }
.fl-val.fl-fade, .fl-cap.fl-fade, .fl-sub.fl-fade { opacity: 0.15; }

.fl-host { position: relative; }

/* time-of-day ambience: the Solar tab wears a whisper of the sky (--sky set
   by renderTab from F.timeAccent — dawn/day/dusk/night) */
#panel-solar {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--sky, transparent) 6%, transparent),
    transparent 360px);
}

/* weather hero micro-motion: condition class from wxAnim() in weather.js */
.wx-hero-ic svg { display: block; }
.wxa-sun svg { animation: spin 36s linear infinite; }
@keyframes wxa-drift { from { transform: translateX(-3px); } to { transform: translateX(3px); } }
.wxa-cloud svg { animation: wxa-drift 4.2s ease-in-out infinite alternate; }
@keyframes wxa-bob { from { transform: translateY(-2px); } to { transform: translateY(2.5px); } }
.wxa-rain svg { animation: wxa-bob 1.5s ease-in-out infinite alternate; }
@keyframes wxa-shudder {
  0%, 86% { transform: rotate(0deg); }
  89% { transform: rotate(-2.4deg); }
  93% { transform: rotate(2deg); }
  97% { transform: rotate(-1.2deg); }
  100% { transform: rotate(0deg); }
}
.wxa-storm svg { animation: wxa-shudder 3.4s ease-in-out infinite; }

/* ==== v1.35: interactive life ============================================== */

/* skeleton shimmer loading — ghost cards until data lands */
.sk-wrap { display: flex; flex-direction: column; gap: 12px; grid-column: 1 / -1; }
.sk-card {
  background: var(--card);
  border: 1px solid color-mix(in srgb, var(--line) 50%, transparent);
  border-radius: var(--r-card);
  padding: 16px;
  opacity: 0;
  animation: sk-in 400ms var(--ease-out) forwards;
  animation-delay: calc(var(--i, 0) * 70ms);
}
@keyframes sk-in { to { opacity: 1; } }
.sk-line, .sk-block {
  border-radius: 6px;
  background: linear-gradient(100deg,
    var(--tile) 40%, color-mix(in srgb, var(--text) 7%, var(--tile)) 50%, var(--tile) 60%);
  background-size: 220% 100%;
  animation: sk-shimmer 1.3s linear infinite;
}
.sk-line { height: 12px; margin-bottom: 10px; }
.sk-block { height: 88px; }
@keyframes sk-shimmer { from { background-position: 120% 0; } to { background-position: -100% 0; } }

/* nav icons pop when selected */
@keyframes nav-pop { 0% { transform: scale(1); } 45% { transform: scale(1.22); } 100% { transform: scale(1); } }
.navbtn.on svg, .snavbtn.on svg { animation: nav-pop 340ms var(--ease-spring); }

/* value blip — a changed live number gets a SOFT glow swell for a beat
   (v1.37, user: the green colour-flash version hurt the eyes). No hue change:
   just a gentle brightness lift + halo that fades out. */
@keyframes blip {
  0% { filter: brightness(1.1) drop-shadow(0 0 1.5px currentColor); }
  100% { filter: none; }
}
.blip { animation: blip 550ms ease-out; }
@keyframes val-blip {
  0% { opacity: 0.7; filter: drop-shadow(0 0 3.5px var(--text)); }
  100% { opacity: 1; filter: none; }
}
.val-blip { animation: val-blip 800ms ease-out; }

/* insights carousel: the incoming card slides from the travel direction */
@keyframes ins-in-r { from { opacity: 0; transform: translate3d(26px, 0, 0); } to { opacity: 1; transform: none; } }
@keyframes ins-in-l { from { opacity: 0; transform: translate3d(-26px, 0, 0); } to { opacity: 1; transform: none; } }
.cardwrap.ins-slide-r { animation: ins-in-r var(--dur-med) var(--ease-out) both; }
.cardwrap.ins-slide-l { animation: ins-in-l var(--dur-med) var(--ease-out) both; }

/* ==== v1.38 ================================================================ */

/* the page must NEVER pan sideways (user: "insights looks zoomed, I can pan").
   Root cause: .ins-chips bleeds -16px into the phone panel's 13px padding →
   393px page on a 390px screen. Clamp globally + fix the bleed. */
/* overflow-x:clip (not hidden) clamps the horizontal bleed WITHOUT making <html>
   a scroll container — hidden here forced the root's overflow-y to compute to
   auto (html has height:100%), which killed whole-page vertical scroll on phones. */
html, body { overflow-x: clip; }
/* (v1.80: the -13px .ins-chips bleed is gone — chips wrap now, no scroll row) */

/* graph → fullscreen smoothing: the sheet's card fades in ONCE after the
   fullscreen/rotation dust settles, hiding the intermediate repaints */
.sheet--chart .cardwrap {
  opacity: 0;
  transform: scale(0.985);
  transition: opacity 300ms var(--ease-out), transform 300ms var(--ease-out);
}
.sheet--chart .cardwrap.zoom-ready { opacity: 1; transform: none; }

/* bottom nav rests compact (icons only); a press expands it to full height + labels */
.bottomnav-inner { transition: height 260ms var(--ease-spring); }
.bottomnav.expanded .bottomnav-inner { height: 60px; }
.bottomnav.expanded .navbtn { gap: 3px; }
.bottomnav.expanded .navbtn span { opacity: 1; max-height: 16px; }

/* ultra-fast zap (v1.38): press = electric ring burst on the chip's host +
   an amber charge flash; while ACTIVE the chip carries a breathing glow */
.uf-zap { position: relative; }
.uf-zap::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 12px;
  border: 2px solid var(--amber);
  animation: uf-ring 700ms var(--ease-out) forwards;
  pointer-events: none;
}
@keyframes uf-ring {
  0% { opacity: 0.9; transform: scale(0.85); }
  100% { opacity: 0; transform: scale(1.45); }
}
.ufchip.on {
  animation: uf-glow 2.2s ease-in-out infinite;
}
@keyframes uf-glow {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--amber) 0%, transparent); }
  50% { box-shadow: 0 0 10px 1px color-mix(in srgb, var(--amber) 45%, transparent); }
}

/* What's-New dialog: a VISIBLE scroller + bottom fade + bouncing hint so
   it's obvious there's more below (user) */
.dialog::-webkit-scrollbar { width: 5px; }
.dialog::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--muted) 55%, transparent); border-radius: 3px; }
.dialog::-webkit-scrollbar-track { background: transparent; }
.dialog::after {
  content: "";
  position: sticky;
  bottom: 0;
  display: block;
  height: 26px;
  margin-top: -26px;
  background: linear-gradient(transparent, var(--card));
  pointer-events: none;
}
.wn-hint {
  position: sticky;
  bottom: 2px;
  display: flex;
  justify-content: center;
  color: var(--muted);
  animation: wn-bounce 1.4s ease-in-out infinite;
  pointer-events: none;
  z-index: 1;
}
@keyframes wn-bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }

/* cards lift in on scroll (v1.37) — below-the-fold cards wait for the IO */
.cardwrap.lift-wait { opacity: 0; transform: translate3d(0, 16px, 0); }
.cardwrap.lift-wait.lift {
  opacity: 1;
  transform: none;
  transition: opacity 460ms var(--ease-out), transform 460ms var(--ease-out);
}

/* action-button success morph (v1.37) */
.btn-ok {
  background: var(--green-c) !important;
  color: #fff !important;
  border-color: var(--green-c) !important;
  transition: background 160ms var(--ease-out);
}

/* on-screen weather effects canvas (v1.37) — viewport overlay, weather tab only */
.wxfx {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 30;   /* over content, under sheets/scrim (40+) */
}

/* real-time sun arc on the Davis hero (v1.37) */
.sunarc { display: block; margin-top: 6px; }
.sunarc .sa-track { stroke: var(--line); stroke-width: 1.4; fill: none; stroke-dasharray: 3 4; }
.sunarc .sa-done { stroke: var(--amber); stroke-width: 1.8; fill: none; }
.sunarc .sa-dot { fill: var(--amber); }
.sunarc .sa-glow { fill: var(--amber); opacity: 0.25; }
.sunarc .sa-moon { color: var(--soc); }

/* fullscreen-graph button (v1.36) — bottom-right of the chart itself (v1.80:
   moved off the card corner, where it covered footer text on cards that keep
   content below the chart) */
.zoombtn {
  position: absolute;
  right: 6px; bottom: 6px;
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--tile) 82%, transparent);
  border: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  border-radius: 8px;
  color: var(--muted);
  cursor: pointer;
  z-index: 3;
}
.zoombtn:active { transform: scale(0.92); }

/* dual-handle time-zoom slider in the fullscreen sheet (v1.36) */
.zoomslider { margin-top: 10px; padding: 0 6px; }
.zs-lab { display: block; text-align: center; font-size: 11.5px; color: var(--muted); margin-bottom: 4px; font-variant-numeric: tabular-nums; }
.zs-track { position: relative; height: 30px; }
.zs-track::before {
  content: "";
  position: absolute; left: 0; right: 0; top: 13px; height: 4px;
  border-radius: 2px; background: var(--tile);
}
.zs-win { position: absolute; top: 13px; height: 4px; border-radius: 2px; background: var(--green-c); }
.zs-track input[type="range"] {
  position: absolute; inset: 0; width: 100%; height: 30px; margin: 0;
  -webkit-appearance: none; appearance: none;
  background: none; pointer-events: none;
}
.zs-track input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  pointer-events: auto;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--text);
  border: 2.5px solid var(--bg);
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.35);
  cursor: ew-resize;
}
/* landscape zoom leaves room for the slider under the chart */
@media (orientation: landscape) and (max-height: 500px) {
  .sheet--chart.has-slider [class*="chart-h"] { height: calc(100dvh - 186px); }
}

/* year heatmap (v1.35): GitHub-style daily-production squares */
.hm-scroll { overflow-x: auto; margin-top: 10px; padding-bottom: 4px; }
.hm-months { display: flex; gap: 3px; margin-bottom: 4px; }
.hm-m { width: 11px; flex: none; font-size: 9px; color: var(--muted); white-space: nowrap; overflow: visible; }
.hm-grid { display: flex; gap: 3px; }
.hm-col { display: flex; flex-direction: column; gap: 3px; }
.hm-c { width: 11px; height: 11px; border-radius: 2.5px; flex: none; display: inline-block; }
.hm-x { background: color-mix(in srgb, var(--tile) 55%, transparent); }
.hm-0 { background: var(--tile); }
.hm-1 { background: color-mix(in srgb, var(--green-c) 30%, var(--tile)); }
.hm-2 { background: color-mix(in srgb, var(--green-c) 55%, var(--tile)); }
.hm-3 { background: color-mix(in srgb, var(--green-c) 80%, var(--tile)); }
.hm-4 { background: var(--green-c); }

/* pull-to-refresh is the amber sun now (was a stock border spinner) */
.ptr .ptr-spin {
  border: none;
  width: auto; height: auto;
  color: var(--amber);
  display: flex;
  align-items: center;
  justify-content: center;
}
.ptr .ptr-spin svg { display: block; }
.ptr.ready .ptr-spin { color: var(--green-c); }

/* respect the OS setting — belt-and-braces with Motion.reduced in JS.
   Kills the new entrances (and the pre-existing wobble) but leaves functional
   spinners running so "loading" still reads. */
@media (prefers-reduced-motion: reduce) {
  .cardwrap.reveal,
  .cardwrap.reveal .bar > i,
  .panel.panel-enter,
  .flow-enter .flow-wire,
  .flow-enter .flow-node,
  .wobble .cardwrap,
  .scrim, .sheet, .menu-pop, .toast, .dialog,
  .fl-rays, .wx-hero-ic svg,
  .navbtn.on svg, .snavbtn.on svg,
  .blip, .val-blip,
  .cardwrap.ins-slide-r, .cardwrap.ins-slide-l {
    animation: none !important;
  }
  .cardwrap.reveal,
  .panel.panel-enter { opacity: 1; transform: none; }
  .flow-enter .flow-node { opacity: 1; transform: none; }
  .flow-enter .flow-wire { opacity: 1; }
  .cardwrap.lift-wait { opacity: 1 !important; transform: none !important; }
}

/* backyard sky camera (v1.43): photo card + fullscreen scrubber. rgba blacks
   here are scrims OVER the photo (not chrome) - identical in both themes */
.skycam-card { position: relative; padding: 0; overflow: hidden; cursor: pointer; }
.skycam-ph { position: relative; width: 100%; aspect-ratio: 16 / 8.8; background: var(--tile); }
.skycam-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
/* caption sits in its OWN box BELOW the photo (user: overlay text blocked the
   camera view) — clean image on top, condition + time + AI line beneath */
.skycam-info { padding: 10px 14px 12px; display: flex; flex-direction: column; gap: 3px; }
.skycam-info-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.skycam-cond { color: var(--text); font-size: 14px; font-weight: 700; }
.skycam-ai { color: var(--muted); font-size: 12.5px; line-height: 1.35; }
.skycam-ts { color: var(--muted); font-size: 11px; white-space: nowrap; }
.skycam-full-ph { margin-top: 12px; }
.skycam-full-ph img {
  width: 100%; display: block; border-radius: var(--r-tile, 14px);
  background: var(--tile); min-height: 160px;
}
.skycam-scrub { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.skycam-scrub input[type="range"] { flex: 1; accent-color: var(--amber); }

/* slim one-line variant of the smart_energy strip (v1.76.2 — free-solar state) */
.smart-slim { padding: 7px 12px; align-items: center; gap: 8px; }
.smart-slim-txt {
  flex: 1; min-width: 0; font-size: 12.5px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ---- AI briefing + ask card (v1.76; compacted v1.76.1) ------------------ */
.aib-card { padding: 12px 14px 11px; }
.aib-card .card-head { margin-bottom: 4px; }
.aib-head { font-size: 14px; font-weight: 700; margin: 0 0 3px; }
.aib-body { font-size: 12.5px; line-height: 1.45; color: var(--text); }
.aib-clamp {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; cursor: pointer;
}
.aib-toggle {
  border: 1px solid var(--line); background: var(--tile); color: var(--muted);
  border-radius: var(--r-pill, 12px); padding: 3px 10px; font: inherit;
  font-size: 11.5px; cursor: pointer; white-space: nowrap;
}
.aib-toggle.open { color: var(--text); }
.aib-tip { display: flex; gap: 6px; align-items: flex-start; margin-top: 8px; font-size: 12.5px; color: var(--muted); }
.aib-anom {
  margin-top: 8px; padding: 8px 10px; border-radius: var(--r-tile, 14px);
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--amber) 35%, transparent);
  font-size: 12.5px; line-height: 1.4;
}
.aib-askrow { display: flex; gap: 8px; margin-top: 12px; }
.aib-input {
  flex: 1; min-width: 0; background: var(--tile); border: 1px solid var(--line);
  border-radius: var(--r-pill, 12px); padding: 9px 12px; color: var(--text);
  font: inherit; font-size: 13px;
}
.aib-input:focus { outline: none; border-color: var(--green); }
.aib-send {
  border: 0; border-radius: var(--r-pill, 12px); padding: 9px 16px;
  background: var(--green); color: #fff; font: inherit; font-size: 13px;
  font-weight: 600; cursor: pointer;
}
.aib-send:active { opacity: 0.85; }
.aib-answer {
  margin-top: 10px; padding: 10px 12px; border-radius: var(--r-tile, 14px);
  background: color-mix(in srgb, var(--green) 9%, transparent);
  font-size: 13px; line-height: 1.55; white-space: pre-wrap;
}
.aib-answer--wait { color: var(--muted); background: transparent; padding: 6px 0 0; }
.aib-answer--err { background: color-mix(in srgb, var(--red) 10%, transparent); }
