/* ============================================================
   vThumbkit v3 - minimalist responsive theme
   Tweak the CSS variables below to re-skin the whole portal.
   ============================================================ */

:root {
    --bg: #0f1115;
    --panel: #171a21;
    --panel-2: #1e222c;
    --border: #272c38;
    --text: #e7eaf0;
    --muted: #8b93a3;
    --accent: #4f8cff;
    --accent-hover: #3a76e8;
    --ok: #2ecc71;
    --warn: #f1c40f;
    --danger: #e74c3c;
    --radius: 10px;
    --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

/* Client-side dark/light toggle (user portal). These two rules force the
   light or dark palette regardless of the admin base theme, so the toggle
   works no matter what the admin has configured. Higher specificity than
   :root, so they win regardless of source order. See vk_theme_toggle_button()
   / js/app.js. (No attribute = use the admin base theme unchanged.) */
html[data-vk-theme="light"] {
    --bg: #f4f6fa;
    --panel: #ffffff;
    --panel-2: #eef1f6;
    --border: #d8dee9;
    --text: #1c2430;
    --muted: #5f6b7c;
    --accent: #2563eb;
    --accent-hover: #1d4fd7;
}
html[data-vk-theme="dark"] {
    --bg: #0f1115;
    --panel: #171a21;
    --panel-2: #1e222c;
    --border: #272c38;
    --text: #e7eaf0;
    --muted: #8b93a3;
    --accent: #4f8cff;
    --accent-hover: #3a76e8;
}

* { box-sizing: border-box; }

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

body {
    margin: 0;
    background-color: var(--bg);
    /* Subtle repeating honeycomb tile behind every page (solid content cards
       sit on top, so text stays readable). Theme-independent. */
    background-image: url("hex-bg.svg");
    background-repeat: repeat;
    background-size: 27.712px 48px;
    color: var(--text);
    font: 16px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

.wrap { max-width: 1100px; margin: 0 auto; padding: 0 16px; }

/* ---------------- Top bar ---------------- */

.topbar {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--bg); /* fallback for browsers without color-mix() (pre-2023 Safari/Firefox/Chrome) */
    background: color-mix(in srgb, var(--bg) 92%, transparent);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border-bottom: 1px solid var(--border);
}

.topbar-in {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
    padding: 10px 16px;
}

.brand {
    font-weight: 700;
    font-size: 17px;
    letter-spacing: 0.2px;
    color: var(--text);
    text-decoration: none;
    white-space: nowrap;
}

.brand-logo {
    display: inline-block;
    height: 26px;
    width: auto;
    vertical-align: middle;
    border-radius: 4px;
}

/* Looping shine sweep across the logo (every ~4s). CSS-only, no JS. */
.brand {
    position: relative;
    display: inline-block;
    overflow: hidden;
}
.brand::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 45%;
    height: 100%;
    background: linear-gradient(105deg, transparent 0%, rgba(255, 255, 255, 0.55) 50%, transparent 100%);
    transform: translateX(-130%) skewX(-20deg);
    animation: vk-logo-shine 4s ease-in-out infinite;
    pointer-events: none;
}
@keyframes vk-logo-shine {
    0%   { transform: translateX(-130%) skewX(-20deg); }
    35%  { transform: translateX(320%) skewX(-20deg); }
    100% { transform: translateX(320%) skewX(-20deg); }
}
@media (prefers-reduced-motion: reduce) {
    .brand::after { animation: none; opacity: 0; }
}

.nav { display: flex; gap: 2px; flex-wrap: wrap; flex: 1; }

.nav a {
    color: var(--muted);
    text-decoration: none;
    padding: 6px 11px;
    border-radius: 8px;
    font-size: 14px;
    white-space: nowrap;
    transition: background 0.15s, color 0.15s;
}

.nav a:hover { color: var(--text); background: var(--panel-2); }
.nav a.active { color: var(--text); background: var(--panel-2); }

/* Icon-only nav link (Logout) — same hit area/style as text links, but a glyph. */
.nav a.nav-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    padding: 6px 0;
}
.nav a.nav-icon svg { display: block; }

/* "Preview portal" link in the admin menu — visually distinct (gold) */
.nav a.nav-preview {
    color: #e8b43a;
    border: 1px dashed rgba(232, 180, 58, 0.55);
    background: rgba(232, 180, 58, 0.06);
}
.nav a.nav-preview:hover {
    background: rgba(232, 180, 58, 0.14);
    color: #f5c400;
}

/* Account menu — the identity part (email / username) is bold so the
   logged-in account is easy to spot. Wraps gracefully on narrow screens. */
.nav a.nav-acct { white-space: normal; }
.nav a.nav-acct .nav-acct-who { font-weight: 700; }

/* Tiny "you're on mobile" indicator (revealed by js/app.js only on mobile). */
.mobile-ind {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    line-height: 1;
    color: var(--warn);
    background: rgba(241, 196, 15, 0.12);
    border: 1px solid rgba(241, 196, 15, 0.4);
    border-radius: 20px;
    padding: 4px 8px;
    margin-right: 2px;
    cursor: default;
}
.mobile-ind[hidden] { display: none; }

/* "Tools" dropdown in the portal top bar (e.g. Calculate SHA-256 Hash) */
.nav .nav-drop { position: relative; display: inline-block; }
.nav .nav-drop-toggle {
    appearance: none;
    background: none;
    border: none;
    color: var(--muted);
    font: inherit;
    font-size: 14px;
    padding: 6px 11px;
    border-radius: 8px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    transition: background 0.15s, color 0.15s;
}
.nav .nav-drop-toggle:hover { color: var(--text); background: var(--panel-2); }
.nav .nav-drop.open .nav-drop-toggle { color: var(--text); background: var(--panel-2); }
.nav .nav-drop-caret { font-size: 10px; transition: transform 0.15s; }
.nav .nav-drop.open .nav-drop-caret { transform: rotate(180deg); }
.nav .nav-drop-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    min-width: 240px;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
    padding: 5px;
    z-index: 40;
    display: none;
}
.nav .nav-drop.open .nav-drop-menu { display: block; }
.nav .nav-drop-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    color: var(--text);
    font: inherit;
    font-size: 14px;
    padding: 9px 12px;
    border-radius: 7px;
    cursor: pointer;
    transition: background 0.12s;
}
.nav .nav-drop-item:hover { background: var(--panel-2); }

.clock {
    font-size: 13px;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

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

main.wrap { padding: 22px 16px 32px; }

/* Admin panel: full-width layout */
.wrap.wide { max-width: none; padding-left: 20px; padding-right: 20px; }

.card {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px 20px;
    margin: 0 auto 18px;
}

.card h1 { font-size: 20px; margin: 0 0 6px; }
.card h2 { font-size: 16px; margin: 0 0 12px; }
.card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.card-head h1 { margin: 0; }
.card-head h2 { margin: 0; }

.head-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}
.head-card h1 { margin: 0; }
/* Button clusters in a card header (Dashboard / Audit log): keep a
   consistent gap between the buttons instead of a single space. */
.head-card .actions { display: flex; align-items: center; gap: 10px; }

/* File-type icon. vk_file_icon() returns an <img class="file-ico"> (from /img);
   the surrounding .file-icon span handles spacing. Base size 18px inline; the
   share page (.share-icon) renders it larger (it's now an <img>, so width/height
   — not font-size — do the scaling). */
.file-icon { margin-right: 6px; }
.file-ico {
    width: 18px;
    height: 18px;
    display: inline-block;
    vertical-align: middle;
    object-fit: contain;
    flex: none;
}
.file-icon.share-icon .file-ico,
.file-ico.share-icon { width: 30px; height: 30px; }

/* Lock indicator on password-protected files (user portal) — own column */
.lock-badge { font-size: 13px; }
.lock-open { color: var(--muted); }

.pw-show {
    font-family: var(--mono);
    font-size: 16px;
    color: var(--accent);
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 4px 10px;
    word-break: break-all;
}

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

