/* Duckery Dark Theme — layered on Bootstrap 5
   Design principles: covers are the stars, dark backgrounds,
   minimal typography, tight grids, no card borders */

/* ============================================
   1. Foundation — CSS custom properties
   ============================================ */
:root {
    --bs-body-bg: #121215;
    --bs-body-color: #e6e6e8;
    --bs-body-font-family: var(--duckery-font-body);
    --bs-emphasis-color: #fff;
    --bs-link-color: #8ecae6;
    --bs-link-hover-color: #b8dff0;

    --duckery-font-body: "Inter", "Inter Fallback", system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
    --duckery-surface: #191a1f;
    --duckery-surface-hover: #20222a;
    --duckery-accent: #e94560;
    --duckery-accent-hover: #d63851;
    --duckery-accent-soft: #f27a92; /* AA for small error text on surface and body backgrounds */
    --duckery-muted: #888;
    --duckery-muted-on-surface: #9a9ab0; /* AA where --duckery-muted falls short on surface backgrounds */
    --duckery-tomato: #e07a5f;
    --duckery-border: #2f3038;
    --duckery-border-control: #3a3b45; /* inputs, cell hover border target, dashed cells */
    --duckery-border-divider: #23242b; /* hairlines under sticky bars, section rules */
    --duckery-border-hover: #4a4c58;
    --duckery-navy-fill: #1c2947; /* the one navy surface left: the filter submit button */
    --duckery-navy-border: #2f4272;
    --duckery-navy-hover-fill: #24345c;
    --duckery-grid-gap: 2px;
    --duckery-shelf-height: 180px;
    --duckery-cover-radius: 2px;
}

body {
    background-color: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-family: var(--duckery-font-body);
}

/* ============================================
   2. Typography — minimal, let imagery speak
   ============================================ */
