/* Everything reusable now lives in ZemoUI. What stays here is tied to what the
   numbers mean in this application, not to how the framework looks. */

.login-error { width: 100%; }

/* Rooms hold between one and six devices, so their heights differ wildly. */
.rooms { --z-column-min: 360px; }
.room-count {
  padding: 1px 9px;
  border: 1px solid var(--z-border);
  border-radius: 999px;
  background: var(--z-surface-2);
  color: var(--z-muted);
  font-size: var(--z-font-xs);
  font-variant-numeric: tabular-nums;
}
/* A device tile carries a headline reading, so it needs more width than the
   framework's narrow-screen default before the number starts wrapping. */
.room .z-tiles { --z-tile-min: 230px; }
/* The line beside the reading is a sentence, not a label: "vrata otvorena,
   dosuti so i ispirač" was cut to "vrata otvore…", and the part that asks for
   something to be done was the part cut off, with no title to read it from.
   It wraps under the reading instead of being cut. */
.room .z-tile-readout { flex-wrap: wrap; row-gap: 0; }
.room .z-tile-readout small { white-space: normal; overflow: visible; }

/* The readout is a big number and the BMI row carries a badge, so these tiles
   need more width than the framework's default before things wrap. */
#people { --z-tile-min: 300px; margin-bottom: var(--z-space-5); }
.person-tile { gap: var(--z-space-2); }
.bmi-line { display: flex; align-items: baseline; gap: var(--z-space-2); font-variant-numeric: tabular-nums; }
.bmi-line b { font-size: var(--z-font-lg); font-weight: 700; }
/* The trend needs its own height: the tile has no natural one to inherit. */
.weight-trend { --z-line-h: 64px; margin-top: var(--z-space-2); }
.composition-missing { font-size: var(--z-font-sm); }

/* Room for the delete button at the end of each reading. */
#readings li { grid-template-columns: auto minmax(0, 1fr) auto; }
.weight-help { margin-top: var(--z-space-5); }
.weight-help .z-details-body p + p { margin-top: var(--z-space-3); }

/* The house switcher sits above the rooms, not inside a card. */
.site-tabs { margin-bottom: var(--z-space-4); }

/* #view-overview is a plain block wrapper -- .z-page's own grid gap does not
   reach inside it, so nothing here was actually spaced by a shared rule.
   The only reason stats/mail-card/dash-row/etc. did not visibly collide was
   .mail-card's own margin-bottom, which only pushed on the section after it,
   not the one before -- the two gaps were never actually equal, whatever it
   looked like. Grid with the same gap .z-page uses replaces that one-off
   margin with the uniform spacing it always should have had. */
#view-overview { display: grid; gap: var(--z-layout-gap); }

/* Three panels of roughly equal appetite for width: the consumption ring, the
   event feed and the router's client list. Fixed at two columns the third one
   sat alone across the whole page; auto-fit gives one column on a phone, two on
   a laptop and three on a monitor without a breakpoint written for each.
   `min(380px, 100%)` rather than a bare 380px: below that width a bare minimum
   would make the single column wider than the screen and push the page
   sideways. */
.dash-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  gap: var(--z-layout-gap);
  /* Left at the grid default (stretch) deliberately: three cards side by side
     read as one row, not three unrelated heights. What makes stretch safe
     here is below -- the ring, the feed and the roster all target the same
     --z-dash-body-h, set as height rather than max-height, so no card's
     content stops short of the height the row actually gives it. Stretch is
     then only ever smoothing over a header that wrapped a pixel taller on
     one card than another, never papering over a real mismatch. */
}


/* The brand and the toolbar fight over one row on any phone, not only at 320px:
   at 393px "Zemo Home" still wrapped onto two lines and took the refresh time
   with it. Dropping the density button's word -- not the button -- gives the
   title back its line; the icon still cycles the density and still says so to a
   screen reader, because ZemoUI writes aria-label and title onto the button. */
