/**
 * Scaleupboard — Design Tokens
 * ============================
 * CZ: Jediný zdroj pravdy pro barvy, spacing, typografii, radius, stíny a z-index.
 *     Přepsáno 6. 9. 2026 podle dodaného vzoru UI — plán a rozhodnutí viz
 *     `docs/REDESIGN_UI_2026-09-06.md` (fáze F1).
 *
 *     Struktura (v tomhle pořadí, neměň ji):
 *       1. RAMPY     — syrové barvy vzoru: pale / tint / base / text
 *       2. SÉMANTIKA — role: povrch, text, linka, akce, akcent
 *       3. FORMA     — typografie, spacing, radius, stíny, pohyb
 *       4. DOPLŇKY   — co vzor nemá, ale aplikace potřebuje (a proč)
 *
 *     ⚠️ JEDEN SLOVNÍK. Do 6. 9. 2026 tu byl navíc MOST — 60+ starých jmen ukazujících na nová
 *        (`--color-accent-strong` → `--action-primary`, `--spacing-3` → `--space-2` …). Most
 *        splnil svůj účel a byl ZRUŠEN: 1428 odkazů se přepsalo na nové názvy a aplikace má
 *        jediné názvosloví. Když pro něco token chybí, PŘIDÁ SE SEM — nezakládá se druhé jméno
 *        pro totéž a nepíše se hodnota natvrdo jinam.
 *
 *     ⚠️ Všechno musí zůstat v JEDNOM bloku `:root`. `web/brand.py` čte první `:root`
 *        po první `}` a z něj barví e-maily a PWA manifest — druhý blok by tiše ignoroval.
 *
 * EN: The single source of truth for colours, spacing, typography, radii, shadows and z-index.
 *     Rewritten 6 Sep 2026 from the supplied UI sample — plan and decisions in
 *     `docs/REDESIGN_UI_2026-09-06.md` (phase F1).
 *
 *     ⚠️ Everything must stay in ONE `:root` block. `web/brand.py` reads the first `:root` up to
 *        the first `}` and colours e-mails and the PWA manifest from it; a second block would be
 *        silently ignored.
 */

