  /* Tokeny (kolor, cień, promień, krój, gęstość) — css/tokens.css.
     W tym pliku nie wolno zapisać wartości koloru wprost.
     Wyjątek: kolory oznakowania szlaków (biel–kolor–biel na .mark),
     bo to fakt z terenu, nie decyzja projektowa. */
  * { box-sizing: border-box; }
  html, body { height: 100%; margin: 0; }
  body {
    font: 15px/var(--leading) var(--font-sans);
    color: var(--foreground);
    background: var(--background);
    overflow: hidden;
  }
  #map { position: absolute; inset: 0; }

  /* ---------- panel ---------- */
  #panel {
    position: absolute;
    top: 0; left: 0; bottom: 0;
    width: var(--panel-w);
    background: var(--background);
    border-right: 1px solid var(--border);
    /* Gradacja głębi: panel leży najwyżej nad mapą, profil niżej. Bez tego
       oba pływały na tym samym poziomie i mapa nie miała się pod czym schować. */
    box-shadow: var(--shadow-ridge);
    display: flex;
    flex-direction: column;
    z-index: 2;
    transition: transform .22s ease;
  }
  #panel.collapsed { transform: translateX(calc(-1 * var(--panel-w))); }
  /* Uchwyt arkusza istnieje tylko na telefonie; na desktopie panel się nie rusza. */
  .arkusz-uchwyt { display: none; }

  .head { padding: 14px 18px 12px; border-bottom: 1px solid var(--border); }
  .head-row { display: flex; align-items: center; gap: 10px; }
  .head-row .logo { margin-right: auto; }
  .head h1 { margin: 0 0 2px; font-size: 16px; font-weight: 600; letter-spacing: -.01em; }

  /* Zakładki panelu to nawigacja między widokami, nie wybór wartości — stąd
     linia pod aktywną, a nie przełącznik segmentowy. Segment mówi „jedna
     z kilku opcji tego samego"; zakładka mówi „inne miejsce". */
  .tabs { display: flex; padding: 0 18px; gap: 2px; border-bottom: 1px solid var(--border); }
  .tab {
    padding: 9px 2px; margin-right: 18px; border: 0; background: none; cursor: pointer;
    font: inherit; font-size: 13.5px; color: var(--muted-foreground);
    border-bottom: 2px solid transparent; margin-bottom: -1px;
  }
  .tab[aria-selected="true"] { color: var(--foreground); font-weight: 600; border-bottom-color: var(--primary); }

  .controls { padding: 12px 18px; border-bottom: 1px solid var(--border); }

  /* ---------- planowanie ---------- */
  #panel.planning .controls, #panel.planning #list { display: none; }
  #plan { display: none; flex: 1; overflow-y: auto; }
  #plan.on { display: block; }
  /* Karta miejsca należy wyłącznie do arkusza — na desktopie ta sama treść
     idzie do dymka przy węźle. Bez tej reguły `#wezel` był na szerokim ekranie
     zwykłym blokiem i pokazywał treść, którą zostawił po sobie telefon: po
     obróceniu tabletu albo rozciągnięciu okna na dnie panelu wisiała karta
     sprzed zmiany szerokości, z żywymi przyciskami. */
  #wezel { display: none; }
  .plan-box { padding: 14px 18px; border-bottom: 1px solid var(--border); }
  .plan-hint { font-size: 13px; color: var(--muted-foreground); line-height: 1.5; }

  .metodologia > summary {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 12.5px; color: var(--muted-foreground);
    cursor: pointer; list-style: none; text-decoration: underline;
    text-underline-offset: 3px;
  }
  .metodologia > summary::-webkit-details-marker { display: none; }
  .metodologia > summary:hover { color: var(--foreground); }
  .metodologia > summary svg { width: 15px; height: 15px; flex: none; }
  .metodologia p {
    margin: 10px 0 0; font-size: 12.5px; line-height: 1.5;
    color: var(--muted-foreground);
  }
  .wp { display: flex; align-items: center; gap: 9px; padding: 6px 0; font-size: 13.5px; }
  /* Trzy stany, bo trasa ma kierunek: skąd, którędy, dokąd. Wypełnienie jest
     zarezerwowane dla początku i końca — punkty pośrednie zostają przy samym
     obrysie, żeby nie konkurowały z tymi dwoma. Wcześniej wszystkie trzy były
     jednakowo zielone i start niczym się nie wyróżniał. */
  .wp .pin {
    width: 21px; height: 21px; border-radius: 50%; flex: none;
    display: grid; place-items: center;
    font-family: var(--font-mono); font-size: 11px; font-weight: 500;
    font-feature-settings: 'tnum';
    background: none; color: var(--foreground);
    box-shadow: inset 0 0 0 2px var(--border-strong);
  }
  .wp:first-child .pin {
    background: var(--primary); color: var(--primary-foreground); box-shadow: none;
  }
  /* Przy jednym punkcie pierwszy jest zarazem ostatnim — i wtedy jest startem,
     nie metą. Stąd :not(:first-child). */
  .wp:last-child:not(:first-child) .pin {
    background: var(--waypoint-end); color: var(--primary-foreground); box-shadow: none;
  }
  .wp .val { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .wp .leg { color: var(--muted-foreground); font-size: 12px; font-variant-numeric: tabular-nums; flex: none; }
  .wp .fin { flex: none; color: var(--primary); font-size: 12px; font-weight: 700; }
  .wp .rm {
    flex: none; width: 22px; height: 22px; border: 0; border-radius: 6px;
    background: none; color: var(--muted-foreground); cursor: pointer; font-size: 15px; line-height: 1;
  }
  .wp .rm:hover { background: var(--muted); color: var(--alert-foreground); }
  .wp-empty { font-size: 13px; color: var(--muted-foreground); padding: 4px 0 2px; line-height: 1.5; }
  /* Trasa układana od celu: samotny punkt jest metą, nie startem. Reguła
     musi stać po :first-child, bo pierwszy punkt jest wtedy zarazem ostatnim. */
  #wp-list.od-konca .wp:only-child .pin {
    background: var(--waypoint-end); color: var(--primary-foreground); box-shadow: none;
  }

  /* ---------- wyszukiwarka punktów ---------- */
  .wp-search { position: relative; display: block; margin-top: 10px; }
  /* wygląd pola: .pole w prymitywy.css; etykieta, wiersze wyników i ekran
     szukania na telefonie — w css/szukaj.css, wspólnym z powłoką mobilną */
  .wp-hits {
    position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 5;
    background: var(--background); border: 1px solid var(--border); border-radius: 9px;
    box-shadow: var(--shadow-soft); overflow: hidden; max-height: 260px; overflow-y: auto;
  }
  /* Ekran szukania z pola należy w planerze do telefonu. Na szerokim otwiera
     go tylko pomyłka w kodzie — i lepiej, żeby wtedy nie było go widać wcale.
     Wyjątkiem jest lupa na mapie (`na-mapie`): tam szukanie wychodzi kartą pod
     rzędem sprzętu — patrz niżej i `otworzSzukanieZMapy()` w plan.js.
     Tylko tutaj, nie we wspólnym css/szukaj.css: w powłoce mobilnej ten sam
     ekran służy polu celu i ma działać przy każdej szerokości okna. */
  @media (min-width: 781px) { .szukaj:not(.na-mapie) { display: none; } }

  /* akcje w dymku po kliknięciu w węzeł */
  /* Nazwa i zamknięcie w jednym wierszu.

     Ujemne marginesy nie są kosmetyką: pole dotyku przycisku jest WYŻSZE niż
     wiersz tytułu (28 px wobec 22), więc bez nich rozpychałoby wiersz i spychało
     podtytuł. Góra i dół zdejmują tę różnicę po połowie — znak siada w osi
     pierwszej linii nazwy, a wiersz zostaje wysokości tekstu. Prawy margines
     dosuwa przycisk do krawędzi dymka, który ma na wszystko 280 px. */
  /* Zamknięcie leży NAD głową karty, a nie w jej kolumnie: odsuwa od siebie
     tylko nazwę, a wiersz z plakietką i wysokością może wejść pod nie. Jako
     kolumna zabierało 44 px na całą wysokość zdjęcia i „1500 m n.p.m."
     spadało na telefonie do drugiego wiersza. */
  .np-glowa { position: relative; display: flex; align-items: flex-start; gap: 10px; }
  .np-tozsamosc { flex: 1; min-width: 0; }
  .np-zamknij { position: absolute; top: -3px; right: -6px; }
  .np-name { font-weight: 600; font-size: 13.5px; line-height: 1.3; padding-right: 26px; }

  /* KARTA WĘZŁA (2026-09-29) — trzy bloki: tożsamość, linki, akcje; kreska
     między nimi. Zdjęcie to miniatura obok nazwy, bez podpisu: atrybucję
     pokazuje lightbox po dotknięciu (`wirePopupPhoto`). */
  .foto-np { flex: none; width: 88px; margin: 0; }
  .foto-np img { aspect-ratio: 4 / 3; object-fit: cover; border-radius: 8px; }
  /* Rodzaj i wysokość w jednym wierszu. Plakietki dzielą styl z dymkiem
     przeglądania (`.pl-kat`, `.pl-status`), tu tylko bez własnych marginesów. */
  .np-sub {
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px;
    margin-top: 5px; font-size: 12px; color: var(--muted-foreground);
  }
  .np-sub .pl-kat, .np-sub .pl-status { margin: 0; }
  .np-wys { font-variant-numeric: tabular-nums; }

  /* Linki pomocnicze — tekstowe, bo nie zmieniają trasy i nie mogą ważyć
     tyle, co akcje planowania pod nimi. */
  .np-linki {
    display: flex; flex-wrap: wrap; gap: 2px 16px;
    margin-top: 10px; padding-top: 6px; border-top: 1px solid var(--border);
  }
  .np-link {
    display: inline-flex; align-items: center; gap: 5px; padding: 4px 0;
    border: 0; background: none; font: inherit; font-size: 12.5px; font-weight: 600;
    color: var(--primary); cursor: pointer;
  }
  .np-link svg { width: 15px; height: 15px; flex: none; }
  .np-link:hover:not(:disabled) { text-decoration: underline; text-underline-offset: 3px; }
  .np-link:disabled { color: var(--muted-foreground); cursor: not-allowed; }
  /* Szewron z symbolu patrzy w lewo: w dół = do rozwinięcia, w górę = otwarte. */
  [data-szczegoly] svg { transform: rotate(-90deg); transition: transform var(--dur-base) var(--ease-out); }
  [data-szczegoly][aria-expanded="true"] svg { transform: rotate(90deg); }
  .np-szczegoly { padding: 4px 0 8px; }
  .np-szczegoly p { margin: 0 0 8px; font-size: 12.5px; line-height: 1.5; }
  .np-szczegoly .pl-fakty { margin-top: 0; }
  .np-szczegoly .pl-www { display: block; margin: 0 0 4px; }
  .np-akcje { margin-top: 8px; padding-top: 10px; border-top: 1px solid var(--border); }
  /* W dymku to samo przypięcie głowy co w arkuszu: rozwinięty opis przewija
     się pod nią (dymek ma sufit 60vh). Ujemne `top` zjada wcięcie treści
     dymka, żeby tekst nie prześwitywał nad głową. */
  .maplibregl-popup-content .np-glowa {
    position: sticky; top: -9px; z-index: 1;
    margin-top: -9px; padding: 9px 0 8px; background: var(--popover);
  }
  .maplibregl-popup-content .np-zamknij { top: 6px; }
  /* Jedna szerokość dla każdego węzła. Dopasowany do treści dymek skakał:
     rozdroże bez zdjęcia było wąskie, szczyt ze zdjęciem szeroki, a przy
     przechodzeniu z punktu do punktu wyglądało to na przypadek. 340 px mieści
     zdjęcie, dwa wiersze nazwy i linki w jednym wierszu. */
  .dymek-wezla .maplibregl-popup-content { box-sizing: border-box; width: 340px; }
  /* Trzy linki w jednym wierszu — dymek rośnie do 340 px, zamiast łamać je na dwa. */
  .maplibregl-popup-content .np-linki { margin-top: 2px; flex-wrap: nowrap; white-space: nowrap; }
  .maplibregl-popup-content .np-glowa + .np-akcje { margin-top: 2px; }
  /* Ile stąd do sieci szlaków. Osobny blok, nie dopisek do podtytułu: to jest
     liczba, od której zależy decyzja, a nie opis miejsca. Powyżej progu
     dojścia zmienia się w ostrzeżenie — ta sama treść, inna waga. */
  .np-dojscie {
    font-size: 12px; line-height: 1.35; margin: 0 0 2px;
    padding: 6px 8px; border-radius: var(--radius-sm, 8px);
    background: var(--muted); color: var(--muted-foreground);
  }
  .np-dojscie b { color: var(--foreground); font-weight: 600; }
  .np-dojscie.ostrzega {
    background: color-mix(in oklab, var(--mapa-utrudnienie) 14%, transparent);
    color: var(--foreground);
  }

  /* Akcje w dymku są przyciskami, bo są decyzjami — wcześniej wyglądały jak
     pozycje listy i nie było widać, że coś się po nich stanie. Wygląd bierze
     .btn z prymitywów; tutaj zostaje tylko odstęp i plakietka z numerem. */
  .np-akcje { display: flex; flex-direction: column; gap: 6px; }
  .np-act { justify-content: flex-start; }
  .np-act svg { width: 16px; height: 16px; flex: none; margin-right: 8px; }
  .np-sep { height: 1px; background: var(--border); margin: 5px 0; }
  /* wygląd przycisków: .btn i warianty w prymitywy.css.
     Odstęp należy do miejsca, w którym przycisk stoi, nie do samego przycisku —
     globalny margines wypychał w dół każdy przycisk w aplikacji, także ikonę
     strzałki w nagłówku, przez co wyglądała na osadzoną za nisko. */
  .result-head { display: flex; align-items: baseline; gap: 10px; }
  .result-head .big { font-size: 24px; font-weight: 600; font-variant-numeric: tabular-nums; }
  .result-head .unit { color: var(--muted-foreground); font-size: 13px; }
  /* Wartość drugorzędna — informacyjna, nie konkuruje z odległością główną. */
  .slope { margin-top: 3px; font-size: 12px; color: var(--muted-foreground);
           font-variant-numeric: tabular-nums; }
  .slope span { cursor: help; opacity: .6; }

  /* ---------- oznakowanie szlaków ---------- */
  /* Znak szlaku w Polsce to trzy poziome pasy: biały, kolor, biały (9x15 cm).
     Odwzorowujemy ten układ, żeby ikona w legendzie była tym samym znakiem,
     którego turysta szuka na drzewie. */
  .mark {
    display: inline-block; width: 15px; height: 10px; flex: none;
    border: 1px solid var(--border-strong); border-radius: 2px; vertical-align: -1px;
    background: linear-gradient(to bottom,
      #fff 0 30%, var(--c, #868e96) 30% 70%, #fff 70% 100%);
  }
  /* ---------- ostrzeżenia ---------- */
  .warns {
    margin: 11px 0 4px; padding: 10px 12px;
    background: var(--alert); border: 1px solid var(--alert-border); border-radius: 9px;
  }
  .warn { display: flex; gap: 9px; align-items: flex-start; font-size: 12.5px; line-height: 1.45; }
  .warn + .warn { margin-top: 7px; }
  .warn-ico {
    flex: none; width: 18px; height: 18px; border-radius: 5px; background: var(--destructive);
    color: var(--destructive-foreground); font-weight: 700; font-size: 12px; display: grid; place-items: center;
  }
  .warn-txt b { font-variant-numeric: tabular-nums; }
  .warn-body { font-size: 13px; line-height: 1.5; }
  .warn-body > div + div { margin-top: 3px; }

  /* ---------- ograniczenia na szlakach ---------- */
  /* Stoją nad ostrzeżeniami technicznymi i wyglądają inaczej: tamte mówią
     „będzie trudno", te „możesz tędy nie przejść". Stąd pełny obrys w kolorze
     skutku, a nie wspólne pudełko jak przy `.warns`. */
  .ogr-blok { margin: 11px 0 4px; }
  .ogr-tytul {
    font-size: 11px; font-weight: 600; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--muted-foreground); margin-bottom: 6px;
  }
  .ogr-tytul-luzne { margin-top: 10px; }
  .ogr-lista { list-style: none; margin: 0; padding: 0; }
  .ogr-lista li {
    font-size: 12.5px; line-height: 1.45; padding: 8px 10px; border-radius: 8px;
    border: 1px solid var(--border); border-left-width: 3px; background: var(--muted);
  }
  .ogr-lista li + li { margin-top: 6px; }
  .ogr-lista + .ogr-lista { margin-top: 6px; }
  /* Modyfikatory MUSZĄ być tak samo swoiste jak `.ogr-lista li`: skrót
     `border` powyżej ustawia też kolor lewej krawędzi, więc sama klasa na
     `li` przegrywała i obrys wychodził w kolorze ramki. */
  .ogr-lista li.ogr-zamkniety { border-left-color: var(--destructive); }
  /* Bursztyn z palety trudności, nie własny token: utrudnienie czyta się
     dokładnie tak samo jak „średnia" na plakietce — uwaga, nie zakaz. */
  .ogr-lista li.ogr-planowane,
  .ogr-lista li.ogr-utrudniony { border-left-color: var(--trail-medium); }
  /* Sezonowe: ta sama czerwień co zamknięcie, ale kropkami — jak na mapie. */
  .ogr-lista li.ogr-sezonowe { border-left: 3px dotted var(--mapa-zamkniecie); }
  /* Komunikat bez przypisanego odcinka jest wyciszony: niesie prawdziwą treść,
     ale nie wiemy, czy dotyczy TEJ trasy — i nie wolno tego udawać. */
  .ogr-luzne { opacity: 0.82; border-left-style: dashed; }
  /* Linijka o utrudnieniu w karcie punktu na szlaku (`ograniczeniaPunktu`
     w plan.js) — bursztyn jak na liście ograniczeń: uwaga, nie zakaz. */
  .np-ogr { display: block; margin-top: 3px; color: var(--trail-medium); font-weight: 600; }
  .ogr-powod, .ogr-zrodlo { display: block; }
  .ogr-powod { margin-top: 3px; color: var(--muted-foreground); }
  .ogr-zrodlo { margin-top: 4px; font-size: 11px; color: var(--muted-foreground); }
  .ogr-okres { font-size: 12px; color: var(--muted-foreground); margin-top: 3px; }
  .ogr-wpis + .ogr-wpis { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }

  .trailbar {
    display: flex; height: 8px; border-radius: 4px; overflow: hidden;
    margin: 11px 0 9px; background: var(--muted);
  }
  .trailbar span { display: block; min-width: 2px; }
  .trailkeys { display: flex; flex-direction: column; gap: 5px; }
  .tkey { display: flex; align-items: center; gap: 7px; font-size: 12.5px; }
  .tkey .marks { display: flex; gap: 2px; flex: none; }
  .tkey .nm { color: var(--foreground); }
  /* Dwie kolumny liczb przy krawędzi: ile i jak długo. Stała szerokość
     czasu trzyma je w pionie, bo „35 min" i „1 h 15 min" różnią się długością. */
  .tkey .km { color: var(--muted-foreground); margin-left: auto; font-variant-numeric: tabular-nums; }
  .tkey .czas {
    color: var(--muted-foreground); font-variant-numeric: tabular-nums;
    min-width: 76px; text-align: right;
  }

  /* Tempo ma sens dopiero wtedy, gdy jest co przeliczać — przy pustej trasie
     to pytanie o nic. */
  /* Działania na trasie: ten sam przedmiot co „Udostępnij" i „Drukuj"
     w stopce, więc ten sam wygląd — mały przycisk-duch z ikoną. Przy lewej
     krawędzi, bo należą do listy punktów nad nimi. Odwrócenie idzie pierwsze:
     „Wyczyść" kasuje bezpowrotnie i nie ma być najłatwiejszym celem w wierszu. */
  .trasa-akcje { display: flex; gap: var(--space-2); margin-top: 10px; margin-left: -6px; }
  .trasa-akcje[hidden] { display: none; }

  .pace-row { margin-top: 12px; }
  .pace-row[hidden] { display: none; }
  .pace-row > span { font-size: 12.5px; color: var(--muted-foreground); }
  .seg { margin-top: 6px; }
  /* wygląd pola: .pole w prymitywy.css */


  #list { flex: 1; overflow-y: auto; overscroll-behavior: contain; }
  .item {
    display: flex; gap: 10px; align-items: flex-start;
    padding: 11px 18px; border-bottom: 1px solid var(--border);
    cursor: pointer; transition: background .1s;
  }
  .item:hover { background: var(--muted); }
  .item.active { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--primary); }
  .item .bar { width: 4px; align-self: stretch; min-height: 30px; border-radius: 2px; flex: none; }
  .item .txt { min-width: 0; }
  .item .nm { font-size: 13.5px; font-weight: 500; line-height: 1.3; }
  .item .meta { font-size: 12px; color: var(--muted-foreground); margin-top: 2px; font-variant-numeric: tabular-nums; }
  .empty { padding: 28px 18px; color: var(--muted-foreground); font-size: 13.5px; text-align: center; }

  /* ---------- szczegóły ---------- */
  #detail {
    position: absolute; left: calc(var(--panel-w) + 14px); top: 14px;
    width: 300px; background: var(--background); border: 1px solid var(--border);
    border-radius: 12px; box-shadow: var(--shadow-soft); padding: 14px 16px; z-index: 3;
    display: none;
  }
  #detail.on { display: block; }
  #detail h2 { margin: 0 6px 8px 0; font-size: 15px; font-weight: 600; line-height: 1.3; }
  #detail .close {
    position: absolute; top: 9px; right: 9px; width: 26px; height: 26px;
    border: 0; border-radius: 7px; background: var(--muted); cursor: pointer;
    font-size: 15px; line-height: 1; color: var(--muted-foreground);
  }
  #detail dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 5px 14px; font-size: 13px; }
  #detail dt { color: var(--muted-foreground); }
  #detail dd { margin: 0; font-variant-numeric: tabular-nums; }
  #detail .osm { display: inline-block; margin-top: 11px; font-size: 12.5px; color: var(--primary); }

  /* ---------- reszta ---------- */
  /* Wspólna forma obu uchwytów: języczek doklejony do krawędzi kontenera,
     wychodzący poza nią, z chevronem obróconym w stronę zwijania. */
  .uchwyt {
    position: absolute; z-index: 4;
    display: grid; place-items: center;
    border: 1px solid var(--border-strong);
    background: var(--card); color: var(--muted-foreground);
    cursor: pointer;
    box-shadow: var(--shadow-soft);
    transition: color var(--dur-fast) var(--ease-out),
                background-color var(--dur-fast) var(--ease-out),
                left .22s ease, transform .22s ease;
  }
  .uchwyt:hover { background: var(--muted); color: var(--foreground); }
  .uchwyt svg { width: 15px; height: 15px; }

  .uchwyt-bok {
    top: 50%; width: 20px; height: 52px;
    border-left: 0;
    border-radius: 0 var(--radius-md) var(--radius-md) 0;
  }
  .uchwyt-gora {
    left: 50%; width: 52px; height: 20px;
    border-bottom: 0;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
  }

  #toggle {
    left: var(--panel-w);
    transform: translateY(-50%);
  }
  #panel.collapsed ~ #toggle { left: 0; }
  #panel.collapsed ~ #toggle svg { transform: rotate(180deg); }
  #detail { transition: left .22s ease; }
  #panel.collapsed ~ #detail { left: 60px; }

  /* ---------- pasek nowej wersji ---------- */

  /* Nad mapą, u góry i na środku. Na desktopie pasek nagłówka siedzi
     w panelu, a na telefonie ma po bokach sygnet i zębatkę — środek jest
     w obu układach wolny. Nad wszystkim poza ustawieniami, bo to komunikat
     o stanie aplikacji, nie element mapy. */
  /* Kontener trzyma paski jeden pod drugim i sam nie łapie kliknięć —
     inaczej przezroczysty pas nad mapą blokowałby jej przewijanie. */
  /* Środek MAPY, nie środek okna. Na desktopie panel zajmuje lewe 400 px,
     a pasek leży wyżej od niego w stosie — wyśrodkowany w oknie wchodziłby
     na treść panelu i zasłaniał ją własnym tłem. */
  #paski {
    position: fixed; top: 14px;
    left: calc(50% + var(--panel-w) / 2); transform: translateX(-50%);
    z-index: 6; pointer-events: none;
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    max-width: calc(100vw - var(--panel-w) - 24px);
  }
  .pasek {
    pointer-events: auto;
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px 8px 14px;
    background: var(--background); color: var(--foreground);
    border: 1px solid var(--border); border-radius: var(--radius-pill);
    box-shadow: var(--shadow-raised);
    font-size: 13.5px;
    max-width: 100%;
  }
  .pasek[hidden] { display: none; }
  /* `.btn` ustawia `display: inline-flex` i tym samym przebija atrybut
     `hidden` — schowany przycisk akcji rysował się jako pusta pastylka
     (test z terenu, 2026-09-29). */
  .pasek [hidden] { display: none; }
  /* Zdanie ustępuje przyciskowi — to on jest tu działaniem. */
  .pasek span { min-width: 0; }
  #nowa-wersja span { white-space: nowrap; }

  @media (max-width: 780px) {
    /* Na telefonie panelu z boku nie ma — pasek wraca na środek okna
       i schodzi POD pasek z sygnetem i zębatką (jego dno jest na 70 px),
       żeby nie zasłonić żadnego z nich.

       `stretch` zamiast `center`: przy zawijaniu się do własnej szerokości
       dłuższy komunikat układał się w wąską kolumnę na 188 z 375 px i rósł
       na cztery linie. Pasek ma zająć szerokość, którą ma. */
    #paski {
      top: calc(74px + env(safe-area-inset-top, 0px));
      left: 50%; width: calc(100vw - 24px); max-width: none;
      align-items: stretch;
    }
    .pasek { font-size: 13px; padding: 7px 8px 7px 12px; gap: 8px; }
    /* Na desktopie pasek dopasowuje się do treści, więc `nowrap` trzyma go
       w jednej linii. Tutaj pasek ma stałą szerokość i ten sam `nowrap`
       wysuwał zdanie POD przycisk — czytało się „Jest nowsza wersja aplikac". */
    #nowa-wersja span { white-space: normal; }
    /* Komunikat o sieci jest dłuższy niż jedna linia i to jest w porządku —
       ważniejsze, żeby dało się go przeczytać, niż żeby zmieścił się w pasek. */
    #pasek-sieci { line-height: 1.35; }
  }

  /* ---------- profil wysokości ---------- */
  /* Profil siedzi w rogu i zajmuje tyle, ile potrzebuje wykres — wcześniej
     rozciągał się na całą wolną szerokość i przykrywał mapę pasem przez pół
     ekranu, choć czyta się z niego kształt, a nie szczegóły. */
  #profile {
    position: absolute; left: auto; right: 14px; bottom: 14px;
    width: 570px; max-width: calc(100% - var(--panel-w) - 28px);
    background: var(--background); border: 1px solid var(--border); border-radius: 12px;
    box-shadow: var(--shadow-raised); padding: 11px 13px 7px; z-index: 3; display: none;
    transition: left .22s ease;
  }
  #profile.on { display: block; }
  #panel.collapsed ~ #profile { left: 14px; }
  #profile .ph {
    display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;
    font-size: 12.5px; color: var(--muted-foreground); margin-bottom: 6px;
  }
  #profile .ph b { color: var(--foreground); font-size: 14px; font-variant-numeric: tabular-nums; }
  /* Nad mapą profil stoi w kontekście: widać wykres i wiadomo, czego dotyczą
     liczby. Podpis należy do arkusza, nie do niego. */
  .ph-tytul { display: none; }
  #profile .ph .dim, #profile .ph .dim b { color: var(--muted-foreground); font-size: 12px; font-weight: 500; }
  #profile .up { color: var(--ascent); }
  #profile .down { color: var(--descent); }
  /* Po id, nie po `#profile > svg`: na telefonie wykres wyprowadza się do
     karty trasy, a styl ma iść za nim. Chevron w uchwycie zostaje nietknięty —
     bez tego rozciągał się na całą szerokość języczka. */
  #p-svg { display: block; width: 100%; height: auto; }
  #profile .readout {
    font-variant-numeric: tabular-nums; margin-left: auto; font-size: 12.5px;
    color: var(--foreground); min-height: 1em;
  }

  /* Przełącznik podkładu — pod kontrolkami MapLibre po prawej stronie. */
  /* Pudełko wykresu w karcie trasy istnieje tylko na telefonie; na desktopie
     profil pływa nad mapą i slot zostaje pusty. */
  #profil-slot { display: none; }

  #loading {
    position: absolute; inset: 0; display: grid; place-items: center; z-index: 9;
    background: var(--background); color: var(--muted-foreground); font-size: 14px;
  }
  #loading.done { display: none; }
  /* Komunikat zamiast wiecznego „Wczytuję szlaki…", gdy start pęknie. */
  .ladowanie-blad {
    display: grid; gap: 8px; max-width: 30rem; padding: 0 24px;
    text-align: center; line-height: 1.5;
  }
  .ladowanie-blad b { color: var(--foreground); font-size: 15px; }
  .ladowanie-blad .dim { font-size: 12.5px; }

  /* Dymek jest powierzchnią interfejsu, nie mapy, więc idzie za motywem.
     MapLibre maluje go na sztywno na biało — w trybie ciemnym dostawaliśmy
     jasny tekst na białym tle. */
  /* Dymek musi leżeć NAD profilem wysokości (`#profile`, z-index 3). Bez tego
     przy wyznaczonej trasie panel profilu przykrywał przyciski dymka i klik
     w „dodaj kolejny punkt" nie robił nic — bez błędu w konsoli, więc z boku
     wyglądało to na zawieszoną aplikację. Zostajemy pod panelem ustawień (7)
     i pod lightboxem (20): tamte są modalne, dymek nie. */
  .maplibregl-popup { z-index: 5; }

  .maplibregl-popup-content {
    font: inherit; font-size: 13px; padding: 9px 12px;
    border-radius: var(--radius-lg);
    background: var(--popover); color: var(--popover-foreground);
    box-shadow: var(--shadow-raised);
  }
  .maplibregl-popup-anchor-top .maplibregl-popup-tip,
  .maplibregl-popup-anchor-top-left .maplibregl-popup-tip,
  .maplibregl-popup-anchor-top-right .maplibregl-popup-tip { border-bottom-color: var(--popover); }
  .maplibregl-popup-anchor-bottom .maplibregl-popup-tip,
  .maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip,
  .maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip { border-top-color: var(--popover); }
  .maplibregl-popup-anchor-left .maplibregl-popup-tip { border-right-color: var(--popover); }
  .maplibregl-popup-anchor-right .maplibregl-popup-tip { border-left-color: var(--popover); }
  .maplibregl-ctrl-attrib { font-size: 11px; }

  /* ---------- punkt oczekujący na decyzję ----------

     Dotknięty węzeł, dla którego stoi otwarte pytanie „co z nim dalej":
     na telefonie karta z akcjami wjeżdża w arkusz przy dolnej krawędzi,
     więc bez znacznika nie ma jak połączyć jej z miejscem na mapie —
     dymek robił to sam swoim dzióbkiem, arkusz nie robi tego wcale.

     Znacznik DOM, nie warstwa MapLibre: pulsowanie ma iść po kompozytorze,
     a nie przez przemalowywanie warstwy w każdej klatce. Mapa i tak walczy
     tu o klatki, a to jedyna animacja, którą da się oddać za darmo. */
  .puls-oczekujacy {
    position: relative; width: 17px; height: 17px; pointer-events: none;
  }
  .puls-oczekujacy span {
    position: absolute; inset: 0; border-radius: 50%;
    background: var(--marker-oczekujacy);
  }
  /* Rdzeń w białej obwódce — tej samej, co reszta znaczników mapy: pod
     spodem bywa i śnieg, i ciemny las. */
  .puls-rdzen {
    border: 2.5px solid var(--mapa-halo);
    box-shadow: 0 1px 3px var(--marker-cien);
  }
  .puls-fala { animation: puls-oczekujacy 1.7s var(--ease-out) infinite; }
  @keyframes puls-oczekujacy {
    from { transform: scale(1); opacity: 0.5; }
    to   { transform: scale(2.9); opacity: 0; }
  }
  /* Bez ruchu zostaje sama poświata: znacznik ma dalej mówić „ten punkt",
     tylko ciszej. */
  @media (prefers-reduced-motion: reduce) {
    .puls-fala { animation: none; transform: scale(1.9); opacity: 0.28; }
  }

  /* ---------- arkusz dolny (telefon) ----------
     Panel przestaje być kolumną z boku i staje się arkuszem wysuwanym od dołu.
     Wysokość zmienia się skokowo między detentami; pasek zakładek stoi POZA
     arkuszem, przy dolnej krawędzi ekranu, więc nie jedzie razem z nim.

     Jednostka dvh, nie vh: na telefonie pasek adresu chowa się przy
     przewijaniu i vh liczone od pełnego ekranu zostawiałoby arkusz uciętym. */
  @media (max-width: 780px) {
    :root {
      --panel-w: 100%;
      --pasek-h: calc(56px + env(safe-area-inset-bottom, 0px));
      /* 18 + 36 (sygnet) + 8 + kreska; wcięcie ekranu dochodzi u góry, bo
         tam jest wycięcie. Arkusz musi znać tę liczbę, żeby nie wjechać
         pod pasek nad mapą. */
      --head-h: calc(63px + env(safe-area-inset-top, 0px));
      /* Górna linia wszystkiego, co pływa nad mapą: sygnetu po lewej i rzędu
         sprzętu po prawej. Jedna zmienna, bo to jedna linia — dopóki liczba
         stała w dwóch miejscach, rząd sprzętu siedział 65 px wyżej niż znak
         i na iPhonie wjeżdżał pod wysepkę z zegarem. */
      --rzad-gora: calc(18px + env(safe-area-inset-top, 0px));
      --arkusz-h: 0px;

      /* Ile mapy ZOSTAJE nad arkuszem, choćby treść w nim była najdłuższa.

         To jest ta liczba, przez którą trasa wybrana z listy rysowała się na
         iPhonie poza kadrem, a w przeglądarce zawsze dobrze. Sufity detentów
         stały w `dvh`, a wcięcia ekranu — wysepka u góry, kreska u dołu —
         zabierają swoje POZA nimi: 59 px w pasku nad mapą i 34 px w pasku
         zakładek. Dziewięćdziesiąt trzy piksele, których nie ma w oknie
         przeglądarki. Z paska mapy zostawało wtedy 85 px, `refitRoute()`
         uznawał, że nie ma czego kadrować, i zostawiał kadr tam, gdzie stał.
         Wyglądało to na loterię, bo zależało od wysokości karty: krótsza nie
         dobijała do sufitu i wszystko działało.

         Sufit liczy się więc od tego, co naprawdę zostaje na ekranie — pełna
         wysokość minus pasek nad mapą, minus pasek zakładek, minus ten zapas.
         Wcięcia siedzą już w obu paskach, więc same się odejmują. */
      --mapa-min: 200px;
      /* Pas do chwycenia arkusza palcem (2026-09-12). Przy 26 px palec trafiał
         raz w uchwyt, raz w treść tuż pod nim — i zamiast arkusza ruszała
         lista. 38 px wydłuża arkusz o dwanaście pikseli, ale chwyt przestaje
         być loterią. Jedna zmienna, bo tę samą liczbę muszą znać warstwa
         gestu, nagłówki widoków, karta miejsca i pasek trasy w peek. */
      --uchwyt-h: 38px;
      --arkusz-sufit: calc(100dvh - var(--head-h) - var(--pasek-h) - var(--mapa-min));
    }

    #panel {
      top: auto; left: 0; right: 0; bottom: var(--pasek-h);
      width: auto; height: var(--arkusz-h);
      border-right: 0; border-top: 1px solid var(--border);
      border-radius: var(--radius-xl) var(--radius-xl) 0 0;
      box-shadow: var(--shadow-ridge);
      overflow: hidden;
      transition: height var(--dur-slow) var(--ease-out);
    }
    #panel.collapsed { transform: none; }

    .arkusz-peek { --arkusz-h: 0px; }
    /* Węzeł dostaje wysokość swojej treści (`--wezel-h` liczy `dopasujWezel()`
       w arkusz.js), ale nie więcej niż połowa z okładem: mapa ma zostać
       widoczna, bo to po nią użytkownik dotknął ekranu. Dolna granica pilnuje,
       żeby karta bez opisu nie zwinęła się do paska. */
    .arkusz-wezel {
      --arkusz-h: clamp(168px, var(--wezel-h, 252px), min(60dvh, var(--arkusz-sufit)));
    }
    /* Peek nie jest pusty, gdy jest już trasa: zostaje jeden wiersz z czasem
       i dystansem, czyli to, po co się na ten ekran patrzy w marszu.
       Wysokość na wiersz plus miejsce na uchwyt nad nim. */
    .arkusz-peek.arkusz-z-trasa { --arkusz-h: calc(24px + var(--uchwyt-h)); }
    /* Roboczy detent sięga tam, gdzie kończy się treść zakładki
       (`--robocza-h` liczy `dopasujTresc()` w arkusz.js), ale nie wyżej niż
       połowa ekranu z okładem — dłuższa karta ma się przewijać, nie zabierać
       mapy. Dolna granica trzyma arkusz w rozmiarze, w którym widać, że to
       arkusz, a nie pasek. */
    .arkusz-robocza {
      --arkusz-h: clamp(180px, var(--robocza-h, 52dvh), min(52dvh, var(--arkusz-sufit)));
    }
    /* Rozwinięta karta trasy dostaje wyższy sufit niż lista: to jeden obiekt,
       który ma się zmieścić w całości, a nie sto trzydzieści do przewijania.
       Ten sam pułap co przy karcie miejsca — mapa i tak zostaje widoczna,
       bo to na niej narysowała się trasa. */
    .arkusz-karta.arkusz-robocza {
      --arkusz-h: clamp(180px, var(--robocza-h, 52dvh), min(62dvh, var(--arkusz-sufit)));
    }
    /* Pełny detent zatrzymuje się pod paskiem nad mapą. Przy stałej wartości
       (88dvh) arkusz wjeżdżał pod pasek i chował własny nagłówek za jego
       przezroczystym tłem — te piksele i tak nie były do czytania. Osiem
       pikseli luzu zostawia widoczny skrawek mapy nad zaokrągleniem. */
    .arkusz-pelna { --arkusz-h: calc(100dvh - var(--pasek-h) - var(--head-h) - 8px); }

    #panel { display: flex; flex-direction: column; }
    #plan, #recommended, #saved { order: 2; }

    /* Karta miejsca leży NAD całą treścią arkusza, więc jej `z-index` musi
       być wyższy niż przypiętych nagłówków zakładek (`.arkusz-naglowek`, 4).
       Przy 3 wychodziło coś, co wyglądało na błąd w treści, a było kolejnością
       malowania: nagłówek „Rekomendowane" ma nieprzezroczyste tło i przykrywał
       górę karty — czyli nazwę miejsca, po którą się w tę mapę stuknęło.
       Zakładka nie ma własnego `z-index`, więc jej przypięty nagłówek
       konkuruje z kartą wprost, w kontekście panelu.

       Szóstki nie ruszamy: to warstwa dotykowa uchwytu i ma zostać na wierzchu,
       żeby arkusz dawało się chwycić także nad kartą.

       Górne wcięcie `--uchwyt-h` to nie oddech, tylko OMINIĘCIE UCHWYTU — ta sama
       liczba i ten sam powód, co w `.arkusz-naglowek`. Przy 18 px nazwa
       miejsca wchodziła pod warstwę gestu i jej dotknięcie zaczynało
       przeciąganie arkusza. */
    #wezel {
      position: absolute; inset: 0; z-index: 5;
      display: none; overflow-y: auto;
      padding: 0 18px 16px; background: var(--background);
    }
    #wezel.on { display: block; }
    #wezel .np-name { font-size: 15px; }
    /* W arkuszu przycisk urasta do 44 px (pole dotyku palca), więc różnica
       wobec wiersza tytułu jest dwa razy większa niż w dymku — i tyle samo
       trzeba zdjąć. Prawy margines znika: karta i nagłówki zakładek mają to
       samo wcięcie 18 px, więc zamknięcia stoją w jednym pionie na wszystkich
       widokach arkusza (patrz `.arkusz-naglowek`). */
    #wezel .np-zamknij { top: calc(var(--uchwyt-h) - 10px); right: 18px; }
    #wezel .np-name { padding-right: 40px; }

    /* Głowa karty jest przypięta: rozwinięty opis przewija się POD nią, a nazwa
       i zamknięcie zostają. Wcięcie `--uchwyt-h` przeszło z pudełka na głowę —
       przy wcięciu na pudełku przypięta głowa zatrzymywała się pod pasem
       uchwytu, a treść prześwitywała nad nią. */
    #wezel .np-glowa {
      position: sticky; top: 0; z-index: 1;
      margin: 0 -18px; padding: var(--uchwyt-h) 18px 12px;
      background: var(--background);
    }
    #wezel .foto-np { width: 120px; }
    #wezel .np-linki { margin-top: 0; }
    /* Bez linków akcje stykają się z głową — odstęp daje jej dolne wcięcie. */
    #wezel .np-glowa + .np-akcje { margin-top: 0; }
    /* Link tekstowy, ale pod palec: wiersz ma wysokość dotyku, nie tekstu. */
    #wezel .np-link { min-height: 36px; font-size: 13px; }

    /* Nagłówek z logo leży nad mapą, nie w arkuszu — w arkuszu zabierał
       piątą część detentu roboczego, a nie niesie żadnej treści roboczej.

       Nad mapą nie ma już jednak belki: pasek z tłem i kreską odcinał górę
       ekranu i ważył tyle, co pełnoprawna nawigacja, mając w środku znak
       i jeden przełącznik. Zostają dwie rzeczy pływające nad mapą — jak
       kontrolki MapLibre po prawej. Każda niesie własne tło, bo mapa pod
       spodem bywa i śnieżnobiała, i ciemnozielona. */
    .head {
      position: fixed; top: 0; left: 0; right: 0; z-index: 4;
      /* Blok, nie flex: układ robi .head-row w środku, a to ono ma być pełnej
         szerokości — inaczej `margin-left: auto` przy przełączniku motywu
         odpycha go tylko do końca własnej, wąskiej ramki. */
      display: block;
      padding: var(--rzad-gora) 12px 8px;
      background: none; border-bottom: 0;
      /* Bez belki pasek jest tylko ramką na dwa znaczki — reszta jego
         powierzchni należy do mapy i musi przepuszczać dotknięcia. */
      pointer-events: none;
    }
    .head-row > * { pointer-events: auto; }

    /* Sam sygnet: kadrujemy znak poziomy do jego lewej, kwadratowej części
       (siatka 200 × 72, sygnet zajmuje pierwsze 72). Wersja pozioma wymaga
       120 px szerokości, których pasek nad mapą nie ma do oddania.
       Zaokrąglenie odpowiada `rx="16"` z siatki 72, a cień odkleja znak
       od mapy — sam kadr nie wystarczy, gdy pod spodem leży jasny stok. */
    .head .logo {
      flex: none; width: 34px; overflow: hidden;
      border-radius: 8px; box-shadow: var(--shadow-raised);
    }
    /* `flex: none` jest tu konieczne: bez niego znak jako element flex
       kurczy się do szerokości kadru zamiast zostać przycięty, i widać
       cały wordmark zmniejszony do 34 px zamiast samego sygnetu. */
    .head .logo svg { flex: none; width: calc(34px * 200 / 72); max-width: none; }

    /* Zębatka na wąskim ekranie nie mieszka już w nagłówku — przenosi ją do
       rzędu sprzętu `sprzet-mapy.js`, więc ubiera ją tam wspólny styl grupy.
       Zostaje samo schowanie podpisu, bo w rzędzie jest na niego za ciasno. */
    #ust-otworz-nazwa { display: none; }

    /* Profil zostaje na dnie arkusza wyłącznie jako pasek liczb w peek —
       po to, żeby czas i dystans dało się sprawdzić w marszu bez otwierania
       czegokolwiek. W każdym innym detencie schodzi z drogi: sam wykres
       mieszka teraz w karcie trasy, a wiersz liczb powtarzałby tylko to,
       co karta ma u góry. */
    #profile {
      position: static; order: 3; flex: none;
      width: auto; max-width: none; margin: 0;
      border: 0; border-radius: 0;
      box-shadow: none; padding: 15px 16px 11px;
      display: none;
    }
    #profile.on { display: none; }
    .arkusz-peek.arkusz-z-trasa #profile.on { display: block; }
    #p-toggle { display: none; }

    /* Zwinięty arkusz mówi jedno zdanie: masz trasę, tyle zajmie i tyle ma.
       Sześć liczb obok siebie było ścianą cyfr — reszta czeka w karcie,
       o jeden gest stąd. */
    #profile .ph {
      padding-right: 0; margin-bottom: 0; gap: 12px;
      align-items: baseline; flex-wrap: nowrap;
    }
    #profile .ph .dim,
    #profile .ph .up,
    #profile .ph .down,
    #profile .ph .max,
    /* Czytnik pozycji na wykresie działa pod kursorem, którego na telefonie
       nie ma — a pusty zbierał do siebie całą wolną szerokość i spychał
       liczby na środek paska. */
    #profile .readout { display: none; }
    .ph-tytul {
      display: inline; margin-right: auto;
      font-size: 13.5px; font-weight: 600; color: var(--foreground);
    }

    /* Wykres na końcu karty trasy: pełna szerokość pudełka, bez ramki wokół
       samego rysunku — sąsiedzi w karcie oddzielają się kreską, nie pudełkiem. */
    #profil-slot:not([hidden]) { display: block; }

    /* Uchwyt do chwycenia arkusza wzrokiem — kreska u góry, jak wszędzie.
       Stoi na środku pasa `--uchwyt-h`, żeby wzrok i palec celowały
       w to samo miejsce. */
    #panel::before {
      content: ''; position: absolute; top: calc((var(--uchwyt-h) - 4px) / 2); left: 50%;
      width: 38px; height: 4px; margin-left: -19px; border-radius: 999px;
      background: var(--border-strong); z-index: 5;
    }

    /* Warstwa do chwycenia arkusza palcem. Kreskę wyżej rysuje pseudoelement,
       ale gest potrzebuje prawdziwego elementu: własne `touch-action: none`
       jest tu jedynym sposobem, żeby przeciągnięcie ruszało arkuszem zamiast
       przewijać listę, która zaczyna się tuż pod spodem. */
    .arkusz-uchwyt {
      display: block; position: absolute; top: 0; left: 0; right: 0;
      height: var(--uchwyt-h); z-index: 6; touch-action: none;
    }

    /* Oddech pod kreską. Sześć pikseli, nie dwadzieścia: arkusz ma wysokość
       treści, więc każdy dodany piksel przekłada się na wysokość panelu. */
    #plan, #recommended, #saved { padding-top: 6px; }

    /* Ciągnięcie ma iść za palcem, a pomiar detentów nie ma być widoczny
       w ogóle — jedno i drugie wyklucza przejście wysokości. */
    .arkusz-ciagniecie #panel,
    .arkusz-mierzenie #panel { transition: none; }

    /* Zwijanie zaczyna się na szczycie listy, więc lista nie może oddawać
       gestu stronie ani odbijać się gumą — inaczej zamiast arkusza rusza
       tło albo przeglądarka przerywa gest. */
    #plan, #recommended, #saved, #wezel { overscroll-behavior: contain; }

    /* ---------- klawiatura ----------
       Klawiatura zabiera do połowy ekranu, a pasek zakładek i nagłówek są
       przypięte do układu strony, nie do obszaru widocznego — schowałyby się
       więc pod nią razem z polem, w które użytkownik właśnie pisze. Arkusz
       przejmuje wtedy cały widoczny obszar, a jego wymiary przychodzą
       z `visualViewport` (--vv-h, --kb-h w arkusz.js): dvh na iOS nie wie
       o klawiaturze. Przełącznik podkładu znika razem z nimi — to sprzęt
       mapy, a mapy w tej chwili nie widać. */
    .arkusz-klawiatura #panel {
      bottom: var(--kb-h, 0px);
      height: calc(var(--vv-h, 100dvh) - 10px);
      transition: none;
    }
    .arkusz-klawiatura .tabs,
    .arkusz-klawiatura .head,
    .arkusz-klawiatura .arkusz-uchwyt { display: none; }
    /* Profil zjadał trzecią część tego, co zostaje nad klawiaturą, a przy
       wpisywanym miejscu nikt nie czyta wykresu — miejsce należy się liście
       podpowiedzi. Wraca, gdy klawiatura schodzi. */
    .arkusz-klawiatura #profile.on { display: none; }

    /* Pasek zakładek: nawigacja przy dolnej krawędzi, zawsze w zasięgu kciuka. */
    .tabs {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 6;
      display: flex; gap: 0; margin: 0; padding: 0 0 env(safe-area-inset-bottom, 0px);
      background: var(--background); border-top: 1px solid var(--border);
      box-shadow: var(--shadow-ridge);
    }
    .tab {
      flex: 1; margin: 0; padding: 0; min-height: 56px;
      font-size: 12.5px; border-bottom: 0;
    }
    .tab[aria-selected="true"] {
      border-bottom: 0;
      box-shadow: inset 0 2px 0 var(--primary);
    }

    /* Przyciemnienie mapy dopiero od detentu roboczego — w peek mapa ma
       zostać w pełni klikalna. */
    #scrim {
      position: absolute; inset: 0; z-index: 1;
      background: oklch(0.19 0.024 160 / 0.32);
      animation: scrim-wejscie var(--dur-base) var(--ease-out);
    }
    #scrim[hidden] { display: none; }
    @keyframes scrim-wejscie { from { opacity: 0; } to { opacity: 1; } }

    /* Szczegóły szlaku wchodzą do arkusza w kolejnym kroku. */
    #detail { display: none !important; }
    #toggle { display: none; }
    .maplibregl-ctrl-bottom-right { bottom: calc(var(--pasek-h) + 4px); }
    .maplibregl-ctrl-bottom-left { bottom: calc(var(--pasek-h) + 4px); }
    /* W prawym górnym rogu MapLibre nie stoi już nic: zoom i położenie
       przeniósł do własnego rzędu `sprzet-mapy.js`, a podziałka siedzi na
       dole. Reguła odsuwająca tamtą kolumnę zniknęła razem z kolumną. */
  }

  @media (prefers-reduced-motion: reduce) {
    #panel { transition: none; }
    #scrim { animation: none; }
  }

  /* ---------- akcje planowania ---------- */
  .plan-actions { display: flex; gap: 8px; }
  .plan-actions .btn { flex: 1; margin-top: 10px; text-align: center; }

  /* ---------- zakładki: sekcje panelu ---------- */
  #recommended, #saved { display: none; flex: 1; overflow-y: auto; }
  #recommended.on, #saved.on { display: block; }

  /* ---------- stan pusty (rekomendowane / moje trasy) ---------- */
  .panel-empty { padding: 34px 22px; text-align: center; color: var(--muted-foreground); }
  .panel-empty .pe-emoji { font-size: 30px; line-height: 1; }
  .panel-empty h3 { margin: 12px 0 6px; font-size: 15px; color: var(--foreground); font-weight: 600; }
  .panel-empty p { margin: 0; font-size: 13px; line-height: 1.55; }

  /* ---------- karta trasy (lista „Moje trasy") ---------- */
  .route-card {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 12px 18px; border-bottom: 1px solid var(--border); cursor: pointer;
    transition: background .1s;
  }
  .route-card:hover { background: var(--muted); }
  .route-card .rc-main { flex: 1; min-width: 0; }
  /* typografia karty i pastylki danych: marka.css */
  /* Trudność stoi tuż pod tytułem, bo dotyczy całej trasy. Na dole zostają
     plakietki o pojedynczych cechach (łańcuchy, brak podglądu) — dwa różne
     rodzaje etykiet nie konkurują wtedy o to samo miejsce. */
  .route-card .rc-diff { margin: 5px 0 0; }
  .route-card .rc-flag { margin-top: 7px; }
  /* Kosz, nie krzyżyk: krzyżyk w rogu karty czyta się jako „zamknij", a tu
     kasuje trasę. Mniejszy od zwykłego przycisku ikonowego — akcja
     nieodwracalna nie powinna być największym celem na karcie. */
  .rc-del {
    flex: none; width: 32px; min-width: 32px; height: 32px; padding: 0;
    color: var(--muted-foreground);
  }
  .rc-del svg { width: 16px; height: 16px; }
  .rc-del:hover { background: var(--alert); border-color: var(--alert-border); color: var(--alert-foreground); }


  /* ---------- przycisk zapisu trasy ---------- */
  /* Sam w swoim pudełku na końcu karty — odstęp robi padding pudełka. */
  .route-save { margin-top: 0; }

  /* „Rozpocznij wycieczkę" stoi POD zapisem i tylko w apce (M13). Kolejność
     nie jest przypadkowa: zapis jest decyzją o planie, start — decyzją
     o dzisiejszym dniu, i to ta druga kończy lekturę karty. */
  .route-start { margin-top: 8px; }

  /* ---------- kontrolka filtrów na mapie ---------- */

  @media (max-width: 780px) {
  }

  /* ---------- notka o objeździe (skrót drogą lokalną) ---------- */
  .route-hint {
    margin: 11px 0 4px; padding: 10px 12px;
    background: var(--info); border: 1px solid var(--info-border); border-radius: 9px;
    font-size: 12.5px; line-height: 1.45; color: var(--foreground);
  }
  .route-hint b { font-variant-numeric: tabular-nums; }
  .route-hint .rh-note { margin-top: 7px; color: var(--muted-foreground); font-size: 11.5px; }

  /* ---------- katalog: nagłówek i pastylki filtrów ---------- */
  /* Tytuł niesie liczbę tras, bo to ona mówi, ile zostało do wyboru —
     wcześniej stała pod filtrami jako przypis i nikt jej nie czytał. */
  .kat-head {
    display: flex; align-items: baseline; gap: 10px;
    padding: 14px 18px 0;
  }
  .kat-head h3 {
    margin: 0; font-size: 17px; font-weight: 600; line-height: 1.25;
    font-family: var(--font-display); letter-spacing: var(--tracking-tight);
  }
  .pills {
    display: flex; flex-wrap: wrap; gap: 6px;
    padding: 10px 18px 12px; border-bottom: 1px solid var(--border);
  }
  /* wygląd pastylek: .pill w prymitywy.css */
  .pill-clear {
    margin-left: auto; flex: none; border: 0; background: none; font: inherit;
    font-size: 12px; color: var(--primary); cursor: pointer; text-decoration: underline;
  }

  /* ---------- pasek akcji katalogu ---------- */
  /* Dwa przyciski zamiast rzędu pastylek. Pastylki pokazywały osiem możliwości
     naraz i zajmowały trzy linijki, zanim padła pierwsza nazwa trasy — a i tak
     nie mieściły ani trudności, ani regionu. */
  /* Przycisk po lewej, przełącznik dosunięty do prawej: to dwie różne rzeczy
     i mają tak wyglądać. „Filtruj" otwiera panel i wraca — jest akcją.
     „Szukaj w tym obszarze" zostaje włączone i zmienia listę przy każdym ruchu
     mapy — jest stanem. Zrównanie ich w dwa identyczne przyciski kazało
     zgadywać, który z nich coś otworzy. */
  .kat-akcje {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 8px 18px 10px; border-bottom: 1px solid var(--border);
  }
  .kat-akcje .kat-obszar { margin-left: auto; }
  .kat-filtruj svg { width: 15px; height: 15px; }
  .kat-licznik {
    display: inline-grid; place-items: center; min-width: 17px; height: 17px;
    padding: 0 4px; border-radius: var(--radius-pill);
    background: var(--primary); color: var(--primary-foreground);
    font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
  }
  .kat-wybor {
    padding: 9px 18px; font-size: 12.5px; line-height: 1.45;
    color: var(--muted-foreground); border-bottom: 1px solid var(--border);
  }

  /* ---------- panel filtrów ---------- */
  .kat-filtry { padding: 14px 18px 18px; }
  .kat-sekcja { margin-top: 16px; }
  .kat-sekcja:first-of-type { margin-top: 4px; }
  .kat-sekcja-tytul {
    font-size: 11px; font-weight: 600; letter-spacing: var(--tracking-label);
    text-transform: uppercase; color: var(--muted-foreground); margin-bottom: 8px;
  }
  /* Pastylki wewnątrz panelu nie mają już własnego paska z obramowaniem —
     sekcja niesie podział, a druga linia pod spodem robiłaby z tego kratkę. */
  .kat-sekcja .pills { padding: 0; border-bottom: 0; }
  .kat-sekcja-pusta { font-size: 12.5px; color: var(--muted-foreground); }
  /* Suwak czasu. Podpis stoi NAD torem, nie obok: obok skakałby szerokością
     między „Do 3 h" a „Bez ograniczenia" i skracał tor pod palcem. */
  .kat-czas-wartosc {
    display: block; font-size: 15px; font-weight: 600;
    font-variant-numeric: tabular-nums; margin-bottom: 6px;
  }
  .kat-suwak {
    display: block; width: 100%; margin: 0; height: 28px;
    accent-color: var(--primary); cursor: pointer;
  }
  .kat-czas-skala {
    display: flex; justify-content: space-between;
    font-size: 11.5px; color: var(--muted-foreground);
    font-variant-numeric: tabular-nums;
  }
  .kat-stopka { display: flex; gap: 8px; margin-top: 22px; }
  .kat-stopka .btn { flex: 1; justify-content: center; }

  /* ---------- karta trasy z katalogu ---------- */
  .route-card .rc-teaser {
    font-size: 12.5px; color: var(--muted-foreground); margin-top: 3px; line-height: 1.45;
  }
  .route-card.no-map { opacity: .72; }
  .rc-flag {
    display: inline-block; margin: 7px 0 0 6px; padding: 1px 8px; border-radius: 999px;
    font-size: 11px; background: var(--warn); color: var(--warn-foreground); font-weight: 600;
  }
  .rc-flag.muted { background: var(--muted); color: var(--muted-foreground); font-weight: 500; }

  /* ---------- opis trasy z bazy wiedzy ---------- */
  .kb {
    margin: 12px 0 4px; padding: 12px 13px;
    background: var(--muted); border: 1px solid var(--border); border-radius: 10px;
  }
  .kb-head { font-size: 13.5px; font-weight: 600; line-height: 1.3; }
  .kb-ref {
    font-size: 12px; color: var(--muted-foreground); margin-top: 3px;
    font-variant-numeric: tabular-nums;
  }
  .kb-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
  .kb-tags span {
    padding: 1px 7px; border-radius: 999px; background: var(--background);
    border: 1px solid var(--border); font-size: 11px; color: var(--muted-foreground);
  }
  .kb-sec { margin-top: 9px; border-top: 1px solid var(--border); padding-top: 8px; }
  .kb-sec summary {
    cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--foreground);
    list-style: none;
  }
  .kb-sec summary::-webkit-details-marker { display: none; }
  .kb-sec summary::before { content: '▸ '; color: var(--muted-foreground); }
  .kb-sec[open] summary::before { content: '▾ '; }
  .kb-sec p { margin: 7px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--foreground); }
  .kb-sec.warn summary { color: var(--warn-foreground); }

  /* ---------- opis miejsca w dymku ---------- */
  .pl { margin: 8px 0 2px; padding-top: 8px; border-top: 1px solid var(--border); }
  .pl-kat {
    display: inline-block; margin-bottom: 5px; padding: 1px 8px; border-radius: 999px;
    background: var(--muted); font-size: 10.5px; color: var(--muted-foreground);
    text-transform: uppercase; letter-spacing: .04em; font-weight: 600;
  }
  .pl-opis { font-size: 12.5px; line-height: 1.5; color: var(--foreground); }
  .pl-more { margin-top: 7px; }
  .pl-more summary {
    cursor: pointer; font-size: 12px; font-weight: 600; color: var(--primary);
    list-style: none;
  }
  .pl-more summary::-webkit-details-marker { display: none; }
  .pl-more summary::before { content: '▸ '; }
  .pl-more[open] summary::before { content: '▾ '; }
  .pl-more ul { margin: 6px 0 0; padding-left: 16px; }
  .pl-more li { font-size: 12px; line-height: 1.45; color: var(--muted-foreground); margin-bottom: 4px; }
  /* Dymek z opisem bywa wyższy niż ekran telefonu — przewijamy zawartość. */
  .maplibregl-popup-content { max-height: 60vh; overflow-y: auto; }

  /* ---------- schronisko w dymku ---------- */
  /* Obiekt nieczynny albo nieistniejący musi być widoczny na pierwszy rzut oka:
     nikt nie może wyruszyć na nocleg do chaty rozebranej sto lat temu. */
  .pl-status {
    display: inline-block; margin: 0 0 5px 5px; padding: 1px 8px; border-radius: 999px;
    background: var(--alert); color: var(--alert-foreground); font-size: 10.5px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em;
  }
  .pl-fakty { margin-top: 6px; font-size: 11.5px; color: var(--muted-foreground); line-height: 1.45; }
  .pl-www { display: inline-block; margin-top: 6px; font-size: 12px; color: var(--primary); }
  .pl-more p { margin: 6px 0 0; font-size: 12px; line-height: 1.45; color: var(--muted-foreground); }

  /* ---------- zdjęcia ---------- */
  /* Podpis jest częścią figury, nie ozdobnikiem: większość zdjęć wymaga
     atrybucji, a przy CC BY-SA nasza przeskalowana kopia zostaje na tej samej
     licencji. Stąd figure+figcaption, a nie samo <img>. */
  .foto { margin: 0; }
  .foto img { display: block; width: 100%; height: auto; background: var(--muted); }
  .foto figcaption {
    margin-top: 4px; font-size: 10.5px; line-height: 1.4; color: var(--muted-foreground);
  }
  .foto figcaption a { color: var(--muted-foreground); }

  /* Dymek miejsca — zdjęcie na całą szerokość, nad kategorią. */
  .foto-pl img { border-radius: 8px; max-height: 150px; object-fit: cover; }
  .foto-pl { margin-bottom: 8px; }

  /* Karta katalogu — miniatura obok tekstu, stała proporcja, żeby lista
     nie skakała przy różnych wysokościach zdjęć. */
  .route-card .foto-karta {
    flex: none; width: 84px; height: 84px; border-radius: 9px; overflow: hidden;
  }
  .route-card .foto-karta img { width: 100%; height: 100%; object-fit: cover; }
  /* Zastępnik: ta sama bryła co miniatura, ale wyraźnie pusty — ma trzymać
     układ karty, nie udawać fotografii. */
  .foto-zastepnik {
    display: grid; place-items: center;
    background: var(--muted); color: var(--muted-foreground);
  }
  .foto-zastepnik svg { width: 30px; height: 30px; opacity: .5; }

  /* Akordeon trasy — siatka miniatur z pełnym podpisem pod każdą. */
  .foto-siatka {
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 9px; margin-top: 8px;
  }
  .foto-trasa img { border-radius: 7px; aspect-ratio: 4 / 3; object-fit: cover; }

  /* ---------- zwijanie paneli: jednolity język strzałek ---------- */
  /* Panel boczny chowa się w lewo, profil w dół — strzałka pokazuje kierunek,
     w którym rzecz zniknie, i obraca się po zwinięciu. */

  #p-toggle { top: 0; transform: translate(-50%, -100%); }
  #p-toggle svg { transform: rotate(-90deg); }
  #profile.collapsed #p-toggle svg { transform: rotate(90deg); }

  /* Zwinięty profil zostawia wiersz z liczbami — czas i dystans są tym, po co
     się na niego patrzy; znika sam wykres, który zabiera najwięcej miejsca. */
  #profile.collapsed { padding-bottom: 12px; }
  #profile.collapsed #p-svg { display: none; }
  #profile.collapsed .ph { margin-bottom: 0; }
  #profile .ph { padding-right: 30px; }

  /* ---------- powiększanie zdjęć (galeria) ---------- */
  .foto-klik { cursor: zoom-in; }
  #lightbox {
    position: fixed; inset: 0; z-index: 20;
    background: var(--scrim);
    display: grid; place-items: center; padding: 40px 16px;
  }
  #lightbox[hidden] { display: none; }
  .lb-fig { margin: 0; max-width: min(1100px, 100%); max-height: 100%; }
  .lb-fig img {
    display: block; max-width: 100%; max-height: calc(100vh - 130px);
    margin: 0 auto; border-radius: 6px; background: var(--scrim-surface);
  }
  .lb-fig figcaption {
    margin-top: 10px; text-align: center; color: var(--scrim-foreground-dim);
    font-size: 12.5px; line-height: 1.5;
  }
  .lb-fig figcaption a { color: var(--scrim-foreground-dim); }
  .lb-fig figcaption b { color: var(--scrim-foreground); }
  #lightbox button {
    position: absolute; border: 0; background: oklch(1 0 0 / 0.12); color: var(--scrim-foreground);
    cursor: pointer; border-radius: 9px; line-height: 1;
  }
  #lightbox button:hover { background: oklch(1 0 0 / 0.22); }
  .lb-close { top: 14px; right: 16px; width: 36px; height: 36px; font-size: 21px; }
  .lb-nav { top: 50%; transform: translateY(-50%); width: 42px; height: 58px; font-size: 26px; }
  .lb-prev { left: 12px; }
  .lb-next { right: 12px; }

  @media (max-width: 780px) {
    #lightbox { padding: 30px 8px; }
    .lb-nav { width: 36px; height: 48px; font-size: 22px; }
  }

  /* ---------- rozwinięta karta trasy w katalogu ---------- */
  /* Kliknięcie w kartę nie przenosi od razu do edycji: trasa rysuje się na
     mapie, a karta rozwija się w miejscu. Do planowania przechodzi się dopiero
     przyciskiem — dzięki temu listę da się przeglądać bez jej opuszczania. */
  .route-card { flex-wrap: wrap; }
  .route-card.otwarta {
    background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--primary);
    cursor: default;
  }
  .route-card .rc-main { cursor: pointer; }
  .rc-rozwiniete { flex-basis: 100%; margin-top: 4px; }
  /* Wewnątrz karty opis nie potrzebuje własnego tła ani ramki — ma je karta. */
  .rc-rozwiniete .kb {
    background: transparent; border: 0; padding: 0; margin-top: 6px;
  }
  .rc-akcje { display: flex; gap: 8px; margin-top: 12px; }
  .rc-akcje button { flex: 1; }

  /* ---------- kreator wycieczki ---------- */
  /* Gdy ankieta jest otwarta, panel należy w całości do niej — ręczne
     planowanie wróci, gdy użytkownik zamknie kreator albo wczyta trasę.

     `!important` jest tu konieczne, nie wygodne: `renderPlan()` pokazuje
     `#plan-result` stylem INLINE (`display:block`), a styl inline bije każdą
     regułę klasową. Bez tego wynik poprzednio wyznaczonej trasy zostawał
     widoczny pod ankietą — razem z przyciskiem „Zapisz trasę", który dotyczył
     zupełnie czego innego.

     To samo dotyczy rozdroża, odkąd strzałka wstecz zostawia trasę przy życiu:
     pod kafelkami wyboru wisiał ucięty pasek z czasem i listą znakowania
     trasy, którą się właśnie odłożyło. Reguła bez `!important` nie miała czym
     go schować, bo nikt już nie wołał `renderPlan()` z pustą trasą. */
  #plan.tryb-wybor > .plan-box,
  #plan.tryb-wybor #wizard,
  #plan.tryb-kreator > .plan-box,
  #plan.tryb-kreator #plan-wybor,
  #plan.tryb-reczne #plan-wybor,
  #plan.tryb-reczne #wizard { display: none !important; }

  /* Nagłówek widoku: strzałka powrotu przed tytułem, tak jak w systemowych
     ekranach „w głąb". Odsyłacz „zamknij" nie mówił, dokąd wraca. */
  .widok-naglowek {
    display: flex; align-items: center; gap: 6px;
    margin: 0 0 10px -6px;
  }
  .widok-naglowek h3 {
    /* Bez własnej interlinii tytuł niósł światło z body (1,6) i jego środek
       optyczny wypadał wyżej niż środek przycisku — strzałka wyglądała na
       osadzoną za nisko, choć oba elementy były wyśrodkowane. */
    /* Tytuł funkcji stoi wyraźnie nad tytułami stanów (pytanie kreatora,
       nagłówek wyniku) — wcześniej wszystkie miały 15 px i hierarchia
       istniała tylko w kodzie. */
    margin: 0; font-size: 20px; font-weight: 600; line-height: 1;
    font-family: var(--font-display); letter-spacing: var(--tracking-tight);
  }

  /* Wyczyść i udostępnij stoją w jednym wierszu z metodologią: to trzy rzeczy
     o tej samej wadze — pomocnicze, potrzebne rzadko, na końcu panelu. */
  /* Działania trasy stoją pierwsze, bo to one są tu treścią; metodologia
     dosuwa się do prawej krawędzi jako przypis. Otwarta przechodzi do
     własnego wiersza — w kolumnie obok przycisków zostaje jej 110 px
     i tekst spada po dwa słowa na wiersz. */
  .stopka {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--space-2) var(--space-3);
  }
  .stopka .metodologia { margin-left: auto; }
  .stopka .metodologia[open] { flex: 1 0 100%; margin-left: 0; }
  .stopka-akcje { flex: none; display: flex; gap: var(--space-2); }
  .stopka-akcje[hidden] { display: none; }
  .stopka-akcje .btn:disabled { opacity: .4; }

  /* Kod QR z trasą — rozwijany bloczek pod paskiem działań. */
  .kod-box {
    margin-top: var(--space-3); padding-top: var(--space-3);
    border-top: 1px solid var(--border);
    display: flex; flex-direction: column; align-items: center;
    gap: var(--space-2);
  }
  .kod-box[hidden] { display: none; }
  /* Biała plansza NIEZALEŻNIE od motywu — czytniki zakładają ciemne moduły na
     jasnym tle, a odwrócony kod część z nich odrzuca w ciszy. */
  .kod-plansza {
    width: 190px; max-width: 100%; padding: 10px;
    background: #fff; border-radius: 12px;
    box-shadow: var(--shadow-soft);
  }
  .kod-plansza svg { display: block; width: 100%; height: auto; }
  .kod-brak { margin: 0; font-size: 12.5px; color: #444; text-align: center; }
  .kod-jak {
    margin: 0; font-size: 12.5px; line-height: 1.5; text-align: center;
    color: var(--muted-foreground);
  }
  .kod-jak b { color: var(--foreground); font-weight: 600; }
  .kod-kopiuj { align-self: center; }
  /* Adres pokazuje się tylko wtedy, gdy zawiodło kopiowanie do schowka. Łamie
     się wtedy na dowolnym znaku: to ciąg bez spacji, a panel ma 400 px —
     bez tego rozpychałby kartę trasy w poziomie. */
  .kod-adres {
    width: 100%; font-size: 11px; line-height: 1.45;
    font-family: var(--font-mono);
    color: var(--muted-foreground); word-break: break-all; text-align: center;
  }
  .kod-adres[hidden] { display: none; }

  /* Na kartce kod jest zbędny: adres stoi w stopce wydruku, a i tak nikt nie
     skanuje własnego wydruku. */
  @media print { .kod-box { display: none; } }

  /* Rozdroże: dwa wejścia w planowanie, każde z własnym powodem. */
  .wybor-hero { padding: 16px 18px 18px; border-bottom: 1px solid var(--border); }
  .wybor-hero.polecany {
    background: linear-gradient(180deg, var(--accent-soft), var(--background));
  }
  .wybor-hero h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
  .wybor-hero p {
    margin: 0 0 12px; font-size: 12.5px; color: var(--muted-foreground); line-height: 1.5;
  }

  /* Nota o pochodzeniu trasy — dziś pisze do niej wyłącznie import GPX-a.
     Wcięcia jak w bloczku panelu, żeby stała w jednej linii z resztą treści,
     ale bez kreski pod spodem: to przypis do trasy, nie osobna sekcja. */
  .plan-nota {
    margin: 0; padding: 12px 18px;
    font-size: 12px; line-height: 1.5;
    color: var(--muted-foreground);
    background: var(--muted);
    border-bottom: 1px solid var(--border);
  }
  .plan-nota[hidden] { display: none; }
  .plan-nota.zle { color: var(--destructive); }
  .plan-nota.ok { color: var(--foreground); }


  /* ---------- ankieta ---------- */
  .kr-box { padding: 14px 18px 18px; border-bottom: 1px solid var(--border); }
  /* Kropki kroków dołączają do nagłówka widoku po prawej stronie tytułu. */
  .widok-naglowek .kr-kroki { margin-left: auto; }
  .kr-kroki { display: flex; gap: 4px; }
  .kr-kropka { width: 6px; height: 6px; border-radius: 50%; background: var(--border); }
  .kr-kropka.on { background: var(--primary); }
  .kr-kropka.byla { background: var(--stone); }
  .kr-pytanie { margin: 12px 0 10px; font-size: 17px; font-weight: 600; line-height: 1.3; }
  .kr-podpis { margin: 0 0 12px; font-size: 12.5px; color: var(--muted-foreground); line-height: 1.5; }

  .kr-opcje { display: flex; flex-direction: column; gap: 7px; }
  .kr-opcja {
    display: block; width: 100%; text-align: left; padding: 10px 12px;
    font: inherit; font-size: 13.5px; color: var(--foreground);
    border: 1px solid var(--border); border-radius: 9px; background: var(--background);
    cursor: pointer; transition: border-color .12s, background .12s;
  }
  .kr-opcja:hover { background: var(--muted); border-color: var(--border-strong); }
  .kr-opcja[aria-pressed="true"] { border-color: var(--primary); background: var(--accent-soft); }
  .kr-opcja b { display: block; font-weight: 600; }
  .kr-opcja span { display: block; margin-top: 2px; font-size: 12px; color: var(--muted-foreground); line-height: 1.45; }

  .kr-nawigacja { display: flex; gap: 8px; margin-top: 13px; }
  .kr-nawigacja .kr-dalej { flex: 1; }

  .kr-szukaj { margin-top: 9px; }
  .kr-lista { max-height: 210px; overflow-y: auto; margin-top: 7px; }
  .kr-lista button {
    display: block; width: 100%; text-align: left; padding: 7px 10px; font: inherit;
    font-size: 13px; color: var(--foreground);
    border: 0; border-radius: 7px; background: none; cursor: pointer;
  }
  .kr-lista button:hover { background: var(--muted); }
  .kr-lista button[aria-pressed="true"] { background: var(--accent-soft); font-weight: 600; }
  .kr-lista .kr-cele { color: var(--muted-foreground); font-size: 11.5px; float: right; }

  /* ---------- wyniki ---------- */
  .kr-wynik-head { padding: 13px 18px 11px; border-bottom: 1px solid var(--border); }
  /* Bezpośrednie dziecko: nagłówek widoku też jest <h3> i siedzi wewnątrz
     .kr-wynik-head, więc bez `>` ta reguła zmniejszała tytuł funkcji. */
  .kr-wynik-head > h3 { margin: 10px 0 3px; font-size: 17px; font-weight: 600; }
  .kr-wynik-head p { margin: 0; font-size: 12.5px; color: var(--muted-foreground); line-height: 1.5; }
  .kr-jak-liczymy { margin-top: 6px !important; font-style: italic; }
  .kr-uwaga {
    margin-top: 8px; padding: 8px 10px; border-radius: 8px;
    background: var(--notice); border: 1px solid var(--notice-border); color: var(--notice-foreground);
    font-size: 12px; line-height: 1.45;
  }
  .kr-znow { margin-top: 10px; }
  /* Uwaga o wjeździe: żółta, gdy to tylko opłata; czerwona, gdy realnie nie
     wjedziesz. Rozróżnienie jest po to, żeby czerwień coś znaczyła — płatny
     parking ma 63 ze 105 punktów startowych w Tatrach. */
  .kr-uwaga.mocna { background: var(--alert); border-color: var(--alert-border); color: var(--alert-foreground); }

  /* Karty wyników używają `.route-card` z katalogu — ten sam byt wizualny dla
     trasy proponowanej i trasy rekomendowanej. Tutaj zostają tylko różnice. */
  .kr-okrezna {
    display: inline-block; margin-right: 3px; color: var(--primary);
    font-weight: 700; cursor: help;
  }
  .kr-plakietki { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }
  .kr-plakietka {
    padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 600;
    background: var(--accent); color: var(--accent-foreground);
  }
  .kr-plakietka.typ { background: var(--muted); color: var(--muted-foreground); font-weight: 500; }
  .kr-plakietka.zakaz { background: var(--alert); color: var(--alert-foreground); }
  /* Oczekiwanie z ankiety, którego ta karta NIE spełnia — dopełnia wynik do
     trzech propozycji i ma to mówić wprost (QA 2026-10-12). Obrys zamiast
     wypełnienia: informacja, nie zaleta. */
  .kr-plakietka.brak {
    background: transparent; color: var(--muted-foreground); font-weight: 500;
    box-shadow: inset 0 0 0 1px var(--border);
  }
  .kr-zakaz { font-size: 11px; }
  .kr-powody { margin: 2px 0 0; padding: 0; list-style: none; }
  .kr-powody li {
    position: relative; padding-left: 14px; font-size: 12.5px; color: var(--muted-foreground);
    line-height: 1.5;
  }
  .kr-powody li::before {
    content: '·'; position: absolute; left: 4px; color: var(--primary); font-weight: 700;
  }
  .kr-ladowanie { padding: 26px 18px; display: grid; justify-items: center; }

  /* ---------- mapka obszarów w kreatorze ---------- */
  .kr-obszary { margin-top: 12px; }
  .kr-mapa {
    display: block; width: 100%; height: auto;
    background: var(--muted); border: 1px solid var(--border); border-radius: 9px;
  }
  .kr-obszar {
    fill: var(--region-fill); stroke: var(--background); stroke-width: 1.5;
    cursor: pointer; transition: fill .12s;
  }
  .kr-obszar:hover { fill: var(--region-fill-hover); }
  .kr-obszar.on { fill: var(--primary); }
  .kr-obszar-opis {
    margin-top: 7px; font-size: 12px; color: var(--muted-foreground); line-height: 1.45;
    min-height: 2.9em;
  }
  .kr-caly {
    width: 100%; padding: 6px 10px; font: inherit; font-size: 12.5px;
    border: 1px solid var(--border); border-radius: 8px; background: var(--background);
    color: var(--muted-foreground); cursor: pointer;
  }
  .kr-caly.on { border-color: var(--primary); color: var(--primary); font-weight: 600; }
  .kr-etykieta {
    fill: var(--muted-foreground); font-size: 9px; font-weight: 600;
    text-anchor: middle; dominant-baseline: middle;
    pointer-events: none; user-select: none;
  }
  .kr-etykieta.on { fill: var(--primary-foreground); }

  /* ---------- typografia marki ---------- */
  /* Nagłówki prowadzą narrację, treść niesie Lexend, dane opisuje
     JetBrains Mono. Monospace nigdy dla zdań.

     Nagłówki chodzą na wadze 600: Space Grotesk ma pełny zakres 300–700,
     więc nie ma powodu trzymać ich na 400 — to było ograniczenie Young
     Serifa, który miał tylko jedną wagę. W małych rozmiarach cięższy
     rysunek jest tym, co realnie podnosi czytelność. */
  .head h1,
  #detail h2,
  .panel-empty h3,
  .kr-hero h3,
  .kr-wynik-head > h3,
  .kr-pytanie {
    font-family: var(--font-display);
    font-weight: 600;
    letter-spacing: var(--tracking-tight);
  }
  .head h1 { font-size: 19px; }

  /* Dane: dystans, przewyższenie, czas — zawsze w jednym kroju,
     żeby liczby w kolumnach stały równo. */
  .result-head .big,
  .wp .leg,
  #profile .ph b,
  #profile .readout,
  .item .meta,
  .tkey .km,
  .tkey .czas,
  #detail dd {
    font-family: var(--font-mono);
    font-feature-settings: 'tnum';
  }

  /* ---------- ustawienia ----------
     Panel przykrywa panel boczny: na desktopie to ten sam prostokąt, ta sama
     szerokość, ta sama krawędź. Ustawienia są widokiem aplikacji, nie okienkiem
     nad nią, więc nie przyciemniamy mapy — praca pod spodem zostaje widoczna.
     Wersja telefonu (arkusz przy dolnej krawędzi) jest w bloku mobilnym. */
  #ustawienia { position: absolute; inset: 0; z-index: 7; }
  #ustawienia[hidden] { display: none; }
  /* Zasłona na desktopie łapie kliknięcia poza panelem — zamyka, ale niczego
     nie zaciemnia. */
  .ust-zaslona { position: absolute; inset: 0; }
  .ust-panel {
    position: absolute; top: 0; left: 0; bottom: 0; width: var(--panel-w);
    display: flex; flex-direction: column;
    background: var(--background); border-right: 1px solid var(--border);
    box-shadow: var(--shadow-ridge);
    padding: 14px 18px 0;
    animation: ust-wejscie var(--dur-base) var(--ease-out);
  }
  @keyframes ust-wejscie {
    from { opacity: 0; transform: translateX(-12px); }
    to { opacity: 1; transform: none; }
  }
  .ust-panel .widok-naglowek { margin-left: 0; }
  .ust-panel .widok-naglowek h3 { margin-right: auto; }

  /* Nie ma paska akcji na dole — wybór zapisuje się w locie — więc oddech
     pod ostatnim bloczkiem musi dać sama treść. */
  .ust-tresc { flex: 1; overflow-y: auto; padding-bottom: 18px; }
  .ust-grupa { margin-bottom: 18px; }
  /* Nazwa grupy jest etykietą zestawu, nie tytułem sekcji — stąd wielkość
     i kolor podpisu pola, a nie nagłówka. */
  .ust-grupa h4 {
    margin: 0 0 7px; font-size: 12px; font-weight: 500;
    color: var(--muted-foreground); letter-spacing: 0.01em;
  }

  /* Bloczki, nie radia: wybór wygląda tu tak samo jak w kreatorze, bo jest tym
     samym gestem. Ikona z lewej niesie różnicę szybciej niż nazwa. */
  .ust-opcje { display: flex; flex-direction: column; gap: 7px; }
  .ust-opcja {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 10px 12px; text-align: left; font: inherit; font-size: 13.5px;
    color: var(--foreground); background: var(--background);
    border: 1px solid var(--border); border-radius: 9px;
    cursor: pointer; transition: border-color .12s, background .12s;
  }
  .ust-opcja:hover { background: var(--muted); border-color: var(--border-strong); }
  .ust-opcja[aria-pressed="true"] { border-color: var(--primary); background: var(--accent-soft); }
  .ust-ikona {
    width: 20px; height: 20px; flex: none;
    color: var(--muted-foreground);
  }
  .ust-opcja[aria-pressed="true"] .ust-ikona { color: var(--primary); }
  .ust-nazwa { min-width: 0; font-weight: 600; }

  /* Na telefonie ustawienia są arkuszem przy dolnej krawędzi. Nie doklejamy
     ich do #panel z jego detentami: panel jest wtedy zajęty planowaniem,
     a ustawienia mają jedną wysokość — swojej treści. Stąd własny arkusz,
     własna zasłona i własny uchwyt. */
  @media (max-width: 780px) {
    #ustawienia { position: fixed; z-index: 8; }
    /* Tu zasłona ma co zasłaniać: arkusz zajmuje część ekranu, a nie cały
       panel, więc mapa pod spodem musi się cofnąć. */
    .ust-zaslona { background: oklch(0.19 0.024 160 / 0.32); }
    .ust-panel {
      top: auto; left: 0; right: 0; width: auto;
      max-height: 88dvh;
      border-right: 0; border-radius: 18px 18px 0 0;
      padding: 16px 16px 0;
      animation: ust-wjazd var(--dur-slow) var(--ease-out);
    }
    @keyframes ust-wjazd {
      from { transform: translateY(100%); }
      to { transform: none; }
    }
    /* Bez kreski u góry: tego arkusza nie da się przeciągnąć, a uchwyt, który
       nie reaguje na palec, testy użyteczności wzięły za zepsuty (2026-09-29).
       Zamyka go krzyżyk w nagłówku, zasłona albo Esc. */
    .ust-tresc { padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px)); }
    /* Arkusz ustawień przykrywa pasek zakładek — dopóki jest otwarty, nie ma
       dokąd nawigować. */
    .ustawienia-otwarte .tabs { display: none; }
  }

  @media (prefers-reduced-motion: reduce) {
    .ust-panel { animation: none; }
  }

  /* ---------- wydruk ----------
     Karta trasy na papierze. PDF robi przeglądarka („Zapisz jako PDF"
     w oknie drukowania), więc nie ma tu ani biblioteki, ani drugiego układu —
     jest ta sama karta, z której korzysta ekran, pozbawiona sprzętu.
     Przygotowanie strony (zdjęcie trybu ciemnego, wykres w toku strony,
     przeliczenie go na szerokość arkusza) robi js/wydruk.js. */
  .tylko-druk { display: none; }

  @media print {
    @page { size: A4 portrait; margin: 14mm; }

    /* Kolory niosą tu znaczenie — barwa szlaku, plakietka trudności, czerwień
       ostrzeżenia — więc nie pozwalamy przeglądarce ich zdjąć. */
    html, body {
      height: auto; overflow: visible; background: #fff;
      -webkit-print-color-adjust: exact; print-color-adjust: exact;
    }

    /* Sprzęt aplikacji nie idzie na papier: mapa, nawigacja, pola, decyzje. */
    #map, .head, .tabs, #scrim, #toggle, #detail, #wezel, #loading,
    .arkusz-uchwyt, #plan-wybor, #wizard, #recommended, #saved,
    #wp-nad, #wp-pod, .pace-row, .trasa-akcje, .widok-naglowek, #zapisz-slot, .stopka,
    #profile, #p-toggle, .maplibregl-ctrl, .wp .rm, .foto-siatka, #ustawienia {
      display: none !important;
    }
    /* Metodologia na papier nie idzie: stopka mówi to samo jednym zdaniem,
       a rozwinięty przypis kosztował 35 mm — czyli mapę albo drugą stronę. */

    /* Uchwyt arkusza: pseudoelement panelu, więc nie łapie go lista wyżej.
       Papier ma szerokość 688 px (A4 minus marginesy), czyli MNIEJ niż próg
       780 px — w druku obowiązują reguły telefonu i wszystko, co dokładają
       do arkusza, trzeba tu zdjąć. */
    #panel::before { display: none; }

    /* Pudełko po pasku działań zostaje puste, a puste nie znaczy zerowej
       wysokości — ma własny odstęp dolny. Chowamy je razem z zawartością. */
    .plan-box:has(> .stopka) { display: none !important; }

    /* Arkusz przestaje być arkuszem, panel panelem: treść wraca w tok strony,
       bo tylko wtedy przeglądarka umie ją złamać na strony. */
    #panel {
      position: static !important; inset: auto !important;
      width: auto !important; height: auto !important; max-height: none !important;
      transform: none !important;
      display: block; border: 0; box-shadow: none; background: none;
    }
    #plan {
      display: block !important;
      height: auto; max-height: none; overflow: visible;
    }
    .plan-box {
      padding: 0 0 3mm; border-bottom: 0;
      /* Etap rozerwany między stronami czyta się dwa razy gorzej niż
         przeniesiony w całości. */
      break-inside: avoid;
    }

    .tylko-druk { display: block; }

    /* Nagłówek dokumentu: tytuł z lewej, znak z prawej. Obok siebie, nie jeden
       pod drugim — pion jest tu towarem deficytowym, a znak i tak zajmuje
       tyle wysokości, co tytuł z datą. */
    #wydruk-nag {
      display: flex; align-items: flex-start; gap: 8mm;
      padding-bottom: 3mm; margin-bottom: 4mm;
      border-bottom: 1.5pt solid var(--foreground);
    }
    .wydruk-tytulblok { flex: 1; min-width: 0; }
    .wydruk-znak { flex: none; width: 36mm; height: auto; margin-top: 0.5mm; }
    #wydruk-nag h2 {
      font-family: var(--font-display); font-weight: 600;
      font-size: 17pt; line-height: 1.1; margin: 0 0 1mm;
    }
    .wydruk-data { margin: 0; font-size: 9pt; color: var(--muted-foreground); }

    /* Dwa boksy obok siebie: przygotowanie zajmuje więcej miejsca, bo to
       cztery zdania; numer ratunkowy ma być krótki i widoczny. Pod nimi
       drobny druk na całą szerokość. */
    #wydruk-stopka {
      margin-top: 3mm; padding-top: 3.5mm;
      border-top: 1pt solid var(--foreground);
      /* Trzy kolumny, nie dwie: cztery punkty przygotowania w jednej wąskiej
         kolumnie łamały się po trzy wiersze każdy i zjadały centymetr, którego
         brakowało mapie. Po dwa w kolumnie schodzą do dwóch wierszy. */
      display: grid; grid-template-columns: 1fr 1fr 0.95fr;
      gap: 2.5mm 3mm; break-inside: avoid;
    }
    /* Lista rozlewa się na dwie pierwsze kolumny siatki nadrzędnej, więc boks
       przygotowania jest jeden, a nie dwa udające jeden. */
    .wydruk-boks-przed { grid-column: span 2; }
    .wydruk-boks-przed ul {
      columns: 2; column-gap: 4mm;
    }
    .wydruk-boks-przed li { break-inside: avoid; }
    .wydruk-boks {
      border: 0.5pt solid var(--border-strong); border-radius: 2mm;
      padding: 2.5mm 3mm;
    }
    .wydruk-boks h3 {
      margin: 0 0 1.5mm; font-family: var(--font-display); font-weight: 600;
      font-size: 9.5pt; letter-spacing: var(--tracking-tight);
    }
    .wydruk-boks ul { margin: 0; padding-left: 3.6mm; }
    .wydruk-boks li { margin-bottom: 1mm; }
    .wydruk-boks li:last-child { margin-bottom: 0; }
    .wydruk-boks, .wydruk-boks p { font-size: 8pt; line-height: 1.4; }
    .wydruk-boks p { margin: 0 0 1mm; }
    .wydruk-boks p:last-child { margin-bottom: 0; }

    /* Numer alarmowy jest jedyną rzeczą na kartce, którą czyta się w panice —
       stąd ramka w barwie ostrzeżenia i cyfry większe niż tytuł sekcji. */
    .wydruk-ratunek { border: 1pt solid var(--alert-border); background: var(--alert); }
    .wydruk-ratunek h3 { color: var(--alert-foreground); }
    /* Selektor przez oba człony, bo `.wydruk-boks p` wyżej jest bardziej
       szczegółowe niż sama klasa i ścinało numer z powrotem do 8 pt. */
    .wydruk-ratunek p.wydruk-numer {
      margin: 0 0 0.5mm; font-family: var(--font-mono); font-size: 17pt;
      font-weight: 600; line-height: 1.05; letter-spacing: -0.02em;
      color: var(--alert-foreground);
    }
    .wydruk-ratunek p.wydruk-numer-opis {
      margin: 0 0 1.5mm; color: var(--muted-foreground);
    }

    .wydruk-drobne, .wydruk-link {
      grid-column: 1 / -1;
      margin: 0; font-size: 7pt; line-height: 1.4; color: var(--muted-foreground);
    }
    .wydruk-drobne { margin-bottom: -1mm; }
    .wydruk-link { font-family: var(--font-mono); word-break: break-all; }

    /* Mapa: zrzut z ukrytej instancji MapLibre, wstawiony przez wydruk.js.
       Proporcja obrazka wychodzi z arytmetyki jednej strony A4 — szczegóły
       przy stałej ZRZUT w module. */
    #wydruk-mapa { margin: 0 0 5mm; break-inside: avoid; }
    #wydruk-mapa[hidden] { display: none !important; }
    #wydruk-mapa img {
      display: block; width: 100%; height: auto;
      border: 0.5pt solid var(--border-strong); border-radius: 2mm;
    }

    /* Wykres: pełna szerokość zadruku, siatkę w pikselach ustawia wydruk.js.
       Ramka, bo bez tła strony wykres traci swoje pudełko. */
    /* Na desktopie pudełko wykresu jest wygaszone na stałe (wykres pływa tam
       nad mapą), więc sam zdjęty atrybut `hidden` nie wystarczy. */
    #profil-slot, #profil-slot[hidden] { display: block !important; }
    #p-svg {
      width: 100%; height: auto;
      border: 0.5pt solid var(--border-strong); border-radius: 2mm;
      padding: 2mm 1mm 0;
    }

    /* Cienie i zaokrąglenia są własnością ekranu — na papierze zostaje
       rysunek kreską. */
    * { box-shadow: none !important; }

  }

