/* SomVTatrach admin — design system.

   This is a back office, not the public site: denser, more neutral, built for people who use it
   every day and know where things are. It shares the brand's warm neutrals and terracotta accent so
   the two feel related, but nothing here is imported from site.css — the two can still move apart.

   Everything a page needs is a class in this file. Pages should not carry style="…" attributes;
   if a page needs a shape that isn't here, the shape belongs here.

   Layout contract (see MainLayout.razor):
     ≥ 1024px  sidebar + content, side by side
     < 1024px  56px top bar + slide-in drawer. The old build turned the sidebar into a wrapping
               row of links that measured 302px — 37% of a 375×812 phone — and was position:sticky,
               so it never gave the space back. That is what the drawer replaces. */

/* ---------- Fonts (self-hosted, no external CDN — same files the public site ships) ---------- */

@font-face {
    font-family: 'Schibsted Grotesk';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('/fonts/schibsted-grotesk-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Schibsted Grotesk';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('/fonts/schibsted-grotesk-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Newsreader is here for exactly one string: the wordmark, so it is set the same way as on the
   public site. Nothing else in the admin uses a serif. */
@font-face {
    font-family: 'Newsreader';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/fonts/newsreader-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Newsreader';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/fonts/newsreader-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------- Tokens ---------- */

:root {
    /* Surfaces climb away from the page background as they gain importance:
       bg → surface (cards) → raised (menus, drawer). */
    --bg: #F5F3EF;
    --surface: #FFFFFF;
    --surface-2: #FAF8F5;
    --raised: #FFFFFF;
    --sidebar: #FBFAF7;

    --text: #1B2733;
    --text-2: #5C6672;
    --text-3: #8B939C;

    --border: #E4E0D8;
    --border-2: #EFECE6;
    --border-strong: #CFC8BC;

    --accent: #A64A26;
    --accent-hover: #8A3B1B;
    --accent-soft: #F6EAE3;
    --accent-line: #E4CBBC;

    --ok: #1C6E47;
    --ok-soft: #E4F1EA;
    --ok-line: #BEDDCC;
    --warn: #8A6100;
    --warn-soft: #FBF0D9;
    --warn-line: #E8D5A6;
    --danger: #A32A2C;
    --danger-soft: #F8E8E8;
    --danger-line: #E9C6C6;
    --info: #2B5D86;
    --info-soft: #E6EFF6;
    --info-line: #C3D8E8;

    --focus: #A64A26;

    --shadow-xs: 0 1px 2px rgba(27, 39, 51, .06);
    --shadow-sm: 0 1px 3px rgba(27, 39, 51, .08), 0 1px 2px rgba(27, 39, 51, .04);
    --shadow-md: 0 4px 12px rgba(27, 39, 51, .10), 0 1px 3px rgba(27, 39, 51, .06);
    --shadow-lg: 0 12px 32px rgba(27, 39, 51, .16), 0 2px 8px rgba(27, 39, 51, .08);

    --r-sm: 6px;
    --r-md: 10px;
    --r-lg: 14px;
    --r-full: 999px;

    --sidebar-w: 252px;
    --topbar-h: 56px;

    /* Height of a text field or a select — 9px padding either side, a 1px border either side, and the
       21px line box of 14px type. Named because a toolbar mixes fields with things that are not fields
       (the "Aj minulé" chip), and those have to line up with them rather than sit two pixels short. */
    --control-h: 41px;

    --font: 'Schibsted Grotesk', system-ui, -apple-system, "Segoe UI", sans-serif;

    /* The chevron a native select loses once appearance:none is set. Carried as a token so the
       stroke colour can follow the theme — a light-grey arrow on the dark surface reads as smudge. */
    --select-arrow: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238B939C' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");

    color-scheme: light;
}

/* Dark: borrowed from the public site's dark palette so a person switching between the two doesn't
   feel like they changed products. Borders lift a little more than in light — on near-black
   surfaces a 1px hairline at the light theme's contrast simply disappears. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #141312;
        --surface: #1E1C1A;
        --surface-2: #242220;
        --raised: #262320;
        --sidebar: #1A1917;

        --text: #ECE5DA;
        --text-2: #A89684;
        --text-3: #857A6D;

        --border: #383430;
        --border-2: #2B2825;
        --border-strong: #4A443E;

        --accent: #C99B6A;
        --accent-hover: #DCAF82;
        --accent-soft: #3A2F26;
        --accent-line: #4E3F32;

        --ok: #6FBF95;
        --ok-soft: #1E2E26;
        --ok-line: #2E4A3B;
        --warn: #D9B25E;
        --warn-soft: #302819;
        --warn-line: #4A3D22;
        --danger: #E0817F;
        --danger-soft: #331E1E;
        --danger-line: #4F2E2E;
        --info: #86B2D6;
        --info-soft: #1C2730;
        --info-line: #2C3E4D;

        --focus: #C99B6A;

        --shadow-xs: 0 1px 2px rgba(0, 0, 0, .4);
        --shadow-sm: 0 1px 3px rgba(0, 0, 0, .5);
        --shadow-md: 0 4px 12px rgba(0, 0, 0, .55);
        --shadow-lg: 0 12px 32px rgba(0, 0, 0, .65);

        --select-arrow: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23A89684' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");

        color-scheme: dark;
    }
}

/* Explicit choice from the theme toggle. Duplicated rather than shared with the media query above
   because a person who picked dark must get dark on a light OS, and vice versa. */
:root[data-theme="dark"] {
    --bg: #141312;
    --surface: #1E1C1A;
    --surface-2: #242220;
    --raised: #262320;
    --sidebar: #1A1917;

    --text: #ECE5DA;
    --text-2: #A89684;
    --text-3: #857A6D;

    --border: #383430;
    --border-2: #2B2825;
    --border-strong: #4A443E;

    --accent: #C99B6A;
    --accent-hover: #DCAF82;
    --accent-soft: #3A2F26;
    --accent-line: #4E3F32;

    --ok: #6FBF95;
    --ok-soft: #1E2E26;
    --ok-line: #2E4A3B;
    --warn: #D9B25E;
    --warn-soft: #302819;
    --warn-line: #4A3D22;
    --danger: #E0817F;
    --danger-soft: #331E1E;
    --danger-line: #4F2E2E;
    --info: #86B2D6;
    --info-soft: #1C2730;
    --info-line: #2C3E4D;

    --focus: #C99B6A;

    --shadow-xs: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-sm: 0 1px 3px rgba(0, 0, 0, .5);
    --shadow-md: 0 4px 12px rgba(0, 0, 0, .55);
    --shadow-lg: 0 12px 32px rgba(0, 0, 0, .65);

    --select-arrow: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23A89684' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");

    color-scheme: dark;
}

/* ---------- Reset / base ---------- */

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

/* The `hidden` attribute is set by script on elements that also carry a class with a `display`
   rule — the push-permission states in Profile.razor are four such alerts, of which exactly one
   is meant to show. An author `display: flex` beats the browser's own `[hidden] { display: none }`,
   so without this line every one of them renders at once and the attribute does nothing.
   `!important` is the point rather than an oversight: `hidden` is meant to win over layout. */
[hidden] { display: none !important; }

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

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

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.011em; }
h1 { font-size: 24px; line-height: 1.25; }
h2 { font-size: 16px; line-height: 1.35; }
h3 { font-size: 14px; line-height: 1.4; }
p { margin: 0 0 10px; }
p:last-child { margin-bottom: 0; }

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

.muted { color: var(--text-2); font-size: 13.5px; }
.tiny { font-size: 12.5px; color: var(--text-3); }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nowrap { white-space: nowrap; }

/* One visible focus treatment for the whole app. :focus-visible only, so a mouse click on a button
   doesn't leave a ring behind but a Tab key always does. */
:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
    border-radius: var(--r-sm);
}

::selection { background: var(--accent-soft); color: var(--text); }

/* Screen-reader-only, for labels that are visually obvious but structurally required. */
.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- Shell ---------- */

.app { min-height: 100vh; }

.app__sidebar {
    position: fixed; inset: 0 auto 0 0; width: var(--sidebar-w); z-index: 40;
    display: flex; flex-direction: column;
    background: var(--sidebar); border-right: 1px solid var(--border);
}

.app__main {
    margin-left: var(--sidebar-w);
    min-height: 100vh;
    display: flex; flex-direction: column;
}

.app__content { flex: 1; padding: 28px 32px 64px; max-width: 1320px; width: 100%; }

/* Top bar exists only below the sidebar breakpoint. */
.app__topbar { display: none; }

.sidebar-head { display: flex; align-items: center; }

/* Hamburger and the drawer's own close button. Both exist only below the sidebar breakpoint —
   the mobile media query is what turns them back on. */
.drawer-btn { display: none; }

.brand {
    display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0;
    padding: 18px 16px 14px; color: var(--text); text-decoration: none;
}
.brand:hover { text-decoration: none; }
/* The mountain mark inherits the text colour; only its baseline rule is accent-coloured, which is
   how the public site draws it too. */
.brand__logo { display: block; flex: none; color: var(--text); }

.brand__text { display: flex; flex-direction: column; gap: 1px; line-height: 1.15; min-width: 0; }

/* Set like the public site's wordmark: Newsreader 500, slightly tightened. */
.brand__name {
    font-family: 'Newsreader', Georgia, serif;
    font-size: 20px; font-weight: 500; letter-spacing: -.01em; line-height: 1;
}
.brand__sub { font-size: 10.5px; color: var(--text-3); letter-spacing: .01em; }

/* ---------- Navigation ---------- */

.nav { flex: 1; overflow-y: auto; padding: 4px 10px 10px; }

.nav__group { margin-bottom: 14px; }
.nav__group-label {
    padding: 6px 10px 4px; font-size: 11px; font-weight: 650;
    text-transform: uppercase; letter-spacing: .07em; color: var(--text-3);
}

.nav__link {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; margin-bottom: 1px; border-radius: var(--r-sm);
    color: var(--text-2); text-decoration: none; font-size: 14px; font-weight: 500;
    transition: background .12s ease, color .12s ease;
}
.nav__link:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.nav__link .icon { flex: none; color: var(--text-3); transition: color .12s ease; }
.nav__link:hover .icon { color: var(--text-2); }

.nav__link.active {
    background: var(--accent-soft); color: var(--accent); font-weight: 600;
}
.nav__link.active .icon { color: var(--accent); }

.nav__label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Count riding on a nav item (pending approvals). Accent-tinted, not red: it's a queue, not a fault. */
.nav__count {
    flex: none; min-width: 20px; padding: 0 6px; border-radius: var(--r-full);
    background: var(--accent); color: #fff;
    font-size: 11.5px; font-weight: 700; line-height: 19px; text-align: center;
}
:root[data-theme="dark"] .nav__count,
:root:not([data-theme="light"]) .nav__count { color: #1E1C1A; }
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) .nav__count { color: #fff; }
}

/* ---------- Version (above the footer's rule) ---------- */

/* Quiet by default and only as loud as a nav link on hover: it is a signpost to a document, not a
   destination anybody navigates to on purpose. */
.version {
    display: block; text-align: center;
    margin: 0 10px 8px; padding: 6px 8px;
    border-radius: var(--r-sm);
    color: var(--text-3); font-size: 12px; font-weight: 600;
    text-decoration: none; letter-spacing: .01em;
}
.version:hover { background: var(--surface-2); color: var(--text-2); text-decoration: none; }

/* ---------- Prose (rendered Markdown — the changelog) ---------- */

/* The one place in this app where the markup is not written as components, so the type scale has to be
   restated rather than inherited from anything. Narrow measure on purpose: it is read, not scanned. */
.prose { max-width: 76ch; font-size: 14.5px; line-height: 1.65; }
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }
.prose h1 { font-size: 21px; margin: 0 0 6px; }
.prose h2 {
    font-size: 17px; margin: 30px 0 4px;
    padding-bottom: 6px; border-bottom: 1px solid var(--border-2);
}
.prose h3 { font-size: 13px; margin: 18px 0 6px; color: var(--text-2); text-transform: uppercase; letter-spacing: .05em; }
.prose p { margin: 0 0 10px; color: var(--text-2); }
.prose ul { margin: 0 0 12px; padding-left: 20px; }
.prose li { margin-bottom: 5px; color: var(--text-2); }
.prose li::marker { color: var(--text-3); }
.prose strong { color: var(--text); font-weight: 650; }
.prose code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12.5px; padding: 1px 5px; border-radius: var(--r-sm);
    background: var(--surface-2); color: var(--text);
}
.prose hr { margin: 22px 0; border: 0; border-top: 1px solid var(--border-2); }
.prose a { color: var(--accent); }

