/* ==========================================================================
   Reset, rozměry hracího pole a kostra stránky
   ========================================================================== */

/* ------------------------------------------------------------------ reset */
*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
  padding: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  /* Podklad lišty je široký 100vw, což je se svislým posuvníkem o pár pixelů
     víc než plocha stránky. Clip ten přesah ustřihne, aniž by (na rozdíl od
     hidden) udělal ze stránky rolovací kontejner, position: sticky funguje
     dál. Svislé rolování zůstává nedotčené. */
  overflow-x: clip;
  /* Místo pro svislý posuvník se drží i na stránkách, které se nerolují.
     Bez toho by stránka bez posuvníku byla o jeho šířku širší a pevné prvky
     (lišta, vystředěné pole) by při přepnutí routerem poskočily do strany.
     Patří to sem, na kořen: posuvník okna je posuvník <html>, na <body> se
     scrollbar-gutter do okna nepropíše. */
  scrollbar-gutter: stable;
  /* pod přilepenou lištou nesmí skončit cíl odkazu ani zaostřený prvek */
  scroll-padding-top: calc(72px * var(--ui));

  /* Kořenová velikost písma je jediné místo, kde se --ui promítá do rem.
     Díky tomu se každý rozměr psaný v rem zvětší sám, bez dalšího zápisu. */
  font-size: calc(16px * var(--ui));
}

body {
  /*
     Velikost pole vychází z jediné proměnné. Bere se nejmenší ze tří mezí:
     šířka okna, zbylá výška po odečtení lišty a konzole, a horní strop.
     Poměr pole: 5 hracích sloupců (1) + 2 bodové (0,55) = 6,10 jednotky
                 1 bodový řádek (0,55) + 5 hracích (1)   = 5,55 jednotky
     --chrome je naměřená výška lišty, konzole a odsazení nad polem; roste
     s --ui, protože se zvětšuje i samotná lišta a konzole. Přičítá se k němu
     --consent-space: dokud dole visí lišta se souhlasem, musí se pole vejít
     nad ni, jinak překrývá spodní řadu políček a klik do ní nedopadne
     na políčko, ale na lištu. Nastavuje ji css/account.css podle atributu
     data-consent, který nasazuje js/boot.js ještě před prvním vykreslením.
     --lang-hint-space: totéž nahoře. Pruh s nabídkou překladu stojí v toku
     nad lištou a o jeho výšku je okna míň. Měří ho js/lang.js.

     --cell-max je ten horní strop. Na velkém displeji ho responsive.css
     zvedá, aby o velikosti pole rozhodla výška okna, ne strop.

     Proč svh a ne dvh
     -----------------
     dvh je výška okna právě teď, tedy se sbalenou lištou prohlížeče o pár
     desítek pixelů větší než s rozbalenou. Safari lištu WebKitu ohlásí
     ještě před prvním vykreslením, takže dvh sedí od začátku. Chrome na
     iOS je cizí aplikace a tuhle možnost nemá: po prokliku dostane webview
     nejdřív výšku podle sbalené lišty (stav zděděný z předchozí stránky),
     stránka se vykreslí, a teprve pak Chrome lištu rozbalí a webview
     zmenší. dvh v tu chvíli spadne o ~90 px a celý obsah, vystředěný
     na výšku v .app-main, poskočí vzhůru — přesně to problikávání při
     přepnutí stránky.

     svh je výška s rozbalenou lištou, tedy ta menší z obou a hlavně
     neměnná. Rozvržení na ní stojí pevně, ať je lišta jakkoli. Zaplatí se
     tím, že se při sbalené liště dole nechá ~90 px prázdných; na stránkách,
     které se nerolují (a to jsou skoro všechny), lištu stejně nemá co
     sbalit, takže svh a dvh vyjdou nastejno.
  */
  --gutter: clamp(12px, 2.4vw, calc(26px * var(--ui)));
  /* svislé odsazení lišty, čte ho .rail a započítává se do --chrome */
  --rail-pad-y: var(--sp-2);
  --chrome: calc(156px * var(--ui) + 2 * var(--rail-pad-y) + var(--consent-space, 0px) + var(--lang-hint-space, 0px));
  --cell-max: calc(128px * var(--ui));
  --cell:
    min(calc((100vw - 2 * var(--gutter) - 4px) / 6.1),
        calc((100vh - var(--chrome)) / 5.55),
        var(--cell-max));
  --cell:
    min(calc((100vw - 2 * var(--gutter) - 4px) / 6.1),
        calc((100svh - var(--chrome)) / 5.55),
        var(--cell-max));
  --score-cell: calc(var(--cell) * 0.55);
  --board-w: calc(var(--cell) * 5 + var(--score-cell) * 2);

  min-height: 100vh;
  min-height: 100svh;
  font-family: var(--font-ui);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--text);
  background-color: var(--bg);
  -webkit-font-smoothing: antialiased;
}