/* Znak jako powrót do ekranu startowego powłoki (apka i planer otwarty
   z apki). Bez zmiany rysunku — tylko kursor i drobne ustąpienie pod palcem,
   żeby było widać, że to działa, i żeby nie wyglądało na przycisk tam, gdzie
   nim nie jest. */
.logo-klikalny { cursor: pointer; }
.logo-klikalny:active { transform: scale(0.98); }
.logo-klikalny:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 4px;
  border-radius: var(--radius-md);
}

/* Katalog zawężony do jednego punktu (wejście z karty węzła na mapie).
   Pastylka mówi, czym ta lista jest, i daje z niej wyjść — bez niej krótka
   lista wygląda jak ubogi katalog, a nie jak odpowiedź na zadane pytanie. */
.kat-wezel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  /* Wcięcie 18 px jak każdy pas panelu (`.kat-akcje`, `.kat-wybor`) — bez niego
     pastylka szła od krawędzi do krawędzi panelu i arkusza. */
  margin: 0 18px 10px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  font-size: 13px;
  color: var(--foreground);
}

.kat-wezel b { font-weight: 600; }

.kat-wezel-x {
  flex-shrink: 0;
  padding: 2px 4px;
  border: 0;
  background: none;
  color: var(--muted-foreground);
  font: inherit;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
}

