:root {
    --bg: #0f1115;
    --panel: #171a21;
    --panel-2: #1f232c;
    --text: #e7e9ee;
    --muted: #9aa3b2;
    --accent: #6ea8fe;
    --line: #2a2f3a;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font: 15px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}

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

main { max-width: 1100px; margin: 0 auto; padding: 24px 20px 64px; }

.error-page { max-width: 560px; }
.error-code { margin: 56px 0 4px; color: var(--accent); font-size: 13px; font-weight: 700; letter-spacing: .12em; }
.error-page h2 { margin: 0 0 8px; font-size: 24px; }

/* ---- gallery ------------------------------------------------------------ */
.gallery-head h1, .archive-head h1 { margin: 0 0 16px; font-size: 24px; }
.gallery-head h1 a { color: var(--text); }

.facets-wrap { margin-bottom: 20px; }
.facets { display: flex; flex-wrap: wrap; gap: 8px; }
/* M23: clamp the tag bar to ~3 rows; the facets controller adds this class in JS (so a no-JS visitor
   keeps the full bar) and the toggle reveals the rest. Not -webkit-line-clamp — that has no effect on
   a flex-wrap chip row; a max-height + overflow does. */
.facets--clamped { max-height: calc(3 * 28px + 2 * 8px); overflow: hidden; }
.facet {
    padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px;
    color: var(--muted); background: var(--panel); font-size: 13px;
}
.facet.is-active { color: var(--text); border-color: var(--accent); }
.facet-count { color: var(--muted); font-size: 11px; }
.facets-toggle {
    margin-top: 8px; padding: 2px 8px; border: 1px solid var(--line); border-radius: 999px;
    color: var(--muted); background: var(--panel); font-size: 12px; cursor: pointer;
}
.facets-toggle:hover { color: var(--text); border-color: var(--accent); }

.card-grid {
    display: grid; gap: 14px;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}

.card { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.card-link { display: block; color: inherit; }
.card-link:hover { text-decoration: none; }
.card-thumb {
    position: relative;
    aspect-ratio: 1 / 1; display: flex; align-items: center; justify-content: center;
    background: var(--panel-2); overflow: hidden;
}
.card-thumb img { width: 100%; height: 100%; object-fit: cover; }
/* Hover-to-play moving preview (M11 clip/GIF): layered over the still, revealed on hover. */
.card-motion {
    position: absolute; inset: 0;
    width: 100%; height: 100%; object-fit: cover;
    opacity: 0; transition: opacity .12s ease;
    pointer-events: none;
}
.card-thumb.is-playing .card-motion { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
    .card-motion { transition: none; }
}
.type-icon { color: var(--muted); display: inline-flex; }
.type-icon .type-glyph { width: 44px; height: 44px; }

/* Small type badge pinned to the top-left corner over the thumbnail. */
.card-badge {
    position: absolute; top: 6px; left: 6px; z-index: 1;
    padding: 2px 7px; border-radius: 999px;
    font-size: 11px; font-weight: 600; line-height: 1.45; letter-spacing: .02em;
    color: #fff; background: rgba(0, 0, 0, .66);
    border: 1px solid rgba(255, 255, 255, .18);
    backdrop-filter: blur(2px); pointer-events: none; text-transform: uppercase;
}
.card-badge--video    { background: rgba(120, 40, 40, .78); }
.card-badge--gif      { background: rgba(120, 74, 40, .78); }
.card-badge--audio    { background: rgba(52, 88, 120, .78); }
.card-badge--pdf      { background: rgba(120, 40, 52, .78); }
.card-badge--archive  { background: rgba(84, 66, 120, .78); }
.card-badge--exec     { background: rgba(96, 40, 96, .82); }
.card-name {
    padding: 8px 10px; font-size: 13px; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.empty { color: var(--muted); padding: 40px 0; }

.pager { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 28px; }
.pager-num {
    min-width: 34px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px;
    color: var(--muted); background: var(--panel); font-size: 13px; text-align: center;
}
a.pager-num:hover { color: var(--text); border-color: var(--accent); }
.pager-num.is-current {
    color: var(--text); background: var(--panel-2); border-color: var(--accent); font-weight: 600;
}
.pager-gap { color: var(--muted); font-size: 13px; padding: 0 2px; }
.pager-pos { color: var(--muted); font-size: 13px; margin-left: 4px; }

/* ---- archive tree ------------------------------------------------------- */
.crumb { margin: 0 0 12px; font-size: 13px; }
.archive-sub { color: var(--muted); margin: 4px 0 16px; font-size: 13px; }
.download-btn {
    display: inline-block; padding: 8px 16px; border-radius: 8px;
    background: var(--accent); color: #0b1020; font-weight: 600;
}
.download-btn:hover { text-decoration: none; filter: brightness(1.05); }

.tree { list-style: none; margin: 24px 0 8px; padding: 0; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.tree-row {
    display: flex; justify-content: space-between; gap: 12px;
    padding: 6px 14px; padding-left: calc(14px + var(--depth, 0) * 18px);
    border-top: 1px solid var(--line);
}
.tree-row:first-child { border-top: none; }
.tree-row.is-dir { color: var(--muted); }
.tree-size { color: var(--muted); font-variant-numeric: tabular-nums; font-size: 12px; }
.tree-note { color: var(--muted); font-size: 12px; }

/* ---- admin chrome ------------------------------------------------------- */
.admin-nav {
    display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
    max-width: 1100px; margin: 0 auto; padding: 14px 20px;
    border-bottom: 1px solid var(--line);
}
/* The `hidden` attribute must always win, even over a component's own display rule (e.g. .share-out
   is inline-flex, which would otherwise reveal a `hidden` copy field before a link is minted). */
[hidden] { display: none !important; }

.admin-brand { color: var(--text); font-weight: 700; }
.admin-brand-ver { color: var(--muted); font-weight: 600; font-variant-numeric: tabular-nums; }
.admin-links { display: flex; gap: 14px; flex-wrap: wrap; }
.admin-links a { color: var(--muted); }
.admin-links a.is-active { color: var(--text); }
.admin-nav-right { margin-left: auto; display: flex; gap: 14px; font-size: 13px; }
.admin-logout-form { margin: 0; }
.admin-logout { padding: 0; border: 0; background: none; color: var(--muted); font: inherit; cursor: pointer; }

.admin-title { margin: 0 0 18px; font-size: 22px; }
.admin-lead { color: var(--muted); margin: 0 0 20px; max-width: 70ch; }
.admin-lead code, code { background: var(--panel-2); padding: 1px 5px; border-radius: 4px; font-size: 13px; }

/* Shared app-wide flash region: a fixed top-right toast stack (templates/_flashes.html.twig). */
.flash-region {
    position: fixed; top: 16px; right: 16px; z-index: 1000;
    display: flex; flex-direction: column; gap: 10px;
    width: min(420px, calc(100vw - 32px)); pointer-events: none;
}
.flash-region:empty { display: none; }
.flash {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 10px 14px; border-radius: 8px; border: 1px solid var(--line);
    background: var(--panel); box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
    pointer-events: auto;
    opacity: 1; transform: translateX(0); transition: opacity 0.25s ease, transform 0.25s ease;
}
.flash--leaving { opacity: 0; transform: translateX(12px); }
.flash-msg { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.flash-close {
    flex: none; padding: 2px; margin: -2px -4px 0 0; border: 0; background: none;
    color: var(--muted); cursor: pointer; line-height: 0;
}
.flash-close:hover { color: var(--text); }
.flash-success { border-color: #2f6b3f; background: #14251a; }
.flash-error { border-color: #7a2f2f; background: #2a1616; }
.flash-info { border-color: #2f4f6b; background: #14202a; }
.copy-field { width: 100%; margin-top: 8px; padding: 6px 8px; background: var(--panel-2); color: var(--text); border: 1px solid var(--line); border-radius: 6px; }

/* stat grid */
.stat-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); margin-bottom: 24px; }
.stat { display: flex; flex-direction: column; gap: 6px; padding: 16px; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; color: inherit; }
.stat:hover { text-decoration: none; border-color: var(--accent); }
.stat-num { font-size: 28px; font-weight: 700; }
.stat-label { color: var(--muted); font-size: 13px; }
.stat-warn .stat-num { color: #e5896b; }

.admin-actions { display: flex; gap: 12px; flex-wrap: wrap; }

/* buttons + forms */
.btn { display: inline-block; padding: 6px 12px; border-radius: 7px; border: 1px solid var(--line); background: var(--panel-2); color: var(--text); font: inherit; font-size: 13px; cursor: pointer; }
.btn:hover { text-decoration: none; border-color: var(--accent); }
.btn-primary { background: var(--accent); color: #0b1020; border-color: var(--accent); font-weight: 600; }
.btn-danger { border-color: #7a2f2f; }
.btn-danger:hover { border-color: #c0524f; }

.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; max-width: 480px; }
.field span { color: var(--muted); font-size: 13px; }
.field-check { flex-direction: row; align-items: center; gap: 8px; }
.field-check input { width: auto; }
.field input, .field textarea, .generate-form select, .generate-form input, .inline-form input {
    padding: 8px 10px; background: var(--panel-2); color: var(--text);
    border: 1px solid var(--line); border-radius: 7px; font: inherit;
}
.form-actions { display: flex; gap: 12px; align-items: center; margin-top: 8px; }
.inline-form { display: inline-flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
.check { color: var(--muted); font-size: 13px; display: inline-flex; gap: 6px; align-items: center; }

.generate-form { display: flex; gap: 18px; flex-wrap: wrap; align-items: flex-end; padding: 16px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); margin-bottom: 24px; }
.generate-form fieldset { border: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.generate-form legend { color: var(--muted); font-size: 13px; padding: 0; }
.radio { display: inline-flex; gap: 6px; align-items: center; font-size: 14px; }

/* tables */
.admin-table { width: 100%; border-collapse: collapse; margin: 8px 0 24px; }
.admin-table th, .admin-table td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
.admin-table th { color: var(--muted); font-weight: 600; font-size: 13px; }
.admin-table tr.is-restricted td:first-child { box-shadow: inset 3px 0 0 #c0524f; }
/* Force long, space-less filenames to wrap inside the name column instead of blowing out the table. */
.admin-table td.col-name { max-width: 420px; overflow-wrap: anywhere; word-break: break-word; }
/* Tags page: keep every cell a normal table-cell (never display:flex, which drops a <td> out of the
   row model and misaligns its bottom border) and strip the in-cell form margin so each row's divider
   lines up across Name / Items / Access / action. */
.tags-table td { vertical-align: middle; }
.tags-table td form { margin: 0; }
.admin-table tr.is-disabled { opacity: 0.55; }
/* Actions sit in their own full-width row under each item: a single horizontal cluster, no column. */
.row-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
/* Every direct action reads as the same control: tree (link), edit, share, delete are all .btn. */
.row-actions > a.btn, .row-actions > .edit-toggle, .row-actions form > .btn { display: inline-flex; align-items: center; gap: 4px; }
.row-actions form { margin: 0; }
/* The actions row is glued to its item above (no top border, tight top padding) and indented to line
   up under the name column, so it reads as belonging to that item rather than as a new record. */
/* An item spans a content row + an actions row (+ an optional edit row); the content row drops its
   bottom border so the pair reads as one block, and the actions/edit row below carries the divider. */
.admin-table tr.item-row > td { border-bottom: 0; }
.admin-table tr.actions-row > td { border-top: 0; padding: 2px 8px 12px 116px; }
@media (max-width: 640px) { .admin-table tr.actions-row > td { padding-left: 8px; } }
.muted { color: var(--muted); }
.small { font-size: 12px; }
.ingest-error { color: #e5896b; font-size: 12px; margin-top: 4px; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.status-age { margin-top: 4px; }

/* worker/queue liveness strip (M-ingest-status) */
.worker-status { display: inline-flex; align-items: center; gap: 8px; margin: 0 0 16px; padding: 5px 12px;
    border: 1px solid var(--line); border-radius: 999px; font-size: 12px; color: var(--muted); }
.worker-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex: none; }
.worker-dot.is-alive { background: #7fd39b; }
.worker-dot.is-stale { background: #d3bd7f; }
.worker-dot.is-offline { background: #e58b8b; }
.worker-sep { opacity: 0.5; }

/* admin blob list */
.table-scroll { overflow-x: auto; }
.stat-grid--compact { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
.cell-thumb { width: 56px; }
.blob-thumb { width: 48px; height: 48px; object-fit: cover; border-radius: 6px; background: var(--panel-2); display: block; }
.copy-sha { width: 130px; max-width: 40vw; margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; }

.badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 12px; border: 1px solid var(--line); color: var(--muted); }
.badge-ready, .badge-public { border-color: #2f6b3f; color: #7fd39b; }
.badge-pending { border-color: #6b5a2f; color: #d3bd7f; }
.badge-failed { border-color: #7a2f2f; color: #e58b8b; }
.badge-restricted { border-color: #7a2f2f; color: #e58b8b; }
.badge-expanded, .badge-awaiting_upload { border-color: var(--line); color: var(--muted); }
/* M18b import-by-URL: an item awaiting its server-side fetch. In-flight, like pending. */
.badge-awaiting_fetch { border-color: #6b5a2f; color: #d3bd7f; }
/* M20a: content-shared (dedup) marker — an item whose blob is referenced by another item. */
.badge-shared { border-color: #3a5a7a; color: #8fbfe0; display: inline-flex; align-items: center; gap: 4px; }
.tag-chip { display: inline-block; padding: 1px 8px; margin: 0 4px 4px 0; border-radius: 999px; background: var(--panel-2); font-size: 12px; }

.edit-pop summary { cursor: pointer; color: var(--accent); font-size: 13px; }
.edit-form { margin-top: 10px; padding: 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }

.preview { margin-bottom: 24px; }
.preview details { border: 1px solid var(--line); border-radius: 8px; padding: 8px 12px; margin-bottom: 8px; background: var(--panel); }
.preview summary { cursor: pointer; }
.preview-list { margin: 8px 0 0; padding-left: 20px; }

/* upload dropzone + progress */
.dropzone {
    border: 2px dashed var(--line); border-radius: 12px; padding: 48px 20px;
    text-align: center; color: var(--muted); cursor: pointer; background: var(--panel);
    margin-bottom: 18px;
}
.dropzone.is-dragging { border-color: var(--accent); color: var(--text); }
.up-list { display: flex; flex-direction: column; gap: 8px; }
.up-row { display: grid; grid-template-columns: 1fr 160px auto auto; gap: 12px; align-items: center; padding: 8px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }
.up-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.up-bar { height: 8px; background: var(--panel-2); border-radius: 999px; overflow: hidden; }
.up-fill { display: block; height: 100%; background: var(--accent); width: 0; transition: width .15s ease; }
.up-status { font-size: 12px; color: var(--muted); }
.up-row[data-state="done"] .up-status { color: #7fd39b; }
.up-row[data-state="error"] { border-color: #7a2f2f; }
.up-row[data-state="error"] .up-status { color: #e58b8b; }
.up-retry { font: inherit; font-size: 12px; padding: 3px 8px; border-radius: 6px; border: 1px solid var(--line); background: var(--panel-2); color: var(--text); cursor: pointer; }

/* login */
.login { max-width: 360px; margin: 80px auto; }
.login h1 { font-size: 22px; margin-bottom: 20px; }
.login-form { display: flex; flex-direction: column; gap: 14px; }
.login-form label { display: flex; flex-direction: column; gap: 6px; }
.login-form span { color: var(--muted); font-size: 13px; }
.login-form input { padding: 9px 11px; background: var(--panel-2); color: var(--text); border: 1px solid var(--line); border-radius: 7px; font: inherit; }
.login-form button { padding: 10px; border-radius: 7px; border: none; background: var(--accent); color: #0b1020; font-weight: 600; cursor: pointer; }

/* ---- M12 icons (inline SVG glyphs share currentColor) -------------------- */
.act-ico, .badge-ico, .meta-ico { width: 1em; height: 1em; vertical-align: -0.15em; }
.meta-ico { color: var(--muted); }

/* ---- M12 admin gallery cards -------------------------------------------- */
.admin-card { display: flex; flex-direction: column; position: relative; }
.admin-card.is-restricted { border-color: #7a2f2f; }
.card-select { position: absolute; top: 6px; right: 6px; z-index: 2; background: rgba(0,0,0,.55); border-radius: 6px; padding: 3px 5px; line-height: 0; }
.card-restricted {
    position: absolute; bottom: 6px; left: 6px; z-index: 1;
    padding: 2px 7px; border-radius: 999px; font-size: 10px; font-weight: 600; letter-spacing: .02em;
    text-transform: uppercase; color: #fff; background: rgba(120, 40, 52, .85);
    border: 1px solid rgba(255,255,255,.18);
}
.card-meta { display: flex; align-items: center; gap: 8px; padding: 8px 10px 0; }
.card-size { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; margin-left: auto; }
.card-tags { padding: 6px 10px 0; }
.card-lineage { margin: 6px 10px 0; font-size: 12px; color: var(--muted); }
.card-links { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px 0; }
.card-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; padding: 10px; margin-top: auto; }

.facet--restricted { border-color: #7a2f2f; color: #e58b8b; }

/* ---- M12 bulk action bar ------------------------------------------------ */
.bulk-bar {
    position: sticky; top: 0; z-index: 5;
    display: flex; flex-wrap: nowrap; gap: 12px; align-items: center;
    padding: 10px 14px; margin-bottom: 14px;
    border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
}
/* One line: the label and the form's fixed controls keep their size; the free-text tag input alone
   absorbs the slack (min-width:0 lets it shrink below its content width instead of forcing a wrap). */
.bulk-all { flex: none; display: inline-flex; gap: 8px; align-items: center; color: var(--muted); font-size: 13px; white-space: nowrap; }
.bulk-form { flex: 1 1 auto; min-width: 0; display: flex; gap: 8px; align-items: center; flex-wrap: nowrap; margin: 0; }
.bulk-form select, .bulk-form .btn { flex: none; }
.bulk-form input[type="text"] { flex: 1 1 auto; min-width: 0; }
.bulk-form select, .bulk-form input {
    padding: 6px 9px; background: var(--panel-2); color: var(--text);
    border: 1px solid var(--line); border-radius: 7px; font: inherit; font-size: 13px;
}
.bulk-result { border: 1px solid #2f6b3f; border-radius: 10px; background: #14251a; padding: 12px 14px; margin-bottom: 14px; }
.bulk-result-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.bulk-result-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.bulk-result-list li { display: flex; flex-direction: column; gap: 3px; }

.col-select { width: 1%; white-space: nowrap; }
.admin-table td.col-thumb, .admin-table th.col-thumb { width: 56px; padding-right: 4px; }
.thumb-link { display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; border-radius: 6px; overflow: hidden; background: var(--panel-2); border: 1px solid var(--line); }
.thumb-link:hover { border-color: var(--accent); }
.thumb-mini { width: 100%; height: 100%; object-fit: cover; display: block; }
.thumb-link .type-icon { color: var(--muted); }
.thumb-link .type-glyph { width: 22px; height: 22px; }
.cell-size { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.cell-tags { max-width: 320px; }

/* ---- M12 tag chips with remove ------------------------------------------ */
.tag-chip { display: inline-flex; align-items: center; gap: 4px; }
.tag-chip--restricted { background: #2a1616; color: #e58b8b; }
.chip-remove { display: inline; margin: 0; }
.chip-remove button {
    border: none; background: none; color: var(--muted); cursor: pointer;
    font: inherit; line-height: 1; padding: 0 2px; border-radius: 4px;
}
.chip-remove button:hover { color: #e58b8b; }

/* ---- M12 per-item edit popover + inline share --------------------------- */
.edit-pop summary { display: inline-flex; align-items: center; gap: 4px; }
.edit-body { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; padding: 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }
.edit-body .field { margin-bottom: 6px; }

/* ---- M20b full-width edit row in the items table ------------------------- */
/* Actions and the edit form each get their own full-width row under the item (see .actions-row and
   .edit-row); there is no longer an actions column. */
/* The card layout keeps M12's grid edit body; the table layout uses a single column of forms. */
.edit-row > td { padding: 0 0 14px; }
.edit-row .edit-body { margin-top: 0; }
.share-inline { display: inline-flex; flex-direction: column; gap: 6px; }
.share-inline form { display: inline; margin: 0; }
.share-inline .btn { display: inline-flex; align-items: center; gap: 4px; }
.share-out { display: inline-flex; gap: 6px; align-items: center; }
.share-out .copy-field { width: 260px; max-width: 60vw; margin: 0; }

/* ---- M12 preview metadata panel ----------------------------------------- */
.preview-head { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; }
.preview-head-main { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.preview-name { margin: 0; font-size: 24px; }
.preview-head .download-btn { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; }
/* Tag row directly under the header (below the name, above the file). On /f the chips are links. */
.preview-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: -6px 0 18px; }
.preview-tags .tag-chip { margin: 0; }
a.tag-chip { color: var(--text); text-decoration: none; border: 1px solid transparent; }
a.tag-chip:hover { border-color: var(--accent); color: var(--text); }
.preview-badge {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 3px 9px; border-radius: 999px; font-size: 12px; font-weight: 600;
    text-transform: uppercase; letter-spacing: .02em;
    color: var(--text); background: var(--panel-2); border: 1px solid var(--line);
}
.meta-panel {
    margin: 24px 0 0; padding: 4px 0 0; border-top: 1px solid var(--line);
    display: grid; gap: 0;
}
.meta-row { display: grid; grid-template-columns: 180px 1fr; gap: 12px; padding: 10px 4px; border-bottom: 1px solid var(--line); }
.meta-row dt { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-size: 13px; margin: 0; }
.meta-row dd { margin: 0; font-size: 14px; word-break: break-word; }
.meta-row dd .tag-chip { margin: 0 4px 4px 0; }
.meta-row dd .copy-field { display: block; margin: 4px 0 0; }
@media (max-width: 560px) {
    .meta-row { grid-template-columns: 1fr; gap: 2px; }
}

/* ---- M14 preview media: center + aspect-fit (ALL viewers, not gated) ----
   The body content is centered and every media element is bounded by the column width AND a
   viewport-relative height, keeping aspect ratio and never upscaling past natural size, so a large
   image/video fits on screen instead of overflowing the column. Applies on /f and /s alike. */
.preview-body { display: flex; justify-content: center; }
.preview-body > * { max-width: 100%; margin: 0 auto; }
/* Text/code/markdown/archive blocks FILL the centered column (only true media shrink-to-fit and
   center); a long code line scrolls inside its own block so the page body never scrolls sideways. */
.preview-render, .preview-archive, .preview-none { width: 100%; }
.preview-render--code, .preview-render--text { overflow-x: auto; }
.preview-image img,
.preview-video video,
.preview-video--gif img {
    display: block; margin: 0 auto;
    max-width: 100%; width: auto; height: auto;
    max-height: min(80vh, 900px);
    object-fit: contain;
}
.preview-note { text-align: center; }
/* Audio has no aspect ratio: just center it and cap the control width. */
.preview-audio { width: 100%; text-align: center; }
.preview-audio audio { width: 100%; max-width: 640px; }
/* pdf.js draws each page into a fixed-pixel <canvas>; make it fluid so a wide page never overflows
   (height:auto keeps the ratio). No max-height cap — pages scroll vertically. */
.preview-pdf { width: 100%; }
.preview-pdf, .preview-pdf-pages { max-width: 100%; }
.preview-pdf-pages { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.preview-pdf-page { max-width: 100%; height: auto; }

/* ---- M14 owner controls on the preview page (ROLE_ADMIN, /f only) ------- */
.owner-controls { margin-top: 28px; padding-top: 4px; border-top: 1px solid var(--line); }
.owner-controls-label { display: inline-flex; align-items: center; gap: 8px; font-size: 15px; color: var(--muted); margin: 14px 0 10px; }
.owner-grid { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; }
.owner-card { border: 1px solid var(--line); border-radius: 10px; background: var(--panel); padding: 8px 12px; flex: 1 1 240px; min-width: 220px; }
.owner-card summary { cursor: pointer; display: inline-flex; align-items: center; gap: 6px; font-weight: 600; color: var(--accent); }
.owner-card--danger summary { color: #e58b8b; }
.owner-card-body { margin-top: 10px; }
.owner-card .edit-form { margin-top: 10px; }
.owner-card .field { max-width: none; }
.owner-card .field textarea, .owner-card .field select {
    padding: 8px 10px; background: var(--panel-2); color: var(--text);
    border: 1px solid var(--line); border-radius: 7px; font: inherit;
}
.owner-card-hint { color: var(--muted); font-size: 13px; margin: 8px 0; }
.share-existing { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 8px; }
.share-existing .share-out { margin-bottom: 8px; }
.owner-card .share-out { flex-wrap: wrap; }
.owner-card .share-out .copy-field { width: 100%; margin: 0; }

/* ---- tag input autocomplete (typeahead dropdown across all tag fields) --- */
.ac-wrap { position: relative; display: block; width: 100%; }
.ac-wrap > input { width: 100%; }
.ac-menu {
    position: absolute; z-index: 30; top: 100%; left: 0; right: 0;
    margin: 2px 0 0; padding: 4px; list-style: none;
    max-height: 240px; overflow-y: auto;
    background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
}
.ac-opt {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 6px 8px; border-radius: 6px; cursor: pointer; font-size: 14px; color: var(--text);
}
.ac-opt:hover, .ac-opt.is-active { background: var(--panel-2); }
.ac-opt--restricted .ac-opt-name { color: #e58b8b; }
.ac-opt-flag {
    font-size: 11px; color: #e58b8b; background: #2a1616;
    border-radius: 999px; padding: 1px 8px; white-space: nowrap;
}

/* ---- text-family previews: real containers + syntax coloring (preview-refinement) ----
   Code, plain text, and markdown are transformed to sanitized HTML on the app origin (M10) and were
   dropped into bare <div>s with no styling. These rules give each a padded, monospace, panel-backed,
   rounded, self-scrolling block that fits the M14 flex-centered .preview-body without the page body
   ever scrolling sideways (each block owns its horizontal scroll; long files cap at a viewport height
   and scroll vertically inside). No CSP change — style-src already allows an app stylesheet. */

/* Shared code/text block chrome. */
.preview-code,
.preview-text {
    margin: 0;
    padding: 14px 16px;
    background: var(--panel-2);
    color: var(--text);
    border: 1px solid var(--line);
    border-radius: 10px;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
    font-size: 13px;
    line-height: 1.55;
    tab-size: 4;
    white-space: pre;
    overflow: auto;
    max-height: min(80vh, 900px);
}

/* The <pre class="preview-code"> owns the scroll; its wrapper must not add a second one. */
.preview-render--code { overflow: visible; }
.preview-render--code, .preview-render--markdown-wrap { width: 100%; }

/* "View raw" toggle (JS-free <details>) for markdown and code — no inline script, CSP unchanged. */
.preview-raw { margin-top: 12px; }
.preview-raw > summary {
    cursor: pointer; display: inline-flex; align-items: center; gap: 6px;
    color: var(--accent); font-size: 13px; user-select: none;
}
.preview-raw > summary:hover { text-decoration: underline; }
.preview-raw .preview-code--raw { margin-top: 10px; }
.preview-code--raw { color: var(--muted); }

/* tempest/highlight token palette — copied from vendor/tempest/highlight/src/Themes/Css/github-dark.css
   (chosen 2026-08-23) and tuned to the app's dark panel. Copied, not linked, so the palette is owned
   by the app and survives a vendor bump. Scoped under .preview-code so the highlighter spans never
   restyle admin inline <code>. The vendor `pre, code { background }` rule is deliberately dropped —
   .preview-code sets its own panel background above. */
.preview-code .hl-keyword  { color: #f97583; }
.preview-code .hl-property { color: #79b8ff; }
.preview-code .hl-attribute { font-style: italic; }
.preview-code .hl-type     { color: #b392f0; }
.preview-code .hl-generic  { color: #b392f0; }
.preview-code .hl-value    { color: #9ecbff; }
.preview-code .hl-literal  { color: #9ecbff; }
.preview-code .hl-number   { color: #9ecbff; }
.preview-code .hl-variable { color: #ffab70; }
.preview-code .hl-comment  { color: #6a737d; font-style: italic; }
.preview-code .hl-blur     { filter: blur(2px); }
.preview-code .hl-strong   { font-weight: bold; }
.preview-code .hl-em       { font-style: italic; }

/* Markdown typography — scoped to .preview-render--markdown so it can't leak into admin chrome. */
.preview-render--markdown {
    width: 100%;
    max-width: 80ch;
    margin: 0 auto;
    text-align: left;
    color: var(--text);
    overflow-wrap: break-word;
}
.preview-render--markdown > :first-child { margin-top: 0; }
.preview-render--markdown > :last-child { margin-bottom: 0; }
.preview-render--markdown h1,
.preview-render--markdown h2,
.preview-render--markdown h3,
.preview-render--markdown h4 { line-height: 1.25; margin: 1.4em 0 .6em; }
.preview-render--markdown h1 { font-size: 1.7em; border-bottom: 1px solid var(--line); padding-bottom: .3em; }
.preview-render--markdown h2 { font-size: 1.4em; border-bottom: 1px solid var(--line); padding-bottom: .3em; }
.preview-render--markdown h3 { font-size: 1.2em; }
.preview-render--markdown p,
.preview-render--markdown ul,
.preview-render--markdown ol { margin: 0 0 1em; }
.preview-render--markdown ul,
.preview-render--markdown ol { padding-left: 1.6em; }
.preview-render--markdown li { margin: .25em 0; }
.preview-render--markdown a { color: var(--accent); }
.preview-render--markdown code {
    background: var(--panel-2); padding: 1px 5px; border-radius: 4px; font-size: .9em;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.preview-render--markdown pre {
    background: var(--panel-2); border: 1px solid var(--line); border-radius: 10px;
    padding: 14px 16px; overflow: auto; max-height: min(70vh, 720px);
}
.preview-render--markdown pre code { background: none; padding: 0; font-size: 13px; }
.preview-render--markdown blockquote {
    margin: 0 0 1em; padding: 2px 14px; border-left: 3px solid var(--line); color: var(--muted);
}
.preview-render--markdown hr { border: 0; border-top: 1px solid var(--line); margin: 1.5em 0; }
.preview-render--markdown img { max-width: 100%; height: auto; }
.preview-render--markdown table { border-collapse: collapse; width: 100%; margin: 0 0 1em; display: block; overflow-x: auto; }
.preview-render--markdown th,
.preview-render--markdown td { border: 1px solid var(--line); padding: 6px 10px; text-align: left; }
.preview-render--markdown th { background: var(--panel-2); }

/* ---- M22 duplicates page ------------------------------------------------- */
.dup-group {
    border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
    padding: 14px 16px; margin: 0 0 18px;
}
.dup-group-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.dup-group-head .blob-thumb { width: 64px; height: 64px; }
.dup-thumb-placeholder {
    width: 64px; height: 64px; border-radius: 6px; background: var(--panel-2);
    display: flex; align-items: center; justify-content: center;
}
.dup-group-meta { min-width: 0; }
.dup-sha { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60ch; }
.dup-items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.dup-item {
    display: flex; align-items: flex-start; gap: 12px;
    padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel-2);
}
.dup-item.is-restricted { box-shadow: inset 3px 0 0 #c0524f; }
.dup-keep {
    display: inline-flex; flex-direction: column; align-items: center; gap: 2px;
    font-size: 11px; color: var(--muted); cursor: pointer; padding-top: 2px;
}
.dup-item-body { flex: 1 1 auto; min-width: 0; }
.dup-name { font-weight: 600; }
.dup-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 4px 0; }
.dup-sub { word-break: break-word; }
.dup-remove { margin-left: auto; }
.dup-remove > summary { list-style: none; cursor: pointer; }
.dup-remove > summary::-webkit-details-marker { display: none; }
.dup-remove-body { margin-top: 8px; }
.dup-source-pick {
    margin: 12px 0 0; padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px;
    background: var(--panel-2); display: flex; flex-direction: column; gap: 8px;
}
.dup-source-pick legend { padding: 0 4px; font-size: 12px; color: var(--muted); }
.dup-source-option { display: flex; align-items: flex-start; gap: 8px; cursor: pointer; }
.dup-source-option > span { display: flex; flex-direction: column; min-width: 0; }
.dup-source-url { word-break: break-all; }
.dup-summary { margin: 10px 0 0; }
.dup-actions { margin-top: 12px; }

/* ---- Albums (version/3): reorderable member list, admin album list, preview prev/next ---------- */

/* The reorderable member list — shared by the upload tagging page (gallery mode) and the album
   detail page. Rows are draggable; the `sortable` controller reveals the handle + ↑/↓ (rendered
   hidden, so nothing looks live with JS off). */
.album-list { list-style: none; margin: 0 0 16px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.album-row {
    display: flex; align-items: center; gap: 10px;
    padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel-2);
}
.album-row.is-dragging { opacity: .5; border-color: var(--accent); }
.album-row.is-cover { border-color: #2f6b3f; }
.album-row-controls { display: inline-flex; align-items: center; gap: 4px; }
.album-row-controls[hidden] { display: none; }
.drag-handle { cursor: grab; color: var(--muted); user-select: none; }
.btn-icon { padding: 2px 8px; line-height: 1.2; }
.album-pos { min-width: 1.6em; text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }
.album-thumb { width: 40px; height: 40px; object-fit: cover; border-radius: 6px; border: 1px solid var(--line); }
.album-order { width: 4.5em; }
.album-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.album-remove { margin-left: auto; }
.album-delete-form { margin-top: 24px; }

/* The /admin/albums list — a card grid of covers. */
.album-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 14px; margin: 0 0 20px;
}
.album-card {
    display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 10px;
    background: var(--panel); overflow: hidden;
}
.album-card:hover { border-color: var(--accent); text-decoration: none; }
.album-card-thumb {
    aspect-ratio: 4 / 3; background: var(--panel-2); display: flex; align-items: center; justify-content: center;
}
.album-card-thumb img { width: 100%; height: 100%; object-fit: cover; }
.album-card-noimg { color: var(--muted); }
.album-card-body { padding: 8px 10px; }
.album-card-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The preview page's album prev/next (a /f affordance only). */
.preview-album-nav {
    display: flex; align-items: center; justify-content: center; gap: 16px;
    margin: 12px 0 0; padding: 8px 12px;
    border: 1px solid var(--line); border-radius: 999px; background: var(--panel-2);
}
.album-nav-link { white-space: nowrap; }
.album-nav-link.is-disabled { color: var(--muted); opacity: .5; cursor: default; }
.album-nav-counter { color: var(--muted); font-variant-numeric: tabular-nums; }
