/* ============================================================================
   spolu — štýly appky. Engine, nie profil.

   ⛔ V TOMTO SÚBORE NIE JE ANI JEDNA KONKRÉTNA FARBA.
   Každá farba je `var(--token)` z brand profilu (`brands/<meno>/brand.css`),
   ktorý sa načíta PRED týmto súborom. Keď sem niekto napíše `#hex`, `rgb()`
   alebo pomenovanú farbu, profil prestane byť profilom.
   Výnimky, ktoré farbou nie sú: `currentColor` a `transparent`.

   Tokeny, ktoré profil (zatiaľ) nemá, sú nižšie v bloku „chýbajúce tokeny".
   Každý má tvar `var(--chce-sa, <odvodenina z tokenov profilu>)` — keď profil
   ten token doplní, prevezme ho bez zásahu do tohto súboru.

   Farbu používaj VŽDY cez rolu (`--fg`, `--accent-fill`), nikdy cez rampu
   (`--ink`, `--accent`). Rola sa preklopí s témou, rampa nie.
   ============================================================================ */

/* ---- chýbajúce tokeny + rozmery appky ------------------------------------
   Rozmery a časy nie sú súčasťou identity, tie tu byť smú. Farby nie —
   tie sú odvodené `color-mix`-om z tokenov profilu, nie napísané rukou. */
:root {
  /* farebné role, ktoré profil nemá — odvodené, nie vymyslené */
  --sp-sunken:       var(--bg-sunken,     color-mix(in srgb, var(--fg) 4%,  var(--bg)));
  --sp-accent-wash:  var(--accent-wash,   color-mix(in srgb, var(--accent-fill) 16%, var(--bg-raised)));
  --sp-second-wash:  var(--second-wash,   color-mix(in srgb, var(--second-fill) 20%, var(--bg-raised)));
  --sp-ok-wash:      var(--ok-wash,       color-mix(in srgb, var(--ok-fill) 18%, var(--bg-raised)));
  --sp-warn-wash:    var(--warn-wash,     color-mix(in srgb, var(--warn-fill) 16%, var(--bg-raised)));
  --sp-danger-wash:  var(--danger-wash,   color-mix(in srgb, var(--danger-fill) 14%, var(--bg-raised)));
  --sp-overlay:      var(--overlay,       color-mix(in srgb, var(--ink) 66%, transparent));

  /* rozmery */
  --sp-tap:          44px;      /* najmenší cieľ pre palec */
  --sp-gap:          .75rem;
  --sp-pad:          1rem;
  --sp-max:          760px;   /* reading-width: nad ~75 znakov sa riadok textu nečíta */
  --sp-wide:         1030px;  /* desktop: šírka pre panely a mriežky (nie pre text) */
  --sp-safe-t:       env(safe-area-inset-top, 0px);
  --sp-safe-b:       env(safe-area-inset-bottom, 0px);
}

/* ---- základ -------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-text);
  font-size: 1rem;
  line-height: 1.5;
  overscroll-behavior-y: none;
}
/* Mobil first: nič sa nesmie scrollovať do šírky. Rieši sa to `min-width: 0` na
   flex položkách a `.hscroll` kontajnerom okolo širokých vecí — NIE `overflow-x:
   hidden` na `body`. To by z tela spravilo scrollovací kontajner a zabilo by
   `position: sticky` hornej lišty. Dlhý text bez medzier sa radšej zlomí. */
.row .ttl, .sheethead h2, .post .text, .linkrow .bt, .banner .bt { overflow-wrap: anywhere; }
h1, h2, h3, h4 { font-family: var(--font-sans); font-weight: 700; line-height: 1.25; margin: 0; }
h1 { font-size: 1.35rem; }
h2 { font-size: 1.1rem; }
h3 { font-size: 1rem; }
p { margin: 0 0 .6rem; }
p:last-child { margin-bottom: 0; }
a { color: var(--accent-fg); }
a:hover { text-decoration-thickness: 2px; }
small { font-size: .82rem; }
img { max-width: 100%; }
.ic { fill: currentColor; vertical-align: middle; flex: 0 0 auto; }

/* Viditeľný focus je nepodmienečný. Bez neho sa appka klávesnicou neovláda. */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* Myš/dotyk nemá dostať krúžok, klávesnica áno — ale iba keď prehliadač
   `:focus-visible` naozaj vie. Preto sa `:focus` neruší nasucho. */