.kat-wezel-x:hover { color: var(--foreground); }

/* Wyjście do generatora spod listy: katalog to trasy opisane ręcznie, więc
   z większości punktów nie ma w nim nic — a ułożyć da się prawie zawsze. */
.kat-ulozy { width: calc(100% - 36px); margin: 12px 18px 16px; }

/* Trasy PRZEZ punkt: skrócone warianty opisanych tras. Wizualnie ciszej niż
   karty katalogu — bo to nie są osobne trasy, tylko ich kawałki, i nie mają
   własnego opisu ani zdjęcia. */
.kat-przez {
  margin-top: 18px;
  padding: 14px 18px 0;
  border-top: 1px solid var(--border);
}

.kat-przez-glowa {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
}

.kat-przez-opis {
  margin: 4px 0 10px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted-foreground);
}

.panel-empty-mini {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--muted-foreground);
}

.kat-fragment {
  display: block;
  width: 100%;
  margin-bottom: 8px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-2xl);
  background: var(--card);
  color: var(--card-foreground);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.kat-fragment:hover { border-color: var(--border-strong); }

.kf-nazwa {
  display: block;
  font-weight: 600;
  font-size: 14px;
  line-height: 1.3;
}

.kf-zrodlo {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--muted-foreground);
}


/* ---------- Sprzęt mapy: jeden poziomy rząd ---------- */
/* Prawa górna krawędź mapy. Wyrównanie do prawej jest tu nośnikiem
   zachowania, nie estetyki: kompas i obrót dochodzą tylko w 3D i rosną
   W LEWO, więc zoom, położenie i ustawienia zostają dokładnie tam, gdzie
   palec nauczył się ich szukać. */
