:root {
  --page-bg: oklch(0.98 0.004 240);
  --card-bg: #ffffff;
  --card-shadow: 0 1px 2px oklch(0.2 0.02 240 / 0.04), 0 12px 32px oklch(0.2 0.02 240 / 0.06);
  --card-border: transparent;

  --text: oklch(0.22 0.015 240);
  --text-strong: oklch(0.25 0.015 240);
  --text-secondary: oklch(0.4 0.015 240);
  --text-muted: oklch(0.5 0.015 240);
  --text-faint: oklch(0.55 0.015 240);
  --text-faintest: oklch(0.6 0.015 240);
  --text-date: oklch(0.35 0.015 240);

  --border: oklch(0.92 0.006 240);
  --border-soft: oklch(0.94 0.004 240);
  --surface-2: oklch(0.97 0.006 240);

  --c-pv: oklch(0.7 0.14 75);
  --c-pv-icon: oklch(0.55 0.13 75);
  --c-pv-bar: oklch(0.85 0.08 75);
  --c-pv-node-bg: oklch(0.97 0.02 75);
  --c-pv-node-border: oklch(0.88 0.05 75);

  --c-home: oklch(0.65 0.02 240);
  --c-home-icon: oklch(0.42 0.015 240);
  --c-home-node-bg: oklch(0.96 0.004 240);
  --c-home-node-border: oklch(0.88 0.006 240);

  --c-storage: oklch(0.55 0.13 165);
  --c-storage-icon: oklch(0.48 0.11 165);
  --c-storage-node-bg: oklch(0.96 0.025 165);
  --c-storage-node-border: oklch(0.85 0.06 165);
  --c-storage-soft-bg: oklch(0.97 0.025 165);

  --c-grid: oklch(0.58 0.11 300);
  --c-grid-icon: oklch(0.5 0.1 300);
  --c-grid-node-bg: oklch(0.96 0.018 300);
  --c-grid-node-border: oklch(0.85 0.045 300);

  --c-forecast: oklch(0.5 0.13 250);
  --c-forecast-tomorrow: oklch(0.55 0.12 285);
  --c-consumption-line: oklch(0.55 0.02 240);

  /* Wykres "Aktualne ceny energii RCE" (Faza 1 integracji RCE, 2026-09-07) - odcien
     pomaranczowo-czerwony (hue 25), swiadomie odrebny od pv (75)/forecast (250/285)/grid (300)/
     storage (165), zeby cena RCE nie mylila sie wizualnie z zadna z istniejacych serii. */
  --c-rce: oklch(0.6 0.15 25);
  --c-rce-tomorrow: oklch(0.68 0.1 25);

  --accent-ai-1: oklch(0.62 0.16 290);
  --accent-ai-2: oklch(0.55 0.18 320);
  --accent-financial: oklch(0.55 0.12 200);
  --accent-history: oklch(0.55 0.12 260);

  --hub-grad-1: oklch(0.4 0.05 230);
  --hub-grad-2: oklch(0.3 0.04 235);
  --hub-shadow: oklch(0.3 0.04 235 / 0.35);

  --donut-track: oklch(0.93 0.008 240);
  --c-reserve: oklch(0.78 0.05 45);
  --chart-axis: oklch(0.92 0.006 240);
  --chart-now-line: oklch(0.6 0.015 240);
  --chart-label: oklch(0.6 0.015 240);

  --badge-pv-text: oklch(0.42 0.1 75);
  --badge-pv-bg: oklch(0.94 0.045 75);
  --badge-storage-text: oklch(0.42 0.1 165);
  --badge-storage-bg: oklch(0.93 0.045 165);
  --badge-cheap-text: oklch(0.42 0.1 165);
  --badge-cheap-bg: oklch(0.93 0.045 165);
  --badge-cheap-dot: oklch(0.5 0.13 165);
  --badge-expensive-text: oklch(0.48 0.16 35);
  --badge-expensive-bg: oklch(0.94 0.05 35);
  --badge-expensive-dot: oklch(0.55 0.18 35);

  --toggle-track-bg: oklch(0.94 0.006 240);
  --toggle-btn-bg: #ffffff;
  --toggle-btn-shadow: 0 1px 4px oklch(0.2 0.02 240 / 0.15);
  --toggle-icon-active: oklch(0.5 0.08 75);
  --toggle-icon-inactive: oklch(0.6 0.01 240);

  --live-dot: oklch(0.6 0.15 145);
  --live-text: oklch(0.5 0.13 145);

  --pill-bg: oklch(0.94 0.03 290);
  --pill-text: oklch(0.5 0.1 290);

  --info-box-bg: var(--surface-2);
  --bill-bg: var(--surface-2);
  --bill-passive-value: oklch(0.4 0.015 240);
  --bill-active-bg: oklch(0.97 0.03 165);
  --bill-active-border: oklch(0.85 0.06 165);
  --bill-active-text: var(--c-storage);

  --savings-positive: var(--c-storage);
  --hero-down: oklch(0.55 0.16 25);

  --radius: 24px;
  --radius-sm: 14px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --page-bg: oklch(0.16 0.012 250);
    --card-bg: oklch(0.2 0.013 250);
    --card-shadow: 0 12px 32px oklch(0 0 0 / 0.35);
    --card-border: oklch(0.27 0.013 250);

    --text: oklch(0.92 0.006 250);
    --text-strong: #ffffff;
    --text-secondary: oklch(0.78 0.008 250);
    --text-muted: oklch(0.62 0.012 250);
    --text-faint: oklch(0.6 0.012 250);
    --text-faintest: oklch(0.55 0.012 250);
    --text-date: oklch(0.78 0.008 250);

    --border: oklch(0.28 0.012 250);
    --border-soft: oklch(0.27 0.013 250);
    --surface-2: oklch(0.24 0.013 250);

    --c-pv: oklch(0.72 0.14 75);
    --c-pv-icon: oklch(0.78 0.13 75);
    --c-pv-bar: oklch(0.62 0.11 75);
    --c-pv-node-bg: oklch(0.27 0.035 75);
    --c-pv-node-border: oklch(0.4 0.07 75);

    --c-home: oklch(0.5 0.012 250);
    --c-home-icon: oklch(0.8 0.008 250);
    --c-home-node-bg: oklch(0.26 0.008 250);
    --c-home-node-border: oklch(0.36 0.01 250);

    --c-storage: oklch(0.68 0.14 165);
    --c-storage-icon: oklch(0.78 0.13 165);
    --c-storage-node-bg: oklch(0.27 0.035 165);
    --c-storage-node-border: oklch(0.42 0.08 165);
    --c-storage-soft-bg: oklch(0.25 0.03 165);

    --c-grid: oklch(0.65 0.12 300);
    --c-grid-icon: oklch(0.75 0.12 300);
    --c-grid-node-bg: oklch(0.27 0.03 300);
    --c-grid-node-border: oklch(0.42 0.07 300);

    --c-forecast: oklch(0.62 0.14 250);
    --c-forecast-tomorrow: oklch(0.68 0.13 285);
    --c-consumption-line: oklch(0.7 0.012 250);

    --c-rce: oklch(0.68 0.16 25);
    --c-rce-tomorrow: oklch(0.75 0.11 25);

    --accent-ai-1: oklch(0.66 0.15 290);
    --accent-ai-2: oklch(0.6 0.17 320);
    --accent-financial: oklch(0.65 0.12 200);
    --accent-history: oklch(0.65 0.12 260);

    --hub-grad-1: oklch(0.62 0.14 165);
    --hub-grad-2: oklch(0.5 0.12 175);
    --hub-shadow: oklch(0.55 0.13 165 / 0.4);

    --donut-track: oklch(0.3 0.01 250);
    --c-reserve: oklch(0.55 0.06 45);
    --chart-axis: oklch(0.3 0.012 250);
    --chart-now-line: oklch(0.5 0.012 250);
    --chart-label: oklch(0.6 0.012 250);

    --badge-pv-text: oklch(0.85 0.09 75);
    --badge-pv-bg: oklch(0.28 0.05 75);
    --badge-storage-text: oklch(0.85 0.08 165);
    --badge-storage-bg: oklch(0.28 0.05 165);
    --badge-cheap-text: oklch(0.85 0.08 165);
    --badge-cheap-bg: oklch(0.28 0.05 165);
    --badge-cheap-dot: oklch(0.68 0.14 165);
    --badge-expensive-text: oklch(0.85 0.1 35);
    --badge-expensive-bg: oklch(0.3 0.06 35);
    --badge-expensive-dot: oklch(0.68 0.18 35);

    --toggle-track-bg: oklch(0.24 0.012 250);
    --toggle-btn-bg: oklch(0.32 0.012 250);
    --toggle-btn-shadow: 0 1px 4px oklch(0 0 0 / 0.3);
    --toggle-icon-active: oklch(0.85 0.1 75);
    --toggle-icon-inactive: oklch(0.85 0.02 250);

    --live-dot: oklch(0.68 0.14 145);
    --live-text: oklch(0.68 0.14 145);

    --pill-bg: oklch(0.3 0.06 290);
    --pill-text: oklch(0.82 0.1 290);

    --bill-passive-value: oklch(0.78 0.008 250);
    --bill-active-bg: oklch(0.25 0.04 165);
    --bill-active-border: oklch(0.42 0.08 165);
    --bill-active-text: var(--c-storage);

    --hero-down: oklch(0.68 0.15 25);
  }
}

:root[data-theme="dark"] {
  --page-bg: oklch(0.16 0.012 250);
  --card-bg: oklch(0.2 0.013 250);
  --card-shadow: 0 12px 32px oklch(0 0 0 / 0.35);
  --card-border: oklch(0.27 0.013 250);

  --text: oklch(0.92 0.006 250);
  --text-strong: #ffffff;
  --text-secondary: oklch(0.78 0.008 250);
  --text-muted: oklch(0.62 0.012 250);
  --text-faint: oklch(0.6 0.012 250);
  --text-faintest: oklch(0.55 0.012 250);
  --text-date: oklch(0.78 0.008 250);

  --border: oklch(0.28 0.012 250);
  --border-soft: oklch(0.27 0.013 250);
  --surface-2: oklch(0.24 0.013 250);

  --c-pv: oklch(0.72 0.14 75);
  --c-pv-icon: oklch(0.78 0.13 75);
  --c-pv-bar: oklch(0.62 0.11 75);
  --c-pv-node-bg: oklch(0.27 0.035 75);
  --c-pv-node-border: oklch(0.4 0.07 75);

  --c-home: oklch(0.5 0.012 250);
  --c-home-icon: oklch(0.8 0.008 250);
  --c-home-node-bg: oklch(0.26 0.008 250);
  --c-home-node-border: oklch(0.36 0.01 250);

  --c-storage: oklch(0.68 0.14 165);
  --c-storage-icon: oklch(0.78 0.13 165);
  --c-storage-node-bg: oklch(0.27 0.035 165);
  --c-storage-node-border: oklch(0.42 0.08 165);
  --c-storage-soft-bg: oklch(0.25 0.03 165);

  --c-grid: oklch(0.65 0.12 300);
  --c-grid-icon: oklch(0.75 0.12 300);
  --c-grid-node-bg: oklch(0.27 0.03 300);
  --c-grid-node-border: oklch(0.42 0.07 300);

  --c-forecast: oklch(0.62 0.14 250);
  --c-forecast-tomorrow: oklch(0.68 0.13 285);
  --c-consumption-line: oklch(0.7 0.012 250);

  --c-rce: oklch(0.68 0.16 25);
  --c-rce-tomorrow: oklch(0.75 0.11 25);

  --accent-ai-1: oklch(0.66 0.15 290);
  --accent-ai-2: oklch(0.6 0.17 320);
  --accent-financial: oklch(0.65 0.12 200);
  --accent-history: oklch(0.65 0.12 260);

  --hub-grad-1: oklch(0.62 0.14 165);
  --hub-grad-2: oklch(0.5 0.12 175);
  --hub-shadow: oklch(0.55 0.13 165 / 0.4);

  --donut-track: oklch(0.3 0.01 250);
  --c-reserve: oklch(0.55 0.06 45);
  --chart-axis: oklch(0.3 0.012 250);
  --chart-now-line: oklch(0.5 0.012 250);
  --chart-label: oklch(0.6 0.012 250);

  --badge-pv-text: oklch(0.85 0.09 75);
  --badge-pv-bg: oklch(0.28 0.05 75);
  --badge-storage-text: oklch(0.85 0.08 165);
  --badge-storage-bg: oklch(0.28 0.05 165);
  --badge-cheap-text: oklch(0.85 0.08 165);
  --badge-cheap-bg: oklch(0.28 0.05 165);
  --badge-cheap-dot: oklch(0.68 0.14 165);
  --badge-expensive-text: oklch(0.85 0.1 35);
  --badge-expensive-bg: oklch(0.3 0.06 35);
  --badge-expensive-dot: oklch(0.68 0.18 35);

  --toggle-track-bg: oklch(0.24 0.012 250);
  --toggle-btn-bg: oklch(0.32 0.012 250);
  --toggle-btn-shadow: 0 1px 4px oklch(0 0 0 / 0.3);
  --toggle-icon-active: oklch(0.85 0.1 75);
  --toggle-icon-inactive: oklch(0.85 0.02 250);

  --live-dot: oklch(0.68 0.14 145);
  --live-text: oklch(0.68 0.14 145);

  --pill-bg: oklch(0.3 0.06 290);
  --pill-text: oklch(0.82 0.1 290);

  --bill-passive-value: oklch(0.78 0.008 250);
  --bill-active-bg: oklch(0.25 0.04 165);
  --bill-active-border: oklch(0.42 0.08 165);
  --bill-active-text: var(--c-storage);

  --hero-down: oklch(0.68 0.15 25);
}

* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--page-bg);
  color: var(--text);
  font-family: 'Plus Jakarta Sans', system-ui, -apple-system, "Segoe UI", sans-serif;
}
a { color: var(--c-storage); }

/* POPRAWKA 2026-09-07 - padding-bottom podniesiony z 60px na 220px (tylko baza/desktop - patrz
   nadpisanie w @media (max-width: 768px) nizej, gdzie wraca do wartosci sprzed tej poprawki),
   zeby pasek .event-log-bar (Wariant B, na stale przyklejony do dolu ekranu TYLKO na desktopie)
   nigdy nie zaslanial ostatniej karty ("Historia 7 dni"). Na telefonie pasek sie nie pokazuje
   (patrz .event-log-bar nizej), wiec dodatkowy odstep tam nie jest potrzebny. */
.wrap { max-width: 1360px; margin: 0 auto; padding: 40px 24px 220px; display: flex; flex-direction: column; gap: 24px; }

.site-header { display: flex; flex-direction: column; gap: 12px; }
.header-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }

.brand { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.brand-icon {
  width: 38px; height: 38px; border-radius: 11px; background: var(--c-storage);
  display: flex; align-items: center; justify-content: center; font-size: 21px; line-height: 1;
}
.brand h1 { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 27.5px; letter-spacing: -0.01em; margin: 0; color: var(--text-strong); }

.badges { display: flex; flex-wrap: wrap; gap: 8px; }
.badge { font-size: 14px; font-weight: 600; padding: 6px 12px; border-radius: 100px; display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.badge-amber { color: var(--badge-pv-text); background: var(--badge-pv-bg); }
.badge-green { color: var(--badge-storage-text); background: var(--badge-storage-bg); }
.badge-tariff { color: var(--badge-expensive-text); background: var(--badge-expensive-bg); }
.badge-tariff .dot { background: var(--badge-expensive-dot); }
.badge-tariff.cheap { color: var(--badge-cheap-text); background: var(--badge-cheap-bg); }
.badge-tariff.cheap .dot { background: var(--badge-cheap-dot); }
.badge .dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }

/* POPRAWKA 2026-09-06 #21 (zgloszona prosba: "zrob znak BETA tak jak to jest na rogozino.pl") -
   ta sama para tokenow co plakietka magazynu (`--badge-storage-*`, zielona), zeby plakietka BETA
   wygladala spojnie z reszta aplikacji zamiast kopiowac kolorystyke z innej domeny 1:1. */
.beta-badge {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--badge-storage-text); background: var(--badge-storage-bg);
  padding: 3px 8px; border-radius: 100px; white-space: nowrap;
}

/* POPRAWKA 2026-09-06 #21 (zgloszona prosba: "daj powrót do strony głównej i daj jeszcze info o
   projekcie w podobnym stylu jak jest na rogozino.pl z tym samym oknem modalnym") - `.icon-btn`
   to okragle przyciski w prawym gornym rogu naglowka, obok istniejacego `.theme-toggle` - ten
   sam rozmiar/tlo (`--toggle-track-bg`) dla spojnosci wizualnej.
   POPRAWKA 2026-09-06 #22 (zgloszony problem: "musi się wyświetlać prawidłowo" + wybor
   uzytkownika z 4 zaproponowanych opcji: "Mała okrągła ikonka w rzędzie") - wczesniejszy
   `.home-link` (dluga pigulka z tekstem "Strona głowna") mial tlo tylko 4% rozne od tla strony
   (`--toggle-track-bg` vs `--page-bg`), wiec przy kompresji/skalowaniu zrzutu ekranu wypelnienie
   "znikalo" i zostawala tylko myląca obwodka - USUNIETY na rzecz trzeciego `.icon-btn` (link
   powrotny do rogozino.pl), identycznego z przyciskiem "i" - `text-decoration: none` dodane
   tutaj (a nie tylko per-element), bo `.icon-btn` jest teraz uzywany zarowno na `<button>` (nie
   potrzebuje tej wlasciwosci) jak i na `<a>` (potrzebuje, zeby przegladarka nie podkreslala
   ikony jako tekst linku). */
.header-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.icon-btn {
  display: flex; align-items: center; justify-content: center; width: 34px; height: 34px;
  border-radius: 50%; background: var(--toggle-track-bg); border: none; cursor: pointer;
  color: var(--text-secondary); flex-shrink: 0; text-decoration: none;
  transition: background 0.15s ease;
}
.icon-btn:hover { background: var(--card-border); }

/* POPRAWKA 2026-09-06 #21 - modal "O tej instalacji", ten sam wzorzec UX co modal "O projekcie
   Rogozino AI" na rogozino.pl (przycisk "i" w naglowku -> wysrodkowane okno z tlem-nakladka).
   `.modal-box` dziedziczy wyglad z `.card` (tlo/border/radius/cien/padding), doklejamy tylko to,
   czego `.card` nie ma: ograniczenie szerokosci/wysokosci i przewijanie dla dluzszej tresci. */
.modal-overlay {
  position: fixed; inset: 0; background: oklch(0.15 0.02 250 / 0.5);
  display: flex; align-items: center; justify-content: center; padding: 24px; z-index: 60;
}
.modal-overlay[hidden] { display: none; }
.modal-box.card { max-width: 520px; width: 100%; max-height: 85vh; overflow-y: auto; }
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.modal-close {
  background: none; border: none; cursor: pointer; color: var(--text-muted); padding: 4px;
  border-radius: 50%; display: flex; flex-shrink: 0; margin: -4px -4px 0 0;
  transition: background 0.15s ease, color 0.15s ease;
}
.modal-close:hover { background: var(--toggle-track-bg); color: var(--text-strong); }
.modal-lead { font-size: 14.5px; color: var(--text-secondary); line-height: 1.5; margin: 10px 0 0; }
.modal-section { margin-top: 20px; }
.modal-section .balance-section-title { margin-bottom: 10px; }

.theme-toggle {
  display: flex; align-items: center; gap: 2px; background: var(--toggle-track-bg); border-radius: 100px; padding: 4px;
  border: none; cursor: pointer; flex-shrink: 0;
}
.theme-toggle span,
.theme-toggle .icon-sun,
.theme-toggle .icon-moon { display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 50%; }
.theme-toggle .icon-sun { background: var(--toggle-btn-bg); box-shadow: var(--toggle-btn-shadow); color: var(--toggle-icon-active); }
.theme-toggle .icon-moon { color: var(--toggle-icon-inactive); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { background: transparent; box-shadow: none; color: var(--toggle-icon-inactive); }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { background: var(--toggle-btn-bg); box-shadow: var(--toggle-btn-shadow); color: var(--toggle-icon-active); }
}
:root[data-theme="dark"] .theme-toggle .icon-sun { background: transparent; box-shadow: none; color: var(--toggle-icon-inactive); }
:root[data-theme="dark"] .theme-toggle .icon-moon { background: var(--toggle-btn-bg); box-shadow: var(--toggle-btn-shadow); color: var(--toggle-icon-active); }

.card {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--card-shadow);
  border: 1px solid var(--card-border);
  position: relative;
  overflow: hidden;
  padding: 24px 28px;
}
.card-accent::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; }
.accent-ai::before { background: linear-gradient(var(--accent-ai-1), var(--accent-ai-2)); }
.accent-storage::before { background: var(--c-storage); }
.accent-pv::before { background: var(--c-pv); }
.accent-financial::before { background: var(--accent-financial); }
.accent-history::before { background: var(--accent-history); }
/* Karta "Aktualne ceny energii RCE" (Faza 1 integracji RCE, 2026-09-07) - akcent WPROST z
   var(--c-rce) (ten sam wzorzec co .accent-pv powyzej), nie z osobnej --accent-* zmiennej jak
   .accent-financial/.accent-history - RCE ma juz wlasny, dedykowany token koloru serii. */
.accent-rce::before { background: var(--c-rce); }

.card-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.card-title { font-size: 14.5px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; }
.subtitle { font-size: 14px; color: var(--text-faintest); margin-top: 4px; }

/* POPRAWKA 2026-09-05 (zgloszony problem: "jak najezdzam na wykres to wyswietlaja sie
   wspolrzedne, ale to powoduje ze wykres przesuwa sie delikatnie w dol") - `.subtitle` (zwykly
   tekst, bez wlasnego pudelka) i `.chart-readout` (padding 6px/12px + ramka 1px - na wczesniejsza
   prosbe uzytkownika "mozna to wziac w obramowanie i uwypuklic") maja INNY model pudelka o ok.
   14px roznicy wysokosci (sam padding+ramka). initChartHover (live.js) podmienia jeden na drugi
   przy najechaniu myszka na wykres, wiec ta roznica podnosila/obnizala wysokosc naglowka karty -
   a wraz z nim caly wykres pod spodem - przy kazdym wejsciu/wyjsciu kursora. Zmierzone na zywo:
   podtytul 17.3px wysokosci, odczyt 31.1px, wykres przesuwal sie o ok. 13.8px.
   Naprawa: `.chart-hover-subtitle` dokleja podtytulowi DOKLADNIE TAKI SAM padding/ramke
   (przezroczysta, wiec niewidoczna na oko) jak `.chart-readout` - obie wersje maja teraz
   identyczna wysokosc pudelka, wiec zamiana nie zmienia juz wysokosci naglowka. Klase dostaja
   TYLKO podtytuly SPAROWANE z odczytem najechania (wykresy "dzis i jutro" i "wczoraj" w
   dashboard.html) - zwykle podtytuly bez pary (np. karta oszczednosci, "historia 7 dni")
   zostaja bez zmian, stad osobna klasa zamiast zmiany calej `.subtitle`. */
.chart-hover-subtitle { padding: 6px 12px; border: 1px solid transparent; border-radius: 10px; box-sizing: border-box; }

.live { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; color: var(--live-text); text-transform: uppercase; letter-spacing: 0.05em; }
.live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--live-dot); display: inline-block; animation: pulse-live 1.6s ease-in-out infinite; }
@keyframes pulse-live { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.35; transform: scale(0.7); } }

@keyframes flow-down { to { stroke-dashoffset: -20; } }
@keyframes flow-right { to { stroke-dashoffset: -20; } }
.flow-v { stroke-dasharray: 1 9; animation: flow-down 0.9s linear infinite; }
.flow-h { stroke-dasharray: 1 9; animation: flow-right 0.9s linear infinite; }
.flow-h.flow-reverse { animation-direction: reverse; }
/* Stan "spoczynek" magazynu (rozdz. 6.1, na zgloszony problem "magazyn nic nie robi a jest
   strzalka animacja ze oddaje energie") - zatrzymuje animacje przeplywu i przygasza linie,
   grot strzalki (nastepny element w DOM) zostaje CALKOWICIE ukryty, zamiast pokazywac
   dowolny, przypadkowy kierunek. */
.flow-h.flow-idle { animation: none; opacity: 0.28; }
.flow-h.flow-idle + polygon { opacity: 0; }

.stat-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.stat-row-sub { padding-left: 27px; }
.stat-label { display: flex; align-items: center; gap: 10px; font-size: 15px; color: var(--text-secondary); }
.stat-label-strong { font-weight: 600; color: var(--text-strong); }
.stat-label svg { flex-shrink: 0; }
.stat-sub-label { font-size: 14px; color: var(--text-faint); }
.stat-sub-value { font-family: 'Outfit', sans-serif; font-weight: 600; font-size: 15px; color: var(--text-muted); white-space: nowrap; flex-shrink: 0; }
.stat-value { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 16.5px; color: var(--text-strong); white-space: nowrap; flex-shrink: 0; }
.stat-value.accent { color: var(--c-storage); }
.stat-list { display: flex; flex-direction: column; gap: 14px; }