@supports not selector(:focus-visible) {
  :focus { outline: 3px solid var(--focus); outline-offset: 2px; }
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* Preskočiť na obsah — prvá vec v tab poradí, viditeľná až po zameraní. */
.skip {
  position: absolute; left: .5rem; top: -6rem; z-index: 60;
  background: var(--bg-raised); color: var(--fg);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  padding: .6rem .9rem; font-weight: 700; text-decoration: none;
  transition: top .12s ease;
}
.skip:focus { top: calc(var(--sp-safe-t) + .5rem); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}

/* ---- horná lišta --------------------------------------------------------- */
.topbar {
  position: sticky; top: 0; z-index: 20;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  padding-top: var(--sp-safe-t);
}
.topbar > .inner { max-width: var(--sp-max); margin: 0 auto; }
.tophead {
  display: flex; align-items: center; gap: .55rem;
  padding: .5rem var(--sp-pad) .4rem;
  min-width: 0;
}
.brand {
  display: flex; align-items: center; gap: .5rem; min-width: 0;
  background: none; border: 0; padding: 0; margin: 0;
  color: inherit; font: inherit; cursor: pointer; text-align: left;
}
.brand .mark { width: 32px; height: 32px; flex: none; object-fit: contain; }
.brand .txt { min-width: 0; display: flex; flex-direction: column; line-height: 1.15; }
.brand .t { font-family: var(--font-sans); font-weight: 700; font-size: 1rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.brand .s { font-size: .72rem; color: var(--fg-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tophead .sp { flex: 1 1 auto; min-width: 0; }

.iconbtn {
  background: var(--bg-raised); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  width: var(--sp-tap); height: var(--sp-tap); flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; font: inherit;
}
.iconbtn:hover { border-color: var(--line-strong); }
.iconbtn:active { transform: scale(.96); }
.iconbtn[aria-pressed="true"] { background: var(--accent-fill); color: var(--on-accent); border-color: var(--accent-fill); }

/* Ambientný indikátor stavu siete tu bol do 2026-08-22. Zrušený zámerne: visel
   v hlavičke stále a väčšinu času bol šum. Jeho užitočnú úlohu — povedať na
   mŕtvej sieti, PREČO sa nič nedeje — prevzala značka `.pending` pri konkrétnej
   položke. Ambientný stav je šum, konkrétne zlyhanie je informácia.
   Nevracaj sem chip; ak treba povedať viac, patrí to k tomu, čo sa nepodarilo. */

/* Neodoslané označenie — pri tej položke, kde vzniklo, nie v rohu obrazovky.
   `--warn-fg` je 6,98:1 na svetlom a 6,83:1 na tmavom podklade; k farbe je
   ikona aj slovo, takže stav sa nehlási iba farbou. */
.pending {
  display: inline-flex; align-items: center; gap: .25rem;
  font-size: .72rem; font-weight: 700; white-space: nowrap;
  color: var(--warn-fg);
}
.pending[hidden] { display: none; }

/* ---- navigácia ----------------------------------------------------------- */
/* Vodorovný pás. Na úzkom displeji sa scrolluje SÁM, nie celá stránka. */
/* ---- B1: paluba DOLE ------------------------------------------------------
   Človek na festivale drží telefón jednou rukou a hore na obrazovku nedočiahne.
   Navigácia preto sedí pri palci.

   DOM sa NEMENÍ: `.topnav` ostáva vnútri `.topbar`, lebo `position: fixed` uniká
   `sticky` predkovi, kým žiadny predok nevytvára containing block. Overené —
   `transform`/`filter`/`will-change` je v tomto súbore len na `:active` stavoch
   tlačidiel a na prepínači, teda nikde nad `.topnav`.
   🔴 Keby na `body`, `#app` alebo `.topbar` niekedy pribudol `transform`, paluba
   spadne späť do toku a bude uprostred stránky. Vtedy sa musí presunúť DOM,
   nie hľadať obchádzka v CSS. */
.topnav {
  position: fixed; inset: auto 0 0 0; z-index: 30;
  display: flex; gap: .1rem;
  padding: 0 .3rem calc(var(--sp-safe-b));
  background: var(--bg-deck, light-dark(var(--bg-raised), var(--base-soft)));
  border-top: 1px solid var(--line);
  overflow-x: auto; scrollbar-width: none;
}
.topnav::-webkit-scrollbar { display: none; }

/* Poloha menu je voľba akcie (admin, §18.6). Východisko je paluba pri palci
   (`position: fixed` dole, pravidlo vyššie); `data-nav="top"` ju vráti do toku
   pod hlavičku. LEN pod 900 px — nad tým je z menu ľavý pruh (B7a) a poloha
   hore/dole naň nesadá. `max-width: 899.98px`, aby sa neprekrývalo s pruhom na
   presne 900 px. */
@media (max-width: 899.98px) {
  :root[data-nav="top"] .topnav {
    position: static; inset: auto; z-index: auto;
    border-top: 0; border-bottom: 1px solid var(--line);
    padding: 0 .3rem;
  }
  /* Aktívna karta: podčiarknutie namiesto pruhu navrchu — nad menu je hlavička,
     takže pruh zdola ukazuje k obsahu, ku ktorému karta vedie. */
  :root[data-nav="top"] .topnav button { border-top: 0; border-bottom: 3px solid transparent; }
  :root[data-nav="top"] .topnav button[aria-current="page"] { border-bottom-color: var(--accent-fill); }
  /* Paluba dole už nedrží miesto, tak sa spodná výplň zmenší na bezpečnú zónu. */
  :root[data-nav="top"] main { padding-bottom: calc(var(--sp-pad) + var(--sp-safe-b)); }
}
.topnav button {
  flex: 1 1 0; min-width: 4.6rem; min-height: var(--sp-tap);
  display: flex; flex-direction: column; align-items: center; gap: .1rem;
  background: none; border: 0; border-bottom: 3px solid transparent;
  padding: .4rem .35rem .3rem; cursor: pointer;
  color: var(--fg-muted); font: inherit; font-size: .72rem; font-weight: 700;
  white-space: nowrap;
}
.topnav button:hover { color: var(--fg); }
.topnav button { border-bottom: 0; border-top: 3px solid transparent; }
.topnav button[aria-current="page"] { color: var(--accent-fg); border-top-color: var(--accent-fill); }
.topnav .nbadge {
  font-size: .62rem; line-height: 1; font-weight: 800;
  background: var(--danger-fill); color: var(--on-danger);
  border-radius: var(--radius-pill); padding: .1rem .3rem; margin-left: .15rem;
}

/* ---- B4: živý pás v hlavičke ---------------------------------------------
   Druhý riadok hlavičky, pod identitou akcie. Namerané na 390 × 844:

     pás povie dve veci (názov + meta)  →  lišta 111 px + paluba 54 = 165 (19,5 %)
     pás povie jednu vec                →  lišta 105 px + paluba 54 = 159 (18,8 %)
     pás nemá čo povedať (`hidden`)     →  lišta  59 px + paluba 54 = 113 (13,4 %)

   Pre porovnanie: víťazný smer sľuboval pevnú hlavičku 236 px = 28 %.

   🔴 `.nowbar` ZÁMERNE nemá `display` — vďaka tomu na ňom funguje natívny
   `hidden` a pás sa vie z obrazovky stratiť úplne. Keď sem niekto dopíše
   `display: flex`, `hidden` prestane platiť a v hlavičke ostane prázdny pruh
   s vlastnou hranou. To je presne tá chyba, kvôli ktorej sa B4 odkladalo. */
.nowbar { border-top: 1px solid var(--line); }
/* 🔴 DVA riadky, nie jeden — a je to zmeraná oprava, nie estetika.
   Prvá verzia mala odznak · názov · metu v jednom flexe. Na 390 px nevyšla:
   „Tiken Jah Fakoly" potrebuje 146 px, ostalo mu 98 → „Tiken Ja…", a
   „Začíname o 2 dni" sa useklo na „Začíname o 2 …", teda presne na tom slove,
   kvôli ktorému pás existuje. Zároveň na 1280 px odletela meta 700 px od názvu —
   tá istá chyba, ktorú porota vyčítala dnešnému „Idem" 620 px od názvu.
   Mriežka rieši oboje naraz: názov dostane celý zvyšok riadku, meta stojí POD
   ním v tom istom stĺpci. Bez `.nb-s` druhý riadok nevznikne, takže pás, ktorý
   má povedať jednu vec, ostáva 44 px vysoký. Overené: `scrollWidth` názvu sa
   vo všetkých štyroch priečkach rovná `clientWidth`, teda sa neskracuje nič. */
.nb {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  align-items: center; gap: 0 .45rem;
  width: 100%; min-height: var(--sp-tap);
  padding: .3rem var(--sp-pad) .35rem;
  border: 0; background: none; color: inherit; font: inherit;
  text-align: left; -webkit-tap-highlight-color: transparent;
}
button.nb { cursor: pointer; }
button.nb:active { background: var(--press, color-mix(in srgb, var(--fg) 12%, transparent)); }
.nb .badge { grid-column: 1; grid-row: 1; }
.nb .nb-c {
  grid-column: 2; grid-row: 1; font-weight: 700; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Meta patrí k názvu, preto stojí v jeho stĺpci, nie pod odznakom.
   13,1 px by bolo pod podlahou, ktorú si B2 stanovil na `.meta`; pás sa číta
   v pohybe, tak drží tých istých 15 px. Zmerané v prehliadači: `--fg-muted`
   na podklade lišty je 5,90:1 vo svetlej a 7,05:1 v tmavej téme. */
.nb .nb-s {
  grid-column: 2; grid-row: 2; font-size: .9375rem; line-height: 1.3;
  color: var(--fg-muted); font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Dve priečky, dva významy: hlina = MOJE rozhodnutie, tichá = festival okolo.
   Obe cez existujúce role a existujúce washe — žiadna nová rola do profilu (C4).
   Zmerané v prehliadači: `nb-now` 5,25:1 svetlá / 6,37:1 tmavá, `nb-next`
   8,71:1 / 10,92:1 — teda na bajt tie isté dvojice ako `.badge.tick`
   a `.badge.plain`, ktoré tu už roky sú.
   Prečo teda nie rovno `.badge.tick` / `.badge.plain`: nesú iný VÝZNAM (lístok,
   neutrálny počet). Prepísať sémantiku triedy kvôli farbe je presne to, čo sa
   o pol roka nedá vysvetliť. */
.badge.nb-now  { color: var(--accent-fg); background: var(--sp-accent-wash); }
.badge.nb-next { color: var(--fg-soft);   background: var(--sp-sunken); }

/* ---- plocha -------------------------------------------------------------- */
/* Spodná výplň musí uvoľniť PALUBU, nie len bezpečnú zónu — inak posledná
   položka programu leží pod navigáciou a nedá sa na ňu ťuknúť. */
main { max-width: var(--sp-max); margin: 0 auto; padding: var(--sp-pad) var(--sp-pad) calc(5.5rem + var(--sp-safe-b)); }
.stack > * + * { margin-top: var(--sp-gap); }
.muted { color: var(--fg-muted); }
.hstack { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.hscroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ---- karta --------------------------------------------------------------- */
.card {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--sp-pad);
  box-shadow: var(--shadow-1);
}
.card > h2, .card > h3 { margin-bottom: .5rem; }
.card.flat { box-shadow: none; }
.cardhead { display: flex; align-items: center; gap: .5rem; margin-bottom: .6rem; }
.cardhead h2, .cardhead h3 { flex: 1 1 auto; min-width: 0; }

/* ---- tlačidlá ------------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  min-height: var(--sp-tap); padding: .55rem .9rem;
  border-radius: var(--radius-sm); border: 1px solid var(--line-strong);
  background: var(--bg-raised); color: var(--fg);
  font: inherit; font-weight: 700; font-size: .92rem;
  cursor: pointer; text-decoration: none; text-align: center;
}
.btn:hover { border-color: var(--fg-muted); }
.btn:active { transform: scale(.985); }
.btn[disabled] { opacity: .55; cursor: not-allowed; }
.btn.primary { background: var(--accent-fill); color: var(--on-accent); border-color: var(--accent-fill); }
.btn.second  { background: var(--second-fill); color: var(--on-second); border-color: var(--second-fill); }
.btn.ghost   { background: none; }
.btn.danger  { color: var(--danger-fg); border-color: var(--danger-fg); background: none; }
.btn.wide    { width: 100%; }
.btn.sm      { min-height: 36px; padding: .3rem .6rem; font-size: .82rem; }
.btnrow { display: flex; flex-wrap: wrap; gap: .5rem; }

/* ---- chipy (filtre) ------------------------------------------------------ */
.chips { display: flex; flex-wrap: wrap; gap: .35rem; }
.chip {
  display: inline-flex; align-items: center; gap: .3rem;
  /* Cieľ pre palec, nie pre myš. `.chip` nesie filtre dní aj scén — na festivale
     sa na ne ťuká za chôdze, takže 36 px je pod hranicou, ktorú si tento súbor
     sám určil (`--sp-tap`). */
  min-height: var(--sp-tap); padding: .3rem .7rem;
  border-radius: var(--radius-pill); border: 1px solid var(--line-strong);
  background: var(--bg-raised); color: var(--fg-soft);
  font: inherit; font-size: .82rem; font-weight: 700; cursor: pointer; white-space: nowrap;
}
.chip:hover { color: var(--fg); border-color: var(--fg-muted); }
/* Zapnutý filter nesie výplň AJ značku „✓" — stav sa nikdy nehlási len farbou. */
.chip[aria-pressed="true"] { background: var(--accent-fill); color: var(--on-accent); border-color: var(--accent-fill); }
.chip .cnt { font-weight: 400; opacity: .8; }
/* Avatar na čipe filtra „Kmeň" (§18.4). Malý a neškrtí výšku pilulky. Vlastné
   pozadie si drží aj na zapnutom čipe — je to tvár človeka, nie stav filtra. */
.chip .ava { flex: 0 0 auto; width: 18px; height: 18px; margin: -2px 0; font-size: .58rem; }
.filtgrp { margin-top: .55rem; }
.filtgrp > .lbl { display: block; font-size: .74rem; font-weight: 800; letter-spacing: .04em;
  text-transform: uppercase; color: var(--fg-muted); margin-bottom: .3rem; }

/* ---- dni ----------------------------------------------------------------- */
.daytabs { display: flex; gap: .35rem; padding-bottom: .25rem; }
.daytabs .chip { flex: 1 0 auto; justify-content: center; flex-direction: column; gap: 0; min-height: 48px; line-height: 1.2; }
.daytabs .chip .wd { font-size: .7rem; font-weight: 400; }

/* ---- hľadanie ------------------------------------------------------------ */
.search { position: relative; display: flex; align-items: center; }
.search .ic { position: absolute; left: .6rem; color: var(--fg-muted); pointer-events: none; }
.search .clr { position: absolute; right: .3rem; }

/* ---- polia --------------------------------------------------------------- */
.field { display: block; }
.field > .lbl { display: block; font-size: .8rem; font-weight: 700; margin-bottom: .25rem; }
.field > .hint { display: block; font-size: .78rem; color: var(--fg-muted); margin-top: .25rem; }
input[type=text], input[type=tel], input[type=url], input[type=search], input[type=number], textarea, select {
  width: 100%; min-height: var(--sp-tap);
  padding: .55rem .7rem;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--bg); color: var(--fg);
  font: inherit; font-size: 1rem;   /* 16 px — menšie písmo iOS pri zameraní priblíži */
}

/* 🔴 Musí stáť ZA shorthandom `padding` vyššie. Prvá oprava menila `padding-left`
   na `padding-inline-start` a to nestačilo: neskorší shorthand prebije aj logickú
   vlastnosť. Rozhoduje PORADIE, nie tvar vlastnosti — a bez toho lupa leží na texte. */
.search input { padding-inline-start: 2.2rem; }
textarea { min-height: 5rem; resize: vertical; line-height: 1.45; }
input::placeholder, textarea::placeholder { color: var(--fg-muted); opacity: 1; }
input[aria-invalid="true"], textarea[aria-invalid="true"] { border-color: var(--danger-fg); }
.err { color: var(--danger-fg); font-size: .84rem; font-weight: 700; }
.okmsg { color: var(--ok-fg); font-size: .84rem; font-weight: 700; }

/* ---- prepínač (opt-in zdieľania) ----------------------------------------- */
/* `role="switch"` + `aria-checked`. Stav nesie poloha jazdca AJ text vedľa. */
.switchrow { display: flex; align-items: center; gap: .6rem; padding: .55rem 0; min-height: var(--sp-tap); }
.switchrow + .switchrow { border-top: 1px solid var(--line); }
.switchrow .txt { flex: 1 1 auto; min-width: 0; }
.switchrow .txt b { display: block; font-size: .95rem; }
.switchrow .txt small { display: block; color: var(--fg-muted); }
.switch {
  flex: none; width: 52px; height: 30px; border-radius: var(--radius-pill);
  border: 1px solid var(--line-strong); background: var(--sp-sunken);
  position: relative; cursor: pointer; padding: 0;
}
.switch::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 24px; height: 24px;
  border-radius: 50%; background: var(--fg-muted); transition: transform .14s ease, background .14s ease;
}
.switch[aria-checked="true"] { background: var(--ok-fill); border-color: var(--ok-fill); }
.switch[aria-checked="true"]::after { transform: translateX(22px); background: var(--on-ok); }

/* ---- riadok zoznamu ------------------------------------------------------ */
.list { display: flex; flex-direction: column; }
.list > .row + .row { border-top: 1px solid var(--line); }
/* §28.8 — trasa výletu: riadok čísel dňa, jedlo po riadkoch, súčet pod dňami, vybavenie. */
.trip-nums { margin-top: .3rem; }
.trip-nums span, .trip-sum span { white-space: nowrap; }
.trip-sum { margin-top: .5rem; padding-top: .5rem; border-top: 1px solid var(--line); }
.trip-food { list-style: none; margin: .3rem 0 0; padding: 0; font-size: .82rem; color: var(--fg-muted); }
.trip-gear { margin: .5rem 0 0; padding-left: 1.3rem; }
.trip-gear li + li { margin-top: .2rem; }
/* §28.10 — spolujazda: žije v karte „Kto ide", preto vlastná linka nad sebou a nie
   vlastná karta. „Odkiaľ" a „voľných miest" sú v jednom riadku; miesta sú úzke. */
.rides { margin-top: .8rem; padding-top: .7rem; border-top: 1px solid var(--line); }
/* Hlavička je `<summary>` — sekcia sa dá zbaliť (Ľubo 9. 9. 2026). Vlastná šípka
   `<details>` je v každom prehliadači iná, tak sa skryje a nahradí vlastnou. */
.ridehead { font-weight: 700; margin-bottom: .45rem; cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: .35rem; min-height: var(--sp-tap); }
.ridehead::-webkit-details-marker { display: none; }
.ridehead::after { content: "▾"; margin-left: auto; color: var(--fg-muted); font-size: .8em; }
.rides:not([open]) .ridehead::after { content: "▸"; }
.ridehead:focus-visible { outline: 2px solid var(--accent-fill); outline-offset: 2px; border-radius: var(--radius-sm); }
.ridechips { margin-top: .1rem; }
.ridefields { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .6rem; }
.ridefield { flex: 1 1 12rem; min-width: 0; }
.ridefield.seats { flex: 0 0 7.5rem; }
/* „Odkiaľ" je celý riadok a viacriadkové: odpoveď býva trasa s časom, nie jedno slovo. */
.ridefield.wide { flex: 1 1 100%; }
.ridefield.wide textarea { width: 100%; min-height: 3.2rem; resize: vertical; }
.ridesum { margin-top: .7rem; }
.ridegroup { margin-top: .6rem; }
.ridenote { margin-top: .5rem; }
/* §28.9 — „dostaň ma na to miesto": dva verby na jeden bod („cieľ dňa" ho zameria
   kompasom, „Navigovať" vedie von, §17.8). V riadku dňa stoja POD textom, kým „na mape"
   (celý deň) ostáva vpravo — sú to dve rôzne veci a nesmú splynúť do jedného radu.
   Tá istá trieda nesie aj riadok v karte Zraz: je to ten istý druh akcie.

   🔴 Podlaha cieľa je `--sp-tap` (B8). `.btn.sm` má 36 px a toto sú tlačidlá, ktoré sa
   mačkajú JEDNOU RUKOU NA HREBENI, prípadne v rukavici — presne ten prípad, pre ktorý
   podlaha existuje. Dvíha sa LEN výška, nie výplň ani písmo: rovnaký dôvod ako pri
   `.row.person` — väčšia výplň by dvojicu tlačidiel zalomila do dvoch riadkov. */
.trip-goto { margin-top: .45rem; }
.trip-goto .btn.sm { min-height: var(--sp-tap); }
/* `position: relative` je základ pre roztiahnutý cieľ nižšie. */
.row { position: relative; display: flex; align-items: flex-start; gap: .6rem; padding: .65rem 0; min-width: 0; }
.row .grow { flex: 1 1 auto; min-width: 0; }
.row .ttl {
  background: none; border: 0; padding: 0; margin: 0; font: inherit; text-align: left;
  color: inherit; font-weight: 700; cursor: pointer; display: block; width: 100%;
}
.row .ttl:hover { text-decoration: underline; }
/* Otvorenie položky je cieľ pre palec CELÝ RIADOK, nie len text názvu. Na 390 px
   mal názov okolo 24 px výšky a človek v dave musel trafiť riadok textu.
   Pseudoprvok neťahá do DOM nič navyše a čítačka obrazovky ďalej číta `<button>`.
   🔴 `.side` musí ostať NAD ním, inak by roztiahnutý cieľ prekryl tlačidlo voľby
   a „Idem" by sa nedalo ťuknúť — celý riadok by otváral detail. */
.row .ttl::after { content: ""; position: absolute; inset: 0; }
.row .side { position: relative; }
/* Bez `.row` predku tiež: panel detailu, „Ja" aj nástenka kreslia `.meta`
   samostatne. Predtým mali plné popredie a zlepené slová, lebo pravidlo bolo
   viazané na `.row`. */
.meta { font-size: .82rem; color: var(--fg-muted); display: flex; flex-wrap: wrap; gap: .15rem .5rem; }
.meta .t { font-variant-numeric: tabular-nums; }
.row .side { flex: none; display: flex; align-items: center; gap: .3rem; }

/* ---- označenie (pick): tri stavy, každý má ikonu aj slovo ---------------- */
.pick { display: flex; align-items: center; gap: .3rem; flex: none; }
.pick .pk,
.pick3 .pk {
  display: inline-flex; align-items: center; justify-content: center; gap: .3rem;
  min-height: var(--sp-tap); min-width: var(--sp-tap); padding: .3rem .55rem;
  /* Stavová vrstva pri ťuknutí. Lokálna rola s náhradou — profil sa netrhá
     (povýšenie do `brand.css` je samostatné rozhodnutie). */
  -webkit-tap-highlight-color: transparent;
  border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
  background: var(--bg-raised); color: var(--fg-soft);
  font: inherit; font-size: .8rem; font-weight: 700; cursor: pointer; white-space: nowrap;
}
.pick .pk:hover,
.pick3 .pk:hover { color: var(--fg); border-color: var(--fg-muted); }
.pick .pk:active,
.pick3 .pk:active { background: var(--press, color-mix(in srgb, var(--fg) 12%, transparent)); }
/* V RIADKU programu je cieľ 64 × 64: človek stojí v dave a mieri palcom za chôdze.
   V detaile (`pick3`) ostáva 44 — tam je tlačidlo široké a rúk býva viac času. */
.row .pick .pk { min-height: 64px; min-width: 64px; }
.pick .pk.want[aria-pressed="true"],
.pick3 .pk.want[aria-pressed="true"] { background: var(--second-fill); color: var(--on-second); border-color: var(--second-fill); }
.pick .pk.tick[aria-pressed="true"],
.pick3 .pk.tick[aria-pressed="true"] { background: var(--accent-fill); color: var(--on-accent); border-color: var(--accent-fill); }

/* Trojica s popiskami — v detaile, kde je miesto. `role="group"`, nie radiogroup:
   sú to tri nezávislé stavy jedného poľa a každé tlačidlo hlási `aria-pressed`. */
.pick3 { display: flex; gap: .4rem; flex-wrap: wrap; }
.pick3 .pk { flex: 1 1 8rem; }

/* ---- hodnotenie hviezdami (§25) ------------------------------------------ */
/* Odznak priemeru v riadku programu — vedľa počtu ľudí. Jantárová hviezda, aby
   sa odlíšila od sivej mety, ale bez výplne cez celý odznak. */
/* Meno v Kmeni ako preklik do programu (§18.4). Vyzerá ako meno, ale je to
   tlačidlo — akcentová farba a podčiarknutie napovedia, že sa dá ťuknúť. */
.person .personname {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; font-weight: 700; color: var(--accent-fg);
  cursor: pointer; text-align: left; text-decoration: underline; text-underline-offset: 2px;
}
.person .personname:hover { text-decoration-thickness: 2px; }

.ratebadge { display: inline-flex; align-items: center; gap: .15rem; color: var(--accent-fg); font-weight: 700; }
.ratebadge small { font-weight: 400; opacity: .8; }
.ratebadge .ic { color: var(--accent-fill); }

/* Karta hodnotenia v detaile. */
.ratecard { border: 1px solid var(--line); border-radius: var(--radius); padding: .7rem .8rem; }
.ratecard .cardhead { margin-bottom: .4rem; }
.ratecard .cardhead .ic { color: var(--accent-fill); }
/* Desať hviezd na výber (1–10). Na telefóne sa 10 × 44 px do riadku nezmestí,
   tak sú užšie a v núdzi sa zalomia do druhého riadku (`flex-wrap`) — radšej dva
   riadky než vodorovný scroll, na ktorom sa hviezda nedá trafiť. Výška ostáva
   palcová (40 px), šírku nesie obsah. */
.stars { display: flex; flex-wrap: wrap; gap: .05rem; }
.stars .star {
  min-width: 0; min-height: 40px; padding: .2rem .1rem;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer; color: var(--fg-soft);
  -webkit-tap-highlight-color: transparent;
}
.stars .star:hover { color: var(--accent-fill); }
.stars .star.on { color: var(--accent-fill); }
.stars .star .ic { width: 24px; height: 24px; }
.rateavg { display: flex; align-items: center; gap: .35rem; margin-top: .3rem; }
.rateavg .ic { color: var(--accent-fill); }
.rateavg b { font-size: 1.05rem; }

/* ---- lístky (predaj, §26) ------------------------------------------------- */
.ticketswrap .card > .list > .row.ticket { align-items: center; }
.row.ticket .side .btn { white-space: nowrap; }
.paycard { border: 1px solid var(--accent-fill); }
.paycard .cardhead .ic { color: var(--accent-fill); }
.payamount { text-align: center; margin: .2rem 0 .6rem; }
.payamount b { font-size: 2rem; font-weight: 800; }
/* QR v strede. Biely podklad aj tichú zónu nesie samotný SVG (natvrdo, kvôli
   skenovateľnosti — viď `paylink.js`); CSS tu dáva len rozmer, rám a tieň. */
.payqrwrap { display: flex; justify-content: center; margin: .2rem 0 .8rem; }
.payqrwrap .payqr {
  width: min(240px, 70vw); height: auto;
  border-radius: var(--radius); box-shadow: var(--shadow-1);
}
.btn.wide { width: 100%; justify-content: center; }
.paydetails { display: flex; flex-direction: column; gap: .3rem; margin: .4rem 0; }
.paydetails > div { display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; }
.paydetails .lbl { color: var(--fg-muted); font-size: .8rem; min-width: 4.5rem; }
.paydetails code { font-family: var(--font-mono, ui-monospace, monospace); word-break: break-all; }
/* Viac spôsobov platby naraz (§26): každý blok má hlavičku a nad druhým čiaru. */
.paymethod { margin: .2rem 0 .6rem; }
.paymethod + .paymethod { border-top: 1px solid var(--line); padding-top: .7rem; margin-top: .7rem; }
.payhead { display: flex; align-items: center; gap: .35rem; font-weight: 700; font-size: .9rem; color: var(--fg-muted); margin: .1rem 0 .4rem; }
.paychoose { text-align: center; margin: 0 0 .4rem; }

/* ---- odznaky ------------------------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: .25rem;
  font-size: .72rem; font-weight: 800; line-height: 1.2;
  padding: .15rem .45rem; border-radius: var(--radius-pill);
  border: 1px solid currentColor;
}
.badge.want   { color: var(--second-fg); background: var(--sp-second-wash); }
.badge.tick   { color: var(--accent-fg); background: var(--sp-accent-wash); }
.badge.clash  { color: var(--danger-fg); background: var(--sp-danger-wash); }
.badge.tight  { color: var(--warn-fg);   background: var(--sp-warn-wash); }
/* `guess` a `plain` sú malé (0,72 rem) — `--fg-muted` by na wash podklade
   spadol tesne pod 4,5:1, tak nesú `--fg-soft` (10,5:1 na svetlom). */
.badge.guess  { color: var(--fg-soft);   background: var(--sp-sunken); }
.badge.admin  { color: var(--accent-fg); background: var(--sp-accent-wash); }
.badge.plain  { color: var(--fg-soft);   background: var(--sp-sunken); }

/* Prekryv v čase — okrem farby aj hrubý pruh vľavo a odznak so slovom. */
.row.clash { border-left: 4px solid var(--danger-fill); padding-left: .5rem; background: var(--sp-danger-wash); }
.row.tight { border-left: 4px solid var(--warn-fill);   padding-left: .5rem; background: var(--sp-warn-wash); }

/* ---- oznamy -------------------------------------------------------------- */
.banner {
  display: flex; gap: .6rem; align-items: flex-start;
  border: 1px solid currentColor; border-radius: var(--radius);
  padding: .7rem .8rem; font-size: .9rem;
}
.banner .bt { flex: 1 1 auto; min-width: 0; color: var(--fg); }
.banner .bt > b { display: block; }
.banner.info   { color: var(--accent-fg); background: var(--sp-accent-wash); }
.banner.warn   { color: var(--warn-fg);   background: var(--sp-warn-wash); }
.banner.danger { color: var(--danger-fg); background: var(--sp-danger-wash); }
.banner.ok     { color: var(--ok-fg);     background: var(--sp-ok-wash); }

.empty { text-align: center; color: var(--fg-muted); padding: 2rem 1rem; }
.empty .ic { display: block; margin: 0 auto .5rem; opacity: .5; }

/* ---- odkazy k akcii (§11) ------------------------------------------------
   Chat je MALÝ ČIP a žije IBA v Kmeni (§11 po redizajne). Bol to veľký oranžový
   baner na vrchu Programu aj Domova a svietil všade — do WhatsAppu sa ide písať
   ĽUĎOM, a tí sú v Kmeni, takže tam patrí aj odkaz. Ostatné odkazy sú riadky. */
.chatrow { display: flex; }
.chatchip {
  /* Dedí z `.chip` (pilulka, palcová výška). Akcentový nádych, aby sa medzi
     ľuďmi našiel, ale nesvietil ako baner. */
  border-color: var(--accent-fill); color: var(--accent-fg);
  background: var(--sp-accent-wash); text-decoration: none;
}
.chatchip span { font-weight: 700; }
.chatchip.add { border-color: var(--line-strong); color: var(--fg-soft); background: var(--bg-raised); }
.linklist { display: flex; flex-direction: column; gap: .35rem; }
.linkrow {
  display: flex; align-items: center; gap: .6rem;
  min-height: var(--sp-tap); padding: .5rem .7rem;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--bg-raised); color: var(--fg); text-decoration: none; font-weight: 700; font-size: .92rem;
}
.linkrow:hover { border-color: var(--line-strong); }
.linkrow .bt { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.linkrow .ext { color: var(--fg-muted); }

/* ---- ľudia --------------------------------------------------------------- */
.ava {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  background: var(--sp-second-wash); color: var(--second-fg);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .78rem; font-weight: 800; overflow: hidden;
  background-size: cover; background-position: center;
}
.ava.img { color: transparent; }
/* „Kto ide na to isté“ je VETA s menami, nie kopa koliesok s iniciálkami.
   Kolieska mali 26 px, ale pri prekryve −8 px z nich bolo vidno 18, iniciálka
   v nich merala 10,56 px a rámik bol `--bg-raised`, teda krémový prstenec aj na
   ružovom `.row.clash`. Vonku na slnku z toho ostala farebná bodka, ktorú bolo
   treba dekódovať; meno sa prečíta priamo.
   Mená sú `--second-fg` (rola „ostatní“ — Egejská znamená moji ľudia), nie
   `--fg-muted`: v riadku je to jediná odpoveď na otázku, kvôli ktorej produkt
   existuje, a patrí jej väčšia váha než času a scéne. Zmerané na VŠETKÝCH
   podkladoch, na ktoré riadok môže padnúť — najhoršie 5,02:1 (svetlá téma,
   `--bg-sunken`) a 5,57:1 (tmavá, `--base-soft`); na `.row.clash` 5,12:1
   a 7,49:1, na `.row.tight` 5,34:1 a 5,94:1.
   14 px, nie palcových 15: názov riadku je dnes 16 px/700 a 15 px/600 by ho
   takmer dobehlo. Keď názov vyrastie, smie vyrásť aj toto.
   Avatary ostávajú v Kmeni a v detaile, kde je jednotkou zoznamu človek. */
.who { font-size: .875rem; font-weight: 600; color: var(--second-fg);
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }

/* ---- spodný panel (detail položky) --------------------------------------- */
.sheetwrap { position: fixed; inset: 0; z-index: 40; display: flex; align-items: flex-end; justify-content: center; }
.sheetwrap[hidden] { display: none; }
.sheetbg { position: absolute; inset: 0; background: var(--sp-overlay); border: 0; padding: 0; cursor: pointer; }
.sheet {
  position: relative; width: 100%; max-width: var(--sp-max);
  max-height: 88vh; max-height: 88dvh;   /* `vh` na mobile ignoruje lištu prehliadača */
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: var(--bg-raised); color: var(--fg);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  border: 1px solid var(--line); border-bottom: 0;
  box-shadow: var(--shadow-2);
  padding: .5rem var(--sp-pad) calc(1.2rem + var(--sp-safe-b));
  animation: sp-up .16s ease-out;
}
@keyframes sp-up { from { transform: translateY(14px); opacity: .6 } to { transform: none; opacity: 1 } }
.sheethead { position: sticky; top: 0; z-index: 1; background: var(--bg-raised);
  display: flex; align-items: flex-start; gap: .5rem; padding: .5rem 0 .6rem; }
.sheethead h2 { flex: 1 1 auto; min-width: 0; }
@media (min-width: 620px) {
  .sheetwrap { align-items: center; padding: 1rem; }
  .sheet { border-radius: var(--radius-lg); border-bottom: 1px solid var(--line); max-height: 84vh; max-height: 84dvh; }
}

/* ---- obrázok na celú obrazovku (mapa) ------------------------------------ */
.lightbox { position: fixed; inset: 0; z-index: 50; background: var(--sp-overlay); }
.lightbox[hidden] { display: none; }
.lightbox .lbscroll { position: absolute; inset: 0; overflow: auto; display: flex; align-items: center; justify-content: center; padding: 1rem; }
.lightbox img { max-width: none; }
.lightbox .lbclose { position: absolute; top: calc(var(--sp-safe-t) + .6rem); right: .6rem; z-index: 1; }

/* ---- nástenka ------------------------------------------------------------ */
.post { display: flex; gap: .6rem; padding: .7rem 0; }
.post + .post { border-top: 1px solid var(--line); }
.post .body { flex: 1 1 auto; min-width: 0; }
.post .head { display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; font-size: .84rem; }
.post .head b { font-size: .92rem; }
.post .head .when { color: var(--fg-muted); }
.post .text { white-space: pre-wrap; overflow-wrap: anywhere; }
.post .pinned { font-size: .8rem; }
.post .acts { display: flex; gap: .3rem; margin-top: .25rem; }
.post.gone .text { color: var(--fg-muted); font-style: italic; }
.replies { margin-top: .5rem; padding-left: .7rem; border-left: 2px solid var(--line); }

/* ---- mapa ---------------------------------------------------------------- */
.mapbox { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--sp-sunken); }
.mapbox button { display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; }
.mapbox img { display: block; width: 100%; height: auto; }

/* ---- pätka --------------------------------------------------------------- */
.foot { margin-top: 1.5rem; padding-top: .8rem; border-top: 1px solid var(--line);
  font-size: .78rem; color: var(--fg-muted); display: flex; flex-wrap: wrap; gap: .3rem .8rem; align-items: center; }

/* Identifikácia prevádzkovateľa (handover 2026-09-07, § 3a ObZ) + odkazy na
   /ochrana-osobnych-udajov a /podmienky — na landingu, prihlásení, joine, admine,
   článkoch a v appke (obrazovka „Ja"). */
.operator, .operator-links { flex-basis: 100%; font-size: .74rem; color: var(--fg-muted); margin: .2rem 0 0; }
.operator a, .operator-links a { color: inherit; }
.lp-foot { flex-direction: column; align-items: flex-start; }

/* ---- úzky displej -------------------------------------------------------- */
@media (max-width: 380px) {
  :root { --sp-pad: .75rem; }
  /* Podnadpis sa SKRÁTI, nezmizne. `display:none` bral na úzkom displeji (SE,
     bežný Android) jediný riadok, ktorý hovorí, KDE sa akcia koná — a práve tam
     je obrazovka najmenšia a človek najviac stratený. */
  .brand .s { max-width: 46vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .topnav button { min-width: 4.1rem; font-size: .68rem; }
}

/* ---- rozbaľovačka (filtre, návod na živú polohu) ------------------------- */
details > summary {
  display: flex; align-items: center; gap: .45rem;
  min-height: var(--sp-tap); padding: .35rem 0;
  font-family: var(--font-sans); font-weight: 700; font-size: .95rem;
  cursor: pointer; list-style: none;
}
details > summary::-webkit-details-marker { display: none; }
/* Šípka je vlastná, nie natívny trojuholník — natívny sa nedá zarovnať a na
   iOS mizne. `content` je znak, nie obrázok, takže drží farbu textu. */
details > summary::after { content: "▾"; margin-left: auto; color: var(--fg-muted); transition: transform .12s ease; }
details[open] > summary::after { transform: rotate(180deg); }
details > summary:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/* ---- krátky oznam ------------------------------------------------------- */
/* `position: fixed` nad všetkým, ale POD panelom detailu (z-index 40) — oznam
   nesmie prekryť tlačidlo, ktorým sa panel zatvára. */
.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(1rem + var(--sp-safe-b)); z-index: 35;
  max-width: calc(100vw - 2rem);
  background: var(--bg-raised); color: var(--fg);
  border: 1px solid var(--line-strong); border-left: 4px solid var(--ok-fill);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-2);
  padding: .6rem .9rem; font-size: .9rem; font-weight: 700;
}
.toast[hidden] { display: none; }
.toast.bad { border-left-color: var(--danger-fill); color: var(--danger-fg); }

.pinned { margin: .3rem 0; display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }

/* ---- vzhľad: kto čo vlastní ----------------------------------------------
   🔴 TIETO DVA RIADKY SÚ JEDINÉ, ČO DRŽÍ PREPÍNAČ VZHĽADU FUNKČNÝ. Nemaž ich.

   Deliaca čiara s profilom:
     profil  →  `:root { color-scheme: light dark; }`  (jeho vlastný default,
                bez ktorého jeho `light-dark()` nedáva zmysel)
     engine  →  tieto dve pravidlá, ktoré tému VYNÚTIA

   Prečo v engine a nie v profile: profil, ktorý ich zabudne, by rozbil prepínač
   celej appke. Tu fungujú pre každý profil.

   ⚠️ 2026-08-22 boli chvíľu preč z OBOCH strán naraz — každá strana ich zmazala
   v presvedčení, že ich má tá druhá, a prepínač bol mŕtvy v oboch smeroch.
   Nič to nenahlásilo, lebo CSS na chýbajúce pravidlo nenadáva. Preto na to
   existuje test v `tests/klient-render.test.mjs`. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

/* Rolové hodnoty vlastní profil — nesie ich v `light-dark()` a preklápa ich
   práve podľa `color-scheme` nastaveného vyššie.

   Tu predtým bol mostík, ktorý odvodzoval tmavé role `color-mix`-om, kým profil
   vedel len `@media (prefers-color-scheme: dark)`. Zmazaný 2026-08-22, keď
   profil prešiel na `light-dark()`: tmavá paleta bola dovtedy napísaná dvakrát —
   raz zmeraná v profile, raz odvodená tu — a pri VYNÚTENEJ tmavej platila tá
   odvodená. Teraz je na jednom mieste a je zmeraná. Nevracaj to sem.

   `--sp-*` fallbacky v `:root` hore ostávajú: sú pre budúce profily, ktoré tie
   tokeny nedoplnia. `lubomier` ich má, takže sa neuplatnia. */

/* ---- zrušená položka (§5.1) ----------------------------------------------
   Stav sa NIKDY nehlási iba prečiarknutím ani iba farbou. Prečiarknutie je
   druhý signál; prvý je odznak so slovom „ZRUŠENÉ“ a poznámka v detaile. */
.badge.cancel { color: var(--danger-fg); background: var(--sp-danger-wash); letter-spacing: .03em; }
.row.gone { opacity: .78; border-left: 4px solid var(--line-strong); padding-left: .5rem; }
.row.gone .ttl, h2.gone { text-decoration: line-through; text-decoration-thickness: 2px; }
/* Prečiarknutý text nesmie spadnúť pod 4,5:1 — `opacity` na riadku sa preto
   nesmie prehnať a samotný titulok si drží plnú farbu textu. */
.row.gone .ttl { color: var(--fg); }

/* Fotka v detaile položky. `aspect-ratio` drží miesto ešte pred stiahnutím, takže
   sa obsah pod ňou nepreskladá, keď obrázok dorazí. */
.itemimg { display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover;
  border-radius: var(--radius); background: var(--sp-sunken); }

/* ---- dve triedy dôveryhodnosti (§14.2) -----------------------------------
   Neoverený hosť si meno píše sám. Označuje sa TROMI signálmi naraz, lebo
   jeden z nich vždy niekomu vypadne: slovom v odznaku, ikonou v ňom, a
   prerušovaným rámikom avatara. Farba je až štvrtá v poradí, nikdy jediná. */
.badge.unverified { color: var(--warn-fg); background: var(--sp-warn-wash); }
.ava.unverified { border: 2px dashed var(--warn-fg); }
/* Poznámka pod menami v „kto ide na to isté“ — vysvetľuje „?“ pri mene. */
.who .unote { display: inline-flex; align-items: center; gap: .2rem;
  font-size: .74rem; color: var(--fg-muted); }

/* ---- odznak ceny (§16) ---------------------------------------------------
   Druhotná informácia. Sedí v riadku metadát pod názvom, nie pri ňom — nesmie
   prehlušiť názov predstavenia. Preto tichý podklad a normálna váha číslic,
   nie akcentová výplň. Suma je text, takže sa nehlási iba farbou; `lístok`
   nesie slovo aj ikonu. */
.badge.price { color: var(--fg-soft); background: var(--sp-sunken);
  border-color: var(--line-strong); font-weight: 700;
  font-variant-numeric: tabular-nums; }

/* „Zadarmo“ a „25 €“ nesú INÝ DRUH rozhodnutia, tak vyzerajú inak.
   Cena sa ČÍTA (25 € vs 10 € je rozdiel v detaile) → tichý obrys.
   „Zadarmo“ sa SKENUJE (binárne: zaoberať sa lístkom, áno či nie) → plná výplň,
   ktorú oko chytí v pohybe, na mobile, na slnku.

   Prečo to nie je šum, keď je ich na obrazovke 37 zo 68: odznak sedí hneď za
   časom, ktorý má pevnú šírku, takže sa v stĺpci zarovná do zvislice. Šum
   nerobí výplň, ale nepravidelné umiestnenie.

   `--on-ok` na `--ok-fill` je 5,57:1 (zmerané v profile) a výplň je v oboch
   témach tá istá. Slovo aj ikona nesú význam samy — farba len pomáha nájsť ich. */
.badge.free {
  color: var(--on-ok); background: var(--ok-fill); border-color: var(--ok-fill);
  font-weight: 800;
}

/* „Čo sa práve deje“ — sedí na Domove nad tým, kam idem, tak nesmie zabrať pol
   obrazovky. Riadky sú kompaktnejšie než v zozname programu. */
.nowblock .row { padding: .45rem 0; }
.nowblock .cardhead { margin-bottom: .35rem; }
/* „Idem" v „Práve beží" je MALÝ ČIP, nie 64px palcový cieľ z programu — tu ich
   vedľa seba beží veľa (na festivale ~9 scén naraz) a musia sa zmestiť. Ikona
   AJ slovo ostávajú (§3); mení sa len veľkosť. Plný cieľ je v zozname programu. */
.nowblock .row .pick .pk { min-height: 40px; min-width: 0; padding: .25rem .5rem; font-size: .72rem; }
.nowblock .row .pick .pk svg { width: 15px; height: 15px; }

/* „Vypredané“ — nie je to chyba, je to nedostupnosť. Preto rodina `danger`,
   ale bez plnej výplne: nemá kričať ako výstraha, má sa dať prečítať pri
   scrollovaní a povedať „tu už netreba klikať“. Cena vedľa neho ostáva
   („25 € · vypredané“ povie viac než holé „vypredané“).
   `--danger-fg` na `--danger-wash` je 6,21:1 svetlá / 6,11:1 tmavá (zmerané
   v profile). Slovo aj ikona nesú význam samy — farba len pomáha nájsť ich. */
.badge.soldout { color: var(--danger-fg); background: var(--sp-danger-wash);
  border-color: var(--danger-fg); font-weight: 800; }

/* ---- upozornenie na novú verziu ------------------------------------------
   Pruh pod hornou lištou, nie prekrytie: človek môže byť uprostred označovania
   programu a nič mu nesmie zavadzať. Reload sa NEROBÍ sám — je to tlačidlo. */
#verbar[hidden] { display: none; }
.verbar {
  display: flex; align-items: center; gap: .5rem;
  max-width: var(--sp-max); margin: 0 auto;
  padding: .5rem var(--sp-pad);
  background: var(--sp-accent-wash); color: var(--accent-fg);
  border-bottom: 1px solid var(--line);
  font-size: .88rem; font-weight: 700;
}
.verbar .bt { flex: 1 1 auto; min-width: 0; }
.verbar .iconbtn { width: 34px; height: 34px; background: none; border-color: transparent; }

/* ---- hlavička akcie -------------------------------------------------------
   Logo akcie je CUDZIA značka — nefarbí sa a neaplikujú sa naň filtre. Preto
   žiadny `filter`, žiadny `mix-blend-mode`. Rám a pozadie sú neutrálne, aby
   logo s priehľadnosťou nezaniklo na tmavom ani na svetlom podklade.

   B6: blok už nenesie názov ani podnadpis akcie — tie stoja v `.topbar`, ktorá
   je `sticky`. Ostalo logo a dátum. Preto tu NIE JE pravidlo pre `h1`: nadpis je
   `sr-only` (osnova pre čítačku), takže veľkosť písma na ňom nič neznamená.

   Logo je 56 px na každej šírke. Bolo 72 px so zlomom na 56 px pod 380 px.
   Odmerané po zmene na 390 × 844: pri 72 px ostane vedľa dlaždice jediný riadok
   s dátumom a blok vyzerá ako obrázok, ktorý sa nedokreslil; pri 56 px je to
   odznak a veta. Nápis v logu Uprisingu je nečitateľný v oboch veľkostiach —
   od 72 px sa teda nekupuje nič. Cena bloku: 101 px → 56 px. */
.evhead { display: flex; align-items: center; gap: .8rem; }
.evhead .bt { flex: 1 1 auto; min-width: 0; }
/* Dátum je jediné dieťa v toku (`h1` je mimo neho), takže žiadny odstup zhora. */
.evhead .bt p { margin: 0; }
.evlogo { flex: none; display: block; }
.evlogo img { display: block; width: 56px; height: 56px; object-fit: contain;
  border-radius: var(--radius-sm); background: var(--bg-raised); }

/* ============================================================================
   Serverom vykreslené stránky bez `#app`: rozcestník (`/`), pozvánka
   (`/i/<token>`), prihlásenie a admin. Majú `<body class="plain">` a vlastné
   markup od `jadra`.

   Prečo sú tieto triedy TU a nie v `admin.css`: `.note`, `.mini`, `.events`,
   `.sub` a `.gbtn` používa aj **pozývacia stránka** — teda prvá vec, ktorú
   uvidí každý pozvaný človek. Bez nich bola neštýlovaná. Admin má vlastný
   `admin.css` len na to, čo je naozaj len jeho (tabuľky panela).
   ============================================================================ */
body.plain {
  background: var(--bg); color: var(--fg);
  font-family: var(--font-text); line-height: 1.55;
}
body.plain .wrap {
  max-width: 46rem; margin: 0 auto;
  padding: calc(1.5rem + var(--sp-safe-t)) var(--sp-pad) calc(3rem + var(--sp-safe-b));
}
body.plain h1 { font-family: var(--font-sans); font-size: 1.5rem; margin: 0 0 .3rem; }
body.plain h2 { font-family: var(--font-sans); font-size: 1.1rem; margin: 1.6rem 0 .5rem; }
body.plain .sub { color: var(--fg-muted); margin: 0 0 1.2rem; }
body.plain .card { margin: 0 0 1rem; }
body.plain label { display: block; font-weight: 700; font-size: .9rem; margin: .8rem 0 .25rem; }
body.plain code { font-family: var(--font-mono); font-size: .86rem;
  background: var(--sp-sunken); padding: .05rem .3rem; border-radius: var(--radius-sm); }

/* Vysvetľujúci text pod ovládacím prvkom — tichý, ale čitateľný.
   `--fg-muted` je 4,9:1 na `--bg`, čiže nad hranicou aj pri tejto veľkosti. */
.note { color: var(--fg-muted); font-size: .88rem; margin: .4rem 0 1rem; }
.mini { font-size: .82rem; }

/* Drobná akcia — v admine je to `<button>`, na rozcestníku `<a>`. Obe musia
   vyzerať a fungovať rovnako a obe potrebujú cieľ pre palec. */
a.mini, button.mini {
  display: inline-flex; align-items: center; gap: .3rem;
  min-height: 34px; padding: .2rem .6rem;
  border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
  background: var(--bg-raised); color: var(--fg-soft);
  font: inherit; font-size: .82rem; font-weight: 700;
  text-decoration: none; cursor: pointer;
}
a.mini:hover, button.mini:hover { color: var(--fg); border-color: var(--fg-muted); }

/* Varovanie. NIE IBA FARBOU — nesie znak `⚠`, ktorý je text, nie dekorácia.
   V admine je za tým napríklad „toto pole si menil ručne, prevezmi ho vedome“. */
.warn { color: var(--warn-fg); font-weight: 700; }
.warn::before { content: "⚠ "; }
.card.warn {
  color: var(--warn-fg); background: var(--sp-warn-wash);
  border-color: var(--warn-fg); font-weight: 400;
}
.card.warn::before { content: none; }
.card.warn p { color: var(--fg); font-weight: 700; }
.card.warn p::before { content: "⚠ "; color: var(--warn-fg); }

/* Hlavné tlačidlo servera (vstup do akcie, Google, hosť). */
.gbtn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  width: 100%; min-height: var(--sp-tap); padding: .65rem 1rem;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--bg-raised); color: var(--fg);
  font: inherit; font-weight: 700; font-size: .98rem;
  text-decoration: none; cursor: pointer; text-align: center;
}
.gbtn:hover { border-color: var(--fg-muted); }
.gbtn.primary { background: var(--accent-fill); color: var(--on-accent); border-color: var(--accent-fill); }

