/* Minimal app stylesheet — layout is composed from native FluentUI primitives
   (FluentStack/FluentGrid/FluentDataGrid), no custom component styling.
   Documented exceptions: blazor-error-ui (framework), login box (centered auth screen),
   logout button (native submit inside the header form). */
/* ── Typography ──────────────────────────────────────────────────────────────────
   The app never declared a base font, so everything outside a FluentUI component
   inherited the browser default — Times New Roman — and form controls fell back to
   Arial 13px, which is what made the UI read like a document instead of a web app.
   One stack for the whole app, controls inherit it, and FluentUI is pointed at the
   same stack through its own --body-font token.
   ("Segoe UI Variable" is not a real family name; the installed ones are the
   Display / Text / Small optical sizes.) */
:root {
    --app-font: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, Inter, Roboto,
                "Helvetica Neue", "Noto Sans", Arial, sans-serif;
    --app-font-display: "Segoe UI Variable Display", "Segoe UI Variable Text", "Segoe UI",
                        system-ui, -apple-system, Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
    --body-font: var(--app-font);
}
html,
body {
    /* Výchozí osmipixelový okraj body dělal na stránce kola svislý posuvník navíc:
       obsah vysoký přesně na okno plus 16 px okrajů = stránka přeteče. */
    margin: 0;
    font-family: var(--app-font);
    font-size: 15px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, h4, h5, h6 {
    font-family: var(--app-font-display);
    line-height: 1.25;
    letter-spacing: -0.01em;
}
/* Form controls do not inherit typography by default — that is the Arial 13px. */
input,
button,
select,
textarea {
    font: inherit;
}
#blazor-error-ui {
    background: #a4262c;
    color: #ffffff;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}
    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }
/* Theme: FluentDesignTheme (hidden below) drives the design tokens; surfaces read the
   tokens with light fallbacks so the transient pre-JS render matches the light scheme. */