/* Rozmiar przycisków rzędu — wysokość sygnetu po lewej, nie cel dotykowy
   z brand booka (2026-09-12, decyzja użytkownika). Przy 44 px cztery
   pastylki zabierały górny pas mapy i to one, a nie teren, rzucały się
   w oczy. Mapa w terenie ma być czytelna; brand book przegrywa tu świadomie. */
:root { --sprzet-h: 34px; }

#sprzet-mapy {
  position: absolute;
  top: .75rem;
  right: .75rem;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: .5rem;
}

/* Skupisko: pastylka mieszcząca jeden lub dwa przyciski. Ten sam materiał,
   co arkusz i panel — półprzezroczyste tło z rozmyciem, żeby mapa pod spodem
   była widoczna, a przyciski nadal czytelne nad zdjęciem satelitarnym. */
.sprzet-grupa,
#slot-lokalizacja .maplibregl-ctrl-group {
  display: flex;
  align-items: center;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--card) 82%, transparent);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-raised);
}

/* Nadpisanie stylu MapLibre, który daje własne białe tło i promień 4 px. */
#slot-lokalizacja .maplibregl-ctrl-group { margin: 0; }

.sprzet-btn {
  display: grid;
  place-items: center;
  width: var(--sprzet-h);
  height: var(--sprzet-h);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--card-foreground);
  cursor: pointer;
}