/* §14.3 — hosť je primárna cesta, Google je ponuka VEDĽA nej, nie menší odkaz
   pod ňou. Preto rovnaká šírka aj rovnaká veľkosť písma; líšia sa výplňou
   (hosť má akcent) a dôvodom pod tlačidlom, nie hierarchiou. */
body.plain form { margin: 0 0 1rem; }
body.plain form.guest, body.plain form.google {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: .9rem; background: var(--bg-raised);
}
body.plain form.google small { display: block; margin-top: .4rem;
  color: var(--fg-muted); font-size: .84rem; text-align: center; }
body.plain input[type=text], body.plain input:not([type]) { margin-bottom: .6rem; }

/* Zoznam akcií na rozcestníku aj v admine. */
.events { list-style: none; margin: 0 0 1rem; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.events li {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg-raised); padding: .7rem .9rem;
}
.events li > a:first-child {
  flex: 1 1 12rem; min-width: 0; display: flex; flex-direction: column; gap: .1rem;
  color: inherit; text-decoration: none; font-weight: 700;
}
.events li > a:first-child:hover { text-decoration: underline; }
.events small { display: block; font-weight: 400; color: var(--fg-muted); font-size: .84rem; }

/* Stránky, ktoré nesú jedno rozhodnutie (pozvánka, prihlásenie), sú užšie —
   na mobile je to jedno, na notebooku sa inak text roztiahne cez celú šírku
   a stratí sa v ňom to podstatné. Rozcestník ostáva široký, je to zoznam. */
