/* ═══════════════════════════════════════════════════════════════════════
   Členská sekce — design system.

   VĚDOMÁ KOPIE brand tokenů hlavní aplikace (kap. 9.3 návrhu). Zdrojem
   pravdy zůstává `.CeskeBudejovice_Lasergame/app/static/style/theme.css`;
   tenhle blok se při změně značky aktualizuje ručně. Je celý pohromadě
   nahoře přesně proto, aby bylo zřejmé, kam sáhnout.

   Odlišení od hlavního webu (rozhodnutí R3): stejná paleta a font, ale
   „uvnitř“ — menší základní písmo, těsnější rytmus, panely místo
   marketingových karet, o stupeň tmavší pozadí stránky.
   ═══════════════════════════════════════════════════════════════════════ */

/* Fonty jsou stejné jako na hlavním webu — Space Grotesk na všechno,
   Beon jen na krátké verzálkové popisky. Soubory jsou kopie z
   `.CeskeBudejovice_Lasergame/app/static/fonts/` (self-hosted schválně:
   Google Fonts = render-blocking request navíc, další host v CSP a přenos
   IP návštěvníka ven). `unicode-range` dělí latinku a latin-ext, takže se
   soubor s háčky stáhne jen tam, kde je potřeba.

   Sora, na které stála členská sekce dřív, je pryč: dvě různá písma na
   webu a v jeho členské sekci znamenají dvě různé značky. Soubory
   `fonts/sora-*.woff2` zůstaly v repu, aby šel návrat udělat jedním
   commitem — nikde se nenačítají. */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/space-grotesk-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/space-grotesk-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Beon — display font z brand manuálu. Neonový stencil: krátké verzálkové
   popisky, NIKDY běžný text. V členské sekci ho nosí přesně dva prvky
   (`.eyebrow` a `.nav-tag`) — v odstavci nebo v malé velikosti se jeho
   stencilové mezery slijí a text je nečitelný. */
@font-face {
  font-family: 'Beon';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/beon-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Beon';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/beon-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* ── kopie brand tokenů (theme.css hlavní aplikace, 1:1) ──
     Hodnoty se sem opisují DOSLOVA. Dřív tu byla vlastní, o stupeň
     světlejší varianta palety (#002F3F a spol.) a členská sekce vypadala
     vedle webu jako jiná firma. #002F3F v manuálu vůbec není. */
  --bg: #000E12;          /* pozadí stránky */
  --surface: #00222D;     /* Neon OFF 2 — hlavička, karty, sekce */
  --surface-2: #002F3C;   /* Neon OFF — pilulky, inputy, hover */
  --surface-3: #013D4E;   /* o stupeň výš, jen pro vnořené prvky */
  --text: #E8F1EE;
  --text-strong: #FFFFFF;
  --muted: #8FA9AE;
  --faint: #5E7F86;
  --line: rgba(255, 255, 255, .09);
  --line-strong: rgba(255, 255, 255, .18);
  --line-neon: rgba(105, 188, 68, .38);
  --accent: #69BC44;
  --accent-ink: #00222D;  /* text na plné zelené — nikdy bílá */
  --accent-dim: rgba(105, 188, 68, .12);
  --accent-border: rgba(105, 188, 68, .38);
  --team-blue: #3da9fc;
  --accent-2: #f25c7a;
  --danger: #f25c7a;
  --info: #3da9fc;
  --success: #34d39a;
  --warn: #f7c548;
  --font: 'Space Grotesk', system-ui, -apple-system, sans-serif;
  --font-display: 'Beon', 'Space Grotesk', system-ui, sans-serif;
  /* Beon nemá kurzívu ani jinou váhu — `letter-spacing` je jediné, čím se
     v něm dá dělat hierarchie, a verzálky potřebují prostor. */
  --display-tracking: .04em;
  --ease-out: cubic-bezier(.23, 1, .32, 1);
  --d-press: 140ms;
  --d-hover: 180ms;
  --d-pop: 220ms;

  /* ── neonová záře (manuál: barva Neon ON, opacity 75 %, blur 3 mm) ──
     Dvě vrstvy: úzká drží tvar ostrý, široká dělá rozliv do okolí. */
  --glow: 0 0 6px rgba(105, 188, 68, .75), 0 0 18px rgba(105, 188, 68, .40);
  --glow-2: 0 0 4px rgba(105, 188, 68, .55), 0 0 12px rgba(105, 188, 68, .28);
  --glow-filter: drop-shadow(0 0 5px rgba(105, 188, 68, .75))
                 drop-shadow(0 0 14px rgba(105, 188, 68, .35));

  /* ── vlastní pro členskou sekci: hustší režim ──
     Povrchy se odvozují z brand tokenů, ne z vlastních čísel — jinak se
     kopie palety rozejde hned prvním „jen o kousek tmavší" panelem. */
  --page: var(--bg);
  --panel: var(--surface);
  --panel-2: var(--surface-2);
  --radius: 13px;
  --radius-sm: 9px;
  --radius-pill: 999px;
  --gap: 16px;
  --fs: 14.5px;             /* menší základ než marketingový web */
  --page-max: 1460px;

  /* ── hlavička: logo je míra všeho ostatního ──
     Stejná mechanika jako na hlavním webu: tloušťka PULSu i výška
     hlavičky se POČÍTAJÍ z výšky loga, takže hlavička drží proporce
     v každém okně a kmit se nemůže rozejít s linkou. Členská sekce je
     „uvnitř" — logo je proto o stupeň menší než na webu (72 px strop
     místo 90) a řádka nižší (54 místo 66). */
  --nav-logo-h: clamp(40px, 5.4vw, 72px);
  --puls-w: calc(var(--nav-logo-h) / 15.5);
  --puls-gap: calc(var(--puls-w) * 2);
  --nav-row-h: 54px;
  /* Do obou výšek je započítaný `safe-area-inset-top`: hlavička je `fixed`
     u horní hrany, takže na telefonu s výřezem by jinak logo leželo pod
     ním. Prostor se přidává jako `padding-top` na `.nav`, proto všechno,
     co se počítá z VNITŘNÍ výšky hlavičky, tenhle vklad zase odečítá. */
  --nav-h: calc(max(calc(var(--nav-logo-h) * 2.2),
                    calc(var(--nav-logo-h) * 1.3 + var(--nav-row-h) + 10px))
                + var(--safe-t));
  --nav-h-compact: calc(62px + var(--safe-t));
  /* Logo ve složeném pruhu. Šířku potřebuje spočítat řádka pilulek (odsadí
     se o ni doprava), proto je i poměr stran token: viewBox 1650×500. */
  --nav-logo-h-compact: 26px;
  --nav-logo-ar: 3.3;
  --pad: clamp(1rem, 2.2vw, 2.2rem);

  /* ── hloubka ──
     Panely byly do redesignu jen barva + 1px linka, takže na desktopu
     splývaly v jednu plochu. Vrstvení dělají tři věci dohromady: měkký
     stín dolů, světlá linka nahoře (--edge) a o stupeň světlejší povrch
     u zvednutých prvků. Používat vždycky společně — samotný stín na
     tmavém pozadí není vidět. */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .3), 0 10px 26px -16px rgba(0, 0, 0, .75);
  --shadow-2: 0 2px 6px rgba(0, 0, 0, .32), 0 22px 46px -20px rgba(0, 0, 0, .85);
  --edge: inset 0 1px 0 rgba(255, 255, 255, .06);

  /* ── barva dat ──
     Akcent dělal do redesignu všechno naráz: odkaz, odznak, tlačítko,
     pruh, graf i aktivní položku menu. Když má všechno stejnou zelenou,
     nevyčnívá nic. Grafy a pruhy postupu proto jedou na modré (--data),
     zelená zůstává pro STAV a AKCI. Nemíchat. */
  --data: #3da9fc;
  --data-2: #8ad4ff;
  --data-dim: rgba(61, 169, 252, .16);

  /* ── mikropopisky ──
     `--faint` je na panelu (#00222D) v kontrastu 3,4 : 1 — pod hranicí
     4,5 : 1 pro drobný text. Na hlavním webu leží na světlejším povrchu,
     kde to vyjde, tady ne. Prokládané verzálky v hlavičkách tabulek,
     dlaždic a `dt` proto jedou na vlastním, světlejším tónu; brand token
     se kvůli tomu nemění, aby se kopie nerozešla s `theme.css`. */
  --label: #9fbabf;

  /* Mobil: výška spodní lišty včetně gesture baru. Počítá se s ní na
     dvou místech (odsazení stránky, pozice šuplíku), takže je to token. */
  --tabbar: 58px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  /* Ploché pozadí bylo na velkém monitoru největší zdroj pocitu „prázdná
     stránka“. Dvě ambientní záře nahoře (stejný postup jako u .auth) dají
     ploše hloubku, aniž by přibyl jediný obrázek. `fixed` proto, aby záře
     zůstala u horní hrany okna a neodrolovala se pryč. */
  background:
    radial-gradient(1180px 620px at 76% -12%, rgba(61, 169, 252, .09), transparent 66%),
    radial-gradient(880px 500px at 6% -8%, rgba(106, 190, 69, .075), transparent 60%),
    var(--page);
  background-attachment: fixed;
  color: var(--text);
  font: 400 var(--fs)/1.5 var(--font);
  /* Dashboard je plný čísel — proporcionální číslice by nechaly sloupce
     poskakovat při každém překreslení. */
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  /* Šedý blik po klepnutí na Androidu vypadá jako chyba vykreslení. */
  -webkit-tap-highlight-color: transparent;
  /* Bez tohohle se při přetažení pod spodní lištou objeví bílé pozadí. */
  overscroll-behavior-y: none;
}

img, svg { max-width: 100%; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Hierarchie: h1 proti základu 14,5 px byl skok o půl stupně a stránky
   pak vypadaly jako jeden dlouhý odstavec. Nadpis se škáluje s šířkou
   okna, mezitituly zůstávají pevné — jinak by se hlavičky panelů začaly
   na širokém monitoru prát s h1. */
h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.015em; line-height: 1.2; }
h1 { font-size: clamp(1.5rem, 1.15rem + .95vw, 2.1rem); letter-spacing: -.025em; }
h2 { font-size: 1.02rem; }
h3 { font-size: .92rem; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-ink);
  padding: .6rem 1rem; border-radius: 0 0 var(--radius) 0; font-weight: 600;
}
.skip-link:focus { left: 0; }

/* ── ikony ─────────────────────────────────────────────────────────── */
.ic { width: 1em; height: 1em; flex: none; vertical-align: -.125em; }
.ic-lead { margin-right: .45em; }

/* ═══ Neonová primitiva ════════════════════════════════════════════════
   PULS a pilulka. Obojí je VĚDOMÁ KOPIE z hlavního webu
   (`style/public/02-primitives.css`) — stejné pravidlo jako u brand tokenů
   výš: zdrojem pravdy je hlavní aplikace, tady se to jen udržuje shodné.

   PULS — manuál: „sekundární grafický prvek za logem, buď ve spodku
   grafiky a nebo jako dělící čára nahoře; šířka by měla být dvojnásobek
   podtržení loga." Skládá se z rovných úseků (.puls-rail, roztahují se)
   a pevného srdečního kmitu (.puls-beat). Mezera je JEDNA na celou čáru —
   proto je na .puls-break jako margin, ne `gap` na kontejneru, který by ji
   dal i před kmit a čára by se rozpadla na kusy. Poměry --f i geometrie
   kmitu jdou 1:1 z `img/PULS.svg` přes `puls.py`, žádné číslo z kresby se
   tu neopisuje. */
.puls {
  display: flex; align-items: center; width: 100%;
  pointer-events: none;
}
.puls-rail {
  flex: var(--f, 1) 1 0;
  height: var(--puls-w);
  border-radius: var(--puls-w);
  background: var(--accent);
  box-shadow: var(--glow);
}
.puls-break { margin-left: var(--puls-gap); }
/* Strana přivrácená ke kmitu je NAPOJENÍ, ne konec linky: zaoblený konec by
   se od šikmé čáry odtáhl a ve spoji by zůstaly dva výřezy. */
.puls-join-r { border-start-end-radius: 0; border-end-end-radius: 0; }
.puls-join-l { border-start-start-radius: 0; border-end-start-radius: 0; }
/* --beat-w / --beat-h nastavuje makro z `PULS.svg`. Šířka nesmí být `auto`,
   aby se dala ANIMOVAT (při složení hlavičky se kmit srazí k nule a linka se
   za ním zavře). */
.puls-beat {
  flex: none;
  height: calc(var(--puls-w) * var(--beat-h));
  width: calc(var(--puls-w) * var(--beat-w));
  color: var(--accent);
  filter: var(--glow-filter);
  /* viewBox je vodorovně těsný na krajní body kmitu, takže kulatý konec tahu
     leží přesně na hraně a ořez by ho sřízl naplocho. */
  overflow: visible;
}
/* `non-scaling-stroke` drží tloušťku kmitu přesně na --puls-w bez ohledu na
   to, jak je SVG vysoké. */