@media (max-width: 620px) {
  .z-topbar [data-z-density-toggle] [data-z-density-label] { display: none; }
  .z-brand-title { white-space: nowrap; }
  /* The timestamp is the first thing that may be shortened; it is a detail, and
     an ellipsis is better than a second line that doubles the bar's height. */
  .z-brand-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* Wide screens.
   The framework's 1180px page is right for a form or a page of prose. A
   dashboard is a different thing: a wall of independent readings, and on a 4K
   monitor 1180px left roughly two thirds of the screen empty while the room
   list scrolled. It grows in steps rather than filling the width, because a
   stat card stretched across 900px is not more readable, only wider. */
@media (min-width: 1600px) { :root { --z-page-w: 1480px; } }
@media (min-width: 2100px) { :root { --z-page-w: 1860px; } }
@media (min-width: 2800px) { :root { --z-page-w: 2280px; } }

/* With the extra width the rooms get more columns instead of longer ones.
   --z-column-count is a ceiling in the `columns` shorthand: the browser still
   refuses to make a column narrower than --z-column-min. */
@media (min-width: 1600px) { .rooms { --z-column-count: 4; } }
@media (min-width: 2400px) { .rooms { --z-column-count: 5; } }

/* Lists that scroll inside a card are sized for a laptop. On a tall monitor
   that leaves a scrollbar in the middle of an otherwise empty screen. */
/* Tall enough for the ring and six legend rows under it (208 + gap + about
   150), the most the ring lists. */
:root { --z-dash-body-h: 390px; }
@media (min-height: 1000px) {
  #readings { --z-feed-max-h: 340px; }
  :root { --z-dash-body-h: 400px; }
}

/* The three .dash-row cards share one body height instead of the feed and the
   roster each capping at their own framework default (232px and 320px --
   already uneven before anything in this application touched them). Set as
   height, not max-height: a card with less to show still fills the row's
   height instead of stopping at its own content and leaving the row uneven,
   which is the same shape of bug as the one stretch caused on its own (see
   .dash-row above) with the fix applied one level down, at the content
   instead of the card. A list longer than this still scrolls inside it
   exactly as before -- only a short one no longer looks cut off doing it. */
#events-list { --z-feed-max-h: var(--z-dash-body-h); height: var(--z-dash-body-h); }
#network-list { --z-roster-max-h: var(--z-dash-body-h); height: var(--z-dash-body-h); }
/* The ring wraps below the legend on anything narrower than roughly ring +
   legend side by side (.z-chart's own flex-wrap, from ZemoUI), which every
   .dash-row column is. Stacked, the two together routinely need more than
   --z-dash-body-h -- six metered devices alone need more room than the ring
   leaves them. Fixing .z-chart's total height without giving the legend
   somewhere to go was the mistake: the overflow had nowhere to go but out of
   the card, over whatever sits below it on the page. Column layout with the
   ring pinned to its own real size and the legend as the flexible, scrolling
   part -- the same shape --z-feed/--z-roster already had -- fixes that: the
   ring is never clipped, and a legend that does not fit scrolls instead of
   spilling out, exactly the failure mode --z-feed/--z-roster were already
   built to avoid. */
.dash-row .z-chart {
  height: var(--z-dash-body-h);
  flex-direction: column;
  /* ZemoUI's .z-chart wraps. In a column of fixed height that wrap put the
     legend -- whose flex-basis of 190px becomes a height here -- in a second
     column beside the ring, at the full width of the card: the kWh and share
     of every device landed past the card's right edge and were clipped,
     leaving only the names. Measured 24 September 2026 with the real six
     metered devices. Never wrap: the legend stays under the ring and scrolls
     if it runs out of room. */
  flex-wrap: nowrap;
  overflow: hidden;
}
.dash-row .z-chart-legend {
  flex: 0 1 auto;
  width: 100%;
  min-height: 0;
  max-height: 100%;
  overflow-y: auto;
}

@layer app {
    /* Power cuts: a bar per day, and the four figures above them. */
    .outage-stats {
        margin-bottom: 0.75rem;
    }

    .outage-stat {
        display: flex;
        flex-direction: column;
        gap: 0.15rem;
    }

    .outage-stat strong {
        font-size: 1.25rem;
        font-variant-numeric: tabular-nums;
    }

    .outage-chart {
        margin: 0.25rem 0 0.75rem;
    }

    .outage-svg {
        width: 100%;
        height: 120px;
        display: block;
        border-bottom: 1px solid var(--z-border);
    }

    .outage-bar {
        fill: var(--z-danger-strong, #9c474c);
    }

    /* A day that never lost power still gets a mark on the baseline, so the
       chart reads as thirty days rather than as a few scattered bars. */
    .outage-none {
        fill: var(--z-border-strong);
    }

    .outage-axis {
        display: flex;
        justify-content: space-between;
        font-size: 0.75rem;
        padding-top: 0.35rem;
    }
}


/* ---- Performanse ---------------------------------------------------------
   Colour is set through custom properties from ZemoUI's data-viz tokens, so the
   dark theme swaps the hues without a rule of its own. The Content-Security-
   Policy allows no inline styles, which is why the charts are SVG driven by
   these classes and not by style attributes. Colour never works alone: each
   machine also has its own dash pattern and marker shape (WEB_APP_RULES 125). */
.perf-lede { margin-bottom: var(--z-space-4); max-width: 70ch; }
.perf-controls { display: flex; flex-wrap: wrap; gap: var(--z-space-3) var(--z-space-6); margin-bottom: var(--z-space-4); }
.perf-filter { display: flex; flex-direction: column; gap: var(--z-space-1); min-width: 0; }
.perf-filter-label { font-size: var(--z-font-sm); color: var(--z-muted); font-weight: 600; }
.perf-choices { display: flex; flex-wrap: wrap; gap: var(--z-space-1); }
.perf-choices .z-btn[aria-pressed="true"] {
  background: var(--z-selected);
  border-color: var(--z-primary);
  box-shadow: inset 0 0 0 1px var(--z-primary);
  font-weight: 700;
}
.perf-state { min-height: 1.5em; margin-bottom: var(--z-space-3); display: flex; flex-wrap: wrap; align-items: center; gap: var(--z-space-2); }
.perf-state:empty { min-height: 0; margin: 0; }
.perf-section-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--z-space-2) var(--z-space-4); margin: var(--z-space-4) 0 var(--z-space-2); }
.perf-section-title { font-size: var(--z-font-lg); margin: 0; }
.perf-hint { margin: 0 0 var(--z-space-3); }