body.plain .wrap.join, body.plain .wrap.login { max-width: 32rem; }

/* --- Heslová cesta na pozývacej stránke (§14, tretia cesta dnu) -------------
   Zbalená v `<details>` zámerne: hosť je primárna cesta (§14.3) a heslo nesmie
   pred ňou stáť. Kto ho chce, rozklikne. */
.sp-pass { margin: 14px 0; text-align: left; }
.sp-pass > summary { cursor: pointer; font-weight: 600; }
.sp-pass form.pass { margin-top: 10px; }
.sp-pass label { display: block; margin-top: 8px; font-weight: 600; }
.sp-pass input { width: 100%; box-sizing: border-box; }
.sp-pass small { display: block; margin-top: 4px; opacity: .7; }
.pass-btns { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.pass-btns .gbtn { flex: 1 1 auto; }

/* ============================================================================
   Landing na koreni (`/`, `index.php`, vetva bez akcie).

   Rozcestník má dve podoby a obe nesú `.lp`: neprihlásený dostane celý landing
   (čo to je, ako to funguje, príbeh mena), prihlásený kompaktnú hlavičku a hneď
   pod ňou svoje akcie — príbeh mu ostáva zložený pod `.lp-why`.

   ⛔ Ani tu nie je konkrétna farba. `--accent-fill` je VÝPLŇ (2,6:1 na svetlom
   podklade), takže je pod číslom kroku, kde nesie vlastný `--on-accent` popis,
   a pod 4 px lištou, za ktorou je navyše `--bg-raised`. Text v akcente ide vždy
   cez `--accent-fg`, ktorý je meraný proti všetkým trom podkladom.

   Žiadna animácia. Nie je čo tlmiť pri `prefers-reduced-motion` — a globálne
   pravidlo hore ju aj tak vypína.
   ============================================================================ */

/* Užšie než ostatný rozcestník: `body.plain .wrap` má 46rem, čo je na súvislý
   text ~95 znakov na riadok. Zoznam akcií sa v 38rem nezlomí, odsek áno. */
body.plain .wrap.lp { max-width: 38rem; }

.lp-hero { text-align: center; padding: 1rem 0 .5rem; }
.lp-mark { display: block; margin: 0 auto .8rem; width: 104px; height: 104px; }

/* Titulok je meno z brand profilu — sádže sa ako wordmark: displejové písmo,
   800 a preloženie, presne ako `logo.png` v profile. Verzálky robí CSS, takže
   čítačke aj `<title>` ostáva meno tak, ako je v `brand.json`. */
body.plain .wrap.lp h1 {
  font-family: var(--font-sans); font-weight: 800;
  font-size: 1.5rem; letter-spacing: .12em; text-transform: uppercase;
  margin: 0 0 .6rem;
}
.lp-hero .sub { margin-bottom: .8rem; }

/* Sľub, nie meno, je najväčší text na stránke. */
.lp-lede {
  font-family: var(--font-sans); font-weight: 700; line-height: 1.2;
  font-size: clamp(1.4rem, 6.5vw, 1.95rem);
  margin: 0 0 .7rem; text-wrap: balance;
}
.lp-sub { color: var(--fg-soft); max-width: 30rem; margin: 0 auto; }

.lp-cta { text-align: center; margin: 1.4rem 0 .5rem; }
.lp-cta .gbtn { margin-top: .6rem; }
.lp-cta .note { margin-bottom: 0; }

/* Kroky. Číslo je vo výplni akcentu a nesie vlastný popis (`--on-accent`),
   takže neplatí výhrada k hrane výplne z profilu. */
.lp-steps { list-style: none; counter-reset: lp; margin: 0 0 1.4rem; padding: 0; }
.lp-steps > li {
  counter-increment: lp; position: relative;
  padding: 0 0 0 2.6rem; margin: 0 0 .9rem;
}
.lp-steps > li::before {
  content: counter(lp);
  position: absolute; left: 0; top: -.1rem;
  width: 1.8rem; height: 1.8rem; border-radius: var(--radius-pill);
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-fill); color: var(--on-accent);
  font-family: var(--font-sans); font-weight: 800; font-size: .95rem;
}