.puls-beat polyline {
  fill: none; stroke: currentColor; stroke-width: var(--puls-w);
  stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.pill {
  display: inline-flex; align-items: center; justify-content: center;
  gap: .4rem; height: 34px; padding: 0 .95rem;
  border-radius: var(--radius-pill);
  background: var(--surface-2); color: var(--accent);
  border: 1px solid transparent;
  font-size: .86rem; font-weight: 500; line-height: 1;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  /* Height/padding/font-size jsou tu kvůli SKLÁDÁNÍ HLAVIČKY: pilulka se při
     scrollu zmenší. Bez přechodu je to skok. */
  transition: background var(--d-hover) var(--ease-out),
              border-color var(--d-hover) var(--ease-out),
              box-shadow var(--d-hover) var(--ease-out),
              color var(--d-hover) var(--ease-out),
              transform var(--d-press) var(--ease-out);
}
.pill:hover { background: var(--surface-3); border-color: var(--line-neon); text-decoration: none; }
.pill:active { transform: scale(.97); }
/* Aktivní položka je obtažená a svítí, nemá plnou výplň — plná zelená patří
   akci (tlačítko), ne stavu. Stejné rozhodnutí jako na hlavním webu. */
.pill.on,
.pill[aria-current="page"] {
  background: transparent; border-color: var(--accent);
  color: var(--accent); box-shadow: var(--glow-2);
}
.pill-icon { width: 34px; height: 34px; padding: 0; }

/* Odznak s počtem bonusů k vyzvednutí. Nosí ho pilulka v hlavičce, položka
   v šuplíku i ikona ve spodní liště — proto je to samostatný prvek. */
.nav-badge {
  min-width: 19px; height: 19px; padding: 0 5px;
  border-radius: 10px;
  background: var(--accent); color: var(--accent-ink);
  font-size: .68rem; font-weight: 700; line-height: 19px; text-align: center;
}
.avatar {
  width: 32px; height: 32px; flex: none; border-radius: 50%;
  background: var(--accent-dim); border: 1px solid var(--accent-border);
  color: var(--accent); font-weight: 700; font-size: .85rem;
  display: grid; place-items: center;
}

/* ═══ Hlavička ═════════════════════════════════════════════════════════
   Skladba z brand manuálu, stejná jako na hlavním webu: logo, pod ním PULS
   přes celou šířku a pod ním řada pilulek. Po odscrollování se složí do
   úzkého pruhu, ve kterém je PULS spodní hranou.

   VŠECHNY TŘI VRSTVY JSOU `position: absolute`. Na hlavním webu je takhle
   vyvázané jen logo (zbytek přehazuje `order` při přidání třídy z JS), tady
   to platí pro všechny: skládání řídí `animation-timeline: scroll()` a to
   umí interpolovat jen hodnoty, ne přeskládat tok. Co se tedy mezi stavy
   mění, musí být číslo — výška, odsazení, velikost písma. Pořadí ve složeném
   pruhu (řádka NAD PULSem) se proto nedělá `order`, ale tím, že PULS dojede
   z `bottom: var(--nav-row-h)` na `bottom: 0` a řádka se posune nad něj.

   Výška hlavičky je token (--nav-h), protože ji potřebuje i odsazení
   stránky. Dvě čísla na dvou místech by se rozešla. */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  height: var(--nav-h);
  padding-top: var(--safe-t);
  /* Jemná radiální záře OD loga — hlavička není plochá barva, logo v ní
     opticky svítí. Doslova stejný gradient jako na hlavním webu. */
  background: radial-gradient(ellipse 70% 100% at 10% 25%, var(--surface-3), var(--surface) 65%);
}
/* Doznění barvy hlavičky do obsahu POD ní. NESMÍ to být samostatný prvek
   v `main`: dva různé boxy (fixed hlavička + statický obsah) se i se stejnou
   barvou na styku renderují jako dvě kompozitní vrstvy a na švu je vidět
   tenká čára. Pseudoelement je součástí TÉŽE vrstvy jako hlavička. */
.nav::after {
  content: ""; position: absolute; left: 0; right: 0; top: 100%;
  height: clamp(2rem, 7vh, 5rem); pointer-events: none;
  /* Stejné RGB jako --surface (#00222D) na všech zastávkách, mění se jen
     alfa. Doznění DO `transparent` by interpolovalo i barvu k černé a
     uprostřed by vznikl tmavý pruh místo hladkého spádu. */
  background: linear-gradient(to bottom,
    rgba(0, 34, 45, 1)   0%,
    rgba(0, 34, 45, .7)  35%,
    rgba(0, 34, 45, .35) 65%,
    rgba(0, 34, 45, 0)   100%);
}

.nav-brand-row {
  position: absolute; left: var(--pad); right: var(--pad); top: 0;
  height: calc(var(--nav-h) - var(--safe-t) - var(--nav-row-h) - var(--puls-w));
  display: flex; align-items: center; gap: .8rem;
}
.nav-brand { display: inline-flex; align-items: center; flex: none; }
.nav-brand:hover { text-decoration: none; }
/* Logo svítí filtrem, ne obtaženým SVG — `drop-shadow` respektuje tvar tahů,
   `box-shadow` by rozzářil neviditelný obdélník okolo. */
.nav-logo {
  display: block; width: auto; height: var(--nav-logo-h);
  filter: var(--glow-filter);
}
/* Jediné dvě místa s Beonem v celé aplikaci (druhé je `.eyebrow`): krátký
   verzálkový popisek, přesně to, na co je stencil dělaný. */
.nav-tag {
  font-family: var(--font-display);
  font-size: .72rem; letter-spacing: var(--display-tracking);
  text-transform: uppercase; color: var(--accent);
  padding-left: .85rem; border-left: 1px solid var(--line-strong);
  white-space: nowrap;
}

/* PULS jde od kraje ke kraji, takže se vyvazuje z odsazení hlavičky.
   Zbylých 6 px z každé strany je místo pro zaoblený konec linky. */
.nav-puls {
  position: absolute; left: 0; right: 0; bottom: var(--nav-row-h);
  padding-inline: 6px;
  /* Pevná výška = jen tloušťka linky, takže kmit přesahuje ven a
     NEROZTAHUJE hlavičku. Bez toho si vysoký kmit vynutí trojnásobnou
     hlavičku a logo se vytlačí nad horní hranu okna. */
  height: var(--puls-w);
}
/* Podle `PULS.svg` začíná kmit v 81 % šířky — přesně tam má hlavička pravý
   shluk (účet, odhlášení) a kmit by jím projížděl. Posouvá se proto do
   prázdného místa mezi pilulkami a účtem. Přebíjí se `flex-grow`, ne `--f`:
   makro nastavuje inline jen custom property, takže pravidlo s větší
   specificitou vyhraje. */
.nav-puls .puls-rail:nth-of-type(1) { flex-grow: 52; }
.nav-puls .puls-rail:nth-of-type(2) { flex-grow: 17; }
.nav-puls .puls-rail:nth-of-type(3) { flex-grow: 31; }

.nav-bar {
  position: absolute; left: var(--pad); right: var(--pad); bottom: 0;
  height: var(--nav-row-h);
  display: flex; align-items: center; justify-content: space-between; gap: .8rem;
}
/* `space-between` platí v obou stavech — ve složeném pruhu se řádka
   neodsazuje jinak zarovnaná, ale odsazením zleva o šířku loga
   (viz `nav-collapse-bar`). Tím se z přeskládání stane jedno číslo, které
   jde animovat. */
.nav-center { display: flex; align-items: center; gap: .3rem; min-width: 0; }
.nav-center .pill { position: relative; }

.nav-right { display: flex; align-items: center; gap: .45rem; flex: none; }
/* Účet je dlaždice, ne pilulka: nese dva řádky (přezdívka + číslo) a vede
   na Nastavení, které proto v řadě pilulek chybí. */
.nav-me {
  display: flex; align-items: center; gap: .5rem;
  height: 34px; padding: 0 .6rem 0 .35rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text); text-decoration: none;
  transition: background var(--d-hover) var(--ease-out),
              border-color var(--d-hover) var(--ease-out);
}
.nav-me:hover { background: var(--surface-3); border-color: var(--line-neon); text-decoration: none; }
.nav-me.on { border-color: var(--accent); box-shadow: var(--glow-2); }
.nav-me .avatar { width: 26px; height: 26px; font-size: .74rem; }
.nav-me-t { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.nav-me-t b { font-size: .78rem; font-weight: 600; }
.nav-me-t small { font-size: .66rem; color: var(--faint); }
.nav-me-t b, .nav-me-t small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── SKLÁDÁNÍ PO SCROLLU, BEZ JEDINÉHO ŘÁDKU JS ──
   `animation-timeline: scroll(root block)` váže postup animace na pozici
   scrollu dokumentu, ne na čas: v rozsahu 0 → 140 px hlavička plynule
   přejde do složeného pruhu a při scrollu zpět se stejně plynule vrátí.
   Prohlížeč bez podpory (`@supports` níž) nechá hlavičku rozbalenou —
   funkčně se neztratí nic, jen zabírá víc místa.

   Rozsah je schválně krátký: hlavička se má složit hned na začátku
   scrollování, ne až v polovině stránky.

   `animation-fill-mode: both` je nutný — bez něj by se na nule scrollu
   nepoužil první klíčový snímek a hlavička by blikla. */
@supports (animation-timeline: scroll()) {
  .nav, .nav-brand-row, .nav-logo, .nav-tag,
  .nav-puls, .nav-puls .puls-beat, .nav-bar,
  .nav-center .pill, .nav-me, .nav-right .pill-icon {
    animation-timeline: scroll(root block);
    animation-range: 0 140px;
    animation-timing-function: linear;
    animation-fill-mode: both;
    /* `auto` = celý rozsah timeline. KONKRÉTNÍ doba by tu byla chyba:
       u progress-based timeline se čas počítá jako podíl rozsahu, takže
       třeba `1ms` znamená „hotovo hned na prvním pixelu scrollu". */
    animation-duration: auto;
  }
  .nav { animation-name: nav-collapse; }
  .nav-brand-row { animation-name: nav-collapse-brand; }
  .nav-logo { animation-name: nav-collapse-logo; }
  .nav-tag { animation-name: nav-collapse-tag; }
  .nav-puls { animation-name: nav-collapse-puls; }
  .nav-puls .puls-beat { animation-name: nav-collapse-beat; }
  .nav-bar { animation-name: nav-collapse-bar; }
  .nav-center .pill, .nav-me, .nav-right .pill-icon { animation-name: nav-collapse-pill; }

  @keyframes nav-collapse {
    to { height: var(--nav-h-compact); }
  }
  @keyframes nav-collapse-brand {
    to { height: calc(var(--nav-h-compact) - var(--safe-t) - var(--puls-w)); }
  }
  @keyframes nav-collapse-logo {
    to { height: var(--nav-logo-h-compact); }
  }
  /* Popisek u loga ve složeném pruhu mizí: v 62px pruhu vedle devíti pilulek
     je to už jen šum, a hlavně by se s nimi na 1100 px pral o místo. */
  @keyframes nav-collapse-tag {
    to { opacity: 0; }
  }
  /* PULS dojede na spodní hranu pruhu — tím se z něj stane ta „dělící čára
     nahoře" z manuálu a zároveň se řádka ocitne nad ním. */
  @keyframes nav-collapse-puls {
    to { bottom: 0; }
  }
  /* Kmit se ve složeném pruhu nekreslí: pruh je vysoký 62 px, kmit skoro
     dvakrát tolik — okno by mu ukrojilo špičku a vypadal by zmenšený.
     Místo po něm se zavírá ZÁPORNÝM pravým okrajem, ne zužováním šířky:
     SVG si obsah v užším boxu přeškáluje a kmit by se splácl, kdežto se
     záporným okrajem se rovné úseky plynule stáhnou k sobě a v lince
     nezůstane díra. Proto má `.puls-beat` šířku dopočítanou číslem. */
  @keyframes nav-collapse-beat {
    from { opacity: 1; margin-inline-end: 0; }
    to   { opacity: 0; margin-inline-end: calc(var(--puls-w) * var(--beat-w) * -1); }
  }
  /* Řádka se posune nad PULS a odsadí se zleva o složené logo — tím se
     dostane vedle něj, aniž by se měnilo zarovnání (to animovat nejde). */
  @keyframes nav-collapse-bar {
    to {
      bottom: var(--puls-w);
      height: calc(var(--nav-h-compact) - var(--safe-t) - var(--puls-w));
      padding-left: calc(var(--nav-logo-h-compact) * var(--nav-logo-ar) + 1rem);
    }
  }
  @keyframes nav-collapse-pill {
    to { height: 29px; font-size: .8rem; }
  }
}

/* ═══ Stránka ══════════════════════════════════════════════════════════
   Hlavička je `fixed`, obsah se pod ni odsazuje o její PLNOU výšku — po
   složení tam zůstane vzduch, ne skok obsahu.

   margin-inline: auto je oprava, ne kosmetika — bez něj zůstávala na
   1440 px a víc prázdná pravá třetina obrazovky a celá aplikace vypadala
   jako přilepená k levému okraji. */
.page {
  padding: clamp(1rem, 2.2vw, 2.2rem);
  padding-top: calc(var(--nav-h) + clamp(1rem, 2.2vw, 2.2rem));
  max-width: var(--page-max); width: 100%; margin-inline: auto;
  min-height: 60vh;
}

.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.5rem;
  padding-bottom: 1.1rem;
  border-bottom: 1px solid var(--line);
}
.page-head p { margin: .45rem 0 0; color: var(--muted); font-size: .87rem; max-width: 62ch; }
/* Druhé (a poslední) místo s Beonem — krátký verzálkový nadpisek nad h1. */
.eyebrow {
  font-family: var(--font-display);
  font-size: .78rem; letter-spacing: var(--display-tracking);
  text-transform: uppercase;
  color: var(--accent); margin-bottom: .35rem;
  text-shadow: 0 0 10px rgba(105, 188, 68, .45);
}

/* ═══ Patička ══════════════════════════════════════════════════════════
   Horní hranu dělá PULS, ne `border-top` — proto tady žádná linka. Je to
   tentýž prvek jako v hlavičce, jen dole (manuál: „ve spodku grafiky"). */