.sprzet-btn:hover { background: color-mix(in oklab, var(--accent-soft) 70%, transparent); }
.sprzet-btn:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }

/* Przycisk, który COŚ WŁĄCZA, musi wyglądać na włączony — inaczej jedyną
   informacją o stanie nakładki jest pasek na dole ekranu, a ten bywa zasłonięty
   arkuszem. Wypełnienie barwą główną, tak jak przy wybranej zakładce. */
.sprzet-btn[aria-pressed="true"] {
  background: var(--primary);
  color: var(--primary-foreground);
}

.sprzet-btn[aria-pressed="true"]:hover {
  background: color-mix(in oklab, var(--primary) 88%, var(--foreground));
}

.sprzet-btn svg {
  width: 18px;
  height: 18px;
  pointer-events: none;
}

/* Tarcza kompasu zostaje okrągła i to jest informacja, nie niekonsekwencja:
   jako jedyny element tego rzędu się OBRACA, a koło mówi o tym bez podpisu. */
/* Bez sztywnego rozmiaru: pastylka ma rosnąć od przycisku tak samo jak
   pozostałe grupy, inaczej wychodzi o 2 px niższa (grubość obramowania)
   i tarcza odstaje od rzędu. */
.sprzet-kompas { border-radius: 50%; }

.kompas-igla {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  /* Krótkie przejście, bo obrót bywa ciągły przy przeciąganiu myszą —
     dłuższe sprawia, że wskazówka wlecze się za mapą. */
  transition: transform .1s linear;
}