.perf-machines {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: var(--z-layout-gap);
}
.perf-machine .z-badge { white-space: nowrap; }
.perf-machine { cursor: pointer; }
/* Not ticked: a dashed edge next to the empty tick box. Not dimmed -- dimmed text would drop under
   the contrast the rules ask for (WEB_APP_RULES 049), and colour would be the only signal. */
.perf-machine.is-off { border-style: dashed; }
.perf-pick { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--z-space-2); margin-top: var(--z-space-3); padding-top: var(--z-space-3); border-top: 1px solid var(--z-border-soft); }
.perf-check { display: inline-flex; align-items: center; gap: var(--z-space-2); cursor: pointer; min-height: 24px; }
.perf-check input { width: 18px; height: 18px; margin: 0; cursor: pointer; }
.perf-figures { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--z-space-2) var(--z-space-4); margin: var(--z-space-3) 0 0; }
.perf-figure { display: flex; justify-content: space-between; gap: var(--z-space-2); min-width: 0; }
.perf-figure dt { color: var(--z-muted); }
/* A figure may wrap under its label: with the text spacing of WCAG 1.4.12
   at 320 px "Prosek 12,4 %" no longer fits on one line (WCAG-22.md Z08). */
.perf-figure { flex-wrap: wrap; }
.perf-figure dd { margin: 0 0 0 auto; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }

.perf-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(560px, 100%), 1fr)); gap: var(--z-layout-gap); margin-top: var(--z-space-5); }
.perf-chart-card { min-width: 0; display: flex; flex-direction: column; gap: var(--z-space-3); }
.perf-chart-card h2 { font-size: var(--z-font-lg); line-height: var(--z-leading-tight); }
.perf-empty { margin: 0; }
.perf-name { display: inline-flex; align-items: center; gap: var(--z-space-2); }
.perf-swatch { width: 34px; height: 12px; flex: 0 0 auto; }
.perf-swatch-line { stroke: var(--perf-color); stroke-width: 2.5; }
/* Reserved height, so the chart does not jump when the readout appears. */
.perf-readout { min-height: 1.4em; font-variant-numeric: tabular-nums; }
.perf-chart { touch-action: pan-y; cursor: crosshair; border-radius: var(--z-radius-sm); }
.perf-plot { display: block; width: 100%; height: auto; overflow: visible; }
.perf-tick, .perf-axis-title { fill: var(--z-muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.perf-grid { stroke: var(--z-border-soft); stroke-width: 1; }
.perf-line { fill: none; stroke: var(--perf-color); stroke-width: 2.25; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.perf-marker { fill: var(--perf-color); stroke: none; }
.perf-marker .perf-marker-line { fill: none; stroke: var(--perf-color); stroke-width: 2.5; }
.perf-marker .perf-marker-ring { fill: var(--z-surface); stroke: var(--perf-color); stroke-width: 2.5; }
.perf-cursor { stroke: var(--z-muted); stroke-width: 1; opacity: 0; vector-effect: non-scaling-stroke; }
.perf-cursor.is-on { opacity: 1; }

.perf-s0 { --perf-color: var(--z-viz-1); }
.perf-s1 { --perf-color: var(--z-viz-2); }
.perf-s2 { --perf-color: var(--z-viz-3); }
.perf-s3 { --perf-color: var(--z-viz-4); }
.perf-s4 { --perf-color: var(--z-viz-5); }
.perf-s5 { --perf-color: var(--z-viz-6); }
.perf-s1 .perf-line, .perf-s1 .perf-swatch-line { stroke-dasharray: 8 4; }
.perf-s2 .perf-line, .perf-s2 .perf-swatch-line { stroke-dasharray: 2 4; }
.perf-s3 .perf-line, .perf-s3 .perf-swatch-line { stroke-dasharray: 10 3 2 3; }
.perf-s4 .perf-line, .perf-s4 .perf-swatch-line { stroke-dasharray: 5 2; }
.perf-s5 .perf-line, .perf-s5 .perf-swatch-line { stroke-dasharray: 1 3; }

.perf-summary, .perf-data { font-variant-numeric: tabular-nums; }
.perf-summary { width: 100%; }
/* The framework gives every table 620 px on a phone and expects a scrolling wrapper; this
   one has four short columns and must fit the card instead. */
@media (max-width: 620px) { .perf-summary { min-width: 0; } }
.perf-summary th[scope="row"], .perf-data th[scope="row"] { text-align: left; font-weight: 600; white-space: nowrap; }
.perf-summary tbody th, .perf-data tbody th { background: transparent; }
.perf-summary td, .perf-data td { text-align: right; }
.perf-summary thead th:not(:first-child), .perf-data thead th:not(:first-child) { text-align: right; }
.perf-caption { text-align: left; padding: 0 0 var(--z-space-2); color: var(--z-muted); font-size: var(--z-font-sm); caption-side: top; }
.perf-data-wrap { max-height: 320px; overflow: auto; }
.perf-actions { display: flex; flex-wrap: wrap; gap: var(--z-space-2); }
.perf-source { margin-top: var(--z-space-5); max-width: 80ch; }

@media (max-width: 480px) {
  .perf-summary th, .perf-summary td { padding-inline: 6px; }
  .perf-swatch { width: 26px; }
  .perf-name { gap: var(--z-space-1); }
}

/* Matter bridges: the roster row carries its button, and the pairing
   dialog a QR code large enough to scan from a phone held at arm's
   length. The code itself is drawn black on white in any theme. Outside
   @layer app, like the .dash-row rules: inside it ZemoUI's own sizing of
   dialog content won and drew the code 518px wide on a laptop. */
.matter-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
}

/* On a phone the name gets the row; badge and button go under it. */
@media (max-width: 560px) {
    .matter-row {
        grid-template-columns: auto minmax(0, 1fr);
    }

    .matter-actions {
        grid-column: 1 / -1;
    }
}

.matter-pairing {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 1rem 1.5rem;
}

.matter-qr {
    display: block;
    /* Also bound by the screen's height, so on a short phone the whole
       dialog, its Close button included, fits without scrolling. */
    width: min(240px, 70vw, 28dvh);
    height: auto;
    border-radius: 8px;
}

.matter-manual {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    text-align: center;
}

.matter-code {
    font-size: 1.5rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
}

.matter-steps {
    padding-left: 1.25rem;
    display: grid;
    gap: 0.25rem;
}

/* A ZemoUI badge never wraps. On a tile the pause note is a sentence with a
   date in it, and unwrapped it ran past the right edge of the tile on desktop
   (29. 9. 2026). */
.z-device-tile .pause-note {
    white-space: normal;
    max-width: 100%;
    align-items: flex-start;
}
.z-device-tile .pause-note .z-dot {
    flex: none;
    margin-top: 0.35em;
}