:root {
  /* =========================================================================
   * 1. RAMPY / RAMPS
   * =========================================================================
   * CZ: Barvy vzoru v původních hodnotách. Rozhodnutí majitele 6. 9. 2026:
   *     „drž se barev jak byly zadané" — base odstíny se NEMĚNÍ ani o stupeň.
   *
   *     `-text` krok je PŘÍDAVEK nad rámec vzoru. Vzor má jen pale/tint/base a počítá
   *     s tím, že se sytá barva použije i na text — jenže bílý text na `--orange-base`
   *     má 2.22:1 a propadá WCAG AA (práh 4.5:1). Sytý odstín proto zůstává na PLOCHY,
   *     a kdekoli má být barva textem, se sáhne po `-text`. Hodnoty nejsou vymyšlené:
   *     v aplikaci už žily jako `--sb-*-text` a jsou ověřené.
   * EN: The sample's colours at their original values. Owner's decision, 6 Sep 2026:
   *     "keep the colours as supplied" — the base shades do NOT move.
   *
   *     The `-text` step is an ADDITION beyond the sample. The sample has only pale/tint/base
   *     and assumes the saturated colour also carries text — but white on `--orange-base` is
   *     2.22:1 and fails WCAG AA (4.5:1). The saturated shade therefore stays on SURFACES, and
   *     wherever the colour must be text, `-text` is used. The values are not invented: they
   *     already lived in the app as `--sb-*-text` and are proven. */

  /* CZ: Modrá — primární akce. EN: Blue — the primary action. */
  --blue-pale: #EDF7FF;
  --blue-tint: #D9EFFF;
  --blue-base: #0092FF;
  --blue-text: #005E9E;                    /* 6.79:1 na bílé */

  /* CZ: Oranžová — sekundární akce, akcenty, čepičky karet.
     EN: Orange — secondary action, accents, card caps. */
  --orange-pale: #FFF4E6;
  --orange-tint: #FEE9CE;
  --orange-base: #F59926;
  --orange-text: #96500A;                  /* 6.09:1 na bílé */

  /* CZ: Zelená — terciární akce, kladný stav. EN: Green — tertiary action, positive state. */
  --green-pale: #F4F9E6;
  --green-tint: #EAF3D2;
  --green-base: #92C01F;
  --green-text: #4A6B10;                   /* 6.17:1 na bílé */

  /* CZ: Šedá — neutrální plochy a sekundární text. EN: Gray — neutral surfaces, secondary text. */
  --gray-pale: #F5F5F5;
  --gray-tint: #EDEDED;
  --gray-base: #878786;
  --gray-text: #5F5F5E;                    /* 6.05:1 na bílé */

  /* CZ: Neutrály. `--navy` je tmavý režim i rovina řízení — pravidlo vzoru zní
         „dark mode = navy, nikdy černá". EN: Neutrals. `--navy` is both dark mode and the
         management plane — the sample's rule is "dark mode = navy, never pure black". */
  --black: #000000;
  --white: #FFFFFF;
  --navy: #0F1115;
  --border: #D0D0D0;
  --body-gray: #555555;

  /* =========================================================================
   * 2. SÉMANTIKA / SEMANTIC LAYER
   * ========================================================================= */

  /* ---- Povrchy / Surfaces ---- */
  --surface-page: var(--white);
  --surface-card: var(--white);
  --surface-panel: var(--gray-pale);
  --surface-dark: var(--navy);
  --surface-card-dark: rgba(255, 255, 255, 0.08);
  /* CZ: NEPRŮHLEDNÁ plocha pro vrstvy, které PŘEKRÝVAJÍ obsah — vysouvací panel navigace,
         rozbalovací nabídka, modal.
         ⚠️ Nesmí to být `--surface-card`: ta je v tmavém režimu `rgba(255,255,255,.08)`, tedy
         PRŮSVITNÁ. Na kartě ležící ve stránce je to správně (jemné nadzvednutí), ale pod
         panelem, který má obsah zakrýt, prosvítá text zespodu a nečitelné jsou obě vrstvy.
         Odhaleno 6. 9. 2026 (F7) na mobilním panelu v tmavém režimu — `visual_snapshot.py` to
         nikdy nenafotil, protože panel se otevírá až klikem.
     EN: An OPAQUE surface for layers that COVER content — the navigation drawer, a dropdown
         menu, a modal.
         ⚠️ It must not be `--surface-card`: in dark mode that is `rgba(255,255,255,.08)`, i.e.
         TRANSLUCENT. On a card sitting in the page that is correct (a subtle lift), but under a
         panel meant to hide content the text below shows through and both layers become
         unreadable. Found 6 Sep 2026 (F7) on the mobile drawer in dark mode — `visual_snapshot.py`
         never photographed it, because the drawer only opens on a click. */
  --surface-overlay: var(--white);
  /* CZ: Závoj pod modálním dialogem nového UI (`frontend/src/ui/Dialog`, F3 6. 10. 2026). Barva
         ze vzoru (`BrAIen-Pristav-X.dc.html`, detail výzvy: `rgba(15,17,21,.5)` = `--navy` s alfou):
         navy závoj čte jako ztlumená stránka, černý jako díra. Django `.modal-overlay` má tutéž
         hodnotu zatím jako literál s výjimkou v testu; nové UI literál mít nesmí (Stylelint).
     EN: The scrim under a modal dialog of the new UI (`frontend/src/ui/Dialog`, F3 6 Oct 2026).
         The sample's colour (challenge detail: `rgba(15,17,21,.5)` = `--navy` with alpha): a navy
         scrim reads as a dimmed page, a black one as a hole. Django's `.modal-overlay` still has
         the same value as a literal with a test exception; the new UI must not (Stylelint). */
  --surface-scrim: rgba(15, 17, 21, 0.5);

  /* ---- Text ---- */
  --text-primary: var(--black);
  --text-body: var(--body-gray);
  /* CZ: `--gray-base` (#878786) má na bílé jen 3.54:1 — na text nestačí. Vzor ho na sekundární
         text používá; tady na něj sahá `--gray-text`. Sytý `--gray-base` zůstává na oddělovače
         a dekoraci. EN: `--gray-base` (#878786) is only 3.54:1 on white — not enough for text.
         The sample uses it for secondary text; here `--gray-text` does that job, while the
         saturated `--gray-base` stays on dividers and decoration. */
  --text-secondary: var(--gray-text);
  /* CZ: Odkaz musí být čitelný jako text, takže `-text` krok, ne `--blue-base` (3.20:1).
     EN: A link must be legible as text, hence the `-text` step, not `--blue-base` (3.20:1). */
  --text-link: var(--blue-text);
  --text-on-dark: var(--white);

  /* CZ: Popředí NA barevné výplni. Modrá nese BÍLÝ text, oranžová a zelená ČERNÝ.
     ⚠️ VÝJIMKA Z BRAND MANUÁLU — rozhodnutí majitele 14. 9. 2026, potvrzené znovu po dodání
        upřesněného manuálu v2 (`docs/BRAND_MANUAL_DIFF_2026-09-14.md`, BR-01).

        Manuál v2 žádá dvakrát bílý text na každém barevném tlačítku (§6 „Nikdy černý ani
        barevný text na barevném tlačítku", §3 „text vždy bílý"). PRO APLIKACI TOHLE PRAVIDLO
        NEPLATÍ a výjimka patří do manuálu. Důvod je měřený, ne estetický: bílá na
        `--action-secondary` (#F59926) dává **2.22:1** a na `--action-tertiary` (#92C01F)
        **2.15:1**, proti prahu WCAG AA 4.5:1. Černá dává 9.47:1, resp. 9.78:1. Aplikace není
        leták — je to přihlašovací a datová plocha, kde je čitelnost tlačítka provozní věc.

        ZTMAVIT VÝPLŇ NEJDE (u modré to vyšlo, tady ne): aby bílý text na oranžové prošel na
        4.5:1, musela by výplň klesnout zhruba na #AD6C1B, což je HNĚDÁ — a tu manuál v §3
        zakazuje. Zelená vyjde na tmavou olivovou se stejným problémem. U modré ten krok prošel
        jen proto, že tmavá modrá je pořád modrá.

        Sám manuál tuhle cestu jinde potvrzuje: §6 chce u odznaku „malý oranžový pill s tmavým
        textem" — černý text na oranžové je tedy uvnitř brandu, ne proti němu.

        Modrá: černá na `--blue-base` sice dá 6.56:1, jenže tlačítko při najetí a stisku TMAVNE
        a černý text se s ním zhoršuje až na 3.75:1. Bílý text se chová opačně — čím tmavší
        výplň, tím lépe. Proto modrá akční výplň klesá o krok na `#0069BC` (odstín ze samotného
        vzoru) a dává 5.60:1. Rampa `--blue-base` se tím NEMĚNÍ: `#0092FF` dál platí pro plochy,
        chipy a dekoraci.
     EN: The foreground ON a coloured fill. Blue carries WHITE text, orange and green BLACK.
     ⚠️ AN EXCEPTION TO THE BRAND MANUAL — owner's decision of 14 Sep 2026, reaffirmed after the
        refined manual v2 arrived (`docs/BRAND_MANUAL_DIFF_2026-09-14.md`, BR-01).

        Manual v2 asks twice for white text on every coloured button (§6 "Never black or coloured
        text on a coloured button", §3 "text always white"). THAT RULE DOES NOT APPLY TO THE
        APPLICATION and the exception belongs in the manual. The reason is measured, not
        aesthetic: white on `--action-secondary` (#F59926) gives **2.22:1** and on
        `--action-tertiary` (#92C01F) **2.15:1**, against the WCAG AA threshold of 4.5:1. Black
        gives 9.47:1 and 9.78:1. The app is not a flyer — it is a login and data surface where a
        legible button is an operational matter.

        DARKENING THE FILL IS NOT AN OPTION (it worked for blue, not here): for white on orange to
        reach 4.5:1 the fill would have to drop to roughly #AD6C1B, which is BROWN — forbidden by
        the manual in §3. Green lands on a dark olive with the same problem. The step worked for
        blue only because dark blue is still blue.

        The manual itself endorses this elsewhere: §6 wants "a small orange pill with dark text"
        on the badge — black on orange is therefore inside the brand, not against it.

        Blue: black on `--blue-base` does give 6.56:1, but the button DARKENS on hover and press
        and black text degrades with it, down to 3.75:1. White behaves the opposite way — the
        darker the fill, the better. Hence the blue action fill steps down to `#0069BC` (a shade
        from the sample itself) and gives 5.60:1. The `--blue-base` ramp does NOT move: `#0092FF`
        still governs surfaces, chips and decoration. */
  --text-on-primary: var(--white);
  --text-on-secondary: var(--black);
  --text-on-tertiary: var(--black);

  /* ---- Linky / Lines ---- */
  --line-card: var(--border);
  --line-divider: var(--border);
  --line-on-dark: rgba(255, 255, 255, 0.15);

  /* ---- Akce / Actions (hierarchie 1-2-3) ----
     CZ: Pořadí důležitosti: modrá > oranžová > zelená. Vždy pilulka, plná výplň.
     EN: Order of importance: blue > orange > green. Always a pill, always a solid fill. */
  /* CZ: Modrá výplň klesá o krok proti rampě — viz `--text-on-primary` výše. `#0069BC` je
         `press` odstín vzoru; hover a press pod ním pokračují stejným krokem dál do tmavé.
     EN: The blue fill steps down from the ramp — see `--text-on-primary` above. `#0069BC` is the
         sample's `press` shade; hover and press below continue darkening at the same step. */
  --action-primary: #0069BC;
  --action-primary-hover: #005A9E;
  --action-primary-press: #004B85;
  --action-secondary: var(--orange-base);
  --action-secondary-hover: #DF861A;
  --action-secondary-press: #C67514;
  --action-tertiary: var(--green-base);
  --action-tertiary-hover: #7DA717;
  --action-tertiary-press: #6B9012;

  /* ---- Akcenty / Accents ---- */
  --accent-attention: var(--orange-base);
  --accent-positive: var(--green-base);
  --accent-cap: var(--orange-base);
  --marker: var(--orange-base);

  /* ---- Sémantické stavy / Semantic states ----
     CZ: Vzor stavy neřeší (nemá chybové ani varovné plochy), aplikace je má na desítkách míst.
         Kladný a varovný stav se proto navěšují na rampy vzoru — zelená = v pořádku, oranžová
         = pozor. Chyba a informace zůstávají mimo paletu vzoru: červená proto, že žádná barva
         vzoru „chybu" nesignalizuje, a modrá vzoru je obsazená primární akcí.
     EN: The sample does not cover states (it has no error or warning surfaces); the app uses them
         in dozens of places. The positive and warning states therefore hang off the sample's
         ramps — green = fine, orange = caution. Error and info stay outside the sample's palette:
         red because no colour in the sample signals "error", and the sample's blue is taken by
         the primary action. */
  --state-positive: var(--green-base);
  --state-positive-soft: var(--green-pale);
  --state-positive-text: var(--green-text);
  --state-warning: var(--orange-base);
  --state-warning-soft: var(--orange-pale);
  --state-warning-text: var(--orange-text);
  --state-danger: #D92D20;
  --state-danger-soft: #FEF3F2;
  --state-danger-text: #B42318;            /* 6.17:1 na bílé */
  --state-info: var(--blue-base);
  --state-info-soft: var(--blue-pale);
  --state-info-text: var(--blue-text);
  --text-on-danger: var(--white);          /* bílá na #D92D20 = 4.83:1 */

  /* =========================================================================
   * 3. FORMA / FORM
   * ========================================================================= */

  /* ---- Typografie / Typography ----
     CZ: Poppins je jediná značková rodina, fallback Inter. Obojí se hostuje LOKÁLNĚ
         (`static/css/fonts.css` + `static/fonts/`) — rozhodnutí majitele 6. 9. 2026, drží
         se tím rozhodnutí ze 17. 8. Vzor tahá písmo z Google Fonts; to se NEPŘEBÍRÁ.
     EN: Poppins is the only brand family, Inter the fallback. Both are hosted LOCALLY
         (`static/css/fonts.css` + `static/fonts/`) — owner's decision, 6 Sep 2026, upholding the
         17 Aug decision. The sample pulls the font from Google Fonts; that is NOT adopted. */
  --font-core: Poppins, Inter, system-ui, -apple-system, sans-serif;
  --font-fallback: Inter, system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", "Fira Code", monospace;

  --weight-body: 400;
  --weight-eyebrow: 500;
  --weight-subhead: 600;
  --weight-heading: 700;
  --weight-hero: 800; /* web-only */

  /* CZ: Dvojice `size` / `size-min` = plná a zmenšená velikost. Zmenšená se použije na úzkých
         obrazovkách; propojení do `clamp()` patří do F2, tady jsou jen hodnoty.
     EN: The `size` / `size-min` pair = the full and reduced size. The reduced one is for narrow
         screens; wiring them into `clamp()` belongs to F2, these are just the values. */
  --h1-size: 54px;  --h1-size-min: 44px;  --h1-weight: 800;  --h1-tracking: -1.5px;   --h1-leading: 1.08;
  --h2-size: 36px;  --h2-size-min: 30px;  --h2-weight: 600;  --h2-tracking: -0.5px;   --h2-leading: 1.15;
  --h3-size: 24px;  --h3-size-min: 20px;  --h3-weight: 500;  --h3-tracking: -0.25px;  --h3-leading: 1.3;
  --body-size: 17px;  --body-size-sm: 15px;  --body-weight: 400;  --body-leading: 1.6;
  --caption-size: 12px;  --caption-size-sm: 11px;  --caption-weight: 600;  --caption-tracking: 2px;

  --measure-body: 64ch;

  /* ---- Spacing (základ 8 px / 8 px base) ----
     CZ: Pravidlo vzoru: „každý margin a gap je násobek osmi".
     EN: The sample's rule: "every margin and gap is a multiple of eight". */
  /* CZ: Číslo v názvu = NÁSOBEK OSMI. `--space-3` je 3 × 8 = 24 px. Díky tomu se pravidlo vzoru
         („every margin and gap is a multiple of 8") dá zkontrolovat pohledem na jméno tokenu.
         Vzor uvádí šest kroků; aplikace potřebuje ještě 0, 40, 80 a 96 px — všechny na mřížce
         leží (0×8, 5×8, 10×8, 12×8), jen je vzor ve svých osmi obrazovkách nepotřeboval.
     EN: The number in the name is the MULTIPLE OF EIGHT. `--space-3` is 3 × 8 = 24 px. That makes
         the sample's rule ("every margin and gap is a multiple of 8") checkable by reading the
         token's name. The sample lists six steps; the app also needs 0, 40, 80 and 96 px — all of
         which sit on the grid (0×8, 5×8, 10×8, 12×8), the sample's eight screens just never
         needed them. */
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
  --space-5: 40px;
  --space-6: 48px;
  --space-8: 64px;
  --space-10: 80px;

  /* CZ: `-lg` varianty rytmu jsou HORNÍ MEZ rozsahů, které vzor udává („sekce 48-64 px",
         „nadpis k textu 16-24 px"). Aplikace si z každého rozsahu vybírá JEDNU hodnotu — hustá
         obrazovka s kartami nemá kde tu širší uplatnit. Zůstávají kvůli paritě s webem, kde se
         dlouhé obsahové stránky dýchají víc, a jsou proto `web-only`.
     EN: The `-lg` rhythm variants are the UPPER BOUND of the ranges the sample states ("section
         48-64px", "heading to body 16-24px"). The app picks ONE value from each range — a dense
         card screen has nowhere to spend the wider one. They stay for parity with the web, where
         long content pages breathe more, hence `web-only`. */
  --gap-heading-body: 16px;
  --gap-heading-body-lg: 24px; /* web-only */
  --gap-section: 48px;
  --gap-section-lg: 64px; /* web-only */

  --pad-card: 24px;
  /* CZ: `--well-web` 1200 px je hodnota z brand manuálu §12 („content well 1200 px") a platí
         pro přístav klienta — tedy pro plochy, které odpovídají webu.
         `--well-ops` 1280 px je VĚDOMÁ VÝJIMKA pro rovinu řízení (`/rizeni/`): manuál mluví
         o webu, ne o provozní konzoli, a tabulky auditu, seatů a doručení potřebují víc místa
         než marketingová stránka. Výjimka je zapsaná v `docs/BRAND_MANUAL_DIFF_2026-09-14.md`
         (BR-17), ať se za půl roku nepřepíše na 1200 s tím, že „manuál říká 1200".
     EN: `--well-web` 1200 px is the brand manual's §12 value ("content well 1200 px") and applies
         to the client harbour — the surfaces that mirror the website.
         `--well-ops` 1280 px is a DELIBERATE EXCEPTION for the management plane (`/rizeni/`):
         the manual speaks about the website, not an operations console, and the audit, seat and
         delivery tables need more room than a marketing page. The exception is recorded in
         `docs/BRAND_MANUAL_DIFF_2026-09-14.md` (BR-17) so nobody "corrects" it back to 1200 in
         six months on the grounds that "the manual says 1200". */
  --well-web: 1200px;                      /* obsahová studna přístavu */
  --well-ops: 1280px;                      /* obsahová studna roviny řízení */
  --well-email: 600px; /* web-only */                     /* šířka newsletteru */

  /* ---- Radius ----
     CZ: Jeden radius na roli. Pravidlo vzoru: „NEVER mix radii inside one element or view."
     EN: One radius per role. The sample's rule: "NEVER mix radii inside one element or view." */
  --radius-card: 16px;
  --radius-container: 12px;
  --radius-chip: 8px;
  --radius-pill: 999px;
  /* CZ: „Ocásek" chatové bubliny — jediný roh, který se záměrně LIŠÍ od zbytku prvku.
         Vzor to má takhle: bublina 12 px na třech rozích a 4 px na tom, který ukazuje ke
         svému mluvčímu. Je to výjimka z „jeden radius na roli", a proto má vlastní token:
         kdyby zůstala napsaná natvrdo, obešla by kontrolu, že tvarosloví dědí ze základu.
     EN: The chat bubble's "tail" — the one corner that deliberately DIFFERS from the rest of the
         element. The sample does it this way: 12 px on three corners and 4 px on the one pointing
         at its speaker. It is an exception to "one radius per role", hence its own token: written
         literally it would slip past the check that shape language is inherited from the base. */
  --radius-bubble-tail: 4px;
  /* CZ: Vlaječka jazyka (20–22 px široká) — druhá vědomá výjimka z „jeden radius na roli".
         `--radius-chip` (8 px) by na tak malém obrázku zakulatil rohy tak, že by se vlajka
         deformovala; jde o vlásečnicové srazení hrany, ne o tvar prvku. Vlastní token ze
         stejného důvodu jako u ocásku bubliny: natvrdo napsané `2px` v šabloně obchází
         kontrolu tvarosloví — a přesně tam se 9. 9. 2026 našlo.
     EN: A language flag (20–22 px wide) — the second deliberate exception to "one radius per
         role". `--radius-chip` (8 px) would round such a small image enough to deform the flag;
         this is a hairline edge break, not the element's shape. Its own token for the same reason
         as the bubble tail: a literal `2px` in a template bypasses the shape check — and that is
         exactly where it was found on 9 Sep 2026. */
  --radius-flag: 2px;

  /* ---- Obrysy a stíny / Outlines and shadows ----
     CZ: Karta = obrys, ŽÁDNÝ stín. Vzor: „Elevation is intentionally near-absent in this brand."
         Stín má jedinou povolenou roli — plovoucí vrstva (modal, rozbalovací menu).
     EN: A card is an outline with NO shadow. The sample: "Elevation is intentionally near-absent
         in this brand." A shadow has one permitted role — a floating layer (modal, dropdown). */
  --border-card: 1.25px;
  --border-card-style: 1.25px solid var(--line-card);
  --border-ghost: 1.5px;
  --cap-height: 6px;                       /* oranžová čepička na hraně karty */

  --shadow-none: none;
  --shadow-overlay: 0 8px 32px rgba(15, 17, 21, 0.14);

  --marker-height: 0.36em;
  --marker-offset: 0.02em;

  /* ---- Pohyb / Motion ----
     CZ: Vzor: „Restrained motion: short fades and colour shifts. No bounce, no spring,
         no parallax." EN: The sample: "Restrained motion… No bounce, no spring, no parallax." */
  --duration-fast: 120ms;
  --duration-base: 180ms;
  --duration-slow: 280ms;
  --ease-standard: cubic-bezier(0.2, 0, 0.2, 1);
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
  --press-scale: 0.98;

  /* ---- Layout ---- */
  --sidebar-width: 240px;
  --header-height: 72px;                   /* topbar vzoru */
  /* CZ: Výška jedné pilulky `.chip` — 2×7px padding + řádek 13px/1.4 + 2×1px rám. Existuje
         proto, aby `.chip-rows` uměl spočítat „dva řádky a dost"; kdo mění box `.chip`,
         musí srovnat i tenhle token, jinak se ořez rozejde s obsahem.
     EN: The height of one `.chip` pill — 2×7px padding + a 13px/1.4 line + 2×1px border. It
         exists so `.chip-rows` can compute "two rows and no more"; whoever changes the `.chip`
         box must keep this token in step, or the clamp drifts from the content. */
  --chip-height: 34px;
  /* CZ: Šířka jednoho slajdu schůzky. Čtou ji OBĚ obrazovky — sdílená (`.pres-slot`) i
         klientská (`.pres-shell`). Než token vznikl, měla každá svou hodnotu (76rem × 60rem),
         takže klientovi zvenčí ořezala tentýž slajd a mřížky se lámaly jinam než obchodníkovi.
     EN: The width of one meeting slide. BOTH screens read it — the shared one (`.pres-slot`) and
         the client one (`.pres-shell`). Before this token each had its own value (76rem × 60rem),
         so the client's frame cropped the very same slide and the grids wrapped elsewhere than
         they did for the rep. */
  --pres-slide-width: 76rem;
  --max-content-width: var(--well-web);
  --max-reading-width: var(--measure-body);
  /* CZ: Vnitřní odsazení tlačítka podle velikosti, přesně ze vzoru
         (`_design_vzor/rozbaleno/design-system-komponenty.js`, `Button.jsx` `SIZES`). Čte je
         komponenta nového UI `frontend/src/ui/Button` (F2, 5. 10. 2026); Django `.btn` má
         vědomou odchylku (`components.css`), proto je zatím nepoužívá.
     EN: The button's padding per size, exactly from the sample (`Button.jsx` `SIZES`). Read by
         the new UI's `frontend/src/ui/Button` component (F2, 5 Oct 2026); Django's `.btn` has a
         deliberate deviation (`components.css`), hence does not use them yet. */
  --button-padding-sm: 10px 20px;
  --button-padding-md: 14px 28px;
  --button-padding-lg: 16px 32px;

  /* ---- Z-index ---- */
  --z-dropdown: 50;
  --z-sticky: 100;
  /* CZ: Plovoucí tlačítko (FAB) na obchodní nástěnce. Schválně POD `.modal-overlay` i pod
     toasty — tlačítko, které překryje hlášku o výsledku vlastní akce, je horší než tlačítko,
     na které je vidět hůř.
     EN: The floating action button on the sales board. Deliberately BELOW `.modal-overlay` and
     below the toasts — a button covering the message about the result of its own action is worse
     than a slightly less visible button. */
  --z-fab: 300;
  --z-modal-backdrop: 400;
  --z-modal: 500;
  --z-popover: 600;
  /* CZ: Oznámení (toast) nového UI: NAD modalem i rozbalovací nabídkou, aby výsledek akce
         potvrzené v dialogu byl vidět, ale POD tooltipem, který patří k prvku pod kurzorem.
     EN: The new UI's notifications (toasts): ABOVE a modal and a dropdown, so the result of an
         action confirmed in a dialog stays visible, but BELOW a tooltip, which belongs to the
         element under the pointer. */
  --z-toast: 650;
  --z-tooltip: 700;

  /* ---- Focus (a11y) ----
     CZ: PŘÍDAVEK nad rámec vzoru — vzor nemá jediný `:focus-visible`, jen hover a press přes
         React state. Projekt focus vyžaduje (`CLAUDE.md`), takže se dopočítal v barvách vzoru:
         oranžová jako „pozornost". Zapsáno v plánu jako SM-10.
     EN: An ADDITION beyond the sample — the sample has not a single `:focus-visible`, only hover
         and press via React state. The project requires focus (`CLAUDE.md`), so it was derived in
         the sample's palette: orange as "attention". Recorded in the plan as SM-10. */
  --focus-ring: 0 0 0 3px rgba(245, 153, 38, 0.45);
  --focus-ring-error: 0 0 0 3px rgba(217, 45, 32, 0.30);

  /* ---- Grafy / Charts ----
     CZ: Vzor grafy neřeší vůbec (SM-12), aplikace jede na ECharts. Kategorická paleta se proto
         odvozuje z ramp vzoru v pořadí hierarchie akcí, aby graf seděl do zbytku UI.
         Tooltip je ZVEDNUTÁ plocha nad grafem — v tmavém režimu musí zůstat SVĚTLEJŠÍ než
         stránka, jinak splyne s pozadím a zbyde z něj jen rámeček.
     EN: The sample does not cover charts at all (SM-12); the app runs on ECharts. The categorical
         palette is therefore derived from the sample's ramps in the action hierarchy's order so a
         chart sits with the rest of the UI. The tooltip is a RAISED surface above the chart — in
         dark mode it must stay LIGHTER than the page, otherwise it merges into the background and
         only a border is left. */
  --chart-1: var(--blue-base);
  --chart-2: var(--orange-base);
  --chart-3: var(--green-base);
  --chart-4: var(--gray-base);
  --chart-5: var(--blue-text);
  --chart-6: var(--orange-text);
  --chart-tooltip-bg: var(--white);

  /* ---- Záložní barvy pro data z DB / Fallbacks for DB-driven colours ----
     CZ: Obchodní plátno bere barvy z databáze (`CanvasTheme.color`, `ScbProgram.color`,
         `SalesAmbition.color`) a šablony k nim měly napsaný záložní hex pro případ, že
         záznam barvu nemá. Tím se ale redesign i dark mode těch sedmi míst tiše minul —
         proto tokeny.
         Žlutá lepíku zůstává žlutá i v tmavém režimu: je to metafora papírového lístku,
         ne plocha aplikace, a černý text na ní má 14:1.
     EN: The sales canvas takes colours from the database (`CanvasTheme.color`,
         `ScbProgram.color`, `SalesAmbition.color`) and the templates carried a hardcoded hex
         fallback for records without one. That silently excluded those seven spots from both
         the redesign and dark mode — hence the tokens.
         The sticky note's yellow stays yellow in dark mode too: it is a paper-note metaphor,
         not an app surface, and black text on it gives 14:1. */
  /* CZ: ⚠️ MUSÍ být `-text` odstín, ne `--gray-base`. `--theme-color` se v `sales.css` používá
         nejen na plochy (`border-top`, `background`), ale i na TEXT (r. 47 `color:`). `--gray-base`
         (#878786) má na bílé 3.60:1 a `tokens.css` ho sám o pár řádků výš označuje za pouze
         dekorativní — dosadit ho do textové role by porušilo vlastní pravidlo souboru.
         `--gray-text` (#5F5F5E) dává 6.05:1 a funguje v obou rolích.
         Odhaleno 6. 9. 2026 adversariálním ověřením ve F10: první verze (F2) sem dala
         `--gray-base` a zároveň tím změnila barvu z původní břidlicové #7c8aa5.
     EN: ⚠️ It MUST be a `-text` shade, not `--gray-base`. In `sales.css`, `--theme-color` is used
         not only on surfaces (`border-top`, `background`) but also on TEXT (line 47, `color:`).
         `--gray-base` (#878786) is 3.60:1 on white and `tokens.css` itself marks it a few lines
         above as decoration-only — putting it in a text role would break the file's own rule.
         `--gray-text` (#5F5F5E) gives 6.05:1 and works in both roles.
         Found 6 Sep 2026 by adversarial verification in F10: the first version (F2) put
         `--gray-base` here and in doing so also changed the colour from the original slate #7c8aa5. */
  --canvas-theme-fallback: var(--gray-text);
  --sticky-note-default: #FFD966;

  /* =========================================================================
   * 4. DOPLŇKY NAD RÁMEC VZORU / ADDITIONS BEYOND THE SAMPLE
   * =========================================================================
   * CZ: Tokeny, které vzor NEMÁ, ale aplikace je potřebuje. Nejsou to aliasy — každý má
   *     vlastní hodnotu a vlastní roli. Do 6. 9. 2026 tu místo nich stál MOST: 60+ starých
   *     jmen ukazujících na nová. Most splnil, k čemu byl (držel 236 šablon při životě, než
   *     se přepsaly komponenty), a rozhodnutím majitele byl zrušen — aplikace má mít JEDEN
   *     slovník, ze kterého se dědí.
   *
   *     Co tu zůstalo a proč:
   *       · `--text-*`  — utility stupnice velikostí pro třídy `.text-xs` … `.text-5xl`.
   *                       Vzor má jen sémantické velikosti (`--body-size`, `--h1-size`), což
   *                       na utility třídy nestačí.
   *       · `--leading-*`, `--tracking-*` — pomocné hodnoty pro tytéž utility.
   *       · `--transition-*` — SLOŽENÉ tokeny (doba + křivka). Vzor je má rozdělené na
   *                       `--duration-*` a `--ease-*`; složená dvojice se v CSS píše častěji.
   *       · `--event-type-*` — lokální dvojice štítku typu akce, kterou přepisují
   *                       třídy `.event-type-*`.
   *     Co tu NAOPAK UŽ NENÍ: `--gradient-hero`. Byl to jediný gradient v aplikaci a
   *     14. 9. 2026 byl zrušen — brand manuál v2 (§12, Web) gradient pozadí zakazuje a
   *     majitel rozhodl manuál v tomhle bodě dodržet (BR-02). Hero pás dashboardu je teď
   *     plochý navy. Kdyby někdo token hledal: nevrací se, plochost je záměr.
   * EN: Tokens the sample does NOT have but the application needs. They are not aliases —
   *     each has its own value and its own role. Until 6 Sep 2026 a BRIDGE stood here instead:
   *     60+ old names pointing at new ones. The bridge did its job (kept 236 templates alive
   *     while the components were rewritten) and was retired by the owner's decision — the
   *     application is to have ONE dictionary that everything inherits from.
   *
   *     What stayed and why: the `--text-*` utility size scale (the sample has only semantic
   *     sizes, which utility classes cannot use), the `--leading-*` / `--tracking-*` helpers for
   *     those same utilities, the COMPOSED `--transition-*` tokens (duration + easing; the sample
   *     splits them, but the composed pair is what CSS actually writes), the local
   *     `--event-type-*` chip pair.
   *
   *     What is NO LONGER here: `--gradient-hero`. It was the app's only gradient and was removed
   *     on 14 Sep 2026 — brand manual v2 (§12, Web) forbids gradient backgrounds and the owner
   *     decided to follow the manual on this point (BR-02). The dashboard hero band is now flat
   *     navy. If anyone goes looking for the token: it is not coming back, flatness is the intent. */

  /* ---- Utility stupnice velikostí / Utility size scale ---- */
  --text-xs: 0.75rem;       /* 12 */
  --text-sm: 0.875rem;      /* 14 */
  --text-base: 1rem;        /* 16 */
  --text-lg: 1.125rem;      /* 18 */
  --text-xl: 1.25rem;       /* 20 */
  --text-2xl: 1.5rem;       /* 24 */
  --text-3xl: 1.875rem;     /* 30 */
  --text-4xl: 2.25rem;      /* 36 */
  --text-5xl: 3rem;         /* 48 */
  --leading-tight: 1.2;
  --leading-snug: 1.4;
  --tracking-tight: -0.02em;
  --tracking-normal: 0;

  /* ---- Složené přechody / Composed transitions ---- */
  --transition-fast: var(--duration-fast) var(--ease-standard);
  --transition-normal: var(--duration-base) var(--ease-standard);
  --transition-emphasized: var(--duration-slow) var(--ease-out);

  /* ---- Štítek typu akce / Event-type chip ----
     CZ: Výchozí neutrální dvojice; třídy `.event-type-*` v `components.css` ji přepisují na
         konkrétní rodinu. Dark mode řeší podkladové tokeny samy.
     EN: The neutral default pair; the `.event-type-*` classes in `components.css` override it
         with a concrete family. Dark mode is handled by the underlying tokens. */
  --event-type-soft: var(--gray-pale);
  --event-type-text: var(--text-secondary);

  /* ---- Prvky nového UI (F3 „Prvky a značka", 6. 10. 2026) / New UI elements ----
     CZ: Rozměry komponent `frontend/src/ui/` (Logo, Avatar, IconButton, Chip, Badge, StatBlock,
         ReferenceCard, Marker). Hodnoty jsou ze závazného vzoru (`_design_vzor/rozbaleno/
         design-system-komponenty.js` a obrazovky `BrAIen-*.dc.html`), ne vymyšlené:
           · logo: výška 24 px (topbar řízení), 32 px (topbar Djanga), 40 px (výchozí `Logo.jsx`);
             lockup má poměr 6.68:1, takže i 24 px dává 160 px šířky nad minimem 120 px (§2);
           · avatar: 36 px topbar, 40 px seznamy, 48 px karta člena, 96 px profil;
           · ikonové tlačítko: kruh na mřížce 8 px, výška jako `Button` sm / md / lg; nejmenší
             32 px (`xs`) = zavření oznámení (dříve vlastní `CloseButton`, 7. 10. 2026);
           · chip a odznak: vnitřní odsazení `Chip.jsx` (7 × 12) a `PillBadge.jsx` (6 × 12),
             mezera ikona–text 6 px (`Chip.jsx` `gap: 6`), křížek chipu 24 px = minimální cíl
             dotyku WCAG 2.5.8;
           · číslo `StatBlock`: 32 px = `size="sm"` vzoru, jak ho kreslí obrazovky řízení
             („Firma v číslech"); menší a větší krok sedí na utility škálu (24 / 48);
           · akcentní linka `ReferenceCard.jsx` 40 × 2 px;
           · odsazení výplně `Marker` (varianta `block`) 0.18em z `Marker.jsx`.
     EN: Sizes of the `frontend/src/ui/` components. The values come from the binding sample, not
         invention: logo 24 (ops topbar) / 32 (Django topbar) / 40 (the sample's default); avatar
         36 topbar / 40 lists / 48 member card / 96 profile; icon button = a circle on the 8 px
         grid matching `Button` sm / md / lg, the smallest 32 px (`xs`) = closing a toast (formerly
         the separate `CloseButton`, 7 Oct 2026); chip and badge padding from `Chip.jsx` and
         `PillBadge.jsx`, the icon–text gap 6 px (`Chip.jsx`), the chip's remove target 24 px =
         the WCAG 2.5.8 minimum; the `StatBlock` number 32 px = the sample's `size="sm"` as the ops
         screens draw it, the smaller and larger steps on the utility scale (24 / 48); the
         `ReferenceCard.jsx` accent rule 40 × 2 px; the `Marker` `block` padding 0.18em. */
  --logo-height-sm: 24px;
  --logo-height-md: 32px;
  --logo-height-lg: 40px;
  --avatar-size-sm: 36px;
  --avatar-size-md: 40px;
  --avatar-size-lg: 48px;
  --avatar-size-xl: 96px;
  --icon-button-size-xs: 32px;
  --icon-button-size-sm: 40px;
  --icon-button-size-md: 48px;
  --icon-button-size-lg: 56px;
  --chip-padding: 7px 12px;
  --icon-text-gap: 6px;
  --chip-remove-size: 24px;
  --badge-padding: 6px 12px;
  --stat-value-sm: var(--text-2xl);
  --stat-value-md: 32px;
  --stat-value-lg: var(--text-5xl);
  --accent-rule-width: 40px;
  --accent-rule-height: 2px;
  --marker-block-padding: 0.18em;

  /* CZ: Nejmenší dotykový cíl (WCAG 2.5.5, 44 × 44 px) pro `@media (pointer: coarse)` v novém
         UI: tablet a telefon s dotykem (8. 10. 2026, profil tabletu `frontend/e2e/viewports.ts`).
         Stejná hodnota jako `.btn-sm` / `.btn-icon` v Django `components.css`. Myš zůstává
         kompaktní, cíl se zvětší jen na dotyku (`min-block-size`, nebo průhledná plocha sdílené
         třídy `frontend/src/styles/touchTarget.module.css`, která vzhled nemění).
     EN: The smallest touch target (WCAG 2.5.5, 44 × 44 px) for `@media (pointer: coarse)` in the
         new UI: a touch tablet and phone (8 Oct 2026, the tablet profile in
         `frontend/e2e/viewports.ts`). The same value as `.btn-sm` / `.btn-icon` in Django's
         `components.css`. A mouse stays compact, the target grows only under touch
         (`min-block-size`, or the transparent area of the shared class
         `frontend/src/styles/touchTarget.module.css`, which keeps the look). */
  --touch-target-min: 44px;
}