/* POPRAWKA 2026-09-05 #10 (zgloszony problem: "ale czemu sa odstepy ?" - po dodaniu osobnej
   notatki pod kazdym wierszem w grupie "Dodatkowo z PV i magazynu" odstepy wygladaly tak samo
   duze miedzy WIERSZEM a JEGO WLASNA notatka, jak miedzy notatka a KOLEJNYM wierszem (bo
   `.stat-list` ma jeden wspolny `gap: 14px` dla wszystkich dzieci, wiec para wiersz+notatka nie
   byla wizualnie "sklejona"). `.stat-list-notes` to wariant tylko dla list mieszajacych
   `.stat-row` z `.savings-note` (na razie: grupa pasywna w savings_card.html) - NIE dotyka
   zwyklego `.stat-list` uzywanego np. w balance_card.html (tam sa same rownorzedne wiersze bez
   notatek, wiec stary, rowny odstep 14px zostaje bez zmian). Mniejszy bazowy `gap` sklejwa
   wiersz z jego notatka (razem z `margin-top: 3px` notatki daje ok. 7px), a `margin-top` na
   kolejnym wierszu (nie pierwszym) dokleja wiekszy odstep PRZED nowa grupa wiersz+notatka. */
.stat-list-notes { gap: 4px; }
.stat-list-notes .stat-row:not(:first-child) { margin-top: 16px; }

.balance-divider { height: 1px; background: var(--border); margin: 18px 0; }

.row-flow { display: flex; gap: 24px; align-items: stretch; }
.diagram-card { flex: 1.55 1 0; min-width: 0; padding: 28px 32px 36px; }
.balance-card { flex: 1 1 0; min-width: 0; padding: 28px 26px 30px; display: flex; flex-direction: column; }

.diagram-canvas { position: relative; width: 100%; aspect-ratio: 800 / 520; margin-top: 8px; }
.diagram-lines { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }

/* POPRAWKA 2026-09-06 #25 (zgloszony problem: "w diagramie nie masz falownika na srodku i jest
   lekko przesuniety" - zrzuty z iPhone'a) - `left`/`top` wczesniej wskazywaly gorny-lewy rog
   pudelka (45%/40.4%), a srodek wychodzil poprawnie na 50%/48.1% TYLKO dopoki realna szerokosc/
   wysokosc pudelka rowna sie dokladnie 10%/15.4% szerokosci/wysokosci `.diagram-canvas`. Na
   waskim telefonie 10% szerokosci canvasu bywa mniejsze niz `min-width: 60px` (np. canvas 320px ->
   10% = 32px < 60px), wiec przegladarka wymusza min. szerokosc 60px - ale ROG pudelka zostaje w
   tym samym miejscu (45%), wiec dodatkowe px "doklejaja sie" tylko po PRAWEJ/DOLNEJ stronie,
   wizualnie przesuwajac srodek w prawo/w dol. Naprawa: `left`/`top` wskazuja teraz sam SRODEK
   (50%/48.1% - te same wspolrzedne co poprzednio wyliczony srodek: 45+10/2 i 40.4+15.4/2), a
   `transform: translate(-50%, -50%)` centruje pudelko WOKOL tego punktu niezaleznie od jego
   faktycznej (procentowej lub min-width/min-height wymuszonej) szerokosci/wysokosci. */
.diagram-hub {
  position: absolute; left: 50%; top: 48.1%; width: 10%; height: 15.4%;
  transform: translate(-50%, -50%);
  border-radius: 22px; background: linear-gradient(155deg, var(--hub-grad-1), var(--hub-grad-2));
  box-shadow: 0 10px 28px var(--hub-shadow);
  display: flex; align-items: center; justify-content: center;
  min-width: 60px; min-height: 60px;
}

.diagram-node { position: absolute; width: 14.5%; min-width: 140px; max-width: 176px; text-align: center; }

.node-pv { left: 50%; top: 1.5%; transform: translateX(-50%); }
.node-home { left: 50%; top: 71.2%; transform: translateX(-50%); }
.node-storage { left: 21.75%; top: 40.8%; transform: translateX(-50%); }
.node-grid { left: 78.25%; top: 40.8%; transform: translateX(-50%); }

.flow-icon-circle {
  width: 76px; height: 76px; max-width: 90%; aspect-ratio: 1;
  margin: 0 auto 10px; border-radius: 50%; border: 1.5px solid transparent;
  display: flex; align-items: center; justify-content: center;
}
.flow-icon-circle svg { width: 40%; height: 40%; }
.circle-pv { background: var(--c-pv-node-bg); border-color: var(--c-pv-node-border); }
.circle-home { background: var(--c-home-node-bg); border-color: var(--c-home-node-border); }
.circle-storage { background: var(--c-storage-node-bg); border-color: var(--c-storage-node-border); }
.circle-grid { background: var(--c-grid-node-bg); border-color: var(--c-grid-node-border); }

.diagram-label { font-size: 14.5px; color: var(--text-muted); margin-bottom: 2px; }
.diagram-value { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 22px; color: var(--text-strong); }
/* Plakietka stanu magazynu (rozdz. 6.1, "Ladowanie"/"Rozladowanie"/"Spoczynek") - aktualizowana
   na zywo przez SSE (live.js: setBatteryFlowState), kolor zalezny od data-state. */
.diagram-flow-badge {
  display: inline-block; margin-top: 5px; padding: 2px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
}
.diagram-flow-badge[data-state="charging"], .diagram-flow-badge[data-state="discharging"] {
  background: var(--c-storage-node-bg); color: var(--c-storage);
}
.diagram-flow-badge[data-state="idle"] { background: var(--surface-2); color: var(--text-faint); }

.day-summary { margin-top: 32px; padding-top: 22px; border-top: 1px solid var(--border); }
.day-summary-title { font-size: 13px; font-weight: 700; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 14px; }
/* CSS grid (nie flex-wrap) - rozdz. 6.1, na zgloszony problem "rostrzelone", kiedy 6 kafelkow
   nie dzieli sie rowno na pelne wiersze: flex-wrap z `flex: 1 1 150px` rozciagal kafelki w
   NIEPELNYM ostatnim wierszu (2 sztuki) na cala szerokosc, wygladajac na przypadkowo
   rozstrzelone i wiekszsze niz reszta. Stala liczba kolumn gridu daje kazdemu kafelkowi ZAWSZE
   ta sama szerokosc, niezaleznie od tego, ile ich jest w ostatnim wierszu. */
/* POPRAWKA 2026-09-06 #26 (zgloszony problem: "w sekcji przebieg dnia uciete bloki" - zrzuty z
   iPhone'a) - `1fr` w CSS Grid to skrot od `minmax(auto, 1fr)`: kolumna NIGDY nie skurczy sie
   ponizej "auto" (min-content) zawartosci. Dluzsze etykiety kafelka (np. "Szczyt mocy PV dziś")
   lamia sie normalnie po slowach, ale sama IKONA (`.summary-tile-icon`, sztywne 40px) plus
   padding/gap ustalaja minimalna szerokosc kafelka - na waskim telefonie (2 kolumny) ta minimalna
   szerokosc bywa wieksza niz dostepna 1/2 szerokosci karty, wiec siatka rozciaga kolumny ponad
   szerokosc `.diagram-card`, a `.card { overflow: hidden }` (patrz wyzej w pliku) po prostu UCINA
   nadmiar zamiast pokazac scroll - stad "uciete bloki". `minmax(0, 1fr)` pozwala kolumnie skurczyc
   sie do 0 zamiast do min-content, wiec siatka zawsze miesci sie w dostepnej szerokosci karty; do
   tego `min-width: 0` na kafelku i jego kolumnie tekstu (ponizej) pozwala WEWNETRZNEMU flexowi
   rowniez skurczyc sie i zawijac tekst, zamiast wypychac wlasna szerokosc. */
.summary-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.summary-tile {
  display: flex; align-items: center; gap: 12px; min-width: 0;
  padding: 14px 16px; border-radius: 16px;
  background: var(--surface-2); border: 1px solid var(--border);
}
.summary-tile > div:last-child { min-width: 0; }
.summary-tile-icon {
  flex: 0 0 40px; width: 40px; height: 40px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
}
.summary-tile-icon svg { width: 20px; height: 20px; }
.summary-tile-icon.tile-pv, .summary-tile-icon.tile-peak { background: var(--c-pv-node-bg); border: 1px solid var(--c-pv-node-border); }
.summary-tile-icon.tile-self { background: var(--c-storage-node-bg); border: 1px solid var(--c-storage-node-border); }
.summary-tile-icon.tile-coverage { background: var(--c-home-node-bg); border: 1px solid var(--c-home-node-border); }
.summary-tile-value { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 21px; color: var(--text-strong); line-height: 1.2; }
/* POPRAWKA 2026-09-06 #27 (zgloszony problem: "autokonsumpcja wychodzi poza ekran" - zrzut z
   iPhone'a) - `.summary-tiles`/`.summary-tile` maja juz `minmax(0, 1fr)`/`min-width: 0` (POPRAWKA
   #26 wyzej), wiec siatka/kafelek NIE wychodzi juz poza karte - ale "Autokonsumpcja" to JEDNO
   dlugie slowo bez spacji, wiec nie ma gdzie sie zawinac normalnie (po slowach); bez tej regoly
   przegladarka po prostu pozwala mu wizualnie wyjsc poza wlasny kafelek (a na waskim telefonie w
   2 kolumnach czesto akurat poza widoczny ekran). `overflow-wrap: break-word` pozwala przelamac
   slowo w dowolnym miejscu, GDY naprawde sie nie miesci - dla krotszych etykiet ("Autokonsumpcja"
   na szerszym ekranie, wszystkie pozostale) nic sie nie zmienia. */
.summary-tile-label { font-size: 13px; color: var(--text-muted); margin-top: 1px; overflow-wrap: break-word; }

.balance-head { margin-bottom: 22px; display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 10px 12px; }
.balance-head > * { flex-shrink: 0; }
.balance-date { font-size: 15.5px; color: var(--text-date); margin-top: 4px; }