/* Odrážka je kruh — značka je kruh a zoznam je najmenšie miesto, kde sa to dá
   zopakovať. Farba je `--accent-fg`, nie výplň: je to malý tvar bez popisu. */
.lp-list { list-style: none; margin: 0 0 1.4rem; padding: 0; }
.lp-list > li { position: relative; padding: 0 0 0 1.3rem; margin: 0 0 .7rem; }
.lp-list > li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: .45rem; height: .45rem; border-radius: var(--radius-pill);
  background: var(--accent-fg);
}

/* §27 — tri typy akcie ako tri karty. Na mobile pod sebou, na širokom vedľa seba
   (blok v `@media` nižšie). Zoznam v karte je posledný prvok, spodný okraj by len
   nafúkol kartu. */
.lp-intro { margin: 0 0 .9rem; }
.lp-tvary { display: grid; gap: .8rem; margin: 0 0 1.4rem; }
/* `body.plain .card` dáva kartám spodný okraj; v mriežke medzeru drží `gap`. */
body.plain .lp-tvary > .lp-tvar { margin: 0; }
.card.lp-tvar > h3 { margin: 0 0 .6rem; }
.lp-tvar .lp-list { margin: 0; }
.lp-tvar .lp-list > li:last-child { margin-bottom: 0; }
.lp-tvary + h3 { margin: 0 0 .6rem; }

/* Príbeh mena. Vlastná kapitola, nie ďalší odsek: teplý papier, lišta v akcente
   a za ňou vlastný podklad — hrana výplne tu teda nenesie význam sama. */
.lp-name {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-left: 4px solid var(--accent-fill);
  border-radius: var(--radius);
  padding: var(--sp-pad);
  margin: 0 0 1.4rem;
}
.lp-name h2 { margin-top: 0; }

/* Grécke slovo. Profil má self-hostované len latin a latin-ext, takže grécke
   znaky vykreslí systémové písmo zo záložného radu — je to vedomé, nie chyba. */
.lp-greek {
  font-family: var(--font-sans); font-weight: 700;
  font-size: clamp(1.6rem, 7vw, 2.2rem); line-height: 1.15;
  letter-spacing: .02em; color: var(--accent-fg);
  margin: 0 0 .6rem;
}
/* Odbočka o latinčine — tichšia než hlavný výklad, ale stále nad 4,5:1. */
.lp-aside {
  color: var(--fg-muted); font-size: .92rem;
  border-left: 2px solid var(--line-strong);
  padding-left: .8rem; margin: 0 0 .8rem;
}

.lp-znak { display: flex; align-items: flex-start; gap: .9rem; flex-wrap: wrap; }
.lp-znak img { flex: none; width: 72px; height: 72px; }
/* Základ 16rem, nie 14: na 390 px telefóne ostane vedľa 72 px značky len ~240 px
   a text sa do stĺpca zalomí po troch slovách. Pri 16rem sa preto pod ňu zalomí
   celý odsek a na notebooku, kde je miesta dosť, ostane vedľa. */
.lp-znak p { flex: 1 1 16rem; min-width: 0; margin: 0; }

/* Stav projektu na konci — oddelený čiarou, aby sa nečítal ako ďalšia funkcia. */
.lp-status { border-top: 1px solid var(--line); padding-top: .9rem; margin-top: 1.6rem; }

/* Kompaktná hlavička prihláseného: prišiel po svoje akcie, nie po manifest. */
.lp-head { display: flex; align-items: center; gap: .7rem; margin-bottom: 1.2rem; }
.lp-head .lp-mark { margin: 0; width: 44px; height: 44px; }
.lp-head h1 { margin-bottom: 0; }
.lp-head .sub { margin: .15rem 0 0; }

/* Príbeh mena pre prihláseného — zložený, aby nestál nad zoznamom akcií. */
.lp-why { margin: 1.2rem 0; }
/* `details > summary` vyššie už rieši cieľ pre palec, displejové písmo aj vlastnú
   šípku vpravo — tá je konvencia enginu, nie náhoda. Tu sa dopĺňa jediné, čím sa
   tento riadok líši: je to odkaz do príbehu, nie filter, tak nesie akcent. */
.lp-why > summary { color: var(--accent-fg); }
.lp-why .lp-greek { font-size: clamp(1.3rem, 6vw, 1.7rem); }

@media (max-width: 380px) {
  .lp-mark { width: 84px; height: 84px; }
  .lp-znak img { width: 56px; height: 56px; }
}