/* =========================================================================
 * TMAVÝ REŽIM / DARK MODE — html.dark
 * =========================================================================
 * CZ: Rozhodnutí majitele 6. 9. 2026: dark mode se ZACHOVÁVÁ. Vzor s ním ale skoro
 *     nepočítá — přepisuje osm tokenů, aplikace jich potřebuje desítky (stavy, grafy,
 *     rampy, stíny). Zbytek je proto dopočítaný podle jediného pravidla, které vzor dává:
 *     „dark mode = navy, nikdy černá; sekce je celá světlá NEBO celá tmavá."
 *
 *     Klíč k převodu ramp: `pale` a `tint` jsou v tmavém režimu POZADÍ (musí ztmavnout),
 *     `base` zůstává (je to sytá dekorativní barva), `text` musí naopak ZESVĚTLET, jinak
 *     tmavý text sedí na tmavém podkladu.
 * EN: Owner's decision, 6 Sep 2026: dark mode STAYS. The sample barely accounts for it —
 *     it overrides eight tokens where the app needs dozens (states, charts, ramps, shadows).
 *     The rest is derived from the one rule the sample does give: "dark mode = navy, never pure
 *     black; a section is fully light OR fully dark."
 *
 *     The key for converting the ramps: in dark mode `pale` and `tint` are BACKGROUNDS (they must
 *     darken), `base` stays (it is a saturated decorative colour), and `text` must LIGHTEN,
 *     otherwise dark text sits on a dark ground. */