.battery-badge { display: flex; align-items: center; gap: 10px; background: var(--c-storage-soft-bg); border-radius: 999px; padding: 6px 14px 6px 6px; flex-shrink: 0; }
.battery-badge-icon { width: 30px; height: 30px; border-radius: 50%; background: var(--c-storage); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.battery-badge-icon svg { width: 58%; height: 58%; }
.battery-badge-text { line-height: 1.3; }
.battery-badge-brand { font-weight: 700; font-size: 13px; color: var(--text-strong); }
.battery-badge-model { font-size: 11.5px; color: var(--text-muted); }

.battery-status-row { display: flex; align-items: center; gap: 20px; margin-bottom: 20px; }
.battery-soc-ring { position: relative; width: 108px; height: 108px; flex-shrink: 0; }
.battery-soc-ring svg { width: 100%; height: 100%; }
.battery-soc-ring-label { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.battery-soc-pct { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 22px; color: var(--text-strong); }
.battery-soc-caption { font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; margin-top: 2px; }

.battery-status-text { min-width: 0; }
.battery-hero { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 26px; color: var(--text-strong); }
.battery-hero-label { font-size: 14px; color: var(--text-muted); margin-top: 2px; margin-bottom: 8px; }
.battery-hero-desc { font-size: 13px; color: var(--text-faint); line-height: 1.45; margin: 0; }

.battery-chip-row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 24px; }
.battery-chip { flex: 1 1 calc(50% - 6px); min-width: 140px; display: flex; align-items: center; gap: 10px; background: var(--c-storage-soft-bg); border-radius: var(--radius-sm); padding: 10px 14px; }
.battery-chip-icon { width: 26px; height: 26px; border-radius: 50%; background: var(--c-storage); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.battery-chip-icon svg { width: 55%; height: 55%; }
.battery-chip-label { font-size: 11.5px; color: var(--text-muted); }
.battery-chip-value { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 16px; color: var(--text-strong); margin-top: 1px; }
/* Krotki opis pod wartoscia chipa (rozdz. 6.1, 2026-09-05) - narazie tylko chip "Rezerwa
   energii", zeby wyjasnic skad sie bierze ta liczba bez mylenia jej z "Pozostala energia
   uzyteczna" w hero powyzej. */
.battery-chip-desc { font-size: 10.5px; color: var(--text-faint); line-height: 1.3; margin-top: 1px; }
.balance-section-title { font-size: 13px; font-weight: 700; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 12px; }
/* POPRAWKA 2026-09-06 #31 (zgloszona prosba: "zrob naglowek Solplanet podaj model falownika" +
   wybrany wariant "ikonka" zamiast stalego opisu na widoku, patrz balance_card.html) - naglowek
   podsekcji z malutkim przyciskiem "i" obok, ktory pokazuje/chowa `.info-popover`. `position:
   relative` na wierszu (nie na samym `.balance-section-title`) - tak, zeby popover mogl sie
   pozycjonowac wzgledem CALEGO wiersza (a wiec zaczynac sie od lewej krawedzi karty), a nie
   tylko wzgledem szerokosci samego tekstu naglowka. */
.balance-section-title-row { display: flex; align-items: center; gap: 6px; margin-bottom: 12px; position: relative; }
.balance-section-title-row .balance-section-title { margin-bottom: 0; }
.info-icon-btn {
  display: flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%; padding: 0; border: none; cursor: pointer;
  background: var(--toggle-track-bg); color: var(--text-faint); flex-shrink: 0;
  transition: background 0.15s ease;
}
.info-icon-btn:hover, .info-icon-btn[aria-expanded="true"] { background: var(--card-border); }
.info-icon-btn svg { width: 12px; height: 12px; }
/* `max-width: min(260px, 100%)` (nie sztywne 260px) - `.card` ma `overflow: hidden` (patrz
   wyzej w pliku), wiec popover szerszy niz dostepna szerokosc karty (zdarza sie na waskim
   telefonie, gdzie tresc karty bywa wezsza niz 260px) zostalby po prostu UCIETY zamiast sie
   zawinac - `100%` liczy sie tu wzgledem szerokosci `.balance-section-title-row` (najblizszy
   pozycjonowany przodek), ktora rozciaga sie na cala szerokosc karty (`.stat-list` to flex
   column z domyslnym `align-items: stretch`), wiec popover NIGDY nie jest szerszy niz karta. */
.info-popover {
  position: absolute; top: 100%; left: 0; margin-top: 8px; z-index: 20;
  background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--card-shadow); padding: 10px 12px; width: max-content; max-width: min(260px, 100%);
  font-size: 12.5px; font-weight: 400; line-height: 1.45; color: var(--text-secondary);
  text-transform: none; letter-spacing: normal;
}
.info-popover[hidden] { display: none; }

/* POPRAWKA 2026-09-05 (1): pierwsza wersja tego marginesu (margin-top: 24px) okazala sie
   niewystarczajaca w praktyce - uzytkownik zglosil wrazenie "nakladania sie" tytulu wykresu
   na dolny rzad kafelkow "Podsumowanie dnia" tuz nad nim. Naprawione tym samym wzorcem co
   .day-summary powyzej (border-top + padding-top + wiekszy margin-top), zeby dostac WYRAZNA,
   widoczna linie oddzielajaca zamiast polegac wylacznie na samym marginesie. */
.load-chart-block { margin-top: 28px; padding-top: 22px; border-top: 1px solid var(--border); margin-bottom: 4px; }
.load-chart-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.load-chart-title { font-size: 13px; font-weight: 700; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 4px; }
/* POPRAWKA 2026-09-05 (to samo zgloszenie co przy .chart-hover-subtitle wyzej - dotyczylo TAKZE
   wykresu "Zuzycie energii przez dom", ktory ma wlasna pare .load-chart-stats/.load-chart-readout)
   - padding/ramka (przezroczysta) dopasowane do .load-chart-readout.chart-readout ponizej, zeby
   zamiana przy najechaniu myszka nie zmieniala wysokosci naglowka tej karty. */
.load-chart-stats { font-size: 12.5px; color: var(--text-faintest); white-space: nowrap; padding: 5px 10px; border: 1px solid transparent; border-radius: 10px; box-sizing: border-box; }
.load-chart-total { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 20px; color: var(--text-strong); white-space: nowrap; }
/* POPRAWKA 2026-09-05 (2): cursor:default usuniety - wykres ma teraz podpowiedz przy
   najechaniu myszka (patrz initLoadChartHover w live.js), wiec wraca do domyslnego
   cursor:crosshair z ogolnej reguly .chart-svg powyzej, tak samo jak wykres produkcji PV. */
.load-chart-wrap .chart-svg { margin-top: 8px; }

/* Analiza AI WEWNATRZ karty "Bilans dnia" (rozdz. 6.1, POPRAWKA 2026-09-05: przeniesiona
   tutaj z wlasnej, osobnej karty na wyrazna prosbe uzytkownika: "daj analize AI do Bilans
   energii po oddano do sieci i wyrownaj do marginesow"). CELOWO bez wlasnego paddingu
   (w odroznieniu od .ai-card ponizej, ktora wciaz istnieje/jest uzywana jako samodzielna
   karta gdzie indziej) - dziedziczy padding z .balance-card (.card { padding: 24px 28px }),
   zeby lewa/prawa krawedz tekstu byla idealnie wyrownana z "Magazyn"/"Siec" powyzej, a nie
   miala dodatkowy wciety margines.
   POPRAWKA 2026-09-05 (2): align-items zmienione z flex-start na center - na zgloszony
   problem "oblewamy ikone tekstem" (ikona 44px dosunieta do gory dużo wyzszego, wielolinijkowego
   akapitu wygladala jak wtopiona w pierwsza linijke zamiast stac czytelnie obok calego bloku).
   POPRAWKA 2026-09-05 (11) - CELOWE ODWROCENIE powyzszej decyzji na WYRAZNA prosbe uzytkownika:
   ikona ma stac w lewym gornym rogu, PRZY pierwszych liniach tekstu (nie wysrodkowana pionowo,
   nie jako osobna kolumna) - dokladnie efekt "oblewania" opisany wyzej jako problem, teraz
   pożądany. Flex (ktory ustawial ikone i .ai-body OBOK siebie jako dwie kolumny) zastapiony
   zwyklym blokiem - ikona jest teraz `float: left` WEWNATRZ `.ai-body`, przed `.ai-text` (patrz
   balance_card.html), wiec to `.balance-ai-row` juz nie musi (i nie moze) ustawiac ich w rzad. */
.balance-ai-row { display: block; }

/* .balance-ai-block: cala sekcja "Analiza AI" (dzielnik + tytul + ikona+tekst) doklejona do
   SAMEGO DOLU karty "Bilans dnia" - na zgloszony problem "ostatnia linia powinna byc na
   samym dole w tym okienku". Dziala w parze z #balance-card-fragment (flex column) ponizej:
   karta "Bilans dnia" rozciaga sie do wysokosci sasiedniej karty diagramu
   (.row-flow { align-items: stretch }), wiec bez tej reguly zostawala pusta przestrzen POD
   sekcja AI, zamiast NAD nia. margin-top: auto zjada cala nadmiarowa wysokosc kolumny flex
   PRZED tym blokiem, doklejajac go do dolnej krawedzi karty niezaleznie od tego, ile miejsca
   zajmuje reszta tresci (Magazyn/Siec) powyzej. */
.balance-ai-block { margin-top: auto; }

/* #balance-card-fragment musi byc flex-column WYPELNIAJACYM cala wysokosc .balance-card
   (ktora sama jest flex-column, patrz .balance-card powyzej), zeby margin-top: auto na
   .balance-ai-block mialo w ogole jakas nadmiarowa przestrzen do zjedzenia - bez flex: 1
   tutaj ten div zajmowalby tylko wysokosc swojej wlasnej tresci (naturalna, "auto"), a caly
   nadmiar wysokosci zostalby przez .balance-card wyrenderowany jako martwa przestrzen PO NIM
   (poza fragmentem), gdzie margin-top:auto w srodku fragmentu nie ma juz na co dzialac. */
#balance-card-fragment { display: flex; flex-direction: column; flex: 1; min-height: 0; }

.ai-card { padding: 22px 28px; display: flex; gap: 20px; align-items: flex-start; }
/* POPRAWKA 2026-09-05 (11), zgloszona prosba uzytkownika: ikona ma stac w lewym gornym rogu
   bloku tekstu analizy, PRZY pierwszych liniach - NIE wysrodkowana pionowo, NIE jako osobna
   kolumna obok tekstu, NIE na srodku tekstu - a tekst ma ja "oplywac": pierwsze linie po
   prawej stronie ikony, a po przekroczeniu jej dolnej krawedzi (44px + odstep) tekst ma
   automatycznie wrocic do pelnej szerokosci kontenera. To dokladnie klasyczny efekt "inicjalu"
   z prasy/DTP - jedyny sposob, by uzyskac go w CSS bez JS, to `float: left` na samej ikonie,
   umieszczonej jako PIERWSZE dziecko `.ai-body`, PRZED akapitem `.ai-text` w znacznikach
   (float dziala tylko na elementy PO nim w tym samym kontekscie formatowania blokowego - stad
   kolejnosc w balance_card.html ma znaczenie). `.ai-card` (samodzielna karta - w tej chwili BEZ
   uzycia w zadnym szablonie, patrz grep po `ai-card`/`ai-icon`/`ai-body` w templates/) nadal
   ustawia wlasny flex-row wokol tych samych klas `.ai-icon`/`.ai-body`, wiec gdyby kiedys znow
   zaczela byc uzywana, ikona w NIEJ tez bedzie floatowac (flex-item, ktory jest jednoczesnie
   floatowany, po prostu ignoruje float - flex ma pierwszenstwo), wiec zachowanie tam sie nie
   zmieni. */
.ai-icon {
  float: left;
  width: 44px; height: 44px; border-radius: 14px;
  margin: 0 16px 8px 0;
  background: linear-gradient(155deg, var(--accent-ai-1), var(--accent-ai-2));
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 8px 20px oklch(0.55 0.18 320 / 0.3);
}
.ai-body { min-width: 0; }
.pill { font-size: 12.5px; font-weight: 600; padding: 3px 9px; border-radius: 100px; background: var(--pill-bg); color: var(--pill-text); white-space: nowrap; }
/* POPRAWKA 2026-09-05: nowy prompt Asystenta AI (rozdz. 4.3) zwraca ustrukturyzowana,
   wieloliniowa odpowiedz z sekcjami i emoji (np. "DLACZEGO:" / "JUTRO:" / "PEWNOSC:" w
   osobnych liniach), a nie pojedynczy akapit jak poprzednio - `white-space: pre-line`
   zachowuje znaki nowej linii z tresci (`ai_analysis.content`) zamiast zlepiac wszystko w
   jeden ciag.
   POPRAWKA 2026-09-05 (7b) - `text-align` z powrotem na `justify` na wyrazne zyczenie
   uzytkownika ("musi byc justify - lewy i prawy margines"), po tym jak wczesniej w tym samym
   dniu zostalo zmienione na `left` (obawa, ze krotkie, pojedyncze linie typu "PEWNOSC: XX%"
   rozciagalyby sie brzydko przy justowaniu). Uzasadnienie tamtej zmiany zostaje aktualne jako
   PRZYSZLE ryzyko: jesli po wdrozeniu nowego promptu Asystent AI zacznie faktycznie zwracac
   pelny, wieloliniowy format z krotkimi naglowkami sekcji (a nie - jak na razie - dluzszy,
   plynny akapit prozy z fallbacku silnika decyzyjnego), justify moze rozciagac te krotkie
   linie w nieladny sposob - do ponownej oceny wizualnej, gdy realna odpowiedz AI bedzie
   dostepna do obejrzenia. */
.ai-text { text-align: justify; white-space: pre-line; line-height: 1.55; margin: 0; font-size: 14px; color: var(--text-secondary); }
/* POPRAWKA 2026-09-06 (#37), zgloszona prosba uzytkownika ("moze zrobmy mniejsza czcionke dla
   odpowedzi AI .. i nieco zwiekszmy ilośc znaków"): font-size obnizony z 16px do 14px (ten sam
   rozmiar co np. .legend-item, wpisuje sie w istniejaca skale tekstu pomocniczego w tej
   aplikacji) - RAZEM z podniesieniem AI_RESPONSE_MAX_CHARS (collector.py) i limitu znakow w
   prompcie (ai_client.py) z 450 do 600, zeby dluzszy dozwolony tekst nadal miescil sie
   czytelnie w karcie "Bilans dnia" bez nadmiernego przewijania (patrz max-height/overflow-y
   ponizej - 420px przy mniejszej czcionce i tej samej line-height miesci proporcjonalnie
   wiecej linii tekstu niz przy 16px). */
/* POPRAWKA 2026-09-05 (7c), zgloszone pytanie uzytkownika "ile linii bedzie mial nowy format
   odpowiedzi - chodzi o to, zeby to miescilo sie w Bilansie dnia": nowy, ustrukturyzowany
   format (rozdz. 4.3) ma z zalozenia min. ok. 13 linii samej struktury (naglowki sekcji +
   wymagane pola), a wiecej po zawinieciu dluzszych zdan w "DLACZEGO"/"OPINIA AI" - bez limitu
   to WYRAZNIE wiecej niz poprzedni format (1 akapit, 3-5 zdan). Karta "Bilans dnia" nie ma
   wlasnej stalej wysokosci - rozciaga sie do wysokosci sasiedniej karty diagramu (.row-flow:
   align-items: stretch), WIEC bardzo dlugi tekst AI rozciagnalby ROWNIEZ karte diagramu do tej
   samej (zbyt duzej) wysokosci, zostawiajac tam pusta przestrzen. Max-height + scroll na SAMYM
   bloku tekstu AI (nie na calej karcie) to zabezpieczenie: normalny, kilkunastoliniowy tekst
   miesci sie w calosci bez przewijania, a nietypowo dlugi (model "rozgadal sie" mimo instrukcji
   w prompcie) dostaje pasek przewijania zamiast rozciagac cala karte/wiersz kart. 420px przy
   line-height 1.55 i 16px to ok. 17 linii widocznych na raz - z zapasem ponad typowa dlugosc
   tego formatu. */
.ai-text { max-height: 420px; overflow-y: auto; }
/* POPRAWKA 2026-09-05 (8), zgloszona prosba uzytkownika: "na koniec ma pojawic sie wyrozniona
   Rekomendacja: doladowac (ile %) albo brak". Dwa warianty koloru na bazie JUZ istniejacych,
   motywowo-swiadomych tokenow (bez nowych zmiennych): `--c-reserve` (bursztynowy, uzywany dla
   strefy rezerwy SOC ponizej progu - tutaj: "trzeba doladowac", czyli wymaga uwagi) i
   `--c-storage`/`--c-storage-soft-bg` (zielono-turkusowy, juz uzywany dla "savings-positive" -
   tutaj: "brak doladowania", czyli wszystko w porzadku). `color-mix` do delikatnego podbarwienia
   tla wariantu "charge" (dla "ok" jest juz gotowy `--c-storage-soft-bg`). Ukryty atrybutem
   `hidden` (a nie brakiem elementu), dopoki nie ma jeszcze zadnej rekomendacji - patrz
   balance_card.html/live.js. */
.ai-recommendation {
  margin-top: 12px;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  border-left: 4px solid var(--c-storage);
  background: var(--c-storage-soft-bg);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--text-secondary);
}
.ai-recommendation strong { font-weight: 700; }
.ai-recommendation--ok { border-left-color: var(--c-storage); background: var(--c-storage-soft-bg); }
.ai-recommendation--charge {
  border-left-color: var(--c-reserve);
  background: color-mix(in oklch, var(--c-reserve) 18%, transparent);
}
/* POPRAWKA 2026-09-06 (#40), zgloszona prosba uzytkownika: "trzeba wiedziec kiedy ten magazyn
   sie rozladuje i czy jest potrzeba zatrzymania rozladowania" - osobny, ZAWSZE deterministyczny
   blok ostrzegawczy (patrz balance_card.html/collector.py), widoczny TYLKO gdy realne tempo
   rozladowania wskazuje na ryzyko wyczerpania magazynu przed koncem dzisiejszej drogiej taryfy.
   Stylistycznie klon `.ai-recommendation--charge` (ten sam token `--c-reserve` - "wymaga
   uwagi") zamiast nowej zmiennej koloru - to ten sam poziom pilnosci co "trzeba doladowac",
   tylko inny kontekst czasowy (dzis wieczorem, nie jutro rano). Ukryty atrybutem `hidden`
   (nie brakiem elementu), dopoki `discharge_warning_active` nie jest prawdziwe - ten sam
   wzorzec co `.ai-recommendation` powyzej.*/