/* LightSkyBlue links (dashboard → audit / settings shortcuts) */
a.link-lightsky { color: lightskyblue; }
a.link-lightsky:hover { color: lightskyblue; text-decoration: underline; }

/* ---------------- Notices ---------------- */

.notice {
    padding: 10px 14px;
    border-radius: var(--radius);
    margin-bottom: 16px;
    font-size: 14px;
    border: 1px solid var(--border);
    background: var(--panel-2);
    transition: opacity 0.6s;
}

.notice.ok { border-color: rgba(46, 204, 113, 0.4); color: var(--ok); }
.notice.danger { border-color: rgba(231, 76, 60, 0.45); color: #f29a92; }
/* Persistent, dismissible warning (e.g. "write down your new password").
   Stays until the user closes it — it is NOT auto-dismissed by the flash
   auto-hide script (which targets #vk-flash). */
.notice.warn {
    position: relative;
    border-color: rgba(241, 196, 15, 0.55);
    background: rgba(241, 196, 15, 0.10);
    color: var(--text);
}
.notice.warn strong { color: var(--warn); }
.notice .notice-close {
    position: absolute;
    top: 8px;
    right: 10px;
    border: none;
    background: transparent;
    color: var(--muted);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 2px 6px;
    border-radius: 6px;
}
.notice .notice-close:hover { color: var(--text); background: rgba(255, 255, 255, 0.08); }
.notice.welcome {
    border: none;
    background: var(--panel-2);
    color: var(--text);
    line-height: 1.6;
}
.notice.welcome a { color: var(--accent); }
.notice.welcome h1 {
    margin: 0 0 4px;
    font-size: 17px;
    line-height: 1.3;
    font-weight: 700;
}
.notice.welcome p { margin: 0 0 6px; }
.notice.welcome p:last-child { margin-bottom: 0; }
.notice.announcement {
    border-color: rgba(255, 193, 7, 0.6);
    border-radius: 4px;
    background: gold;
    color: #000;
    font-weight: 700;
    overflow: hidden;
    white-space: nowrap;
    padding-top: 12px;
    padding-bottom: 12px;
}
.notice.announcement .marquee-track {
    display: flex;
    flex-wrap: nowrap;
    width: max-content;
    will-change: transform;
    animation: vk-announcements-scroll 18s linear infinite;
}
.notice.announcement .marquee-item {
    display: inline-block;
    white-space: nowrap;
    flex: 0 0 auto;
}
/* Seamless loop: js/app.js tiles identical copies to fill the bar and sets
   --vk-marquee-step to one copy's measured width, so the end of the loop is
   pixel-identical to the start at any text length (no empty gaps). */
@keyframes vk-announcements-scroll {
    from { transform: translateX(0); }
    to { transform: translateX(calc(-1 * var(--vk-marquee-step, 100%))); }
}
.notice.announcement:hover .marquee-track { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
    .notice.announcement .marquee-track { animation: none; }
}

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

.table-scroll { overflow-x: auto; }

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

th, td {
    text-align: left;
    padding: 9px 12px;
    border-bottom: 1px solid var(--border);
    font-size: 14px;
    vertical-align: top;
}

th {
    color: var(--muted);
    font-weight: 600;
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    white-space: nowrap;
}

tbody tr:hover td { background: var(--panel-2); }

td.fname { font-weight: 600; }
td.desc { color: var(--muted); }
td.actions { white-space: nowrap; }
td.empty, .empty { color: var(--muted); font-style: italic; text-align: center; padding: 22px; }

/* File list on the user portal: fixed layout so the table always fits
   the container — no horizontal scrollbar. Every cell truncates with an
   ellipsis; the full value is shown in a custom tooltip (js/app.js). */
.vk-files { table-layout: fixed; width: 100%; }
.vk-files th, .vk-files td {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
}
/* Uniform row heights so every row lines up regardless of content/buttons */
.vk-files th { height: 38px; font-weight: 700; }
.vk-files td { height: 46px; }
/* User-portal file table: slightly larger text for readability (admin files
   table keeps its denser 14px via .admin-files). */
.vk-files:not(.admin-files) td { font-size: 15px; }
.vk-files:not(.admin-files) .fname, .vk-files:not(.admin-files) .desc { font-size: 15px; }
/* Name column: 18% for the observer file table (unchanged). The MAIN file
   table (class vk-files-main) overrides to auto — its name cell now holds the
   filename + an inline lock badge + a tiny size·upload-date line, so it is the
   flexible column that absorbs leftover space. The two tables share id="vk-files"
   but render in mutually exclusive branches; scoping by class keeps the observer
   table's single-line name cell intact. */
.vk-files th.fname-col { width: 18%; }
.vk-files-main th.fname-col { width: auto; }
/* Name cell: a normal (stretching) <td> whose child .fname-flex is the flex
   row — file icon on the left, then a "body" holding the filename (line 1) and
   the tiny size·upload-date line (line 2). Putting the flex on the <td>
   directly made that cell stop stretching to the row height, so its bottom
   border sat above the other cells' (crooked row line). Wrapping keeps the
   <td> a normal table cell so its border aligns with its neighbours. */
.vk-files-main td.fname { white-space: normal; overflow: hidden; }
/* The icon is taken OUT of flow (position:relative on the span, absolute on the
   img) so its natural 64px height can't inflate the row. The text block alone
   defines the row height; .file-icon stretches to it (align-items:stretch) and
   the img is centered + clamped to that box → the icon is exactly as tall as the
   filename + size/date text. */
.vk-files-main td.fname .fname-flex { display: flex; align-items: stretch; gap: 6px; }
.vk-files-main td.fname .file-icon { flex: none; width: 40px; position: relative; }
.vk-files-main td.fname .file-ico {
    position: absolute;
    top: 0; bottom: 0; left: 0; right: 0;
    margin: auto;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 100%;
}
.vk-files-main td.fname .fname-body { flex: 1 1 auto; min-width: 0; overflow: hidden; }
.vk-files-main td.fname .fname-main {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 600;
}
.vk-files-main td.fname .fname-text {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: bottom;
}
.vk-files-main td.fname .fname-meta {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 11px;
    line-height: 1.3;
    color: var(--muted);
    margin-top: 2px;
    font-weight: 400;
}
.vk-files-main td.fname .lock-badge { margin-right: 5px; font-size: 12px; }
/* User home table: SHA-256 on ONE line (full 64-char hash). The main table
   gets an explicit width so the whole hash + copy button fit; the admin table
   keeps its compact 2-line wrap. (Two classes = higher specificity than the
   base .vk-files hash-col width below, so this wins.) */
.vk-files.vk-files-main th.hash-col, .vk-files.vk-files-main td.hash-cell { width: 486px; }
.vk-files.vk-files-main td.hash-cell .hash { white-space: nowrap; flex: none; }
.vk-files th.lock-col, .vk-files td.lock-col { width: 52px; overflow: visible; text-align: center; white-space: nowrap; }
/* SHA-256: the full 64-char hash is shown, wrapped into two 32-char lines
   (.hash.hash-full) so it stays fully visible in a compact column. */
.vk-files th.hash-col, .vk-files td.hash-cell { width: 284px; overflow: hidden; }
/* Per-column widths so the fixed table distributes space sensibly. The count,
   date, size and category columns are kept narrow; Description (desc-col)
   takes the remaining space. */
.vk-files th.dl-col, .vk-files td.dl-col { width: 60px; text-align: left; }
.vk-files th.size-col, .vk-files td.size-col { width: 88px; white-space: nowrap; }
.vk-files th.date-col, .vk-files td.date-col { width: 156px; white-space: nowrap; }
.vk-files th.cat-col, .vk-files td.cat-col { width: 104px; }
.vk-files th.desc-col { width: auto; }
.vk-files th:last-child, .vk-files td.actions { width: 92px; overflow: visible; white-space: nowrap; }
.vk-files td.actions .btn { padding: 5px 9px; }

/* Admin files table: same fixed-layout + tooltip treatment. The 10 columns are
   fixed-order, so nth-child widths are safe. Name (col 1) is left as the only
   auto column so it absorbs leftover space; the rest are explicit. */
.vk-files.admin-files th:nth-child(2) { width: 90px; }   /* Category */
.vk-files.admin-files th:nth-child(3) { width: 70px; }   /* Size */
.vk-files.admin-files th:nth-child(4) { width: 284px; }  /* SHA-256 (full hash) */
.vk-files.admin-files th:nth-child(5) { width: 44px; }   /* Lock */
.vk-files.admin-files th:nth-child(6) { width: 76px; }   /* Share */
.vk-files.admin-files th:nth-child(7) { width: 48px; }   /* DL */
.vk-files.admin-files th:nth-child(8) { width: 150px; }  /* Uploaded (compact date) */
.vk-files.admin-files th:nth-child(9) { width: 70px; }   /* Status */
.vk-files.admin-files th:first-child { width: auto; }
.vk-files.admin-files th:last-child,
.vk-files.admin-files td.actions { width: 192px; overflow: visible; white-space: nowrap; }

/* Custom tooltip (full value of a truncated table cell) */
.tooltip {
    position: fixed;
    z-index: 100;
    max-width: 340px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 7px 11px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--text);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
    pointer-events: none;
    word-break: break-all;
}
.tooltip[hidden] { display: none; }