html.dark {
  /* ---- Rampy: pozadí ztmavnou, text zesvětlí ---- */
  --blue-pale: #0D2440;
  --blue-tint: #123A5E;
  --blue-text: #67B2EB;                    /* 8.22:1 na navy */
  --orange-pale: #3A2708;
  --orange-tint: #4E340B;
  --orange-text: #FFB454;                  /* 10.4:1 na navy */
  --green-pale: #26330D;
  --green-tint: #334513;
  --green-text: #BAD671;                   /* 11.9:1 na navy */
  --gray-pale: #1A1C21;
  --gray-tint: #24272E;
  --gray-text: #A8A8A7;                    /* 8.5:1 na navy */

  /* ---- Povrchy ----
     CZ: Vzor má `--surface-card: rgba(255,255,255,.08)` — průsvitná karta nad navy. Drží se to.
     EN: The sample uses `--surface-card: rgba(255,255,255,.08)` — a translucent card over navy.
         Kept as is. */
  --surface-page: var(--navy);
  --surface-card: var(--surface-card-dark);
  --surface-panel: rgba(255, 255, 255, 0.06);
  /* CZ: Překrývající vrstva je o stupeň SVĚTLEJŠÍ než stránka a NEPRŮHLEDNÁ — jinak by přes
         ni prosvítal obsah pod ní. Stejný důvod jako u tooltipu grafu.
     EN: An overlaying layer is one step LIGHTER than the page and OPAQUE — otherwise the content
         beneath would show through. The same reason as for the chart tooltip. */
  --surface-overlay: #1A1C21;
  /* CZ: Navy závoj nad navy stránkou by nic neztlumil; tmavý režim proto zatemňuje hlouběji
         (stejná alfa jako `--shadow-overlay` níž).
     EN: A navy scrim over a navy page would dim nothing; dark mode therefore darkens deeper (the
         same alpha as `--shadow-overlay` below). */
  --surface-scrim: rgba(0, 0, 0, 0.6);

  /* ---- Text ---- */
  --text-primary: var(--white);
  --text-body: rgba(255, 255, 255, 0.78);
  --text-secondary: rgba(255, 255, 255, 0.55);
  /* CZ: `--text-link` se tu ZÁMĚRNĚ nepřepisuje — ve světlém bloku ukazuje na `--blue-text`
         a ten se o kus výš překlápí na #67B2EB. Doslovné zopakování `--text-link: var(--blue-text)`
         by nic nezměnilo a `test_light_palette_never_returns_dark_mode_values` ho hlásí jako
         podezření na prosakující tmavou hodnotu. Stejný důvod platí pro `--color-brand-primary`.
     EN: `--text-link` is DELIBERATELY not overridden here — in the light block it points at
         `--blue-text`, which flips to #67B2EB above. Repeating `--text-link: var(--blue-text)`
         would change nothing and `test_light_palette_never_returns_dark_mode_values` reports it as
         a suspected dark-value leak. The same reasoning applies to `--color-brand-primary`. */

  /* CZ: Popředí na barevné výplni se NEMĚNÍ. Výplně (`--action-*`) zůstávají v tmavém režimu
         syté a stejné, takže černý text na oranžové je čitelný v obou režimech. Kdyby se token
         překlopil, text na tlačítku by v dark modu zbělal a spadl na 2.22:1.
     EN: The foreground on a coloured fill does NOT change. The fills (`--action-*`) stay
         saturated and identical in dark mode, so black text on orange is legible in both. Were
         this token to flip, the button label would turn white in dark mode and drop to 2.22:1. */

  /* ---- Linky ---- */
  --line-card: var(--line-on-dark);
  --line-divider: var(--line-on-dark);
  --border: rgba(255, 255, 255, 0.15);

  /* ---- Stavy ---- */
  --state-danger: #F97066;
  --state-danger-soft: #3A0F0F;
  --state-danger-text: #FDA29B;
  --text-on-danger: var(--black);

  /* ---- Grafy ----
     CZ: Tooltip zůstává SVĚTLEJŠÍ než stránka (navy #0F1115), jinak z něj zbyde jen rámeček.
     EN: The tooltip stays LIGHTER than the page (navy #0F1115), otherwise only a border is left. */
  --chart-tooltip-bg: #1A1C21;


  /* CZ: Stíny na tmavém pozadí musí být hlubší — světlý závoj by je jinak neoddělil.
     EN: Shadows on a dark ground must be deeper — a light veil would not separate them. */
  --shadow-overlay: 0 8px 32px rgba(0, 0, 0, 0.6);
}