.ai-discharge-warning {
  margin-top: 12px;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  border-left: 4px solid var(--c-reserve);
  background: color-mix(in oklch, var(--c-reserve) 18%, transparent);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--text-secondary);
}
.ai-discharge-warning strong { font-weight: 700; }
/* POPRAWKA 2026-09-06 (#43), wprost sformulowana przez uzytkownika istota calej serii poprawek
   #40-#42: "musze zadbac o blokade SOC i nie pozwolic na jego rozladowanie ponizej sugerowanej
   wartosci". Modyfikator klasy bazowej `.ai-discharge-warning` powyzej (ten sam ksztalt/padding/
   tekst) - jedyna roznica to kolor obramowania (`--c-grid`, fioletowy odcien juz zdefiniowany w
   :root i w trybie ciemnym, wizualnie odrozniajacy to ostrzezenie NOCNE od dziennego
   `.ai-discharge-warning` (bursztynowe `--c-reserve`) - oba moga byc widoczne jednoczesnie, wiec
   musza dawac sie odroznic na pierwszy rzut oka.*/
.ai-night-soc-lock-warning {
  border-left-color: var(--c-grid);
  background: color-mix(in oklch, var(--c-grid) 18%, transparent);
}

/* POPRAWKA 2026-09-07 - Wariant A (rozdz. 6.1): kontener trzech blokow powyzej
   (.ai-recommendation/.ai-discharge-warning/.ai-night-soc-lock-warning), PRZENIESIONYCH z karty
   "Bilans dnia" przed karte "Produkcja z paneli PV" (patrz partials/current_status.html po pelne
   uzasadnienie). Sam kontener nie ma wlasnego tla/ramki - uklad pionowy (`display:flex`) zostaje
   po `.card`, ktory od POPRAWKI 2026-09-07 (2) juz owija cala ta sekcje (patrz
   templates/dashboard.html - `<section class="card card-accent accent-ai">`, na wyrazna prosbe
   uzytkownika: "moze niech pojawia sie w takim samym polu jak wykresy czy inne elementy tej
   strony") - tlo/zaokraglenie/cien/padding i tytul "Rekomendacje i ostrzeżenia" dostarcza juz
   `.card`/`.card-title-row` powyzej w tym pliku, wiec `.current-status-row` samo w sobie
   pozostaje "bezstylowe", tylko pionowy stack trzech blokow. */
.current-status-row { display: flex; flex-direction: column; }

/* POPRAWKA 2026-09-07 (3) - znacznik "ostatnia weryfikacja" w tym samym wierszu co tytul karty
   "Rekomendacje i ostrzeżenia" (.card-title-row, patrz #current-status-refresh w
   partials/current_status.html). Uklad (lewo/prawo) zalatwia juz `.card-title-row { justify-
   content: space-between }` powyzej w tym pliku - ta regula odpowiada WYLACZNIE za typografie
   samego znacznika. Uzytkownik wprost odrzucil pierwsza wersje (osobna linijka pod spodem,
   "wyglada jak doklejone") i poprosil o "mniejsza czcionke" - stad CELOWO nie recyklujemy tu
   `.live` (pogrubiony, uppercase, z pulsujaca kropka - to plakietka "na zywo", nie metadana
   czasu) tylko lekki, wyciszony styl zgodny z `.ai-meta` obok w karcie "Bilans dnia" (ten sam
   pierwowzor: maly rozmiar, kolor `--text-faint`, bez pogrubienia). */
.current-status-refresh {
  font-size: 12px;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex: 0 0 auto;
}
.current-status-refresh[hidden] { display: none; }

/* POPRAWKA 2026-09-07 - Wariant B (rozdz. 6.1): dziennik zdarzen, pasek NA STALE widoczny w
   dolnej czesci przegladarki, TYLKO na desktopie (ukryty ponizej 768px - patrz @media
   (max-width: 768px) wyzej w tym pliku). Wprost ustalone z uzytkownikiem w rozmowie 2026-09-07:
   pasek ma byc zawsze widoczny (nie zwijany), pokazywac 2-3 zdarzenia naraz z mozliwoscia
   przewijania starszych (.event-log-list ponizej, max-height odpowiadajacy ~2,5 wiersza),
   auto-odswiezac sie samo (pollFragment w live.js, patrz tam uzasadnienie interwalu). `position:
   fixed` zamiast bycia potomkiem `.wrap` (patrz komentarz w dashboard.html przy tym elemencie) -
   .wrap ma odpowiednio zwiekszony padding-bottom (patrz na poczatku tego pliku), zeby pasek nie
   zaslanial ostatniej karty.

   POPRAWKA 2026-09-07 (2) - zgloszona prosba o odswiezenie wygladu paska: "soft / modern /
   clean / premium", bez ciezkiego prostokatnego panelu przyklejonego krawedz-do-krawedzi. Pasek
   jest teraz PLYWAJACA "kapsula dokowana" - wysrodkowana, z marginesem od lewej/prawej/dolnej
   krawedzi ekranu (`left: 50%` + `transform: translateX(-50%)` + `bottom: 18px`, zamiast
   `left: 0; right: 0; bottom: 0`), z wiekszym zaokragleniem (24px, ten sam rzad co `--radius`
   uzywany przez `.card`), delikatna polprzezroczystoscia + rozmyciem tla (`color-mix`/
   `backdrop-filter`, zeby tresc pod paskiem lekko "przeswiecala" zamiast plaskiego bloku koloru)
   oraz miekkim, rozproszonym cieniem naokolo zamiast twardej gornej krawedzi/`box-shadow` w gore.
   `env(safe-area-inset-bottom, 0)` zostaje w dolnym paddingu jako tanie zabezpieczenie na
   przyszlosc (np. iPad w trybie desktopowym), mimo ze na typowym telefonie ten pasek jest
   ukryty (@media ponizej 768px, patrz nizej). Najnowsze zdarzenie - pierwszy wiersz listy,
   `.event-log-row:first-child` ponizej (kolejnosc "najnowsze pierwsze" ustalona w
   event_log.py) - dostaje wlasna, miekka kapsule/pill (`--surface-2`, promien 999px) jako
   wizualne wyroznienie "aktywnego" stanu wzgledem starszych wpisow pod przewijaniem - ten sam
   wzorzec podswietlenia "aktywny element = --surface-2 + zaokraglenie", jaki jest juz w tym
   pliku uzywany np. przez `.history-row.is-active`. */
.event-log-bar {
  position: fixed;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  z-index: 60;
  width: min(1360px, calc(100% - 48px));
  background: color-mix(in oklch, var(--card-bg) 80%, transparent);
  backdrop-filter: blur(18px) saturate(1.3);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  border: 1px solid color-mix(in oklch, var(--border-soft) 65%, transparent);
  border-radius: 24px;
  box-shadow: 0 16px 40px oklch(0.2 0.02 240 / 0.14), 0 2px 10px oklch(0.2 0.02 240 / 0.06);
  padding: 12px 22px calc(12px + env(safe-area-inset-bottom, 0px));
}
.event-log-bar-head {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 6px;
  padding: 0 6px;
}
/* max-height dobrany tak, zeby zmiescic ~2,5 wiersza - reszta pod przewijaniem (overflow-y:
   auto), zgodnie z wyraznym zyczeniem uzytkownika: "chce widziec 2-3 zdarzenia conajmniej ...
   dac mozliwosc przewijania". */
.event-log-list { max-height: 96px; overflow-y: auto; display: flex; flex-direction: column; gap: 3px; }
.event-log-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 10px;
  border-radius: 12px;
  font-size: 13.5px;
  line-height: 1.35;
}
.event-log-row:first-child {
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--border-soft) 90%, transparent);
  border-radius: 999px;
  padding: 7px 14px;
}
.event-log-icon { flex: 0 0 auto; }
.event-log-time { flex: 0 0 auto; color: var(--text-faintest); font-variant-numeric: tabular-nums; }
.event-log-text { color: var(--text-secondary); }
.event-log-empty { padding: 7px 10px; font-size: 13.5px; color: var(--text-faintest); }

/* POPRAWKA 2026-09-07, zgloszona przez uzytkownika obserwacja ukladu: karta "Bilans dnia" (patrz
   .ai-night-soc-lock-warning powyzej) potrafi urosnac wyzej niz karta diagramu, zostawiajac puste
   miejsce pod wykresem "Zuzycie energii przez dom" (patrz .load-chart-block/.day-summary) w
   sasiedniej karcie - obie karty w .row-flow maja wspolna wysokosc (align-items: stretch), wiec
   "krotsza" karta po prostu dociaga sie do dluzszej pusta przestrzenia. Uzytkownik wybral
   wypelnienie tego miejsca uzyteczna trescia, widoczna DOKLADNIE wtedy, gdy aktywny jest ten sam
   warning: "jak pojawia sie warning ze historyczne tempo rozladowania nocnego sugeruje ...
   dokladamy punkt 3" (patrz templates/dashboard.html). Stylistycznie klon .summary-tile (ten sam
   ksztalt "ikona + wartosc + etykieta", zeby wizualnie pasowalo do kafelkow "Przebieg dnia"
   powyzej), ale z akcentem koloru --c-grid (ten sam fioletowy odcien co samo ostrzezenie w
   karcie "Bilans dnia" - laczy wizualnie przyczyne, pokazana tutaj, ze skutkiem tam). Mniejszy
   gorny margines i brak border-top (w odroznieniu od .load-chart-block powyzej) - to celowo
   NIE jest nowa, samodzielna sekcja, tylko uzupelniajaca notatka do wykresu tuz nad nia. */
