/*
 * phmanager — visual standard per DESIGN.md (Lovec family, dark only).
 *
 * No framework, no build step: a small reset followed by the app's own
 * components, all built from the :root tokens below. Templates never carry
 * inline styles — a new screen composes these classes or a class is added here.
 */

:root {
    color-scheme: dark;

    /* Surfaces */
    --bg: #0d1117;
    --surface: #151a23;
    --surface-hover: #1b212c;
    --surface-sunken: #10151d;

    /* Lines */
    --border: #2a3140;
    --border-strong: #3a4353;
    --border-accent: rgba(109, 106, 248, 0.35);

    /* Text */
    --text: #e6e9ef;
    --text-strong: #ffffff;
    --text-muted: #8b94a7;

    /* Accents */
    --accent: #6d6af8;
    --accent-hover: #8b88ff;
    --accent-soft: rgba(109, 106, 248, 0.16);
    --accent-contrast: #ffffff;
    --link: #58a6ff;
    --link-hover: #86c1ff;

    /* States */
    --ok: #3fb950;
    --ok-soft: rgba(63, 185, 80, 0.16);
    --warn: #d29922;
    --warn-soft: rgba(210, 153, 34, 0.16);
    --danger: #f85149;
    --danger-soft: rgba(248, 81, 73, 0.16);
    --info: #58a6ff;
    --info-soft: rgba(88, 166, 255, 0.16);
    --neutral: #8b94a7;
    --neutral-soft: rgba(139, 148, 167, 0.16);

    /* Shape and rhythm */
    --radius: 8px;
    --radius-sm: 6px;
    --radius-pill: 999px;
    --shadow-panel: 0 1px 2px rgba(0, 0, 0, 0.4);
    --content-width: 82rem;

    /*
     * Rozestupy sekcí (DESIGN.md, „Rozestupy sekcí"). Nadpis patří k tomu, co
     * je pod ním, ne k tomu, co je nad ním — takže mezera nad je zřetelně
     * větší než mezera pod. Jedno místo pro celou aplikaci: do běhu 26 měl
     * `.app-section-title` jen `margin-bottom` a každý nadpis na detailu se
     * lepil na předchozí kartu.
     */
    --mezera-nad-sekci: 2.25rem;
    --mezera-pod-nadpisem: 0.75rem;

    /*
     * Výstražný blok hned pod nadpisem karty (běh 28, C7). Běžný odstavec pod
     * nadpisem drží 0,75 rem a je to správně — patří k němu. Ohraničený barevný
     * blok ne: má vlastní rám a výplň, takže při 12 px vypadá jako podtržení
     * nadpisu, ne jako varování. Žlutý „Nezařazený zbytek" na obrazovce
     * rozdělení byl přesně tenhle případ.
     */
    --mezera-nad-vystrahou: 1.5rem;

    --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

/* ---------------------------------------------------------------- reset -- */

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

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-sans);
    font-size: 15px;
    line-height: 1.5;
}

h1,
h2,
h3,
p,
figure,
blockquote,
dl,
dd {
    margin: 0;
}

ul,
ol {
    margin: 0;
    padding: 0;
    list-style: none;
}

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

a:hover {
    color: var(--link-hover);
    text-decoration: underline;
}

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

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

code,
kbd,
samp,
pre {
    font-family: var(--font-mono);
    font-size: 0.9em;
}

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* --------------------------------------------------------------- navbar -- */

/*
 * Wraps at any width, not only on a phone. The menu grows by a tab every time a
 * module is added, and the eighth one (Přesuny, běh 24) was the one that pushed
 * a 1024px window into a horizontal scrollbar — on every screen at once,
 * because the navbar is on every screen. Wrapping is the cheap answer and the
 * right one: DESIGN.md gives a wide page the whole window precisely so that
 * nothing has to scroll sideways.
 */
.app-navbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.5rem;
    padding: 0.65rem 1.5rem;
    background: var(--surface);
    border-bottom: 2px solid var(--accent);
}

.app-brand {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text-strong);
    letter-spacing: 0.2px;
    text-decoration: none;
}

.app-brand:hover {
    color: var(--text-strong);
    text-decoration: none;
}

.app-menu {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
}

.app-menu-link {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.35rem 0.8rem;
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-weight: 500;
}

.app-menu-link:hover {
    background: var(--surface-hover);
    color: var(--text);
    text-decoration: none;
}

.app-menu-link.is-active {
    background: var(--accent);
    color: var(--accent-contrast);
    font-weight: 600;
}

.app-menu-link.is-active:hover {
    background: var(--accent-hover);
    color: var(--accent-contrast);
}

.app-navbar-spacer {
    flex: 1;
}

.app-user {
    color: var(--text-muted);
    font-size: 0.875rem;
}

.app-badge {
    display: inline-block;
    min-width: 1.5em;
    padding: 0.05em 0.45em;
    border-radius: var(--radius-pill);
    background: var(--warn-soft);
    color: var(--warn);
    font-size: 0.75rem;
    font-weight: 700;
    line-height: 1.6;
    text-align: center;
}

.app-menu-link.is-active .app-badge {
    background: rgba(0, 0, 0, 0.28);
    color: var(--accent-contrast);
}

/* --------------------------------------------------------------- layout -- */

.app-main {
    max-width: var(--content-width);
    margin: 1.75rem auto;
    padding: 0 1.5rem;
}

/* Stránky, jejichž hlavní obsah je datová tabulka, dostávají celé okno —
   82 rem ořezávalo import i výkazy do vodorovného scrollu, přestože vedle
   tabulky bylo prázdno. Zapíná to <body class="app-siroko"> z šablony
   (base.html.twig, blok telo_trida); patička jde s obsahem, aby se linka
   nad ní nezastavovala uprostřed. Viz DESIGN.md, „Šířka obsahu". */
.app-siroko .app-main,
.app-siroko .app-footer {
    max-width: none;
}

.app-footer {
    max-width: var(--content-width);
    margin: 2.5rem auto 1.5rem;
    padding: 1rem 1.5rem 0;
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 0.8125rem;
}

/* The deployed version, from the VERSION file. Deliberately quiet — it is
   there to be quoted in a bug report, not to be read every day. */
.app-footer-verze {
    margin-left: 0.5rem;
    font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    opacity: 0.8;
}

.app-page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}

.app-title {
    font-size: 1.875rem;
    font-weight: 700;
    color: var(--text-strong);
    line-height: 1.2;
}

.app-subtitle {
    margin-top: 0.35rem;
    color: var(--text-muted);
    max-width: 60ch;
}

/*
 * Drobečky nad nadpisem: klient › projekt › zakázka. Malé a tlumené — jsou to
 * souřadnice stránky, ne její obsah, a nadpis pod nimi musí zůstat to hlavní,
 * co oko najde.
 */
.app-breadcrumb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-bottom: 0.3rem;
    font-size: 0.8125rem;
    color: var(--text-muted);
}

.app-breadcrumb-sep {
    color: var(--border);
}

/*
 * Zanořený řádek tabulky: zakázka pod svým projektem na detailu klienta.
 * Odsazení a tlumené pozadí místo druhé tabulky — je to jeden seznam, jen
 * dvouúrovňový, a dvě tabulky pod sebou by to popíraly.
 */
.app-row-vnorena > td:first-child {
    padding-left: 1.75rem;
}

.app-row-vnorena {
    background: color-mix(in srgb, var(--surface) 55%, transparent);
}

.app-tree-vetev {
    margin-right: 0.35rem;
    color: var(--text-muted);
}

/*
 * Nadpis sekce — jediné místo, kde se rozestup mezi sekcemi určuje.
 *
 * Nahoře zřetelná mezera, dole menší: nadpis se tím přilepí k obsahu, který
 * uvádí, a odlepí od karty, která skončila. Obojí z proměnných, ne z čísel
 * rozsypaných po šablonách — DESIGN.md, „Rozestupy sekcí".
 */
.app-section-title {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-strong);
    margin: var(--mezera-nad-sekci) 0 var(--mezera-pod-nadpisem);
}

/*
 * Nadpis na začátku karty je nadpisem té karty, ne další sekce — mezeru nad
 * ním už udělal její padding a druhá by kartu jen roztáhla.
 */
.app-panel > .app-section-title:first-child {
    margin-top: 0;
}

/*
 * Nadpis a ohraničený blok pod ním se nesmí dotýkat (běh 28, C7). Platí pro
 * oba nadpisy, protože důvod je týž — barevný rám nalepený na nadpis vypadá
 * jako jeho podtržení. Řeší to jedno místo, ne šablona: DESIGN.md, „Rozestupy
 * sekcí".
 */
.app-section-title + .app-note,
.app-subsection-title + .app-note {
    margin-top: var(--mezera-nad-vystrahou);
}

.app-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

/* ---------------------------------------------------------------- panel -- */

.app-panel {
    background: var(--surface);
    border: 1px solid var(--border-accent);
    border-radius: var(--radius);
    padding: 1.1rem 1.25rem;
    box-shadow: var(--shadow-panel);
}

.app-panel + .app-panel {
    margin-top: 1.25rem;
}

/* -------------------------------------------------------------- filters -- */

.app-filters {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 0.9rem 1.1rem;
}