[hidden] {
  display: none !important;
}

img,
svg {
  display: block;
  max-width: 100%;
}

button,
input {
  font: inherit;
  color: inherit;
}

a {
  color: var(--accent);
  text-decoration: none;
}

:focus-visible {
  outline: 2px solid var(--accent-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--r-sm);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ------------------------------------------------------------------ shell */
.app {
  width: 100%;
  max-width: calc(1180px * var(--ui));
  min-height: 100vh;
  min-height: 100svh;
  margin-inline: auto;
  padding: calc(12px * var(--ui)) var(--gutter) calc(14px * var(--ui));
  padding-top: 0;
  display: flex;
  flex-direction: column;
  gap: calc(10px * var(--ui));
}

/* Lišta se souhlasem visí přes obsah (`position: fixed`), takže se musí
   odečíst i z místa, do kterého se obsah centruje. Bez toho ji --chrome
   sice započítá do velikosti pole, jenže menší pole se pak vystředí do celé
   výšky okna, spadne o polovinu ušetřeného místa dolů a spodní řada políček
   skončí pod lištou stejně jako předtím: klik do ní pak dopadne na lištu,
   ne na pole. Kolik místa si lišta bere, stojí v css/account.css. */
.app-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(10px * var(--ui));
  min-height: 0;
  padding-bottom: var(--consent-space, 0px);
}

.app-main--top {
  justify-content: flex-start;
  gap: clamp(16px, 3vh, calc(28px * var(--ui)));
  padding-top: clamp(8px, 4vh, calc(40px * var(--ui)));
}

/* -------------------------------------------------- přechod mezi stránkami */

/* Web je sada samostatných stránek a každý proklik je nové načtení. Prohlížeč
   umí i tak starou stránku plynule prolnout do nové (cross-document view
   transitions, Chrome 126+, Safari 18.2+), když se k tomu přihlásí obě
   strany, a přihlášení je tohle pravidlo: base.css stojí na každé stránce.
   Prohlížeč, který to neumí, pravidlo přeskočí a naviguje střihem jako dřív;
   stejně tak proklik na cizí doménu. Ztišení pro prefers-reduced-motion
   stojí u ostatních v responsive.css.

   Lišta nahoře (header.rail) vlastní view-transition-name nedostane, ač je
   na všech stránkách táž a prolínat by se nemusela: pojmenovaný prvek je
   podle specifikace i backdrop root, takže backdrop-filter na .rail::before
   (components.css) by neměl co rozostřit a pruh by ztratil sklo. Lišta se
   tedy prolne se zbytkem stránky; vypadá všude stejně, poznat je to jen na
   drobečcích. */
@view-transition {
  navigation: auto;
}

.card {
  width: 100%;
  background-color: var(--surface);
  border: var(--hairline) solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  padding: clamp(18px, 3vw, calc(34px * var(--ui)));
}

/* Úvodní plachta aplikace přidané na plochu. Mezi klepnutím na ikonu
   a hotovou stránkou umí prohlížeč ukázat prázdné (v tmavém režimu černé)
   okno; plachta ho překryje barvou motivu a logem, žádný ukazatel průběhu.

   Ukazuje se jen s atributem data-splash na <html>: ten nasazuje js/boot.js,
   a to pouze v nainstalované aplikaci (display-mode: standalone) při
   spuštění, ne při přecházení mezi stránkami. V obyčejném prohlížeči tedy
   prvek zůstane skrytý a neovlivní ani měření rychlosti stránky. Schování
   řídí tentýž skript: data-splash="done" plachtu vybledne a pak ji odebere
   z dokumentu i s atributem. */