.night-history-note {
  display: flex; align-items: center; gap: 12px; min-width: 0;
  margin-top: 16px;
  padding: 12px 16px; border-radius: 16px;
  background: color-mix(in oklch, var(--c-grid) 12%, var(--surface-2));
  border: 1px solid color-mix(in oklch, var(--c-grid) 35%, var(--border));
  /* POPRAWKA 2026-09-07 - pozycjonowany przodek dla #night-discharge-history-info-popover
     ponizej (ten sam wzorzec co .balance-section-title-row - patrz komentarz przy .info-popover
     nizej w tym pliku), zeby popover mogl sie rozciagac az do ok. 260px (szerokosc calej tej
     notatki), a nie tylko do waskiej szerokosci samego wiersza "wartosc + przycisk i". */
  position: relative;
}
.night-history-note > div:last-child { min-width: 0; }
.night-history-note-icon {
  flex: 0 0 40px; width: 40px; height: 40px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; line-height: 1;
  background: color-mix(in oklch, var(--c-grid) 20%, transparent);
  border: 1px solid color-mix(in oklch, var(--c-grid) 45%, var(--border));
}
/* POPRAWKA 2026-09-07, zgloszona prosba uzytkownika: "to musi byc jakis przypis ktory tlumaczy
   skad bierze sie ta wartosc" - wiersz "wartosc + przycisk i", ten sam wzorzec co
   .balance-section-title-row (tam naglowek + przycisk), tylko bez wlasnego position:relative -
   to ma teraz .night-history-note powyzej (cala notatka), zeby popover mogl byc szerszy niz
   sam ten waski wiersz. */
.night-history-note-value-row { display: flex; align-items: center; gap: 6px; }
/* POPRAWKA 2026-09-07, wykryty na zywo na produkcji problem: #night-discharge-history-note
   siedzi na samym DOLE karty diagramu (ostatni element przed </section>), a `.card` ma
   `overflow: hidden` (patrz uzasadnienie tam) - popover otwierany W DOL (jak przy "Solplanet
   ..." w balance_card.html, ktory ma pod soba jeszcze kawalek karty) natychmiast obcinalby sie
   o dolna krawedz karty, zanim zdazylby pokazac caly tekst. Nad notatka jest za to mnostwo
   miejsca (caly wykres "Zuzycie energii przez dom" + kafelki "Przebieg dnia" powyzej, w TEJ
   SAMEJ karcie) - stad ten jeden popover (identyfikowany po id, bo to jego wlasciwosc
   wynikajaca z POLOZENIA na stronie, nie z klasy `.info-popover` jako takiej) otwiera sie w
   gore zamiast w dol. */
#night-discharge-history-info-popover { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 8px; }
.night-history-note-value { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 21px; color: var(--text-strong); line-height: 1.2; }
.night-history-note-label { font-size: 13px; color: var(--text-muted); margin-top: 1px; overflow-wrap: break-word; }
/* POPRAWKA 2026-09-07 - ten sam blad/naprawa co .chart-readout[hidden] wyzej w tym pliku (patrz
   komentarz tam po pelne wyjasnienie): `.night-history-note` ustawia wlasne `display: flex`,
   ktore jako regula z arkusza AUTORA wygrywa z domyslnym stylem przegladarki dla atrybutu
   `[hidden]` (`display: none` z arkusza UA) NIEZALEZNIE od kolejnosci/specyficznosci - bez tego
   nadpisania blok bylby ZAWSZE widoczny (z pustymi/domyslnymi wartosciami "-"/"0 nocy"),
   niezaleznie od warunku `hidden` ustawianego w dashboard.html/live.js. Wykryte na zywo na
   produkcji zaraz po pierwszym wdrozeniu tej funkcji - stara, juz raz udokumentowana pulapka w
   tym samym pliku, tym razem powtorzona w nowym kodzie. */
.night-history-note[hidden] { display: none; }
.ai-meta { font-size: 13px; color: var(--text-faint); margin-top: 10px; }

.chart-legend { display: flex; gap: 18px; flex-wrap: wrap; padding-top: 2px; }
.legend-item { display: flex; align-items: center; gap: 7px; font-size: 14px; color: var(--text-secondary); white-space: nowrap; }
.chart-wrap { position: relative; }
.chart-svg { width: 100%; height: auto; margin-top: 12px; display: block; cursor: crosshair; }
.chart-label { font-family: 'Plus Jakarta Sans', sans-serif; font-size: 13px; }
.muted-note { color: var(--text-muted); font-size: 15px; margin: 8px 0 0; }
/* Notatki pod wykresem "Aktualne ceny energii RCE" (2026-09-07, na wyrazna prosbe uzytkownika:
   "nieco zmniejsz czcionke w opisie pod wykresem") - CELOWO osobna, dodatkowa klasa (nie zmiana
   .muted-note wprost powyzej), zeby nie zmniejszyc czcionki takze w pozostalych miejscach, ktore
   juz uzywaja .muted-note (production_chart.html/yesterday_chart.html/history_chart.html/stopka
   strony) - tam rozmiar 15px zostaje bez zmian. */
.rce-note { font-size: 13px; }

/* POPRAWKA 2026-09-05 (zgloszony problem: "okienko [podpowiedzi] przykrywa ten wykres z
   danymi" - dawny floating .chart-tooltip byl pozycjonowany WEWNATRZ obszaru wykresu, tuz
   nad kursorem, wiec przy wiekszej liczbie serii (5 wierszy na wykresie produkcji PV po
   dolozeniu "Trend") jego wysokosc bywala porownywalna z wysokoscia calego, dosc niskiego
   wykresu (viewBox 1264x200 renderowany na pelna szerokosc karty) - nie dalo sie go w
   niezawodny sposob zmiescic NAD ani POD krzywymi bez zaslaniania danych. Naprawa: podpowiedz
   nie jest juz floatujacym dymkiem NAD wykresem, tylko doklejana w miejscu podtytulu/statystyk
   W NAGLOWKU karty (patrz initChartHover w live.js) - miejsce calkowicie poza obszarem SVG,
   wiec fizycznie nie moze juz przykryc zadnej krzywej. */
.chart-readout {
  display: flex; align-items: center; flex-wrap: wrap; column-gap: 14px; row-gap: 4px;
  font-size: 14px; color: var(--text-secondary); margin-top: 4px;
  /* POPRAWKA 2026-09-05 (na prosbe uzytkownika: "mozna to wziac w obramowanie i uwypuklic") -
     wyrozniona "karta" (tlo + ramka, ten sam wzorzec co .summary-tile) zamiast zwyklego tekstu w
     miejscu podtytulu, zeby bylo od razu widac, ze to aktywny, "na zywo" odczyt, a nie stala
     czesc naglowka. */
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px;
  padding: 6px 12px;
}
.chart-readout .tt-time { font-weight: 700; color: var(--text-strong); }
.chart-readout .tt-row { display: inline-flex; align-items: center; gap: 6px; }
.chart-readout .tt-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
.load-chart-readout.chart-readout { font-size: 12.5px; margin-top: 0; padding: 5px 10px; }
/* POPRAWKA 2026-09-05 (zgloszony problem: "jak zjezdzam z wykresu zostaje linia taka
   pogrubiona") - `.chart-readout` ustawia wlasne `display: flex`, ktore (jako regula z
   arkusza autora) wygrywa z domyslnym stylem przegladarki dla atrybutu `[hidden]`
   (`display: none` z arkusza UA) niezaleznie od kolejnosci/specyficznosci - stad po
   `readout.hidden = true` w live.js zostawal widoczny PUSTY box z tlem/ramka (obramowanie bez
   tekstu wygladajace jak pogrubiona linia). Jawne nadpisanie ponizej przywraca ukrywanie. */
.chart-readout[hidden] { display: none; }

.savings-card .card-title-row { align-items: flex-start; margin-bottom: 22px; }
.savings-hero { text-align: right; flex-shrink: 0; }
.hero-value-row { display: flex; align-items: baseline; gap: 8px; justify-content: flex-end; }
.hero-value { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 33px; color: var(--savings-positive); }
.hero-delta { display: flex; align-items: center; gap: 2px; font-size: 14px; font-weight: 700; color: var(--savings-positive); }
.hero-delta.down { color: var(--hero-down); }
.hero-delta.down svg { transform: rotate(180deg); }
.hero-label { font-size: 13px; color: var(--text-faint); margin-top: 2px; }
.hero-sub { font-size: 12.5px; color: var(--text-faintest); margin-top: 6px; }
.hero-sub strong { color: var(--text-strong); }

.info-box { font-size: 14.5px; color: var(--text-secondary); margin-bottom: 16px; padding: 12px 16px; background: var(--info-box-bg); border-radius: var(--radius-sm); }
.info-box strong { color: var(--text-strong); }

/* POPRAWKA 2026-09-06 #7 (zgloszony problem: "to musi byc pokazane bardzo przejrzyscie" -
   zaakceptowany "Wariant C", patrz komentarz przy tym bloku w savings_card.html) - `.info-box`
   powyzej zostaje w pliku nieuzywany (na wypadek powrotu do niego), a nowy uklad to krotki
   naglowek (`.usage-headline`) + tabelka dzien/noc w TYM SAMYM jezyku wizualnym co
   `.history-table` (siatka, cienka linia rozdzielajaca miedzy wierszami, naglowek WIELKIMI
   literami, liczby wyrownane do prawej i .mono) - stad prawie identyczne reguly do .history-row/
   .history-head/.history-num ponizej, tylko z inna liczba kolumn (3 zamiast 6: etykieta + dzien +
   noc) i bez marginesu dolnego .info-box (calosc konczy .usage-compare-foot). */
.usage-headline { font-size: 14.5px; color: var(--text-secondary); margin-bottom: 12px; }
.usage-headline strong { color: var(--text-strong); }
.usage-compare { border-top: 1px solid var(--border-soft); }
.usage-compare-row {
  display: grid; grid-template-columns: 1.3fr 1fr 1fr;
  align-items: center; padding: 9px 2px; border-bottom: 1px solid var(--border-soft);
  font-size: 14px;
}
.usage-compare-row-last { border-bottom: none; }
.usage-compare-head { color: var(--text-faint); font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; border-bottom: none; padding-bottom: 6px; }
.usage-compare-label { color: var(--text-secondary); }
.usage-compare-num { text-align: right; }
.usage-compare-foot { font-size: 12px; color: var(--text-faint); margin-top: 8px; margin-bottom: 16px; }
/* Tabelka min/maks/srednia pod wykresem "Aktualne ceny energii RCE" (2026-09-07, na wyrazna
   prosbe uzytkownika) - reuse .usage-compare* w calosci (patrz komentarz przy tych regulach
   powyzej), tylko z odstepem od wykresu nad nia (analogicznie do .history-table margin-top).
   POPRAWKA 2026-09-07 (zgloszony problem, potwierdzony na zywo przez javascript_tool - inspekcja
   DOM/computed-style: "tabela pod wykresem jest dziwnie rozjechana") - `.usage-compare-row`
   powyzej definiuje TYLKO 3 kolumny gridu (`1.3fr 1fr 1fr`), bo powstala dla tabelki dzien/noc w
   savings_card.html (etykieta + dzien + noc = 3 dzieci). Tabelka RCE ma 4 dzieci w kazdym wierszu
   (etykieta + Min + Maks + Srednia) - przy tylko 3 sciezkach gridu CSS Grid domyslnie (
   `grid-auto-flow: row`) zawija 4. dziecko do NIEJAWNEGO wiersza 2/kolumny 1, ladujac je na tej
   samej wspolrzednej x co kolumna etykiety zamiast we wlasnej, skrajnie prawej kolumnie -
   potwierdzone: `getComputedStyle(row).gridTemplateColumns` zwracalo dokladnie 3 szerokosci
   sciezek, a `getBoundingClientRect().x` komorki "Srednia" pokrywalo sie z komorka etykiety.
   Ten selektor NADPISUJE liczbe kolumn WYLACZNIE w obrebie `.rce-stats` (4 sciezki zamiast 3),
   wiec tabelka dzien/noc w savings_card.html (bez klasy `.rce-stats`) zostaje nietknieta. */
.rce-stats { margin-top: 14px; }
.rce-stats .usage-compare-row { grid-template-columns: 1.15fr 1fr 1fr 1fr; }

.bill-compare { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; }
.bill { flex: 1; padding: 14px 16px; border-radius: var(--radius-sm); background: var(--bill-bg); min-width: 0; }
.bill-label { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); margin-bottom: 4px; }
.bill-value { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 23px; color: var(--bill-passive-value); }
.bill-sub { font-size: 12px; color: var(--text-faintest); margin-top: 2px; }
.bill-active { background: var(--bill-active-bg); border: 1px solid var(--bill-active-border); }
.bill-active .bill-label,
.bill-active .bill-value,
.bill-active .bill-sub { color: var(--bill-active-text); }
.bill-arrow-icon { flex-shrink: 0; }