.theme-engine {
    display: none;
}
body {
    background-color: var(--neutral-layer-1, #fff);
    color: var(--neutral-foreground-rest, #1a1a1a);
}
/* The wheel fills the free space of its (flexible) column: always a square, capped by the
   viewport height so the wheel + Spin button stay on screen without scrolling. */
/* Wheel page layout: a wheel column that grows and a fixed 400px management panel.
   The Fluent stack sizes to content, so on a narrow viewport it ends up narrower than its
   own children (320px min wheel + 400px panel) and an ancestor clips the overflow — the
   right-hand panel then hangs off screen. Below the breakpoint both columns go full width. */
.wheel-layout {
    width: 100%;
    box-sizing: border-box;
}
@media (max-width: 900px) {
    .wheel-layout > * {
        flex: 1 1 100% !important;
        width: 100% !important;
        min-width: 0 !important;
    }
    .wheel-layout .wheel-canvas {
        max-width: min(100%, calc(100dvh - 240px));
    }
}
.wheel-canvas {
    width: 100%;
    max-width: calc(100dvh - 240px);
    margin: 0 auto;
}
/* FluentHeader paints itself with --accent-fill-rest by design (FluentUI scoped CSS), so
   header text must use the matching on-accent foreground token. Specificity war: FluentUI's
   .fluent-typography[b-*] rule ties .header .fluent-typography (0,2,0) and wins on link
   order — the .header-gutters ancestor (0,3,0) beats it regardless of order.
   FluentLabel renders as div.fluent-typography (not a fluent-label element). */
.header .header-gutters,
.header .header-gutters .fluent-typography,
.header .header-gutters .logout-btn,
.header .header-gutters fluent-badge {
    color: var(--foreground-on-accent-rest);
}
.header .header-gutters .fluent-typography {
    white-space: nowrap;
}
/* Login / register / change-password screens.
   These render in EmptyLayout, which has NO FluentDesignTheme engine — the Fluent design
   tokens are undefined here, so every var() below actually resolves to its fallback. The
   fallbacks are therefore the real colours: light by default, flipped in the
   prefers-color-scheme block at the end. (They used to be dark-theme values, which put
   near-black text on near-black inputs on a white page.) */
.login-page {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--neutral-layer-1, #ffffff);
    color: var(--neutral-foreground-rest, #1a1a1a);
}
.login-box {
    width: 360px;
    padding: 32px;
    background: var(--neutral-layer-2, #fbfbfb);
    border: 1px solid var(--neutral-stroke-divider-rest, #dddddd);
    border-radius: 8px;
}
    .login-box h2 {
        margin: 0 0 4px 0;
    }
    .login-box .login-sub {
        margin: 0 0 20px 0;
        color: var(--neutral-foreground-hint, #666666);
    }
    .login-box a {
        color: var(--accent-foreground-rest, #0f6cbd);
    }
    .login-box hr {
        width: 100%;
        margin: 20px 0 4px 0;
        border: none;
        border-top: 1px solid var(--neutral-stroke-divider-rest, #dddddd);
    }
    .login-box form {
        display: flex;
        flex-direction: column;
        gap: 6px;
    }
    .login-box label {
        font-size: 13px;
        margin-top: 8px;
    }
    .login-box input {
        padding: 8px 10px;
        border-radius: 4px;
        border: 1px solid var(--neutral-stroke-rest, #b3b3b3);
        background: var(--neutral-fill-input-rest, #ffffff);
        color: inherit;
    }
        .login-box input:focus-visible {
            outline: 2px solid var(--accent-fill-rest, #0f6cbd);
            outline-offset: 1px;
        }
    .login-box button {
        margin-top: 16px;
        padding: 10px;
        border: none;
        border-radius: 4px;
        background: var(--accent-fill-rest, #0f6cbd);
        color: var(--foreground-on-accent-rest, #ffffff);
        font-weight: 600;
        cursor: pointer;
    }
        .login-box button:hover {
            filter: brightness(1.08);
        }
.login-error {
    padding: 10px;
    margin-bottom: 12px;
    border-radius: 4px;
    background: #fdf3f4;
    border: 1px solid #a4262c;
    color: #a4262c;
}
@media (prefers-color-scheme: dark) {
    .login-page {
        background: #1b1b1b;
        color: #f3f3f3;
    }
    .login-box {
        background: #242424;
        border-color: #3d3d3d;
    }
        .login-box .login-sub {
            color: #a8a8a8;
        }
        .login-box a {
            color: #6cb8f6;
        }
        .login-box hr {
            border-top-color: #3d3d3d;
        }
        .login-box input {
            background: #111111;
            border-color: #555555;
        }
    .login-error {
        background: rgba(196, 43, 28, .15);
        color: #fde7e9;
    }
}
/* Sign-out button in the header — native button inside a form POST. */
.logout-btn {
    background: none;
    border: 1px solid var(--neutral-stroke-rest, #555);
    border-radius: 4px;
    color: inherit;
    padding: 4px 12px;
    cursor: pointer;
    font-size: 13px;
}
/* App shell. The side nav is a drawer at every width, not just on phones: it slides in from
   the left on the header's ☰ button, over the content, and closes on the backdrop or on any
   click inside it (so following a link does not leave it hanging open). Keeping it out of the
   flow gives the wheel the whole width — the fixed 220px column was eating it for no reason.
   The shell no longer scrolls internally; the page scrolls, so there is one scrollbar. */
.nav-toggle {
    display: inline-flex;
    align-items: center;
    background: none;
    border: none;
    color: inherit;
    font-size: 20px;
    line-height: 1;
    padding: 4px 8px;
    border-radius: 4px;
    cursor: pointer;
}
    .nav-toggle:hover {
        background: rgba(255, 255, 255, .15);
    }
.app-shell {
    position: relative;
}
    .app-shell .nav-holder {
        position: fixed;
        top: 48px;
        left: 0;
        bottom: 0;
        z-index: 30;
        transform: translateX(-100%);
        transition: transform .18s ease;
        background: var(--neutral-layer-1, #ffffff);
        box-shadow: 2px 0 12px rgba(0, 0, 0, .25);
    }
    .app-shell.nav-open .nav-holder {
        transform: translateX(0);
    }
    .app-shell .app-nav {
        height: 100% !important;
        border-right: none !important;
    }
.nav-backdrop {
    display: none;
}
.app-shell.nav-open .nav-backdrop {
    display: block;
    position: fixed;
    inset: 48px 0 0 0;
    z-index: 20;
    background: rgba(0, 0, 0, .4);
}
.body-content {
    box-sizing: border-box !important;
    width: 100% !important;
}
@media (max-width: 700px) {
    /* FluentBodyContent ships 24px of padding on a content-box, which overflowed the viewport
       by 48px on a phone. */
    .body-content {
        padding: 12px !important;
    }
    /* Hlavička má pevných 50 px; při zalomení se obsah kreslil přes sebe. Necháme ji
       růst a výběr motivu zúžíme, ať se řádek vejde. */
    .header,
    .header .header-gutters {
        height: auto !important;
        min-height: 50px;
        padding-block: 4px;
    }
    .header .stack-horizontal {
        flex-wrap: wrap;
        row-gap: 4px;
    }
    .header fluent-select {
        width: 150px !important;
    }
}
/* Data grids keep their column widths, so on a phone the right-hand columns (including the
   action buttons) fell outside the viewport and were clipped. Let the grid scroll sideways
   inside its own box instead of overflowing the page. */
.grid-scroll {
    width: 100%;
    max-width: 100%;
    overflow-x: auto;
}
/* Native create form on the home page — anonymous visitors cannot use the interactive one,
   because the wheel's share cookie can only be set on a real HTTP response. */
.inline-create {
    display: flex;
    gap: 8px;
    width: 100%;
    flex-wrap: wrap;
}
    .inline-create input {
        flex: 0 1 220px;
        min-width: 0;
        padding: 6px 10px;
        border-radius: 4px;
        border: 1px solid var(--neutral-stroke-rest, #b3b3b3);
        background: var(--neutral-fill-input-rest, #fff);
        color: inherit;
    }
    .inline-create button {
        padding: 6px 16px;
        border: none;
        border-radius: 4px;
        background: var(--accent-fill-rest, #0f6cbd);
        color: var(--foreground-on-accent-rest, #fff);
        font-weight: 600;
        cursor: pointer;
    }
/* Account deletion form (login-box styling, plus the bits the auth screens do not have). */
.login-box .checkline {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: 12px;
    font-size: 13px;
}
    .login-box .checkline input {
        width: auto;
        margin-top: 2px;
    }
.login-box button.danger {
    background: #a4262c;
}
/* Neutral notice on the auth screens (account deleted, and anything else that is not an error). */
.login-note {
    padding: 10px;
    margin-bottom: 12px;
    border-radius: 4px;
    background: #eef6ec;
    border: 1px solid #4a7c3f;
    color: #2c5222;
}
@media (prefers-color-scheme: dark) {
    .login-note {
        background: rgba(74, 124, 63, .18);
        color: #d7ecd2;
    }
}
/* Links. FluentAnchor keeps its <a> in a shadow root, so the colour has to go through
   ::part(control) as well as the plain-anchor selector.
   - In the header the background is the accent fill, so hypertext blue on blue was barely
     legible; use the on-accent foreground the header text already uses.
   - In page content the browser default (#0000EE) is off-palette in light mode and nearly
     invisible on the dark theme. */
.header a,
.header fluent-anchor::part(control) {
    color: var(--foreground-on-accent-rest, #ffffff);
    text-decoration: underline;
}
.body-content a,
.body-content fluent-anchor::part(control) {
    color: var(--accent-foreground-rest, #0f6cbd);
}
/* ── Transient messages ──────────────────────────────────────────────────────────
   Fixed above the page, so appearing and disappearing never reflows the content
   underneath; they fade in, dismiss on click and clear themselves after a few seconds. */
.toast-area {
    position: fixed;
    top: 60px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: min(560px, calc(100vw - 24px));
    pointer-events: none;
}
.toast {
    pointer-events: auto;
    padding: 12px 16px;
    border-radius: 8px;
    border: 1px solid transparent;
    box-shadow: 0 6px 24px rgba(0, 0, 0, .28);
    cursor: pointer;
    animation: toast-in .18s ease-out;
}
.toast-error {
    background: #fdf3f4;
    border-color: #a4262c;
    color: #7a1c22;
}
.toast-info {
    background: #eef6ec;
    border-color: #4a7c3f;
    color: #2c5222;
}
@keyframes toast-in {
    from { opacity: 0; transform: translateY(-8px); }
    to { opacity: 1; transform: none; }
}
/* ── Winner ──────────────────────────────────────────────────────────────────────
   The result used to be a message bar in the flow — easy to miss and it shifted the
   layout. Now it lands in the middle of the screen and waits to be clicked away. */
.winner-overlay {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(0, 0, 0, .55);
    animation: winner-fade .15s ease-out;
}
.winner-card {
    text-align: center;
    padding: 32px 48px;
    border-radius: 16px;
    max-width: min(90vw, 520px);
    background: var(--neutral-layer-1, #ffffff);
    color: var(--neutral-foreground-rest, #1a1a1a);
    box-shadow: 0 24px 64px rgba(0, 0, 0, .45);
    animation: winner-pop .32s cubic-bezier(.2, 1.3, .4, 1);
}
.winner-emoji {
    font-size: 56px;
    line-height: 1;
    animation: winner-bounce 1s ease-in-out 2;
}
.winner-label {
    font-size: 15px;
    opacity: .7;
    margin-top: 8px;
}
.winner-name {
    font-family: var(--app-font-display);
    font-size: clamp(32px, 7vw, 56px);
    font-weight: 700;
    line-height: 1.15;
    margin: 4px 0 8px;
    word-break: break-word;
}
.winner-action {
    font-size: 13px;
    opacity: .75;
}
.winner-close {
    margin-top: 20px;
    padding: 10px 28px;
    border: none;
    border-radius: 6px;
    background: var(--accent-fill-rest, #0f6cbd);
    color: var(--foreground-on-accent-rest, #ffffff);
    font-weight: 600;
    cursor: pointer;
}
@keyframes winner-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes winner-pop {
    from { opacity: 0; transform: scale(.86); }
    to { opacity: 1; transform: none; }
}
@keyframes winner-bounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-10px); }
}
@media (prefers-color-scheme: dark) {
    .toast-error {
        background: #3a1d20;
        color: #ffd9dc;
    }
    .toast-info {
        background: #1e2f1a;
        color: #d7ecd2;
    }
    .winner-card {
        background: #242424;
        color: #f3f3f3;
    }
}
/* Respect the system setting — the celebration should not be a problem for anyone. */
@media (prefers-reduced-motion: reduce) {
    .toast,
    .winner-overlay,
    .winner-card,
    .winner-emoji {
        animation: none;
    }
}
/* ── Hlavička stránky kola ───────────────────────────────────────────────────────
   The name keeps one line and stays in the layout; the winner-action badge and the
   back link are lifted out of the flow so they cannot squeeze it. They sit over the
   empty corner beside the wheel, which is round, so nothing is covered. max-width
   reserves the space they occupy — the title ellipsises instead of sliding under. */
.wheel-head {
    position: relative;
    width: 100%;
    min-height: 34px;
}
.wheel-title {
    margin: 0;
    max-width: calc(100% - 250px);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.wheel-meta {
    position: absolute;
    top: 2px;
    right: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
}
@media (max-width: 700px) {
    /* Na telefonu není kam odznak a odkaz odsunout, aby nepřekryly název — vrátí se
       do toku pod titulek. Ten drží jeden řádek dál. */
    .wheel-title {
        max-width: 100%;
        font-size: 20px;
    }
    .wheel-meta {
        position: static;
        margin-top: 6px;
    }
    .wheel-head {
        min-height: 0;
    }
}
/* ── Ztráta spojení ──────────────────────────────────────────────────────────────
   Blazor Server sám vykresluje okno "reconnect"; ve výchozí podobě je to bílý
   obdélník bez textu, který v tmavém motivu vypadá jako rozbitá stránka. Třídy
   components-reconnect-* přepíná framework na #components-reconnect-modal:
   show = zkouší se připojit, failed/rejected = nepovedlo se. */
#components-reconnect-modal {
    display: none;
}
    #components-reconnect-modal.components-reconnect-show,
    #components-reconnect-modal.components-reconnect-failed,
    #components-reconnect-modal.components-reconnect-rejected {
        display: flex;
        position: fixed;
        inset: 0;
        z-index: 100;
        align-items: center;
        justify-content: center;
        padding: 16px;
        background: rgba(0, 0, 0, .6);
    }
.reconnect-card {
    text-align: center;
    padding: 28px 40px;
    border-radius: 14px;
    max-width: min(90vw, 420px);
    background: var(--neutral-layer-1, #ffffff);
    color: var(--neutral-foreground-rest, #1a1a1a);
    box-shadow: 0 20px 56px rgba(0, 0, 0, .45);
}
.reconnect-spinner {
    width: 34px;
    height: 34px;
    margin: 0 auto 14px;
    border: 3px solid var(--neutral-stroke-divider-rest, #d0d0d0);
    border-top-color: var(--accent-fill-rest, #0f6cbd);
    border-radius: 50%;
    animation: reconnect-spin .9s linear infinite;
}
.reconnect-title {
    font-family: var(--app-font-display);
    font-size: 18px;
    font-weight: 700;
    margin-bottom: 6px;
}
.reconnect-retrying,
.reconnect-lost {
    display: none;
    font-size: 14px;
    opacity: .85;
}
.components-reconnect-show .reconnect-retrying,
.components-reconnect-failed .reconnect-lost,
.components-reconnect-rejected .reconnect-lost {
    display: block;
}
/* Když se spojení obnovit nepovedlo, točící se kolečko jen mate. */
.components-reconnect-failed .reconnect-spinner,
.components-reconnect-rejected .reconnect-spinner {
    animation: none;
    border-top-color: #a4262c;
}
.reconnect-lost button {
    display: block;
    margin: 14px auto 0;
    padding: 9px 22px;
    border: none;
    border-radius: 6px;
    background: var(--accent-fill-rest, #0f6cbd);
    color: var(--foreground-on-accent-rest, #ffffff);
    font-weight: 600;
    cursor: pointer;
}
@keyframes reconnect-spin {
    to { transform: rotate(360deg); }
}
@media (prefers-color-scheme: dark) {
    .reconnect-card {
        background: #242424;
        color: #f3f3f3;
    }
}
/* Na stránce kola roluje JEN pravý panel. Aby nevznikl druhý posuvník, nesmí rolovat
   stránka — proto je obsah svázaný do jednoho výškového řetězu: obsahová oblast má přesnou
   výšku okna bez hlavičky, stránka kola ji vyplní, řádek se sloupci si vezme zbytek a panel
   roluje uvnitř něj. min-height: 0 je nutné, jinak flex položka odmítne být menší než obsah. */
/* Stránka kola: rolovat má jedině pravý panel.
   Celý výškový řetěz je schválně jen pro širokou obrazovku — když se sloupce složí pod
   sebe, omezovat výšku nedává smysl a dřívější varianta (pravidla globálně a pak je na
   mobilu přebíjet zpátky) panel smrskla na 16 px. Výška se nikde nepočítá z konstanty:
   hlavička měří 50 px, ne 48, a ten dvoupixelový rozdíl stačil na posuvník přes celou
   stránku — proto zbytek vyplní shell flexem. */
.layout {
    min-height: 100dvh;
}
@media (min-width: 901px) {
    /* Rolovat má jedině pravý panel. Výška se počítá z okna, ne procentem přes rodiče:
       FluentStacky mezi tím nemají určenou výšku, takže max-height: 100 % se neprořízne
       a neomezí vůbec nic. Odečtená hodnota kryje hlavičku (50), odsazení obsahové
       oblasti a řádek s názvem kola. */
    .wheel-side {
        max-height: calc(100dvh - 180px);
        overflow-y: auto;
        overscroll-behavior: contain;
        padding-right: 6px;
    }
        /* Karty by se jinak natáhly na plnou výšku panelu (mají height: 100 %) a jeden
           řádek obsahu by zabral celou obrazovku; smršťovat se naopak nesmí, jinak by
           panel nikdy nepřetekl a posuvník by se nerozjel. */
        .wheel-side > * {
            flex: 0 0 auto;
            height: auto;
        }
}
/* Neuložené změny */
.dirty-note {
    font-size: 12px;
    padding: 6px 10px;
    border-radius: 4px;
    border-left: 3px solid var(--accent-fill-rest, #0f6cbd);
    background: rgba(15, 108, 189, .12);
}
.dirty-mark {
    color: var(--accent-fill-rest, #0f6cbd);
    font-weight: 700;
    cursor: help;
}
/* Vyřazená položka: místo popisku „povoleno" u každého řádku stačí, že řádek zešedne. */
.entry-row.entry-off {
    opacity: .45;
}
/* Sdílecí odkaz se ukazuje jen na vyžádání. */
.share-url {
    font-size: 12px;
    word-break: break-all;
    display: block;
    padding: 6px 8px;
    border-radius: 4px;
    background: var(--neutral-fill-secondary-rest, rgba(127, 127, 127, .15));
}
/* Na telefonu si hlavička brala tři řádky z výšky obrazovky. Popisek u výběru motivu a
   odznak se jménem jsou ozdoba — vypadnou a hlavička se srovná do dvou řádků. Interní
   číslo kola (#) na úzké tabulce jen ubírá místo sloupci s akcemi. */
@media (max-width: 700px) {
    .header-theme-label,
    .header-user {
        display: none !important;
    }
    .col-seq {
        display: none !important;
    }
}
/* Výběr motivu: v hlavičce na širokém displeji, ve vysouvacím menu na úzkém. */
.drawer-theme {
    display: none;
    padding: 12px 16px;
    border-top: 1px solid var(--neutral-stroke-divider-rest, #ddd);
}
@media (max-width: 700px) {
    .header-theme {
        display: none;
    }
    .drawer-theme {
        display: block;
    }
}
/* ── Řádek formuláře s popisky ───────────────────────────────────────────────
   FluentTextField vykresluje Label jako sourozence vstupu, ne uvnitř komponenty.
   Ve vodorovném FluentStacku se tedy popisek a pole staly dvěma nezávislými
   položkami flexu a při zalomení se od sebe odtrhly. .form-field drží dvojici
   pohromadě jako jeden blok. */
.form-row {
    /* Nadřazený svislý stack zarovnává na začátek, takže bez tohohle se řádek scvrkne
       na obsah a zalomí se dřív, než je karta plná. */
    width: 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
}
.form-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 0 1 220px;
    /* Pole nesmí růst — jinak spolykají celou kartu a tlačítko spadne na vlastní řádek. */
    min-width: 180px;
}
/* Zaškrtávátko a tlačítko popisek nemají — nemají se ani roztahovat. */
.form-field-tight {
    flex: 0 0 auto;
    min-width: 0;
    padding-bottom: 4px;
}

/* ── Hledání v seznamu položek ───────────────────────────────────────────────
   Vzhled pole si řeší OmniTextField z vlastního návrhového systému; tady se jen
   roztáhne na šířku panelu. Počet nalezených se nevypisuje do pole — je
   v nadpisu ("Položky (1 z 5)") a v poli by se pral s nativním křížkem
   type="search". */
.entry-filter {
    width: 100%;
}

.entry-filter .oc-theme,
.entry-filter .oc-field,
.entry-filter input {
    width: 100%;
}

/* ── Sloupec "Přístup" v přehledu kol ────────────────────────────────────────
   Štítek a popisek odkazu musí zůstat na jednom řádku — buňka mřížky má pevnou
   výšku, takže cokoliv zalomeného se pod ní uřízne. */
.access-cell {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.access-note {
    font-size: 12px;
    opacity: .7;
}