/* ============================================================================
   B8 — päť nenakreslených obrazoviek.

   Kmeň, Nástenka, Ja, Mapa a Fotky nenakreslil ani jeden z troch smerov, takže
   palcová geometria z B2 do nich nikdy nedorazila. Toto je PRENOS JAZYKA smeru
   „Jednou rukou“, nie prepis logiky: podlaha cieľa 44 px (`--sp-tap`), hlavný
   cieľ 56 px, meno položky 19 px, samostatný text 15 px, a vysvetlenie POD
   odpoveďou. Žiadny nový reťazec, žiadna nová rola v profile.

   Namerané v Chromiu na 390 × 844 (svetlá téma, dáta Uprisingu) PRED zmenou:
   Kmeň 11 cieľov pod 44 px · Nástenka 8 · Ja 13 (z toho 3 prepínače 52 × 30)
   · Mapa 9 · Fotky 8 (v `gallery.css`).

   Každý blok je scopovaný triedou KOREŇA pohľadu, takže sa dá vrátiť sám:
   `.tribewrap`, `.boardwrap`, `.mewrap`, `.mapview`. Spoločný je jediný
   primitív — `.row.person`.
   ============================================================================ */

/* ---- B8.1 + B8.4: riadok človeka -------------------------------------------
   Kmeň aj „Kde je kmeň“ na Mape kreslia človeka tou istou funkciou
   (`contactHtml`), preto je `.row.person` spoločná. Riadok programu palcovú
   geometriu dostal v B2; riadok človeka ju nemal — kontaktné tlačidlá merali
   36 px, teda pod podlahou. Celý riadok cieľom NIE JE a nesmie sa ním stať:
   detail človeka neexistuje, takže by to bol terč, ktorý nikam nevedie. */
.row.person { padding: .7rem 0; }
.row.person .grow > b { font-family: var(--font-sans); font-size: 1.1875rem; line-height: 1.2; }
.row.person .meta { font-size: .9375rem; gap: .2rem .6rem; margin-top: .15rem; }
.row.person .badge { font-size: .875rem; padding: .2rem .5rem; }
/* Odsadenie bolo do B8 v `style` priamo v `contactHtml` — teda mimo dosahu
   ktoréhokoľvek pravidla. Tu je preto, aby riadok mal jedno miesto pravdy. */
.row.person .btnrow { gap: .4rem; margin-top: .45rem; }
/* 🔴 Dvíha sa LEN výška. Výplň ani písmo nie — a je to zmerané, nie odhadnuté.
   Pri `padding: .45rem .8rem` + `font-size: .88rem` (o 0,96 px väčšie písmo) sa
   „pri Arena Stage (približne) · pred 30 min“ prestane zmestiť do 288 px širokej
   `.grow` a zalomí sa: z 36 px jednoriadkového tlačidla je 70 px dvojriadkový
   blok na celú šírku, ktorý v riadku kričí najhlasnejšie, hoci je to popiska.
   Kmeň so 6 ľuďmi narástol na 1 509 px. Bez tých dvoch deklarácií rastie na
   1 436 px, ciele sú rovnako 44 px a nezalamuje sa ani jedno tlačidlo. */
.row.person .btnrow .btn.sm { min-height: var(--sp-tap); }

/* Vysvetlenie ide POD odpoveď, a je to poznámka pod čiarou, nie odsek. Čiara
   nad ňou hovorí „zoznam sa skončil“ — bez nej by splynula s posledným riadkom. */
.posnote { margin-top: .9rem; padding-top: .6rem; border-top: 1px solid var(--line); }

/* ---- B8.1: Kmeň ------------------------------------------------------------
   Iba podlaha — pre „Pozvať ďalších“ (namerané 131 × 36). Výplň sa NEDVÍHA:
   toto pravidlo trafí aj kontaktné tlačidlá v `.row.person` a prebilo by im
   výplň cez kaskádu, lebo `.row.person …` nad ňou deklaruje len výšku. */
.tribewrap .btnrow .btn.sm { min-height: var(--sp-tap); }

/* ---- B8.2: Nástenka --------------------------------------------------------
   Iba podlaha cieľa. Pole na písanie ostáva 80 px zámerne: zbaliť ho a nechať
   vyrásť pri zameraní by ušetrilo 26 px z 844 (3 %) a zaplatilo by sa skokom
   rozloženia pod prstom presne v okamihu ťuknutia — a na iOS s otvorenou
   klávesnicou to nemá ako overiť nikto z nás. Tri percentá to nestoja.
   Text príspevku sa nedvíha, lebo dvíhať netreba: `.post .text` dedí 16 px
   z tela a 15 px podlahu samostatného textu už spĺňa. */
.boardwrap .btnrow .btn.sm,
.boardwrap .post .acts .btn.sm,
.boardwrap .post .pinned .btn.sm { min-height: var(--sp-tap); }

/* ---- B8.3: Ja --------------------------------------------------------------
   🔴 Riadok sa tu cieľom NESTÁVA. Smer „Jednou rukou“ hovorí: hlavná plocha
   riadku smie byť len VRATNÁ akcia. Tu je akciou opt-in do zdieľania telefónu
   a polohy — omyl palcom by vydal cudzie číslo. Cieľom ostáva prepínač; len
   sa dvíha z 52 × 30 na 60 × 44 a jazdec rastie s ním. */
.mewrap .switchrow { padding: .7rem 0; }
.mewrap .switchrow .switch { width: 60px; height: var(--sp-tap); }
.mewrap .switchrow .switch::after { top: 6px; left: 4px; width: 30px; height: 30px; }
/* Dráha jazdca sa MUSÍ prepísať spolu so škatuľou. `box-sizing: border-box`
   (:41) + rám 1 px ⇒ vnútro je 58 px, takže dráha = 58 − 4 − 30 − 4 = 20 px.
   Základné pravidlo (:295) má 22 px a odvádza ho z vnútra 50 px. Keby sa tie
   dve rozišli, prepínač by vyzeral vypnutý, keď je zapnutý — a pri telefónnom
   čísle a polohe je to najhoršia možná lož, akú vie appka povedať. */
.mewrap .switchrow .switch[aria-checked="true"]::after { transform: translateX(20px); }
.mewrap .switchrow .txt b { font-size: 1rem; }
/* `<small>` bez pravidla je 12,8 px — a je to celá veta, ktorá hovorí, čo sa
   zdieľaním stane. Samostatný text má podlahu 15 px, rovnako ako `.meta`. */
.mewrap .switchrow .txt small { font-size: .9375rem; }
.mewrap .card > .row .grow > b { font-family: var(--font-sans); font-size: 1.1875rem; line-height: 1.2; }
.mewrap .btn.sm { min-height: var(--sp-tap); }
/* Voľba, ktorá sa ťuká raz za mesiac, ale ťuká sa palcom. Pri „Čoho sa to týka“
   je cieľom `<label>`, nie natívne rádio v ňom — to smie ostať malé, lebo celá
   menovka je klikateľná (`for=`). Preto sa `.chips` v „Ja“ dvíhajú tu a nie
   globálne: `.daytabs .chip` má 48 px už z B2 a filtre programu do B8 nepatria.
   Dvíha sa LEN výška, rovnako ako pri `.row.person`: pri `padding: .5rem .85rem`
   + `font-size: .88rem` sa trojica „Podľa telefónu / Svetlá / Tmavá“ prestane
   zmestiť do riadku a „Ja“ narastie o 54 px za nič. Zvislá výplň je pod
   `min-height` aj tak bezvýznamná. */
.mewrap .chip { min-height: 48px; }
.mewrap .chip input[type="radio"] { width: 18px; height: 18px; accent-color: var(--accent-fill); }
.mewrap .foot { margin-top: 1.1rem; }

/* ---- B8.4: Mapa ------------------------------------------------------------
   `#livemap` a `map.css` sa nedotýkame — vlastní ich agent `mapa` (§19).
   „Otvoriť miesto v Google Mapách“ je na tejto obrazovke HLAVNÝ cieľ, teda
   56 px, nie 44. Malé tlačidlá dvíhajú len výšku — z toho istého dôvodu ako
   v `.tribewrap`: to pravidlo trafí aj kontakty človeka v `.row.person`. */
.mapview .btn.wide { min-height: 56px; font-size: 1rem; }
.mapview .btnrow .btn.sm { min-height: var(--sp-tap); }

/* ---- MODUL POČASIE -------------------------------------------------------
   Vlastná obrazovka (`#/pocasie`) + jeden riadok na Domove, ktorý na ňu vedie.
   Farby sú výhradne role z profilu; graf má tri vlastné role, aby ich profil
   mohol prevziať bez zásahu sem (`--wx-temp`, `--wx-rain`, `--wx-wind`).
   Prahy nárazov (50 / 65 km/h) sa farbia `--warn-*` a `--danger-*` — sú to
   tie isté čísla, na ktorých server hlási výstrahu, nie iné odtiene toho istého. */