/* Self-hosted latin subset; Danish fits in U+0000-00FF. */
@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 100 900;
    font-display: optional;
    src: url('fonts/inter-var-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Inter";
    font-style: italic;
    font-weight: 100 900;
    font-display: swap;
    src: url('fonts/inter-var-italic-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Arial carrying Inter's metrics (Capsize values). */
@font-face {
    font-family: "Inter Fallback";
    src: local('Arial');
    ascent-override: 90.20%;
    descent-override: 22.48%;
    line-gap-override: 0%;
    size-adjust: 107.40%;
}

h1, h2, h3, h4, h5, h6 {
    color: #fff;
    font-weight: 600;
}

.text-muted {
    color: var(--duckery-muted) !important;
}

a {
    color: var(--bs-link-color);
}
a:hover {
    color: var(--bs-link-hover-color);
}

/* ============================================
   3. Buttons — dark theme overrides
   ============================================ */
.btn-primary {
    background-color: var(--duckery-accent);
    border-color: var(--duckery-accent);
    color: #fff;
}
.btn-primary:hover, .btn-primary:active {
    background-color: var(--duckery-accent-hover);
    border-color: var(--duckery-accent-hover);
}

.btn-outline-primary {
    color: var(--duckery-accent);
    border-color: var(--duckery-accent);
}
.btn-outline-primary:hover, .btn-outline-primary.active {
    background-color: var(--duckery-accent);
    border-color: var(--duckery-accent);
    color: #fff;
}

.btn-outline-secondary {
    color: var(--duckery-muted);
    border-color: var(--duckery-border);
}

/* ============================================
   4. Cards & surfaces — dark, borderless
   ============================================ */
.card {
    background-color: var(--duckery-surface);
    border: none;
    border-radius: var(--duckery-cover-radius);
}
.card-body {
    color: var(--bs-body-color);
}
.card-footer {
    background-color: transparent;
    border-top: 1px solid var(--duckery-border);
}

/* ============================================
   5. Forms — dark inputs
   ============================================ */
.form-control, .form-select {
    background-color: var(--duckery-surface);
    border-color: var(--duckery-border);
    color: var(--bs-body-color);
}
.form-control:focus, .form-select:focus {
    background-color: var(--duckery-surface-hover);
    border-color: var(--duckery-accent);
    color: var(--bs-body-color);
    box-shadow: 0 0 0 0.2rem rgba(233, 69, 96, 0.25);
}
.form-control::placeholder {
    color: var(--duckery-muted);
}

/* ============================================
   6. Alerts — dark theme
   ============================================ */
.alert-info {
    background-color: rgba(142, 202, 230, 0.1);
    border-color: rgba(142, 202, 230, 0.3);
    color: #8ecae6;
}
.alert-warning {
    background-color: rgba(255, 183, 3, 0.1);
    border-color: rgba(255, 183, 3, 0.3);
    color: #ffb703;
}
.alert-danger {
    background-color: rgba(233, 69, 96, 0.1);
    border-color: rgba(233, 69, 96, 0.3);
    color: #e94560;
}
.alert-success {
    background-color: rgba(38, 176, 80, 0.1);
    border-color: rgba(38, 176, 80, 0.3);
    color: #26b050;
}

/* ============================================
   7. Cover Grid — tight, no borders, covers as hero
   ============================================ */
.cover-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: var(--duckery-grid-gap);
}

@media (min-width: 768px) {
    .cover-grid {
        grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    }
}

/* An outlier wider than its grid's typical cover (MagazineGridLayout.ColumnSpan) claims extra tracks —
   aspect-ratio alone only changes a fixed-width cell's height, never its width. */
.cover-grid-span-2 {
    grid-column: span 2;
}
.cover-grid-span-3 {
    grid-column: span 3;
}

.cover-cell {
    position: relative;
    aspect-ratio: 2 / 3;
    overflow: hidden;
    border-radius: var(--duckery-cover-radius);
    cursor: pointer;
    transition: transform 0.15s ease;
    display: block;
    color: inherit;
    text-decoration: none;
}
.cover-cell:hover {
    transform: scale(1.02);
    z-index: 1;
    color: inherit;
    text-decoration: none;
}
.cover-cell:focus-visible {
    outline: 2px solid var(--duckery-gold);
    outline-offset: 2px;
}

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

/* Stock photo treatment — desaturated placeholder */
.cover-cell.stock-photo img {
    filter: saturate(0.15) brightness(0.6);
}
.cover-cell.stock-photo .add-photo-overlay {
    display: flex;
}

.add-photo-overlay {
    display: none;
    position: absolute;
    inset: 0;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 4px;
    color: rgba(255, 255, 255, 0.8);
    font-size: 0.75rem;
    text-align: center;
    pointer-events: none;
}
.add-photo-overlay .camera-icon {
    font-size: 1.5rem;
}

/* Overlays — small, non-intrusive badges */
.cover-overlay {
    position: absolute;
    padding: 2px 6px;
    font-size: 0.65rem;
    font-weight: 600;
    border-radius: 3px;
    line-height: 1;
    pointer-events: none;
}

.cover-overlay.top-left { top: 4px; left: 4px; }
.cover-overlay.bottom-left { bottom: 4px; left: 4px; }
.cover-overlay.bottom-right { bottom: 4px; right: 4px; }

.cover-overlay.issue-label {
    background: rgba(0, 0, 0, 0.65);
    color: #ddd;
}

/* Privacy dot — tiny colored indicator */

/* ============================================
   8. Shelf View — horizontal scrollable rows
   ============================================ */



/* ============================================
   12. Filter bar — scope toggles
   ============================================ */
.filter-bar {
    display: flex;
    gap: 6px;
    margin-bottom: 1rem;
    flex-wrap: wrap;
}
.filter-chip {
    padding: 4px 12px;
    border-radius: 16px;
    font-size: 0.8rem;
    border: 1px solid var(--duckery-border);
    background: transparent;
    color: var(--bs-body-color);
    cursor: pointer;
    transition: all 0.15s;
}
.filter-chip:hover {
    border-color: var(--duckery-accent);
}
.filter-chip.active {
    background: var(--duckery-accent);
    border-color: var(--duckery-accent);
    color: #fff;
}

/* ============================================
   13. Empty states
   ============================================ */
.empty-state {
    text-align: center;
    padding: 3rem 1rem;
    color: var(--duckery-muted);
}
.empty-state h3 {
    color: var(--bs-body-color);
    margin-bottom: 0.5rem;
}
.empty-state p {
    margin-bottom: 1.5rem;
}

/* ============================================
   14. Cover hero — detail page header
   ============================================ */
.cover-hero {
    position: relative;
    width: 100%;
    max-height: 50vh;
    overflow: hidden;
    background: #000;
    display: flex;
    justify-content: center;
}
.cover-hero img {
    max-width: 100%;
    max-height: 50vh;
    object-fit: contain;
}

/* ============================================
   15. Badges — dark theme overrides
   ============================================ */
.badge.bg-secondary { background-color: #444 !important; }
.badge.bg-info { background-color: rgba(142, 202, 230, 0.2) !important; color: #8ecae6 !important; }
.badge.bg-success { background-color: rgba(38, 176, 80, 0.2) !important; color: #26b050 !important; }

/* ============================================
   16. Tables — dark
   ============================================ */
.table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--bs-body-color);
    --bs-table-border-color: var(--duckery-border);
    --bs-table-striped-bg: rgba(255, 255, 255, 0.02);
    --bs-table-hover-bg: rgba(255, 255, 255, 0.04);
}

/* ============================================
   17. Loading spinner
   ============================================ */
.loading-spinner {
    display: flex;
    justify-content: center;
    padding: 2rem;
    color: var(--duckery-muted);
}

/* ============================================
   18. Blazor error UI — dark override
   ============================================ */
#blazor-error-ui {
    background: var(--duckery-surface);
    color: var(--duckery-accent);
}

/* ============================================
   20. Condition tier badges
   ============================================ */
.condition-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.3px;
}
.condition-badge.tier-high { background: rgba(38, 176, 80, 0.2); color: #26b050; }
.condition-badge.tier-above-average { background: rgba(38, 176, 80, 0.15); color: #4ecb71; }
.condition-badge.tier-average { background: rgba(255, 183, 3, 0.2); color: #ffb703; }
.condition-badge.tier-below-average { background: rgba(233, 69, 96, 0.15); color: #e9806e; }
.condition-badge.tier-low { background: rgba(233, 69, 96, 0.2); color: #e94560; }
.condition-badge.tier-top { background: rgba(38, 176, 80, 0.25); color: #26b050; }
.condition-badge.tier-bottom { background: rgba(233, 69, 96, 0.25); color: #e94560; }
.condition-badge.ungraded { background: rgba(136, 136, 136, 0.2); color: #888; }
.condition-badge.cgc { background: rgba(142, 202, 230, 0.2); color: #8ecae6; }

/* ============================================
   21. Condition selector
   ============================================ */
.condition-selector {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
    margin-bottom: 0.5rem;
}
.condition-selector .tier-btn {
    padding: 4px 12px;
    border-radius: 14px;
    font-size: 0.75rem;
    font-weight: 600;
    border: 1px solid var(--duckery-border);
    background: transparent;
    color: var(--bs-body-color);
    cursor: pointer;
    transition: all 0.15s;
}
.condition-selector .tier-btn:hover { border-color: var(--duckery-accent); }
.condition-selector .tier-btn.selected { background: var(--duckery-accent); border-color: var(--duckery-accent); color: #fff; }

.condition-cgc-input {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.25rem;
}
.condition-cgc-input input {
    width: 80px;
}
.condition-notes-display {
    font-size: 0.75rem;
    color: var(--duckery-muted);
    font-style: italic;
    margin-top: 2px;
}

/* ============================================
   22. Copy cards (per-copy detail)
   ============================================ */
.copy-card {
    background: var(--duckery-surface);
    border-radius: 4px;
    padding: 0.75rem;
    margin-bottom: 0.5rem;
    border-left: 3px solid var(--duckery-border);
}
.copy-card.primary { border-left-color: var(--duckery-accent); }
.copy-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.5rem;
}
a.copy-label { color: inherit; text-decoration: none; }
a.copy-label:hover { color: var(--duckery-gold); text-decoration: underline; }
a.copy-label:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.copy-card-header .copy-label {
    font-weight: 600;
    font-size: 0.85rem;
}
.copy-card-header .copy-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}
.special-chip {
    font-size: 0.6rem;
    padding: 1px 6px;
    border-radius: 10px;
    background: rgba(212, 168, 90, 0.18);
    color: var(--duckery-gold);
    align-self: center;
}
.variant-chip {
    font-size: 0.6rem;
    padding: 1px 6px;
    border-radius: 10px;
    border: 1px solid var(--duckery-border);
    color: var(--duckery-muted-on-surface);
    align-self: center;
}

/* ============================================
   23. Photo gallery (per copy)
   ============================================ */
.photo-gallery {
    display: flex;
    gap: 4px;
    overflow-x: auto;
    padding: 4px 0;
}
.photo-gallery img {
    width: 60px;
    height: 80px;
    object-fit: cover;
    border-radius: 2px;
    cursor: pointer;
    transition: opacity 0.15s;
}
.photo-gallery img:hover { opacity: 0.8; }
.photo-gallery a:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.photo-gallery .add-photo-btn {
    width: 60px;
    height: 80px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--duckery-surface-hover);
    border: 1px dashed var(--duckery-border);
    border-radius: 2px;
    color: var(--duckery-muted);
    cursor: pointer;
    font-size: 1.2rem;
    flex-shrink: 0;
}
.photo-gallery .add-photo-btn:hover { border-color: var(--duckery-accent); color: var(--duckery-accent); }
/* The trigger is a real button now, so it needs the chrome reset and the focus ring its <div> ancestor never had. */
.photo-gallery .add-photo-btn { font-family: inherit; padding: 0; }
.photo-gallery .add-photo-btn:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.photo-gallery-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    flex-shrink: 0;
}
.photo-gallery-item .btn { font-size: 0.65rem; padding: 0 4px; }
.photo-gallery-order, .photo-gallery-confirm { display: flex; gap: 2px; }
/* No-op controls dim via [data-empty] (#261/#264, ReasonedControl.EmptyFlag): 0.7 keeps the label AA-legible,
   matching .year-chip.dim. A surface opts in by setting the attribute — no per-surface rule needed. */
[data-empty] { opacity: 0.7; }
/* The glyph sits near the AA floor already, so an end-of-list arrow marks itself with a dashed edge instead —
   :not(:disabled) so a lock (photoActionBusy) still dims it via .btn:disabled, like every other locked arrow. */
.photo-gallery-order [data-empty]:not(:disabled) { opacity: 1; border-style: dashed; }
.photo-gallery-order [aria-disabled="true"] { opacity: 0.65; }
@media (pointer: coarse) { .photo-gallery-order .btn { min-width: 24px; min-height: 24px; } }
/* Why a control would do nothing, said on screen and announced; mounted always so the live region can fire. */
.control-reason { display: block; font-size: 0.8rem; color: var(--duckery-muted-on-surface); margin: 0 0 0.5rem; }
.control-reason:empty { display: none; }
.remember-me { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; }
/* The confirm is the irreversible action, so it clears the 24px minimum the tile's other controls sit under. */
.photo-gallery-confirm .btn { min-height: 24px; padding: 0 8px; }
/* Single-activation photo picker: the label is the button; the input stays focusable but invisible. */
.photo-add, .photo-camera { position: relative; overflow: hidden; }
.photo-add input[type="file"], .photo-camera input[type="file"] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.photo-add:focus-within, .photo-camera:focus-within { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.photo-camera { display: none; }
@media (hover: none) and (pointer: coarse) { .photo-camera { display: inline-block; } }
.photo-add-control { display: contents; }
[data-photo-paste][aria-disabled="true"] { opacity: 0.65; }
.photo-drop-active { position: relative; outline: 2px dashed var(--duckery-gold); outline-offset: 4px; }
.photo-drop-active::after { content: attr(data-drop-label); position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; justify-content: center; padding: 0.5rem; text-align: center; font-weight: 600; color: var(--bs-body-color); background: color-mix(in srgb, var(--bs-body-bg) 85%, transparent); border-radius: inherit; pointer-events: none; }
/* Muted-on-surface at ≥4.5:1 (--duckery-muted falls just short on the card surface). */
.copy-photo-cap, .copy-photo-progress { font-size: 0.7rem; color: var(--duckery-muted-on-surface); }
.photo-gallery-duplicate-note { display: block; font-size: 0.7rem; color: var(--duckery-muted-on-surface); }
/* Post-claim block: one flex item stacking the visible success line over the offer row. */
.add-block { display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem; min-width: 0; }
.add-confirm { font-size: 0.8rem; color: var(--bs-body-color); }
.add-offer { display: inline-flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
/* Touch-first targets on the new offer surface; compact again where a fine pointer is guaranteed. */
.add-offer .btn, .add-offer .photo-add, .add-offer .photo-camera { min-height: 44px; }
@media (hover: hover) and (pointer: fine) {
    .add-offer .btn, .add-offer .photo-add, .add-offer .photo-camera { min-height: 0; }
}
.muted-on-surface { color: var(--duckery-muted-on-surface); }

/* ============================================
   26. Standing bid cards & form
   ============================================ */
.standing-bid-card {
    background: var(--duckery-surface);
    border-radius: 4px;
    padding: 0.75rem;
    margin-bottom: 0.5rem;
    border-left: 3px solid #8ecae6;
}
.standing-bid-card .bid-amount {
    font-size: 1.1rem;
    font-weight: 700;
    color: #fff;
}
.standing-bid-card .bid-meta {
    font-size: 0.75rem;
    /* Muted-on-surface at ≥4.5:1 (--duckery-muted falls just short on the card surface). */
    color: var(--duckery-muted-on-surface);
    margin-top: 2px;
}
.standing-bid-card .bid-actions {
    display: flex;
    gap: 4px;
    margin-top: 0.5rem;
}


/* ============================================
   27. Restoration badge
   ============================================ */
.restoration-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.65rem;
    font-weight: 600;
}
.restoration-badge.original { background: rgba(38, 176, 80, 0.15); color: #26b050; }
.restoration-badge.conserved { background: rgba(255, 183, 3, 0.15); color: #ffb703; }
.restoration-badge.restored { background: rgba(233, 69, 96, 0.15); color: #e94560; }

/* ============================================
   29. A+C visual language — applied to the STABLE V1 collection grid only,
   ahead of the full §4.8.10 sweep.
   Scoped gold accent via --duckery-gold; the global --duckery-accent (red)
   is left untouched for the not-yet-swept surfaces. Approved 2026-06-01
   (see ~/.gstack/projects/belgaard-duckery/designs/.../approved.json).
   ============================================ */
:root { --duckery-gold: #d4a85a; --duckery-gold-hover: #c2954a; }

/* Collection-grid filter bar → C's text + gold-hairline treatment */
/* View toggle (Shelf/Grid) → gold active instead of red */

/* ============================================
   31. PR-B: anonymous catalog landing + login nudge (T10)
   ============================================ */
/* Status/error toast (used by the anonymous landing's catalog-load failure). */

/* Quiet value-prop band under the top bar — one product sentence, not a hero. */

/* "Se alle årgange" drill-in affordance at the end of each shelf row. */

/* No-cover placeholder on the anonymous landing + browse. */

/* Catalog browse (drill-in) header. */

/* Login nudge = centered modal. Sits ABOVE the issue-detail sheet (z 1050) when opened from inside
   it, and reads as a focused login prompt rather than the wide right-side sheet panel. */

/* Browse covers open a lightweight issue-detail sheet on tap. */
/* Cover tap target: a real button so it's keyboard-focusable (Enter/Space opens the detail). */

/* Slide-up sheet primitives: full-width bottom sheet on mobile, right-side panel on desktop. */
@media (min-width: 768px) {
    .sheet-panel {
        top: 0;
        bottom: 0;
        right: 0;
        left: auto;
        width: 460px;
        height: auto;
        border-radius: 0;
        box-shadow: -4px 0 24px rgba(0, 0, 0, 0.5);
    }
}


/* ============================================
   32. PR-B: front-page era fan (one cover per year-group, overlapping, format-aware) + browse year divider
   ============================================ */

/* ============================================
   Consolidated view (#115): MagazineObject tile
   frame = ownership (solid/dashed/neutral), interior = photo, parts at the edges
   ============================================ */
.mag-object {
    /* Parts sit ON the cover as tabs. Grid tiles size them to the tile (cq units resolve against this container);
       the detail view overrides them to touch-target sizes. The *-clear vars are how far a populated edge's tabs
       reach into the cover, so side tabs, badges and the label stay clear of them. */
    container-type: inline-size;
    --mag-tab: 15cqi;
    --mag-tab-length: 28cqi;
    --mag-tab-gap: 2cqi;
    --mag-inset: 3cqi;
    --mag-side-inset: var(--mag-inset);
    --mag-top-clear: 0px;
    --mag-bottom-clear: 0px;
    --mag-left-clear: 0px;
    --mag-right-clear: 0px;
    position: relative;
    /* Real cover aspect when the object supplies one (set inline as --mag-aspect: W/H); else the portrait default. */
    aspect-ratio: var(--mag-aspect, 2 / 3);
    /* Relative size: fraction of the grid cell (set inline as --mag-scale), centered; 1 = fill the cell (default). */
    width: calc(var(--mag-scale, 1) * 100%);
    margin-inline: auto;
    border-radius: var(--duckery-cover-radius);
    overflow: hidden;
    box-sizing: border-box;
}
.mag-object:has(.mag-top) { --mag-top-clear: calc(var(--mag-tab) + var(--mag-inset)); }
.mag-object:has(.mag-bottom) { --mag-bottom-clear: calc(var(--mag-tab) + var(--mag-inset)); }
.mag-object:has(.mag-left) { --mag-left-clear: calc(var(--mag-tab) + var(--mag-side-inset)); }
.mag-object:has(.mag-right) { --mag-right-clear: calc(var(--mag-tab) + var(--mag-side-inset)); }
/* Ownership frame (style, not colour alone, for a11y) hugs the cover. Outline, not border, so the frame never
   changes the cover's box; inset because the tile's overflow clip would swallow an outer one. */
.mag-object.owned .mag-primary { outline: 2px solid var(--duckery-gold); }
.mag-object.not-owned .mag-primary { outline: 2px dashed var(--duckery-muted); }
.mag-object.neutral .mag-primary { outline: 1px solid var(--duckery-border); }

.mag-open {
    display: block;
    width: 100%;
    height: 100%;
    padding: 0;
    border: none;
    background: none;
    cursor: default;
    position: relative;
}
/* Only the interactive (link) variant looks and behaves clickable; the role=img presentation does not. */
a.mag-open { cursor: pointer; text-decoration: none; color: inherit; }
a.mag-open:focus-visible { outline: none; }
/* Drawn above the cover and tabs: the cover fills the tile, so a ring on the link itself would sit underneath. */
a.mag-open:focus-visible::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 7;
    box-shadow: inset 0 0 0 3px var(--duckery-gold), inset 0 0 0 6px rgba(10, 12, 30, 0.85);
    pointer-events: none;
}

.mag-primary {
    position: absolute;
    inset: 0;
    z-index: 2;
    box-sizing: border-box;
    background: var(--duckery-surface);
    border-radius: var(--duckery-cover-radius);
    overflow: hidden;
    outline-offset: -2px;
}
.mag-primary img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mag-primary.photo-dimmed img,
.mag-primary img[data-cover-catalog],
.fullview-page-image-frame.photo-dimmed img,
img.fullview-option-thumb.photo-dimmed,
img.fullview-option-thumb[data-cover-catalog],
.related-item img.photo-dimmed { filter: saturate(0.15) brightness(0.6); }
.mag-primary:has(img[data-cover-catalog]) :is(.mag-photo-badge, .mag-photo-warning) { display: none; }
.photo-hatch {
    background-image: repeating-linear-gradient(45deg, #1a2744 0, #1a2744 6px, #16213e 6px, #16213e 12px);
}
/* Cover chain (js/cover-fallback.js): the hatch sibling stays collapsed until revealed on final failure. */
[data-cover-hatch][hidden] { display: none !important; }
.mag-primary.photo-hatch .mag-photo-badge { display: none; }
.cover-hero:has(> img[data-cover-failed]) + div { display: none; }
span.band-cover.photo-hatch { width: 30px; height: 44px; margin-left: -28px; }
img.band-cover[data-cover-failed]:first-child + span.band-cover.photo-hatch { margin-left: 0; }
span.band-cover.photo-hatch:not([hidden]) + img.band-cover { margin-left: -28px; }
.cover-cell .photo-hatch { display: block; width: 100%; height: 100%; }
.photo-hatch.cover-block { display: block; width: 100%; max-width: 240px; aspect-ratio: 2 / 3; }
.mag-photo-badge {
    position: absolute;
    top: calc(var(--mag-top-clear) + 4px); left: calc(var(--mag-left-clear) + 4px);
    background: rgba(38, 176, 80, 0.85);
    color: #fff;
    font-size: 0.55rem;
    font-weight: 700;
    padding: 1px 5px;
    border-radius: 3px;
    z-index: 5;
}

/* Only ever set on the single full-view's primary (CoverMismatchChecker), so full-size styling is fine here.
   top clears .mag-photo-badge, which always co-occurs (both require PhotoSource.UserPhoto) and would otherwise sit under this band. */
.mag-photo-warning {
    position: absolute;
    top: calc(var(--mag-top-clear) + 22px); left: var(--mag-left-clear); right: var(--mag-right-clear);
    background: rgba(255, 183, 3, 0.92);
    color: #1a1400;
    font-size: 0.8rem;
    font-weight: 700;
    text-align: center;
    padding: 6px 8px;
    z-index: 6;
}
.mag-photo-warning::before { content: "⚠ "; }

.mag-label {
    position: absolute;
    bottom: calc(var(--mag-bottom-clear) + 4px); left: calc(var(--mag-left-clear) + 4px);
    background: rgba(0, 0, 0, 0.65);
    color: #ddd;
    font-size: 0.65rem;
    font-weight: 600;
    padding: 1px 5px;
    border-radius: 3px;
    z-index: 4;
    max-width: calc(100% - var(--mag-left-clear) - 8px);
    box-sizing: border-box;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Tabs: tillæg top, bagklap right, indlæg bottom, omslag left, centred on their edge so the corners stay free
   for the count chips. Side edges run between the top and bottom tabs. */
.mag-edge { position: absolute; display: flex; gap: var(--mag-tab-gap); justify-content: center; z-index: 3; }
.mag-top, .mag-bottom { left: var(--mag-side-inset); right: var(--mag-side-inset); height: var(--mag-tab); flex-direction: row; }
.mag-top { top: var(--mag-inset); }
.mag-bottom { bottom: var(--mag-inset); }
.mag-left, .mag-right {
    top: calc(var(--mag-top-clear) + var(--mag-inset));
    bottom: calc(var(--mag-bottom-clear) + var(--mag-inset));
    width: var(--mag-tab);
    flex-direction: column;
}
.mag-left { left: var(--mag-side-inset); }
.mag-right { right: var(--mag-side-inset); }

.mag-part {
    flex: 0 1 var(--mag-tab-length);
    min-width: 0;
    min-height: 0;
    overflow: hidden;
    background-color: var(--duckery-surface);
    box-sizing: border-box;
    border-radius: 3px;
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.8);
}
.mag-part img { width: 100%; height: 100%; object-fit: cover; object-position: top; display: block; }
.mag-part.photo-dimmed,
.mag-part:has(img[data-cover-catalog]) { opacity: 0.6; }
.mag-part.owned { border: 2px solid var(--duckery-gold); }
.mag-part.not-owned { border: 2px dashed var(--duckery-muted); }
.mag-part.neutral { border: 2px solid rgba(255, 255, 255, 0.85); }
.mag-part-link { display: block; cursor: pointer; }
.mag-part-link:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: -2px; }
.mag-overflow {
    flex: 0 0 var(--mag-tab);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 0.55rem;
    font-weight: 700;
    color: #ddd;
    background: rgba(0, 0, 0, 0.7);
}

/* Variant / dublet counts — shown only when present, drawn over any tab in the corner. */
.mag-count {
    position: absolute;
    z-index: 6;
    top: 4px;
    right: 4px;
    font-size: 0.6rem;
    font-weight: 700;
    padding: 1px 5px;
    border-radius: 3px;
    background: rgba(0, 0, 0, 0.7);
    color: #ddd;
    line-height: 1.2;
}
.mag-count.mag-variants { border: 1px dashed var(--duckery-muted); }
.mag-count.mag-variants.owned { border: 1px solid var(--duckery-gold); }
/* When both show, stack the dublet chip under the variant chip. */
.mag-variants ~ .mag-duplicates { top: 24px; }

/* On a phone the grid runs nearly to the screen edges; the gap is what separates covers. */
@media (max-width: 767.98px) {
    .cover-grid { margin-inline: -0.75rem; gap: 4px; }
}

/* ============================================
   Consolidated view (#115): bands landing + segment grid
   Responsive redesign (design handoff, 2026-08-22): the shell caps and centers at 1720px, the
   sticky filter strip and the series index are new; everything else keeps its class names so the
   existing bUnit pins (band-cell, bands-lens, band-series-title, ...) still find their elements.
   ============================================ */
.bands-shell { max-width: 1720px; margin: 0 auto; padding: 0 clamp(1rem, 3vw, 2.5rem) 4rem; }

/* The sticky filter bar bleeds edge-to-edge under the top bar; its own inner row re-applies the cap. */
.bands-filter-strip {
    position: sticky; top: 52px; z-index: 110;
    margin: 0 calc(-1 * clamp(1rem, 3vw, 2.5rem));
    padding: 0.7rem clamp(1rem, 3vw, 2.5rem);
    background: rgba(18, 18, 21, 0.92);
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--duckery-border-divider);
}
/* Two atomic blocks (form, lens chips), never split internally, so a width that can't fit both side by
   side wraps them as WHOLE units — never the half-split (button orphaned, chips creeping up) a bare
   flex-wrap produced. Below 700px they stack outright as full-width rows (owner call: a controlled,
   tested layout at every width, not an emergent one). */
.bands-filter-strip-inner { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.bands-filter-strip .segment-filter { flex: 1 1 320px; min-width: 260px; margin: 0; flex-wrap: nowrap; }
.bands-filter-strip .bands-lens { flex: 0 0 auto; }
@media (max-width: 700px) {
    .bands-filter-strip .segment-filter { flex-basis: 100%; }
    .bands-filter-strip .bands-lens { flex-basis: 100%; }
}

/* nowrap: the aside is a HIDE/SHOW toggle (below), never a third "stacked above the content" state. */
.bands-content { display: flex; gap: clamp(1.5rem, 3vw, 3rem); align-items: flex-start; flex-wrap: nowrap; padding-top: clamp(1rem, 2.5vw, 2rem); }

/* Series index (new): a sticky table of contents, structural facts only — stable across lens switches.
   No overflow of its own — a second scroll owner alongside .bands-index-more would fight it over which
   region moves the owner's own series list, so this one never scrolls and stays sized to its content. */
.bands-index { flex: 1 1 230px; max-width: 300px; position: sticky; top: 118px; }
.bands-index-header { margin-bottom: 0.9rem; }
/* An h1 for the heading outline (the outside section's h2 must not be the page's first heading); now the aside's own header. */
.bands-scope { display: block; color: var(--duckery-muted-on-surface); font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; margin: 0; }
/* The label is the editor's focus hand-off target (tabindex=-1), so it needs the shared gold ring. */
.bands-scope:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.bands-index-subline { color: var(--duckery-muted-on-surface); font-size: 0.8rem; margin: 0.3rem 0 0; }
/* Its own bounded scroll region, same reasoning as .bands-index-more: unbounded (IndexSeries() has no
   cap), so a scope with many series needs somewhere to go besides pushing the sticky aside past the
   viewport — each list handles its own overflow rather than the parent aside handling either's. */
.bands-index-nav { display: flex; flex-direction: column; gap: 0.55rem; border-left: 2px solid var(--duckery-border-divider); padding-left: 0.9rem; margin-top: 0.75rem; max-height: 40vh; overflow-y: auto; }
.bands-index-nav::-webkit-scrollbar { width: 4px; }
.bands-index-nav::-webkit-scrollbar-thumb { background: var(--duckery-border); border-radius: 2px; }
.bands-index-nav { scrollbar-width: thin; scrollbar-color: var(--duckery-border) transparent; }
.bands-index-row { flex-shrink: 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; column-gap: 0.5rem; }
.bands-index-link { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; min-width: 0; color: inherit; text-decoration: none; font-size: 0.85rem; font-weight: 500; }
.bands-index-link:only-child { grid-column: 1 / -1; }
.bands-index-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bands-index-link:hover { color: var(--duckery-gold); }
.bands-index-link:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.bands-index-total { flex-shrink: 0; color: var(--duckery-muted-on-surface); font-variant-numeric: tabular-nums; }
.bands-index-bar { grid-column: 1 / -1; width: 100%; height: 3px; border-radius: 1.5px; background: var(--duckery-border); overflow: hidden; margin-top: 0.2rem; }
.bands-index-bar > span { display: block; height: 100%; background: #2bb673; width: calc(var(--pct, 0) * 1%); }
.bands-index-pct { min-width: 2.6em; text-align: right; font-size: 0.7rem; color: var(--duckery-muted-on-surface); font-variant-numeric: tabular-nums; }
.bands-index-summary { margin-top: 1rem; padding-top: 0.75rem; border-top: 1px solid var(--duckery-border-divider); font-size: 0.8rem; color: var(--duckery-muted-on-surface); }
.bands-index-summary a { display: block; margin-top: 0.3rem; color: var(--duckery-gold); text-decoration: none; }
.bands-index-summary a:hover { text-decoration: underline; }
.bands-index-summary a:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }

/* v0.5 §W1c.2: the rest of the catalog, beneath the scope's own series list — same aside, same 1080px breakpoint. */
.bands-index-more-heading { margin-top: 1.1rem; padding-top: 0.9rem; border-top: 1px solid var(--duckery-border-divider); font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--duckery-muted-on-surface); }
/* Its own scroll region, not the aside's (see .bands-index): this list is the one part that routinely
   outgrows the viewport, so it scrolls in place while the scope's own series list above stays put. */
.bands-index-more { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.6rem; padding-left: 0.9rem; border-left: 2px solid var(--duckery-border-divider); max-height: 260px; overflow-y: auto; }
.bands-index-more::-webkit-scrollbar { width: 4px; }
.bands-index-more::-webkit-scrollbar-thumb { background: var(--duckery-border); border-radius: 2px; }
.bands-index-more { scrollbar-width: thin; scrollbar-color: var(--duckery-border) transparent; }
.bands-index-more-link { color: inherit; text-decoration: none; font-size: 0.82rem; }
.bands-index-more-link:hover { color: var(--duckery-gold); }
.bands-index-more-link:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.bands-index-more-all { display: block; margin-top: 0.6rem; font-size: 0.8rem; color: var(--duckery-gold); text-decoration: none; }
.bands-index-more-all:hover { color: var(--duckery-gold); text-decoration: underline; }
.bands-index-more-all:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }

/* The main column: series sections. flex: 999 makes it claim all remaining width before the aside grows past its cap. */
.bands-sections { flex: 999 1 640px; min-width: 0; }

/* v0.5 W2.4: collapsed by default — a native <details>, so a returning visitor never re-pays this cost. */
.bands-orientation { margin-bottom: 1.25rem; padding: 0.6rem 0.9rem; border: 1px solid var(--duckery-border-divider); border-radius: 8px; background: var(--duckery-surface); font-size: 0.88rem; }
.bands-orientation summary { cursor: pointer; font-weight: 600; }
.bands-orientation summary:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
@media (pointer: coarse) {
    /* min-height alone, not display:flex — flex would replace the native list-item box and remove
       the disclosure triangle marker along with it. */
    .bands-orientation summary { min-height: 44px; padding-block: 0.6rem; }
}
.bands-orientation p { margin: 0.6rem 0 0; color: var(--duckery-muted-on-surface); }

.band-series { margin-bottom: clamp(1.75rem, 3vw, 2.75rem); }
.band-series:has(> .band-row) { margin-bottom: 1rem; }
/* Fragment target (D8 #serie- anchors): clears both sticky bars (52px top bar + ~66px filter strip). */
.band-series-title { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; font-size: clamp(1rem, 1.3vw, 1.25rem); font-weight: 600; border-bottom: 1px solid var(--duckery-border-divider); padding-bottom: 0.35rem; margin-bottom: 0.5rem; scroll-margin-top: 130px; }
.band-series-title:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
/* Server-computed (CatalogViewService): the series' true year span + decade count, never the calendar decade. */
.band-series-meta { flex: 0 0 auto; font-size: 0.8rem; font-weight: 400; color: var(--duckery-muted-on-surface); white-space: nowrap; }
/* Same subordinate caption idiom as the segment head's, one step up from the title it qualifies. */
.band-series-other-name { color: var(--duckery-muted); font-size: 0.8rem; margin: -0.25rem 0 0.4rem; overflow-wrap: anywhere; }
/* One line per series, always (owner call, 2026-08-22): the row never wraps, it scrolls — the aside
   (wide screens) and future per-series content (wanted-vs-dubletter matches) are what fill the rest
   of the width, not more columns of the same cards. */
.band-row {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: clamp(0.6rem, 1vw, 1rem);
    padding-bottom: 0.35rem;
    -webkit-overflow-scrolling: touch;
}
.band-row::-webkit-scrollbar { height: 4px; }
.band-row::-webkit-scrollbar-thumb { background: var(--duckery-border); border-radius: 2px; }

.band-cell {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    align-content: start;
    text-decoration: none;
    gap: 0.3rem 0.5rem;
    padding: 0.45rem;
    border: 1px solid var(--duckery-border);
    border-radius: 6px;
    background: var(--duckery-surface);
    color: inherit;
    cursor: pointer;
    text-align: left;
}
.band-cell:hover { background: var(--duckery-surface-hover); border-color: var(--duckery-border-hover); color: inherit; }
.band-cell:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.band-cell.dim { opacity: 0.5; }

/* Landing lens toggle (alt / min samling / mangler / extras). Chips are links (URL-driven ?lens=), so reset anchor styling. */
.bands-lens { margin-top: 0; }
.bands-lens .filter-chip { text-decoration: none; color: var(--bs-body-color); }
/* Gold replaces the shared filter-chip's red active fill here only — the weakest contrast pairing on this page. */
.bands-lens .filter-chip.active { background: var(--duckery-gold); border-color: var(--duckery-gold); color: #17171a; }
.bands-lens .filter-chip:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }

/* Decorative cover fan; the band's aria-label carries the name. One flat size everywhere (owner call,
   2026-08-22: bigger covers over more columns) rather than shrinking to fit a wrapped row. */
.band-fan { display: flex; align-items: flex-end; min-width: 0; overflow: hidden; }
.band-cell > :is(.band-fan, .band-bar), .band-cell.ghost .band-count, .band-cell.beyond .beyond-hint { grid-column: 1 / -1; }
.band-cover {
    position: relative;
    flex: 0 0 auto;
    max-height: 190px;
    max-width: 150px;
    height: auto;
    width: auto;
    border-radius: 3px;
    box-shadow: -4px 0 10px rgba(0, 0, 0, 0.55);
    margin-left: -82px;
}
.band-cover:first-child { margin-left: 0; }

.band-decade { font-weight: 600; font-size: clamp(0.9rem, 1vw, 1.05rem); font-variant-numeric: tabular-nums; }
.band-count { font-size: 0.8rem; color: var(--duckery-muted-on-surface); font-variant-numeric: tabular-nums; }
.band-bar { width: 100%; height: 4px; border-radius: 2px; background: var(--duckery-border); overflow: hidden; }
.band-bar > span { display: block; height: 100%; background: #2bb673; width: calc(var(--pct, 0) * 1%); }

/* Beyond the preset (#115): "+ flere år" reveals out-of-preset decades (greyed, dashed = not tracked). */
.band-more {
    align-self: center;
    min-width: 96px;
    padding: 0.5rem 0.6rem;
    border: 1px dashed var(--duckery-border);
    border-radius: 4px;
    background: transparent;
    color: var(--duckery-gold);
    cursor: pointer;
    font-size: 0.8rem;
}
.band-more:hover { background: var(--duckery-surface-hover); }
.band-more:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.band-cell.beyond, .band-cell.ghost { border-style: dashed; opacity: 0.6; }
.band-cell.beyond .beyond-hint { font-style: italic; }
/* The ghost's count line is its payload: cell text color, since muted #888 composites below AA under the 0.6 group. */
.band-cell.ghost .band-count { font-style: italic; color: var(--bs-body-color); }
/* The enriched cell travels somewhere its neighbors don't (D9b): the door's gold marks the hint. */
.band-cell.beyond .beyond-hint-door { color: var(--duckery-gold); }

/* D9b: a qualifying band adds a sibling outside link below its cell. Sizing lives here, not on
   .band-cell (always nested in a stack in current usage) — one flat card size everywhere, scaling
   only at the very narrow end so a stack never exceeds its own viewport (min(), not a media query). */
.band-cell-stack { display: flex; flex-direction: column; gap: 0.25rem; flex: 0 0 min(78vw, 320px); }
.band-cell-stack > .band-cell { flex: 1 0 auto; }
/* Hover/focus ride the shared gold-link lists below; the base differs (narrower, smaller under the cell). */
.band-outside { max-width: 160px; font-size: 0.75rem; color: var(--duckery-gold); text-decoration: none; }

/* The aside is a hide/show toggle, never a third "stacked above the content" state (owner-reported
   bug, 2026-08-22) — tested at this exact boundary, not merely reasoned about. */
@media (max-width: 1080px) {
    .bands-index { display: none; }
}

/* Mobile counterpart to .bands-index-more (hidden above): a fixed handle + full-screen sheet, driven by
   rest-sheet.js. Fixed/overlay rather than in-flow, so it can never become the aside's "third state". */
.rest-sheet { display: none; }
@media (max-width: 1080px) {
    .rest-sheet { display: block; }
}
.rest-sheet-handle {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
    display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
    min-height: 44px; padding: 0.45rem 1rem calc(0.55rem + env(safe-area-inset-bottom));
    background: var(--duckery-surface); border-top: 1px solid var(--duckery-border);
    color: inherit; text-decoration: none; touch-action: none;
}
.rest-sheet-handle:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: -2px; }
.rest-sheet-grip { width: 36px; height: 4px; border-radius: 2px; background: var(--duckery-border); }
.rest-sheet-label { font-size: 0.8rem; color: var(--duckery-muted-on-surface); }
.rest-sheet-panel {
    position: fixed; inset: 0; z-index: 200;
    display: flex; flex-direction: column; gap: 0.5rem;
    padding: calc(1.25rem + env(safe-area-inset-top)) 1.25rem 1rem;
    background: var(--duckery-surface);
    transform: translateY(100%);
    transition: transform 220ms ease-out;
}
.rest-sheet-panel.dragging { transition: none; }
.rest-sheet-panel-heading { margin: 0; font-size: 1rem; font-weight: 600; }
@media (prefers-reduced-motion: reduce) {
    .rest-sheet-panel { transition: none; }
}

/* Drag-start zones (rest-sheet.js): pointer-events toggled in JS. Not a bottom/top-edge sliver, so
   it doesn't compete with the OS's own home-indicator/back-gesture zones there. touch-action:none,
   not a directional pan-up or pan-down value: confirmed via real-hardware testing (a Pixel Chrome,
   over USB debugging) that a directional value here gets pointercancel'd by the browser almost the instant
   a touch starts at the scroll boundary the catcher arms at, discarding the gesture with no visible
   effect — reproducible with the official wiring in complete isolation, unrelated to element size,
   aria-hidden, overscroll-behavior, or any DOM writes rest-sheet.js makes during the drag. The
   tradeoff: an ordinary scroll-back touch starting inside the zone no longer passes through to native
   scrolling (a small area, and releasing and scrolling from just outside it works as always). */
.rest-sheet-catcher, .series-back-catcher { display: none; }
@media (max-width: 1080px) {
    /* Cover images and links are draggable by default; a browser that recognizes drag intent over one
       mid-gesture cancels the in-flight pointer stream (rest-sheet.js never sees pointerup), no matter
       how quickly a dragstart listener calls preventDefault() — the cancellation is a side effect of
       recognizing the drag, not of the drag completing. Scoped to the two pages the swipe gesture is
       actually wired up on, so a mouse user's native drag-and-drop is untouched everywhere else. */
    .bands-shell img, .bands-shell a, .series-index img, .series-index a { -webkit-user-drag: none; }

    .rest-sheet-catcher, .series-back-catcher {
        display: block; position: fixed; left: 0; right: 0; z-index: 85;
        pointer-events: none; touch-action: none;
    }
    .rest-sheet-catcher { bottom: 0; height: 55vh; }
    .series-back-catcher { top: calc(52px + env(safe-area-inset-top)); height: 55vh; }
}

/* The segment door's own lines; head, cap, tiles and the exit link reuse the landing door's treatment. */
.segment-outside-subhead, .segment-outside-note { font-size: 0.8rem; color: var(--duckery-muted); margin: 0 0 0.5rem; }
.segment-outside-exit { margin: 0.75rem 0 0; }

@media (pointer: coarse) {
    .band-outside { display: flex; align-items: center; min-height: 44px; }
    .band-cell { min-height: 44px; }
}

/* Excerpt shelf (search-as-filter slice 3): tiles per band under the series header, band title as decade caption. */
.band-context { font-size: 0.8rem; color: var(--duckery-muted); margin: 0.35rem 0 0.3rem; }

/* The omfang door (slice 3). Closed: a full-width dashed row — readable control, not the beyond cell's faded strip. */
.outside-door {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 44px;
    margin: 1rem 0;
    padding: 0.6rem 0.75rem;
    border: 1px dashed var(--duckery-border);
    border-radius: 4px;
    background: transparent;
    color: var(--duckery-gold);
    font-size: 0.85rem;
    text-decoration: none;
    text-align: center;
}
.outside-door:hover { background: var(--duckery-surface-hover); color: var(--duckery-gold); }
.outside-door:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }

/* Open: no container box — the heading and the dashed idiom it replaced carry the "elsewhere" signal. */
.outside-section { margin: 1rem 0 0.5rem; }
.outside-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; border-top: 1px dashed var(--duckery-border); padding-top: 0.6rem; }
/* A door group's head: the series link and its Tilføj on one row. */
.outside-series-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; }
.outside-series-title { margin: 0 0 0.4rem; min-width: 0; }
.outside-series-link { color: var(--duckery-gold); text-decoration: none; overflow-wrap: anywhere; }
.outside-series-link:hover { color: var(--duckery-gold); text-decoration: underline; }
.outside-series-link:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
@media (pointer: coarse) {
    .outside-series-link { display: inline-flex; align-items: center; min-height: 44px; }
}
.outside-series-note { font-size: 0.8rem; color: var(--duckery-muted); margin: 0 0 0.4rem; }
.outside-heading { font-size: 1.05rem; font-weight: 600; margin: 0 0 0.4rem; }
.outside-heading:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.outside-cap { font-size: 0.8rem; color: var(--duckery-muted); margin: 0 0 0.5rem; }

/* find serie (#115 + series-selection-plan.md D3a): the door on the scope row — drill in, or add to the omfang.
   Mobile-first: the door takes its own wrapped line in the scope row; ≥768px sits inline. */
.find-series { margin: 0; flex-basis: 100%; min-width: 0; }
@media (min-width: 768px) {
    .find-series { flex-basis: auto; }
}
.find-series-open {
    min-height: 44px;
    padding: 0.35rem 1rem;
    border-radius: 22px;
    border: 1px dashed var(--duckery-border);
    background: transparent;
    color: var(--duckery-gold);
    cursor: pointer;
    font-size: 0.8rem;
}
.find-series-open:hover { background: var(--duckery-surface-hover); }
.find-series-open:focus-visible, .find-series-close:focus-visible {
    outline: 2px solid var(--duckery-gold); outline-offset: 2px;
}
.find-series-panel { display: flex; gap: 0.4rem; align-items: flex-start; max-width: 520px; }
.find-series-panel .series-search { flex: 1; min-width: 0; }
.find-series-close {
    background: none;
    border: none;
    color: var(--duckery-muted);
    cursor: pointer;
    font-size: 1.1rem;
    line-height: 1;
    min-width: 44px;
    min-height: 44px;
}
.find-series-close:disabled { opacity: 0.7; cursor: default; }

/* Series search (series-selection-plan.md D3a/D3b): shared by the scope editor and the landing door. */
.series-search { max-width: 480px; }
.series-search-input {
    width: 100%;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--duckery-border);
    border-radius: 4px;
    background: var(--duckery-surface);
    color: inherit;
}
.series-search-status { color: var(--duckery-muted-on-surface); font-size: 0.85rem; padding: 0.4rem 0; min-height: 1.5rem; }
.series-search-heading { color: var(--duckery-muted-on-surface); font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; margin: 0.5rem 0 0.2rem; }
.series-search-error { color: var(--duckery-tomato, #e07a5f); font-size: 0.85rem; padding: 0.2rem 0; }
.series-search-results { list-style: none; margin: 0.4rem 0 0; padding: 0; }
.series-search-row { display: flex; align-items: center; gap: 0.5rem; }
.series-search-result {
    display: flex;
    flex: 1;
    min-width: 0; /* lets the title's ellipsis engage instead of widening the row */
    text-decoration: none;
    justify-content: space-between;
    gap: 0.75rem;
    text-align: left;
    padding: 0.6rem;
    min-height: 44px;
    align-items: center;
    /* Lets the matched name take its own line; reflow beats overflow if the meta ever outgrows the row too. */
    flex-wrap: wrap;
    border-radius: 4px;
    background: transparent;
    color: inherit;
}
.series-search-result:hover { background: var(--duckery-surface-hover); color: inherit; }
.series-search-result:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; color: inherit; }
/* Zero basis so a long title ellipsises rather than wrapping the meta onto its own line. */
.series-search-title { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Full width: the title column has no room for a name at 375px. */
.series-search-other-name { flex-basis: 100%; min-width: 0; color: var(--duckery-muted-on-surface); font-size: 0.8rem; overflow-wrap: anywhere; }
.series-search-meta { color: var(--duckery-muted-on-surface); font-size: 0.8rem; white-space: nowrap; }
.series-search-add {
    min-height: 44px;
    padding: 0.35rem 0.9rem;
    border-radius: 16px;
    border: 1px solid var(--duckery-gold);
    background: transparent;
    color: var(--duckery-gold);
    cursor: pointer;
    font-size: 0.85rem;
    white-space: nowrap;
}
.series-search-add:hover:not(:disabled) { background: var(--duckery-surface-hover); color: var(--duckery-gold); }
.series-search-add:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.series-search-add:disabled { opacity: 0.7; cursor: default; }
.series-search-state { color: var(--duckery-muted-on-surface); font-size: 0.85rem; white-space: nowrap; }
.series-search-refusal { color: var(--duckery-tomato, #e07a5f); font-size: 0.85rem; padding: 0.1rem 0.6rem 0.4rem; }
.series-search-escape {
    display: inline-flex; /* min-height is inert on an inline element (the beyond page's escape is an <a>) */
    align-items: center;
    background: none;
    border: none;
    color: var(--bs-link-color);
    cursor: pointer;
    font-size: 0.85rem;
    text-decoration: underline;
    min-height: 44px;
    padding: 0 0.4rem;
}
.series-search-escape:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.series-search-cap { color: var(--duckery-muted-on-surface); font-size: 0.85rem; padding: 0.4rem 0.6rem; }

/* Series inventory (the editor's disclosed member list; every state is text, never color alone). */
.series-inventory { list-style: none; margin: 0.4rem 0; padding: 0; max-width: 480px; }
.series-inventory-row { padding: 0.1rem 0; }
.series-inventory-label { display: flex; align-items: center; gap: 0.5rem; min-height: 44px; cursor: pointer; }
/* min-width: 0 keeps the title's ellipsis engaging instead of widening the row (#242). */
.series-inventory-main { display: flex; flex-direction: column; min-width: 0; }
.series-inventory-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.series-inventory-other-name { color: var(--duckery-muted-on-surface); font-size: 0.8rem; overflow-wrap: anywhere; }
.series-inventory-count { color: var(--duckery-muted-on-surface); font-size: 0.85rem; margin-left: auto; white-space: nowrap; }
.series-inventory-pending { color: var(--duckery-gold); font-size: 0.85rem; }
.series-inventory-undo {
    background: none;
    border: none;
    color: var(--bs-link-color);
    cursor: pointer;
    font-size: 0.85rem;
    text-decoration: underline;
    min-height: 44px;
    padding: 0 0.4rem;
}
.series-inventory-undo:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.series-inventory-empty { color: var(--duckery-muted-on-surface); font-size: 0.85rem; padding: 0.4rem 0; }

/* Content depth (series-content-depth-plan.md §5 D1): a subordinate second line, indented and muted —
   a rare sub-decision under the row, never a second primary control. */
.series-inventory-depth {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-left: 1.5rem;
    min-height: 44px;
    color: var(--duckery-muted-on-surface);
    font-size: 0.85rem;
    cursor: pointer;
}
.series-inventory-depth-reason,
.series-inventory-depth-pending {
    margin-left: 1.5rem;
    color: var(--duckery-muted-on-surface);
    font-size: 0.8rem;
}

/* Beyond-page header action (series-selection-plan.md §7): commits like the landing door. */
.segment-add-series { margin: 0.25rem 0; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.segment-add-series-btn {
    min-height: 44px;
    padding: 0.35rem 1rem;
    border-radius: 22px;
    border: 1px solid var(--duckery-gold);
    background: transparent;
    color: var(--duckery-gold);
    cursor: pointer;
    font-size: 0.85rem;
}
.segment-add-series-btn:hover:not(:disabled) { background: var(--duckery-surface-hover); }
.segment-add-series-btn:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.segment-add-series-btn:disabled { opacity: 0.7; cursor: default; }

.segment-beyond-note {
    color: var(--duckery-muted);
    font-size: 0.85rem;
    font-style: italic;
    margin: -0.4rem 0 0.75rem;
}

.segment-grid { padding: 0.5rem 0; }
.segment-back { background: none; border: none; color: var(--duckery-gold); cursor: pointer; font-size: 0.85rem; padding: 0.25rem 0; text-decoration: none; display: inline-block; }
.segment-back:hover { color: var(--duckery-gold); }
.segment-back:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.segment-title { font-size: 1.05rem; font-weight: 600; margin: 0.25rem 0 0.75rem; }
/* Sticks under the 52px top bar (desktop only: a mobile keyboard-reduced viewport keeps the space);
   opaque page background so covers slide beneath it. */
.segment-head {
    background: var(--bs-body-bg, #121215);
    padding-bottom: 0.35rem;
}
.segment-head > :last-child { margin-bottom: 0; }
/* Keyboard focus scrolled into view must clear the top bar (+ the sticky head from the desktop breakpoint). */
.segment-grid a { scroll-margin-top: 4rem; }
@media (min-width: 768px) {
    .segment-head { position: sticky; top: 52px; z-index: 20; }
    /* 21rem: measured worst-case head is 325px (back, title, other names, Tilføj, scrub, filter, status, mark). */
    .segment-grid a { scroll-margin-top: 21rem; }
}

/* Slice 1 (search-as-filter): the segment filter form, count line, and match evidence. */
.segment-filter { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin: 0 0 0.5rem; }
.segment-filter-label { font-size: 0.8rem; color: var(--duckery-muted); white-space: nowrap; }
.segment-filter-input {
    flex: 1; min-width: 9rem; max-width: 34rem;
    background: var(--duckery-surface); border: 1px solid var(--duckery-border-control); border-radius: 6px;
    color: inherit; font-size: max(16px, 0.875rem); padding: 0.45rem 0.7rem; /* 16px floor: iOS zooms focused inputs below 16px */
}
.segment-filter-input:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 1px; }
/* Compact only for mouse-driven screens: a wide iPad is still iOS and must keep the 16px floor. */
@media (min-width: 768px) and (hover: hover) and (pointer: fine) {
    .segment-filter-input { font-size: 0.875rem; }
}
.segment-filter-submit {
    background: var(--duckery-navy-fill); border: 1px solid var(--duckery-navy-border); border-radius: 6px;
    color: #fff; font-size: 0.85rem; font-weight: 500; padding: 0.45rem 1.1rem; cursor: pointer; min-height: 38px;
    min-width: 6.2em; /* fits "Søger…" so the busy swap never shifts the row */
}
.segment-filter-submit:hover { background: var(--duckery-navy-hover-fill); border-color: var(--duckery-gold); }
.segment-filter-submit:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.segment-filter-hint { color: var(--duckery-muted); font-size: 0.78rem; margin: -0.25rem 0 0.4rem; }
/* Screen-reader only, never display:none (SRs drop hidden live regions). */
.segment-filter-announcer, .select-live {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.segment-filter-statusrow { display: flex; align-items: baseline; gap: 0.35rem; margin: 0 0 0.35rem; }
.segment-filter-status { font-size: 0.8rem; margin: 0; }
.segment-filter-clear, .segment-filter-outside, .outside-hide, .consolidated-empty a, .segment-outside-exit a { color: var(--duckery-gold); font-size: 0.8rem; text-decoration: none; }
.segment-filter-clear:hover, .segment-filter-outside:hover, .outside-hide:hover, .consolidated-empty a:hover, .segment-outside-exit a:hover, .band-outside:hover { color: var(--duckery-gold); text-decoration: underline; }
.segment-filter-clear:focus-visible, .segment-filter-outside:focus-visible, .outside-hide:focus-visible, .consolidated-empty a:focus-visible, .segment-outside-exit a:focus-visible, .band-outside:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
/* 0.7 keeps the year text AA-legible; the chip stays a working link (dimming is cosmetic only). */
.year-chip.dim { opacity: 0.7; }
/* The matched part's halo: a two-tone INSET ring (gold + dark separator) — an offset outline would be
   clipped by the tile's overflow:hidden, and a plain gold line would read as the 1px owned border. */
.mag-part.part-match { box-shadow: inset 0 0 0 2px var(--duckery-gold), inset 0 0 0 4px rgba(10, 12, 30, 0.85); }
/* The under-tile rail, one geometry for both states (D5): match evidence while filtering, the part-tile
   context caption otherwise. Centered like the scaled tiles above it, so the line never detaches sideways. */
.mag-evidence, .mag-context {
    display: block; min-height: 1.05rem; margin-top: 3px;
    color: var(--duckery-muted); font-size: 0.65rem; font-weight: 600; line-height: 1.05rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: center;
}
/* The segment head's unit line (D12): names what a parts page's tiles are, muted, under the title. */
.segment-unit-count,
.segment-other-names { color: var(--duckery-muted); font-size: 0.8rem; margin: -0.5rem 0 0.75rem; }
.segment-other-names { overflow-wrap: anywhere; }
.segment-depth-denominator { margin-left: 0.35rem; }
.segment-depth-toggle {
    display: inline-block;
    color: var(--duckery-gold);
    font-size: 0.85rem;
    min-height: 44px;
    line-height: 44px;
    margin: -0.5rem 0 0.25rem;
}
.segment-depth-toggle:hover { color: var(--duckery-gold); }
.segment-depth-toggle:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
/* Nested in a scrub row (below): sits right after the chips (the row's own gap spaces it), sized to the row rather than its own 44px line. */
.year-scrub .segment-depth-toggle, .decade-scrub .segment-depth-toggle {
    margin: 0;
    min-height: 32px;
    line-height: 32px;
}
.year-scrub { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: -0.25rem 0 0.9rem; }
.year-chip {
    display: inline-flex; align-items: baseline; gap: 4px;
    background: var(--duckery-surface); border: 1px solid var(--duckery-border); border-radius: 14px;
    padding: 4px 11px; font-size: 0.8rem; color: inherit; text-decoration: none; white-space: nowrap;
}
.year-chip:hover { border-color: var(--duckery-gold); color: inherit; }
.year-chip:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.year-chip.active { background: var(--duckery-gold); border-color: var(--duckery-gold); color: #14162b; font-weight: 600; }
.year-chip-count { color: var(--duckery-muted-on-surface); font-size: 0.68rem; }
.year-chip.active .year-chip-count { color: rgba(20, 22, 43, 0.8); }
.year-arrow { font-weight: 700; padding: 4px 9px; }

/* Sibling-decade nav — same chip idiom as the year scrubber. */
.decade-scrub { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: -0.25rem 0 0.9rem; }
.decade-chip {
    display: inline-flex; align-items: baseline; gap: 4px;
    background: var(--duckery-surface); border: 1px solid var(--duckery-border); border-radius: 14px;
    padding: 4px 11px; font-size: 0.8rem; color: inherit; text-decoration: none; white-space: nowrap;
}
.decade-chip:hover { border-color: var(--duckery-gold); color: inherit; }
.decade-chip:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.decade-chip.active { background: var(--duckery-gold); border-color: var(--duckery-gold); color: #14162b; font-weight: 600; }
.decade-arrow { font-weight: 700; padding: 4px 9px; }

/* ============================================
   Image viewer (full-screen pan/zoom)
   ============================================ */
.image-viewer { display: flex; flex-direction: column; gap: 0.4rem; padding: 0.5rem 0; }
.image-viewer-bar { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; }
.image-viewer-caption { font-size: 0.9rem; font-weight: 600; }
.image-viewer-source,
.fullview-page-image-badge { color: #fff; background: rgba(20, 20, 24, 0.85); border: 1px solid var(--duckery-border); border-radius: 3px; font-weight: 700; }
.image-viewer-source { display: inline-block; margin-left: 0.4rem; padding: 1px 6px; font-size: 0.75rem; }
.image-viewer-source.own-photo { background: #1a803a; border-color: transparent; }
.image-viewer-source-fallback { display: none; }
.image-viewer:has(img[data-cover-catalog]) .image-viewer-source.own-photo { display: none; }
.image-viewer:has(img[data-cover-catalog]) .image-viewer-source-fallback { display: inline-block; }
.image-viewer-attribution { color: var(--duckery-muted); font-size: 0.78rem; }
.image-viewer-attribution a:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.image-viewer-nav { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; }
.image-viewer-nav a { color: var(--duckery-gold); text-decoration: none; padding: 0.2rem 0.4rem; }
.image-viewer-nav a:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.image-viewer-zoom { margin-left: auto; display: flex; gap: 6px; }
.image-viewer-zoom button {
    width: 36px; height: 36px; border-radius: 6px; border: 1px solid var(--duckery-border);
    background: var(--duckery-surface); color: inherit; font-size: 1.15rem; line-height: 1; cursor: pointer;
}
.image-viewer-zoom button:hover { border-color: var(--duckery-gold); }
.image-viewer-zoom button:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.image-viewer-stage {
    position: relative; overflow: hidden; border-radius: 8px; background: #0a0b14;
    height: calc(100vh - 11rem); height: calc(100dvh - 11rem); min-height: 320px;
    display: flex; align-items: center; justify-content: center;
    touch-action: none; cursor: grab;
}
.image-viewer-stage.zoomed { cursor: move; }
.image-viewer-img { width: 100%; height: 100%; object-fit: contain; transform-origin: center center; user-select: none; }
.image-viewer-stage .photo-hatch { position: absolute; inset: 0; }
/* On a phone the image bleeds to both screen edges, like the detail view's cover (cancels .main-content's padding). */
@media (max-width: 767.98px) {
    .image-viewer-stage { margin-inline: -1rem; border-radius: 0; }
}

/* Detail view: the hero cover opens the image viewer. Focus is drawn inside: outline IS the ownership frame. */
a.mag-primary { display: block; cursor: zoom-in; text-decoration: none; color: inherit; }
a.mag-primary:focus-visible::after { content: ""; position: absolute; inset: 0; z-index: 1; box-shadow: inset 0 0 0 3px var(--duckery-gold), inset 0 0 0 6px rgba(10, 12, 30, 0.85); pointer-events: none; }
.consolidated-empty, .consolidated-error { color: var(--duckery-muted); padding: 1rem 0; }
.consolidated-error a { margin-left: 0.5rem; color: var(--duckery-gold); }

/* Full view (zoom 3) — mobile-first: the cover/anatomy owns the first screenful, meta stacks below.
   On a phone the cover bleeds to both screen edges (the margin cancels .main-content's padding). A 2/3 cover
   capped at 58vh wide stays within ~87vh tall; the 20rem floor keeps five part tabs and "+N" on an edge.
   Desktop returns to a side-by-side row. */
.fullview { padding: 0.5rem 0; }
.fullview-header { display: flex; flex-direction: column; align-items: center; gap: 1rem; margin-bottom: 1.25rem; }
.fullview-tile { flex: none; width: min(100vw, max(58vh, 20rem)); margin-inline: -1rem; }
.fullview-tile .mag-object, .fullview-tile .mag-primary { border-radius: 0; }
.fullview-tile .mag-label { display: none; }

/* Detail-view tabs are touch targets. */
.fullview-tile .mag-object {
    --mag-tab: 44px;
    --mag-tab-length: 8rem;
    --mag-tab-gap: 4px;
    --mag-inset: 6px;
}
/* Keeps side tabs out of the OS back-swipe strip at the screen edge. */
@media (pointer: coarse) {
    .fullview-tile .mag-object { --mag-side-inset: 12px; }
    /* A finger misses a tab by a few pixels: each tab also takes the taps between it and the tile's edge (and, at the
       top and bottom, the empty space beyond it) plus half the gap to its neighbours. The 2px is every tab's border. */
    .fullview-tile .mag-object { overflow: visible; }
    .fullview-tile .mag-part { position: relative; overflow: visible; }
    .fullview-tile .mag-part img { border-radius: 2px; }
    .fullview-tile .mag-part-link[href]::after { content: ""; position: absolute; inset: 0; }
    .fullview-tile :is(.mag-top, .mag-bottom) .mag-part-link::after {
        left: calc(var(--mag-tab-gap) / -2 - 2px);
        right: calc(var(--mag-tab-gap) / -2 - 2px);
    }
    .fullview-tile :is(.mag-left, .mag-right) .mag-part-link::after {
        top: calc(var(--mag-tab-gap) / -2 - 2px);
        bottom: calc(var(--mag-tab-gap) / -2 - 2px);
    }
    .fullview-tile .mag-top .mag-part-link::after { top: calc(-1 * (var(--mag-inset) + 0.5rem) - 2px); }
    .fullview-tile .mag-bottom .mag-part-link::after { bottom: calc(-1 * (var(--mag-inset) + 1rem) - 2px); }
    .fullview-tile .mag-left .mag-part-link::after { left: calc(-1 * var(--mag-side-inset) - 2px); }
    .fullview-tile .mag-right .mag-part-link::after { right: calc(-1 * var(--mag-side-inset) - 2px); }
}
/* An edge's empty stretch beside its tabs belongs to the cover underneath. */
.fullview-tile .mag-edge { pointer-events: none; }
.fullview-tile .mag-part { pointer-events: auto; }
.fullview-tile .mag-part { min-width: var(--mag-tab); min-height: var(--mag-tab); border-radius: 4px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.7); }
/* Outside the tab, with a dark halo: an owned tab's gold border would hide an inset ring. */
.fullview-tile .mag-part-link:focus-visible { outline: 3px solid var(--duckery-gold); outline-offset: 2px; box-shadow: 0 0 0 5px #000; }
.fullview-tile .mag-overflow { font-size: 0.85rem; color: #fff; text-decoration: none; border: 2px solid transparent; }
.fullview-parts { scroll-margin-top: 4rem; }
/* The phone header wraps to two rows. */
@media (max-width: 767.98px) {
    .fullview-parts { scroll-margin-top: 7rem; }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .fullview-tile .mag-part-link { transition: transform 0.12s ease, box-shadow 0.12s ease; }
    .fullview-tile .mag-part-link:hover:not(:focus-visible) { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.8); }
}
/* Detail view shows the whole scan: unusual formats (wide ottes comparisons) letterbox, never crop. */
.fullview-tile .mag-primary img { object-fit: contain; }
.fullview-meta { width: 100%; text-align: center; }
.fullview-meta h2 { margin: 0 0 0.25rem; font-size: 1.15rem; }
.fullview-sub { color: var(--duckery-muted); font-size: 0.85rem; }
.fullview-parent { font-size: 0.85rem; }
/* Symmetric negative margin cancels the touch-target padding so the text aligns with the meta column. */
.fullview-parent a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 0.6rem; margin: 0 -0.6rem; }
.fullview-parent a:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
/* The object page's up-links: the breadcrumb is permanent, the back link joins it only when it points elsewhere. */
.fullview-uplinks { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; }
.fullview-series-nav { min-width: 0; }
.fullview-series-link { color: var(--duckery-gold); font-size: 0.85rem; font-weight: 600; text-decoration: none; overflow-wrap: anywhere; padding: 0.25rem 0; display: inline-block; }
.fullview-series-link:hover { color: var(--duckery-gold); text-decoration: underline; }
.fullview-series-link:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
@media (pointer: coarse) {
    .fullview-series-link { display: inline-flex; align-items: center; min-height: 44px; padding: 0 0.6rem; margin: 0 -0.6rem; }
}
.fullview-issue-nav { display: flex; justify-content: space-between; gap: 1rem; margin-top: 1.5rem; }
.fullview-issue-link { color: var(--duckery-gold); font-size: 0.85rem; font-weight: 600; text-decoration: none; padding: 0.25rem 0; }
.fullview-issue-link:hover { text-decoration: underline; }
.fullview-issue-link:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.fullview-issue-next { margin-left: auto; }
@media (pointer: coarse) {
    .fullview-issue-link { display: inline-flex; align-items: center; min-height: 44px; }
}
.fullview-detail { margin-top: 0.75rem; text-align: left; }
.fullview-detail-desc { margin: 0 0 0.5rem; font-size: 0.9rem; white-space: pre-line; overflow-wrap: anywhere; }
.fullview-detail-source { margin: -0.35rem 0 0.6rem; font-size: 0.78rem; color: var(--duckery-muted); }
.fullview-detail-source a:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.fullview-detail-meta { display: grid; grid-template-columns: max-content 1fr; gap: 0.15rem 0.75rem; margin: 0; font-size: 0.85rem; }
.fullview-detail-meta dt { color: var(--duckery-muted); font-weight: normal; }
.fullview-detail-meta dd { margin: 0; overflow-wrap: anywhere; }
.fullview-detail-inducks { display: inline-block; margin-top: 0.6rem; font-size: 0.85rem; }
.fullview-detail-inducks:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }

.fullview-parts { margin-bottom: 1.5rem; }
.fullview-parts h3, .fullview-copies h3 { font-size: 0.9rem; margin: 0 0 0.5rem; }
.fullview-part-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.fullview-part { display: flex; align-items: stretch; gap: 0.5rem; }
.fullview-part-link { flex: 1 1 auto; display: flex; align-items: baseline; gap: 0.5rem; padding: 0.4rem 0.6rem; border-radius: 4px; background: var(--duckery-surface); text-decoration: none; color: var(--bs-body-color); }
a.fullview-part-link:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.fullview-part-link:hover { color: inherit; }
.fullview-part.owned .fullview-part-link { border-left: 3px solid var(--duckery-gold); }
.fullview-part.not-owned .fullview-part-link { border-left: 3px dashed var(--duckery-muted); }
.fullview-part-kind { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--duckery-muted-on-surface); flex: 0 0 auto; }
.fullview-part-name { flex: 1 1 auto; font-size: 0.85rem; }
/* Small text on a surface row needs the safe muted tone; --duckery-muted lands under AA there. */
.fullview-part-own { font-size: 0.7rem; color: var(--duckery-muted-on-surface); }
/* Shrinkable and wrappable so an open photo offer never crushes the part label on a phone. */
.fullview-part-actions { display: flex; align-items: center; flex: 0 1 auto; min-width: 0; flex-wrap: wrap; }
.fullview-option-row { align-items: center; }
.fullview-option-thumb { flex: 0 0 auto; width: 64px; height: 64px; object-fit: contain; border-radius: 3px; }
.fullview-option-text { flex: 1 1 auto; display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.fullview-option-desc { font-size: 0.75rem; color: var(--duckery-muted-on-surface); overflow-wrap: anywhere; }
.fullview-none { color: var(--duckery-muted); font-size: 0.85rem; }
.fullview-page-image-list { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 0.6rem; }
.fullview-page-image { position: relative; display: flex; flex-direction: column; gap: 0.25rem; width: 8.5rem; text-decoration: none; color: var(--bs-body-color); }
.fullview-page-image-badge {
    position: absolute; top: 4px; left: 4px;
    font-size: 0.6rem; padding: 1px 5px; pointer-events: none;
}
.fullview-page-image:hover { color: inherit; }
.fullview-page-image:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.fullview-page-image-frame { position: relative; display: block; height: 8.5rem; border-radius: 4px; background: var(--duckery-surface); overflow: hidden; }
.fullview-page-image-frame img { width: 100%; height: 100%; object-fit: contain; }
.fullview-page-image-frame .photo-hatch { position: absolute; inset: 0; }
.fullview-page-image-caption { font-size: 0.75rem; overflow-wrap: anywhere; }
.island-error { color: var(--bs-danger, #dc3545); font-size: 0.75rem; margin-left: 0.4rem; }
.fullview-actions { margin-top: 0.5rem; display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; }
.fullview-copy-actions { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.5rem; flex-wrap: wrap; }
.fullview-copy-actions .form-control { max-width: 16rem; }

/* Desktop: the cover grows but is capped so future per-magazine info below stays readable; header is a row again. */
@media (min-width: 768px) {
    .fullview-header { flex-direction: row; align-items: flex-start; }
    .fullview-tile { width: clamp(320px, 30vw, 420px); margin-inline: 0; }
    .fullview-tile .mag-object, .fullview-tile .mag-primary { border-radius: var(--duckery-cover-radius); }
    .fullview-meta { width: auto; text-align: left; }
    .fullview-actions { justify-content: flex-start; }
    .fullview-detail-meta { grid-template-columns: max-content 1fr max-content 1fr; column-gap: 1.25rem; }
}



/* ============================================
   27. Select mode — bulk claim (add-flow PR2)
   ============================================ */
.select-trigger-row { margin-top: 0.35rem; }
.select-trigger { font-size: 0.85rem; }
.select-trigger, .select-skip { display: inline-flex; align-items: center; min-height: 44px; }
.select-trigger:focus-visible, .select-skip:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) {
    .select-trigger, .select-skip { min-height: 0; }
}

.select-banner { display: flex; align-items: center; gap: 0.5rem 0.75rem; flex-wrap: wrap; padding: 0.35rem 0; }
/* Fragment targets clear the 52px sticky top bar, like .segment-grid a. */
.select-heading, .select-result, .select-error { scroll-margin-top: 4rem; }
.select-heading { font-size: 1rem; margin: 0; }
.select-skip { font-size: 0.8rem; }
.select-banner .select-cancel { margin-left: auto; }
.select-error { color: var(--duckery-accent-soft); margin: 0.25rem 0 0; font-size: 0.9rem; }

/* The whole tile is the toggle (candidate B): the checkbox hides, the frame carries the state. */
.select-tile { display: block; position: relative; cursor: pointer; }
.select-inert { cursor: default; }
.select-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; pointer-events: none; scroll-margin: 4rem 0 6rem; }
@media (min-width: 768px) and (hover: hover) and (pointer: fine) {
    .select-input { scroll-margin-top: 18rem; }
}
.select-tile:has(> .select-input:focus-visible) .mag-object { box-shadow: 0 0 0 2px var(--bs-body-bg), 0 0 0 4px var(--duckery-gold); }
.select-tile:has(> .select-input:checked) .mag-object { outline: 3px solid var(--duckery-accent); outline-offset: 2px; }
.select-tile:has(> .select-input:checked)::after {
    /* Plain form first: engines without alt-text syntax drop the second declaration and keep the glyph. */
    content: "✓";
    content: "✓" / "";
    position: absolute; top: 0.2rem; right: 0.2rem;
    width: 1.3rem; height: 1.3rem; line-height: 1.3rem; text-align: center;
    border-radius: 50%;
    background: var(--duckery-accent); color: var(--bs-emphasis-color);
    font-size: 0.8rem; font-weight: 700;
    pointer-events: none;
}
.select-chip {
    position: absolute; top: 0.2rem; left: 0.2rem;
    font-size: 0.65rem; line-height: 1.2; padding: 0.05rem 0.35rem;
    border-radius: 0.6rem;
    background: var(--duckery-surface); border: 1px solid var(--duckery-border);
    color: var(--bs-body-color);
    pointer-events: none;
}
.select-chip-inert { color: var(--duckery-muted-on-surface); }
/* A shift-click must not start a text-selection drag; the mode is not a reading surface. */
.select-mode .cover-grid { user-select: none; }
.select-form[data-range-armed] .select-tile { cursor: crosshair; }
/* The picked endpoint: DASHED accent, never gold — gold means owned, solid accent means selected. Scoped to the
   armed form so it outranks the :has(:checked) solid ring, which specificity alone would otherwise win. */
.select-form[data-range-armed] .select-tile[data-range-start] .mag-object {
    outline: 3px dashed var(--duckery-accent); outline-offset: 2px;
}
.select-chip-start { display: none; bottom: 0.2rem; top: auto; border-style: dashed; border-color: var(--duckery-accent); }
.select-form[data-range-armed] .select-tile[data-range-start] .select-chip-start { display: block; }
.select-inert .mag-object { opacity: 0.55; }
.select-failed .mag-object { outline: 2px dashed var(--duckery-accent); outline-offset: 2px; }

/* The selection toolbar (D4): sticky above the grid, so the tools stay one glance from any scroll position.
   In this mode the toolbar is the sticky band and the head is not: two stacked sticky bands cannot share one
   offset, and the head's tall variable height would cost the mode most of a phone viewport. */
.select-mode .segment-head { position: static; }
.select-toolbar {
    position: sticky; top: 52px; z-index: 29;
    display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem 0.75rem;
    padding: 0.4rem 0.75rem;
    background: var(--duckery-surface); border-bottom: 1px solid var(--duckery-border);
}
.select-tools { display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem 0.6rem; }
/* Alt-text form, as with the check badge above: engines without it drop the second declaration and keep the glyph. */
.select-tools > .select-tool + .select-tool::before {
    content: "·";
    content: "·" / "";
    margin-right: 0.6rem; color: var(--duckery-muted-on-surface);
}
.select-tool { display: inline-flex; align-items: center; min-height: 44px; font-size: 0.85rem; }
.select-tool:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
/* font-family, never the font shorthand: the shorthand would reset .select-tool's own font-size. */
.select-range { background: none; border: 0; padding: 0; color: var(--bs-link-color); font-family: inherit; cursor: pointer; }
.select-range:hover { color: var(--duckery-gold); }
.select-hint { display: none; color: var(--duckery-muted-on-surface); font-size: 0.78rem; }
.select-count { margin-left: auto; color: var(--duckery-muted-on-surface); font-size: 0.85rem; }
.select-undo-offer:not(:empty) { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; }
@media (hover: hover) and (pointer: fine) {
    .select-tool { min-height: 0; }
    /* Only a fine pointer has the key the hint names. */
    .select-hint { display: inline; }
}
@media (max-width: 767.98px) {
    /* Two rows at 375px: tools first, the count on its own row right-aligned. */
    .select-count { flex-basis: 100%; margin-left: 0; text-align: right; }
}

/* Sticky, in flow: the bar reserves its own height, so a wrapped bar can never cover the last tile row. */
.select-bar {
    position: sticky; bottom: 0; z-index: 30;
    display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem 0.75rem;
    margin-top: 0.5rem;
    padding: 0.5rem 0.75rem calc(0.5rem + env(safe-area-inset-bottom));
    background: var(--duckery-surface); border-top: 1px solid var(--duckery-border);
}
.select-note { color: var(--duckery-accent-soft); font-size: 0.8rem; }
.select-bar-beyond, .select-result-beyond { color: var(--duckery-muted-on-surface); font-size: 0.8rem; }
.select-bar [data-select-cancel] { margin-left: auto; }

/* The standing result banner (D9): stays until Luk or navigation; gold ties it to the flipped frames. */
.select-result {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem;
    margin: 0.5rem 0; padding: 0.5rem 0.75rem;
    background: var(--duckery-surface);
    border: 1px solid var(--duckery-border); border-left: 3px solid var(--duckery-gold);
    border-radius: 4px;
}
.select-result-line { margin: 0; }
.select-undo { display: inline; margin: 0; }
.select-result-close { margin-left: auto; }

form[data-submitting] .select-input, form[data-submitting] [data-select-cancel],
form[data-submitting] .select-tool, form[data-submitting] [data-select-undo-clear] { pointer-events: none; }
form[data-submitting] [data-select-cancel], form[data-submitting] .select-tool { opacity: 0.6; }

/* Touch-first targets on the mode's controls; compact again where a fine pointer is guaranteed (#217). */
.select-bar .btn, .select-banner .btn, .select-result .btn { min-height: 44px; }
@media (hover: hover) and (pointer: fine) {
    .select-bar .btn, .select-banner .btn, .select-result .btn { min-height: 0; }
}

/* The public series index (/view/series, v0.5): every publication, unscoped, A-Z. */
.series-index {
    max-width: 900px; margin: 0 auto; padding: clamp(1rem, 3vw, 2.5rem) clamp(1rem, 3vw, 2.5rem) 4rem;
    transition: transform 220ms ease-out;
}
.series-index.dragging { transition: none; }
@media (prefers-reduced-motion: reduce) {
    .series-index { transition: none; }
}
/* rest-sheet.js's drag target: touch-action:none only on the control itself, not every .segment-back,
   and only where the gesture is actually wired up (rest-sheet.js opts out above this breakpoint too) —
   otherwise a wide touchscreen couldn't scroll by starting a pan on this link. */
@media (max-width: 1080px) {
    .segment-back[data-series-back] { touch-action: none; }
}
.series-index-heading { font-size: clamp(1.4rem, 2.5vw, 1.8rem); margin-bottom: 0.3rem; }
.series-index-intro { color: var(--duckery-muted-on-surface); margin: 0 0 1.25rem; }
.series-index-filter { margin-bottom: 1.25rem; }

.series-index-jump {
    position: sticky;
    top: 52px;
    z-index: 10;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    padding: 0.6rem 0;
    background: var(--bs-body-bg, #121215);
    border-bottom: 1px solid var(--duckery-border-divider);
    margin-bottom: 1rem;
}
.series-index-jump a {
    color: var(--duckery-muted-on-surface);
    text-decoration: none;
    font-size: 0.8rem;
    font-weight: 600;
    padding: 0.15rem 0.4rem;
    border-radius: 3px;
}
.series-index-jump a:hover { color: var(--duckery-gold); }
.series-index-jump a:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }

.series-index-group { margin-bottom: 1.5rem; }
.series-index-letter {
    font-size: 1rem;
    font-weight: 700;
    color: var(--duckery-gold);
    border-bottom: 1px solid var(--duckery-border-divider);
    padding-bottom: 0.3rem;
    margin-bottom: 0.5rem;
    scroll-margin-top: 100px;
}
.series-index-list { list-style: none; margin: 0; padding: 0; }
.series-index-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--duckery-border-divider);
}
.series-index-title { color: inherit; text-decoration: none; font-weight: 500; }
.series-index-title:hover { color: var(--duckery-gold); text-decoration: underline; }
.series-index-title:focus-visible { outline: 2px solid var(--duckery-gold); outline-offset: 2px; }
.series-index-meta { color: var(--duckery-muted-on-surface); font-size: 0.8rem; white-space: nowrap; }

/* ---- Static content pages (v0.5 W4): About, Feedback ---- */
.static-page { max-width: 640px; margin: 0 auto; padding: 1.5rem 1rem; }
.static-page h1 { margin-bottom: 1rem; }
.static-page p { margin-bottom: 1rem; }
.static-page a { color: var(--duckery-gold); }
.static-page a:hover { color: var(--duckery-gold); }
.not-found-page { text-align: center; }
.not-found-page .not-found-icon { display: block; width: min(15rem, 60vw); height: auto; margin: 1rem auto 1.5rem; }
.not-found-page ul { list-style: none; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem 1.5rem; }
.static-page h2 { font-size: 1.15rem; margin: 1.5rem 0 0.5rem; }
.cookie-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; margin-bottom: 1rem; }
.cookie-table th, .cookie-table td { text-align: left; vertical-align: top; padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--duckery-border-divider); }
.cookie-table code { overflow-wrap: anywhere; }
.stats-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; margin-bottom: 1.5rem; font-variant-numeric: tabular-nums; }
.stats-table caption { caption-side: top; color: var(--duckery-muted-on-surface); font-size: 0.85rem; padding: 0 0 0.4rem; }
.stats-table th, .stats-table td { text-align: left; vertical-align: top; padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--duckery-border-divider); }
.stats-table td { text-align: right; }
.stats-table thead th { text-align: right; font-weight: 500; }
.stats-table thead th:first-child { text-align: left; }
.static-page .about-version { margin-top: 2rem; font-size: 0.8rem; color: var(--duckery-muted-on-surface); }

.feedback-form { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 1.5rem; }
.feedback-form .form-label { margin-top: 0.6rem; margin-bottom: 0; font-weight: 500; }
.feedback-form button { align-self: flex-start; margin-top: 0.75rem; }
.feedback-email { margin-top: 1.5rem; }