.app-filters-actions {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

/*
 * Pole přes celou šířku panelu (běh 28, C4). `.app-form` to umělo od začátku,
 * `.app-filters` ne — a nápověda AI, která je odstavec, se do jedné dvanáctinové
 * buňky mřížky nevešla ani omylem.
 */
.app-filters .app-field-wide {
    grid-column: 1 / -1;
}

/* --------------------------------------------------------------- fields -- */

.app-field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    min-width: 0;
}

.app-field-label,
.app-label {
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}

.app-input,
.app-select,
.app-textarea {
    width: 100%;
    padding: 0.5rem 0.65rem;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
}

/*
 * Vybraná volba, která se do políčka nevejde, se jinak uřízne uprostřed slova
 * a čte se jako jiná hodnota: filtr *Původ* na výkazech ukazoval
 * „— vše kromě stornovanýcl". Sloupce filtrů jsou mřížka o pevném dílu, takže
 * políčko se za textem nezvětší; výpustka aspoň řekne, že je hodnota delší.
 */
.app-select {
    text-overflow: ellipsis;
}

.app-textarea {
    min-height: 7rem;
    resize: vertical;
}

.app-input:hover,
.app-select:hover,
.app-textarea:hover {
    border-color: var(--border-strong);
}

.app-input:focus,
.app-select:focus,
.app-textarea:focus {
    border-color: var(--accent);
    outline: none;
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.app-input[aria-invalid="true"],
.app-select[aria-invalid="true"],
.app-textarea[aria-invalid="true"] {
    border-color: var(--danger);
}

/*
 * A field with an action next to it — the IČO box and its "Načíst z ARES"
 * button. The input takes the space, the button keeps its own width.
 */
.app-field-akce {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

.app-field-akce .app-input {
    flex: 1 1 auto;
    min-width: 0;
}

.app-field-akce .app-btn {
    flex: 0 0 auto;
}

.app-field-hint {
    font-size: 0.8125rem;
    color: var(--text-muted);
}

/*
 * Pole s heslem a okem „zobrazit heslo" (task 73, DESIGN.md „Formuláře
 * s heslem"). Oko leží uvnitř pravého okraje pole; pole mu uhýbá odsazením,
 * aby dlouhé heslo nepsalo pod ikonu.
 */
.app-heslo {
    position: relative;
    display: block;
}

.app-heslo .app-input {
    padding-right: 2.75rem;
}

.app-heslo-oko {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    padding: 0;
    color: var(--text-muted);
    background: none;
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
}

.app-heslo-oko[hidden] {
    display: none;
}

.app-heslo-oko svg {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.app-heslo-oko:hover {
    color: var(--text);
}

.app-heslo-oko[aria-pressed="true"] {
    color: var(--accent-hover);
}

/* Přeškrtnutí patří stavu „heslo je skryté" — stisknuté oko je čisté. */
.app-heslo-oko[aria-pressed="true"] .app-heslo-oko-skrt {
    display: none;
}

.app-heslo-oko:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Účet, kterému se heslo nastavuje: jen ke čtení, pro správce hesel. */
.app-input[readonly] {
    color: var(--text-muted);
    cursor: default;
}

/* --------------------------------------------------------- vysvětlivky -- */

/*
 * Dlouhá nápověda za ikonou „?" vedle popisku (DESIGN.md, „Vysvětlivky").
 *
 * Řádek popisku je kvůli tomu flex: ikona patří k popisku, ne pod pole, a mřížku
 * filtrů nesmí rozhodit — proto se nezalamuje a nese vlastní výšku popisku.
 */
.app-field-label-radek {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    min-width: 0;
}

.app-field-label-radek .app-field-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-vysvetlivka {
    position: relative;
    display: inline-flex;
    flex: 0 0 auto;
    line-height: 1;
}

.app-vysvetlivka-ikona {
    width: 1.05rem;
    height: 1.05rem;
    padding: 0;
    font-size: 0.6875rem;
    font-weight: 700;
    line-height: 1;
    color: var(--text-muted);
    background: var(--surface-sunken);
    border: 1px solid var(--border-strong);
    border-radius: 50%;
    cursor: help;
}

.app-vysvetlivka-ikona:hover,
.app-vysvetlivka-ikona[aria-expanded="true"] {
    color: var(--text);
    border-color: var(--accent);
}

.app-vysvetlivka-ikona:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/*
 * Bublina. Normální písmo, ne verzálky popisku — je to věta k přečtení.
 *
 * Nad polem, ne pod ním: pod polem by ji ve filtrech překryl řádek s tlačítky
 * a ve formulářích chybová hláška.
 */
.app-vysvetlivka-text {
    position: absolute;
    z-index: 30;
    bottom: calc(100% + 0.4rem);
    left: -0.5rem;
    right: auto;
    width: 22rem;
    max-width: min(22rem, 60vw);
    padding: 0.6rem 0.75rem;
    font-size: 0.8125rem;
    font-weight: 400;
    line-height: 1.45;
    text-transform: none;
    letter-spacing: normal;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-panel);
}

/*
 * Bublina u pravého okraje se otevírá doleva.
 *
 * Přetečení doprava přidá stránce vodorovný scrollbar (nález GUI auditu na
 * 1024 px u posledního sloupce filtru); přetečení doleva v LTR nestojí nic,
 * a s `max-width` výš se sem stejně vejde. Stranu určuje `assets/app.js` podle
 * polohy ikony — šířku schované bubliny změřit nejde.
 */
.app-vysvetlivka.je-vlevo .app-vysvetlivka-text {
    left: auto;
    right: -0.5rem;
}

/*
 * Najetí myší bublinu ukáže, aniž by se na cokoli klikalo.
 *
 * Přebíjí to `[hidden]`, které drží stav klikání — schválně: `hidden` zůstává
 * jedinou pravdou o tom, co je *otevřené* (a co hlásí `aria-expanded`), tohle je
 * čistě vizuální zkratka pro myš. Klávesnice a odečítač jdou přes tlačítko.
 */
.app-vysvetlivka:hover .app-vysvetlivka-text[hidden] {
    display: block;
}

/*
 * Odrážky uvnitř bubliny.
 *
 * Bublina je `span`, takže `ul`/`li` do ní nesmí — je to neplatné HTML.
 * Sémantiku proto nese ARIA (`role="list"` / `role="listitem"` v šabloně)
 * a odrážku `display: list-item`; odečítač i oko dostanou totéž, co by dostaly
 * od `ul`. Zavedeno v běhu 29 pro vysvětlivku stavů roztřídění na `/import`,
 * kde je výčet stavů čitelnější než odstavec.
 */
.app-vysvetlivka-seznam {
    display: block;
    margin: 0.4rem 0 0;
    padding-left: 1.1rem;
}

.app-vysvetlivka-polozka {
    display: list-item;
    list-style: disc outside;
}

.app-vysvetlivka-polozka + .app-vysvetlivka-polozka {
    margin-top: 0.3rem;
}

.app-field-error {
    font-size: 0.8125rem;
    color: var(--danger);
}

.app-form {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1rem 1.25rem;
    max-width: 52rem;
}

.app-form .app-field-wide {
    grid-column: 1 / -1;
}

.app-form-actions {
    grid-column: 1 / -1;
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    align-items: center;
    margin-top: 0.25rem;
}

.app-form-actions-end {
    margin-left: auto;
}

/* -------------------------------------------------------------- buttons -- */

.app-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.95rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: transparent;
    color: var(--text);
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

.app-btn:hover {
    text-decoration: none;
}

.app-btn-primary {
    background: var(--accent);
    color: var(--accent-contrast);
}

.app-btn-primary:hover {
    background: var(--accent-hover);
    color: var(--accent-contrast);
}

.app-btn-secondary {
    border-color: var(--border-strong);
    color: var(--text);
}

.app-btn-secondary:hover {
    background: var(--surface-hover);
    color: var(--text);
}

.app-btn-danger {
    border-color: var(--danger);
    color: var(--danger);
}

.app-btn-danger:hover {
    background: var(--danger-soft);
    color: var(--danger);
}

.app-btn-sm {
    padding: 0.3rem 0.65rem;
    font-size: 0.8125rem;
}

.app-btn:disabled,
.app-btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* ------------------------------------------------------------- meta row -- */

.app-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin: 1.25rem 0 0.6rem;
    color: var(--text-muted);
    font-size: 0.875rem;
}

.app-meta strong {
    color: var(--text);
}

.app-meta-form {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.app-meta-form .app-select {
    width: auto;
    padding: 0.25rem 0.5rem;
    font-size: 0.8125rem;
}

/* --------------------------------------------------------------- tables -- */

.app-table-wrap {
    overflow-x: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.app-table {
    width: 100%;
    border-collapse: collapse;
}

.app-table th,
.app-table td {
    padding: 0.7rem 0.9rem;
    text-align: left;
    vertical-align: top;
    border-bottom: 1px solid var(--border);
}

.app-table thead th {
    padding-top: 0.6rem;
    padding-bottom: 0.6rem;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    white-space: nowrap;
    background: var(--surface-sunken);
}

.app-table tbody tr:last-child th,
.app-table tbody tr:last-child td {
    border-bottom: none;
}

.app-table tbody tr:hover {
    background: var(--surface-hover);
}

.app-table tbody th {
    font-weight: 600;
    color: var(--text-strong);
}

/*
 * A cancelled row: still readable, visibly not live work. Struck-through
 * figures rather than a hidden row — the výkaz counts towards no budget any
 * more, but it stays on record and has to stay findable.
 */
.app-table tbody tr.app-row-storno td {
    color: var(--text-muted);
}

.app-table tbody tr.app-row-storno td.app-numeric {
    text-decoration: line-through;
}

.app-numeric {
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.app-nowrap {
    white-space: nowrap;
}

.app-muted {
    color: var(--text-muted);
}

.app-clamp {
    display: block;
    max-width: 32rem;
}

.app-sort {
    color: var(--accent);
    text-decoration: none;
    white-space: nowrap;
}

.app-sort:hover {
    color: var(--accent-hover);
    text-decoration: none;
}

.app-sort-arrow {
    color: var(--text-muted);
}

/* ---------------------------------------------------------------- cards -- */

/*
 * Seznam jako karty na úzké obrazovce (task 63, DESIGN.md „Mobil"). Šablona
 * nese tabulku i karty a tady se rozhoduje, co je vidět: `.app-jen-siroke`
 * nad hranicí 48 rem, `.app-jen-uzke` pod ní.
 *
 * Karta je jeden záznam na čtyřech řádcích a žádný řádek se neláme — dlouhý
 * text končí výpustkou. Lámání je přesně to, co kartu zavedlo: typ služby
 * o šesti řádcích ve sloupci tabulky.
 */
.app-jen-uzke {
    display: none;
}

/* Číslo záznamu před názvem tam, kde na jeho sloupec není místo (task 77). */
.app-id-u-nazvu {
    margin-right: 0.35rem;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    font-weight: 400;
}

.app-karty {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

/* Seznam pod souhrnnou kartou na detailu (služby klienta). */
.app-panel + .app-table-wrap,
.app-panel + .app-table-wrap + .app-karty {
    margin-top: 1.25rem;
}

.app-karta {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.15rem;
    padding: 0.7rem 0.85rem;
    border-bottom: 1px solid var(--border);
}

.app-karta:last-child {
    border-bottom: none;
}

.app-karta-nazev {
    display: flex;
    align-items: center;
    min-width: 0;
}

.app-karta-odkaz {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
}

/* Klepnout jde na celou kartu, ne jen na název — cíl pro prst je karta. */
.app-karta-odkaz::after {
    content: "";
    position: absolute;
    inset: 0;
}

.app-karta-popis {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-muted);
    font-size: 0.8125rem;
}

.app-karta-radek {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    font-size: 0.875rem;
    white-space: nowrap;
}

.app-karta-radek > :first-child {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.app-karta-cena {
    color: var(--text-strong);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/*
 * Tečka semaforu před názvem. Barvy jsou stavové tokeny pilulek — tatáž čtyři
 * čtení jako `.app-semafor`, žádná druhá paleta. Mezera za tečkou 0,7 rem
 * (11 px): menší splývala s prvním písmenem názvu.
 */
.app-tecka {
    flex: none;
    width: 0.625rem;
    height: 0.625rem;
    margin-right: 0.7rem;
    border-radius: 50%;
    background: var(--neutral);
}

.app-tecka-ok {
    background: var(--ok);
}

.app-tecka-warn {
    background: var(--warn);
}

.app-tecka-danger {
    background: var(--danger);
}

.app-tecka-neutral {
    background: var(--neutral);
}

/* „4 × 430 Kč" pod cenou za období ve sloupci tabulky. */
.app-cena-rozpad {
    display: block;
    font-size: 0.8125rem;
}

/* ---------------------------------------------------------------- pills -- */

.app-pill {
    display: inline-block;
    padding: 0.1rem 0.6rem;
    border-radius: var(--radius-pill);
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}

.app-pill-ok {
    background: var(--ok-soft);
    color: var(--ok);
}

.app-pill-warn {
    background: var(--warn-soft);
    color: var(--warn);
}

.app-pill-danger {
    background: var(--danger-soft);
    color: var(--danger);
}

.app-pill-info {
    background: var(--info-soft);
    color: var(--info);
}

.app-pill-neutral {
    background: var(--neutral-soft);
    color: var(--neutral);
}

.app-pill-accent {
    background: var(--accent-soft);
    color: var(--accent-hover);
}

.app-code {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--text);
    white-space: nowrap;
}

/*
 * Indikátor běžícího požadavku vedle tlačítka (běh 28, C5). Tlumený text, ne
 * pilulka: je to stav akce, ne stav dat, a zmizí s odpovědí.
 */
.app-prubeh {
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* ------------------------------------------------------- kroky importu -- */

/*
 * Lišta kroků importu (běh 28, C1). Tři kroky vedle sebe, aktuální zvýrazněný,
 * pod nimi věta „kam dál". Není to stránkování ani menu — je to odpověď na
 * otázku „kde jsem", kterou import do běhu 28 nezodpovídal nikde.
 */
.app-kroky {
    margin-bottom: 1.25rem;
    padding: 0.75rem 1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.app-kroky-seznam {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 1.5rem;
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: none;
}

.app-krok {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
    color: var(--text-muted);
}

.app-krok-cislo {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    font-size: 0.8rem;
    font-weight: 600;
}

.app-krok-aktualni {
    color: var(--text-strong);
    font-weight: 600;
}

.app-krok-aktualni .app-krok-cislo {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.app-kroky-popis,
.app-kroky-dal {
    margin: 0.5rem 0 0;
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* ---------------------------------------------------------------- flash -- */

.app-flash {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.65rem 1rem;
    margin-bottom: 1rem;
    border: 1px solid;
    border-radius: var(--radius-pill);
}

.app-flash-success {
    background: var(--ok-soft);
    border-color: rgba(63, 185, 80, 0.4);
    color: var(--ok);
}

.app-flash-error {
    background: var(--danger-soft);
    border-color: rgba(248, 81, 73, 0.4);
    color: var(--danger);
}

.app-flash-text {
    flex: 1;
}

.app-flash-close {
    border: none;
    background: transparent;
    color: inherit;
    font-size: 1.1rem;
    line-height: 1;
    padding: 0.1rem 0.3rem;
    cursor: pointer;
    opacity: 0.7;
}

.app-flash-close:hover {
    opacity: 1;
}

/*
 * Hláška, která končí odkazem „kam dál" (běh 28, C1). Odkaz je uvnitř zprávy
 * a musí být vidět jako odkaz i na barevném podkladu pilulky, jinak ho oko
 * přečte jako zbytek věty a průvodce krokem nikam nedovede.
 */
.app-flash-akce {
    margin-left: 0.35rem;
    color: inherit;
    font-weight: 600;
    text-decoration: underline;
    white-space: nowrap;
}

/* --------------------------------------------------------- empty states -- */

.app-empty {
    padding: 2.5rem 1.5rem;
    text-align: center;
    color: var(--text-muted);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.app-empty-icon {
    display: block;
    font-size: 1.75rem;
    margin-bottom: 0.5rem;
    opacity: 0.75;
}

.app-empty-actions {
    margin-top: 1rem;
    display: flex;
    justify-content: center;
    gap: 0.5rem;
}

/* ----------------------------------------------------------- pagination -- */

.app-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    flex-wrap: wrap;
    margin-top: 1rem;
}

.app-page {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.1rem;
    padding: 0.3rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-size: 0.875rem;
    text-decoration: none;
}

.app-page:hover {
    background: var(--surface-hover);
    color: var(--text);
    text-decoration: none;
}

.app-page.is-active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-contrast);
    font-weight: 600;
}

.app-page.is-disabled {
    opacity: 0.4;
    pointer-events: none;
}

.app-page-gap {
    padding: 0.3rem 0.2rem;
    color: var(--text-muted);
}

/* --------------------------------------------------------- detail lists -- */

.app-detail {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 1rem 1.5rem;
}

.app-detail-item {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

.app-detail-value {
    color: var(--text-strong);
    overflow-wrap: anywhere;
}

.app-detail-popis {
    margin-top: 1.25rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--border);
}

/* ------------------------------------------------------ bulk selection -- */

.app-bulkbar {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-bottom: 0.6rem;
    padding: 0.6rem 0.9rem;
    background: var(--surface);
    border: 1px solid var(--border-accent);
    border-radius: var(--radius);
}

.app-bulkbar .app-select {
    width: auto;
    min-width: 14rem;
}

.app-bulkbar-count {
    margin-right: auto;
    color: var(--text-muted);
    font-size: 0.875rem;
}

.app-bulkbar-count.is-active {
    color: var(--text);
    font-weight: 600;
}

.app-check-col {
    width: 1px;
    white-space: nowrap;
}

.app-check {
    width: 1rem;
    height: 1rem;
    accent-color: var(--accent);
    cursor: pointer;
}

.app-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

/* ---------------------------------------------------- e-mailové adresy -- */

/*
 * Klient smí mít víc adres (task 60). V buňce tabulky se ukáže první — dlouhá
 * se zkrátí výpustkou — a „+N" za ní, takže sloupec drží šířku při jedné
 * adrese i při deseti. Na detailu a ve formuláři je seznam pod sebou a dlouhá
 * adresa se zalomí, místo aby stránku roztáhla do strany.
 */
.app-emaily {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    max-width: 16rem;
    vertical-align: bottom;
}

.app-emaily-prvni {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-emaily-seznam {
    margin: 0.75rem 0 0;
    padding: 0;
    list-style: none;
}

.app-emaily-seznam li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    max-width: 36rem;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--border);
}

.app-emaily-adresa {
    min-width: 0;
    color: var(--text-strong);
    overflow-wrap: anywhere;
}

.app-emaily-pridat {
    max-width: 36rem;
    margin-top: 1rem;
}

.app-emaily-radky {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* Na úzkém displeji jde tlačítko pod pole, místo aby ho zmáčklo na pár znaků. */
.app-emaily-radky .app-field-akce,
.app-emaily-pridat .app-field-akce {
    flex-wrap: wrap;
}

.app-emaily-radky .app-input,
.app-emaily-pridat .app-input {
    flex: 1 1 14rem;
}

.app-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* --------------------------------------------------------------- notes -- */

.app-note {
    margin: 0.75rem 0 0;
    padding: 0.55rem 0.85rem;
    border: 1px solid;
    border-radius: var(--radius);
    font-size: 0.875rem;
}

.app-note:first-child {
    margin-top: 0;
}

.app-note-warn {
    background: var(--warn-soft);
    border-color: rgba(210, 153, 34, 0.4);
    color: var(--warn);
}

.app-note-danger {
    background: var(--danger-soft);
    border-color: rgba(248, 81, 73, 0.4);
    color: var(--danger);
}

.app-note strong {
    color: inherit;
}

/* -------------------------------------------------------------- semafor -- */

/*
 * Zdraví zakázky. Žádná nová barevná paleta — semafor jede na stavových
 * pilulkách (ok / warn / danger / neutral), protože je to stav jako každý jiný
 * a DESIGN.md říká „stavy vždy pilulkou". Tohle je jen náskok v procentních
 * bodech uvnitř pilulky: menší a tlumenější než samotný verdikt, aby barva
 * zůstala tím, co oko najde první.
 */
.app-semafor-naskok {
    font-variant-numeric: tabular-nums;
    font-size: 0.85em;
    opacity: 0.75;
    margin-left: 0.25rem;
}

/*
 * Auditní výpis vrácení běhu: text, který server složil po řádcích, takže
 * `nl2br` je jediné, co ho drží čitelný. Monospace ne — jsou to věty, ne kód.
 */
.app-vypis-vraceni {
    margin: 0;
    line-height: 1.7;
    color: var(--text-muted);
}

/* Podnadpis uvnitř panelu — o stupeň tišší než .app-section-title. */
.app-subsection-title {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin: 1rem 0 0.5rem;
}

/*
 * Potvrzovací zaškrtávátko u nevratné akce (přesun mezi klienty): text vedle
 * boxu, ne pod ním, a celá věta klikatelná — je to poslední místo, kde se dá
 * couvnout, takže musí být čitelná jako věta.
 */
.app-potvrzeni {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    cursor: pointer;
    line-height: 1.5;
}

/* ------------------------------------------------------------- čerpání -- */

/*
 * The budget burn-down. The bar is a native <progress> so that its fill can be
 * driven by the `value` attribute — DESIGN.md forbids inline styles, and a
 * div-based bar would need one for its width.
 */

.app-progress {
    display: block;
    width: 100%;
    height: 0.4rem;
    margin-top: 0.3rem;
    border: none;
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
    appearance: none;
    -webkit-appearance: none;
}

.app-progress-lg {
    height: 0.65rem;
    margin-top: 1rem;
}

.app-progress::-webkit-progress-bar {
    background: var(--surface-sunken);
    border-radius: var(--radius-pill);
}

.app-progress::-webkit-progress-value {
    background: var(--ok);
    border-radius: var(--radius-pill);
}

.app-progress::-moz-progress-bar {
    background: var(--ok);
    border-radius: var(--radius-pill);
}

.app-progress-varovani::-webkit-progress-value {
    background: var(--warn);
}

.app-progress-varovani::-moz-progress-bar {
    background: var(--warn);
}

.app-progress-prekroceno::-webkit-progress-value {
    background: var(--danger);
}

.app-progress-prekroceno::-moz-progress-bar {
    background: var(--danger);
}

.app-cerpani {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
    min-width: 11rem;
}

.app-cerpani-cisla {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.app-cerpani-radek {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.app-cerpani-zbyva {
    color: var(--text-muted);
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.app-cerpani-prekroceno .app-cerpani-cisla {
    color: var(--danger);
    font-weight: 600;
}

.app-cerpani-prekroceno .app-cerpani-zbyva {
    color: var(--danger);
}

.app-cerpani-panel {
    border-color: var(--border-accent);
}

.app-cerpani-panel.app-cerpani-varovani {
    border-color: rgba(210, 153, 34, 0.45);
}

.app-cerpani-panel.app-cerpani-prekroceno {
    border-color: rgba(248, 81, 73, 0.45);
}

/* ------------------------------------------------------------ checkbox -- */

.app-checkbox {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0;
}

/* Popisek smí nést e-mailové adresy (task 60) — dlouhá se zalomí, nepřeteče. */
.app-checkbox span {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* --------------------------------------------------------- inline forms -- */

.app-inline-form {
    display: flex;
    gap: 0.4rem;
    align-items: center;
    flex-wrap: nowrap;
}

.app-inline-form .app-select {
    min-width: 12rem;
}

.app-inline-form .app-combo {
    min-width: 14rem;
}

/*
 * Buňka se dvěma akcemi vedle sebe (úprava a smazání pravidla). Zalamuje se,
 * protože na 1024 px se dvě tlačítka a pole do jednoho sloupce nevejdou — a
 * vodorovný posuvník kvůli akční buňce je horší než dva řádky.
 */
.app-akce-bunka {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
}

.app-input-sm {
    width: 10rem;
    padding: 0.25rem 0.45rem;
}

/* ---------------------------------------------------------------- AI -- */

/*
 * Panel „AI návrh" a jeho výsledek. Akcentní rám, protože je to nabídka, ne
 * stav dat — a nabídka, kterou uživatel vyvolal, má být vidět na první pohled.
 */
.app-ai-panel,
.app-ai-vysledek {
    border-color: var(--border-accent);
}

/*
 * Návrh pod prahem jistoty. Zůstává v seznamu — schovat ho by z „AI navrhuje"
 * udělalo „AI rozhoduje" —, jen se nezaškrtne a je tlumený.
 */
.app-ai-pod-prahem > * {
    color: var(--text-muted);
}

/* --------------------------------------------- souhrn nad seznamem -- */

/*
 * Součty za celou vyfiltrovanou množinu, nad tabulkou (DESIGN.md, „Součty nad
 * seznamem hodin"). Vodorovný pruh vedle meta řádku: je to odpověď na filtr,
 * takže musí být vidět dřív než první řádek tabulky.
 */
.app-souhrn {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1.5rem;
    align-items: baseline;
    margin: 0 0 0.75rem;
    padding: 0.6rem 0.9rem;
    background: var(--surface);
    border: 1px solid var(--border-accent);
    border-radius: var(--radius);
}

.app-souhrn-polozka {
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
    min-width: 0;
}

.app-souhrn-popisek {
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
}

.app-souhrn-hodnota {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Nedoceněné hodiny jsou varování, ne další číslo v řadě. */
.app-souhrn-nedoceneno .app-souhrn-hodnota {
    color: var(--warn);
    font-size: 0.9375rem;
}

/*
 * Rozpad téhož součtu podle účtování (fakturovatelné × režie × opravy) — druhý
 * řádek téhož pruhu, ne další pruh: tři čísla, která dávají dohromady to nad
 * nimi, a tak mají být i vidět. Menší písmo, protože je to rozklad odpovědi,
 * ne odpověď.
 */
.app-souhrn-rozpad {
    display: flex;
    flex-wrap: wrap;
    flex-basis: 100%;
    gap: 0.25rem 1.5rem;
    align-items: baseline;
    padding-top: 0.45rem;
    border-top: 1px solid var(--border);
}

.app-souhrn-rozpad .app-souhrn-hodnota {
    font-size: 0.9375rem;
}

/* ------------------------------------------------ rozhodnutí v řádku -- */

/*
 * Řádek importu: vidět je **výsledek rozhodnutí**, formulář až po rozkliknutí
 * (DESIGN.md, „Rozhodnutí v řádku"). Nativní <details>, takže rozbalení funguje
 * i bez JavaScriptu a CSP na něj nemá co říct.
 */
.app-col-vyber {
    width: 1%;
    white-space: nowrap;
}

.app-col-rozhodnuti {
    min-width: 20rem;
}

/* „Upravit" a „Hotovo" v seznamu úkolů (task 84): vždy jedna řada vedle sebe —
   na telefonu tabulka roluje ve vlastním rámu, takže se nemusí zalamovat. */
.app-ukol-akce {
    flex-wrap: nowrap;
}

.app-rozhodnuti-shrnuti {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-wrap: wrap;
    cursor: pointer;
    list-style: none;
}

/* Trojúhelníček Safari kreslí zvlášť. */
.app-rozhodnuti-shrnuti::-webkit-details-marker {
    display: none;
}

/*
 * Vlastní šipka místo té systémové: řádek tabulky je hustý a nativní
 * trojúhelníček v něm sedí nakřivo.
 */
.app-rozhodnuti-shrnuti::before {
    content: "▸";
    color: var(--text-muted);
    font-size: 0.75rem;
}

.app-rozhodnuti[open] > .app-rozhodnuti-shrnuti::before {
    content: "▾";
}

.app-rozhodnuti-shrnuti:hover::before {
    color: var(--text);
}

.app-rozhodnuti-formular {
    margin-top: 0.6rem;
    padding: 0.75rem;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

/*
 * Nerozhodnutý řádek je jediná práce, která na obrazovce zbývá — hotové jsou
 * tiché. Proužek vlevo, ne obarvené pozadí: pilulky v řádku mají svoje barvy
 * a podbarvení by je přebilo.
 */
.app-row-nerozhodnuto > * {
    background: rgba(210, 153, 34, 0.06);
}

.app-row-nerozhodnuto > *:first-child {
    box-shadow: inset 3px 0 0 var(--warn);
}

.app-bulkbar-akce {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

/*
 * Pole hromadné lišty jako jeden celek: popisek a jeho ovládací prvek.
 *
 * Kvůli tomu existuje — `hidden` na samotném inputu by nechal viset osiřelý
 * popisek, takže „jen pole zvolené akce" potřebuje obal, na který se dá
 * `data-akce-pole` pověsit. Šířku určuje obsah, ne sto procent: v liště pole
 * stojí vedle sebe, ne pod sebou.
 */
.app-bulkbar-pole {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.app-bulkbar-pole .app-input,
.app-bulkbar-pole .app-combo-input {
    width: 14rem;
}

/* ---------------------------------------------------------- combobox -- */

/*
 * The one control this application picks a číselník row with. Built from the
 * same tokens as .app-input and .app-select so a field that suggests looks like
 * a field that does not; the dropdown borrows the panel's surface and border.
 */
.app-combo {
    position: relative;
    display: block;
}

.app-combo-input {
    width: 100%;
    /* Room for the arrow, so a long name does not run under it. */
    padding-right: 2rem;
}

/*
 * The arrow that says "this is a roletka".
 *
 * Drawn here rather than taken from the browser's select, because the control
 * underneath is an <input>: without it the field looked like somewhere to type
 * a name, which is exactly the reading the combobox is meant to stop. A chevron
 * of two borders — no image, no icon font, nothing to load.
 *
 * pointer-events: none is the part that matters: the arrow sits over the input,
 * so a click on it has to land on the field and open the offer, not be swallowed
 * by decoration.
 */
.app-combo-sipka {
    position: absolute;
    top: 50%;
    right: 0.85rem;
    width: 0.5rem;
    height: 0.5rem;
    margin-top: -0.35rem;
    border-right: 2px solid var(--text-muted);
    border-bottom: 2px solid var(--text-muted);
    transform: rotate(45deg);
    pointer-events: none;
}

.app-combo-input:focus ~ .app-combo-sipka {
    border-color: var(--accent);
}

.app-combo-input:disabled ~ .app-combo-sipka {
    opacity: 0.4;
}

.app-combo-list {
    position: absolute;
    z-index: 20;
    top: calc(100% + 0.25rem);
    left: 0;
    right: 0;
    margin: 0;
    padding: 0.25rem;
    list-style: none;
    max-height: 16rem;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: var(--shadow-panel);
}

.app-combo-item {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.4rem 0.55rem;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.app-combo-item:hover,
.app-combo-item.is-active {
    background: var(--accent-soft);
}

.app-combo-nazev {
    color: var(--text);
}

.app-combo-popis {
    font-size: 0.8rem;
    color: var(--text-muted);
}

.app-combo-empty {
    padding: 0.4rem 0.55rem;
    color: var(--text-muted);
}

/* ------------------------------------------------------- API klíč -- */

/*
 * The one-time display of a freshly issued API key. Monospace and selectable,
 * because it is meant to be copied — and framed as a warning, because it is the
 * only moment it can be read.
 */
.app-token {
    display: block;
    margin: 0.5rem 0;
    padding: 0.6rem 0.75rem;
    font-family: var(--font-mono);
    font-size: 0.9rem;
    color: var(--text-strong);
    background: var(--surface-sunken);
    border: 1px solid var(--warn);
    border-radius: var(--radius);
    overflow-wrap: anywhere;
    user-select: all;
}

/* ------------------------------------------------------------ disclosure -- */

/*
 * Collapsed detail (the rate timeline). Native <details>, so it works without
 * JavaScript; the summary is styled as a panel header and keeps the focus ring
 * the browser gives it.
 */
.app-details {
    margin: 0.5rem 0 1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.app-details-summary {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    padding: 0.65rem 0.9rem;
    color: var(--text);
    cursor: pointer;
    user-select: none;
}

.app-details-summary:hover {
    color: var(--text-strong);
    background: var(--surface-hover);
    border-radius: var(--radius);
}

.app-details[open] .app-details-summary {
    border-bottom: 1px solid var(--border);
    border-radius: var(--radius) var(--radius) 0 0;
}

.app-details .app-table-wrap {
    border: 0;
    border-radius: 0 0 var(--radius) var(--radius);
}

/*
 * Obsah sbalené sekce, který není tabulka (nabídka zakázky): tabulka si okraje
 * nese sama, próza a formulář ne — bez tohohle by text začínal na hraně rámu.
 */
.app-details-telo {
    padding: 0.9rem 1.1rem 1.1rem;
}

/* Nabídka je odstavce, ne řádek — textarea musí být vidět celá naráz. */
.app-textarea-vysoky {
    min-height: 16rem;
}

/* ------------------------------------------------------------- přihlášení -- */

/*
 * The login page. Narrow and centred rather than full width: it is the one
 * screen with two fields and no data, and stretching it across the content
 * width would leave the form floating in an empty page.
 */
.app-login {
    max-width: 26rem;
    margin: 3rem auto;
}

.app-login .app-form-actions {
    justify-content: stretch;
}

.app-login .app-form-actions .app-btn {
    flex: 1;
    justify-content: center;
}

.app-login-note {
    margin-top: 1rem;
    font-size: 0.8125rem;
}

/* The warning above the MCP login form (task 41) keeps its distance from the panel. */
.app-login .app-note {
    margin-bottom: 1rem;
}

/* The identity block in the navbar: e-mail plus role pill, then Odhlásit. */
.app-user .app-pill {
    margin-left: 0.35rem;
}

/* What each role may do, under the role select. A quiet list, not a table:
   it is read once when an account is founded and ignored ever after. */
.app-role-popis {
    margin: 0.5rem 0 0;
    padding-left: 1.1rem;
    color: var(--text-muted);
    font-size: 0.8125rem;
    line-height: 1.6;
}

.app-role-popis strong {
    color: var(--text);
}

/* --------------------------------------------------- rendered documents -- */

/*
 * docs/API.md rendered inside the application. Built from the same tokens as
 * everything else: headings borrow the section-title scale, code blocks the
 * sunken surface, and tables come out as .app-table so they read like every
 * other table in the app.
 */
.app-doc {
    display: block;
    max-width: 62rem;
    line-height: 1.65;
}

.app-doc h2,
.app-doc h3,
.app-doc h4 {
    margin: 1.75rem 0 0.5rem;
    color: var(--text-strong);
    font-weight: 600;
}

.app-doc h2 {
    font-size: 1.35rem;
    padding-bottom: 0.35rem;
    border-bottom: 1px solid var(--border);
}

.app-doc h3 {
    font-size: 1.1rem;
}

.app-doc h4 {
    font-size: 1rem;
}

.app-doc > :first-child {
    margin-top: 0;
}

.app-doc p,
.app-doc ul {
    margin: 0.75rem 0;
}

.app-doc ul {
    padding-left: 1.25rem;
}

.app-doc li {
    margin: 0.35rem 0;
}

.app-doc code {
    padding: 0.1rem 0.35rem;
    font-family: var(--font-mono);
    font-size: 0.85em;
    color: var(--text-strong);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}

.app-doc-code {
    margin: 0.75rem 0;
    padding: 0.75rem 0.9rem;
    overflow-x: auto;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.app-doc-code code {
    padding: 0;
    font-size: 0.85rem;
    background: none;
    border: 0;
    white-space: pre;
}

.app-doc .app-table-wrap {
    margin: 1rem 0;
}

/* ------------------------------------------------------------- hidden -- */

/*
 * `hidden` znamená schované, i když má prvek třídu s `display`.
 *
 * Bez tohohle pravidla vyhraje kterákoli naše třída nad `[hidden]` z výchozího
 * stylu prohlížeče — autorské styly ho přebíjejí celé —, takže `.app-field`
 * s `display: flex` zůstane vidět, i když na něm `hidden` je. Přesně to potkalo
 * pole formuláře roztřídění, která se schovávají podle zvolené akce.
 *
 * Záměrně **bez** `!important` a až na konci souboru: vyhrává nad jednoduchými
 * selektory pořadím, ale `.app-vysvetlivka:hover .app-vysvetlivka-text[hidden]`
 * je specifičtější a bublinu na hover pořád ukáže.
 */
[hidden] {
    display: none;
}

/* -------------------------------------------------- nastavení (task 51) -- */

/* Rozcestník sekcí nad dlouhým formulářem — na telefonu je to pět obrazovek. */
.app-kotvy {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 1.25rem;
}

.app-kotva {
    display: inline-flex;
    align-items: center;
    padding: 0.3rem 0.75rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pill);
    color: var(--text-muted);
    font-size: 0.8125rem;
}

.app-kotva:hover {
    color: var(--text);
    border-color: var(--accent);
    text-decoration: none;
}

.app-nastaveni-popis {
    margin-bottom: 1rem;
}

.app-nastaveni-pole {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

.app-nastaveni-jednotka {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-size: 0.8125rem;
    white-space: nowrap;
}

.app-nastaveni-zdroj {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8125rem;
}

.app-akce-dole {
    margin-top: 1.25rem;
}

/* Tlačítko, které vypadá jako odkaz — vedlejší akce u pole, ne druhé „Uložit". */
.app-btn-odkaz {
    padding: 0.2rem 0;
    background: none;
    border: 0;
    color: var(--link);
    font-size: 0.8125rem;
    cursor: pointer;
}

.app-btn-odkaz:hover {
    color: var(--link-hover);
    text-decoration: underline;
}

/* ---------------------------------------------- účtenky (task 71) -- */

/* Přepínač pohledu nad seznamem: k vyřízení · archiv · vše. */
.app-pohledy {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 1rem;
}

.app-pohledy .app-kotva {
    min-height: 2.75rem;
}

.app-pohledy .app-kotva.is-active {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

.app-tecka-info {
    background: var(--info);
}

/*
 * Focení (DESIGN.md, „Náhled fotky"): velké tlačítko, po vyfocení fotka ve
 * velkém a pod ní dvě akce. Souborové pole je schované za svým popiskem —
 * ale ne `display: none`, to by ho vyřadilo z klávesnice i z validace.
 */
.app-foceni {
    display: grid;
    gap: 1rem;
    justify-items: center;
}

.app-foceni-krok {
    display: grid;
    gap: 0.6rem;
    justify-items: center;
    width: 100%;
}

/* Krok, který právě neplatí, je schovaný — `display: grid` výš by `hidden` přebil. */
.app-foceni-krok[hidden] {
    display: none;
}

/* Dvě třídy schválně: pravidlo o 44 px cíli pro prst níž by výšku jinak přebilo. */
.app-foceni .app-foceni-tlacitko {
    justify-content: center;
    gap: 0.6rem;
    width: 100%;
    max-width: 24rem;
    min-height: 5.5rem;
    font-size: 1.25rem;
    cursor: pointer;
}

/* „Nahrát" (task 77) je druhá cesta k téže fotce: stejně široké, ale nižší
   a vedlejší — hlavní zůstává fotoaparát. */
.app-foceni .app-foceni-nahrat {
    min-height: 3.25rem;
    font-size: 1.0625rem;
}

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

.app-foceni-vstup:focus-visible + .app-field-hint {
    color: var(--text);
}

.app-foceni-nahled {
    width: 100%;
    text-align: center;
}

.app-foceni-obrazek {
    max-width: 100%;
    max-height: 70vh;
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.app-foceni .app-form-actions {
    justify-content: center;
    width: 100%;
}

/*
 * Ořez fotky (task 74, DESIGN.md „Ořez fotky").
 *
 * `app-orez` se smrskne přesně na náhled a rámeček se v něm polohuje procenty
 * (nastavuje je `orez.js`). Co je mimo rámeček, ztmaví stín „okna", které
 * rámeček kopíruje — jeden prvek místo čtyř masek. Okno má vlastní vrstvu
 * s `overflow: hidden`, aby se stín ořízl na fotku; rámeček v ní schválně
 * není, jinak by se s ním ořízly i úchyty, když stojí na kraji fotky.
 */
.app-orez {
    position: relative;
    display: inline-block;
    max-width: 100%;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    line-height: 0;
    vertical-align: top;
}

/* `display: inline-block` výš by `hidden` přebil. */
.app-orez[hidden] {
    display: none;
}

.app-orez .app-foceni-obrazek {
    display: block;
    border: 0;
    border-radius: inherit;
}

.app-orez .app-foceni-obrazek[hidden] {
    display: none;
}

/* Úchyty na spodním kraji fotky sahají pod ni; nápověda jim uhne. */
.app-foceni-nahled .app-field-hint {
    margin: 0.9rem 0 0;
}

.app-orez-stin {
    position: absolute;
    inset: 0;
    overflow: hidden;
    border-radius: inherit;
    pointer-events: none;
}

.app-orez-okno {
    position: absolute;
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.6);
}

.app-orez-ram {
    position: absolute;
    box-sizing: border-box;
    border: 2px solid var(--accent);
    cursor: move;
    /* Tah prstem hýbe rámečkem, ne stránkou. */
    touch-action: none;
    user-select: none;
}

.app-orez-ram:focus-visible {
    outline: 2px solid var(--accent-contrast);
    outline-offset: -4px;
}

/* Úchyt je cíl 44 px pro prst se středem na rohu nebo hraně; vidět je z něj jen tečka. */
.app-orez .app-orez-uchyt {
    position: absolute;
    display: block;
    width: 44px;
    height: 44px;
    min-height: 0;
    margin: -22px 0 0 -22px;
    padding: 0;
    border: 0;
    background: none;
    touch-action: none;
}

.app-orez-uchyt::after {
    position: absolute;
    inset: 15px;
    border: 2px solid var(--accent-contrast);
    border-radius: 50%;
    background: var(--accent);
    content: "";
}

.app-orez-uchyt:focus-visible {
    outline: none;
}

.app-orez-uchyt:focus-visible::after {
    inset: 11px;
}

/* Světové strany: s = horní hrana, j = dolní, z = levá, v = pravá. */
.app-orez-uchyt-sz,
.app-orez-uchyt-s,
.app-orez-uchyt-sv {
    top: 0;
}

.app-orez-uchyt-z,
.app-orez-uchyt-v {
    top: 50%;
}

.app-orez-uchyt-jz,
.app-orez-uchyt-j,
.app-orez-uchyt-jv {
    top: 100%;
}

.app-orez-uchyt-sz,
.app-orez-uchyt-z,
.app-orez-uchyt-jz {
    left: 0;
}

.app-orez-uchyt-s,
.app-orez-uchyt-j {
    left: 50%;
}

.app-orez-uchyt-sv,
.app-orez-uchyt-v,
.app-orez-uchyt-jv {
    left: 100%;
}

.app-orez-uchyt-s,
.app-orez-uchyt-j {
    cursor: ns-resize;
}

.app-orez-uchyt-z,
.app-orez-uchyt-v {
    cursor: ew-resize;
}

.app-orez-uchyt-sz,
.app-orez-uchyt-jv {
    cursor: nwse-resize;
}

.app-orez-uchyt-sv,
.app-orez-uchyt-jz {
    cursor: nesw-resize;
}

/*
 * Indikátor běžící akce roztažený přes celou obrazovku. Tentýž `data-prubeh`
 * jako u AI návrhu (běh 28) — jen místo řádku vedle tlačítka zakryje stránku,
 * protože na telefonu je tlačítko po stisku pod prstem a řádek by nebyl vidět.
 */
.app-prubeh-obrazovka:not([hidden]) {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.25rem;
    padding: 2rem;
    background: rgba(13, 17, 23, 0.92);
    color: var(--text);
    font-size: 1.05rem;
    text-align: center;
}

.app-prubeh-obrazovka:not([hidden])::before {
    content: "";
    width: 3rem;
    height: 3rem;
    border: 4px solid var(--border-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: app-toceni 0.9s linear infinite;
}

@keyframes app-toceni {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .app-prubeh-obrazovka:not([hidden])::before {
        animation-duration: 4s;
    }
}

/* Fotka vedle formuláře (nebo detailu); na úzké obrazovce nad ním. */
.app-uctenka-rozlozeni {
    display: grid;
    grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
    gap: 1.25rem;
    align-items: start;
}

.app-note + .app-uctenka-rozlozeni {
    margin-top: 1rem;
}

.app-uctenka-fotka img {
    display: block;
    max-width: 100%;
    max-height: 80vh;
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

/*
 * Pole, které model z fotky nepřečetl (DESIGN.md, „Nepřečtené pole"): prázdné,
 * s oranžovým rámečkem a štítkem vedle popisku. Barva je stavový token
 * varování — není to chyba, je to „doplň".
 */
.app-field-neprecteno .app-input,
.app-field-neprecteno .app-select,
.app-field-neprecteno .app-textarea {
    border-color: var(--warn);
    box-shadow: 0 0 0 2px var(--warn-soft);
}

.app-neprecteno {
    gap: 0.25rem;
    font-size: 0.7rem;
}

.app-form .app-textarea {
    min-height: 4rem;
}

/* Miniatura fotky v řádku seznamu a na kartě. */
.app-uctenka-radek {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.app-uctenka-miniatura {
    flex: none;
    width: 2.5rem;
    height: 2.5rem;
    object-fit: cover;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}

.app-karta-s-miniaturou {
    grid-template-columns: 3rem minmax(0, 1fr);
    column-gap: 0.7rem;
}

.app-karta-s-miniaturou > .app-uctenka-miniatura {
    grid-row: 1 / span 4;
    align-self: center;
    width: 3rem;
    height: 3.75rem;
}

.app-karta-s-miniaturou > :not(.app-uctenka-miniatura) {
    grid-column: 2;
}

/* Všechny fotky účtenky na detailu; aktivní má akcentní rám. */
.app-uctenka-fotky {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.app-uctenka-fotky-polozka {
    display: grid;
    gap: 0.4rem;
    width: 9rem;
}

.app-uctenka-fotky-polozka img {
    display: block;
    width: 9rem;
    height: 11rem;
    object-fit: cover;
    border: 2px solid var(--border);
    border-radius: var(--radius);
}

.app-uctenka-fotky-polozka.je-aktivni img {
    border-color: var(--accent);
}

.app-uctenka-fotky-popis {
    display: grid;
    gap: 0.2rem;
    justify-items: start;
    font-size: 0.8125rem;
}

.app-note > p {
    margin: 0 0 0.6rem;
}

/* „Nová" hned po odeslání účtenky (task 77): další krok, takže větší než
   akce vedle ní. */
.app-btn-nova-uctenka {
    gap: 0.5rem;
    min-height: 2.75rem;
    padding-inline: 1.5rem;
    font-size: 1.0625rem;
}

@media (max-width: 48rem) {
    .app-uctenka-rozlozeni {
        grid-template-columns: minmax(0, 1fr);
    }

    /* Na telefonu přes celou šířku a první — palec na ni dosáhne z obou stran. */
    .app-actions .app-btn-nova-uctenka {
        flex: 1 1 100%;
        justify-content: center;
        min-height: 3.5rem;
        font-size: 1.1875rem;
    }

    .app-uctenka-fotka img {
        max-height: 45vh;
        margin: 0 auto;
    }

    .app-foceni .app-form-actions .app-btn {
        flex: 1 1 100%;
        justify-content: center;
    }

    /* „Celá fotka" a „Vyfotit znovu" vedle sebe: lišta akcí drží u spodku
       displeje a každý její řádek ubírá z fotky, nad kterou se ořez upravuje. */
    .app-foceni .app-form-actions button.app-btn-secondary {
        flex: 1 1 40%;
    }
}

/* ------------------------------------------------ mobil (task 51) -- */

/*
 * Ovládání, které existuje jen na úzké nebo dotykové obrazovce. Na široké je
 * schované úplně — markup je všude stejný a o šířce rozhoduje jen tenhle
 * soubor (DESIGN.md, „Mobil").
 */
.app-menu-check {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.app-menu-prepinac,
.app-filtry-prepinac,
.app-combo-zavrit,
.app-vysvetlivka-zavrit {
    display: none;
}

/*
 * Úzká obrazovka: telefon na výšku a menší tablet. 48 rem je šířka, pod kterou
 * se vodorovné menu i filtry ve dvou sloupcích už nevejdou bez zalomení do
 * půlky obrazovky.
 */
@media (max-width: 48rem) {
    /* Menu: značka a tlačítko „Menu · Aktivní položka"; zbytek po rozbalení
       pod sebou, s cíli pro prst. Stav drží zaškrtávátko, ne skript. */
    .app-navbar {
        gap: 0.5rem;
        padding: 0.5rem 0.75rem;
    }

    .app-brand {
        order: 1;
    }

    .app-menu-prepinac {
        order: 2;
        display: inline-flex;
        align-items: center;
        gap: 0.55rem;
        min-height: 2.75rem;
        margin-left: auto;
        padding: 0 0.9rem;
        border: 1px solid var(--border-strong);
        border-radius: var(--radius);
        color: var(--text);
        font-weight: 600;
        cursor: pointer;
        user-select: none;
    }

    .app-menu-prepinac-aktivni {
        max-width: 40vw;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        color: var(--text-muted);
        font-weight: 500;
    }

    /* Tři čárky z rámečků — žádný obrázek, žádný font s ikonami. */
    .app-menu-ikona {
        width: 1rem;
        height: 0.7rem;
        border-top: 2px solid currentColor;
        border-bottom: 2px solid currentColor;
        background: linear-gradient(currentColor, currentColor) center / 100% 2px no-repeat;
    }

    .app-menu-check:focus-visible + .app-menu-prepinac {
        outline: 2px solid var(--accent);
        outline-offset: 2px;
    }

    .app-menu-check:checked + .app-menu-prepinac {
        border-color: var(--accent);
        background: var(--accent-soft);
    }

    .app-menu {
        order: 3;
        flex-basis: 100%;
        flex-direction: column;
        align-items: stretch;
        gap: 0.2rem;
        padding-top: 0.25rem;
    }

    .app-menu-link {
        display: flex;
        min-height: 2.75rem;
        padding: 0.55rem 0.9rem;
    }

    .app-navbar-spacer {
        display: none;
    }

    .app-user {
        order: 4;
        flex: 1 1 auto;
        min-width: 0;
        overflow-wrap: anywhere;
    }

    .app-odhlasit {
        order: 5;
    }

    .app-menu-check:not(:checked) ~ .app-menu,
    .app-menu-check:not(:checked) ~ .app-user,
    .app-menu-check:not(:checked) ~ .app-odhlasit {
        display: none;
    }

    /* Obsah blíž k okraji — na 390 px je každý pixel okraje pixel tabulky. */
    .app-main {
        margin-top: 1rem;
        padding: 0 0.75rem;
    }

    .app-footer {
        padding: 1rem 0.75rem 0;
    }

    .app-title {
        font-size: 1.5rem;
    }

    .app-page-header {
        gap: 0.75rem;
        margin-bottom: 1rem;
    }

    .app-subtitle {
        font-size: 0.875rem;
    }

    .app-panel {
        padding: 0.9rem;
    }

    /* Filtry sbalené do jednoho panelu: tlačítko „Filtry (N aktivní)",
       formulář až po stisku (assets/app.js). */
    .app-filtry-prepinac {
        display: inline-flex;
        justify-content: space-between;
        width: 100%;
    }

    .app-filters.je-sbalitelne:not(.je-rozbaleno) {
        display: none;
    }

    .app-filtry-prepinac + .app-filters {
        margin-top: 0.9rem;
    }

    .app-filters,
    .app-form {
        grid-template-columns: minmax(0, 1fr);
    }

    /* Formuláře: pole pod sebou, ovládací prvky na prst (44 px), písmo 16 px,
       aby iOS při fokusu nezvětšoval stránku. */
    .app-input,
    .app-select,
    .app-textarea {
        min-height: 2.75rem;
        font-size: 1rem;
    }

    .app-btn {
        min-height: 2.75rem;
        justify-content: center;
    }

    .app-btn-sm {
        min-height: 2.25rem;
    }

    /* Hlavní tlačítko formuláře drží dole u palce, dokud je formulář vidět —
       ne až za posledním polem, pět obrazovek rolování daleko. Totéž dělá
       `.app-akce-dole` pro hlavní akci mimo formulář („Provést import"). */
    .app-form-actions,
    .app-akce-dole {
        position: sticky;
        bottom: 0;
        z-index: 15;
        background: var(--surface);
        box-shadow: 0 -1px 0 var(--border);
    }

    .app-form-actions {
        padding: 0.65rem 0;
    }

    .app-form-actions .app-btn-primary {
        flex: 1 1 auto;
    }

    /* Seznam, který má karty, je na úzké obrazovce ukáže místo tabulky
       (task 63). */
    .app-jen-siroke {
        display: none;
    }

    .app-jen-uzke {
        display: block;
    }

    .app-id-u-nazvu.app-jen-uzke {
        display: inline;
    }

    /*
     * Tabulky bez karet: vodorovné rolování uvnitř rámu a první sloupec pevně
     * vlevo.
     *
     * Platí pro tabulky čísel (hodiny, Kč, čerpání), která se čtou napříč
     * řádky — karta by každý řádek rozepsala na půl obrazovky a porovnat dva
     * by znamenalo rolovat. Pevný první sloupec drží, o čem řádek je, zatímco
     * se roluje k číslům. Seznam záznamů (služby) má od tasku 63 karty.
     */
    .app-table-wrap {
        overscroll-behavior-x: contain;
    }

    .app-table th,
    .app-table td {
        padding: 0.55rem 0.65rem;
    }

    /* `break-word`, ne `anywhere`: to druhé smí lámat uprostřed slova už při
       výpočtu šířky sloupce, a tabulka ho pak smáčkne na „Migrac|e Beta". */
    /* Sloupec jen pro širokou obrazovku (ID zakázky) tu není vidět — pevný
       je pak ten za ním. */
    .app-table tr > :first-child,
    .app-table tr > .app-jen-siroke:first-child + * {
        position: sticky;
        left: 0;
        z-index: 1;
        min-width: 7.5rem;
        max-width: 45vw;
        background: var(--surface);
        box-shadow: 1px 0 0 var(--border);
        overflow-wrap: break-word;
    }

    .app-table thead tr > :first-child,
    .app-table thead tr > .app-jen-siroke:first-child + * {
        z-index: 2;
        background: var(--surface-sunken);
        white-space: normal;
    }

    .app-table tbody tr:hover > :first-child,
    .app-table tbody tr:hover > .app-jen-siroke:first-child + * {
        background: var(--surface-hover);
    }

    .app-meta {
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    /* Nabídka komboboxu se vejde na displej i s klávesnicí. */
    .app-combo-list {
        max-height: min(16rem, 45vh);
    }

    .app-combo-item {
        justify-content: center;
        min-height: 2.75rem;
    }

    /*
     * Vysvětlivka „?" jako panel u spodního okraje: bublina u ikony se na úzké
     * obrazovce nemá kam otevřít a prst by ji stejně zakryl. Zavírá se
     * křížkem, klepnutím jinam nebo Escapem.
     */
    .app-vysvetlivka-text,
    .app-vysvetlivka.je-vlevo .app-vysvetlivka-text {
        position: fixed;
        z-index: 60;
        top: auto;
        right: 0.75rem;
        bottom: 0.75rem;
        left: 0.75rem;
        width: auto;
        max-width: none;
        max-height: 60vh;
        overflow-y: auto;
        padding: 1rem 3rem 1rem 1rem;
        font-size: 0.9375rem;
        box-shadow: 0 -0.25rem 1.5rem rgba(0, 0, 0, 0.6);
    }

    .app-vysvetlivka-zavrit {
        position: absolute;
        top: 0.25rem;
        right: 0.25rem;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 2.75rem;
        height: 2.75rem;
        padding: 0;
        background: none;
        border: 0;
        color: var(--text-muted);
        font-size: 1.5rem;
        line-height: 1;
        cursor: pointer;
    }
}

/*
 * Dotyková obrazovka, ať je jakkoli široká, a každá úzká: prst nemá hover
 * a potřebuje větší cíl než myš. Úzká šířka je tu vedle `pointer: coarse`
 * schválně — telefon je úzký vždycky, kdežto hlášení ukazovátka prohlížeče
 * (a emulace v GUI auditu) spolehlivé není.
 */
@media (max-width: 48rem), (hover: none), (pointer: coarse) {
    /* Najetí myší, které na dotyku „ulpí" po klepnutí, nesmí bublinu držet
       otevřenou, když ji křížek zavřel. */
    .app-vysvetlivka:hover .app-vysvetlivka-text[hidden] {
        display: none;
    }

    /* Ikona zůstává malá, cíl ne: neviditelný okraj kolem ní dělá 44 px. */
    .app-vysvetlivka-ikona {
        position: relative;
    }

    .app-vysvetlivka-ikona::after {
        content: "";
        position: absolute;
        inset: -0.85rem;
    }

    /* S otevřenou nabídkou místo šipky křížek — zavření prstem. */
    .app-combo.is-otevreno .app-combo-zavrit {
        position: absolute;
        top: 0;
        right: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 2.75rem;
        height: 100%;
        min-height: 2.75rem;
        padding: 0;
        background: none;
        border: 0;
        color: var(--text-muted);
        font-size: 1.35rem;
        cursor: pointer;
    }

    .app-combo.is-otevreno .app-combo-sipka {
        display: none;
    }
}

/* ------------------------------------------------------------------
 * Záložky detailu zakázky (task 82, F)
 *
 * Lišta odkazů nad panely. Bez JavaScriptu je každá záložka odkaz
 * `?zalozka=…`, s ním přepíná assets/zalozky.js. Na úzké obrazovce se
 * nezalamuje, ale roluje vodorovně — šest záložek pod sebou by sežralo
 * celou první obrazovku telefonu.
 * ------------------------------------------------------------------ */
.app-zalozky {
    margin: 0.5rem 0 1.25rem;
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
}

.app-zalozky-seznam {
    display: flex;
    gap: 0.25rem;
    margin: 0;
    padding: 0;
    list-style: none;
    white-space: nowrap;
}

.app-zalozka {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 2.75rem;
    padding: 0.5rem 0.9rem;
    border-bottom: 2px solid transparent;
    color: var(--text-muted);
    font-weight: 500;
}

.app-zalozka:hover {
    color: var(--text);
    text-decoration: none;
}

.app-zalozka.is-active {
    border-bottom-color: var(--accent);
    color: var(--text-strong);
}

.app-zalozka-pocet {
    display: inline-block;
    min-width: 1.4rem;
    padding: 0 0.4rem;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    color: var(--text);
    font-size: 0.75rem;
    line-height: 1.4rem;
    text-align: center;
}

.app-zalozka-panel[hidden] {
    display: none;
}

/* Kotva na začátku záložky, kam míří `#prehled` a `#nastaveni`. */
.app-kotva-cil {
    display: block;
    scroll-margin-top: 5rem;
}

/* ------------------------------------------------------------------
 * Detail úkolu, vlákno komentářů, přílohy (task 82, B a E)
 * ------------------------------------------------------------------ */
.app-ukol-popis,
.app-komentar-text {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* ------------------------------------------------------------------
 * Detail položky: hlavička s klíči + dva sloupce + vlákno (task 83,
 * DESIGN.md „Detail položky"). Prvním je detail úkolu.
 * ------------------------------------------------------------------ */

/* Levá část hlavičky stránky: drobečky, nadpis a řádek klíčů. Akce zůstávají vpravo. */
.app-ukol-hlavicka {
    flex: 1 1 32rem;
    min-width: 0;
}

/* Klíče položky vedle sebe — popisek nad hodnotou, zalomí se, až dojde šířka. */
.app-klice {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem 2rem;
    margin-top: 0.9rem;
}

.app-klic {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

.app-klic-hodnota {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    min-height: 2rem;
    color: var(--text-strong);
    overflow-wrap: anywhere;
}

/* Formulář o jednom ovládacím prvku: stav v hlavičce, „Přiložit" v hlavičce panelu. */
.app-klic-formular {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.app-klic .app-select {
    width: auto;
    min-height: 2rem;
    padding: 0.3rem 0.5rem;
}

/* Dva sloupce od 64 rem (1024 px); užší obrazovka je má pod sebou. */
.app-sloupce {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.25rem;
}

@media (min-width: 64rem) {
    .app-sloupce {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
    }
}

.app-sloupce > .app-panel + .app-panel {
    margin-top: 0;
}

.app-sloupce + .app-panel {
    margin-top: 1.25rem;
}

/*
 * Nadpis karty s akcí vpravo („Zadání" … „Přiložit"). Je to nadpis na začátku
 * karty (DESIGN.md, „Rozestupy sekcí"): mezeru nad ním dělá padding karty,
 * pod ním drží tutéž `--mezera-pod-nadpisem` jako samotný nadpis.
 */
.app-panel-hlavicka {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    min-height: 2rem;
    margin-bottom: var(--mezera-pod-nadpisem);
}

.app-panel-hlavicka > .app-section-title {
    margin: 0;
}

/* Pole souborů schované v tlačítku: klik na popisek otevře výběr, fokus klávesnicí je vidět na tlačítku. */
.app-soubor-tlacitko {
    position: relative;
    cursor: pointer;
}

.app-soubor-tlacitko:focus-within {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.app-ukol-cisla {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 1.25rem;
}

.app-ukol-cisla strong {
    color: var(--text-strong);
}

.app-ukol-prubeh {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem 0.75rem;
    margin: 1rem 0 0.4rem;
}

.app-ukol-prubeh .app-field {
    flex: 1 1 7rem;
}

/* Přílohy zadání: řádek na soubor — ikona typu, název, velikost. */
.app-prilohy-seznam {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0.75rem 0 0;
    padding: 0;
    list-style: none;
}

.app-ukol-popis + .app-kotva-cil > .app-prilohy-seznam {
    margin-top: 1rem;
}

.app-kotva-cil > .app-prilohy-seznam:first-child {
    margin-top: 0;
}

.app-priloha-radek {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
}

.app-priloha-ikona {
    display: inline-flex;
    flex: 0 0 2.25rem;
    align-items: center;
    justify-content: center;
    height: 2.25rem;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    color: var(--text-muted);
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.04em;
}

.app-priloha-miniatura img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.app-priloha-nazev {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-komentar-formular .app-textarea {
    min-height: 4.5rem;
}

.app-komentar-akce {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.75rem;
    margin-top: 0.6rem;
}

.app-vlakno {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin: 0 0 1.25rem;
    padding: 0;
    list-style: none;
}

.app-komentar {
    padding: 0.75rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-sunken);
}

.app-komentar-hlavicka {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.4rem;
}

.app-komentar-smazat {
    margin-left: auto;
}

.app-prilohy {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin: 0.75rem 0;
    padding: 0;
    list-style: none;
}

.app-priloha {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    max-width: 14rem;
}

.app-priloha-popis {
    display: flex;
    flex-direction: column;
    overflow-wrap: anywhere;
}

.app-priloha-nahled img {
    display: block;
    max-width: 14rem;
    max-height: 10rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    object-fit: contain;
    background: var(--surface);
}

.app-schranka-nahled {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 0.5rem;
}

.app-schranka-nahled[hidden] {
    display: none;
}

.app-schranka-polozka {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    max-width: 10rem;
    overflow-wrap: anywhere;
}

.app-schranka-polozka img {
    max-width: 8rem;
    max-height: 8rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    object-fit: contain;
}

@media (max-width: 48rem) {
    .app-priloha,
    .app-priloha-nahled img {
        max-width: 100%;
    }

    .app-komentar {
        padding: 0.6rem 0.75rem;
    }
}