/* ---------- Sidebar footer (user + controls) ---------- */

.sidebar-foot { border-top: 1px solid var(--border); padding: 10px; }

.user { display: flex; align-items: center; gap: 9px; padding: 6px; min-width: 0; }
.user__avatar {
    width: 30px; height: 30px; flex: none; border-radius: var(--r-full);
    background: var(--accent-soft); color: var(--accent);
    display: grid; place-items: center; font-size: 12.5px; font-weight: 700;
    text-transform: uppercase;
}
.user__text { min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.user__name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user__role { font-size: 11.5px; color: var(--text-3); }

.sidebar-foot form { margin: 0; }

/* ---------- Account menu (the gear beside the name) ---------- */

.menu { position: relative; margin-left: auto; flex: none; }

.menu__trigger {
    display: grid; place-items: center; width: 30px; height: 30px;
    border-radius: var(--r-sm); color: var(--text-3); cursor: pointer;
    list-style: none; transition: background .12s ease, color .12s ease;
    -webkit-tap-highlight-color: transparent;
}
.menu__trigger::-webkit-details-marker { display: none; }
.menu__trigger::marker { content: ''; }
.menu__trigger:hover { background: var(--surface-2); color: var(--text); }
.menu[open] .menu__trigger { background: var(--accent-soft); color: var(--accent); }

/* Opens upward and grows leftward: the trigger sits at the bottom-right of the sidebar, so there is
   no room below it, and anchoring the panel's left edge to it pushed 232px of menu off the right of
   the screen once the sidebar became a drawer. */
.menu__panel {
    position: absolute; bottom: calc(100% + 6px); right: 0;
    min-width: 232px; z-index: 60; padding: 6px;
    background: var(--raised); border: 1px solid var(--border-strong);
    border-radius: var(--r-md); box-shadow: var(--shadow-lg);
}

/* Identity block: text, not a target. */
.menu__head { display: flex; flex-direction: column; gap: 1px; padding: 8px 10px 10px; min-width: 0; }
.menu__head-name { font-size: 14px; font-weight: 650; }
.menu__head-mail { font-size: 12px; color: var(--text-3); overflow: hidden; text-overflow: ellipsis; }

.menu__item {
    display: flex; align-items: center; gap: 9px; width: 100%;
    padding: 8px 10px; border: 0; border-radius: var(--r-sm);
    background: transparent; color: var(--text);
    font: inherit; font-size: 14px; font-weight: 500; text-align: left;
    text-decoration: none; cursor: pointer;
}
.menu__item:hover { background: var(--surface-2); text-decoration: none; }

/* The one item that navigates somewhere gets a border and a chevron, so it reads as a control
   rather than as another line of the list it sits above. */
.menu__item--action {
    border: 1px solid var(--border-strong); background: var(--surface);
    font-weight: 550; margin-bottom: 2px;
}
.menu__item--action:hover { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }
.menu__item--action:hover .icon { color: var(--accent); }
.menu__item-label { flex: 1; min-width: 0; }
.menu__chev { color: var(--text-3); }
.menu__item .icon { color: var(--text-3); }
.menu__item--danger { color: var(--danger); }
.menu__item--danger:hover { background: var(--danger-soft); }
.menu__item--danger .icon { color: var(--danger); }

.menu__row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 6px 10px;
}
.menu__label { font-size: 13px; color: var(--text-2); }