.splash {
  display: none;
}

html[data-splash] .splash {
  position: fixed;
  inset: 0;
  z-index: 70;             /* nade vším včetně hlášek (60) */
  display: grid;
  place-items: center;
  /* --splash-bg nastavuje js/boot.js z vlastní tabulky barev: stylopis
     motivu se dopisuje za běhu a první snímek se umí vykreslit dřív, než
     dojede, holé var(--bg) by v tu chvíli bylo prázdné a plachta průhledná.
     var(--bg) tu zůstává jako pojistka do druhého snímku. */
  background-color: var(--splash-bg, var(--bg));
  transition: opacity 0.25s ease;
}

html[data-splash] .splash img {
  width: clamp(64px, 18vw, 96px);
  height: auto;
}

html[data-splash="done"] .splash {
  opacity: 0;
  pointer-events: none;
}

/* Plynulé přelití barev při přepnutí světlého a tmavého režimu.
   Atribut věší js/theme.js jen na dobu přepnutí (o chlup déle, než trvá
   --t-slide) a zase ho sundá, takže mimo tu chvíli tohle pravidlo nic
   nestojí a nepřebíjí vlastní přechody prvků. Ztišení pro
   prefers-reduced-motion řeší společné pravidlo v responsive.css,
   stojí ve zdroji později, a tak vyhrává i nad zdejším !important.

   V seznamu musí být každá barevná vlastnost, kterou motivy mění, jinak
   ta jedna střihne uprostřed přelití zbytku:
     box-shadow        --shadow-lg se mezi motivy liší (0,18 proti 0,6),
                       a inset prstence na poli jedou přes --accent;
                       navíc tohle !important nahrazuje transition prvku,
                       takže bez něj by se ztratil i přechod, který si
                       prvek deklaruje sám (.cell v board.css)
     outline-color     obrys fokusu je --accent-ring, jiný v každém motivu
     text-decoration-color, caret-color  podtržení a kurzor v poli

   Pseudoprvky se do `*` nepočítají, ::placeholder a ::marker si berou
   barvu z motivu (--text-muted, --text-faint) a musí být vyjmenované. */
:root[data-theme-anim] *,
:root[data-theme-anim] *::before,
:root[data-theme-anim] *::after,
:root[data-theme-anim] *::placeholder,
:root[data-theme-anim] *::marker {
  transition: background-color var(--t-slide), border-color var(--t-slide),
    color var(--t-slide), fill var(--t-slide), stroke var(--t-slide),
    box-shadow var(--t-slide), outline-color var(--t-slide),
    text-decoration-color var(--t-slide), caret-color var(--t-slide) !important;
}

/* Výjimky z pravidla výš: !important nahrazuje celý seznam transition,
   jezdci vypínače by na dobu přepnutí vzal jízdu (transform) a ikonám
   na dráze i v řádku nabídky prolnutí (opacity), tedy přesně tu animaci,
   kvůli které se přepínalo. Border-color tu musí zůstat taky, jinak obrys
   jezdce střihne uprostřed přelití zbytku. */
:root[data-theme-anim] .theme-thumb {
  transition: transform var(--t-slide), background-color var(--t-slide),
    border-color var(--t-slide) !important;
}

/* Ikony slunce a měsíce: na dráze vypínače (.theme-track) i v řádku sbalené
   nabídky (.theme-item-icon). Neplatnou zakrývá opacity, a protože se
   aria-checked přepíná ve stejném snímku jako barvy motivu, jede prolnutí
   ve stejném rytmu s nimi (--t-slide, ne --t-fast řádku v nabídce). */
:root[data-theme-anim] .theme-track .icon,
:root[data-theme-anim] .theme-item-icon .icon {
  transition: color var(--t-slide), opacity var(--t-slide) !important;
}