/* riadok na Domove — jeden dotyk, celý je odkazom na záložku */
.wxhome {
  display: flex; align-items: center; gap: .5rem; width: 100%;
  min-height: var(--sp-tap); padding: .55rem .7rem; margin: 0;
  font: inherit; text-align: left; cursor: pointer;
  color: var(--fg); background: var(--bg-raised);
  border: 1px solid var(--line); border-radius: var(--radius, 14px);
}
.wxhome:hover { border-color: var(--line-strong); }
.wxhome:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.wxhome .ic { flex: 0 0 auto; color: var(--fg-muted); }
.wxhome .wh-day { font-weight: 700; text-transform: capitalize; }
.wxhome .wh-t { font-weight: 800; font-variant-numeric: tabular-nums; }
/* miesto na Domove — ustúpi výstrahe, ale nezalomí riadok (§30.3c) */
.wxhome .wh-pl { flex: 0 1 auto; min-width: 0; font-size: .8rem; color: var(--fg-muted);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.wxhome .wh-warn { flex: 1 1 auto; min-width: 0; font-size: .8rem; color: var(--fg-muted);
  display: inline-flex; align-items: center; gap: .25rem; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.wxhome .wh-warn .ic { color: inherit; }
.wxhome.soft { border-color: var(--warn-fill); background: var(--sp-warn-wash); }
.wxhome.soft .wh-warn { color: var(--warn-fg); font-weight: 700; }
.wxhome.loud { border-color: var(--danger-fill); background: var(--sp-danger-wash); }
.wxhome.loud .wh-warn { color: var(--danger-fg); font-weight: 800; }

/* pruh „najbližšia hodina“ */
.wxnow { display: flex; align-items: center; flex-wrap: wrap; gap: .15rem .55rem; }
.wxnow .ic { flex: 0 0 auto; color: var(--fg-muted); }
.wxnow .t { font-size: 1.35rem; font-weight: 800; line-height: 1.15; white-space: nowrap; font-variant-numeric: tabular-nums; }
.wxnow .wn-when { font-weight: 700; color: var(--fg-soft); text-transform: capitalize; }
.wxnow .wn-pl { display: inline-flex; align-items: center; gap: .2rem; min-width: 0;
  font-size: .82rem; font-weight: 700; color: var(--fg-soft);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.wxnow .wn-pl .ic { flex: 0 0 auto; color: var(--fg-muted); }
.wxnow .wn-d { flex: 1 0 100%; font-size: .82rem; color: var(--fg-muted); line-height: 1.35; }

/* §30.3c — miesto, PRE KTORÉ predpoveď je. Bez neho je teplota číslo bez významu:
   na hrebeňovke je medzi dolinou a hrebeňom šesť stupňov. */
.wxplace { margin: .45rem 0 0; font-size: .82rem; font-weight: 700; color: var(--fg-soft); }
.wxplace .ic { vertical-align: -3px; margin-right: .2rem; color: var(--fg-muted); }

/* riadok „Aktualizované …“; `wxold` je jeho hlasná podoba (staré dáta) */
.wxupd { margin: .5rem 0 0; font-size: .78rem; color: var(--fg-muted); }
.wxupd .ic { vertical-align: -3px; margin-right: .2rem; }
.wxupd.wxold { display: flex; align-items: flex-start; gap: .35rem;
  font-size: .85rem; color: var(--warn-fg); background: var(--sp-warn-wash);
  padding: .5rem .6rem; border-radius: var(--radius-sm, 8px); }
.wxupd.wxold .ic { flex: 0 0 auto; margin: .1rem .1rem 0 0; }
.wxupd.wxold small { display: block; font-weight: 400; opacity: .9; }

/* banner výstrah — `loud` = aspoň jedna `severity: warn` */
.wbanner { display: flex; flex-direction: column; gap: .55rem;
  padding: .75rem .85rem; border-radius: var(--radius, 14px);
  color: var(--warn-fg); background: var(--sp-warn-wash); border: 1px solid var(--warn-fill); }
.wbanner.loud { color: var(--danger-fg); background: var(--sp-danger-wash); border-color: var(--danger-fill); }
.wbanner .wa { display: flex; gap: .55rem; align-items: flex-start; }
.wbanner .wa .ic { flex: 0 0 auto; margin-top: .1rem; }
.wbanner .bt { flex: 1 1 auto; min-width: 0; color: inherit; }
.wbanner .bt > b { display: block; font-size: 1rem; line-height: 1.25; }
.wbanner .wa-when { display: block; font-weight: 700; font-size: .85rem; text-transform: capitalize; }
.wbanner .bt > small { display: block; font-size: .85rem; opacity: .92; margin-top: .15rem; }
.wbanner .wa.wa-off { opacity: .55; }
.wbanner .wa-note { display: block; font-size: .75rem; font-weight: 700; opacity: .85; margin-top: .15rem; }
.wbanner .wa-btn { display: inline-flex; align-items: center; gap: .35rem;
  min-height: var(--sp-tap); margin-top: .35rem; padding: .3rem .6rem;
  font: inherit; font-size: .82rem; font-weight: 700; cursor: pointer;
  color: inherit; background: transparent; border: 1px solid currentColor; border-radius: var(--radius-pill, 999px); }
.wbanner .wa-btn:hover { background: var(--bg-raised); }
.wbanner .wa-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.wbanner .wa-stale { display: flex; align-items: center; gap: .35rem;
  font-size: .78rem; font-weight: 700; opacity: .9;
  padding-top: .5rem; border-top: 1px solid currentColor; }
.wbanner .wa-stale .ic { flex: 0 0 auto; }

/* čipy dní — vodorovný pás, prst ho roluje */
.wxdays { display: flex; gap: .4rem; overflow-x: auto; padding: .1rem 0 .4rem; scrollbar-width: none; }
.wxdays::-webkit-scrollbar { display: none; }
.wxday { flex: 0 0 auto; min-width: 62px; min-height: var(--sp-tap);
  display: flex; flex-direction: column; align-items: center; gap: .1rem;
  padding: .45rem .5rem; font: inherit; cursor: pointer;
  color: var(--fg); background: var(--bg-raised);
  border: 1px solid var(--line); border-radius: var(--radius-sm, 10px); }
.wxday:hover { border-color: var(--line-strong); }
.wxday:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.wxday.on { color: var(--on-accent); background: var(--accent-fill); border-color: var(--accent-fill); }
.wxday .wd { font-weight: 800; font-size: .8rem; }
.wxday .dt { font-size: .64rem; opacity: .78; font-variant-numeric: tabular-nums; }
.wxday .ic { margin: .1rem 0; }
.wxday .tmax { font-weight: 800; font-size: .84rem; font-variant-numeric: tabular-nums; }
.wxday .tmin { font-size: .72rem; opacity: .75; font-variant-numeric: tabular-nums; }
/* Meno miesta v čipe NIE JE vidieť — je v `.sr-only` pre čítačku (§30.3c). Malo tu
   `max-width: 88px` s `text-overflow`, lenže ellipsis reže až pri pretečení: krátke
   meno nechalo čip 62 px, dlhé ho roztiahlo na 88 px, a rad bol nerovný. Celé meno
   hovorí `.wxplace` pod čipmi, kde je naň priestor. */

/* nadmorská výška pri mieste — to, čo vysvetľuje rozdiel teplôt medzi dňami (§30.3d) */
.wxele { font-variant-numeric: tabular-nums; opacity: .75; }
/* „cez deň vystúpiš na…" — predpoveď platí pre koniec dňa, toto hovorí zvyšok (§30.3e) */
.wxpeak { display: flex; align-items: center; gap: .3rem; margin: .2rem 0 0;
  font-size: .82rem; color: var(--warn-fg); }

/* hodinový rozpis vybraného dňa */
.wxhours { margin: .3rem 0 0; }
.wxhour { display: flex; align-items: center; gap: .6rem; padding: .45rem .25rem; border-top: 1px solid var(--line); }
.wxhour:first-child { border-top: 0; }
.wxhour.now { background: var(--sp-accent-wash); border-radius: var(--radius-sm, 10px); border-top-color: transparent; }
.wxhour .wh-t { flex: 0 0 auto; min-width: 44px; font-weight: 700; font-variant-numeric: tabular-nums; }
.wxhour .ic { flex: 0 0 auto; color: var(--fg-soft); }
.wxhour .wh-temp { flex: 0 0 auto; min-width: 40px; font-weight: 800; font-variant-numeric: tabular-nums; }
.wxhour .wh-rain { flex: 1 1 auto; min-width: 0; font-size: .8rem; color: var(--second-fg); font-variant-numeric: tabular-nums; }
.wxhour .wh-wind { flex: 0 0 auto; display: inline-flex; align-items: center; gap: .25rem;
  font-size: .8rem; color: var(--fg-muted); font-variant-numeric: tabular-nums; }
.wxhour .wh-wind .ic { color: inherit; }
.wxhour .wh-wind .u { font-weight: 400; opacity: .72; }   /* jednotka decentne, nech číslo dominuje */
.wxhour .wh-wind.mod { color: var(--warn-fg); font-weight: 700; }
.wxhour .wh-wind.strong { color: var(--danger-fg); font-weight: 800; }

/* legenda nárazov vetra */
.wxscale > summary { font-weight: 700; }
.wsc { display: flex; align-items: baseline; gap: .6rem; padding: .35rem 0; border-top: 1px solid var(--line); font-size: .88rem; }
.wsc:first-of-type { border-top: 0; }
.wsv { flex: 0 0 auto; min-width: 68px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--fg-muted); }
.wsc.mod .wsv { color: var(--warn-fg); }
.wsc.strong .wsv { color: var(--danger-fg); }

/* grafy (inline SVG) */
.wxchart { margin: .4rem 0 .2rem; padding: .45rem .5rem .3rem;
  background: var(--sp-sunken); border: 1px solid var(--line); border-radius: var(--radius-sm, 12px); }
.wxchart svg { display: block; width: 100%; height: auto; }
.wxchart text { font-family: inherit; font-size: 9.5px; fill: var(--fg-muted); }
.wxlgnd { display: flex; flex-wrap: wrap; gap: .25rem .8rem; margin: 0 0 .25rem .1rem; }
.wxlg { display: inline-flex; align-items: center; gap: .3rem; font-size: .7rem; font-weight: 700; color: var(--fg-muted); }
.wxlg::before { content: ""; width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.wxlg-temp::before { background: var(--wx-temp, var(--accent-fill)); }
.wxlg-rain::before { background: var(--wx-rain, var(--second-fill)); }
.wxlg-wind::before { background: var(--wx-wind, var(--ok-fill)); }
.wxchart .wxtline { fill: none; stroke: var(--wx-temp, var(--accent-fill)); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.wxchart .wxtarea { fill: var(--wx-temp, var(--accent-fill)); opacity: .16; stroke: none; }
.wxchart .wxtdot { fill: var(--wx-temp, var(--accent-fill)); }
.wxchart .wxrange { fill: var(--wx-temp, var(--accent-fill)); }
.wxchart .wxbar { fill: var(--wx-rain, var(--second-fill)); }
.wxchart .wxwline { fill: none; stroke: var(--wx-wind, var(--ok-fill)); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.wxchart .wxwarea { fill: var(--wx-wind, var(--ok-fill)); opacity: .15; stroke: none; }
.wxchart .wxwdot { fill: var(--wx-wind, var(--ok-fill)); }
.wxchart .wxwdot.strong { fill: var(--danger-fill); }
.wxchart .wxthr { stroke: var(--danger-fill); stroke-width: 1; stroke-dasharray: 2 3; opacity: .6; }
.wxchart .wxaxis { stroke: var(--line-strong); stroke-width: 1; }
.wxchart .wxgrid { stroke: var(--line); stroke-width: 1; stroke-dasharray: 2 3; }
.wxchart .wxnowline { stroke: var(--accent-fill); stroke-width: 1; stroke-dasharray: 3 2; }
.wxchart .wxnowdot { fill: var(--accent-fill); }
.wxchart .wxval { fill: var(--fg); font-weight: 700; }
.wxchart .wxval.wxloud { fill: var(--danger-fg); }
.wxchart .wxdim { fill: var(--fg-muted); }
.wxchart .wxmuted { fill: var(--fg-muted); opacity: .8; }
.wxchart .wxtick { fill: var(--fg-muted); text-transform: capitalize; }
.wxchart .wxtick.on { fill: var(--fg); font-weight: 800; }
.wxchart .wxhit { fill: transparent; cursor: pointer; }

/* ---- B7a: desktop — paluba sa postaví na výšku ---------------------------
   Dnes leží navigácia dole cez celú šírku aj na 1280 px, kde palec na obrazovku
   nesiaha vôbec — je to myš. Tu sa `.topnav` otočí do ľavého pruhu.

   DOM sa NEMENÍ. Používa sa presne ten istý mechanizmus ako v B1: `.topnav` je
   `position: fixed`, takže uniká `sticky` predkovi a je jedno, kde v strome leží.
   🔴 Platí tá istá poistka ako v B1 — keby na `body`, `#app` alebo `.topbar`
   pribudol `transform`, pruh spadne do toku a bude uprostred stránky. Vtedy sa
   presúva DOM, nie hľadá obchádzka v CSS.

   🔴 Obsah sa tu ZÁMERNE NEROZŠIRUJE. `--sp-max` (760 px) ostáva, len sa centruje
   v zvyšku po pruhu: 1280 − 248 = 1032, teda 136 px po stranách namiesto dnešných
   260. Rozšíriť jediný stĺpec na 1030 px by znamenalo riadok textu, ktorý sa
   nečíta. Široko je až B7b, a len vtedy, keď je čím tú šírku zaplniť.

   Namerané na 1280 × 900: pruh 248 px, plátno 760 px, `scrollWidth` 1280,
   nula vodorovného pretečenia. */
@media (min-width: 900px) {
  :root { --sp-rail: 248px; }
  body { padding-left: var(--sp-rail); }
  .topnav {
    inset: 0 auto 0 0; width: var(--sp-rail);
    flex-direction: column; justify-content: flex-start; gap: .15rem;
    padding: calc(var(--sp-safe-t) + .8rem) .5rem .8rem;
    border-top: 0; border-right: 1px solid var(--line);
    overflow-x: hidden; overflow-y: auto;
  }
  .topnav button {
    flex: 0 0 auto; width: 100%; min-width: 0;
    flex-direction: row; justify-content: flex-start; gap: .6rem;
    padding: .7rem .8rem; border-top: 0; border-radius: var(--radius);
    font-size: .95rem;
  }
  /* Stav nesie farba PÍSMA (dedí sa z pravidla mimo tejto vetvy) aj podklad —
     dva signály, nie jeden. Pruh navrchu tlačidla je vodorovná vec a v zvislom
     zozname by ukazoval do prázdna. */
  .topnav button[aria-current="page"] { background: var(--sp-accent-wash); }
  /* Spodná výplň 5,5 rem uvoľňovala palubu. Tá je na desktope vľavo, takže
     posledná položka programu už pod ňou neleží. */
  main { padding-bottom: calc(var(--sp-pad) + var(--sp-safe-b)); }
}

/* ---- B7b: tretí stĺpec ----------------------------------------------------
   Na mobile panel neexistuje ani vizuálne, ani obsahovo — `renderAside()` doň
   nič nezapíše, lebo `wide()` je nepravda. Toto pravidlo je druhá poistka, nie
   hlavná. `.canvas` NEMÁ mimo mediálnej vetvy žiadne pravidlo: je to obyčajný
   blok, takže `main` v ňom má na 390 px na pixel ten istý box ako predtým. */
.aside { display: none; }

/* 🔴 1240 px, nie 900. Pruh vľavo berie 248 px, takže tri stĺpce potrebujú
   248 + 460 + 570 = 1278 px. Pod tým je jeden stĺpec — a `wide()` v `app.js`
   sa pýta na TO ISTÉ číslo, aby sa panel neposkladal do DOM tam, kde ho CSS
   nemá kam položiť. Test „B7b: prah v JS a v CSS sa nesmie rozísť" to drží.

   Stopy sú `fr`, nie `px`: `minmax(0, 460px)` je pevná dĺžka, takže tesne nad
   prahom by dva stĺpce dali 1030 px do užšieho miesta a stránka by sa
   scrollovala do šírky. Pomer 46 : 57 je tých istých 460 : 570.

   Namerané na 1280 × 900: pruh 248 + plátno 1030 = 1278, `scrollWidth` 1280,
   nula vodorovného pretečenia. */
@media (min-width: 1240px) {
  :root { --sp-wide: 1030px; }
  /* Bez panelu ostáva plátno jednostĺpcové a úzke — `main` si drží `--sp-max`.
     Široké je LEN vtedy, keď je čím tú šírku zaplniť. */
  .canvas.split {
    display: grid; grid-template-columns: minmax(0, 46fr) minmax(0, 57fr);
    align-items: start;
    max-width: var(--sp-wide); margin: 0 auto;
  }
  .canvas.split > main { max-width: none; margin: 0; padding-right: var(--sp-pad); padding-left: 0; }
  /* 🔴 ZÁMERNE bez `position: sticky`. Sticky by potrebovalo `top` rovné výške
     hornej lišty — a tá nie je konštanta: pás v hlavičke má 51 px, keď hovorí
     dve veci, 45 px, keď jednu, a 0, keď mlčí. Zapísané číslo by tichlo
     zastaralo a panel by buď visel pod lištou, alebo pod ňou zmizol. */
  /* Bez zvislého oddeľovača. `align-items: start` necháva panel vysoký presne
     na svoj obsah, takže čiara by skončila v polovici stránky a visela by vo
     vzduchu. Kartu oddeľuje jej vlastný rám a tieň — to je ten istý oddeľovač,
     aký nesie každá karta v appke, a nemusí sa nikdy dopočítavať. */
  .canvas.split > .aside { display: block; padding-left: var(--sp-pad); }
  /* B7c — master-detail. Detail otvorenej položky žije v treťom stĺpci ako
     karta (rovnaký rám a tieň ako každá iná), nie ako modál. `sheetHtml` sa
     nemení; `.sheethead` tu NIE je sticky — stĺpec sa scrolluje so stránkou,
     nemá vlastný scroll a sticky by viselo pod hornou lištou (rovnaká zásada
     ako `.canvas.split > .aside` bez sticky). */
  .aside .asidedetail {
    background: var(--bg-raised); color: var(--fg);
    border: 1px solid var(--line); border-radius: var(--radius-lg);
    box-shadow: var(--shadow-1);
    padding: .2rem var(--sp-pad) 1.1rem;
  }
  .aside .asidedetail .sheethead { position: static; background: transparent; padding-top: .5rem; }
}

/* ==== DESKTOP verzia (desktop-ux-plan.md, 2026-08-30) =======================
   Jeden DOM, žiadny build. Šírku uvoľňuje `data-view` na `#view` (v `render()`)
   + tieto `@media (min-width: …)` vetvy. Mobil (`< 900`) sa NEDOTKNE ničoho —
   všetko je v `min-width`. Farby len cez existujúce `var(--token)`.

   Prah 1100, nie 900: pri 900 px viewportu berie ľavý pruh 248 px, ostane 652 px
   obsahu — menej než 760. Reálna „šírka na využitie" začína okolo 1100 px. */

/* ---- Detail položky: pravý drawer (A1, len CSS) --------------------------
   Spodný panel (mobil) / centrovaný modál (≥620) sa na desktope stane BOČNOU
   zásuvkou sprava. `sheetHtml` sa nemení — mení sa len pozícia overlayu, takže
   `trapTab`/`Esc`/`aria-modal` fungujú bez zásahu a testy (HTML string) tiež.
   Pozadie ďalej stmieva zoznam; je to stále modál, len pôsobí desktopovo. */
@media (min-width: 900px) {
  .sheetwrap { align-items: stretch; justify-content: flex-end; padding: 0; }
  .sheet {
    width: min(460px, 94vw); max-width: 460px;
    max-height: 100vh; max-height: 100dvh; height: 100dvh;
    border-radius: var(--radius-lg) 0 0 var(--radius-lg);
    border: 1px solid var(--line); border-right: 0; border-bottom: 1px solid var(--line);
    animation: sp-right .16s ease-out;
  }
  .sheethead { top: 0; }
}
@keyframes sp-right { from { transform: translateX(16px); opacity: .5 } to { transform: none; opacity: 1 } }

/* ---- šírka + mriežky per obrazovka (≥1100) ------------------------------- */
@media (min-width: 1100px) {
  /* Kmeň — mriežka kariet ľudí. Najlacnejšia veľká výhra: HTML `viewTribe()` sa
     nemení, len sa zo zoznamu riadkov stane grid, a z každého človeka karta. */
  main[data-view="tribe"] { max-width: var(--sp-wide); }
  .tribewrap .card > .list {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: var(--sp-gap);
  }
  .tribewrap .card > .list > .row.person {
    border: 1px solid var(--line); border-radius: var(--radius);
    padding: var(--sp-pad); align-items: flex-start;
  }
  /* V gride oddeľuje karta, nie čiara — vodorovný oddeľovač riadkov by sekol mriežku. */
  .tribewrap .card > .list > .row.person + .row.person { border-top: 1px solid var(--line); }

  /* Fotky — kontajner sa uvoľní, `gallery.js` mriežku (`auto-fill minmax(9rem)`)
     dorastie sám. Lightbox/consent v `gallery.js` sa nedotýkame. */
  main[data-view="gallery"] { max-width: var(--sp-wide); }

  /* Mapa — širší kontajner, veľká mapa. Zámerne JEDEN stĺpec (nie grid s bočným
     panelom): `#livemap` už nesie vlastný zoznam „Kde bol kmeň" z `map.js`, takže
     dvojstĺpec by ho postavil vedľa app.js karty „Kde je kmeň" — tá istá vec dvakrát.
     Výšku samotného plátna dvíha `map.css` (§17). */
  main[data-view="map"] { max-width: var(--sp-wide); }

  /* Nástenka — dvojstĺpec: composer + pripnuté vľavo (sticky), vlákno vpravo
     v reading-width (~40rem). Text príspevku sa nad ~72 znakov nečíta, tak sa
     NEROZŤAHUJE — šírka ide do rozloženia, nie do riadku. `viewBoard()` skladá
     presne dve `.card`, `grid-column` ich rozdelí bez zmeny DOM. */
  main[data-view="board"] { max-width: var(--sp-wide); }
  .boardwrap {
    display: grid; grid-template-columns: minmax(0, 20rem) minmax(0, 40rem);
    gap: var(--sp-gap); align-items: start; justify-content: center;
  }
  .boardwrap > .card:first-child { position: sticky; top: var(--sp-pad); }

  /* Deň-taby v Programe sa na širokej obrazovke zmestia bez vodorovného rolovania. */
  .daytabs { flex-wrap: wrap; }

  /* Lístky — na desktope sa zoznam typov rozloží do mriežky kariet a platobný
     blok (QR) stojí vedľa, nie natiahnutý cez celú šírku. */
  main[data-view="tickets"] { max-width: var(--sp-wide); }
  .ticketswrap .card > .list {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--sp-gap);
  }
  .ticketswrap .card > .list > .row.ticket {
    border: 1px solid var(--line); border-radius: var(--radius); padding: var(--sp-pad);
  }
  .paycard { max-width: 32rem; margin-left: auto; margin-right: auto; }
}

/* ---- hover afordancie (len myš) -----------------------------------------
   `hover: hover` + `pointer: fine`: na dotyku sa NIČ nezmení. Celý riadok je
   klikací cieľ (`.ttl::after inset:0`), takže hover patrí riadku, nie len názvu. */
@media (hover: hover) and (pointer: fine) {
  .row.person:hover, #proglist .row:hover, .aside .row:hover {
    background: var(--sp-sunken); border-radius: var(--radius-sm);
  }
  .tribewrap .card > .list > .row.person:hover {
    border-color: var(--line-strong); box-shadow: var(--shadow-1); background: var(--bg-raised);
  }
}

/* ---- prepínače na serverom vykreslených stránkach ------------------------- */
/* Rozcestník a prihlásenie nemajú `app.js`, takže si nesú vlastné, malé ovládanie.
   Cieľ je `--sp-tap`: je to prvá vec, ktorú človek na stránke ťukne. */
.lp-tools {
  display: flex; align-items: center; justify-content: flex-end; gap: .4rem;
  max-width: var(--sp-max); margin: 0 auto;
  padding: calc(var(--sp-safe-t) + .4rem) var(--sp-pad) 0;
}
.langsw { display: flex; gap: .15rem; }
.langsw a, .langsw-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--sp-tap); min-height: var(--sp-tap);
  border: 1px solid transparent; border-radius: var(--radius-pill);
  background: none; color: var(--fg-muted);
  font: inherit; font-size: .78rem; font-weight: 700; text-decoration: none;
  cursor: pointer; line-height: 1;
}
.langsw a:hover, .langsw-btn:hover { color: var(--fg); border-color: var(--line-strong); }
.langsw a[aria-current="true"] { color: var(--accent-fg); border-color: var(--accent-fill); }
.langsw-btn { font-size: 1.05rem; }

/* ---- §29 Články + desktop landing --------------------------------------
   `/clanky` (zoznam + článok) a širší landing na desktope. Mobil landing sa
   NEMENÍ — desktop je celý v `@media (min-width: 900px)`. Len brand tokeny,
   dark/light rieši token sám. Čítací stĺpec drží `--sp-max` (760 px). */
.lp-back { margin: 0 0 1.2rem; font-size: .9rem; }
.lp-back a { color: var(--fg-muted); text-decoration: none; }
.lp-back a:hover { color: var(--fg); text-decoration: underline; }

/* Hlavička blogu — malý brand mark, nech je to súčasť webu, nie cudzí generátor. */
.cl-head { text-align: center; margin: .6rem 0 1.6rem; }
.cl-head .cl-mark { display: block; margin: 0 auto .6rem; }
.cl-head h1 { margin: 0 0 .25rem; }

/* Zoznam = karty: titul → dátum → perex. Perex je odsek, nie ozdoba. */
.lp-clanky { list-style: none; padding: 0; margin: 0; }
.cl-card { padding: 1.05rem 1.1rem; margin: 0 0 .9rem; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--bg-raised); }
.cl-title { display: block; font-weight: 700; font-size: 1.12rem; line-height: 1.35;
  color: var(--fg); text-decoration: none; }