.menu__sep { height: 1px; margin: 5px 0; background: var(--border-2); }

/* The same menu inside a table row, where there IS room below and the trigger has to look like the
   button it replaced rather than like a bare icon. */
.menu--down { margin-left: 0; }
.menu--down .menu__panel { top: calc(100% + 6px); bottom: auto; min-width: 208px; }

.menu__trigger--btn {
    display: inline-flex; gap: 6px; width: auto; height: auto;
    padding: 5px 10px; border: 1px solid var(--border-strong);
    background: var(--surface); color: var(--text);
    font-size: 13px; font-weight: 550; white-space: nowrap;
}
.menu__trigger--btn:hover { background: var(--surface-2); }
.menu[open] .menu__trigger--btn { background: var(--accent-soft); color: var(--accent); border-color: var(--accent-line); }

/* .table-wrap scrolls sideways, and per spec that makes it a scroll container on BOTH axes — so a
   panel hanging out of a row would be clipped by it. Un-clip it only while a menu is actually open:
   the alternative is positioning the panel with script, and this page has none. */
.table-wrap:has(details.menu[open]) { overflow: visible; }

/* Disabled items still have to look disabled — .menu__item was written for the account menu, where
   nothing is ever unavailable. */
.menu__item:disabled { opacity: .5; cursor: default; }
.menu__item:disabled:hover { background: transparent; }

@media (max-width: 1023px) {
    .menu__trigger { width: 40px; height: 40px; }
    .menu__trigger--btn { width: auto; height: auto; }
    .menu__item { padding: 11px 10px; min-height: 44px; }
}

/* ---------- Theme + language controls ---------- */

.seg {
    display: inline-flex; padding: 2px; gap: 2px;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
}
.seg__item {
    display: grid; place-items: center; min-width: 30px; height: 26px; padding: 0 7px;
    border: 0; border-radius: 4px; background: transparent; color: var(--text-2);
    font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; text-decoration: none;
    transition: background .12s ease, color .12s ease;
}
.seg__item:hover { background: var(--surface); color: var(--text); text-decoration: none; }
.seg__item.is-current { background: var(--surface); color: var(--accent); box-shadow: var(--shadow-xs); }

/* ---------- Page header ---------- */

.page-header {
    display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap;
    margin-bottom: 20px;
}
.page-header__text { flex: 1; min-width: 220px; }
.page-header__title { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.page-header__sub { margin: 4px 0 0; color: var(--text-2); font-size: 14px; max-width: 68ch; }
.page-header__actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.breadcrumb {
    display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
    margin-bottom: 10px; font-size: 13px; color: var(--text-3);
}
.breadcrumb a { color: var(--text-2); }
.breadcrumb a:hover { color: var(--accent); }
.breadcrumb__sep { color: var(--text-3); }

/* ---------- Cards ---------- */

.card {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-lg); box-shadow: var(--shadow-xs);
    margin-bottom: 16px;
}
.card > :not(.card__header):not(.card__foot):not(.table-wrap):not(.toolbar):not(table):not(.empty):not(.alert) {
    padding-left: 18px; padding-right: 18px;
}
.card > :not(.card__header):not(.card__foot):not(.table-wrap):not(.toolbar):not(table):not(.empty):not(.alert):first-child { padding-top: 18px; }
.card > :not(.card__header):not(.card__foot):not(.table-wrap):not(.toolbar):not(table):not(.empty):not(.alert):last-child { padding-bottom: 18px; }

/* A body block sandwiched between a header and a footer is neither :first-child nor :last-child, so
   the two rules above skip it and it ends up flush against both borders. These give it the missing
   vertical padding: the first block after a header, and the last one before a footer. */
.card > .card__header + :not(.card__foot):not(.table-wrap):not(.toolbar):not(table):not(.empty):not(.alert) { padding-top: 16px; }
.card > :not(.card__header):not(.table-wrap):not(.toolbar):not(table):not(.empty):not(.alert):has(+ .card__foot) { padding-bottom: 16px; }

/* An alert is tinted, so it must not inherit the card's own side padding — that would override its
   internal padding and run the tint edge to edge. It gets margins instead. */
.card > .alert { margin: 14px 18px; }

/* Escape hatch: a card whose whole body is one padded block. */
.card--pad { padding: 18px; }
.card--pad > * { padding-left: 0 !important; padding-right: 0 !important; }

.card__header {
    display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
    padding: 14px 18px; border-bottom: 1px solid var(--border-2);
}
.card__header--plain { border-bottom: 0; padding-bottom: 0; }
.card__header-text { flex: 1; min-width: 180px; }
.card__title { font-size: 15px; font-weight: 650; }
.card__desc { margin: 3px 0 0; font-size: 13.5px; color: var(--text-2); max-width: 72ch; }
.card__header-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Actions on their own, at the right edge. A footer with two children (a note and the buttons) spaces
   them apart on its own; one with only buttons would leave them hanging on the left, and a Save button
   is looked for in the same corner on every screen. */
.card__foot--end { justify-content: flex-end; }

.card__foot {
    display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
    padding: 12px 18px; border-top: 1px solid var(--border-2);
    background: var(--surface-2);
    border-radius: 0 0 var(--r-lg) var(--r-lg);
}

/* Danger zone — a card that says "read this twice" without shouting. */
.card--danger { border-color: var(--danger-line); }
.card--danger .card__header { border-bottom-color: var(--danger-line); }

/* ---------- Toolbar (filters above a list) ---------- */

.toolbar {
    display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
    padding: 14px 18px;
}
.toolbar + .table-wrap { border-top: 1px solid var(--border-2); }
.toolbar__grow { flex: 1; min-width: 200px; }

/* How many rows the filters actually left on screen, sitting between the toolbar and the list. It
   takes over the divider .toolbar + .table-wrap would otherwise have drawn, since it now stands
   between the two. Side padding comes from the .card > * rule, which wins on specificity anyway. */
.toolbar__count {
    padding-top: 0; padding-bottom: 11px;
    border-bottom: 1px solid var(--border-2);
}
.toolbar__count + .table-wrap { border-top: 0; }

/* The toolbar aligns its controls on their baseline, so the stacked margin a .field carries in a
   form would lift every labelled control 14px above the bare checkboxes sitting next to it. */
.toolbar .field { margin-bottom: 0; }

/* An input sized to its content rather than to its column — dates and clock times inside a table row,
   where a full-width field would push the columns that carry the actual words off the screen. */
.input--compact { width: auto; min-width: 0; }