.kompas-igla svg {
  width: calc(var(--sprzet-h) - 10px);
  height: calc(var(--sprzet-h) - 10px);
}

.sprzet-kompas .sprzet-btn { border-radius: 50%; }

/* Przycisk położenia przychodzi z MapLibre razem z regułą `.maplibregl-ctrl
   button` o rozmiarze 29 px. Ta reguła jest BARDZIEJ SZCZEGÓŁOWA niż nasza
   klasa, więc sama kolejność arkuszy nie wystarcza — stąd identyfikator
   z przodu. Bez tego położenie byłoby o 5 px niższe od reszty rzędu i poniżej
   progu 44 px z brand booka. */
#slot-lokalizacja .sprzet-btn {
  width: var(--sprzet-h);
  height: var(--sprzet-h);
}

/* Ikona MapLibre'a znika, bo rysuje się jako background-image z barwą wpisaną
   na sztywno — na ciemnej karcie byłaby niewidoczna. Nasza idzie obok i bierze
   barwę tekstu, więc chodzi za motywem. Stany kontrolki zostają klasami na
   przycisku, więc dalej mamy z czego je pokazać. */
#slot-lokalizacja .maplibregl-ctrl-icon { display: none; }
#slot-lokalizacja .maplibregl-ctrl-geolocate-active .sprzet-ikona { color: var(--primary); }
#slot-lokalizacja .maplibregl-ctrl-geolocate-waiting .sprzet-ikona { opacity: .5; }
#slot-lokalizacja button:disabled .sprzet-ikona { opacity: .35; }