.savings-group { margin-bottom: 20px; }
.savings-group-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; }
.savings-group-head.active { color: var(--c-storage); }
.savings-group-head.passive { color: var(--text-secondary); }
.savings-group-body { display: flex; flex-direction: column; gap: 16px; }
.savings-group-body.active { padding: 14px 16px; background: var(--c-storage-soft-bg); border-radius: var(--radius-sm); }
.savings-group-body .stat-value { color: var(--c-storage); }
.savings-note { font-size: 13px; color: var(--text-faintest); padding-left: 27px; margin-top: 3px; }

/* POPRAWKA 2026-09-05 #6 (na wyrazna prosbe uzytkownika: "to zrob paragon graficzny - ... ma
   to byc cos co jest przy cenie ostatecznej ... nie moze to byc 'zobacz jak paragon': to co
   jest jest bzdura") - zastapiono wczesniejsza "stonowana" wersje (bez papieru/cienia/obrotu):
   (a) `.bill-summary-row` to caly wiersz-`<summary>` (etykieta + kwota + ikonka paragonu TUZ
   PRZY kwocie koncowej) - klika sie caly wiersz, ale wizualna podpowiedz jest dokladnie przy
   cenie, nie w oddzielnym linku tekstowym; (b) `.bill-receipt-paper` to PRAWDZIWY graficzny
   paragon (papier, postrzepione krawedzie, cien, lekki obrot, kod kreskowy) - kolory papieru
   (--receipt-*) sa NA SZTYWNO, niezalezne od jasnego/ciemnego motywu strony (prawdziwy paragon
   nie zmienia koloru z dniem/noca). */
.bill-receipt-details { margin-top: 6px; }
.bill-receipt-details summary { list-style: none; cursor: pointer; }
.bill-receipt-details summary::-webkit-details-marker { display: none; }
.bill-receipt-details summary::marker { content: ""; }
.bill-summary-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

/* POPRAWKA 2026-09-06 #20 (zgloszona prosba: "wyroznij ten z PV i tyle") - delikatne tlo w
   barwie PV (`--c-pv-node-bg`) pod calym wierszem-podsumowaniem paragonu "z PV i magazynem" +
   pogrubiona, kolorowa etykieta (`--c-pv-icon`) - odrozniamy go wizualnie od paragonu "bez PV"
   ponizej, ktory zostaje bez zmian. Ujemny margines rekompensuje dodany padding, zeby tekst
   zostal DOKLADNIE tak wyrownany jak w niewyroznionym wierszu ponizej (0 - 10px marginesu +
   10px paddingu = 0). */
.bill-summary-featured { background: var(--c-pv-node-bg); border-radius: var(--radius-sm); padding: 8px 10px; margin: 0 -10px; }
.bill-summary-featured .stat-label { font-weight: 700; color: var(--c-pv-icon); }

/* POPRAWKA 2026-09-05 #7 (zgloszony problem: "jest jakis potrzebny drobny komentarz na
   dashboard bo zapominam ... zachec do klikniecia w paragon") - sama szara ikonka nie
   wygladala jak przycisk. `.receipt-chip` to kolorowe, okragle tlo pod ikonka (te same tokeny
   `--c-storage` co reszta karty oszczednosci).
   POPRAWKA 2026-09-05 #8 (zgloszony problem: "niechce kliknij po paragon to musi byc efekt
   wizualny jakis") - tekstowy podpis USUNIETY (`.bill-value-hint`/`.bill-value-block` juz nie
   sa uzywane w szablonie). Zamiast slow - stala, delikatna animacja pulsowania
   (`receipt-chip-pulse`): rozszerzajacy sie i zanikajacy pierscien w kolorze `--c-storage`,
   typowy wzorzec "tu jest cos klikalne" bez zadnego tekstu. Zatrzymuje sie po otwarciu
   (`details[open]` - nie ma sensu pulsowac, gdy uzytkownik juz kliknal). */