/* Search field with a leading icon. */
.search { position: relative; display: block; }
.search .icon {
    position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
    color: var(--text-3); pointer-events: none;
}
.search input { padding-left: 34px; }

/* ---------- Forms ---------- */

.field { display: block; margin-bottom: 14px; }
.field:last-child { margin-bottom: 0; }
.field__label {
    display: block; margin-bottom: 5px;
    font-size: 13px; font-weight: 600; color: var(--text);
}
.field__hint { margin: 5px 0 0; font-size: 12.5px; color: var(--text-3); }
.field__error { margin: 5px 0 0; font-size: 12.5px; color: var(--danger); font-weight: 500; }

input, select, textarea {
    width: 100%; padding: 9px 11px;
    border: 1px solid var(--border-strong); border-radius: var(--r-sm);
    font: inherit; font-size: 14px; color: var(--text); background: var(--surface);
    transition: border-color .12s ease, box-shadow .12s ease;
}
input:hover, select:hover, textarea:hover { border-color: var(--text-3); }
input:focus, select:focus, textarea:focus {
    outline: none; border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
input:disabled, select:disabled, textarea:disabled {
    background: var(--surface-2); color: var(--text-3); cursor: not-allowed;
}
input::placeholder, textarea::placeholder { color: var(--text-3); }
textarea { min-height: 96px; resize: vertical; line-height: 1.5; }

select {
    appearance: none; padding-right: 30px; cursor: pointer;
    background-image: var(--select-arrow);
    background-repeat: no-repeat; background-position: right 10px center;
}

input[type="color"] { padding: 3px; height: 38px; cursor: pointer; }
input[type="file"] { padding: 7px 9px; }

/* Checkbox / radio as an inline control with its own label — the whole thing is the hit target. */
input[type="checkbox"], input[type="radio"] {
    width: 17px; height: 17px; flex: none; margin: 0;
    accent-color: var(--accent); cursor: pointer;
}
.check {
    display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
    font-size: 14px; color: var(--text); user-select: none;
}
.check input { margin: 0; }
.check--block { display: flex; padding: 7px 0; }

/* Filter chip: a toggle that belongs in a toolbar next to a search box, where a bare checkbox and a
   sentence of label read as a stray form field.

   The box stays VISIBLE. Hiding it and letting a tint carry the state was tried and did not work —
   with nothing but a faint fill to go on, you cannot tell a chip that is on from one that is merely
   hovered, and nothing says the thing is a toggle at all. A checkbox is the one control everybody
   already reads; the chip around it supplies the emphasis, not the meaning. */
.check--chip {
    /* Sized to the fields it stands next to in a toolbar, not to its own text: a row of controls that
       are 41, 41 and 34 pixels tall reads as one of them being slightly broken. */
    min-height: var(--control-h);
    padding: 6px 13px 6px 9px; gap: 7px;
    border: 1px solid var(--border-strong); border-radius: var(--r-full);
    background: var(--surface); color: var(--text-2);
    font-size: 13.5px; font-weight: 550; white-space: nowrap;
    transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.check--chip input { width: 16px; height: 16px; }
.check--chip:hover { border-color: var(--text-3); background: var(--surface-2); color: var(--text); }

/* On: filled, outlined and in the accent colour, so it reads as pressed from across the room and
   not only from the tick. */
.check--chip:has(input:checked) {
    background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 650;
}
.check--chip:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
@media (max-width: 1023px) {
    /* Taller still where the finger is the pointer — 44px is the touch target, above the 41 above. */
    .check--chip { min-height: 44px; }
}

.fieldset { border: 0; padding: 0; margin: 0 0 14px; }
.fieldset__legend {
    padding: 0; margin-bottom: 8px;
    font-size: 13px; font-weight: 650; color: var(--text);
}

/* ---------- Buttons ---------- */

.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    padding: 8px 14px; min-height: 36px;
    border: 1px solid var(--border-strong); border-radius: var(--r-sm);
    background: var(--surface); color: var(--text);
    font: inherit; font-size: 14px; font-weight: 550; line-height: 1.2;
    cursor: pointer; text-decoration: none; white-space: nowrap;
    transition: background .12s ease, border-color .12s ease, color .12s ease, box-shadow .12s ease;
}
.btn:hover { background: var(--surface-2); border-color: var(--text-3); text-decoration: none; }
.btn:active { transform: translateY(.5px); }

.btn--primary, .btn-primary {
    background: var(--accent); border-color: var(--accent); color: #fff;
    box-shadow: var(--shadow-xs);
}
.btn--primary:hover, .btn-primary:hover {
    background: var(--accent-hover); border-color: var(--accent-hover); color: #fff;
}
:root[data-theme="dark"] .btn--primary, :root[data-theme="dark"] .btn-primary { color: #1E1C1A; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .btn--primary,
    :root:not([data-theme="light"]) .btn-primary { color: #1E1C1A; }
    :root:not([data-theme="light"]) .btn--primary:hover,
    :root:not([data-theme="light"]) .btn-primary:hover { color: #1E1C1A; }
}

.btn--danger, .btn-danger { color: var(--danger); border-color: var(--danger-line); }
.btn--danger:hover, .btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); }

.btn--ghost { background: transparent; border-color: transparent; color: var(--text-2); }
.btn--ghost:hover { background: var(--surface-2); color: var(--text); border-color: transparent; }

.btn--quiet, .btn-quiet {
    background: transparent; border-color: transparent; color: var(--text-2); padding-left: 8px; padding-right: 8px;
}
.btn--quiet:hover, .btn-quiet:hover { background: var(--surface-2); color: var(--text); border-color: transparent; }

.btn--sm { min-height: 30px; padding: 5px 10px; font-size: 13px; }
.btn--block { width: 100%; }

/* Square icon-only button. Always needs an aria-label. */
.btn--icon { padding: 0; width: 36px; min-height: 36px; flex: none; }
.btn--icon.btn--sm { width: 30px; min-height: 30px; }

.btn:disabled, .btn[disabled] { opacity: .45; cursor: not-allowed; }
.btn:disabled:hover, .btn[disabled]:hover { background: var(--surface); border-color: var(--border-strong); }

.btn-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.btn-row--end { justify-content: flex-end; }

/* ---------- Tables ---------- */

.table-wrap { width: 100%; overflow-x: auto; }

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

th, td { text-align: left; padding: 11px 14px; vertical-align: middle; }

/* Not sticky: .table-wrap has overflow-x:auto, which per spec forces overflow-y to auto as well,
   making it the scroll container a sticky header would resolve against — and that container never
   scrolls vertically, so the header would simply never stick. */
thead th {
    background: var(--surface-2); color: var(--text-2);
    font-size: 11.5px; font-weight: 650; text-transform: uppercase; letter-spacing: .05em;
    white-space: nowrap;
    border-bottom: 1px solid var(--border);
}

tbody td { border-bottom: 1px solid var(--border-2); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background .1s ease; }
tbody tr:hover { background: var(--surface-2); }

/* The name-and-context pairing every list uses: a link on top, dimmer detail beneath. */
.cell-primary { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cell-primary > a, .cell-primary > strong { font-weight: 600; color: var(--text); }
.cell-primary > a:hover { color: var(--accent); }
.cell-meta { font-size: 12.5px; color: var(--text-3); }
/* The recurrence rule written out as a sentence. Set apart from the hints around it because it is the
   one line in that block that has to be read: it is the only place the dropdowns say what they mean. */
.recurrence-summary {
    margin: 10px 0 0; padding: 10px 12px;
    border-left: 3px solid var(--accent); border-radius: 0 var(--r-sm) var(--r-sm) 0;
    background: var(--surface-2); color: var(--text-1);
    font-size: 14px; font-weight: 600;
}

/* An input with a short note beside it — "Uložené" after a row saves itself. The input keeps the row's
   width and the note takes only what it needs, so the column does not jump when the note appears and
   disappears. */
.field-with-note { display: flex; align-items: center; gap: 8px; }
.field-with-note > input { flex: 1; min-width: 0; }

.saved-note {
    display: inline-flex; align-items: center; gap: 4px; flex: none;
    font-size: 12.5px; font-weight: 600; color: var(--ok);
    animation: saved-note-in 140ms ease-out;
}

@keyframes saved-note-in { from { opacity: 0; transform: translateX(-4px); } to { opacity: 1; transform: none; } }

.cell-actions { display: flex; align-items: center; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }

/* A column that should take only the width its content needs — a checkbox, a row of buttons.
   Needed on both the header and the body cell, or the header's natural width wins the negotiation. */
th.shrink, td.shrink { width: 1%; white-space: nowrap; }

/* Room for the longest category name rather than for the shortest. Sized in ch so it follows the font
   instead of guessing at pixels: "Poetické večery" is the longest one in the seed. */
th.col-category, td.col-category { width: 18ch; }

/* Actions on a line of their own, under the record they belong to and ending at its right edge. Squeezed
   into a last column they had to wrap once the table carried more than a few columns, which stacked
   three buttons vertically and cost a click's worth of aim each time. One <tbody> per record is what
   keeps the two rows behaving as one: the hover is on the group, and so is the separating line. */
tbody.record > tr > td { border-bottom: 0; }
tbody.record { border-bottom: 1px solid var(--border-2); }
tbody.record:last-of-type { border-bottom: 0; }
tbody.record:hover { background: var(--surface-2); }
tbody.record > tr:hover { background: transparent; }
tbody.record > tr.record__actions > td { padding-top: 0; padding-bottom: 11px; }

/* The record's actions: a strip across the bottom of the record, ending at its right edge, wrapping
   onto a second line when the window is too narrow for one.

   It used to hide behind a "⋯ Akcie" trigger on narrow screens, built as a <details> whose panel CSS
   forced flat and open on wide ones. That was wrong in a way worth remembering: a closed <details>
   hides its content through ::details-content, and newer Chrome no longer lets a display on the child
   override that. The wide rule stopped working while the rule hiding the summary kept working, so the
   actions vanished entirely — no buttons, no menu, nothing to click. A layout that depends on a browser
   quirk to reveal controls fails silently and takes the whole feature with it; wrapping never does. */
}

/* A wrapping field of tick-chips — a few hundred suggested words, where a table would be a mile of
   scrolling and a multi-select would hide the very thing being judged. */
.chip-cloud { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 18px 16px; }

/* ---------- Pills, badges, dots ---------- */

.pill {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 2px 9px; border-radius: var(--r-full);
    border: 1px solid transparent;
    font-size: 12px; font-weight: 600; white-space: nowrap;
}
.pill--published { background: var(--ok-soft); color: var(--ok); border-color: var(--ok-line); }
.pill--pending   { background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line); }
.pill--draft     { background: var(--surface-2); color: var(--text-2); border-color: var(--border); }
.pill--hidden    { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-line); }
.pill--cancelled { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-line); text-decoration: line-through; }
.pill--info      { background: var(--info-soft); color: var(--info); border-color: var(--info-line); }
/* Same colours as --pending, separate name: that one is an event status, and this marks something that
   wants a look without being a status at all. */