/* Zębatka, gdy przeniesie się tu z nagłówka na wąskim ekranie. Dostaje własną
   pastylkę, bo jest osobnym skupiskiem, a nie częścią zoomu. */
#sprzet-mapy #ust-otworz {
  width: var(--sprzet-h);
  min-width: var(--sprzet-h);
  height: var(--sprzet-h);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--card) 82%, transparent);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-raised);
  color: var(--card-foreground);
}

#sprzet-mapy #ust-otworz svg { width: 18px; height: 18px; }

#sprzet-mapy [hidden] { display: none; }

/* Na wąskim ekranie znika pastylka obrotu — i to jest decyzja o zachowaniu,
   nie o miejscu. Na dotyku widok obraca się dwoma palcami, więc przyciski
   ±15° dublują gest, który jest wygodniejszy od nich. Na desktopie zostają,
   bo tam takiego gestu nie ma.

   Skutek uboczny jest tym, po co to zrobiliśmy: rząd schodzi z 332 do 238 px
   i mieści się w JEDNEJ LINII z sygnetem nawet przy 320 px. Wcześniej stał
   wiersz niżej, zostawiając nad sobą puste pasmo, a przy 360 px w trybie 3D
   i tak nachodziłby na sygnet o 30 px. Jedna reguła zamiast trzech. */
@media (max-width: 780px) {
  #obrot-blok { display: none; }

  /* …i dopiero teraz ta jedna linia jest naprawdę jedną linią.

     Rząd trzymał się dotąd górnej krawędzi MAPY, a sygnet — bezpiecznego
     obszaru EKRANU. Na desktopie to jedno i to samo, na telefonie nie:
     na iPhonie między nimi leży wysepka z zegarem i baterią, więc pastylka
     zoomu wjeżdżała wprost pod nią. Teraz oba rzędy schodzą razem, bo liczą
     odsunięcie z tej samej zmiennej.

     Odejmowanie to WYRÓWNANIE DO ŚRODKA, nie margines: sygnet ma 34 px,
     a pastylka `--sprzet-h` plus obramowanie — dziś tyle samo co znak
     z dwoma pikselami ramki, ale wzór zostaje, gdyby rozmiar znów urósł. Prawa krawędź zostaje na 12 px — tyle, ile ma wcięcie `.head`
     po drugiej stronie. */
  #sprzet-mapy {
    top: calc(var(--rzad-gora) - (var(--sprzet-h) + 2px - 34px) / 2);
    right: 12px;
  }
}

/* Na papierze sprzęt nie ma sensu — wydruk jest płaski i nieinteraktywny. */
@media print { #sprzet-mapy { display: none; } }

/* Szukanie z lupy na szerokim ekranie: karta pod rzędem sprzętu, wyrównana do
   jego prawej krawędzi — tam, gdzie kursor przed chwilą był. Na telefonie ta
   klasa nic nie zmienia: szukanie zostaje pełnym ekranem z css/szukaj.css,
   bo dół i tak zabiera klawiatura. */
@media (min-width: 781px) {
  .szukaj.na-mapie {
    inset: calc(.75rem + var(--sprzet-h) + .5rem) .75rem auto auto;
    width: min(360px, calc(100vw - 1.5rem));
    height: auto;
    max-height: min(480px, calc(100dvh - 7rem));
    padding: 12px 14px 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--card);
    color: var(--card-foreground);
    box-shadow: var(--shadow-raised);
  }
  .szukaj.na-mapie .szukaj-wyniki { padding-bottom: 8px; }
}


/* ---------- Przycisk „Prognoza pogody" przy atrybucji ---------- */
/* Włącznik nakładki opadu, od 2026-09-11 na dole mapy, na lewo od zwiniętej
   atrybucji „i" (`js/ui/sprzet-mapy.js`). Rozmiar i biel wzięte z tej
   atrybucji — 24 px wysokości, promień 12, `#fff` z arkusza MapLibre — żeby
   oba stały w rzędzie jak para, a nie jak dwa różne języki.

   Róg układa SIATKA, nie pływanie MapLibre. Na telefonie atrybucja startuje
   ROZWINIĘTA (tekst na 320 px) i zwija się do „i" dopiero przy pierwszym
   przesunięciu mapy — z pływaniem przycisk spadał wtedy pod tekst, do samej
   krawędzi ekranu. W siatce: podziałka na górze, pod nią [przycisk][„i"],
   a rozwinięty tekst dostaje własne piętro na dole, pod przyciskiem, który
   zostaje tam, gdzie był. Klasę rogu nadaje `js/ui/sprzet-mapy.js`. */
.rog-z-pogoda {
  display: grid;
  grid-template-columns: auto auto;
  justify-items: end;
  align-items: end;
}

.rog-z-pogoda > .maplibregl-ctrl-scale { grid-row: 1; grid-column: 1 / -1; }
.rog-z-pogoda > .maplibregl-ctrl-attrib { grid-row: 2; grid-column: 2; }

/* Ujemny prawy margines zjada część 10 px lewego marginesu zwiniętej
   atrybucji — między przyciskiem a „i" zostaje 6 px. Dolne 10 px to jej
   dolny margines: oba stoją na jednej linii. */
.rog-z-pogoda > .pogoda-ctrl {
  grid-row: 2;
  grid-column: 1;
  margin: 0 -4px 10px 0;
}

.rog-z-pogoda:has(> .maplibregl-compact-show) > .maplibregl-ctrl-attrib {
  grid-row: 3;
  grid-column: 1 / -1;
}

.rog-z-pogoda:has(> .maplibregl-compact-show) > .pogoda-ctrl {
  grid-column: 1 / -1;
  margin: 0 10px 0 0;
}

/* Szeroka mapa: atrybucja jest wierszem tekstu bez marginesów, przy samej
   krawędzi — przycisk staje obok niej, dół do dołu. */
.rog-z-pogoda:has(> .maplibregl-ctrl-attrib:not(.maplibregl-compact)) > .pogoda-ctrl {
  margin: 0 6px 0 0;
}

.pogoda-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 10px 0 7px;
  border: 0;
  border-radius: 12px;
  background: #fff;
  color: oklch(0.24 0.028 160);
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  box-shadow: 0 0 0 1px oklch(0 0 0 / 0.08);
  cursor: pointer;
}

.pogoda-btn svg { width: 16px; height: 16px; flex: none; pointer-events: none; }
.pogoda-btn:hover { background: oklch(0.96 0.01 150); }
.pogoda-btn:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

/* Włączona nakładka — to samo wypełnienie barwą główną, co wybrana zakładka
   i dawny przycisk w rzędzie. Pasek opadu bywa zasłonięty arkuszem, więc
   przycisk jest jedynym stałym śladem, że warstwa jest włączona. */
.pogoda-btn[aria-pressed="true"] {
  background: var(--primary);
  color: var(--primary-foreground);
  box-shadow: none;
}

@media print { .pogoda-ctrl { display: none; } }


/* ---------- Pasek nakładki opadu ---------- */
/* Przy dolnej krawędzi mapy, po stronie panelu — nad podziałką i z dala od
   atrybucji, która siedzi po prawej. Ten sam materiał, co pastylki sprzętu:
   półprzezroczyste tło z rozmyciem, żeby pole opadu było pod nim widoczne. */
.opad-pasek {
  position: absolute;
  left: calc(var(--panel-w) + 14px);
  bottom: 14px;
  z-index: 5;
  width: 340px;
  max-width: calc(100vw - var(--panel-w) - 28px);
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-2xl);
  background: color-mix(in oklab, var(--card) 88%, transparent);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-raised);
  color: var(--card-foreground);
  font-size: 12.5px;
}

.opad-pasek[hidden] { display: none; }

/* Tytuł i źródło w jednym wierszu, dopóki się mieszczą — a gdy nie, źródło
   schodzi CAŁE do drugiego wiersza. Bez `nowrap` łamał się tytuł i pasek
   otwierał się słowami „Opad —", co jest dokładnie tym nagłówkiem, którego
   ta warstwa nie może mieć. */
.opad-glowa {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 2px 8px;
}

.opad-glowa b { font-size: 13px; font-weight: 600; white-space: nowrap; }

/* Krzyżyk w prawym górnym rogu paska, poza przepływem nagłówka: ten zawija
   źródło do drugiego wiersza, a zamknięcie ma zostać tam, gdzie się go szuka.
   Nagłówek ustępuje mu miejsca z prawej. Ta sama okrągła rodzina, co „▶". */
.opad-glowa:has(.opad-zamknij) { padding-right: 30px; }