/* Copy-share-link button */
.share-btn { line-height: 1; }
.share-btn.copied { color: var(--ok); border-color: rgba(46, 204, 113, 0.5); }

/* SHA-256 cell: a normal (stretching) table cell whose child .hash-flex is the
   flex row (hash + copy button). Flexing the <td> itself stops it matching the
   row height, so its bottom border sits above the other cells' (crooked row
   line) — keeping the <td> a table cell fixes that. `white-space: pre` on the
   hash lets the server-side vk_wrap_hash newline render (2-line) unless a table
   overrides it to nowrap (1-line, user home). */
.vk-files td.hash-cell {
    overflow: hidden;
    text-overflow: clip;
    white-space: normal;
    display: table-cell;
    vertical-align: middle;
}
.vk-files td.hash-cell .hash-flex { display: flex; align-items: center; gap: 6px; }
.vk-files td.hash-cell .hash {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: pre;
    line-height: 1.4;
    font-size: 12px;
    min-width: 0;
}
.vk-files td.hash-cell .copy-hash {
    display: inline-block;
    flex: none;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 5px;
    color: var(--muted);
    font-size: 12px;
    line-height: 1;
    padding: 3px 5px;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
    margin: 0;
}
.copy-hash:hover { color: var(--text); border-color: var(--accent); }
.copy-hash.copied { color: var(--ok); border-color: rgba(46, 204, 113, 0.5); }

/* ---------------- IP access graph (dashboard) ---------------- */

.ip-graph { display: flex; flex-direction: column; gap: 7px; }

.bar-row {
    display: grid;
    grid-template-columns: 150px 1fr 44px;
    align-items: center;
    gap: 10px;
    font-size: 13px;
}