.pill--warn      { background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line); }
.pill--accent    { background: var(--accent-soft); color: var(--accent); border-color: var(--accent-line); }

.badge {
    display: inline-block; padding: 1px 7px; border-radius: var(--r-full);
    background: var(--surface-2); border: 1px solid var(--border);
    font-size: 11.5px; font-weight: 600; color: var(--text-2);
}

.dot { width: 8px; height: 8px; border-radius: var(--r-full); flex: none; display: inline-block; }

/* Image preview on the event and venue forms. Capped by width rather than a fixed box so a portrait
   poster and a landscape photo both keep their own proportions. */
.thumb {
    display: block; max-width: 280px; width: 100%; height: auto;
    border: 1px solid var(--border); border-radius: var(--r-md);
    background: var(--surface-2);
}

/* Category colour chip. Kept as .swatch — it is set inline from the category's own colour. */
.swatch {
    display: inline-block; width: 10px; height: 10px; border-radius: 3px;
    vertical-align: baseline; margin-right: 7px;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .08) inset;
}

/* ---------- Alerts ---------- */

.alert {
    display: flex; align-items: flex-start; gap: 9px;
    padding: 11px 13px; border-radius: var(--r-md);
    border: 1px solid var(--danger-line); background: var(--danger-soft); color: var(--danger);
    font-size: 14px; margin-bottom: 14px;
}
.alert .icon { flex: none; margin-top: 1px; }
.alert--ok { border-color: var(--ok-line); background: var(--ok-soft); color: var(--ok); }
.alert--warn { border-color: var(--warn-line); background: var(--warn-soft); color: var(--warn); }
.alert--info { border-color: var(--info-line); background: var(--info-soft); color: var(--info); }

/* ---------- Stats ---------- */

.stats {
    display: grid; gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    margin-bottom: 16px;
}

.stat {
    display: flex; flex-direction: column; gap: 2px;
    padding: 14px 16px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-lg); box-shadow: var(--shadow-xs);
    color: inherit; text-decoration: none;
    transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
a.stat:hover {
    text-decoration: none; border-color: var(--border-strong);
    box-shadow: var(--shadow-sm); transform: translateY(-1px);
}
.stat__num { font-size: 28px; font-weight: 650; line-height: 1.1; letter-spacing: -.02em; }
.stat__label { font-size: 13px; color: var(--text-2); }
.stat--accent .stat__num { color: var(--accent); }
.stat--warn .stat__num { color: var(--warn); }

/* ---------- Empty state ---------- */

.empty {
    display: flex; flex-direction: column; align-items: center; gap: 8px;
    padding: 44px 24px; text-align: center;
}
.empty .icon { color: var(--text-3); }
.empty__title { font-size: 15px; font-weight: 600; }
.empty__text { font-size: 13.5px; color: var(--text-2); max-width: 44ch; margin: 0; }

/* ---------- Pagination ---------- */

.pager { display: flex; align-items: center; gap: 4px; }
.pager__label { font-size: 13px; color: var(--text-2); padding: 0 8px; white-space: nowrap; }

/* ---------- Bulk action bar ---------- */

/* Reserves nothing when empty and floats above the list when active, so ticking a row never
   pushes the row you were aiming at out from under your finger. */
.bulkbar {
    position: sticky; bottom: 12px; z-index: 20;
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding: 10px 14px; margin: 12px 0 0;
    background: var(--raised); border: 1px solid var(--border-strong);
    border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
}
.bulkbar__count { font-size: 13.5px; font-weight: 650; margin-right: 2px; }
.bulkbar__spacer { flex: 1; }

/* ---------- Grids / rows (layout helpers) ---------- */

.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; }
.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }

.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.row--end { justify-content: flex-end; }

/* A value and the button that acts on it are one thing and must not be split across two lines. Used
   where the value can be long (a GUID, a URL): the text truncates, the buttons keep their size. */