.footer {
  padding: 0 var(--pad) 1.4rem;
  color: var(--muted); font-size: .8rem;
}
/* Kontejner má výšku jen `--puls-w` a `align-items: center`, takže kmit
   vyčuhuje na obě strany o `(--beat-h − 1) / 2` násobku tloušťky linky.
   Spodní odstup je proto ODVOZENÝ, ne zapsaný: s pevným číslem by špička
   kmitu projížděla přes první řádek patičky. */
.footer-puls {
  height: var(--puls-w);
  margin-block: 2.5rem calc(var(--puls-w) * (var(--beat-h) - 1) / 2 + .8rem);
}
.footer-row {
  max-width: var(--page-max); margin: 0 auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1.4rem;
  padding-bottom: 1rem; border-bottom: 1px solid var(--line);
}
.footer-brand { flex: none; display: inline-flex; align-items: center; }
.footer-brand:hover { text-decoration: none; }
/* 44 px je nejmenší výška, ve které je v lockupu ještě čitelné
   „Laser Arena" pod značkou. */
.footer-logo { height: 44px; width: auto; display: block; }
.footer-links { display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; flex: 1; }
.footer-links a { color: var(--muted); font-size: .82rem; text-decoration: none; }
.footer-links a:hover { color: var(--accent); text-decoration: none; }
.footer-mail { color: var(--muted); font-size: .82rem; text-decoration: none; }
.footer-mail:hover { color: var(--text); text-decoration: none; }
/* Deset odkazů se do jedné řady vedle loga nevejde a volné zalomení dělalo
   9 + 1 osamělý odkaz. Odkazy proto stojí vedle loga jako mřížka s pevným
   počtem sloupců — zalomení je záměrné a sloupce jsou zarovnané. */
@media (min-width: 601px) {
  .footer-row { flex-wrap: nowrap; gap: .5rem 1.5rem; }
  .footer-links {
    flex: 1; min-width: 0;
    display: grid; grid-template-columns: repeat(3, auto); justify-content: start;
    gap: .35rem 1.3rem;
  }
  .footer-links a { white-space: nowrap; }
  .footer-mail { flex: none; margin-left: auto; }
}
@media (min-width: 1100px) {
  .footer-links { grid-template-columns: repeat(5, auto); }
}
.footer-bottom {
  max-width: var(--page-max); margin: .9rem auto 0;
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; gap: .4rem 1rem;
  font-size: .76rem; color: var(--faint);
}
/* Poznámka o fixture datech patří do patičky, ne do obsahu: platí pro celou
   aplikaci a na každé stránce zvlášť by z ní byl banner. Až se aréna
   připojí, mizí jedním řádkem v šabloně. */
.footer-note { color: var(--faint); }

/* ═══ Panely ═══════════════════════════════════════════════════════════
   Povrch je plochý `--panel`, světlo dělá `--edge` (inset linka nahoře),
   ne gradient na pozadí. Je to schválně: `thead` se na desktopu lepí
   (`position: sticky`) a musí mít pod sebou přesně stejnou barvu jako
   panel — pod gradientem by při odrolování prosvítala spára. */
.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.1rem 1.25rem;
  box-shadow: var(--shadow-1), var(--edge);
}
.panel + .panel { margin-top: var(--gap); }
/* Mezera mezi bloky stránky nesmí záviset na tom, jestli je soused panel
   nebo mřížka panelů. `.panel + .panel` na `.grid` nesedí, takže panel
   pod dvousloupcovou mřížkou se v Nastavení lepil na řádek nad sebou. */
.page > .grid + .panel,
.page > .panel + .grid,
.page > .grid + .grid { margin-top: var(--gap); }
/* Hlavička panelu má vlastní linku. Bez ní splýval nadpis s obsahem
   a panel vypadal jako nezformátovaný blok textu. */
.panel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: .8rem; margin-bottom: 1rem;
  padding-bottom: .75rem; border-bottom: 1px solid var(--line);
}
.panel-head h2 { display: flex; align-items: center; gap: .5rem; }
.panel-head h2 .ic { color: var(--accent); font-size: 1.05rem; }
/* Dovětek vpravo v hlavičce se nesmí lámat, ale ani přetéct — dlouhý
   e-mail nebo „43 her · odehráno 16 dní“ radši useknout třemi tečkami. */
.panel-head .more {
  font-size: .78rem; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.panel-note {
  margin: .9rem 0 0; padding-top: .8rem; border-top: 1px dashed var(--line);
  color: var(--muted); font-size: .755rem;
}
/* Zvýrazněný / varovný panel. Do redesignu se barva okraje psala inline,
   což CSP zahodila a panel zůstal stejný jako ostatní. */
.panel--hi {
  border-color: var(--accent-border);
  box-shadow: var(--shadow-1), var(--edge), inset 0 0 0 1px rgba(106, 190, 69, .12);
}
.panel--danger { border-color: rgba(242, 92, 122, .32); }
.panel--danger .panel-head h2 .ic { color: var(--danger); }

.grid { display: grid; gap: var(--gap); }
/* Uvnitř mřížky dělá mezery `gap`. Odsazení ze `.panel + .panel` by druhý
   panel v řádku posunulo o gap dolů — a protože se panely v řádku roztahují
   na stejný spodní okraj, vyšel by pravý sloupec nižší než levý. */
.grid > .panel + .panel { margin-top: 0; }
.g-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.g-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.g-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.g-side { grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr); align-items: start; }

/* ═══ Mřížka přehledu ══════════════════════════════════════════════════
   Šest sloupců, panely si berou 6 / 4 / 3 / 2 podle důležitosti. Šestka
   proto, že se dělí dvěma i třemi — stejná mřížka tak obslouží dvou-
   i třísloupcový desktop bez druhé sady tříd.

   `dense` je tady nutnost, ne ozdoba: „Další cíl“ i „K vyzvednutí“ jsou
   podmíněné panely. Bez dense by po chybějícím panelu zůstala v řádku
   díra a přehled by se rozpadl zrovna novému členovi. */
.dash {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(6, minmax(0, 1fr));
  grid-auto-flow: row dense;
}
.dash > * { grid-column: span 6; }
/* V mřížce dělá mezery `gap`. Svislé odsazení ze `.panel + .panel` by se
   k němu přičetlo a řádky by přestaly lícovat. */
.dash .panel + .panel { margin-top: 0; }

/* ═══ Statistické dlaždice ═════════════════════════════════════════════ */
.tile {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem 1.1rem 1.05rem;
  position: relative;
  overflow: hidden;
  box-shadow: var(--shadow-1), var(--edge);
  transition: transform var(--d-hover) var(--ease-out),
              box-shadow var(--d-hover) var(--ease-out);
}
.tile:hover { transform: translateY(-2px); box-shadow: var(--shadow-2), var(--edge); }
/* Proužek nahoře drží barevný klíč celé aplikace: dlaždice ukazuje data,
   proto modrá. Zelený proužek si nechává jen zvýrazněná dlaždice. */
.tile::after {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
  background: linear-gradient(90deg, var(--data), transparent 72%);
  opacity: .6;
}
.tile.hi::after { background: linear-gradient(90deg, var(--accent), transparent 72%); opacity: .8; }
.tile-k {
  font-size: .68rem; letter-spacing: .11em; text-transform: uppercase;
  color: var(--label); font-weight: 600;
  display: flex; align-items: center; gap: .35rem;
}
.tile-v {
  font-size: clamp(1.75rem, 1.3rem + 1.15vw, 2.5rem);
  font-weight: 700; line-height: 1.05; margin-top: .45rem;
  letter-spacing: -.035em;
}
/* Jednotka u čísla (%, ×) se sází menší a tlumeně — jinak ukousne z výšky
   displejového čísla a dlaždice přestane fungovat jako jeden údaj. */
.tile-v .unit { font-size: .42em; font-weight: 600; color: var(--muted); margin-left: .12em; }
.tile-s { font-size: .75rem; color: var(--muted); margin-top: .3rem; }
.tile.hi .tile-v { color: var(--accent); }

/* ═══ Postupové pruhy ══════════════════════════════════════════════════
   Pruh = data → modrá. Zelený zůstává jen pruh, který hlásí splněný stav
   (`.bar.ok`), aby zelená pořád znamenala „hotovo“ a nic jiného. */