.cl-title:hover { text-decoration: underline; }
.cl-datum { color: var(--fg-muted); font-size: .82rem; margin: .25rem 0 .45rem; }
.cl-perex { margin: 0; color: var(--fg-muted); line-height: 1.55; }

/* Článok — časopisová typografia: H1, dátum, lede, potom telo s dychom. */
.cl-clanok { max-width: var(--sp-max); margin: 0 auto; }
.cl-chead { margin: 0 0 1.4rem; padding: 0 0 1.1rem; border-bottom: 1px solid var(--line); }
.cl-chead h1 { line-height: 1.25; margin: 0 0 .35rem; }
.cl-lede { font-size: 1.12rem; line-height: 1.55; color: var(--fg-muted); margin: .7rem 0 0; }
.cl-body { line-height: 1.65; }
.cl-body p { margin: 0 0 1rem; }
.cl-body h2 { font-size: 1.25rem; margin: 1.8rem 0 .6rem; }
.cl-body h3 { font-size: 1.05rem; margin: 1.4rem 0 .5rem; }
.cl-body ul { margin: 0 0 1rem; padding-left: 1.4rem; }
.cl-body li { margin: 0 0 .4rem; }
.cl-body blockquote { border-left: 3px solid var(--accent-fill, var(--line));
  margin: 1.2rem 0; padding: .15rem 0 .15rem 1rem; color: var(--fg-muted); font-style: italic; }
.cl-body a { text-decoration: underline; }
.cl-footnav { margin: 2rem 0 0; padding-top: 1rem; border-top: 1px solid var(--line); font-size: .9rem; }

/* §29 — obrázky článkov. Thumb drží 16/9 + cover (rozmery z <img> width/height dá
   prehliadač do aspect-ratio sám, toto je poistka proti skoku layoutu aj bez nich).
   Karta BEZ obrázka je to isté bez .has-img — žiadna diera. */
.cl-hero { display: block; width: 100%; height: auto; margin: 0 0 1.2rem;
  border-radius: var(--radius); box-shadow: var(--shadow-1); }
.cl-thumb { display: block; width: 100%; aspect-ratio: 16 / 9; height: auto;
  object-fit: cover; border-radius: var(--radius); margin: 0 0 .6rem; }
.cl-cardtext { min-width: 0; }

/* Landing teaser „Z článkov" — titul + perex orezaný na 3 riadky. */
.lp-articles { margin-top: 1.8rem; }
.lp-clanok-teaser { margin: 0 0 .9rem; padding: .9rem 1rem; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--bg-raised); }
.cl-clamp { margin: .35rem 0 0; color: var(--fg-muted); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }

@media (min-width: 900px) {
  body.plain .wrap.lp { max-width: 60rem; }
  /* Blog drží čitateľský stĺpec aj na širokom — zoznam aj článok. */
  body.plain .wrap.lp.clanky { max-width: var(--sp-max); }
  .lp-hero { text-align: center; padding: 1.2rem 0 .4rem; }
  .lp-hero h1 { font-size: 2.3rem; }
  .lp-hero .lp-lede { font-size: 1.25rem; }
  .lp-hero .lp-sub { max-width: 40rem; margin-left: auto; margin-right: auto; }
  .lp-steps, .lp-list { columns: 2; column-gap: 2.2rem; }
  .lp-steps li, .lp-list li { break-inside: avoid; margin-bottom: .55rem; }
  /* Karta je úzka — dva stĺpce by v nej rozlomili tri riadky na 2 + 1. */
  .lp-tvary { grid-template-columns: repeat(3, 1fr); }
  .lp-tvar .lp-list { columns: 1; }
  .cl-card.has-img { display: grid; grid-template-columns: 11rem 1fr; gap: 1rem; align-items: start; }
  .cl-card.has-img .cl-thumb { margin: 0; }
  .cl-card { transition: border-color .12s ease; }
  .cl-card:hover { border-color: var(--accent-fill, var(--fg-muted)); }
  .lp-articles .lp-clanky { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
  .lp-articles .lp-clanok-teaser { margin: 0; transition: border-color .12s ease; }
  .lp-articles .lp-clanok-teaser:hover { border-color: var(--accent-fill, var(--fg-muted)); }
}
/* §29 — previous/next na článku: dve karty, starší vľavo, novší vpravo. Prázdna
   strana drží mriežku (flex placeholder), nech osamelý sused nepláva do stredu. */
.cl-prevnext { display: flex; gap: var(--sp-gap); justify-content: space-between; margin: 1.6rem 0 .4rem; }
.cl-pn { flex: 1 1 0; min-width: 0; display: block; padding: .6rem .8rem; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--bg-raised); text-decoration: none; color: var(--fg); }
.cl-pn small { display: block; color: var(--fg-muted); margin-bottom: .15rem; }
.cl-pn span { font-weight: 700; overflow-wrap: anywhere; }
.cl-pn-next { text-align: right; }
.cl-pn-empty { border: 0; background: transparent; }
a.cl-pn:hover { border-color: var(--accent-fill); }