.row--tight { flex-wrap: nowrap; gap: 6px; }
.row--tight > .btn { flex: none; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.stack { display: flex; flex-direction: column; gap: 10px; }
.stack--tight { gap: 6px; }

/* ---------- Inline field rows (compact settings) ---------- */

/* Label, control and button on one line. Wraps rather than shrinking below a usable width, so the
   same markup stacks by itself on a narrow screen without a second rule. */
.row-field {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.row-field__label {
    flex: none; font-size: 13px; font-weight: 600; color: var(--text);
}
.row-field__control { flex: 0 1 auto; min-width: 0; }
.row-field__note { flex: 1 1 260px; min-width: 0; font-size: 12.5px; color: var(--text-3); }

/* Widths for inputs that hold something short. The old build gave a two-letter language code the
   same full-bleed field as a URL. */
.w-xs { width: 96px; }
.w-sm { width: 150px; }
.w-md { width: 230px; }
.w-lg { width: 340px; }
@media (max-width: 560px) {
    .w-xs, .w-sm, .w-md, .w-lg { width: 100%; }
}

/* ---------- Sticky action bar (edit pages) ---------- */

/* Sits directly under the page header and stays there. On a long form the actions are the thing you
   reach for from anywhere in it, so they follow rather than sitting at the bottom of the scroll. */
.action-bar {
    position: sticky; top: 0; z-index: 18;
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding: 10px 14px; margin-bottom: 16px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-md); box-shadow: var(--shadow-sm);
}
.action-bar__spacer { flex: 1; }
@media (max-width: 1023px) {
    /* Below the sidebar breakpoint the top bar owns the top of the viewport. */
    .action-bar { top: var(--topbar-h); }
    .action-bar .btn { flex: 1 1 auto; }
}

/* ---------- Collapsible section ---------- */

.collapse {
    border: 1px solid var(--border); border-radius: var(--r-md);
    background: var(--surface-2);
}
.collapse + .collapse { margin-top: 8px; }
.collapse > summary {
    display: flex; align-items: center; gap: 8px;
    padding: 10px 12px; cursor: pointer; list-style: none;
    font-size: 13.5px; font-weight: 600; color: var(--text);
    border-radius: var(--r-md);
    -webkit-tap-highlight-color: transparent;
}
.collapse > summary::-webkit-details-marker { display: none; }
.collapse > summary:hover { background: var(--surface); }
.collapse > summary .icon { color: var(--text-3); transition: transform .15s ease; }
.collapse[open] > summary { border-bottom: 1px solid var(--border); border-radius: var(--r-md) var(--r-md) 0 0; }
.collapse[open] > summary .icon.collapse__chevron { transform: rotate(180deg); }
.collapse__count { margin-left: auto; font-size: 12px; color: var(--text-3); font-weight: 500; }
.collapse__body { padding: 12px; background: var(--surface); border-radius: 0 0 var(--r-md) var(--r-md); }

/* ---------- Attribute icons (what a row has filled in) ---------- */

.attrs { display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.attrs .icon { color: var(--text-3); }
.attr {
    display: grid; place-items: center; width: 22px; height: 22px;
    border-radius: var(--r-sm); background: var(--surface-2); border: 1px solid var(--border);
}
.attr--on { background: var(--accent-soft); border-color: var(--accent-line); }
.attr--on .icon { color: var(--accent); }
.attr--off { opacity: .35; }

/* ---------- Combo box (select you can type into) ---------- */

.combo { position: relative; }
.combo__list {
    position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 4px);
    max-height: 260px; overflow-y: auto;
    display: flex; flex-direction: column;
    background: var(--raised); border: 1px solid var(--border-strong);
    border-radius: var(--r-md); box-shadow: var(--shadow-md);
}
.combo__option {
    display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
    padding: 9px 11px; border: 0; border-bottom: 1px solid var(--border-2);
    background: transparent; color: var(--text); font: inherit; font-size: 14px;
    text-align: left; cursor: pointer; width: 100%;
}
.combo__option:last-child { border-bottom: 0; }
.combo__option:hover, .combo__option.is-active { background: var(--accent-soft); }
.combo__option-meta { font-size: 12px; color: var(--text-3); }
.combo__empty { padding: 10px 11px; font-size: 13px; color: var(--text-3); }

/* ---------- Two-factor pairing ---------- */

.totp { display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-start; }

/* The SVG is generated without a fixed size, so it fills whatever box it is given. White plate
   underneath regardless of theme — a scanner needs the light modules to actually be light. */
.totp__qr {
    flex: none; width: 190px; height: 190px; padding: 10px;
    background: #fff; border: 1px solid var(--border); border-radius: var(--r-md);
}
.totp__qr svg { display: block; width: 100%; height: 100%; }
.totp__text { flex: 1; min-width: 240px; }
.totp__key {
    display: inline-block; margin: 2px 0 14px; padding: 7px 10px;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
    font-size: 14px; letter-spacing: .08em; user-select: all;
}

/* Recovery codes: a grid you can read down, and select in one go to copy. */
.codes {
    display: grid; gap: 6px; margin-bottom: 4px;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    user-select: all;
}
.codes code {
    padding: 6px 9px; text-align: center;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
    letter-spacing: .04em;
}

/* ---------- Wide page (dense tables) ---------- */

/* A page that needs more room than the comfortable reading width says so on its own header, and the
   layout stays none the wiser about which pages those are. /zdroje is the case: ten columns.

   No cap at all, rather than a bigger one. 1320 was too narrow, so it became 1680, and that was too
   narrow as well — because the right number is not a number, it is however wide the screen is. A table
   this dense either gets the whole width or goes on wrapping on somebody's monitor. */
.app__content:has(> .page-header.page-wide) { max-width: none; }

/* The 68ch measure above is there so an intro paragraph stays readable on a wide screen. On a page
   that has already declared it wants the whole window, it stops helping and starts looking like a
   mistake instead: a short column of text with the table it introduces running twice as wide beneath
   it. Those pages get the full width for their intro too. */
.page-header.page-wide .page-header__sub { max-width: none; }

/* ---------- Icons ---------- */

.icon { display: inline-block; vertical-align: -0.15em; flex: none; }

/* ---------- Drawer (mobile navigation) ---------- */

/* Checkbox-driven so it works without a Blazor circuit — MainLayout renders statically. admin.js
   only adds the niceties (Escape, close after navigating). */
.drawer-toggle { position: absolute; opacity: 0; pointer-events: none; }

/* BELOW the drawer, above everything else. The stack is: top bar (30) — scrim (35) — drawer (40).
   At 45 it sat on top of the drawer it was meant to sit behind, so the menu came up blurred by its
   own backdrop-filter and every tap landed on the scrim, which closes it: the drawer opened and
   nothing in it could be pressed. */
.scrim {
    position: fixed; inset: 0; z-index: 35;
    background: rgba(20, 19, 18, .45); backdrop-filter: blur(1.5px);
    opacity: 0; visibility: hidden; transition: opacity .2s ease, visibility .2s ease;
}

/* ---------- Modal ---------- */

/* A window that covers the page until it is closed, for content a table cell cannot hold — today the
   raw capture on /zber, which as an in-row <details> grew the row past the height of the screen and
   pushed every action button out of reach.

   Above the drawer (40) and the row menus (60), because it has to cover them too. */
.modal {
    position: fixed; inset: 0; z-index: 70;
    display: grid; place-items: center; padding: 24px;

    /* Deliberately NOT locking the page behind with overflow:hidden. On Windows that removes the
       scrollbar, and everything under the scrim jumps sideways by its width at the exact moment the
       window opens — the shift this whole component exists to get rid of. Containing the scroll here
       instead keeps the wheel inside the panel without moving anything. */
    overscroll-behavior: contain;
}

/* Behind the panel rather than under it, so a click that closes the window can never be a click that
   landed on the window. Absolute, so it stays out of the centring grid. */
.modal__scrim {
    position: absolute; inset: 0;
    background: rgba(20, 19, 18, .55); backdrop-filter: blur(2px);
}

.modal__panel {
    position: relative;
    display: flex; flex-direction: column;
    width: min(1080px, 100%); max-height: min(84vh, 900px);
    background: var(--raised); border: 1px solid var(--border-strong);
    border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
    overflow: hidden;   /* keeps the body's corners inside the panel's radius */
}

.modal__header {
    display: flex; align-items: flex-start; gap: 12px;
    padding: 14px 16px; border-bottom: 1px solid var(--border);
}

/* Which row the window belongs to. The row itself is behind the scrim and cannot be pointed at, so
   this line is the only thing saying it — hence the source name at full weight and the external key
   below it, both of them, not one or the other. */
.modal__heading { min-width: 0; flex: 1; }
.modal__title { margin: 0; font-size: 15.5px; font-weight: 650; }
.modal__sub {
    margin: 2px 0 0; color: var(--text-2);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.modal__body { flex: 1; min-height: 0; overflow: auto; padding: 14px 16px; }

/* The capture is machine-formatted and its lines are long. Wrapping them would fold every nested
   object into a paragraph, so the body scrolls sideways instead and the indentation survives. */
.modal__body pre { margin: 0; white-space: pre; }

.modal__footer {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 12px 16px; border-top: 1px solid var(--border); background: var(--surface);
}

@media (max-width: 767px) {
    .modal { padding: 12px; }
    .modal__panel { max-height: 90vh; }
    .modal__footer { justify-content: flex-end; }
    /* No room for it next to the button, and Escape/tap-outside still close the window. */
    .modal__footer .muted { display: none; }
}

/* ---------- Login ---------- */

.login {
    min-height: 100vh; display: grid; place-items: center; padding: 24px;
    background:
        radial-gradient(1100px 520px at 50% -8%, var(--accent-soft), transparent 62%),
        var(--bg);
}
.login__card {
    width: 100%; max-width: 380px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-lg); box-shadow: var(--shadow-md);
    padding: 26px 24px;
}
.login__brand { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-bottom: 20px; text-align: center; }
.login__foot { margin-top: 18px; display: flex; justify-content: center; }

/* ---------- Blazor error strip ---------- */

#blazor-error-ui {
    display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;
    background: var(--warn-soft); color: var(--warn);
    border-top: 1px solid var(--warn-line);
    padding: 11px 44px 11px 16px; font-size: 14px;
    box-shadow: 0 -2px 10px rgba(0, 0, 0, .12);
}
#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 14px; top: 10px; }