.bar {
  height: 7px; border-radius: 4px; overflow: hidden;
  background: rgba(255, 255, 255, .06);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .35);
}
.bar > i {
  display: block; height: 100%; border-radius: 4px;
  background: linear-gradient(90deg, var(--data), var(--data-2));
  transition: width var(--d-hover) var(--ease-out);
}
.bar.thin { height: 5px; }
.bar.ok > i { background: linear-gradient(90deg, var(--accent), #8fe06a); }
.bar.warnc > i { background: linear-gradient(90deg, var(--warn), #ffd97a); }
.bar.dim > i { background: linear-gradient(90deg, var(--faint), #7c9ba1); }
/* Pruh v seznamu milníků nesmí přes celou šířku panelu — vedle popisku
   by se roztáhl na metr a přestal se dát odhadnout. */
.bar--short { display: block; margin-top: .45rem; max-width: 220px; }

.goal-row { display: flex; align-items: baseline; justify-content: space-between; gap: .6rem; }
.goal-row b { font-size: 1.35rem; letter-spacing: -.02em; }

/* ═══ Sloupcový graf (čisté CSS, žádná knihovna) ═══════════════════════
   Holé sloupce bez osy a bez měřítka byly to, kvůli čemu grafy „nedávaly
   smysl“ — nešlo z nich přečíst, kolik ten nejvyšší sloupec vlastně je.
   Skládají se proto ze tří dílů:

     .chartbox   mřížka: úzký sloupec s osou Y + vlastní plocha grafu
     .chart-y    popisky maxima a nuly
     .chart      sloupce; vodorovná mřížka a základna jsou POZADÍ, ne prvky

   Klíč k tomu, aby sloupce seděly na mřížku: popisky měsíců (.chart-x)
   nejsou součástí plochy. Sedí ve spodním `padding` grafu a jsou z něj
   vytažené absolutním pozicováním. Kreslicí plocha je tedy přesně content
   box, výška sloupce v procentech odpovídá měřítku a `background-clip:
   content-box` položí mřížku na tutéž plochu. Kdyby popisek zůstal uvnitř
   sloupce (původní stav), byla by každá výška o výšku popisku vedle. */
.chartbox {
  --lbl: 1.5rem;          /* pruh pro popisky pod osou X */
  --chart-h: 190px;
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .6rem;
}
.chart-y {
  display: flex; flex-direction: column; justify-content: space-between;
  height: var(--chart-h); padding-bottom: var(--lbl);
  font-size: .64rem; color: var(--faint); text-align: right;
  font-variant-numeric: tabular-nums;
}
.chart-y { color: var(--label); letter-spacing: .04em; }
/* Ryska u čísla ukotví popisek k mřížce — bez ní číslo jen visí vedle
   plochy a není zřejmé, ke které lince patří. */
.chart-y span { line-height: 1; position: relative; padding-right: .4rem; }
.chart-y span::after {
  content: ''; position: absolute; right: 0; top: 50%;
  width: 4px; height: 1px; background: var(--line-strong);
}
.chart-y span:first-child { margin-top: -.3em; }
.chart-y span:last-child { margin-bottom: -.3em; }

.chart {
  display: flex; align-items: flex-end; gap: 5px;
  height: var(--chart-h); padding-bottom: var(--lbl);
  background-image:
    /* základna (0) */
    linear-gradient(var(--line-strong), var(--line-strong)),
    /* vodorovná mřížka po 25 % */
    repeating-linear-gradient(to bottom, var(--line) 0 1px, transparent 1px 25%);
  background-size: 100% 1px, 100% 100%;
  background-position: 0 100%, 0 0;
  background-repeat: no-repeat;
  background-origin: content-box;
  background-clip: content-box;
}
/* Denní graf: 365 sloupců, popisky by stejně nebyly čitelné. */
.chart.dense { --lbl: 0rem; --chart-h: 130px; gap: 1px; }
.chart-col {
  flex: 1 1 0; min-width: 0; height: 100%;
  display: flex; flex-direction: column; justify-content: flex-end;
  position: relative;
}
/* Sloupec je jedna plná plocha, ne přechod. Přechod ve sloupci předstírá
   druhý rozměr, který data nemají — a hlavně rozmazává horní hranu, tedy
   jediné místo, ze kterého se hodnota čte. */
.chart-col > i {
  display: block; width: 100%; min-height: 2px;
  background: var(--data);
  border-radius: 3px 3px 0 0;
  transition: background var(--d-hover) var(--ease-out);
}
/* Aktuální měsíc je jediný sloupec se zelenou — je to stav („tady jsi“),
   ne další datová řada. */
.chart-col.now > i { background: var(--accent); }
.chart-col.zero > i { background: rgba(255, 255, 255, .07); }
.chart-col:hover > i { background: var(--data-2); }
.chart-col.now:hover > i { background: #8fe06a; }
.chart-col.zero:hover > i { background: rgba(255, 255, 255, .12); }
.chart-x {
  position: absolute; top: 100%; left: 0; right: 0;
  padding-top: .38rem;
  font-size: .66rem; color: var(--label); text-align: center;
  white-space: nowrap; overflow: hidden;
}
.chart-col.now .chart-x { color: var(--accent); font-weight: 600; }

/* ── Kumulativní křivka (tempo) ──
   Stejná mřížka jako sloupcový graf, jen navrch leží SVG křivka
   (poměr stran se v `.chart--line` může natáhnout nerovnoměrně — u křivky
   to nevadí, protože afinní zvětšení bezieru je zase bezier a tah se díky
   `non-scaling-stroke` nedeformuje; u teček ano, proto tečky NEJSOU
   součástí SVG. Kreslí se stejným trikem jako sloupce: neviditelný
   `i.chart-dot` s výškou `.h-N` (stejné generované třídy jako u sloupců)
   sedí na dně stejně jako bar, a viditelná tečka je jeho `::after` na
   horním okraji — tím pádem svisle přesně sedí na hodnotu, aniž by šel
   `style=""`, co CSP (`style-src 'self'`) nedovoluje.

   Bez výplně pod křivkou: plocha pod kumulativním součtem nenese žádnou
   informaci navíc (je to jen „všechno pod čárou“) a v panelu přebíjela
   samotnou linku, ze které se čte. */
/* Křivka nepotřebuje výšku sloupcového grafu: sloupce se čtou proti sobě
   (a užší sloupec = horší odhad), křivka se čte jako sklon a ten je na
   190 px stejně čitelný jako na 120. Dvě třídy schválně — přebít tím
   `--chart-h` ze všech mediálních dotazů bez ohledu na pořadí v souboru. */
.chartbox.chartbox--line { --chart-h: 118px; --lbl: 1.2rem; }
.chart--line {
  position: relative;
  /* Mřížka po 50 % místo 25 %: tři linky, tři popisky, nic bez čísla. */
  background-image:
    linear-gradient(var(--line-strong), var(--line-strong)),
    repeating-linear-gradient(to bottom, var(--line) 0 1px, transparent 1px 50%);
}
/* Výška MUSÍ být zadaná číslem, ne dvojicí `top`/`bottom`. `<svg>` je
   nahrazený element: s `height: auto` si vezme vlastní poměr stran z
   `viewBox` (tady 1:1), `bottom` se zahodí a z grafu je čtverec vysoký
   jako celá šířka panelu — křivka pak teče přes půl stránky. */
.chart-line {
  position: absolute; top: 0; left: 0;
  width: 100%; height: calc(100% - var(--lbl));
  overflow: visible;
}
.chart-line-path {
  fill: none; stroke: var(--accent); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.chart--line .chart-col > i.chart-dot {
  background: transparent; min-height: 0; border-radius: 0; position: relative;
}
/* Dvanáct teček na dvanáct měsíců křivku jen zubatí — hodnota se čte
   z linky, ne z bodů. Vidět je proto jen aktuální měsíc („tady jsi“) a
   ten, na kterém je myš; ostatní zůstávají jako neviditelná kotva pro
   hover s tooltipem. */
.chart-dot::after {
  content: ''; position: absolute; top: -3px; left: 50%;
  width: 6px; height: 6px; margin-left: -3px;
  border-radius: 50%; background: var(--accent);
  border: 2px solid var(--panel);
  opacity: 0;
  transition: opacity var(--d-hover) var(--ease-out),
              transform var(--d-hover) var(--ease-out);
}
.chart-col.now .chart-dot::after { background: #8fe06a; opacity: 1; }
.chart-col:hover .chart-dot::after { opacity: 1; transform: scale(1.3); }
/* Denní křivka (30–365 bodů): žádné popisky pod osou a nulová mezera mezi
   kotvami. Mezera 5 px by při 365 dnech sežrala víc místa než samotné
   sloupce a poslední tečka („dnes“) by seděla vedle konce křivky.
   Musí stát ZA `.chartbox--line`: stejná specificita, rozhoduje pořadí. */
.chartbox--line.chartbox--dense { --lbl: 0rem; }
.chartbox--dense .chart { gap: 0; }

.chart-legend {
  display: flex; justify-content: space-between; gap: .6rem;
  font-size: .73rem; color: var(--muted); margin-top: .9rem;
  padding-top: .7rem; border-top: 1px solid var(--line);
}

/* ═══ Heatmapa aktivity ════════════════════════════════════════════════
   Rok po dnech, 53 sloupců po sedmi. Nejvíc „současný“ prvek přehledu za
   nejmíň práce: žádný skript, žádná knihovna, jen mřížka a pět odstínů.
   Odstíny jsou modré, protože je to zobrazení dat — zelená v aplikaci
   znamená stav a akci. */
.heat { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .45rem; }
.heat-scroll { overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.heat-scroll::-webkit-scrollbar { display: none; }
.heat-grid {
  display: grid; grid-template-columns: repeat(53, minmax(11px, 1fr));
  gap: 3px; min-width: 100%;
}
.heat-col { display: grid; grid-template-rows: repeat(7, 1fr); gap: 3px; }
.heat-d {
  aspect-ratio: 1; border-radius: 2px;
  background: rgba(255, 255, 255, .045);
  transition: transform var(--d-press) var(--ease-out);
}
.heat-d:hover { transform: scale(1.35); }
.heat-d.lv1 { background: rgba(61, 169, 252, .28); }
.heat-d.lv2 { background: rgba(61, 169, 252, .5); }
.heat-d.lv3 { background: rgba(61, 169, 252, .72); }
.heat-d.lv4 { background: var(--data-2); }
/* Den, který ještě nenastal, není nula — je prázdno. Proto nemá ani
   podklad nulového dne, jinak by mapa tvrdila, že se ten den nehrálo. */
.heat-d.fut { background: transparent; }
.heat-dow {
  display: grid; grid-template-rows: repeat(7, 1fr); gap: 3px;
  font-size: .6rem; color: var(--faint); padding-top: 1.1rem;
}
.heat-dow span { display: grid; align-items: center; line-height: 1; }
.heat-months {
  display: grid; grid-template-columns: repeat(53, minmax(11px, 1fr));
  font-size: .62rem; color: var(--faint); margin-bottom: .3rem; min-width: 100%;
}
.heat-months span { grid-row: 1; }
.heat-key { display: flex; align-items: center; gap: .3rem; }
.heat-key i { width: 11px; height: 11px; border-radius: 2px; display: block; }

/* ═══ Tabulky ══════════════════════════════════════════════════════════
   `.tw` je vodorovný scroller pro úzké obrazovky. Na desktopu se ruší
   (viz mediální dotaz níž) — scroll container v jedné ose udělá z prvku
   scroll container i ve druhé a `position: sticky` na hlavičce by pak
   nemělo k čemu se přilepit. */
.tw { overflow-x: auto; margin: 0 -1.25rem; padding: 0 1.25rem; }
table.t { width: 100%; border-collapse: collapse; font-size: .855rem; }
table.t th {
  text-align: left; font-weight: 600; font-size: .68rem;
  letter-spacing: .09em; text-transform: uppercase; color: var(--label);
  padding: .1rem .65rem .6rem; white-space: nowrap;
  border-bottom: 1px solid var(--line-strong);
}
table.t td { padding: .72rem .65rem; border-bottom: 1px solid var(--line); }
table.t tbody tr { transition: background var(--d-hover) var(--ease-out); }
table.t tbody tr:hover { background: rgba(255, 255, 255, .04); }
table.t tbody tr.me { background: var(--accent-dim); }
table.t tbody tr.me td { border-bottom-color: var(--accent-border); }
table.t tbody tr:last-child td { border-bottom: 0; }
table.t .num { text-align: right; font-variant-numeric: tabular-nums; }
table.t a.rowlink { color: inherit; display: block; }
table.t a.rowlink:hover { color: var(--accent); text-decoration: none; }

.rank {
  display: inline-grid; place-items: center;
  width: 24px; height: 24px; border-radius: 7px;
  background: rgba(255, 255, 255, .06); font-size: .74rem; font-weight: 700;
}
.rank.r1 { background: #f7c548; color: #002F3F; }
.rank.r2 { background: #cfd8dc; color: #002F3F; }
.rank.r3 { background: #c98b52; color: #002F3F; }

/* ═══ Odznaky ══════════════════════════════════════════════════════════ */
.tag {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .12rem .45rem; border-radius: 5px;
  font-size: .7rem; font-weight: 600; letter-spacing: .01em;
  border: 1px solid var(--line-strong); color: var(--muted);
  white-space: nowrap;
}
.tag.win { color: var(--success); border-color: rgba(52, 211, 154, .35); background: rgba(52, 211, 154, .08); }
.tag.loss { color: var(--accent-2); border-color: rgba(242, 92, 122, .3); background: rgba(242, 92, 122, .07); }
.tag.draw { color: var(--warn); border-color: rgba(247, 197, 72, .3); background: rgba(247, 197, 72, .07); }
.tag.tm-zeleny { color: var(--accent); border-color: var(--accent-border); background: var(--accent-dim); }
.tag.tm-cerveny { color: var(--accent-2); border-color: rgba(242, 92, 122, .3); background: rgba(242, 92, 122, .07); }
.tag.solo { color: var(--info); border-color: rgba(61, 169, 252, .3); background: rgba(61, 169, 252, .07); }
.tag.ok { color: var(--accent); border-color: var(--accent-border); background: var(--accent-dim); }

.dash { color: var(--faint); }

/* ═══ Tlačítka ═════════════════════════════════════════════════════════ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  padding: .52rem .95rem; border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong); background: var(--surface-2);
  color: var(--text); font: 600 .84rem/1 var(--font); cursor: pointer;
  text-decoration: none;
  transition: transform var(--d-press) var(--ease-out),
              background var(--d-hover) var(--ease-out),
              border-color var(--d-hover) var(--ease-out);
}
.btn:hover { background: var(--surface-3); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn.primary {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .22) inset, 0 8px 18px -10px rgba(106, 190, 69, .8);
}
.btn.primary:hover { background: #7bd254; box-shadow: 0 1px 0 rgba(255,255,255,.22) inset, 0 10px 22px -10px rgba(106, 190, 69, .95); }
.btn.ghost { background: transparent; }
.btn.danger { border-color: rgba(242, 92, 122, .5); color: var(--danger); }
.btn.danger:hover { background: rgba(242, 92, 122, .1); border-color: rgba(242, 92, 122, .75); }
.btn.sm { padding: .36rem .68rem; font-size: .76rem; }
.btn[disabled], .btn.off { opacity: .45; pointer-events: none; }

/* ═══ Filtrační pruh ═══════════════════════════════════════════════════ */
.filters {
  display: flex; gap: .5rem; flex-wrap: wrap; align-items: center;
  margin-bottom: var(--gap);
}
.filters select, .filters input, .field input, .field select, .field textarea {
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  padding: .45rem .6rem; font: 400 .84rem var(--font);
}
.chips { display: flex; gap: .35rem; flex-wrap: wrap; }
.chip {
  padding: .3rem .65rem; border-radius: 20px;
  border: 1px solid var(--line-strong); background: transparent;
  color: var(--muted); font-size: .78rem; font-weight: 500; text-decoration: none;
  transition: all var(--d-hover) var(--ease-out);
}
.chip:hover { border-color: var(--line-strong); color: var(--text); text-decoration: none; }
.chip.on { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent); }

.pager { display: flex; gap: .4rem; align-items: center; justify-content: center; margin-top: 1rem; }
.pager span { font-size: .8rem; color: var(--muted); }

/* ═══ Porovnání dvou hráčů ═════════════════════════════════════════════
   Tři sloupce: co se měří, moje hodnota, jeho hodnota. Ne `<table>` —
   půlka řádků nese pruh pod číslem a buňka tabulky s pruhem uvnitř se na
   mobilu nedá zúžit bez vodorovného posuvníku.

   Neveřejné řádky (cizí detailní historie se neotevírá — kap. 7.5) mají
   prázdný pravý sloupec. Schválně zůstávají v tabulce: vypustit je by
   vypadalo, že se ta statistika nesleduje, místo aby bylo vidět, že se
   *neporovnává*. */
.cmp { display: grid; gap: .1rem; }
.cmp-row {
  display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: .8rem; align-items: start;
  padding: .55rem 0; border-top: 1px solid var(--line);
}
.cmp-head {
  border-top: 0; padding-top: 0;
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--label);
}
/* Přezdívka v hlavičce nesmí rozhodit sloupce — dlouhé jméno se ořízne. */
.cmp-head .cmp-v { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmp-k { font-size: .8rem; color: var(--muted); align-self: center; }
.cmp-k small { font-size: .68rem; }
.cmp-v { min-width: 0; font-variant-numeric: tabular-nums; }
.cmp-v b { display: block; font-size: .95rem; font-weight: 600; color: var(--text); }
.cmp-v .bar { margin-top: .35rem; }
/* Vede jen jedna strana a pozná se to barvou hodnoty, ne pruhu: obarvit
   celý pruh znamená druhou hodnotu opticky vypnout, i když je skoro
   stejná. Remíza se nezvýrazňuje vůbec. */
.cmp-v b.lead { color: var(--accent); }
.cmp-row--private .cmp-k, .cmp-row--private .cmp-v b { color: var(--muted); }
.cmp-off {
  display: inline-block; padding: .1rem .4rem; border-radius: 4px;
  border: 1px dashed var(--line-strong);
  font-size: .68rem; color: var(--faint);
}
.cmp-sum {
  display: flex; align-items: center; gap: .4rem;
  margin: .9rem 0 0; font-size: .82rem; color: var(--text);
}
.cmp-sum .ic { color: var(--accent); flex: none; }
.panel-note.warn { color: var(--warn); border-top-color: rgba(247, 197, 72, .35); }

/* ═══ Prázdné stavy ════════════════════════════════════════════════════
   Prázdný stav je součást zadání, ne detail — vždycky vede k akci. */
.empty {
  text-align: center; padding: 3rem 1.4rem;
  border: 1px dashed var(--line-strong); border-radius: var(--radius);
  background: rgba(255, 255, 255, .015);
}
.empty .ic { font-size: 2.1rem; color: var(--faint); }
.empty h3 { margin: .8rem 0 .4rem; font-size: 1.08rem; }
.empty p { margin: 0 auto 1.2rem; color: var(--muted); font-size: .87rem; max-width: 46ch; }
/* Prázdný stav uvnitř panelu (málo dat na graf) — panel má vlastní
   odsazení i rámeček, druhá vrstva by dělala rámeček v rámečku. */
.empty.flat { padding: 1.6rem 1.2rem; }

/* ═══ Seznam her ═══════════════════════════════════════════════════════ */
.mlist { display: flex; flex-direction: column; }
.mrow {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) auto auto;
  align-items: center; gap: .85rem;
  padding: .7rem .55rem; margin: 0 -.55rem;
  border-bottom: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: inherit; text-decoration: none;
  transition: background var(--d-hover) var(--ease-out),
              transform var(--d-press) var(--ease-out);
}
.mrow:last-child { border-bottom: 0; }
.mrow:hover {
  background: rgba(255, 255, 255, .05); text-decoration: none;
  transform: translateX(2px);
}
.mrow-n {
  display: grid; place-items: center; height: 36px;
  border-radius: var(--radius-sm); background: var(--surface);
  border: 1px solid var(--line);
  font-size: .72rem; font-weight: 700; color: var(--muted);
}
.mrow b { display: block; font-size: .88rem; font-weight: 600; color: var(--text); }
.mrow small { color: var(--faint); font-size: .74rem; }
.mrow-score { font-size: 1.1rem; font-weight: 700; letter-spacing: -.02em; color: var(--text); }

/* ═══ Detail hry ═══════════════════════════════════════════════════════ */
.score-hero {
  display: flex; align-items: center; gap: clamp(1.4rem, 4vw, 3.4rem); flex-wrap: wrap;
  padding: 1.5rem 1.6rem; border-radius: var(--radius);
  background:
    radial-gradient(520px 220px at 0% 0%, rgba(61, 169, 252, .13), transparent 70%),
    linear-gradient(135deg, var(--panel-2) 0%, var(--panel) 62%);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-2), var(--edge);
}
.score-hero .big {
  font-size: clamp(2.3rem, 1.4rem + 2.6vw, 3.6rem);
  font-weight: 700; line-height: 1; letter-spacing: -.04em; margin-top: .25rem;
}
.score-hero .lbl { font-size: .7rem; letter-spacing: .12em; text-transform: uppercase; color: var(--label); }
.vs {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 1rem; margin-top: .2rem;
}
.vs .side { text-align: center; padding: .7rem; border-radius: var(--radius-sm); }
.vs .side.zeleny { background: var(--accent-dim); border: 1px solid var(--accent-border); }
.vs .side.cerveny { background: rgba(242, 92, 122, .07); border: 1px solid rgba(242, 92, 122, .3); }
.vs .side b { display: block; font-size: 1.5rem; }
.vs .side small { color: var(--muted); font-size: .72rem; }
.vs .mid { color: var(--faint); font-size: .8rem; font-weight: 600; }

.kv { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 1rem .9rem; }
.kv > div { min-width: 0; }
.kv dt { font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; color: var(--label); }
/* Dlouhá hodnota se musí zalomit, ne přetéct. E-mail (25 znaků) potřeboval
   206 px do buňky široké 150 a přeléval se přes okraj panelu až pod
   sousední panel, který ho překreslil — vypadalo to jako uříznutý text. */
.kv dd { margin: .25rem 0 0; font-size: .95rem; font-weight: 600; overflow-wrap: anywhere; color: var(--text); }
/* Varianta pro údaje, které se nezalamují hezky (e-mail, členské číslo):
   míň sloupců, každý širší. */
.kv--wide { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
/* Údaj, který se nemá lámat uprostřed slova (e-mail), dostane celý řádek. */
.kv .kv-full { grid-column: 1 / -1; }

/* ═══ Zaškrtávátka ═════════════════════════════════════════════════════
   Systémové políčko se na tmavém podkladu kreslí jako bílý čtverec
   a v nastavení byly tři vedle sebe — jediné místo v aplikaci, které
   vypadalo jako neupravené HTML. Fajfka je otočený `border`, ne obrázek:
   nepotřebuje data-URI ani další požadavek. */
input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  flex: none; position: relative;
  width: 19px; height: 19px; margin: 0;
  border: 1px solid var(--line-strong); border-radius: 5px;
  background: var(--surface);
  cursor: pointer;
  transition: background var(--d-hover) var(--ease-out),
              border-color var(--d-hover) var(--ease-out);
}
input[type="checkbox"]:hover { border-color: var(--accent-border); }
input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
input[type="checkbox"]:checked::after {
  content: ""; position: absolute; left: 6px; top: 2px;
  width: 5px; height: 10px;
  border: solid var(--accent-ink); border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
/* ── řádek s volbou ──
   Zaškrtávátko + název + vysvětlení, ne holé políčko s jednou větou.
   Klikací cíl je celý řádek, ne ten devatenáctipixelový čtvereček. */
.optrow {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: .8rem; align-items: start;
  padding: .8rem .7rem; margin: 0 -.7rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--d-hover) var(--ease-out);
}
.optrow:hover { background: rgba(255, 255, 255, .045); }
.optrow + .optrow { border-top: 1px solid var(--line); border-radius: 0; }
.optrow input[type="checkbox"] { margin-top: .12rem; }
.optrow b { display: block; font-size: .88rem; font-weight: 600; }
.optrow small { display: block; color: var(--muted); font-size: .76rem; margin-top: .2rem; }
/* Zaškrtnutá volba se musí poznat i bez dívání na políčko. */
.optrow:has(input:checked) b { color: var(--text); }

/* ── seznam zařízení ──
   Dva řádky v tabulce vypadaly rozbitě: sloupec s tlačítkem měl prázdnou
   hlavičku a u aktuálního zařízení i prázdnou buňku, takže čtvrtina
   panelu byla bílé místo. Seznam řádků to unese líp a na telefonu se
   nemusí překlápět do karet. */
.dlist { display: flex; flex-direction: column; }
.drow {
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto;
  gap: .9rem; align-items: center;
  padding: .85rem .7rem; margin: 0 -.7rem;
  border-bottom: 1px solid var(--line);
  transition: background var(--d-hover) var(--ease-out);
}
.drow:last-child { border-bottom: 0; }
.drow:hover { background: rgba(255, 255, 255, .035); }
.drow-ic {
  display: grid; place-items: center; width: 40px; height: 40px;
  border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--line);
  color: var(--muted); font-size: 1.1rem;
}
.drow.now .drow-ic {
  background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent);
}
.drow b { display: block; font-size: .89rem; font-weight: 600; }
.drow-meta {
  display: flex; flex-wrap: wrap; gap: .15rem .5rem;
  color: var(--muted); font-size: .76rem; margin-top: .15rem;
}
.drow-meta .ip { font-variant-numeric: tabular-nums; }

/* ═══ Parťáci ══════════════════════════════════════════════════════════ */
.pcard {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.15rem 1.25rem;
  display: flex; flex-direction: column; gap: .6rem;
  box-shadow: var(--shadow-1), var(--edge);
  transition: transform var(--d-hover) var(--ease-out),
              box-shadow var(--d-hover) var(--ease-out);
}
.pcard:hover { transform: translateY(-2px); box-shadow: var(--shadow-2), var(--edge); }
/* Zamčená kategorie se nesmí zvedat — není na co klikat a zvednutí by
   slibovalo obsah, který tam není. */
.pcard.locked {
  border-style: dashed; background: rgba(255, 255, 255, .012); box-shadow: none;
}
.pcard.locked:hover { transform: none; box-shadow: none; }
.pcard-h { display: flex; align-items: center; gap: .5rem; }
.pcard-h .ic { color: var(--accent); font-size: 1.1rem; }
.pcard.locked .pcard-h .ic { color: var(--faint); }
.pcard-d { color: var(--muted); font-size: .8rem; margin: 0; }
.pcard-hit { display: flex; align-items: center; gap: .7rem; margin-top: .2rem; }
.pcard-hit b { font-size: 1.05rem; }
.threshold {
  font-size: .71rem; color: var(--muted);
  padding-top: .55rem; border-top: 1px dashed var(--line);
}

/* ═══ Bonusy ═══════════════════════════════════════════════════════════ */
.bstep {
  display: grid; grid-template-columns: 62px minmax(0, 1fr) auto;
  gap: .9rem; align-items: center;
  padding: .8rem 0; border-bottom: 1px solid var(--line);
}
.bstep:last-child { border-bottom: 0; }
.bstep-n {
  display: grid; place-items: center; height: 46px;
  border-radius: var(--radius-sm); font-weight: 700; font-size: .92rem;
  background: var(--surface); border: 1px solid var(--line); color: var(--muted);
}
.bstep.done .bstep-n { background: var(--accent-dim); border-color: var(--accent-border); color: var(--accent); }
.bstep.ready .bstep-n { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.bstep b { font-size: .9rem; }
.bstep small { display: block; color: var(--faint); font-size: .74rem; margin-top: .12rem; }
.bstep.locked { opacity: .62; }

/* ═══ Akce ═════════════════════════════════════════════════════════════ */
.ecard {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.15rem 1.25rem;
  display: flex; flex-direction: column; gap: .6rem;
  box-shadow: var(--shadow-1), var(--edge);
  transition: transform var(--d-hover) var(--ease-out),
              box-shadow var(--d-hover) var(--ease-out);
}
.ecard:hover { transform: translateY(-2px); box-shadow: var(--shadow-2), var(--edge); }
.ecard.signed { border-color: var(--accent-border); background: linear-gradient(180deg, var(--accent-dim), transparent 60%), var(--panel); }
.ecard-date {
  display: inline-flex; align-items: baseline; gap: .35rem;
  font-size: .74rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent); font-weight: 700;
}
.ecard p { margin: 0; color: var(--muted); font-size: .84rem; }
.ecard-foot { display: flex; align-items: center; justify-content: space-between; gap: .7rem; margin-top: auto; }

/* ═══ Chat ═════════════════════════════════════════════════════════════ */
.chat-wrap { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: var(--gap); align-items: start; }
.glist a {
  display: block; padding: .6rem .7rem; border-radius: var(--radius-sm);
  color: var(--text); text-decoration: none; border: 1px solid transparent;
}
.glist a:hover { background: rgba(255, 255, 255, .04); text-decoration: none; }
.glist a.on { background: var(--accent-dim); border-color: var(--accent-border); }
.glist b { display: block; font-size: .85rem; }
.glist small { color: var(--faint); font-size: .72rem; }

.msg { display: flex; gap: .65rem; padding: .6rem 0; border-bottom: 1px solid var(--line); }
.msg:last-child { border-bottom: 0; }
.msg-b { min-width: 0; }
.msg-b b { font-size: .82rem; }
.msg-b time { color: var(--faint); font-size: .72rem; margin-left: .4rem; }
.msg-b p { margin: .2rem 0 0; font-size: .87rem; color: var(--text); overflow-wrap: anywhere; }

/* ── pole pro psaní ──
   Vlastní schránka místo systémového `<input>` vedle tlačítka. Rám drží
   `.composer-box`, ne samotné pole — proto se odsazení, tlačítko i hláška
   o soukromí vejdou dovnitř a celek vypadá jako jeden prvek.

   Zvýraznění při psaní jede na `:focus-within`, tedy bez skriptu.
   Textarea má vypnutý vlastní `outline`, aby se rám nekreslil dvakrát;
   viditelnost fokusu přebírá schránka (i pro klávesnici). */
.composer {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: .7rem; align-items: start;
  margin-top: 1.1rem; padding-top: 1rem;
  border-top: 1px solid var(--line);
}
.composer > .avatar { margin-top: .3rem; }
.composer-box {
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  transition: border-color var(--d-hover) var(--ease-out),
              box-shadow var(--d-hover) var(--ease-out);
}
.composer-box:focus-within {
  border-color: var(--accent-border);
  box-shadow: 0 0 0 3px rgba(106, 190, 69, .13);
}
.composer textarea {
  display: block; width: 100%; resize: none;
  min-height: 58px; padding: .7rem .85rem .1rem;
  background: transparent; border: 0; outline: none;
  color: var(--text); font: 400 .88rem/1.55 var(--font);
}
.composer textarea::placeholder { color: var(--faint); }
.composer-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: .7rem; padding: .3rem .5rem .5rem .85rem;
}
/* Komu je zpráva vidět, musí být u pole, ne v poznámce pod panelem —
   jinak to člověk přečte až potom, co odeslal. */
.composer-hint {
  display: flex; align-items: center; min-width: 0;
  font-size: .73rem; color: var(--muted);
}
.composer-hint .ic { flex: none; }

/* ═══ Přihlášení ═══════════════════════════════════════════════════════ */
.auth {
  min-height: 100vh; min-height: 100dvh;
  display: grid; place-items: center;
  padding: max(1.5rem, var(--safe-t)) 1.5rem max(1.5rem, var(--safe-b));
  background:
    radial-gradient(900px 460px at 50% -8%, rgba(106, 190, 69, .13), transparent 65%),
    var(--page);
}
.auth-box { width: 100%; max-width: 430px; }
.auth-card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.8rem;
  box-shadow: var(--shadow-2), var(--edge);
}
.auth-card h1 { font-size: 1.32rem; margin-bottom: .35rem; }
.auth-card > p { color: var(--muted); font-size: .85rem; margin: 0 0 1.2rem; }
.field { margin-bottom: .95rem; }
.field label { display: block; font-size: .78rem; font-weight: 600; margin-bottom: .3rem; }
.field input { width: 100%; }
.field .hint { font-size: .74rem; color: var(--faint); margin-top: .3rem; }
.code-input {
  width: 100%; text-align: center;
  font-size: 1.7rem; font-weight: 700; letter-spacing: .5em;
  text-indent: .5em; padding: .7rem !important;
}
.auth-logo { display: flex; justify-content: center; margin-bottom: 1.2rem; }
.auth-logo img { height: 34px; }
.auth-foot { text-align: center; margin-top: 1rem; font-size: .78rem; color: var(--faint); }

.note {
  border-radius: var(--radius-sm); padding: .7rem .85rem;
  font-size: .8rem; border: 1px solid var(--line-strong);
  background: rgba(255, 255, 255, .03);
}
.note.warn { border-color: rgba(247, 197, 72, .35); background: rgba(247, 197, 72, .07); }
.note.bad { border-color: rgba(242, 92, 122, .35); background: rgba(242, 92, 122, .07); }
.note.demo { border-color: var(--accent-border); background: var(--accent-dim); }
.note b { display: block; margin-bottom: .15rem; }
.note + .note, .note + .field, .field + .note { margin-top: .8rem; }

.demo-pick { margin-top: 1rem; }
.demo-pick h3 { font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; color: var(--label); margin-bottom: .5rem; }
.demo-pick button {
  display: block; width: 100%; text-align: left;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: .55rem .7rem; margin-bottom: .4rem;
  color: var(--text); font: 400 .8rem var(--font); cursor: pointer;
  transition: border-color var(--d-hover) var(--ease-out);
}
.demo-pick button:hover { border-color: var(--accent-border); }
.demo-pick b { display: block; font-size: .82rem; }
.demo-pick small { color: var(--faint); }

.flash {
  margin-bottom: var(--gap); padding: .65rem .9rem;
  border-radius: var(--radius-sm);
  background: var(--accent-dim); border: 1px solid var(--accent-border);
  color: var(--text); font-size: .84rem;
}

/* ═══ Drobnosti ════════════════════════════════════════════════════════ */
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.stack { display: flex; flex-direction: column; gap: var(--gap); }
.row { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: .7rem; }
.credit {
  margin-top: var(--gap); font-size: .77rem; color: var(--muted);
  padding: .7rem .9rem; border-left: 2px solid var(--line-strong);
}
/* Řádek seznamu uvnitř panelu (bonus k vyzvednutí, akce, herní mód).
   Byl to nejčastější inline `padding` v šablonách. */
.listrow { padding: .45rem 0; }
.listrow + .listrow { border-top: 1px solid var(--line); }
.listrow b { color: var(--text); }
.mode-n { font-size: .87rem; font-weight: 600; color: var(--text); }
.code-otp { font-size: 1.2rem; letter-spacing: .22em; }

/* ═══ Utility ══════════════════════════════════════════════════════════
   Proč vůbec existují: CSP posílá `style-src 'self'` BEZ `'unsafe-inline'`,
   což zakazuje i atribut `style=`. Prohlížeč ho zahodí ještě před výpočtem
   layoutu — všech ~70 inline stylů v šablonách bylo tiše neúčinných
   (sloupce grafů měly 2 px, mřížky neměly odsazení, tlačítka nebyla přes
   celou šířku). Řešení není povolit inline styly, ale skládat rozměr ze
   statické, kešovatelné třídy.

   Škála je záměrně krátká. Když někde chybí krok, patří to nejspíš do
   komponenty (jako .listrow nebo .bar--short), ne do další utility. */
.m-0 { margin: 0; }
.mt-1 { margin-top: .3rem; }
.mt-2 { margin-top: .55rem; }
.mt-3 { margin-top: .8rem; }
.mt-4 { margin-top: 1rem; }
.mt-5 { margin-top: 2rem; }
.mt-gap { margin-top: var(--gap); }
.mb-1 { margin-bottom: .3rem; }
.mb-2 { margin-bottom: .6rem; }
.ml-auto { margin-left: auto; }
.py-2 { padding: .5rem 0; }
.w-full { width: 100%; }
.fs-xs { font-size: .74rem; }
.fs-sm { font-size: .82rem; }
.fs-lg { font-size: 1.06rem; }
.fw-400 { font-weight: 400; }
.pointer { cursor: pointer; }

/* Krokové rozměry pro datové prvky. Hodnoty vrací vrstva 2 jako celá
   procenta, filtr `pct` je jen ořízne do rozsahu — v šabloně tím pádem
   nevzniká žádné nové rozhodnutí o datech, jen jiný zápis téhož čísla.
   Generuje se skriptem, ručně sem nesahat (viz níž vygenerovaný blok). */
/* >>> generováno: krokové šířky (.w-0 … .w-100) */
.w-0{width:0%} .w-1{width:1%} .w-2{width:2%} .w-3{width:3%} .w-4{width:4%} .w-5{width:5%} .w-6{width:6%} .w-7{width:7%} .w-8{width:8%} .w-9{width:9%} .w-10{width:10%} .w-11{width:11%} .w-12{width:12%} .w-13{width:13%} .w-14{width:14%} .w-15{width:15%} .w-16{width:16%} .w-17{width:17%} .w-18{width:18%} .w-19{width:19%} .w-20{width:20%} .w-21{width:21%} .w-22{width:22%} .w-23{width:23%} .w-24{width:24%} .w-25{width:25%} .w-26{width:26%} .w-27{width:27%} .w-28{width:28%} .w-29{width:29%} .w-30{width:30%} .w-31{width:31%} .w-32{width:32%} .w-33{width:33%} .w-34{width:34%} .w-35{width:35%} .w-36{width:36%} .w-37{width:37%} .w-38{width:38%} .w-39{width:39%} .w-40{width:40%} .w-41{width:41%} .w-42{width:42%} .w-43{width:43%} .w-44{width:44%} .w-45{width:45%} .w-46{width:46%} .w-47{width:47%} .w-48{width:48%} .w-49{width:49%} .w-50{width:50%} .w-51{width:51%} .w-52{width:52%} .w-53{width:53%} .w-54{width:54%} .w-55{width:55%} .w-56{width:56%} .w-57{width:57%} .w-58{width:58%} .w-59{width:59%} .w-60{width:60%} .w-61{width:61%} .w-62{width:62%} .w-63{width:63%} .w-64{width:64%} .w-65{width:65%} .w-66{width:66%} .w-67{width:67%} .w-68{width:68%} .w-69{width:69%} .w-70{width:70%} .w-71{width:71%} .w-72{width:72%} .w-73{width:73%} .w-74{width:74%} .w-75{width:75%} .w-76{width:76%} .w-77{width:77%} .w-78{width:78%} .w-79{width:79%} .w-80{width:80%} .w-81{width:81%} .w-82{width:82%} .w-83{width:83%} .w-84{width:84%} .w-85{width:85%} .w-86{width:86%} .w-87{width:87%} .w-88{width:88%} .w-89{width:89%} .w-90{width:90%} .w-91{width:91%} .w-92{width:92%} .w-93{width:93%} .w-94{width:94%} .w-95{width:95%} .w-96{width:96%} .w-97{width:97%} .w-98{width:98%} .w-99{width:99%} .w-100{width:100%}
/* >>> generováno: krokové výšky (.h-0 … .h-100) */
.h-0{height:0%} .h-1{height:1%} .h-2{height:2%} .h-3{height:3%} .h-4{height:4%} .h-5{height:5%} .h-6{height:6%} .h-7{height:7%} .h-8{height:8%} .h-9{height:9%} .h-10{height:10%} .h-11{height:11%} .h-12{height:12%} .h-13{height:13%} .h-14{height:14%} .h-15{height:15%} .h-16{height:16%} .h-17{height:17%} .h-18{height:18%} .h-19{height:19%} .h-20{height:20%} .h-21{height:21%} .h-22{height:22%} .h-23{height:23%} .h-24{height:24%} .h-25{height:25%} .h-26{height:26%} .h-27{height:27%} .h-28{height:28%} .h-29{height:29%} .h-30{height:30%} .h-31{height:31%} .h-32{height:32%} .h-33{height:33%} .h-34{height:34%} .h-35{height:35%} .h-36{height:36%} .h-37{height:37%} .h-38{height:38%} .h-39{height:39%} .h-40{height:40%} .h-41{height:41%} .h-42{height:42%} .h-43{height:43%} .h-44{height:44%} .h-45{height:45%} .h-46{height:46%} .h-47{height:47%} .h-48{height:48%} .h-49{height:49%} .h-50{height:50%} .h-51{height:51%} .h-52{height:52%} .h-53{height:53%} .h-54{height:54%} .h-55{height:55%} .h-56{height:56%} .h-57{height:57%} .h-58{height:58%} .h-59{height:59%} .h-60{height:60%} .h-61{height:61%} .h-62{height:62%} .h-63{height:63%} .h-64{height:64%} .h-65{height:65%} .h-66{height:66%} .h-67{height:67%} .h-68{height:68%} .h-69{height:69%} .h-70{height:70%} .h-71{height:71%} .h-72{height:72%} .h-73{height:73%} .h-74{height:74%} .h-75{height:75%} .h-76{height:76%} .h-77{height:77%} .h-78{height:78%} .h-79{height:79%} .h-80{height:80%} .h-81{height:81%} .h-82{height:82%} .h-83{height:83%} .h-84{height:84%} .h-85{height:85%} .h-86{height:86%} .h-87{height:87%} .h-88{height:88%} .h-89{height:89%} .h-90{height:90%} .h-91{height:91%} .h-92{height:92%} .h-93{height:93%} .h-94{height:94%} .h-95{height:95%} .h-96{height:96%} .h-97{height:97%} .h-98{height:98%} .h-99{height:99%} .h-100{height:100%}
/* >>> generováno: sloupec v mřížce heatmapy (.gc-1 … .gc-53) */
.gc-1{grid-column-start:1} .gc-2{grid-column-start:2} .gc-3{grid-column-start:3} .gc-4{grid-column-start:4} .gc-5{grid-column-start:5} .gc-6{grid-column-start:6} .gc-7{grid-column-start:7} .gc-8{grid-column-start:8} .gc-9{grid-column-start:9} .gc-10{grid-column-start:10} .gc-11{grid-column-start:11} .gc-12{grid-column-start:12} .gc-13{grid-column-start:13} .gc-14{grid-column-start:14} .gc-15{grid-column-start:15} .gc-16{grid-column-start:16} .gc-17{grid-column-start:17} .gc-18{grid-column-start:18} .gc-19{grid-column-start:19} .gc-20{grid-column-start:20} .gc-21{grid-column-start:21} .gc-22{grid-column-start:22} .gc-23{grid-column-start:23} .gc-24{grid-column-start:24} .gc-25{grid-column-start:25} .gc-26{grid-column-start:26} .gc-27{grid-column-start:27} .gc-28{grid-column-start:28} .gc-29{grid-column-start:29} .gc-30{grid-column-start:30} .gc-31{grid-column-start:31} .gc-32{grid-column-start:32} .gc-33{grid-column-start:33} .gc-34{grid-column-start:34} .gc-35{grid-column-start:35} .gc-36{grid-column-start:36} .gc-37{grid-column-start:37} .gc-38{grid-column-start:38} .gc-39{grid-column-start:39} .gc-40{grid-column-start:40} .gc-41{grid-column-start:41} .gc-42{grid-column-start:42} .gc-43{grid-column-start:43} .gc-44{grid-column-start:44} .gc-45{grid-column-start:45} .gc-46{grid-column-start:46} .gc-47{grid-column-start:47} .gc-48{grid-column-start:48} .gc-49{grid-column-start:49} .gc-50{grid-column-start:50} .gc-51{grid-column-start:51} .gc-52{grid-column-start:52} .gc-53{grid-column-start:53}

/* ═══ Mobilní navigace ═════════════════════════════════════════════════
   Vodorovně rolovací lišta s deseti položkami byla na telefonu k ničemu:
   půlka menu byla mimo obraz a palec na ni nedosáhl. Nahradila ji spodní
   lišta se čtyřmi sekcemi (`config.NAV_PRIMARY`) a šuplík se zbytkem.

   Šuplík běží na :target, ne na JS — projekt je záměrně bezskriptový
   a CSP tady povoluje jen `script-src 'self'`. Vedlejší efekt je vítaný:
   systémové tlačítko Zpět šuplík zavře.

   Oba prvky jsou nad 820 px schované; zapínají se v mediálním dotazu níž. */
.tabbar, .sheet-wrap { display: none; }

/* ── spodní lišta ── */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  -webkit-backdrop-filter: saturate(160%) blur(16px);
  backdrop-filter: saturate(160%) blur(16px);
  border-top: 1px solid var(--line-strong);
  padding-bottom: var(--safe-b);
}
.tab {
  position: relative;
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .16rem;
  /* 56 px je nejmenší cíl, který se dá trefit palcem naslepo. */
  min-height: 56px; padding: .45rem .15rem;
  color: var(--faint); font-size: .63rem; font-weight: 600;
  text-decoration: none;
  transition: color var(--d-hover) var(--ease-out);
}
.tab:hover { text-decoration: none; }
.tab:active { background: rgba(255, 255, 255, .04); }
.tab .ic { font-size: 1.3rem; }
.tab span {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tab.on { color: var(--accent); }
.tab.on::before {
  content: ""; position: absolute; top: 0; left: 50%;
  width: 24px; height: 2px; margin-left: -12px;
  border-radius: 0 0 2px 2px; background: var(--accent);
}
.tab .nav-badge {
  position: absolute; top: 3px; left: 50%; margin-left: 3px;
  min-width: 16px; height: 16px; line-height: 16px; font-size: .62rem;
}

/* ── šuplík ── */
.sheet-wrap { position: fixed; inset: 0; z-index: 60; visibility: hidden; }
.sheet-wrap:target { visibility: visible; }

.sheet-scrim {
  position: absolute; inset: 0;
  background: rgba(0, 12, 17, .62);
  opacity: 0; transition: opacity 220ms var(--ease-out);
}
.sheet-wrap:target .sheet-scrim { opacity: 1; }

.sheet {
  position: absolute; left: 0; right: 0; bottom: 0;
  max-height: 84vh; max-height: 84dvh; overflow-y: auto;
  background: var(--panel);
  border-top: 1px solid var(--line-strong);
  border-radius: 18px 18px 0 0;
  padding: .45rem 1rem calc(1.1rem + var(--safe-b));
  box-shadow: 0 -20px 46px rgba(0, 0, 0, .5);
  transform: translateY(102%);
  transition: transform 280ms var(--ease-out);
  overscroll-behavior: contain;
}
.sheet-wrap:target .sheet { transform: none; }

.sheet-grab {
  display: block; width: 38px; height: 4px; border-radius: 2px;
  background: var(--line-strong); margin: .3rem auto .85rem;
}
.sheet-user {
  display: flex; align-items: center; gap: .65rem;
  padding-bottom: .85rem; margin-bottom: .85rem;
  border-bottom: 1px solid var(--line);
}
.sheet-user > div { min-width: 0; flex: 1; }
.sheet-user b { display: block; font-size: .9rem; font-weight: 600; }
.sheet-user small { color: var(--faint); font-size: .74rem; }
.sheet-x {
  flex: none; display: grid; place-items: center;
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--line);
  color: var(--muted);
}
.sheet-x:hover { text-decoration: none; }

.sheet-nav { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
.sheet-nav a {
  position: relative;
  display: flex; align-items: center; gap: .6rem;
  min-height: 50px; padding: .6rem .7rem;
  border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--line);
  color: var(--text); font-size: .84rem; font-weight: 600;
  text-decoration: none;
}
.sheet-nav a:hover { text-decoration: none; }
.sheet-nav a:active { background: var(--surface-2); }
.sheet-nav a .ic { font-size: 1.15rem; color: var(--faint); flex: none; }
.sheet-nav a span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sheet-nav a.on { background: var(--accent-dim); border-color: var(--accent-border); }
.sheet-nav a.on .ic { color: var(--accent); }
.sheet-nav .nav-badge { margin-left: auto; }

.sheet-out {
  display: flex; align-items: center; justify-content: center;
  min-height: 48px; margin-top: .6rem; padding: .7rem;
  border-radius: var(--radius-sm); border: 1px solid var(--line-strong);
  color: var(--muted); font-size: .85rem; font-weight: 600;
}
.sheet-out:hover { text-decoration: none; }

/* ═══ PWA vrstva ═══════════════════════════════════════════════════════
   Tři pruhy nad spodní lištou: „nejsi online", „nová verze" a nabídka
   přidání na plochu. Všechny vykresluje server s atributem `hidden`
   a odkrývá je `static/js/pwa.js` — v prohlížeči bez JS tu prostě nejsou.

   Jsou DOLE, ne pod hlavičkou. Pruh u horní hrany na telefonu buď zakryje
   nadpis stránky, nebo hlavičku posune (a ta se skládá scroll timeline,
   takže by se posunul i výpočet její výšky). Dole leží nad lištou, kde je
   palec, a nic nepřekrývají — proto má obal `pointer-events: none` a jen
   samotné pruhy ho zase zapínají.

   `[hidden]` se musí přebít výslovně: `display: flex` níž by ho jinak
   převálcovalo a pruh by svítil pořád. */
.pwa-stack {
  position: fixed; left: 0; right: 0; z-index: 45;
  bottom: calc(var(--safe-b) + .7rem);
  display: grid; gap: .45rem; justify-items: center;
  padding-inline: .7rem;
  pointer-events: none;
}
.pwa-stack > * { pointer-events: auto; }

.pwa-bar {
  width: 100%; max-width: 520px; margin: 0;
  display: flex; align-items: center; gap: .55rem;
  padding: .7rem .8rem;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: color-mix(in srgb, var(--panel) 94%, transparent);
  -webkit-backdrop-filter: saturate(160%) blur(16px);
  backdrop-filter: saturate(160%) blur(16px);
  box-shadow: 0 12px 34px -14px rgba(0, 0, 0, .85);
  color: var(--text); font-size: .8rem; line-height: 1.35;
  animation: rise var(--d-pop) var(--ease-out) both;
}
.pwa-bar[hidden] { display: none; }
.pwa-bar .ic { flex: none; }

/* Offline je stav, ne chyba — proto varovná barva jen na ikoně a lince,
   ne celá plocha. Uložené stránky se dají číst dál a pruh nic neblokuje. */
.pwa-offline { border-color: rgba(247, 197, 72, .45); color: var(--text); }
.pwa-offline .ic { color: var(--warn); }

.pwa-update {
  justify-content: center;
  border-color: var(--accent-border); color: var(--text-strong);
  font: 600 .82rem/1.3 var(--font);
  min-height: 46px; cursor: pointer; text-align: left;
}
.pwa-update .ic { color: var(--accent); }
.pwa-update:active { transform: translateY(1px); }

/* Nabídka instalace: ikona vlevo, text uprostřed, akce vpravo. Ikona je
   ta samá, kterou dostane plocha telefonu — člověk vidí, co si přidává. */
/* Tlačítko je VŽDYCKY na vlastním řádku pod textem, i na širokém okně.
   Vedle textu by mu zbylo ~150 px a na popis 100 — a hlavně: přes celou
   šířku pruhu je z něj cíl, který se dá trefit palcem naslepo. Pruh je
   `max-width: 520px`, takže z toho ani na desktopu není přeplácnutý pás. */
.pwa-install { position: relative; align-items: flex-start; flex-wrap: wrap; gap: .7rem; }
.pwa-ico {
  flex: none; width: 40px; height: 40px; border-radius: 10px;
  border: 1px solid var(--line);
}
.pwa-t { flex: 1; min-width: 0; }
/* Odsazení jen u nadpisu, ne u celého pruhu: křížek sedí v pravém horním
   rohu, takže by prázdné místo pod ním jinak posunulo i tlačítko a to by
   nekončilo v ose s ostatními hranami. */
.pwa-t b { display: block; padding-right: 2.4rem; font-size: .86rem; font-weight: 600; color: var(--text-strong); }
.pwa-t small { display: block; margin-top: .12rem; font-size: .74rem; color: var(--muted); }
.pwa-t small[hidden] { display: none; }
/* Zvýrazněný název v návodu („Sdílet", „Přidat na plochu") je `<b>` uvnitř
   `<small>`, takže musí zrušit i odsazení, které si nadpis drží kvůli
   křížku v rohu — jinak za každým zvýrazněním zůstane 2,4rem díra. */
.pwa-t small b { display: inline; padding-right: 0; font-size: inherit; color: var(--text); }
.pwa-act { flex: 1 1 100%; display: flex; gap: .35rem; align-items: center; }
.pwa-act .btn.primary { flex: 1; }
.pwa-act .btn[hidden] { display: none; }
/* Křížek je VYJMUTÝ z toku, v rohu pruhu. V řádku s akcemi by na iOS
   (kde tlačítko „Přidat na plochu" neexistuje — nabídka je návod) zůstal
   sám v prázdném pásu přes celou šířku. Roh drží obě varianty stejně
   a 44 px je nejmenší cíl, který se dá trefit palcem naslepo. */
.pwa-close {
  position: absolute; top: .2rem; right: .2rem;
  display: grid; place-items: center;
  width: 44px; height: 44px; min-height: 0; padding: 0;
  color: var(--faint); border-color: transparent; background: transparent;
}
.pwa-close:hover { background: var(--surface-2); }

/* ═══ Režim aplikace (`display-mode: standalone`) ═══════════════════════
   Třídu `is-app` dává `pwa.js` (iOS dlouho neuměl `display-mode`, takže
   se to nedá napsat jen mediálním dotazem).

   Co se v aplikaci mění a proč: chybí adresní řádek, tedy i tlačítko
   Zpět a odkaz „na hlavní web". Navigaci nese spodní lišta a šuplík, takže
   dvacet odkazů v patičce je jen dlouhý svitek pod každou stránkou —
   zůstane z ní značka, návrat na web a copyright. Nic z toho neplatí
   v prohlížeči, kde je patička jediná cesta ven. */
.is-app .footer-links { display: none; }
.is-app .footer { padding-bottom: calc(1.2rem + var(--safe-b)); }
/* Zpětný odkaz na detailu hry. V prohlížeči ho stránka NEPOTŘEBUJE — má
   tlačítko Zpět; v nainstalované aplikaci žádné není (a na starším iOS
   nefunguje ani gesto od hrany), takže se z detailu nedá odejít jinak než
   přes spodní lištu, která vede o úroveň výš, ne zpátky.
   Dvě cesty ke stejnému stavu schválně: třídu dává `pwa.js`, mediální
   dotaz zabere i tehdy, když se skript nenačte. */
.backlink { display: none; }
.is-app .backlink { display: inline-flex; }
@media (display-mode: standalone) { .backlink { display: inline-flex; } }

/* Dvojklik-zoom na iOS trefoval přesně stránkovací šipky a filtrační
   chipy — dvě rychlá klepnutí vedle sebe jsou tam běžný pohyb. `manipulation`
   vypne JEN dvojklik; posouvání i pinch-zoom zůstávají, takže se to nesmí
   zaměňovat s `maximum-scale=1` ve viewportu, které zvětšování zakáže úplně
   a dělat se nesmí. Píše se to na prvky, aby to pozdější třída mohla přebít. */
a, button, summary, label, .btn, .pill, .chip, .tab,
input[type="checkbox"], input[type="radio"], select {
  touch-action: manipulation;
}

/* ═══ Vstupní animace ══════════════════════════════════════════════════
   Krátké nadechnutí obsahu, ne efekt. Stránka se překresluje při každém
   filtru a stránkování, takže cokoli delšího než čtvrt vteřiny by začalo
   překážet. `prefers-reduced-motion` je ošetřený úplně dole. */
@keyframes rise {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
.page > * { animation: rise .34s var(--ease-out) both; }
.page > *:nth-child(2) { animation-delay: .04s; }
.page > *:nth-child(3) { animation-delay: .08s; }
.page > *:nth-child(n+4) { animation-delay: .12s; }

/* ═══ Desktop ══════════════════════════════════════════════════════════
   Nad 820 px platí to, co dělá z aplikace desktopovou aplikaci a ne
   roztaženou mobilní stránku. */
@media (min-width: 821px) {
  /* Ukotvená hlavička tabulky. `/hry` má u veterána 420 řádků; při
     odrolování nebylo poznat, který sloupec je skóre a který umístění.
     Scroller `.tw` se tady ruší — v jedné ose zapnutý overflow dělá
     scroll container i ve druhé a sticky by se nemělo čeho chytit.
     Podklad musí být plná `--panel`, jinak při odrolování prosvítá řádek. */
  .tw { overflow: visible; margin: 0; padding: 0; }
  table.t thead th {
    position: sticky; top: 0; z-index: 2;
    background: var(--panel);
    box-shadow: 0 1px 0 var(--line-strong);
  }

  /* Přehled na dva sloupce: 6 = celá šířka, 4 → 6 (samostatný řádek),
     3 = polovina, 2 = polovina. */
  .dash > .c-6, .dash > .c-4 { grid-column: span 6; }
  .dash > .c-3, .dash > .c-2 { grid-column: span 3; }
}

/* Třetí stupeň mřížky a o stupeň větší rytmus. Do redesignu neexistoval
   žádný mediální dotaz směrem nahoru — layout byl na 1024 i na 2560 px
   úplně stejný, takže velký monitor jen přidával prázdno. */
@media (min-width: 1400px) {
  :root { --fs: 15.5px; --gap: 20px; }
  .dash > .c-4 { grid-column: span 4; }
  .dash > .c-3 { grid-column: span 3; }
  .dash > .c-2 { grid-column: span 2; }
  .chartbox { --chart-h: 230px; }
  .panel { padding: 1.25rem 1.4rem; }
  .nav-center { gap: .35rem; }
}

/* ═══ Responzivita ═════════════════════════════════════════════════════
   Čtyři stupně: 1100 px překlopí NAVIGACI z hlavičky do spodní lišty,
   1000 px zúží mřížky, 820 px překlopí tabulky do karet a doladí rytmus,
   400 px nejužší telefony.

   Navigace se přepíná dřív než zbytek layoutu schválně: devět pilulek
   a účet potřebují ~1100 px, kdežto dvousloupcové mřížky drží až do 820.
   Svázat obojí na jedno číslo znamenalo buď zlomenou řádku v hlavičce,
   nebo spodní lištu na tabletu, kde je pro ni dost místa v hlavičce. */
@media (max-width: 1100px) {
  /* Z hlavičky zůstane logo a PULS — značka a hrana. Navigaci přebírá
     spodní lišta se šuplíkem (`.tabbar` / `.sheet`). */
  .nav-center { display: none; }
  .tabbar { display: flex; }
  .sheet-wrap { display: block; }
  /* Bez řádky pilulek je hlavička jen logo + PULS, takže může být nižší
     a spodní řádka drží jen účet. */
  :root { --nav-row-h: 46px; }
  /* Poslední panel nesmí skončit pod spodní lištou. */
  body { padding-bottom: calc(var(--tabbar) + var(--safe-b)); }
  /* Se spodní lištou se PWA pruhy posunou nad ni — jinak by překryly
     zrovna ty čtyři cíle, na které se míří palcem nejčastěji. */
  .pwa-stack { bottom: calc(var(--tabbar) + var(--safe-b) + .5rem); }
}

@media (max-width: 1000px) {
  .g-side, .g-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .g-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .chat-wrap { grid-template-columns: 1fr; }
}

@media (max-width: 820px) {

  /* ── hlavička ──
     Na telefonu je z hlavičky jen značka: účet i odhlášení jsou v šuplíku
     pod „Víc", takže pravý shluk v řádce nemá co nést. Zůstává logo, PULS
     a spodní řádka s vlastní výškou nula — hlavička je tím pádem jen pás
     s logem a linkou pod ním. */
  .nav-right { display: none; }
  :root { --nav-row-h: 8px; }
  .nav-tag { font-size: .64rem; padding-left: .6rem; }

  /* ── rytmus stránky ── */
  .page { padding: 1rem .85rem 1.2rem; padding-top: calc(var(--nav-h) + 1rem); }
  .page-head { margin-bottom: 1rem; gap: .8rem; padding-bottom: .85rem; }
  .page-head h1 { font-size: 1.32rem; }
  .page-head p { font-size: .82rem; }
  /* Přehled je na telefonu jeden sloupec — dva sloupce panelů by daly
     grafu i tabulce sto šedesát pixelů. */
  .dash > * { grid-column: span 6; }
  /* Osamocené tlačítko v hlavičce (Rezervovat hru) přes celou šířku —
     vedle nadpisu by se lámalo do dvou řádků. */
  .page-head > .btn { width: 100%; }
  .page-head > .row { width: 100%; }
  .page-head > .row > .btn { flex: 1; }

  .panel { padding: .95rem .9rem; }
  /* Dlouhý dovětek vpravo („43 her · odehráno 16 dní“) nesmí lámat nadpis
     panelu — radši spadne na vlastní řádek pod něj. */
  .panel-head { flex-wrap: wrap; row-gap: .2rem; margin-bottom: .8rem; padding-bottom: .6rem; }
  .panel-head h2 { flex: 1 1 auto; }
  .panel-head .more { font-size: .74rem; }
  /* Patička na telefonu: lockup, pod ním odkazy ve dvou sloupcích a
     nakonec drobnosti — vedle sebe by z toho byly čtyři slepené sloupce. */
  .footer { padding-inline: .85rem; }
  .footer-puls { margin-block: 1.8rem calc(var(--puls-w) * (var(--beat-h) - 1) / 2 + .6rem); }
  .footer-row { flex-direction: column; align-items: flex-start; gap: .9rem; }
  .footer-links {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 100%; gap: .35rem 1rem;
  }
  .footer-mail { margin-left: 0; }
  .footer-bottom { flex-direction: column; align-items: flex-start; gap: .3rem; }

  /* ── mřížky a dlaždice ──
     Dlaždice zůstávají po dvou. Jeden sloupec by z přehledu udělal
     metrový svitek, ve kterém se čtyři čísla nedají porovnat. */
  .g-4, .g-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .g-2, .g-side, .g-cards { grid-template-columns: 1fr; }
  .tile { padding: .7rem .75rem; }
  .tile-k { font-size: .62rem; letter-spacing: .07em; }
  .tile-v { font-size: 1.45rem; }
  .tile-s { font-size: .7rem; }
  .kv { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .7rem; }

  /* ── ovládací prvky: iOS zvětší stránku u každého pole pod 16 px ── */
  .filters select, .filters input,
  .field input, .field select, .field textarea {
    font-size: 16px; min-height: 44px;
  }
  .filters { gap: .45rem; }
  .filters select, .filters input { flex: 1 1 45%; min-width: 0; }
  .filters .btn { min-height: 44px; flex: 1 1 auto; }
  .filters > .faint { margin-left: 0 !important; width: 100%; }
  .btn { min-height: 42px; }
  .btn.sm { min-height: 38px; padding: .4rem .7rem; }
  /* Na dotek musí být cíl větší; políčko je vlastní (appearance: none),
     takže se dá zvětšit i s fajfkou. */
  input[type="checkbox"] { width: 22px; height: 22px; }
  input[type="checkbox"]:checked::after { left: 7px; top: 3px; width: 6px; height: 11px; }
  /* Popisek se musí lámat vedle zaškrtávátka, ne pod ně. */
  label.row { flex-wrap: nowrap; align-items: flex-start; }
  label.row > input[type="checkbox"] { flex: none; margin-top: .05rem; }
  label.row > span { flex: 1; min-width: 0; }
  .optrow { padding: .8rem .6rem; }
  /* Zařízení: tlačítko „Odhlásit“ pod popis, ne vedle — na 360 px by
     ukouslo polovinu řádku a název zařízení by se lámal po slabikách. */
  .drow { grid-template-columns: 40px minmax(0, 1fr); row-gap: .5rem; }
  .drow-act { grid-column: 2 / -1; justify-self: start; }

  .chips {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    width: 100%; padding-bottom: 2px; -webkit-overflow-scrolling: touch;
  }
  .chips::-webkit-scrollbar { display: none; }
  .chip { flex: none; min-height: 38px; display: inline-flex; align-items: center; }

  .pager .btn { flex: 1 1 0; }
  .pager span { flex: none; font-size: .74rem; }

  /* ── grafy ──
     Denní graf má až 365 sloupců. Na 360 px vyjde na sloupec zlomek
     pixelu, takže se místo smrsknutí roluje. */
  .chartbox { --chart-h: 132px; --lbl: 1.3rem; gap: .4rem; }
  .chart.dense {
    --chart-h: 96px;
    overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .chart.dense::-webkit-scrollbar { display: none; }
  .chart.dense .chart-col { min-width: 4px; }
  .chart-x { font-size: .58rem; }
  .chart-y { font-size: .58rem; }
  .chart-legend { font-size: .68rem; gap: .5rem; flex-wrap: wrap; }

  /* Porovnání: tři sloupce vedle sebe znamenají na 360 px popisek přes
     čtyři řádky. Popisek jde proto nad dvojici hodnot. */
  .cmp-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: .3rem .8rem; padding: .7rem 0;
  }
  .cmp-k { grid-column: 1 / -1; font-size: .74rem; }
  .cmp-head .cmp-k { display: none; }

  /* Heatmapa: 53 týdnů se na 360 px nesmrskává, roluje se. Buňka pod
     8 px už není políčko, ale šum. */
  .heat-grid, .heat-months { grid-template-columns: repeat(53, 10px); min-width: auto; }
  .heat-dow { font-size: .55rem; }

  /* ── tabulky → karty ──
     Hlavička zmizí, popisek nese ::before z data-l. Řádek dostane rámeček,
     jinak deset polí za sebou splyne v jeden odstavec. */
  .tw { overflow-x: visible; margin: 0; padding: 0; }
  table.t.cards thead { display: none; }
  table.t.cards tbody tr {
    display: block;
    padding: .7rem .75rem; margin-bottom: .5rem;
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, .02);
  }
  table.t.cards tbody tr:last-child { margin-bottom: 0; }
  table.t.cards tbody tr.me { border-color: var(--accent-border); }
  /* Buňka je řádek „popisek — hodnota“. Flex, ne mřížka: buňka s víc
     prvky (přezdívka + odznak „ty“) musí zůstat jedna hodnota, kdežto
     `display: contents` by z každého prvku udělalo samostatnou položku
     a rozhodilo by celou kartu. Pevná šířka popisku srovná sloupec. */
  table.t.cards td {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: .2rem .5rem; border: 0; padding: .16rem 0;
  }
  table.t.cards td::before {
    content: attr(data-l);
    flex: none; width: 84px;
    font-size: .66rem; letter-spacing: .08em; text-transform: uppercase;
    color: var(--faint);
  }
  /* Zarovnání doprava dává smysl ve sloupci, v kartě ne. */
  table.t.cards td.num { text-align: left; }
  table.t.cards td.full { display: block; }
  table.t.cards td.full::before { display: none; }
  /* Sloupec bez hlavičky (tlačítko na konci řádku) nemá co odsazovat. */
  table.t.cards td[data-l=""]::before { display: none; }

  /* ── seznam her: skóre nahoru k datu, výsledek na druhý řádek ── */
  .mrow {
    grid-template-columns: 34px minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    column-gap: .6rem; row-gap: .3rem;
    padding: .7rem .1rem;
  }
  .mrow > :nth-child(1) { grid-area: 1 / 1 / 3 / 2; align-self: center; }
  .mrow > :nth-child(2) { grid-area: 1 / 2 / 2 / 3; }
  .mrow > :nth-child(4) { grid-area: 1 / 3 / 2 / 4; text-align: right; }
  .mrow > :nth-child(3) { grid-area: 2 / 2 / 3 / 4; justify-self: start; }

  /* ── detail hry ── */
  .score-hero {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .9rem; padding: .95rem 1rem;
  }
  .score-hero .big { font-size: 1.9rem; }
  .vs { gap: .5rem; }
  .vs .side { padding: .6rem .3rem; }
  .vs .side b { font-size: 1.25rem; }
  .vs .side small { font-size: .66rem; }

  /* ── bonusy: odznak stavu na vlastní řádek pod popis ── */
  .bstep { grid-template-columns: 50px minmax(0, 1fr); gap: .7rem; }
  .bstep-n { height: 42px; }
  .bstep > :last-child { grid-column: 2 / -1; justify-self: start; }

  /* ── parťáci / akce ── */
  .pcard, .ecard { padding: .85rem .9rem; }

  /* ── skupiny: seznam skupin je na mobilu vodorovný pás karet ── */
  .glist {
    display: flex; gap: .45rem; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .glist::-webkit-scrollbar { display: none; }
  .glist a {
    flex: none; min-width: 156px;
    border: 1px solid var(--line); background: var(--surface);
  }
  /* Avatar u pole na telefonu ubírá šířku, kterou potřebuje text. */
  .composer { grid-template-columns: minmax(0, 1fr); }
  .composer > .avatar { display: none; }
  .composer textarea { font-size: 16px; min-height: 66px; }
  .composer-foot { flex-wrap: wrap; }
  .composer-foot .btn { min-height: 40px; }

  .empty { padding: 1.7rem 1rem; }
  .empty .ic { font-size: 1.7rem; }

  /* ── přihlášení ── */
  .auth { align-items: start; padding: 1.6rem .9rem 2rem; }
  .auth-card { padding: 1.2rem 1rem; }
  .code-input { font-size: 1.5rem; letter-spacing: .32em; text-indent: .32em; }
  .demo-pick button { min-height: 48px; }
}

/* Nejužší telefony (360 px a míň): dva sloupce dlaždic ještě drží,
   ale popisky ve spodní liště a hlavička potřebují ubrat. */
@media (max-width: 400px) {
  .tab { font-size: .58rem; }
  .tab .ic { font-size: 1.22rem; }
  /* Na 360px displeji je vedle loga na popisek místo jen tehdy, když se
     logo zmenší — radši popisek pryč a logo čitelné. */
  .nav-tag { display: none; }
  .page { padding: .9rem .7rem 1.1rem; }
  .panel { padding: .8rem .7rem; }
  .page-head h1 { font-size: 1.2rem; }
  .tile-v { font-size: 1.3rem; }
  .score-hero .big { font-size: 1.65rem; }
  .kv { grid-template-columns: 1fr; }
}

/* Šuplík „Víc" má deset položek. V jednom sloupci je z něj svitek přes
   půldruhé obrazovky, takže „Nastavení" a „Odhlásit se" — dvě věci, kvůli
   kterým se šuplík otevírá nejčastěji — zůstanou pod hranou a musí se
   k nim scrollovat. Dva sloupce se na 390px telefonu vejdou (na popisek
   zbývá ~120 px, nejdelší „Nastavení" má při 0,84rem kolem 75), takže se
   jeden sloupec zapíná až tam, kde dva opravdu nejsou k přečtení. */
@media (max-width: 340px) {
  .sheet-nav { grid-template-columns: 1fr; }
}

/* Telefon na šířku: fixní lišta by z 380 px výšky ukousla šestinu.
   Nechá se odrolovat s obsahem. */
@media (max-width: 820px) and (orientation: landscape) and (max-height: 500px) {
  .tabbar { position: static; }
  body { padding-bottom: 0; }
  /* Na ležícím telefonu je 88px hlavička pětina výšky okna. Logo se zmenší
     a hlavička s ním — vzorec v `--nav-h` se přepočítá sám. */
  :root { --nav-logo-h: 32px; }
  .sheet { max-height: 92vh; max-height: 92dvh; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  /* Skládání hlavičky JE animace, ale řídí ji pozice scrollu, ne čas.
     Zkrácená doba by u progress-based timeline neznamenala „bez pohybu",
     ale „složeno hned na prvním pixelu" — hlavička by se skokem zmenšila
     a už se nikdy nevrátila. Kdo si vypnul pohyb, dostane hlavičku, která
     se nemění vůbec. */
  .nav, .nav-brand-row, .nav-logo, .nav-tag,
  .nav-puls, .nav-puls .puls-beat, .nav-bar,
  .nav-center .pill, .nav-me, .nav-right .pill-icon {
    animation: none !important;
  }
}