.bill-value-with-icon { display: flex; align-items: center; gap: 7px; flex-shrink: 0; }
.receipt-chip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--c-storage-soft-bg); color: var(--c-storage);
  transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
  animation: receipt-chip-pulse 2.4s ease-out infinite;
}
.receipt-toggle-icon { color: currentColor; flex-shrink: 0; transition: transform 0.15s ease; position: relative; z-index: 1; }
.bill-summary-row:hover .receipt-chip { background: var(--c-storage); color: #fff; transform: scale(1.08); }
details[open] > .bill-summary-row .receipt-chip { background: var(--c-storage); color: #fff; animation: none; }
details[open] > .bill-summary-row .receipt-toggle-icon { transform: rotate(-8deg); }

@keyframes receipt-chip-pulse {
  0% { box-shadow: 0 0 0 0 oklch(0.55 0.13 165 / 0.45); }
  70% { box-shadow: 0 0 0 8px oklch(0.55 0.13 165 / 0); }
  100% { box-shadow: 0 0 0 0 oklch(0.55 0.13 165 / 0); }
}

.bill-receipt-stage { display: flex; justify-content: center; padding: 16px 4px 6px; }
.bill-receipt-paper {
  --receipt-paper: oklch(0.99 0.005 90);
  --receipt-ink: oklch(0.28 0.01 90);
  --receipt-ink-soft: oklch(0.5 0.01 90);
  --receipt-ink-faint: oklch(0.72 0.01 90);
  --receipt-accent: oklch(0.5 0.13 165);
  width: min(320px, 100%);
  background: var(--receipt-paper);
  color: var(--receipt-ink);
  font-family: ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
  padding: 20px 20px 24px;
  box-shadow: 0 10px 26px oklch(0.2 0.02 240 / 0.16), 0 2px 6px oklch(0.2 0.02 240 / 0.08);
  transform: rotate(-1deg);
  position: relative;
}
.bill-receipt-paper::before, .bill-receipt-paper::after {
  content: ''; position: absolute; left: 0; right: 0; height: 10px;
  background:
    linear-gradient(135deg, var(--receipt-paper) 50%, transparent 50%) 0 0 / 14px 14px repeat-x,
    linear-gradient(-135deg, var(--receipt-paper) 50%, transparent 50%) 0 0 / 14px 14px repeat-x;
}
.bill-receipt-paper::before { top: -5px; transform: rotate(180deg); }
.bill-receipt-paper::after { bottom: -5px; }

.bill-receipt-center { text-align: center; }
.bill-receipt-title { font-weight: 700; font-size: 14px; letter-spacing: 0.04em; }
.bill-receipt-sub { font-size: 11px; color: var(--receipt-ink-soft); margin-top: 4px; }
.bill-receipt-dash { border-top: 1px dashed var(--receipt-ink-faint); margin: 14px 0; }

.bill-receipt-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 11.5px; margin-bottom: 9px; padding-bottom: 6px; border-bottom: 1px dotted var(--receipt-ink-faint); }
.bill-receipt-label { color: var(--receipt-ink-soft); }
.bill-receipt-value { color: var(--receipt-ink); font-weight: 700; white-space: nowrap; flex-shrink: 0; }
.bill-receipt-negative .bill-receipt-value { color: var(--receipt-accent); }

.bill-receipt-total-row { display: flex; justify-content: space-between; align-items: baseline; margin-top: 2px; }
.bill-receipt-total-label { font-weight: 700; font-size: 14px; letter-spacing: 0.03em; }
.bill-receipt-total-value { font-weight: 700; font-size: 20px; }
.bill-receipt-footnote { font-size: 11px; color: var(--receipt-ink-soft); margin-top: 8px; font-family: 'Plus Jakarta Sans', sans-serif; line-height: 1.4; }

.bill-receipt-barcode { display: flex; justify-content: center; gap: 2px; margin: 16px 0 8px; height: 28px; align-items: flex-end; }
.bill-receipt-barcode span { width: 2px; background: var(--receipt-ink); }
.bill-receipt-footer { text-align: center; font-size: 10px; color: var(--receipt-ink-faint); letter-spacing: 0.02em; }

.total-label { font-size: 15.5px; font-weight: 700; color: var(--text-strong); }
.total-value { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 21px; color: var(--c-storage); }

.history-svg { margin-top: 4px; }

/* POPRAWKA 2026-09-06 (zgloszony problem: "ja nie wiem patrzac na ten wykres jakie byly [te
   wartosci], dodaj tez doladowanie nocne per dzien") - tabelka pod wykresem "Historia 7 dni"
   (patrz history_chart.html), jedna kolumna na dzien tygodnia + trzy liczbowe. Wzorowana na
   .details-row nizej (ten sam jezyk wizualny - siatka, cienka linia rozdzielajaca, .mono na
   liczbach), ale z prawostronnie wyrownanymi kolumnami liczbowymi (czytelniejsze przy
   porownywaniu kolumny z gory na dol) zamiast lewostronnych jak w .details-row. */
/* POPRAWKA 2026-09-06 #29 (patrz komentarz w history_chart.html) - legenda kolorow widoczna
   TYLKO na telefonie (patrz media query nizej, gdzie dostaje `display: flex`) - na desktopie
   etykiety kolumn juz sa w `.history-head`, wiec legenda tam jest zbedna. */
.history-legend { display: none; }
.history-table { margin-top: 14px; }
/* POPRAWKA 2026-09-06 #5 (zgloszona prosba: "dodaj mi kolumny do tabeli historia - ostatnie 7
   dni Pobrano sieć Wysłano sieć") - z 4 kolumn (dzien + 3 liczby) zrobily sie 6 (dzien + 5 liczb):
   kolumna dnia nieco wezsza niz przedtem (1.4fr -> 1.2fr), piec kolumn liczbowych rownej szerokosci
   (0.9fr kazda, wczesniej 1fr/1fr/1.2fr na trzy) - patrz tez media query nizej (mobile: scroll
   poziomy zamiast dalszego zwezania, 6 kolumn w 12,5px na waskim telefonie bylo juz nieczytelne). */
.history-row {
  display: grid; grid-template-columns: 1.2fr 0.9fr 0.9fr 1fr 0.9fr 0.9fr;
  align-items: center; padding: 9px 4px; border-top: 1px solid var(--border-soft);
  font-size: 14.5px;
}
/* POPRAWKA 2026-09-06 #6 (zgloszony problem: "na urzadzeniu mobilnym ta tabelka nie bedzie
   dobrze wygladac", wybrany "Wariant B" z mockups/history-table-mobile-warianty.html) -
   `.history-primary`/`.history-secondary` (patrz history_chart.html) grupuja komorki wartosci,
   ale na desktopie maja byc NIEWIDOCZNE jako wrappery - `display: contents` usuwa je z drzewa
   layoutu, wiec ich dzieci (`.history-num`) staja sie zwyklymi elementami siatki `.history-row`
   powyzej, dokladnie tak jak przed wprowadzeniem tych wrapperow (plaski wiersz, 6 kolumn). Na
   telefonie te same dwie klasy dostaja realny `display: flex` w media query nizej. */
.history-primary, .history-secondary { display: contents; }
.history-row:first-child { border-top: none; }
.history-head { color: var(--text-faint); font-size: 12.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; padding-bottom: 8px; }
/* POPRAWKA 2026-09-06 #10 (zgloszony problem: "kwh w nowej lini i mniejsza czcionka i ma byc
   kWh a nie KWH") - jednostka w naglowku (patrz `.history-unit` w history_chart.html) dostaje
   WLASNY wiersz (`display: block` w flex-column ukladzie komorki naglowka, zamiast zawijac sie
   losowo w srodku zdania jak wczesniej przy dlugiej etykiecie "Doladowanie w taniej strefie"),
   mniejsza czcionke niz reszta etykiety, oraz `text-transform: none` - `.history-head` ustawia
   uppercase na CALA komorke, wiec bez tego jednostka bylaby "KWH" zamiast poprawnego "kWh"
   (wielkosc liter w jednostkach SI ma znaczenie: k=kilo, W=Wat, h=godzina). */
.history-head .history-num { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.history-unit { display: block; text-transform: none; font-size: 10px; font-weight: 600; letter-spacing: 0; color: var(--text-faintest); }
.history-day { font-weight: 600; color: var(--text); }
.history-row-today .history-day { color: var(--c-pv-icon); }
.history-num { text-align: right; }
/* POPRAWKA 2026-09-06 (zgloszona prosba: "po najechaniu na wykres chce widziec wartosci") -
   podswietlenie wiersza odpowiadajacego dniu, nad ktorym akurat jest kursor (patrz
   initHistoryChartHover w live.js, ktore przelacza te klase po `data-idx`) - slupek/kropka na
   samym wykresie dostaja analogiczne podswietlenie bezposrednio przez atrybuty SVG (fill/r), bez
   osobnej klasy, bo to prostsze niz poleganie na stylowalnosci atrybutu `r` w CSS. */
.history-row.is-active { background: var(--surface-2); border-radius: 8px; }
.history-bar { transition: fill 0.1s ease; }
.history-dot { transition: r 0.1s ease; }

.details-card { padding: 8px 8px 20px; }
.details-row {
  display: grid; grid-template-columns: 1.3fr 0.9fr 1.2fr 0.9fr 0.9fr;
  align-items: center; padding: 14px 24px; border-top: 1px solid var(--border-soft);
  font-size: 15px;
}
.details-head { border-top: none; padding: 0 24px 12px; font-size: 14px; font-weight: 600; color: var(--text-faint); }
.details-name { display: flex; align-items: center; gap: 10px; font-weight: 600; color: var(--text); }
.dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
.dot-pv { background: var(--c-pv); }
.dot-storage { background: var(--c-storage); }
.dot-home { background: var(--c-home); }
.dot-grid { background: var(--c-grid); }
.mono { font-family: 'Outfit', sans-serif; font-weight: 600; }
.muted { color: var(--text-muted); }
.num { text-align: right; }
.num.muted { color: var(--text-muted); }

@media (min-width: 981px) {
  .diagram-canvas { min-height: 560px; }
}

@media (max-width: 980px) {
  .row-flow { flex-direction: column; }
  .diagram-card, .balance-card { flex: 1 1 auto; }
  .diagram-canvas { max-width: 520px; margin-left: auto; margin-right: auto; }

  .diagram-canvas { aspect-ratio: 800 / 820; }
}

@media (max-width: 768px) {
  .wrap { padding: 24px 16px 48px; }
  /* POPRAWKA 2026-09-07 - Wariant B (dziennik zdarzen) wprost ustalony z uzytkownikiem jako
     TYLKO desktop: "na iphone tylko wariant A" - na telefonie/wąskim ekranie pasek w ogole sie
     nie renderuje (zamiast probowac go zmiescic jako position:fixed na dole, co przy tej
     aplikacji ma juz udokumentowana historie problemow z iOS Safari - "uciete bloki", elementy
     "wychodzace poza ekran"). */
  .event-log-bar { display: none; }
  .card { padding: 20px 18px; }
  .diagram-card { padding: 22px 18px 26px; }
  .header-row { flex-direction: column; align-items: flex-start; }
  .card-title-row { flex-direction: column; }
  .savings-hero { text-align: left; }
  .hero-value-row { justify-content: flex-start; }
  .bill-compare { flex-direction: column; align-items: stretch; }
  .bill-arrow-icon { transform: rotate(90deg); align-self: center; }
  .details-row { grid-template-columns: 1fr; gap: 4px; text-align: left; row-gap: 6px; }
  .details-head { display: none; }
  .details-row { border: 1px solid var(--border-soft); border-radius: 12px; margin: 0 16px 10px; padding: 12px 16px; }
  .details-row.details-head { display: none; }
  .num { text-align: left; }
  /* POPRAWKA 2026-09-06 - przy 4 krotkich kolumnach (dzien + 3 liczby kWh) wystarczylo zwezic
     siatke i zmniejszyc czcionke, zeby zmiescic wszystko w jednym wierszu na telefonie.
     POPRAWKA 2026-09-06 #6 (zgloszony problem: "na urzadzeniu mobilnym ta tabelka nie bedzie
     dobrze wygladac" - przy 6 kolumnach ani dalsze zwezanie, ani poziomy scroll (pierwsza proba,
     #5 ponizej w historii zmian) nie wypadly dobrze w ocenie uzytkownika; wybrany "Wariant B" z
     trzech pokazanych propozycji, patrz mockups/history-table-mobile-warianty.html) - `.history-row`
     przechodzi z siatki na flex z zawijaniem: dzien + `.history-primary` (PV/Zuzycie/Tania strefa)
     ida w JEDNEJ linii, a `.history-secondary` (Pobrano/Wyslano siec) ma `flex-basis: 100%`, wiec
     ZAWSZE zawija sie do DRUGIEJ linii - stylizowanej jak kolorowa "plakietka" (tlo/ramka w
     kolorze sieci, --c-grid-node-*), zeby wizualnie odroznic te dwie "dodatkowe" wartosci od
     trzech "glownych" powyzej. Kazda `.history-num` dostaje wlasna etykiete NAD wartoscia przez
     `::before { content: attr(data-label) }` (atrybut ustawiony w history_chart.html) - dlatego
     naglowek `.history-head` (ktory na desktopie pelni te role) jest tu zbedny i ukryty. */
  /* POPRAWKA 2026-09-06 #24 (zgloszony problem: "na iphone nie podoba mi sie" + "tabelka
       historii wyglada zle na telefonie mimo rebuildu") - powyzszy komentarz (i kolejny kilka
       linii nizej, przy POPRAWCE #10) byl przez pomylke zamkniety jinja-owym znacznikiem konca
       komentarza (klamra-hash) zamiast poprawnego znacznika konca komentarza CSS (gwiazdka-
       ukosnik) - pewnie wklejone/przepisane z komentarza w history_chart.html, gdzie taki
       jinja-owy znacznik jest poprawny. Skutek: przegladarka NIGDY nie zamykala tego komentarza
       w tym miejscu - caly kod od ".history-table" ponizej AZ DO KONCA PLIKU (wlacznie z siatka
       "Wariant B" dla telefonu, plakietkami .history-secondary i CALYM media query dla 480px)
       byl w praktyce jednym wielkim, niedzialajacym komentarzem, niewidocznym dla przegladarki -
       stad zadna z tych mobilnych poprawek nigdy realnie nie dzialala, niezaleznie od
       jakiegokolwiek rebuildu kontenera. Naprawiono zamieniajac oba bledne znaczniki na poprawny
       koniec komentarza CSS (tu i przy POPRAWCE #10 nizej) - dowod: tinycss2 (uzywany do
       walidacji w tej rozmowie) parsuje CSS na tyle liberalnie, ze nie zglaszal bledu, mimo ze
       przegladarka (silnik Chrome) faktycznie traktowala to jako otwarty komentarz - stad ten
       blad przeszedl niezauwazony przez standardowa walidacje w tej sesji. */
  .history-table { overflow-x: visible; }
  .history-row {
    display: flex; flex-wrap: wrap; align-items: flex-start; column-gap: 10px; row-gap: 8px;
    padding: 10px 2px; font-size: 13px;
  }
  /* POPRAWKA 2026-09-06 #30 (zgloszony problem: "po co to widze w telefonie?" - zrzut pokazujacy
     surowy naglowek "PRODUKCJA PV / ZUŻYCIE DOMU / ..." zamiast tabelki) - `.history-head` i
     `.history-row` maja ROWNA specyficznosc (obie to pojedyncza klasa), a `.history-row { display:
     flex }` byl zadeklarowany PONIZEJ `.history-head { display: none }` w tym samym bloku @media -
     przy rownej specyficznosci wygrywa reguła zadeklarowana PÓŹNIEJ w zrodle, wiec `display: flex`
     z `.history-row` zawsze nadpisywal `display: none` z `.history-head` (dla elementu z obiema
     klasami na raz: `<div class="history-row history-head">`) - naglowek NIGDY realnie nie byl
     ukryty na telefonie, tylko przeksztalcony we flex i zawijany w nieforemne linijki. Naprawiono
     przenoszac `.history-head { display: none }` PONIZEJ `.history-row` - teraz to ONA jest
     regula zadeklarowana pozniej i wygrywa. */
  .history-head { display: none; }
  .history-day { flex: 1 1 auto; align-self: center; font-size: 14px; }
  /* POPRAWKA 2026-09-06 #9 (zgloszona prosba: "jednostke z tabeli kWh przenies do nazwy
     kolumny") - etykiety w data-label sa teraz dluzsze (np. "Tania strefa (kWh)" zamiast "Tania
     strefa"); `flex-wrap: wrap` tutaj to zabezpieczenie NA WYPADEK, gdyby trzy etykiety + wartosci
     nie zmiescily sie w jednej linii na bardzo waskim telefonie - wtedy PV/Zuzycie/Tania strefa
     zawijaja sie do wlasnych linii zamiast wypychac cala strone w poziomy scroll. */
  .history-primary { display: flex; flex-wrap: wrap; gap: 12px; row-gap: 6px; }
  .history-primary .history-num,
  .history-secondary .history-num {
    display: flex; flex-direction: column; align-items: flex-end; gap: 2px; text-align: right;
  }
  /* POPRAWKA 2026-09-06 #29 (zgloszony problem: "tabelka nie wiadomo o co chodzi z tymi nazwami
     kolumn nad nia" - wybrany "Wariant A", patrz komentarz w history_chart.html) - etykiety
     skrocone do jednego slowa (`data-label`, bez jednostki - patrz `.history-legend` wyzej w
     pliku) i poprzedzone kropka ("● " w `content`), kolorowana TAK SAMO jak tekst etykiety - w
     efekcie wyglada jak mala kolorowa "kropka legendy" bez dodatkowego znacznika w HTML. Kolor
     dobierany przez atrybut `data-color` (ustawiony tylko na trzech "glownych" wartosciach w
     `.history-primary`) - `.history-secondary` (Pobrano/Wysłano siec) ma juz swoj kolor
     (`--c-grid-icon`) bez potrzeby atrybutu, wiec zostaje bez zmian. */
  .history-legend {
    display: flex; flex-wrap: wrap; gap: 8px 14px; padding: 2px 2px 12px;
    border-bottom: 1px solid var(--border-soft); margin-bottom: 4px;
    font-size: 11.5px; color: var(--text-muted);
  }
  .history-legend-item { display: flex; align-items: center; gap: 5px; }
  .history-primary .history-num::before,
  .history-secondary .history-num::before {
    content: "● " attr(data-label); order: -1; font-size: 10px; font-weight: 600;
    letter-spacing: 0.02em; white-space: nowrap;
  }
  .history-primary .history-num::before { color: var(--text-faint); }
  .history-primary .history-num[data-color="pv"]::before { color: var(--c-pv-icon); }
  .history-primary .history-num[data-color="home"]::before { color: var(--c-home-icon); }
  .history-primary .history-num[data-color="storage"]::before { color: var(--c-storage-icon); }
  .history-secondary {
    flex: 1 0 100%; display: flex; justify-content: flex-end; gap: 14px;
    background: var(--c-grid-node-bg); border: 1px solid var(--c-grid-node-border);
    border-radius: 9px; padding: 6px 10px;
  }
  .history-secondary .history-num { color: var(--text-secondary); font-size: 12.5px; }
  .history-secondary .history-num::before { color: var(--c-grid-icon); opacity: 0.85; }
  .diagram-node { min-width: 84px; }
  .flow-icon-circle { width: 56px; height: 56px; margin-bottom: 6px; }
  .diagram-value { font-size: 17.5px; }
  .diagram-canvas { aspect-ratio: 800 / 680; }
  .summary-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* POPRAWKA 2026-09-06 #28 (zgloszona prosba: "duze te liczby moze to zmniejszyc na telefonie
     jakos" - ten sam zrzut co POPRAWKA #27) - w 2 kolumnach na telefonie kafelek ma mniej wiecej
     polowe szerokosci karty; przy niezmienionej ikonie 40px i wartosci 21px (np. "3946 W", "5,0
     kWh") liczbie/jednostce zabraklo miejsca w jednej linii i lamaly sie miedzy liczba a
     jednostka (np. "3946" / "W" w dwoch liniach) - wygladalo to niechlujnie. Mniejsza ikona,
     mniejszy padding/gap i mniejsza czcionka wartosci/etykiety daja liczbie wiecej szansy na
     zmieszczenie sie w jednej linii, a caly kafelek jest lepiej dopasowany do 2-kolumnowego
     ukladu (patrz tez POPRAWKA #29 w summary_tiles.html - spacja miedzy liczba a jednostka
     zamieniona na twarda spacje, zeby jednostka nigdy nie odrywala sie od liczby, niezaleznie od
     szerokosci). */
  .summary-tile { padding: 12px 12px; gap: 10px; }
  .summary-tile-icon { flex: 0 0 34px; width: 34px; height: 34px; }
  .summary-tile-icon svg { width: 18px; height: 18px; }
  .summary-tile-value { font-size: 18px; }
  .summary-tile-label { font-size: 12px; }
  /* Ten sam wzorzec pomniejszenia co .summary-tile powyzej, zeby .night-history-note (klon
     .summary-tile - patrz uzasadnienie przy jej definicji) skalowal sie na telefonie identycznie
     jak kafelki "Przebieg dnia", z ktorymi ma wizualnie tworzyc spojna calosc. */
  .night-history-note { padding: 12px 12px; gap: 10px; }
  .night-history-note-icon { flex: 0 0 34px; width: 34px; height: 34px; font-size: 16px; }
  .night-history-note-value { font-size: 18px; }
  .night-history-note-label { font-size: 12px; }
}

@media (max-width: 480px) {
  .diagram-node { min-width: 100px; }
  .flow-icon-circle { width: 42px; height: 42px; margin-bottom: 4px; }
  .diagram-label { font-size: 12.5px; margin-bottom: 1px; }
  .diagram-value { font-size: 16px; }
  .diagram-canvas { aspect-ratio: 800 / 900; }
}