/* ---------- Geo autocomplete (Photon, on the venue form) ---------- */

.geo-suggestions {
    display: flex; flex-direction: column;
    margin: 6px 0 0; border: 1px solid var(--border); border-radius: var(--r-md);
    overflow: hidden; background: var(--surface); box-shadow: var(--shadow-sm);
}
.geo-suggestion {
    display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
    padding: 9px 11px; border: 0; border-bottom: 1px solid var(--border-2);
    background: var(--surface); color: var(--text); font: inherit; text-align: left; cursor: pointer;
    width: 100%;
}
.geo-suggestion:last-child { border-bottom: 0; }
.geo-suggestion:hover { background: var(--accent-soft); }
.geo-suggestion__name { font-weight: 600; font-size: 14px; }
.geo-suggestion__address { font-size: 12.5px; color: var(--text-2); }

/* ================================================================
   Mobile / tablet — below the sidebar breakpoint
   ================================================================ */

@media (max-width: 1023px) {

    /* Closed, the drawer is not merely off-screen — it is visibility:hidden, so its dozen links stay
       out of the tab order. The visibility flip is delayed by the length of the slide so closing
       still animates; opening flips it immediately (0s) or the drawer would slide in invisible. */
    .app__sidebar {
        transform: translateX(-100%);
        visibility: hidden;
        transition: transform .24s cubic-bezier(.32, .72, 0, 1), visibility 0s linear .24s;
        width: min(302px, 86vw);
        box-shadow: var(--shadow-lg);
        will-change: transform;
    }
    .app__main { margin-left: 0; }

    .drawer-toggle:checked ~ .app__sidebar {
        transform: translateX(0);
        visibility: visible;
        transition: transform .24s cubic-bezier(.32, .72, 0, 1), visibility 0s;
    }
    .drawer-toggle:checked ~ .scrim { opacity: 1; visibility: visible; }

    /* Top bar replaces the sidebar's job of "where am I / take me elsewhere". Compact by design:
       56px, against the 302px the old wrapping nav row occupied. */
    .app__topbar {
        position: sticky; top: 0; z-index: 30;
        display: flex; align-items: center; gap: 8px;
        height: var(--topbar-h); padding: 0 8px 0 6px;
        background: var(--surface); border-bottom: 1px solid var(--border);
    }
    .app__topbar .brand { padding: 0 4px; flex: 1; min-width: 0; }
    .app__topbar .brand__sub { display: none; }

    .app__content { padding: 16px 14px 96px; }

    /* Hamburger / close. A label, because the checkbox is what actually holds the state. */
    .drawer-btn {
        display: grid; place-items: center; width: 40px; height: 40px; flex: none;
        border-radius: var(--r-sm); color: var(--text-2); cursor: pointer;
        -webkit-tap-highlight-color: transparent;
    }
    .drawer-btn:hover { background: var(--surface-2); color: var(--text); }

    .app__sidebar .brand { padding: 14px 4px 12px 16px; }
    .drawer-close { margin-right: 8px; }

    /* Everything a thumb has to hit gets a 44px box. */
    .nav__link { padding: 11px 12px; font-size: 15px; min-height: 44px; }
    .btn { min-height: 44px; padding: 10px 16px; }
    .btn--sm { min-height: 38px; }
    .btn--icon { width: 44px; }
    .btn--icon.btn--sm { width: 38px; }
    .seg__item { min-width: 40px; height: 34px; font-size: 13px; }

    /* 16px stops iOS zooming the page when a field takes focus. */
    input, select, textarea { font-size: 16px; padding: 11px 12px; }
    .search input { padding-left: 36px; }
    input[type="checkbox"], input[type="radio"] { width: 20px; height: 20px; }

    h1 { font-size: 21px; }

    .page-header { margin-bottom: 16px; gap: 12px; }
    .page-header__actions { width: 100%; }
    .page-header__actions .btn { flex: 1; }

    .card { border-radius: var(--r-md); }
    .card__header, .toolbar { padding: 12px 14px; }
    .card > :not(.card__header):not(.card__foot):not(.table-wrap):not(.toolbar):not(table):not(.empty):not(.alert) {
        padding-left: 14px; padding-right: 14px;
    }
    .card--pad { padding: 14px; }
    .card__foot { padding: 12px 14px; border-radius: 0 0 var(--r-md) var(--r-md); }

    .toolbar { flex-direction: column; align-items: stretch; }
    .toolbar__grow { min-width: 0; }

    /* ---- Tables become cards ----
       Each row is a self-contained card and each cell carries its own label, taken from
       data-label. The previous build dropped the header row and ran the remaining cells together
       inline, which left rows reading "10. 8. 2026 19:00 Koncerty published" with no way to tell
       which value was which. */
    /* Only a wrap that actually holds a table stops scrolling — the table has become cards, so there
       is nothing left to scroll sideways. The same class is also used to give a wide <pre> (the raw
       JSON on /zber) its own scrollbar, and that one still needs it, most of all on a phone. */
    .table-wrap:has(> table) { overflow-x: visible; }
    table, .table, tbody, tr, td { display: block; width: 100%; }
    thead { display: none; }

    tbody tr {
        border: 1px solid var(--border); border-radius: var(--r-md);
        background: var(--surface); box-shadow: var(--shadow-xs);
        padding: 12px 14px; margin-bottom: 10px;
    }
    tbody tr:last-child { margin-bottom: 0; }
    tbody tr:hover { background: var(--surface); }

    tbody td { border: 0; padding: 0; }

    /* Labelled cells sit as a label/value pair. */
    tbody td[data-label] {
        display: flex; align-items: baseline; gap: 10px;
        padding: 4px 0; border-top: 1px solid var(--border-2); margin-top: 4px;
    }
    tbody td[data-label]::before {
        content: attr(data-label);
        flex: none; width: 38%; max-width: 140px;
        font-size: 11.5px; font-weight: 650; text-transform: uppercase; letter-spacing: .04em;
        color: var(--text-3);
    }

    /* The first cell is the row's identity — it keeps the full width and no label. */
    tbody td:first-child { border-top: 0; margin-top: 0; }
    tbody td[data-label]:first-child { display: block; }
    tbody td[data-label]:first-child::before { display: none; }

    /* A cell explicitly marked as the row title, wherever it sits. */
    tbody td.cell-title { display: block; border-top: 0; margin-top: 0; padding-bottom: 4px; }
    tbody td.cell-title::before { display: none; }

    /* Actions get their own full-width strip at the bottom of the card. */
    tbody td.cell-actions-cell,
    tbody td:has(> .cell-actions) {
        display: block; border-top: 1px solid var(--border-2); margin-top: 8px; padding-top: 10px;
    }
    tbody td:has(> .cell-actions)::before { display: none; }
    .cell-actions { justify-content: stretch; }
    .cell-actions .btn { flex: 1; }

    /* Selection checkbox rides top-right of the card instead of taking a row of its own. */
    tbody tr { position: relative; }
    tbody td.cell-select {
        position: absolute; top: 10px; right: 12px; width: auto; padding: 0; border: 0; margin: 0;
    }
    tbody td.cell-select::before { display: none; }
    tbody tr:has(td.cell-select) td:first-child { padding-right: 34px; }

    .card__foot { flex-direction: column; align-items: stretch; }
    .pager { justify-content: center; }

    .stats { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
    .stat { padding: 12px 14px; }
    .stat__num { font-size: 24px; }

    .bulkbar {
        position: fixed; left: 12px; right: 12px; bottom: 12px; margin: 0;
        flex-direction: column; align-items: stretch;
    }
    .bulkbar .btn { width: 100%; }
    .bulkbar__spacer { display: none; }

    .row--end { flex-direction: column; align-items: stretch; }
    .row--end .btn { width: 100%; }

    .btn-row--end { flex-direction: column; align-items: stretch; }
    .btn-row--end .btn { width: 100%; }

    /* Form footer that follows you down a long edit page.

       column, NOT column-reverse — the same trap .row--end documents above. Pages put the primary
       action last in the DOM, which in normal column order lands it at the bottom of the stack,
       nearest the thumb. Reversing would lift Save to the top and leave Delete under the thumb. */
    .form-actions {
        position: sticky; bottom: 0; z-index: 15;
        display: flex; flex-direction: column; gap: 8px;
        padding: 12px 14px; margin: 16px -14px -96px;
        background: var(--surface); border-top: 1px solid var(--border);
        box-shadow: 0 -4px 16px rgba(0, 0, 0, .07);
    }
    .form-actions .btn { width: 100%; }
}

/* Desktop form footer: plain right-aligned row, nothing sticky. */
@media (min-width: 1024px) {
    .form-actions {
        display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap;
        margin-top: 18px;
    }
    .form-actions--split { justify-content: space-between; }
}

/* Small phones — reclaim the label column in table cards. */
@media (max-width: 400px) {
    tbody td[data-label]::before { width: 42%; }
    .app__content { padding: 14px 12px 96px; }
}

/* ---------- Adding several events at once (/udalosti/viac) ---------- */

/* One row is one event and one column is one field, so the table is far wider than any list here and
   its cells hold controls rather than text. The rules below are all about that: room for the controls,
   a visible difference between a cell you write in and one that is written for you, and an honest
   admission that none of it belongs on a phone. */

.bulk-table th, .bulk-table td { padding: 8px 10px; vertical-align: top; }

/* Sized to the controls, not to the header words: a picker showing "Kaviareň u Vlka" clipped to eight
   characters is a picker nobody can use. Minimums rather than widths, so a wide monitor still spends
   its extra room on the columns that carry words. */
.bulk-table .col-text { min-width: 210px; }
.bulk-table .col-pick { min-width: 170px; }
.bulk-table .col-when { width: 1%; white-space: nowrap; }

/* The column heading and its "same for all" tick, stacked. The tick is a control inside a <thead>,
   which is styled for uppercase labels — it opts out of all of that or it reads as a heading itself. */
.bulk-head { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.check--same {
    text-transform: none; letter-spacing: 0; font-size: 12px; font-weight: 550;
    color: var(--text-3); gap: 6px;
}
.check--same input { width: 15px; height: 15px; }
.check--same:hover { color: var(--text-2); }
.bulk-head:has(input:checked) .check--same { color: var(--accent); font-weight: 650; }

/* A cell whose value is written for it, one row above. The single colour across the whole locked part
   of a column is what says "this is one answer, not eight" at a glance — beats the grey a disabled
   input would otherwise wear, which says only "broken". Both selectors are needed: the fields really
   are disabled, and input:disabled sets its own background. */
.bulk-table .is-bound,
.bulk-table .is-bound:disabled {
    background: var(--accent-soft); color: var(--text-2);
    border-color: var(--accent-line); border-style: dashed;
}

/* Which event the row is, and the error it earned. The message sits under the row it belongs to
   (spec §4) rather than in one heap above the table, where finding the row it means is the reader's
   problem. */
.bulk-table .bulk-row-num { color: var(--text-3); font-size: 12.5px; font-weight: 650; padding-top: 16px; }
.bulk-table tr.bulk-row--bad td { background: var(--danger-soft); }
.bulk-table td.bulk-row-error {
    padding-top: 0; color: var(--danger); font-size: 12.5px; font-weight: 550;
    background: var(--danger-soft);
}

/* Desktop only, said out loud (spec §5). Below the sidebar breakpoint the table is swapped for a note
   and a way out — not hidden behind a scrollbar, which would look like it might work.

   .wide-only deliberately sets no display of its own: it is put on a flex row (a header's actions) as
   well as on plain blocks, and forcing "block" on the former would stack the buttons. Hiding is all it
   ever has to do, and that only happens in the media query below — which comes later in this file than
   every rule it has to beat, all of them single-class like itself. */
.narrow-only { display: none; }
@media (max-width: 1023px) {
    .wide-only { display: none; }
    .narrow-only { display: block; }
}

/* ---------- Motion ---------- */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ---------- Print ---------- */

@media print {
    .app__sidebar, .app__topbar, .scrim, .bulkbar, .page-header__actions { display: none !important; }
    .app__main { margin-left: 0; }
    .card { box-shadow: none; break-inside: avoid; }
}