.bar-label {
    font-family: var(--mono);
    font-size: 12px;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bar-track {
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 6px;
    height: 16px;
    overflow: hidden;
}

.bar-fill {
    display: block;
    height: 100%;
    background: linear-gradient(90deg, var(--accent), var(--accent-hover));
    border-radius: 6px 0 0 6px;
    min-width: 2px;
}

.bar-fill.alt {
    background: linear-gradient(90deg, var(--ok), #27ae60);
}

.bar-val { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }

/* ---------------- Share page ---------------- */

.share-card { width: 100%; max-width: 560px; }

.share-head {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}

.share-head h1 { font-size: 20px; margin: 0 0 2px; word-break: break-word; }
.share-head .muted { margin: 0; }

.share-icon { font-size: 30px; line-height: 1; }

.share-meta { width: 100%; border-collapse: collapse; margin-bottom: 14px; }
.share-meta th, .share-meta td {
    padding: 7px 10px;
    border-bottom: 1px solid var(--border);
    font-size: 13.5px;
    text-align: left;
    vertical-align: top;
}
.share-meta th {
    width: 110px;
    color: var(--muted);
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
    font-size: 12px;
    white-space: nowrap;
}
.share-meta tr:last-child th, .share-meta tr:last-child td { border-bottom: none; }

.hash {
    font-family: var(--mono);
    font-size: 12px;
    color: var(--muted);
    word-break: break-all;
    cursor: help;
}

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

.btn {
    display: inline-block;
    padding: 8px 14px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--panel-2);
    color: var(--text);
    font: inherit;
    font-size: 14px;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
    vertical-align: middle;
}

.btn:hover {
    background: var(--border); /* fallback for browsers without color-mix() */
    background: color-mix(in srgb, var(--panel-2), var(--text) 8%);
    border-color: color-mix(in srgb, var(--border), var(--text) 15%);
}
.btn.primary { background: gold; border-color: rgba(255, 193, 7, 0.6); color: #000; font-weight: 700; }
.btn.primary:hover { background: #f5c400; border-color: #e0b400; color: #000; }
.btn.gold { background: gold; border-color: rgba(255, 193, 7, 0.6); color: #000; font-weight: 700; }
.btn.gold:hover { background: #f5c400; border-color: #e0b400; color: #000; }
/* Backup page: all links (e.g. "Settings") in gold to match the gold buttons. */
.backup-page a { color: #e8b43a; }
.backup-page a:hover { color: #f5c400; }
/* Admin Dashboard + Observer board: turn the blue accents (stat numbers,
   graph bars, primary buttons) into gold. Scoped to .dash-board so the rest
   of the app is unaffected. */
.dash-board .stat-num { color: #e8b43a; }
.dash-board .bar-fill { background: linear-gradient(90deg, #f5c400, #e8b43a); }
.dash-board .obs-chart-bar { background: linear-gradient(180deg, #f5c400, #d4a017); }
/* Gold text link (used on the Account page). */
a.link-gold { color: #e8b43a; }
a.link-gold:hover { color: #f5c400; }
.btn.danger { border-color: rgba(231, 76, 60, 0.5); color: #f29a92; }
.btn.danger:hover { background: rgba(231, 76, 60, 0.12); }
.btn.small { padding: 5px 10px; font-size: 13px; }
.btn.block { display: block; width: 100%; }

/* Inline SVG icons inside buttons (download icon) */
.btn svg { vertical-align: -3px; }
.vk-files td.actions .btn svg { display: block; vertical-align: middle; }

.tag {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 600;
    background: var(--panel-2);
    border: 1px solid var(--border);
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.4px;
}

.tag-admin { color: var(--accent); border-color: rgba(79, 140, 255, 0.4); }
.tag-observer { color: #e8b43a; border-color: rgba(232, 180, 58, 0.4); }
.tag-ok { color: var(--ok); border-color: rgba(46, 204, 113, 0.4); }
.tag-danger { color: #f29a92; border-color: rgba(231, 76, 60, 0.45); }

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

form.inline { display: inline-block; margin: 0; }
form.rename-form { display: inline-flex; gap: 6px; }
form.rename-form input { width: 170px; }

.field { display: block; margin-bottom: 12px; }
/* Password field with a show/hide (eye) toggle overlaid on the right. */
.pw-wrap { position: relative; display: block; }
.pw-wrap input { padding-right: 40px; }
.pw-reveal {
    position: absolute;
    top: 50%;
    right: 8px;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    margin: 0;
    background: none;
    border: none;
    border-radius: 6px;
    color: var(--muted);
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
}
.pw-reveal:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); }
.pw-reveal svg { display: block; }
/* Only the "eye" shows by default; the "eye-off" shows while revealed. */
.pw-reveal .icon-eye-off { display: none; }
.pw-reveal.is-shown .icon-eye { display: none; }
.pw-reveal.is-shown .icon-eye-off { display: block; }
.field > span {
    display: block;
    font-size: 12.5px;
    color: var(--muted);
    margin-bottom: 5px;
    font-weight: 600;
}
.field.full { grid-column: 1 / -1; }

input, select, textarea {
    width: 100%;
    background: var(--panel-2);
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: 8px;
    padding: 8px 10px;
    font: inherit;
    font-size: 14px;
}

input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--accent);
}

.login-card input:focus {
    border-color: #e8b43a;
    box-shadow: 0 0 0 3px rgba(232, 180, 58, 0.25);
}

input::placeholder, textarea::placeholder {
    color: var(--muted);
    opacity: 1;
}

textarea { resize: vertical; min-height: 70px; }

input.search { width: 220px; }

/* Clear (×) button inside filter inputs */
.filter-wrap { position: relative; display: inline-block; }
.filter-wrap .search { padding-right: 26px; }
.filter-clear {
    position: absolute;
    right: 5px;
    top: 50%;
    transform: translateY(-50%);
    width: 18px;
    height: 18px;
    line-height: 16px;
    padding: 0;
    border: none;
    background: none;
    color: var(--muted);
    font-size: 15px;
    cursor: pointer;
    border-radius: 50%;
}
.filter-clear:hover { color: var(--text); background: var(--border); }

.pw-row { display: flex; gap: 8px; align-items: center; }
.pw-row input { flex: 1; }
.pw-row input[type="checkbox"] { flex: 0 0 auto; width: auto; }
.pw-toggle { flex: 0 0 auto; }

/* ---------------- Searchable combobox (organization picker) ---------------- */

.combobox { position: relative; }

.combobox-list {
    position: absolute;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    z-index: 30;
    max-height: 220px;
    overflow-y: auto;
    margin: 0;
    padding: 4px;
    list-style: none;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}

.combobox-list[hidden] { display: none; }

.combobox-list li {
    padding: 7px 10px;
    border-radius: 6px;
    font-size: 14px;
    cursor: pointer;
}

.combobox-list li:hover, .combobox-list li.hl { background: var(--panel-2); color: var(--text); }

.combobox-list li[data-id=""] { color: var(--muted); font-style: italic; }

/* ---------------- Modal (file password prompt) ---------------- */

.modal {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 50;
    padding: 16px;
}

.modal[hidden] { display: none; }

.modal-box { width: 100%; max-width: 360px; margin: 0; }
.modal-box h3 { margin: 0 0 4px; }
.modal-box .actions { margin-top: 4px; }

/* Logout confirmation modal */
.logout-box { max-width: 380px; text-align: center; }
.logout-box h3 { margin: 0 0 4px; font-size: 18px; }
.logout-box .muted { margin: 0 0 14px; font-size: 13.5px; }
.logout-box .actions { display: flex; gap: 10px; justify-content: center; }
.logout-box .actions .btn { flex: 1; }
.logout-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: color-mix(in srgb, #e8b43a 14%, transparent);
    color: #e8b43a;
    margin-bottom: 12px;
}
.logout-icon svg { width: 28px; height: 28px; }

/* Progress overlay (backup ZIP / Telegram send) */
.vk-progress {
    position: fixed;
    inset: 0;
    z-index: 90; /* above modals (z-50) so it covers the confirm dialog */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(2px);
}
.vk-progress[hidden] { display: none; }
.vk-progress-box {
    max-width: 420px;
    text-align: center;
    padding: 26px 24px;
}
.vk-progress-spin {
    width: 48px;
    height: 48px;
    margin: 0 auto 14px;
    border-radius: 50%;
    border: 4px solid rgba(232, 180, 58, 0.2);
    border-top-color: #e8b43a;
    animation: vk-progress-rotate 0.8s linear infinite;
}
@keyframes vk-progress-rotate { to { transform: rotate(360deg); } }
.vk-progress-msg { margin: 0 0 4px; font-size: 15px; font-weight: 700; }
.vk-progress-sub { margin: 0; font-size: 12.5px; }

/* ---------------- SHA-256 verification tool (user portal) ---------------- */

.sha-modal-box { max-width: 520px; }
.sha-modal-box .field { margin-bottom: 6px; }
.sha-step { margin-top: 10px; }

/* Custom file picker — replaces the native <input type=file> look. The real
   input stays in the DOM (visually hidden) so click-to-browse and the existing
   change-handler still work; drag-and-drop is handled in app.js. */
.sha-pick-label { margin: 0 0 8px; font-size: 14px; }
.sha-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 172px;
    margin: 0;
    padding: 26px 20px;
    text-align: center;
    cursor: pointer;
    position: relative;
    background: var(--panel-2);
    border: 2px dashed var(--border);
    border-radius: 12px;
    transition: border-color .15s ease, background .15s ease, transform .05s ease;
}
.sha-drop:hover { border-color: #e8b43a; background: color-mix(in srgb, #e8b43a 10%, var(--panel-2)); }
.sha-drop:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(79, 140, 255, .25); }
.sha-drop.dragover {
    border-color: var(--accent);
    border-style: solid;
    background: color-mix(in srgb, var(--accent) 16%, var(--panel-2));
    transform: scale(1.005);
}
/* the hidden native input (still focusable / clickable via label[for]) */
.sha-file-input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.sha-drop-icon { color: #e8b43a; display: inline-flex; }
.sha-drop-icon svg { width: 46px; height: 46px; }
.sha-drop-file {
    font-size: 14px;
    font-weight: 700;
    color: var(--text);
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sha-drop-title { font-size: 15px; font-weight: 600; color: #e8b43a; }
.sha-drop-sub { font-size: 12.5px; color: var(--muted); }
.sha-drop-sub b { color: #e8b43a; font-weight: 600; }
/* When a file is chosen, drop the "browse" affordance and show the filename. */
.sha-drop.has-file .sha-drop-icon,
.sha-drop.has-file .sha-drop-title,
.sha-drop.has-file .sha-drop-sub { display: none; gap: 0; }
.sha-drop.has-file { min-height: 96px; padding: 20px; border-style: solid; border-color: var(--ok); background: color-mix(in srgb, var(--ok) 10%, var(--panel-2)); }
.sha-drop.has-file .sha-drop-file { display: inline-block; }

.sha-prog-file {
    margin: 0 0 8px;
    font-size: 13px;
    color: var(--text);
    word-break: break-all;
}
.sha-progress-track {
    height: 12px;
    border-radius: 7px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    overflow: hidden;
}
.sha-progress-fill {
    display: block;
    height: 100%;
    width: 0;
    background: linear-gradient(90deg, var(--accent), var(--ok));
    border-radius: 7px 0 0 7px;
    transition: width 0.1s linear;
}
.sha-progress-meta { margin: 6px 0 10px; font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }

.sha-verdict { margin: 0 0 10px; font-weight: 700; font-size: 14px; }
.sha-verdict.ok { color: var(--ok); }
.sha-verdict.bad { color: var(--danger); }
.sha-verdict.info { color: var(--warn); }
.sha-hash-row { display: flex; gap: 8px; align-items: stretch; margin-bottom: 6px; }
.sha-hash {
    flex: 1 1 auto;
    min-width: 0;
    display: block;
    padding: 9px 10px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    font-family: var(--mono);
    font-size: 13px;
    line-height: 1.4;
    word-break: break-all;
    user-select: all;
    cursor: text;
}
#sha-copy { flex: 0 0 auto; }
#sha-expected { margin: 4px 0 2px; word-break: break-all; }

/* ---------------- First-time quick-start tour ---------------- */

.vk-tour-overlay {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: rgba(0, 0, 0, 0.62);
    animation: vk-tour-fade 0.25s ease;
}
/* Spotlight cut-out: a transparent hole + glowing ring over the target. */
.vk-tour-ring {
    position: fixed;
    border-radius: 10px;
    box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.62), 0 0 0 3px #e8b43a, 0 0 18px 2px rgba(232, 180, 58, 0.5);
    background: transparent;
    transition: left 0.28s ease, top 0.28s ease, width 0.28s ease, height 0.28s ease;
    pointer-events: none;
}
.vk-tour-card {
    position: fixed;
    max-width: 340px;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 14px 16px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
    color: var(--text);
    font-size: 14px;
    line-height: 1.5;
}
.vk-tour-card-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 6px;
}
.vk-tour-title { font-weight: 700; font-size: 15px; color: #e8b43a; }
.vk-tour-count { font-size: 12px; color: var(--muted); white-space: nowrap; }
.vk-tour-text { margin-bottom: 12px; }
.vk-tour-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
.vk-tour-skip {
    background: none;
    border: none;
    color: var(--muted);
    font-size: 13px;
    cursor: pointer;
    padding: 6px 4px;
    margin-right: auto;
}
.vk-tour-skip:hover { color: var(--text); text-decoration: underline; }
.vk-tour-next {
    background: var(--panel-2);
    border: 1px solid var(--border);
    color: var(--text);
    font-size: 13px;
    font-weight: 600;
    padding: 6px 14px;
    border-radius: 8px;
    cursor: pointer;
}
.vk-tour-next:hover { border-color: var(--accent); }
.vk-tour-done { background: #e8b43a; border-color: #e8b43a; color: #000; }
.vk-tour-done:hover { background: #f5c400; border-color: #e0b400; }
@keyframes vk-tour-fade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .vk-tour-overlay { animation: none; }
    .vk-tour-ring { transition: none; }
}

/* Download nudge: after a user sits on the home page without downloading,
   spotlight the Download button (same gold overlay/ring as the tour) with a
   short "where to download" pointer. Dismissed by the X / Escape / any download. */
.vk-nudge-overlay {
    position: fixed;
    inset: 0;
    z-index: 210;
    background: rgba(0, 0, 0, 0.62);
    animation: vk-tour-fade 0.25s ease;
}
.vk-nudge-ring {
    position: fixed;
    border-radius: 8px;
    background: transparent;
    pointer-events: none;
    box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.62), 0 0 0 3px #e8b43a, 0 0 18px 3px rgba(232, 180, 58, 0.55);
    animation: vk-nudge-pulse 1.8s ease-in-out infinite;
}
.vk-nudge-card {
    position: fixed;
    max-width: 320px;
    background: var(--panel);
    border: 1px solid rgba(232, 180, 58, 0.5);
    border-radius: 10px;
    padding: 13px 15px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
    color: var(--text);
    font-size: 14px;
    line-height: 1.5;
    z-index: 211;
}
.vk-nudge-title { font-weight: 700; font-size: 15px; color: #e8b43a; margin-bottom: 5px; }
.vk-nudge-x {
    position: absolute;
    top: 6px;
    right: 9px;
    background: none;
    border: none;
    color: var(--muted);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 2px 4px;
}
.vk-nudge-x:hover { color: var(--text); }
.vk-nudge-actions { display: flex; justify-content: flex-end; margin-top: 10px; }
.vk-nudge-dismiss {
    background: #e8b43a;
    border: 1px solid #e8b43a;
    color: #000;
    font-size: 13px;
    font-weight: 600;
    padding: 6px 14px;
    border-radius: 8px;
    cursor: pointer;
}
.vk-nudge-dismiss:hover { background: #f5c400; }
@keyframes vk-nudge-pulse {
    0%, 100% { box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.62), 0 0 0 3px #e8b43a, 0 0 18px 3px rgba(232, 180, 58, 0.55); }
    50%      { box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.62), 0 0 0 3px #f5c400, 0 0 28px 8px rgba(245, 196, 0, 0.7); }
}
@media (prefers-reduced-motion: reduce) {
    .vk-nudge-overlay { animation: none; }
    .vk-nudge-ring { animation: none; }
}

.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0 14px; }
.stack { display: flex; flex-direction: column; gap: 12px; }

.check {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 0;
    font-size: 14px;
    cursor: pointer;
}

.check input { width: auto; }

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

.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 14px;
    margin-bottom: 18px;
}

.stat { text-align: center; padding: 16px; margin: 0; }
.stat-num { font-size: 28px; font-weight: 700; color: var(--accent); line-height: 1.1; }
.stat-label { color: var(--muted); font-size: 13px; margin-top: 4px; }

.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.two-col .card { margin-bottom: 18px; }

/* ---------------- Collapsible security card (dashboard) ---------------- */

/* Native <details>/<summary> card, collapsed by default. The whole header is
   the click target; a small blinking dot signals a brute-force spike even
   while collapsed. */
details.sec-card {
    display: block;
    margin: 0 0 16px;
    padding: 0; /* summary + body provide their own padding */
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden; /* clip the rounded corners of summary/body backgrounds */
}
details.sec-card > summary.sec-summary {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    cursor: pointer;
    list-style: none;           /* hide the default disclosure triangle */
    user-select: none;
}
details.sec-card > summary.sec-summary::-webkit-details-marker { display: none; }
details.sec-card > summary.sec-summary:hover { background: var(--panel-2); }
details.sec-card > summary.sec-summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.sec-summary .sec-title {
    margin: 0;
    font-size: 16px;
    font-weight: 700;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Inline status hint (icon + short text) — always visible, even collapsed. */
.sec-summary .sec-hint {
    flex: 0 0 auto;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 45%;
}
/* Blinking alert dot — small & subtle (amber) when there are recent failed
   logins, red & more prominent on a brute-force spike. Only rendered when
   there's something to flag, so a clean dashboard shows no dot at all. */
.sec-blink {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--warn);
    box-shadow: 0 0 0 0 rgba(241, 196, 15, 0.5);
    animation: vk-sec-blink 1.6s ease-in-out infinite;
}
.sec-blink.spike {
    width: 12px;
    height: 12px;
    background: var(--danger);
    box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.55);
    animation: vk-sec-blink 1.1s ease-in-out infinite;
}
@keyframes vk-sec-blink {
    0%, 100% { opacity: 1;   box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.5); }
    50%      { opacity: 0.3; box-shadow: 0 0 0 7px rgba(231, 76, 60, 0); }
}
/* Chevron that flips when the card is open. */
.sec-caret {
    flex: 0 0 auto;
    color: var(--muted);
    font-size: 13px;
    transition: transform 0.18s ease;
}
details[open].sec-card > summary.sec-summary .sec-caret { transform: rotate(90deg); }
/* Card body — explicitly hidden while collapsed (don't rely on the native
   <details> UA rule, which can be overridden by the .card display rules). */
details.sec-card:not([open]) > .sec-body { display: none; }
.sec-body {
    padding: 0 16px 14px;
    border-top: 1px solid var(--border);
    margin-top: 2px;
    padding-top: 12px;
}
details[open].sec-card > summary.sec-summary { border-radius: 0; }
@media (max-width: 560px) {
    .sec-summary .sec-hint { max-width: 32%; font-size: 12px; }
}

/* ---------------- Collapsible security card (dashboard) END ---------------- */

/* ---------------- Observer control board ---------------- */

.obs-controls { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.obs-range { display: flex; gap: 4px; }
.obs-range .btn { padding: 5px 11px; }

.obs-live { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--muted); }
.obs-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--ok); animation: vk-obs-pulse 1.8s ease-in-out infinite; }
.obs-live-note { font-size: 11px; opacity: 0.7; }
@keyframes vk-obs-pulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.35; transform: scale(0.8); } }

.obs-stats { margin-top: 2px; }
.obs-stats .stat { padding: 14px; }
.obs-stats .stat-num { font-size: 26px; }
.obs-sub { display: block; font-size: 11px; margin-top: 2px; }

/* 24h activity chart */
.obs-chart { display: flex; align-items: flex-end; gap: 3px; height: 130px; padding-top: 18px; }
.obs-chart-col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }
.obs-chart-val { font-size: 9px; color: var(--muted); height: 14px; line-height: 14px; }
.obs-chart-val.off { visibility: hidden; }
.obs-chart-bar { display: block; width: 100%; max-width: 18px; background: linear-gradient(180deg, var(--accent), var(--accent-hover)); border-radius: 3px 3px 0 0; min-height: 2px; }
.obs-chart-h { font-size: 9px; color: var(--muted); height: 14px; line-height: 14px; }

/* Completion progress bars */
.completion-cell { min-width: 160px; white-space: nowrap; }
.obs-bar-track { display: inline-block; width: 100px; max-width: 90px; height: 9px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; vertical-align: middle; margin-right: 8px; }
.obs-bar-fill { display: block; height: 100%; border-radius: 6px; }
.obs-bar-fill.ok { background: linear-gradient(90deg, var(--ok), #27ae60); }
.obs-bar-fill.mid { background: linear-gradient(90deg, #e8b43a, #d4a017); }
.obs-bar-fill.none { background: var(--border); }
.obs-bar-val { font-size: 12.5px; font-weight: 600; vertical-align: middle; }

@media (max-width: 640px) {
    .obs-chart { height: 90px; }
    .obs-bar-track { width: 70px; }
    .completion-cell { min-width: 130px; }
}


/* ---------------- Column order editor (Admin → Columns) ---------------- */

.col-order { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.col-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 8px;
}
.col-row.col-dragging { opacity: 0.45; border-style: dashed; border-color: var(--accent); }
.col-grip { cursor: grab; color: var(--muted); user-select: none; letter-spacing: 1px; }
.col-row:active .col-grip { cursor: grabbing; }
.col-check { margin: 0; flex: 1 1 auto; display: flex; align-items: center; gap: 8px; }
.col-move { display: flex; gap: 4px; }
.col-move .btn { padding: 3px 8px; line-height: 1; }
/* Per-row order inputs live inside the row (hidden); form serialization
   follows DOM row order automatically. */
.col-row input[name="col_order[]"] { display: none; }

/* ---------------- Menu order editor (Admin → Menu) ----------------
   Reuses the column-row drag/▲▼ machinery; rows carry nav_order[] / 
   user_nav_order[] instead of col_order[]. The label (not a checkbox)
   fills the middle, so flex it. */
.nav-row input[name="nav_order[]"],
.nav-row input[name="user_nav_order[]"] { display: none; }
.nav-row .nav-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
    font-size: 14px;
}
.nav-order { max-width: 440px; }

/* ---------------- File reorder editor (Admin → Files) ---------------- */

.file-order { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.file-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 8px;
}
.file-row.file-dragging { opacity: 0.45; border-style: dashed; border-color: var(--accent); }
.file-grip { cursor: grab; color: var(--muted); user-select: none; letter-spacing: 1px; }
.file-row:active .file-grip { cursor: grabbing; }
.file-ord { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
.file-move { display: flex; gap: 4px; flex: 0 0 auto; }
.file-move .btn { padding: 3px 8px; line-height: 1; }
/* Per-row order inputs live inside the row (hidden); form serialization
   follows DOM row order automatically. */
.file-row input[name="file_order[]"] { display: none; }

/* ---------------- World activity map (admin dashboard + observer) -------- */

.vk-map-card .vk-map {
    position: relative;
    margin-top: 4px;
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
    background: var(--panel-2);
}
.vk-map-canvas {
    width: 100%;
    background: var(--bg);
    /* Leaflet container needs an explicit size; height set by JS. */
    z-index: 0;
}
.vk-map-canvas .leaflet-container {
    background: var(--bg);
    font: inherit;
}
/* OSM tiles are the base (vk-map-remote). When tiles fail and we fall back to
   the bundled GeoJSON world, the tile pane is simply empty. */
.vk-map-canvas.vk-map-remote .leaflet-tile-pane {
    /* Slightly dim the basemap so the accent-colored download dots pop. */
    opacity: 0.85;
}
.vk-map-status {
    padding: 6px 12px;
    font-size: 12px;
    border-top: 1px solid var(--border);
    background: var(--panel-2);
}
.vk-map-unres {
    padding: 8px 12px;
    border-top: 1px solid var(--border);
    background: var(--panel);
}
.vk-map-unres[hidden] { display: none; }
.vk-map-unres-label {
    font-size: 12px;
    color: var(--muted);
    margin-bottom: 6px;
}
.vk-map-unres-list { display: flex; flex-wrap: wrap; gap: 6px; }
.vk-map-unres-chip {
    display: inline-block;
    padding: 2px 8px;
    font-size: 12px;
    font-family: var(--mono);
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: help;
}
.vk-map-unres-chip.vk-map-unres-more {
    color: var(--muted);
    cursor: default;
}

/* Leaflet theming to match the portal (tooltips + popups). */
.leaflet-control-zoom a {
    background: var(--panel);
    color: var(--text);
    border-color: var(--border) !important;
}
.leaflet-control-zoom a:hover { background: var(--panel-2); }
.vk-map .leaflet-tooltip {
    background: var(--panel);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
    font-size: 12px;
    padding: 3px 8px;
}
.vk-map .leaflet-tooltip-top:before { border-top-color: var(--border); }
.vk-map .leaflet-popup-content-wrapper {
    background: var(--panel);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.vk-map .leaflet-popup-tip { background: var(--panel); }
.vk-map .leaflet-popup-close-button { color: var(--muted) !important; }
.vk-map-pop { min-width: 190px; max-width: 260px; }
.vk-map-pop-ip {
    font-family: var(--mono);
    font-size: 14px;
    font-weight: 700;
    color: var(--accent);
    margin-bottom: 2px;
    word-break: break-all;
}
.vk-map-pop-loc { font-size: 12.5px; margin-bottom: 6px; }
.vk-map-pop-stats {
    font-size: 12.5px;
    padding: 6px 8px;
    background: var(--panel-2);
    border-radius: 6px;
    margin-bottom: 4px;
}
.vk-map-pop-last { font-size: 11.5px; color: var(--muted); }

/* "Download again" button shown in the download-manager row when a file is
   already downloaded from the user's IP. */
.vk-dl-redownload {
    display: inline-block;
    margin-top: 8px;
    padding: 6px 14px;
    font-size: 13px;
    font-weight: 700;
    color: #000;
    background: gold;
    border: 1px solid rgba(255, 193, 7, 0.6);
    border-radius: 8px;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    transition: transform 0.1s ease, box-shadow 0.1s ease;
}
.vk-dl-redownload:hover {
    background: #f5c400;
    border-color: #e0b400;
    color: #000;
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.vk-dl-redownload:active {
    transform: translateY(0);
}

@media (max-width: 640px) {
    .vk-map-canvas { height: 320px !important; }
}

/* ---------------- In-browser download manager (user portal) ----------------
   Live progress toasts pinned to the bottom-right corner. Multiple files can
   download at once; each gets its own row with a progress bar + status. */
.vk-dl-panel {
    position: fixed;
    right: 14px;
    bottom: 14px;
    z-index: 400;
    width: min(340px, calc(100vw - 28px));
    max-height: min(60vh, 420px);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
    animation: vk-dl-in 0.22s ease;
}
.vk-dl-panel[hidden] { display: none; }
@keyframes vk-dl-in {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}
.vk-dl-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 9px 12px;
    border-bottom: 1px solid var(--border);
    background: var(--panel-2);
}
.vk-dl-head-title { font-weight: 700; font-size: 13px; }
.vk-dl-clear {
    appearance: none;
    background: none;
    border: 1px solid var(--border);
    color: var(--muted);
    border-radius: 6px;
    padding: 2px 8px;
    font-size: 11.5px;
    cursor: pointer;
}
.vk-dl-clear:hover { color: var(--text); border-color: var(--accent); }
.vk-dl-list { overflow-y: auto; padding: 6px; display: flex; flex-direction: column; gap: 6px; }
.vk-dl-item {
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 9px;
    padding: 8px 10px;
}
.vk-dl-item.error { border-color: var(--danger); }
.vk-dl-item.done { opacity: 0.75; }
.vk-dl-top { display: flex; align-items: center; gap: 8px; }
.vk-dl-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12.5px;
    font-weight: 600;
}
.vk-dl-abort {
    flex: 0 0 auto;
    appearance: none;
    background: none;
    border: 1px solid var(--border);
    color: var(--muted);
    border-radius: 6px;
    padding: 1px 7px;
    font-size: 11px;
    cursor: pointer;
}
.vk-dl-abort:hover { color: var(--danger); border-color: var(--danger); }
.vk-dl-track {
    height: 6px;
    margin: 7px 0 4px;
    background: var(--bg);
    border-radius: 4px;
    overflow: hidden;
}
.vk-dl-fill {
    display: block;
    height: 100%;
    width: 0%;
    border-radius: 4px;
    background: linear-gradient(90deg, var(--accent), var(--accent-hover));
    transition: width 0.15s ease;
}
.vk-dl-item.done .vk-dl-fill { background: linear-gradient(90deg, var(--ok), #27ae60); }
.vk-dl-item.error .vk-dl-fill { background: var(--danger); }
.vk-dl-meta {
    font-size: 11px;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.vk-dl-item.error .vk-dl-meta { color: var(--danger); }
.vk-dl-link { display: none; }
@media (max-width: 480px) {
    .vk-dl-panel { right: 8px; left: 8px; bottom: 8px; width: auto; max-height: 55vh; }
}

/* ---------------- Login (compact) ---------------- */
.login-wrap {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 14px;
}

/* Vignette: a fixed full-viewport overlay that darkens the edges of the
   background toward the corners while keeping the center (behind the login
   card) bright. pointer-events:none so it never blocks the form. Scopes to
   the login page only, since only this page has .login-wrap. */
.login-wrap::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: radial-gradient(
        ellipse 95% 85% at 50% 42%,
        transparent 38%,
        rgba(0, 0, 0, 0.30) 72%,
        rgba(0, 0, 0, 0.58) 100%
    );
}

.login-card { width: 100%; max-width: 320px; padding: 20px 22px 14px; }

.login-brand {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 2px 10px;
    margin-bottom: 14px;
}

.login-logo { display: block; max-width: 150px; max-height: 34px; width: auto; height: auto; }

/* Login-page logo gets the same looping shine sweep as the top-bar logo. */
.login-brand { position: relative; overflow: hidden; }
.login-brand::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 45%;
    height: 100%;
    background: linear-gradient(105deg, transparent 0%, rgba(255, 255, 255, 0.55) 50%, transparent 100%);
    transform: translateX(-130%) skewX(-20deg);
    animation: vk-logo-shine 4s ease-in-out infinite;
    pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
    .login-brand::after { animation: none; opacity: 0; }
}

.login-brand h1 { font-size: 17px; margin: 0; }
.login-brand h1 span { color: var(--accent); font-size: 11px; }

.login-card .field { margin-bottom: 10px; }
.login-card .btn.block { margin-top: 2px; }

/* Settings sub-tabs (each settings section is a tab) */
.subtabs { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 16px; padding: 0; list-style: none; }
.subtabs a {
    display: inline-block;
    padding: 7px 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--panel);
    color: var(--muted);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.subtabs a:hover { color: var(--text); border-color: #e8b43a; }
.subtabs a.active { background: gold; border-color: rgba(255, 193, 7, 0.6); color: #000; font-weight: 700; }

/* Columns menu: gold checkboxes (the column-visibility toggles). */
#col-order input[type="checkbox"] { accent-color: #d4a017; }

/* Settings tab: gold checkboxes (maintenance, trust-proxy, captcha, theme, telegram). */
.settings-page input[type="checkbox"] { accent-color: #d4a017; }

/* Login captcha (enabled via Admin → Settings) */
.captcha-field .captcha-row { display: flex; align-items: center; gap: 8px; }
.captcha-input {
    flex: 1 1 auto;
    box-sizing: border-box;
    height: 42px;
    min-width: 0;
    font-family: var(--mono);
    font-size: 15px;
    letter-spacing: 2px;
    text-transform: uppercase;
    text-align: center;
}
.captcha-img {
    flex: 0 0 auto;
    box-sizing: border-box;
    height: 42px;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: #f5f6fa;
}
.captcha-refresh {
    flex: 0 0 auto;
    width: 42px;
    height: 42px;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--panel-2);
    color: var(--text);
    font-size: 18px;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, transform 0.4s;
}
.captcha-refresh:hover { border-color: var(--accent); color: var(--accent); transform: rotate(180deg); }
.captcha-hint { margin: 6px 0 0; font-size: 12px; }
.captcha-hint a { color: #e8b43a; }
.captcha-hint a:hover { color: #f5c400; text-decoration: underline; }

/* Settings → Security: captcha appearance tuning (live preview + sliders). */
.captcha-tune-grid { display: grid; grid-template-columns: 220px 1fr; gap: 18px; align-items: start; }
@media (max-width: 640px) { .captcha-tune-grid { grid-template-columns: 1fr; } }
.captcha-tune-preview { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.captcha-preview-img { width: 200px; height: auto; border: 1px solid var(--border); border-radius: 8px; background: #f5f6fa; image-rendering: auto; }
.captcha-tune-controls .field { margin-bottom: 10px; }
input[type="range"] {
    -webkit-appearance: none; appearance: none;
    width: 100%; height: 6px; border-radius: 4px;
    background: var(--border); outline: none; padding: 0; border: none;
}
input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: 18px; height: 18px; border-radius: 50%;
    background: gold; border: 2px solid #d4a017; cursor: pointer;
}
input[type="range"]::-moz-range-thumb {
    width: 18px; height: 18px; border-radius: 50%;
    background: gold; border: 2px solid #d4a017; cursor: pointer;
}

/* Anti-bot honeypot: kept in the DOM (so bots still see & fill it) but
   visually hidden from humans. Uses clip instead of display:none because
   some bots ignore display:none fields. */
.vk-hp {
    position: absolute !important;
    left: -9999px !important;
    top: -9999px !important;
    width: 1px;
    height: 1px;
    overflow: hidden;
    white-space: nowrap;
    opacity: 0;
}

/* ---------------- Recovery codes (self-service password reset) ---------------- */

/* "Forgot password?" link under the sign-in form. */
.login-forgot { margin: 12px 0 0; text-align: center; font-size: 13px; }
.login-forgot a { color: #e8b43a; }
.login-forgot a:hover { color: #f5c400; text-decoration: underline; }
.recovery-card { max-width: 420px; }
.recovery-card h2 { margin: 0 0 6px; font-size: 18px; }
.recovery-card .muted { font-size: 13.5px; }
.recovery-card .field-hint { display: block; margin-top: 5px; font-size: 12px; }
.recovery-no-code, .recovery-back { font-size: 12.5px; margin: 14px 0 0; text-align: center; }
.recovery-back a { color: #e8b43a; }
.recovery-back a:hover { color: #f5c400; text-decoration: underline; }

/* One-time "here are your recovery codes" box (Account page / admin Users). */
.recovery-box {
    margin: 14px 0;
    padding: 14px 16px;
    border: 1px solid var(--ok);
    border-left: 4px solid var(--ok);
    border-radius: 10px;
    background: color-mix(in srgb, var(--ok) 9%, var(--panel-2));
}
.recovery-box-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.recovery-box-title { font-weight: 700; font-size: 15px; }
.recovery-box-warn { margin: 8px 0 10px; font-size: 13px; }
.recovery-codes {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 12px;
    background: var(--bg);
    border: 1px dashed var(--border);
    border-radius: 8px;
}
.recovery-code {
    font-family: var(--mono);
    font-size: 15px;
    font-weight: 700;
    letter-spacing: .12em;
    padding: 6px 10px;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 6px;
    user-select: all;
    color: var(--text);
}
.recovery-box-tip { font-size: 12.5px; margin: 10px 0 0; }

/* Admin users table: remaining-code count. */
.rc-count {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 12px;
    font-size: 12px;
    font-weight: 700;
    background: color-mix(in srgb, var(--ok) 14%, transparent);
    color: var(--ok);
    border: 1px solid color-mix(in srgb, var(--ok) 35%, transparent);
}

@media print {
    .recovery-codes { border: 1px solid #000; background: #fff; }
    .recovery-code { border-color: #000; background: #fff; color: #000; }
}

/* ---------------- Rich content pages ---------------- */

.content-page .rich { color: var(--text); }
.rich p { margin: 0 0 12px; }
.rich ul, .rich ol { padding-left: 22px; margin: 0 0 12px; }
.rich a { color: var(--accent); }
/* Contact page: make the Mattermost link stand out in gold (matches .link-gold). */
.contact-page .rich a { color: #e8b43a; }
.contact-page .rich a:hover { color: #f5c400; }
.rich code {
    font-family: var(--mono);
    font-size: 13px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 5px;
    padding: 1px 5px;
}
.rich h1, .rich h2, .rich h3 { margin: 18px 0 8px; }

/* FAQ page: visually distinguish questions from answers (gold accent) */
.faq-page .rich p.q {
    color: #e8b43a;
    font-weight: 600;
    margin: 16px 0 6px;
}
.faq-page .rich p.q strong { color: #e8b43a; }
.faq-page .rich p.q:first-child { margin-top: 0; }
.faq-page .rich p.a {
    background: var(--panel-2);
    border-left: 3px solid #e8b43a;
    border-radius: 6px;
    padding: 8px 12px;
    margin-bottom: 10px;
}
.faq-page .rich p.a:last-child { margin-bottom: 0; }
/* Fallback for legacy content without the q/a classes */
.faq-page .rich strong { color: indianred; }

/* ---------------- Footer ---------------- */

.site-footer {
    border-top: 1px solid var(--border);
    padding: 16px 0 28px;
    color: var(--muted);
    font-size: 13px;
}

/* ---------------- Responsive ---------------- */

@media (max-width: 860px) {
    .two-col { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
    .topbar-in { gap: 8px; padding: 10px 12px; }
    .nav { order: 3; flex-basis: 100%; }
    .nav a { padding: 5px 9px; font-size: 13px; }
    .clock { margin-left: auto; }
    .card-head { flex-direction: column; align-items: stretch; }
    input.search { width: 100%; }
    .filter-wrap { display: block; }
    th, td { padding: 8px 9px; font-size: 13px; }
}

/* ---------------- Upload (Admin → Files) — dropzone + one-time code ------- */

.up-card { padding: 18px 20px; }
.up-card h2 { margin-bottom: 4px; }
.up-card > .muted { margin-bottom: 14px; }

/* one-time upload code box */
.up-code-box {
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 12px 14px;
    margin-bottom: 16px;
}
.up-code-label { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; margin-bottom: 8px; }
.up-code-title { font-weight: 700; font-size: 13px; }
.up-code-hint { font-size: 12px; color: var(--muted); }
.up-code-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.up-code-display {
    font-family: var(--mono);
    font-size: 24px;
    letter-spacing: 5px;
    font-weight: 700;
    color: #b8860b;
    background: #fffbea;
    border: 1px dashed #e8b43a;
    border-radius: 8px;
    padding: 6px 14px;
    cursor: pointer;
    user-select: all;
    transition: background 0.15s, border-color 0.15s;
}
.up-code-display:hover { background: rgba(232, 180, 58, 0.12); }

/* dropzone */
.up-drop {
    position: relative;
    border: 2px dashed var(--border);
    border-radius: 12px;
    background: var(--panel-2);
    min-height: 168px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, transform 0.1s;
}
.up-drop:hover { border-color: var(--accent); background: rgba(79, 140, 255, 0.04); }
.up-drop.is-drag { border-color: var(--accent); background: rgba(79, 140, 255, 0.08); transform: scale(1.005); }
.up-drop.is-error { border-color: var(--danger); background: rgba(231, 76, 60, 0.06); }
.up-drop:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.up-drop-inner { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 18px; }
.up-drop-icon { color: var(--muted); display: inline-flex; }
.up-drop-icon svg { width: 40px; height: 40px; }
.up-drop-text { font-size: 14px; color: var(--muted); }
.up-drop-text strong { color: var(--text); }
.up-drop-hint { font-size: 12px; color: var(--muted); }
.up-fileinput {
    position: absolute;
    width: 1px; height: 1px;
    opacity: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
}

/* selected-file queue */
.up-queue { list-style: none; margin: 10px 0 0; padding: 0; display: none; }
.up-queue:not(:empty) { display: block; }
.up-qitem {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--panel-2);
    margin-bottom: 6px;
}
.up-qname { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.up-qsize { flex: 0 0 auto; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.up-hint { margin: 10px 0 0; font-size: 13px; color: var(--muted); }
.up-hint.err { color: var(--danger); font-weight: 600; }

/* fields grid */
.up-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0 14px; margin-top: 16px; }
.up-fields .field.full { grid-column: 1 / -1; }
.up-mono { font-family: var(--mono); font-size: 13px; }
.up-code-input {
    font-family: var(--mono);
    letter-spacing: 3px;
    text-transform: uppercase;
    font-weight: 700;
    font-size: 16px;
}

/* submit */
.up-submit { margin-top: 16px; }
.up-submit .btn { min-width: 170px; font-weight: 700; }

/* import form */
.up-import { display: block; }
.up-import .field { margin-bottom: 12px; }

/* progress panel */
.up-panel { margin-top: 16px; padding-top: 14px; border-top: 1px dashed var(--border); }
.up-file { margin: 0 0 10px; font-size: 13px; color: var(--text); word-break: break-all; }
.up-bar-group { margin-bottom: 12px; }
.up-bar-label {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 5px;
    font-size: 13px;
    font-weight: 600;
}
.up-bar-label #up-pct, .up-bar-label #up-hash-state { font-weight: 400; color: var(--muted); font-variant-numeric: tabular-nums; }
.up-track {
    height: 12px;
    border-radius: 7px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    overflow: hidden;
}
.up-fill {
    display: block;
    height: 100%;
    width: 0;
    background: linear-gradient(90deg, var(--accent), var(--accent-hover));
    border-radius: 7px 0 0 7px;
    transition: width 0.12s linear;
}
.up-track-hash .up-fill { background: linear-gradient(90deg, var(--warn), var(--ok)); }
.up-meta { margin-top: 5px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.up-hashbar { margin-top: 12px; }
.up-track-hash .up-fill.up-indeterminate {
    width: 100%;
    background: repeating-linear-gradient(90deg,
        var(--warn) 0 24px, rgba(241, 196, 15, 0.35) 24px 48px);
    background-size: 96px 100%;
    animation: up-hash-move 0.9s linear infinite;
    border-radius: 0;
}
.up-track-hash .up-fill.up-done { width: 100%; background: var(--ok); animation: none; border-radius: 0; }
@keyframes up-hash-move { from { background-position: 0 0; } to { background-position: 96px 0; } }
.up-status { margin: 12px 0 8px; font-size: 13px; font-weight: 600; }
.up-status.ok { color: var(--ok); }
.up-status.err { color: var(--danger); }
.up-status.cancel { color: var(--warn); }
.up-status.info { color: var(--muted); }
.up-actions { margin-top: 2px; }
#upload-progress.busy #up-cancel { display: inline-block; }
#upload-progress.done #up-cancel { display: none; }