.opad-zamknij {
  position: absolute;
  top: 6px;
  right: 6px;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: transparent;
  color: var(--card-foreground);
  cursor: pointer;
}

.opad-zamknij svg { width: 14px; height: 14px; pointer-events: none; }
.opad-zamknij:hover { background: color-mix(in oklab, var(--accent-soft) 70%, transparent); }
.opad-zamknij:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

/* Źródło i wiek pobrania przy legendzie — wymóg licencji CC BY i zasada
   „wiek danych jest częścią danych". */
.opad-zrodlo {
  font-size: 11px;
  color: var(--muted-foreground);
  white-space: nowrap;
}

.opad-legenda {
  display: flex;
  gap: 10px;
  margin: 7px 0 0;
  padding: 0;
  list-style: none;
  font-size: 11px;
  color: var(--muted-foreground);
}

.opad-legenda li { display: flex; align-items: center; gap: 4px; }

/* Plamka, nie kreska: opad na mapie jest plamą i legenda ma wyglądać jak to,
   czego dotyczy. Obrys, bo najsłabszy stopień ma alfę 0,34 i na jasnej karcie
   sam z siebie ledwo się odcina. */
.opad-legenda i {
  width: 14px;
  height: 10px;
  border-radius: 3px;
  border: 1px solid var(--border-strong);
}

.opad-sterowanie {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}

.opad-graj {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: transparent;
  color: var(--card-foreground);
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
}

.opad-graj:hover { background: color-mix(in oklab, var(--accent-soft) 70%, transparent); }
.opad-graj:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

.opad-suwak { flex: 1 1 auto; min-width: 0; accent-color: var(--primary); }

/* Godzina w kroju o stałej szerokości — bez tego etykieta skacze przy każdym
   przesunięciu suwaka i pasek drga pod palcem. */
.opad-czas {
  flex: 0 0 auto;
  min-width: 96px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}

.opad-uwaga {
  margin: 6px 0 0;
  font-size: 11.5px;
  color: var(--muted-foreground);
}

.opad-uwaga[hidden] { display: none; }

/* Na telefonie panel przestaje być kolumną z boku i staje się arkuszem od
   dołu — czyli DOLNA KRAWĘDŹ MAPY WĘDRUJE. Pasek jedzie więc na górnej
   krawędzi arkusza, a nie na stałej wysokości: przyklejony do dna ekranu
   leżałby na treści arkusza w każdym detencie poza peekiem.

   `--arkusz-h` siedzi na `:root` (klasy detentów ustawia `ui/arkusz.js` na
   `<html>`), więc widzi ją także rodzeństwo panelu. Przejście to samo, co jego,
   żeby pasek jechał razem z arkuszem, a nie doskakiwał po nim. */
@media (max-width: 780px) {
  .opad-pasek {
    left: 10px;
    right: 10px;
    width: auto;
    max-width: none;
    bottom: calc(var(--pasek-h) + var(--arkusz-h, 0px) + 10px);
    transition: bottom var(--dur-slow) var(--ease-out);
  }

  /* Przy pełnym arkuszu mapy zostaje skrawek — pasek nie ma nad czym stać
     i wyjechałby ponad własny kadr. */
  .arkusz-pelna .opad-pasek { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .opad-pasek { transition: none; }
}

/* Prognoza na papierze to kłamstwo z datą — patrz `js/ui/wydruk.js`. */
@media print { .opad-pasek { display: none; } }

/* ---------------------------------------------------------------
   WIDOK ZE SZCZYTU (2026-09-13) — `js/ui/widok-ze-szczytu.js`

   Płyta to jedna linia przy dolnej krawędzi mapy: nazwa · wysokość · kierunek.
   Pierwsza wersja miała 15–20 % wysokości kadru i zasłaniała smugi ortofoto
   z kąta ślizgowego; zmniejszona na prośbę użytkownika (2026-09-13), bo na
   telefonie zabierała za dużo widoku — smugi na dole kadru są ceną tego
   wyboru. Pełna szerokość mapy i materiał gęstszy niż pasek opadu zostają. */
:root { --plyta-h: 52px; }

.plyta-widoku {
  position: absolute;
  left: var(--panel-w);
  right: 0;
  bottom: 0;
  z-index: 6;
  min-height: var(--plyta-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--border);
  background: color-mix(in oklab, var(--card) 94%, transparent);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow-ridge);
  color: var(--card-foreground);
}

.plyta-widoku[hidden] { display: none; }

/* Nazwa i podpis w jednym wierszu. Nazwa nie ustępuje — to ona mówi, skąd
   się patrzy; gdy miejsca brakuje, wielokropek zjada koniec podpisu. */
.plyta-tekst {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  overflow: hidden;
  white-space: nowrap;
}

.plyta-nazwa {
  flex: none;
  font-size: var(--text-base);
  font-weight: 600;
}

.plyta-nazwa::after {
  content: '·';
  margin: 0 var(--space-2);
  font-weight: 400;
  color: var(--muted-foreground);
}

/* Cyfry tabelaryczne: podpis azymutu zmienia się przy każdym ruchu palca
   i bez nich tekst drgałby w poziomie. */
.plyta-podpis {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--text-sm);
  color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}

.plyta-blad .plyta-podpis { color: var(--card-foreground); }

.plyta-zamknij { flex: none; }

.plyta-akcje {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.plyta-przelacznik { display: inline-flex; align-items: center; gap: 6px; }
.plyta-przelacznik svg { width: 16px; height: 16px; flex: none; }

/* Włączony — to samo wypełnienie barwą główną co „Prognoza pogody". */
.plyta-przelacznik[aria-pressed="true"] {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--primary-foreground);
}

/* Po błędzie odczytu terenu nie ma czego pokazywać. */
.plyta-blad .plyta-przelacznik { display: none; }

/* Zamknięcie na komputerze jest podpisem, na telefonie krzyżykiem. */
.plyta-zamknij svg { display: none; width: 16px; height: 16px; flex: none; }

@media (max-width: 780px) {
  /* Zmierzone: przycisk 34 px + odstępy + kreska = 53 px. Zmienna odsuwa
     atrybucję, więc musi być co najmniej tyle, ile płyta naprawdę zajmuje. */
  :root { --plyta-h: 54px; }

  /* Same ikony: przy 375 px podpis przycisków zabierał nazwie i kierunkowi
     cały wiersz. Nazwa dla czytnika ekranu zostaje w `aria-label`. */
  .plyta-etykieta { display: none; }
  .plyta-zamknij svg { display: block; }
  .plyta-akcje .btn {
    width: 34px;
    padding: 0;
    justify-content: center;
  }
}

/* Mapa w widoku: własne przeciąganie, więc przeglądarka nie może przejąć
   gestu na przewijanie ani powiększanie strony. */
.map-widok .maplibregl-canvas-container {
  touch-action: none;
  cursor: grab;
}
.map-widok .maplibregl-canvas-container.widok-chwyt { cursor: grabbing; }

/* Zdjęcie nieba pod kanwą (widok-ze-szczytu.js, `pokazNiebo`). Warstwy tła,
   ich rozmiar i położenie ustawia skrypt z kamery; tu tylko to, co stałe. */
.widok-niebo {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Sprzęt: zostają kompas i obrót. Szukanie, zoom i położenie zmieniają kamerę
   poza kontrolą trybu. Podziałka z poziomu oczu kłamie, a prognoza opadu
   jest warstwą, której widok nie pokazuje. Atrybucja zostaje — wymóg licencji —
   nad płytą. */
.widok-otwarty #sprzet-mapy > .sprzet-grupa:not(#kompas-blok):not(#obrot-blok):not(#pochyl-blok),
.widok-otwarty #slot-lokalizacja,
.widok-otwarty .maplibregl-ctrl-scale,
.widok-otwarty .pogoda-ctrl,
.widok-otwarty .opad-pasek { display: none; }

.widok-otwarty #map .maplibregl-ctrl-bottom-right,
.widok-otwarty #map .maplibregl-ctrl-bottom-left { bottom: var(--plyta-h); }

/* Na wąskim ekranie strzałki obrotu są w planerze schowane (obrót jest tam
   gestem, a rząd się nie mieści). W widoku rząd jest prawie pusty — szukanie,
   zoom i położenie znikają — więc obrót i pochylenie mieszczą się obok
   siebie, a przyciski są jedyną podpowiedzią, że kadrem da się ruszać. */
.widok-otwarty #obrot-blok:not([hidden]) { display: flex; }

@media (max-width: 780px) {
  .plyta-widoku {
    left: 0;
    bottom: calc(var(--pasek-h) + var(--arkusz-h, 0px));
    padding: var(--space-3) var(--space-4);
    padding-left: max(var(--space-4), env(safe-area-inset-left));
    padding-right: max(var(--space-4), env(safe-area-inset-right));
  }
  .widok-otwarty #map .maplibregl-ctrl-bottom-right,
  .widok-otwarty #map .maplibregl-ctrl-bottom-left {
    bottom: calc(var(--pasek-h) + var(--arkusz-h, 0px) + var(--plyta-h));
  }
}

/* Zasłona wczytywania. Zakrywa ten sam obszar co mapa obok panelu —
   pod nią kamera skacze po pierścieniach, a kadr ma dziury w kaflach.
   Tło z tokenów, gradient od nieba do ziemi: zapowiada, co będzie pod spodem,
   zamiast udawać ekran błędu. */
.zaslona-widoku {
  position: absolute;
  top: 0;
  left: var(--panel-w);
  right: 0;
  bottom: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  padding: var(--space-6);
  background: linear-gradient(to bottom,
    color-mix(in oklab, var(--accent-soft) 80%, var(--card)),
    var(--muted));
  color: var(--card-foreground);
  opacity: 1;
  transition: opacity 300ms var(--ease-out, ease-out);
}

.zaslona-widoku[hidden] { display: none; }
.zaslona-widoku.zaslona-znika { opacity: 0; pointer-events: none; }

.zaslona-tresc {
  width: min(360px, 100%);
  display: grid;
  gap: var(--space-3);
}

.zaslona-nazwa {
  font-size: var(--text-lg);
  font-weight: 600;
}

.zaslona-postep {
  height: 6px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--card-foreground) 12%, transparent);
}

.zaslona-pasek {
  height: 100%;
  background: var(--primary);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 250ms var(--ease-out, ease-out);
}

.zaslona-podpis {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-sm);
  color: var(--muted-foreground);
}

.zaslona-procent { font-variant-numeric: tabular-nums; }

/* W trakcie wczytywania sprzęt nie ma czym sterować. */
.widok-wczytywanie #sprzet-mapy { visibility: hidden; }

@media (max-width: 780px) {
  .zaslona-widoku {
    left: 0;
    bottom: calc(var(--pasek-h) + var(--arkusz-h, 0px));
  }
}

@media (prefers-reduced-motion: reduce) {
  .zaslona-widoku, .zaslona-pasek { transition: none; }
}

@media print { .plyta-widoku, .zaslona-widoku { display: none; } }

/* ---------------------------------------------------------------
   NAGŁÓWKI WIDOKÓW ARKUSZA (2026-09-06)

   Wzorcem był arkusz ustawień: tytuł, zamknięcie, treść przewijająca się
   pod spodem. Zakładki planera nie miały nagłówka w ogóle, a na telefonie
   pasek zakładek wędruje na dół ekranu — u góry arkusza nie zostawało nic,
   co by mówiło, na co się patrzy.

   Dwie różne rzeczy, celowo rozdzielone na dwie klasy:
     `.naglowek-zakladki` — TYTUŁ ZAKŁADKI. Na desktopie zbędny, bo nad
        panelem stoi pasek zakładek i powtarzałby jego etykietę.
     `.arkusz-naglowek`   — PRZYPIĘCIE I ZAMKNIĘCIE. Dotyczy też nagłówków
        stanów (Planuj trasę, Dobierz trasę, Filtry), które na desktopie
        były i zostają.
   --------------------------------------------------------------- */

/* Tytuł zakładki tylko na telefonie — patrz wyżej. */
.naglowek-zakladki { display: none; }

/* Widoczność nagłówków „Zaplanuj" i „Planuj trasę" niesie klasa trybu
   na `#plan`; oba stoją w dokumencie, bo tryb zmienia się bez przerysowania. */
.naglowek-reczne { display: none; }
#plan.tryb-reczne .naglowek-reczne { display: flex; }

/* Zamknięcie zwija arkusz. Na desktopie panel stoi zawsze i nie ma czego
   zamykać, więc przycisku tam nie ma. */
.arkusz-zamknij { display: none; }
.arkusz-naglowek h3 { margin-right: auto; }

@media (max-width: 780px) {
  /* Tytuły zakładek wchodzą dopiero tutaj — patrz wyżej, dlaczego nie na
     desktopie. Kolejność ma znaczenie: najpierw odsłaniamy wszystkie, potem
     z powrotem chowamy „Zaplanuj", bo ten jeden zależy jeszcze od trybu. */
  .naglowek-zakladki { display: flex; }
  .naglowek-wybor { display: none; }
  #plan.tryb-wybor .naglowek-wybor { display: flex; }
  .arkusz-zamknij { display: inline-flex; }

  /* Kropki kroków kreatora tracą tu swoje `margin-left: auto` — inaczej dwa
     elementy z automatycznym marginesem dzieliłyby wolne miejsce po połowie
     i kropki wylądowałyby na środku paska zamiast przy zamknięciu. Odstęp
     rozpycha teraz sam tytuł. */
  .arkusz-naglowek .kr-kroki { margin-left: 0; }

  /* PRZYPIĘCIE. Nagłówek zostaje przy górnej krawędzi, treść jedzie pod nim.

     Górne wcięcie `--uchwyt-h` to nie jest oddech, tylko OMINIĘCIE UCHWYTU:
     warstwa dotykowa gestu ma tę samą wysokość i leży wyżej w kolejności
     malowania. Bez tego wcięcia górna
     krawędź tytułu i zamknięcia wpadałaby pod tę warstwę, a dotknięcie
     zamknięcia zaczynałoby przeciąganie arkusza zamiast go zwijać.

     Tło jest nieprzezroczyste, bo pod nagłówek wjeżdża treść. Ujemne marginesy
     boczne z `.widok-naglowek` trzeba tu odwrócić: pasek ma sięgać krawędzi
     arkusza, a nie kończyć się tam, gdzie kończy się tekst. */
  .arkusz-naglowek {
    position: sticky;
    top: 0;
    z-index: 4;
    margin: 0 0 8px;
    padding: var(--uchwyt-h, 26px) 18px 8px;
    background: var(--background);
  }

  /* Nagłówki zakładek żyją wewnątrz przewijanego pudełka, więc to ONE dają
     teraz oddech pod uchwytem — panel nie potrzebuje własnego wcięcia i każdy
     jego piksel przekładałby się na wysokość arkusza. */
  #plan, #recommended, #saved { padding-top: 0; }

  /* Podnagłówek katalogu („Wybierz spośród 130 tras") stoi teraz POD tytułem
     widoku, więc traci własne górne wcięcie — miałby je podwójne. */
  .kat-head { padding-top: 2px; }

  /* Nagłówek kreatora siedzi w `.kr-box`, które ma własne wcięcia — a pasek
     przypięty MUSI sięgać krawędzi arkusza, bo inaczej treść wjeżdżająca pod
     niego wystaje po bokach jego tła. Zdejmujemy więc wcięcia rodzica, zamiast
     zabierać je `.kr-box`: potrzebuje ich cała reszta ankiety.

     Zmierzone przed poprawką: pasek 18–372 px zamiast 0–390 px. */
  .kr-box > .arkusz-naglowek {
    margin-top: -14px;
    margin-left: -18px;
    margin-right: -18px;
  }
}
