/* ── Search bar ──────────────────────────────────────────────────── */
.search-bar-wrap {
    border-top: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
    padding: 1.25rem 0 1.1rem;
    margin-bottom: 0;
}

.search-input-group {
    max-width: 100%;
}

/* ── Filter bar ──────────────────────────────────────────────────── */
.filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 1rem 2rem;
    padding: 0.85rem 0;
    border-bottom: 3px double var(--rule-dark);
    margin-bottom: 2rem;
    /* No z-index here on purpose: it would create a stacking context that caps the dropdown panels
       (z-index 200) below the sticky masthead (z-index 100). The panels carry their own z-index and
       render correctly in the root context — above both the masthead and the results below. */
}

.filter-group {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

/* The reset link sits on the controls' baseline, where the label row would be empty. */
.filter-group-reset { justify-content: flex-end; }
.filter-reset {
    font-size: 0.8rem;
    color: var(--ink-soft);
    text-decoration: underline;
    padding: 0.3rem 0;
    white-space: nowrap;
}
.filter-reset:hover { color: var(--ink); }

.filter-label {
    font-family: var(--font-body);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-soft);
    line-height: 1.2;
}

/* Readable labels for form fields (login etc.) — body font, normal case. */
.auth-label {
    display: block;
    font-family: var(--font-body);
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--ink);
    margin-bottom: 0.2rem;
}

.filter-select {
    font-size: 0.8rem;
    /* Extra right padding leaves room for the .form-select caret. */
    padding: 0.25rem 1.7rem 0.25rem 0.6rem;
    height: auto;
    border-color: var(--rule-dark);
    background-color: var(--paper);
    color: var(--ink);
    min-width: 8rem;
    border-radius: 0;
    background-position: right 0.5rem center;
}

.filter-select:focus {
    box-shadow: none;
    border-color: var(--ink);
}

/* Date-range picker (von/bis) — matches the print aesthetic of .filter-select. */
.filter-date {
    font-size: 0.8rem;
    padding: 0.22rem 0.5rem;
    height: auto;
    border-color: var(--rule-dark);
    background-color: var(--paper);
    color: var(--ink);
    border-radius: 0;
}

.filter-date:focus {
    box-shadow: none;
    border-color: var(--ink);
}

.filter-count {
    opacity: 0.55;
    font-size: 0.65rem;
    margin-left: 0.2rem;
}

.filter-count:empty,
.filter-count[data-zero="true"] {
    display: none;
}

/* ── Newspaper multi-select dropdown ─────────────────────────────── */
/* <details>/<summary> based dropdown — hide the native disclosure marker */
.filter-dropdown {
    position: relative;
}

.filter-dropdown-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    min-width: 9rem;
    font-size: 0.8rem;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--rule-dark);
    border-radius: 0;
    background: var(--paper);
    color: var(--ink);
    cursor: pointer;
    font-family: var(--font-body);
    text-align: left;
    list-style: none;
}

.filter-dropdown-toggle::-webkit-details-marker { display: none; }
.filter-dropdown-toggle::marker { content: ''; }

.filter-dropdown[open] .filter-dropdown-arrow {
    transform: rotate(180deg);
}

.filter-dropdown-toggle:hover {
    border-color: var(--ink);
}

.filter-dropdown-arrow {
    opacity: 0.5;
    font-size: 0.7rem;
}

.filter-dropdown-panel {
    position: absolute;
    top: calc(100% + 2px);
    left: 0;
    min-width: 14rem;
    background: var(--paper);
    border: 1px solid var(--rule-dark);
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);
    z-index: 200;
    padding: 0.35rem 0;
}

.filter-dropdown-item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.4rem 0.75rem;
    font-size: 0.82rem;
    cursor: pointer;
    color: var(--ink);
    user-select: none;
}

.filter-dropdown-item:hover {
    background: rgba(200, 184, 154, 0.25);
}

a.filter-dropdown-item {
    text-decoration: none;
}

.filter-dropdown-item.active {
    background: rgba(200, 184, 154, 0.35);
    font-weight: 600;
}

.filter-dropdown-item input[type="checkbox"] {
    accent-color: var(--accent);
    width: 0.85rem;
    height: 0.85rem;
    flex-shrink: 0;
    cursor: pointer;
}

.filter-dropdown-name {
    flex: 1;
}

.filter-dropdown-apply {
    display: block;
    width: calc(100% - 1.5rem);
    margin: 0.45rem 0.75rem 0.2rem;
    padding: 0.35rem 0.75rem;
    border: 1px solid var(--ink);
    background: var(--ink);
    color: var(--paper);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-align: center;
    cursor: pointer;
    border-radius: 0;
    font-family: var(--font-body);
}

.filter-dropdown-apply:hover { background: var(--accent); border-color: var(--accent); }

.filter-dropdown-clear {
    display: block;
    width: 100%;
    margin-top: 0.3rem;
    padding: 0.35rem 0.75rem;
    border: none;
    border-top: 1px solid var(--rule);
    background: transparent;
    font-size: 0.75rem;
    color: var(--ink-soft);
    text-align: left;
    cursor: pointer;
    font-family: var(--font-body);
    text-decoration: none;
}

.filter-dropdown-clear:hover {
    background: rgba(200, 184, 154, 0.2);
    color: var(--accent);
    text-decoration: none;
}

/* ── Search tips (collapsed by default to keep the search bar quiet) ─ */
.search-tips {
    margin: 0.45rem 0 0;
}

.search-tips-summary {
    display: inline-block;
    cursor: pointer;
    font-size: 0.76rem;
    font-style: italic;
    color: var(--ink-soft);
    user-select: none;
    list-style: none;
}

.search-tips-summary::-webkit-details-marker { display: none; }

.search-tips-summary::before {
    content: "▸ ";
    font-style: normal;
}

.search-tips[open] .search-tips-summary::before {
    content: "▾ ";
}

.search-tips-summary:hover {
    color: var(--accent);
}

.search-hint {
    margin: 0.35rem 0 0;
    max-width: 100%;
    font-size: 0.76rem;
    font-style: italic;
    color: var(--ink-soft);
    line-height: 1.5;
}

/* ── Section heading with a control on the right ─────────────────── */
/* Used by the landing "latest editions" heading, which carries the page-size select. The underline
   moves from the heading to the row so it runs the full width, under the select as well; a bare
   .section-heading elsewhere keeps its own border. */
.section-heading-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin: 0 0 1.25rem;
    padding-bottom: 0.4rem;
    border-bottom: 2px solid var(--rule-dark);
}

.section-heading-row .section-heading {
    margin: 0;
    padding-bottom: 0;
    border-bottom: none;
}

.latest-size-form {
    flex: 0 0 auto;
}

/* The steps are two digits, so the filter width meant for newspaper names is far too wide here. */
.latest-size-form .filter-select {
    min-width: 0;
}

/* ── Section heading ─────────────────────────────────────────────── */
.section-heading {
    font-family: var(--font-head);
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--ink);
    margin: 0 0 1.25rem;
    padding-bottom: 0.4rem;
    border-bottom: 2px solid var(--rule-dark);
}

/* ── Newspaper tiles (landing) ───────────────────────────────────── */
/* Fixed column counts, every one of them a divisor of 12 (4 desktop, 6 from 2000px, 3 / 2 on phones via
   the mobile overrides below), so every full page of tiles ends on a complete row. auto-fill sizing
   produced 5 or 7 columns depending on the viewport, leaving the last row ragged.
   These counts are why the selectable page sizes are multiples of 12 (LatestPageSizes, F-13):
   changing a column count here to something outside {2,3,4,6} breaks that guarantee. */
/* minmax(0, 1fr) instead of bare 1fr: a bare 1fr track still has min-content as its floor, so a
   single tile with a wide-format thumbnail or a long unbreakable title word inflates its whole
   column on narrow phones, squeezing the others. */
.newspaper-tiles {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 1rem;
}
/* From 2000px on the column holds six ~230px tiles. */
@media (min-width: 2000px) {
    .newspaper-tiles { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

.newspaper-tile {
    position: relative; /* anchors the F-16 "new" badge */
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 1rem 1rem 0.9rem;
    background: var(--card-bg);
    border: 1px solid var(--rule);
    border-top: 3px solid var(--rule-dark);
    color: var(--ink);
    text-decoration: none;
    transition: border-top-color 0.15s, box-shadow 0.15s, transform 0.1s;
}

.tile-thumb {
    display: block;
    width: 100%;
    height: 180px;
    object-fit: cover;
    object-position: top center;
    background: #fff;
    border: 1px solid var(--rule);
    margin-bottom: 0.35rem;
}

.newspaper-tile:hover {
    border-top-color: var(--accent);
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);
    transform: translateY(-2px);
    text-decoration: none;
    color: var(--ink);
}

/* F-16: "new since last login" badge, pinned to the tile's top rule like a kicker label. */
.tile-badge-new {
    position: absolute;
    top: -3px;
    right: 0.6rem;
    padding: 0.15rem 0.5rem 0.1rem;
    background: var(--accent);
    color: #fff;
    font-family: var(--font-head);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.tile-name {
    font-family: var(--font-head);
    font-weight: 700;
    font-size: 1rem;
    line-height: 1.2;
    color: var(--accent);
}

.tile-date {
    font-size: 0.82rem;
    font-style: italic;
    color: var(--ink);
}

.tile-meta {
    margin-top: 0.15rem;
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.empty-hint {
    font-style: italic;
    color: var(--ink-soft);
}

/* ── Results meta line ───────────────────────────────────────────── */
.results-meta {
    display: flex;
    flex-wrap: wrap; /* the filter tags beside the count overflowed on a portrait tablet */
    align-items: center;
    gap: 0.75rem;
    font-size: 0.75rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: 1.5rem;
}

.results-filter-tag {
    background: var(--rule);
    color: var(--ink);
    padding: 0.1rem 0.45rem;
    font-style: italic;
    letter-spacing: 0;
    text-transform: none;
}

/* ── Edition result cards (grouped per edition: thumbnail + per-page hits) ─── */
.edition-card {
    display: flex;
    gap: 1.25rem;
    padding: 1.1rem 0;
    border-top: 1px solid var(--rule);
}

.edition-card:last-child { border-bottom: 1px solid var(--rule); }

.edition-thumb { flex: 0 0 120px; display: block; align-self: flex-start; }

.edition-thumb img {
    width: 120px;
    height: 168px;
    object-fit: cover;
    object-position: top center;
    background: #fff;
    border: 1px solid var(--rule-dark);
}

.edition-body { flex: 1 1 auto; min-width: 0; }

.edition-header {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 0.5rem;
}

.edition-hitcount {
    margin-left: auto;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-soft);
}

.edition-hits { list-style: none; margin: 0; padding: 0; }
.edition-hit { border-top: 1px dotted var(--rule); }
.edition-hit:first-child { border-top: none; }

.edition-hit a {
    display: flex;
    gap: 0.6rem;
    padding: 0.4rem 0.5rem;
    color: var(--ink);
    text-decoration: none;
    border-left: 3px solid transparent;
    transition: border-left-color 0.12s, background 0.12s;
}

.edition-hit a:hover {
    border-left-color: var(--accent);
    background: rgba(200, 184, 154, 0.13);
    text-decoration: none;
}

.hit-page {
    flex: 0 0 4.5rem;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--accent);
    white-space: nowrap;
    padding-top: 0.15rem;
}

/* The column is as wide as the admin tables; the excerpt keeps a readable line length. */
.hit-fragments { flex: 1 1 auto; min-width: 0; max-width: 80ch; font-size: 0.9rem; line-height: 1.55; }
.hit-fragment { display: block; }
.hit-fragment + .hit-fragment { margin-top: 0.15rem; color: var(--ink-soft); }

/* ── Newspaper wordmark (F-20) ────────────────────────────────────── */
/* The logo lives inside text lines: a fixed height, the width follows. The upload only accepts
   landscape images (≥ 2:1), so a 20 px wordmark stays legible without swallowing the row. */
.newspaper-logo {
    height: 20px;
    width: auto;
    max-width: 12rem;
    vertical-align: -0.25em;
    margin-right: 0.35rem;
    object-fit: contain;
}
.tile-name .newspaper-logo { display: block; margin: 0 0 0.25rem; }
.reader-logo {
    height: 24px;
    width: auto;
    max-width: 14rem;
    vertical-align: middle;
    object-fit: contain;
}

/* ── Result meta (shared by the edition cards' header row) ────────── */
.result-newspaper {
    font-family: var(--font-head);
    font-weight: 700;
    font-size: 0.82rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent);
    white-space: nowrap;
}

.result-dot {
    color: var(--rule-dark);
    font-size: 0.75rem;
}

.result-date {
    font-size: 0.78rem;
    font-style: italic;
    color: var(--ink-soft);
}

/* ── Highlighted terms ───────────────────────────────────────────── */
mark {
    background-color: #f5d87a;
    color: var(--ink);
    padding: 0 0.12em;
    font-weight: 600;
    border-radius: 0;
}

/* ── Neutral notice (e.g. "page not found" in the viewer) ────────── */
.notice {
    border-left: 3px solid var(--rule-dark);
    background: var(--rule);
    color: var(--ink);
    padding: 0.75rem 1rem;
    font-size: 0.88rem;
    margin-bottom: 1rem;
}

/* ── Error ───────────────────────────────────────────────────────── */
.result-error {
    border-left: 3px solid var(--accent);
    background: #fdf0f0;
    color: var(--ink);
    padding: 0.75rem 1rem;
    font-size: 0.88rem;
    margin-bottom: 1rem;
}

/* ── Inline SVG icons ────────────────────────────────────────────── */
/* Icons inherit the control's colour (currentColor) and size (1em). The small negative
   vertical-align centres them against adjacent label text. */
.icon { vertical-align: -0.125em; }
/* Icon + label buttons get a small gap; icon-only buttons are unaffected. */
.btn .icon, .reader-download .icon { margin-right: 0.4em; }

/* ── Pagination ──────────────────────────────────────────────────── */
.newspaper-pagination {
    display: flex;
    justify-content: center;
    gap: 0.25rem;
    margin-top: 2rem;
    border-top: 1px solid var(--rule);
    padding-top: 1.25rem;
}

.newspaper-pagination .page-link {
    display: inline-block;
    font-family: var(--font-body);
    font-size: 0.8rem;
    color: var(--ink-soft);
    border: 1px solid var(--rule);
    border-radius: 0;
    padding: 0.3rem 0.65rem;
    background: transparent;
    cursor: pointer;
    text-decoration: none;
    transition: background 0.1s;
}

.newspaper-pagination .page-link:hover {
    background: var(--rule);
    color: var(--ink);
    text-decoration: none;
}

.newspaper-pagination .page-item.active .page-link {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--paper);
}

/* ── Reader chrome (back link, edition, page nav, zoom, in-document search, download) ───
   One bar carries every reader control. In the immersive layout (below) it floats over the dark
   stage as the page's title bar; without JS it degrades to a plain bar above the document. */
.reader-chrome {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1rem;
    padding: 0.5rem 0 0.75rem;
    border-bottom: 1px solid var(--rule);
    margin-bottom: 1rem;
    font-size: 0.8rem;
}

.reader-group { display: flex; align-items: center; gap: 0.3rem; }

.reader-ident { gap: 0.75rem; min-width: 0; color: var(--ink-soft); }
.reader-back { display: inline-flex; align-items: center; }
/* min-width:0 so the edition line truncates when the bar runs out of room, instead of holding its
   content width and pushing the download action onto a second row. */
.reader-ident .viewer-meta { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.reader-btn {
    border: 1px solid var(--rule-dark);
    background: var(--paper);
    color: var(--ink);
    border-radius: 0;
    padding: 0.15rem 0.55rem;
    line-height: 1.4;
    cursor: pointer;
    font-family: var(--font-body);
}

.reader-btn:hover:not(:disabled) { background: var(--rule); }
.reader-btn:disabled { opacity: 0.4; cursor: default; }
/* Pressed state of the spread toggle (single/double page). */
.reader-btn.is-active { background: var(--rule); }

.reader-pageinfo { display: flex; align-items: center; gap: 0.25rem; color: var(--ink-soft); }

#pdf-page-input {
    width: 3.2rem;
    text-align: center;
    padding: 0.1rem 0.2rem;
    border: 1px solid var(--rule-dark);
    border-radius: 0;
    font-family: var(--font-body);
}

/* Zoom percentage doubles as the reset-to-fit button (Ctrl + wheel / +,-,0 do the same). */
.reader-zoom-label {
    min-width: 3.4rem;
    text-align: center;
    color: var(--ink-soft);
    background: transparent;
    border: 0;
    padding: 0.15rem 0.2rem;
    cursor: pointer;
    font-family: var(--font-body);
    font-size: inherit;
    font-variant-numeric: tabular-nums;
}
.reader-zoom-label:hover { color: var(--ink); }

/* min-width:0 so the group may shrink below its content width — otherwise it pushes the download
   action out of the single-row chrome bar in the immersive layout. */
.reader-find { flex: 1 1 14rem; min-width: 0; }
.reader-find-input { flex: 1 1 auto; min-width: 6rem; font-size: 0.8rem; padding: 0.15rem 0.5rem; }
.reader-find-status {
    min-width: 2.5rem;
    text-align: center;
    color: var(--ink-soft);
    font-variant-numeric: tabular-nums;
}

/* Phone-only opener of the find row (see the mobile block); on wide screens the form is in the bar. */
.reader-find-toggle { display: none; }

/* Download menu (<details>): the summary reads as the former plain link, the panel drops below the
   bar's right end with one entry for the issue and one per page on screen. */
.reader-download { position: relative; margin-left: auto; font-size: 0.78rem; white-space: nowrap; }
.reader-download-toggle {
    list-style: none;
    cursor: pointer;
    color: var(--ink-soft);
    padding: 0.15rem 0.3rem;
}
.reader-download-toggle::-webkit-details-marker { display: none; }
.reader-download-toggle:hover { color: var(--ink); }
.reader-download-menu {
    position: absolute;
    top: calc(100% + 0.35rem);
    right: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    min-width: 11rem;
    border: 1px solid var(--rule-dark);
    background: var(--paper);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
}
#pdf-download-pages { display: flex; flex-direction: column; }
.reader-download-menu a {
    padding: 0.5rem 0.75rem;
    color: var(--ink);
    text-decoration: none;
}
.reader-download-menu a:hover { background: var(--rule); text-decoration: none; }

/* ── Immersive reader ───────────────────────────────────────────────────
   The reading surface fills the window on every device: site header gone, page centered on a dark
   stage, controls in the chrome bar floating on top of it. pdfViewer.js enters it on init — it is
   pure fixed positioning, so it needs no user gesture (unlike the Fullscreen API, which the
   fullscreen button adds on click) and works where element fullscreen doesn't (iOS Safari).

   The shell is a flex column: bar first, stage taking the rest. No fixed bar height to keep in sync
   — the bar may wrap to a second row on a narrow phone and the stage simply gets what is left. */
body.is-pdf-immersive { overflow: hidden; }
body.is-pdf-immersive .site-top { display: none; }
body.is-pdf-immersive .newspaper-main { max-width: none; margin: 0; padding: 0; }

body.is-pdf-immersive .reader-shell {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    background: #111;
}

body.is-pdf-immersive .reader-chrome {
    flex: none;
    gap: 0.5rem 0.75rem;
    margin: 0;
    padding: 0.4rem 0.75rem;
    padding-top: calc(env(safe-area-inset-top, 0px) + 0.4rem);
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
    background: rgba(17, 17, 17, 0.92);
    backdrop-filter: blur(6px);
}

/* The bar sits on the dark stage, so its controls flip to a dark palette. */
body.is-pdf-immersive .reader-chrome .reader-btn,
body.is-pdf-immersive .reader-chrome .btn,
body.is-pdf-immersive .reader-chrome #pdf-page-input,
body.is-pdf-immersive .reader-chrome .reader-find-input {
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.22);
    color: #f0ece4;
}
body.is-pdf-immersive .reader-chrome .reader-btn.is-active {
    background: rgba(255, 255, 255, 0.22);
    color: #fff;
}
body.is-pdf-immersive .reader-chrome .reader-btn:hover:not(:disabled),
body.is-pdf-immersive .reader-chrome .btn:hover,
body.is-pdf-immersive .reader-chrome .reader-zoom-label:hover,
body.is-pdf-immersive .reader-chrome .reader-download-toggle:hover,
body.is-pdf-immersive .reader-chrome .reader-download-menu a:hover {
    background: rgba(255, 255, 255, 0.16);
    color: #fff;
}
body.is-pdf-immersive .reader-chrome,
body.is-pdf-immersive .reader-chrome .reader-ident,
body.is-pdf-immersive .reader-chrome .reader-pageinfo,
body.is-pdf-immersive .reader-chrome .reader-zoom-label,
body.is-pdf-immersive .reader-chrome .reader-find-status,
body.is-pdf-immersive .reader-chrome .reader-download-toggle,
body.is-pdf-immersive .reader-chrome .reader-download-menu a { color: #d8d4cc; }
body.is-pdf-immersive .reader-chrome .reader-download-menu {
    background: #1b1b1b;
    border-color: rgba(255, 255, 255, 0.22);
}
body.is-pdf-immersive .reader-chrome .viewer-meta strong { color: #e0b96a; }

/* The search field takes the slack in the single row, but not all of it — the download action stays
   at the right end and the bar keeps its balance on wide screens. */
body.is-pdf-immersive .reader-chrome .reader-find { flex: 1 1 12rem; max-width: 26rem; }
body.is-pdf-immersive .reader-chrome .reader-download { flex: none; }

/* The stage takes whatever the bar leaves, so the bar never covers the page. The page-sized
   container is centered inside it (flex + margin:auto — the auto margins collapse to 0 once the page
   is zoomed larger than the stage, so scrolling still reaches every corner). */
body.is-pdf-immersive .pdf-stage {
    flex: 1 1 auto;
    min-height: 0;
    max-width: none;
    overflow: auto;
    background: #111;
    display: flex;
    /* The whole reading surface — page and the dark margin beside it — belongs to the viewer's own
       pinch (pdfViewer.js), which re-renders sharp. Without this the browser zooms the document
       itself the moment the fingers miss the page, and the two zooms fight each other. */
    touch-action: pan-x pan-y;
}
body.is-pdf-immersive .pdf-viewer-container {
    margin: auto;
    flex: none;
    border: none;
    box-shadow: none;
}

/* ── PDF viewer ──────────────────────────────────────────────────── */
/* Loading state: spinner + download progress, shown until the first page has rendered. It lives in
   the stage, so `margin: auto` centres it on the reading surface (the stage is a flex container in
   the immersive layout) and it stays visible on the dark background. */
.pdf-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    margin: auto;
    padding: 3rem 0;
    text-align: center;
}
/* While loading, the page container is an empty box that would compete with the spinner for the
   stage's auto margins and knock it off centre — hide it until there is something to show. */
.pdf-stage.is-loading .pdf-viewer-container { display: none; }
/* The loading block stays in the DOM for the whole session: pdfViewer.js re-shows it whenever a
   page flip has to wait on the network (skipping into an unfetched part of the issue). */
.pdf-loading.is-hidden { display: none; }

.pdf-loading-text { font-size: 0.85rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
body.is-pdf-immersive .pdf-loading-text { color: #d8d4cc; }
body.is-pdf-immersive .newspaper-spinner { border-color: rgba(255, 255, 255, 0.2); border-top-color: #f0ece4; }
.newspaper-spinner {
    display: inline-block;
    width: 2rem;
    height: 2rem;
    border: 3px solid var(--rule);
    border-top-color: var(--ink);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Stage: a horizontal-overflow container so a zoomed page scrolls inside the content column instead
   of widening the page and breaking the surrounding layout. This governs the normal (in-page) view;
   the immersive layout above turns the same element into the full-window reading surface. */
.pdf-stage { max-width: 100%; overflow-x: auto; }

/* Container size (width/height), the flex gap and --scale-factor are set by pdfViewer.js. It holds
   one .pdf-page slot per shown page (two side by side in spread mode); inside a slot the three
   layers are absolutely positioned at 0,0 with identical dimensions so the canvas, text layer and
   highlights stay pixel-aligned. */
.pdf-viewer-container {
    position: relative;
    display: flex;
    align-items: flex-start;
    margin: 0 auto;
    box-shadow: 0 4px 24px rgba(0,0,0,0.18);
    border: 1px solid var(--rule-dark);
    /* Allow one-finger panning/scrolling but let the viewer own two-finger pinch-zoom (pdfViewer.js),
       so pinch works inside native fullscreen too and re-renders sharp; this also disables the
       browser's double-tap-to-zoom, freeing double-tap for the fullscreen toggle. */
    touch-action: pan-x pan-y;
}

/* One shown page; slot dimensions are set by pdfViewer.js. In spread mode the gap between the two
   slots shows the dark stage — it reads as the paper's fold. */
.pdf-page {
    position: relative;
    flex: none;
}

.pdf-page canvas {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
}

/* PDF.js text layer — transparent, selectable glyphs over the raster (copy-paste). */
.textLayer {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
    overflow: clip;
    opacity: 1;
    line-height: 1;
    text-align: initial;
    forced-color-adjust: none;
    transform-origin: 0 0;
}

.textLayer span,
.textLayer br {
    position: absolute;
    color: transparent;
    white-space: pre;
    cursor: text;
    transform-origin: 0 0;
}

.textLayer span.markedContent {
    top: 0;
    height: 0;
}

.textLayer ::selection {
    background: rgba(40, 100, 255, 0.30);
}

.pdf-overlay {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 3;
    pointer-events: none;
}

.pdf-highlight {
    position: absolute;
    background-color: rgba(245, 216, 122, 0.40);
    border: 1px solid rgba(200, 150, 0, 0.55);
    mix-blend-mode: multiply;
}

/* ── Viewer nav ──────────────────────────────────────────────────── */
.viewer-nav {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 0 1.25rem;
    border-bottom: 1px solid var(--rule);
    margin-bottom: 1.5rem;
    font-size: 0.85rem;
    color: var(--ink-soft);
}

.viewer-meta strong {
    font-family: var(--font-head);
    font-weight: 700;
    color: var(--accent);
}

/* ── Utility bar (auth + language) & login ───────────────────────── */
.utility-bar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.75rem;
    width: 100%;
    max-width: var(--content-max);
    margin: 0 auto;
    padding: 0.35rem clamp(1rem, 3vw, 3rem) 0;
    font-size: 0.75rem;
    color: var(--ink-soft);
}

.lang-switch {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    letter-spacing: 0.04em;
}

.lang-link {
    color: var(--ink-soft);
    text-decoration: none;
    padding: 0.05rem 0.3rem;
    border-bottom: 1px solid transparent;
}

.lang-link:hover { color: var(--ink); }

.lang-link.is-active {
    color: var(--ink);
    font-weight: 600;
    border-bottom-color: var(--accent);
}

.auth-card {
    max-width: 380px;
    margin: 1rem auto;
}

.auth-form {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.auth-form .form-control { margin-bottom: 0.4rem; }

.auth-remember {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: var(--ink-soft);
    margin: 0.2rem 0 0.6rem;
}

.auth-form .btn-primary { align-self: flex-start; }

.auth-divider {
    text-align: center;
    font-style: italic;
    color: var(--ink-soft);
    margin: 1rem 0;
}

.auth-sso { width: 100%; }

/* ── Responsive / mobile ─────────────────────────────────────────── */
@media (max-width: 640px) {
    .search-bar-wrap { padding: 1rem 0 0.9rem; }
    .search-input-group { max-width: 100%; }

    /* Filters: each control takes a flexible full-width-ish slot and wraps */
    .filter-bar { gap: 0.7rem 1rem; }
    .filter-group { flex: 1 1 9rem; }
    .filter-select,
    .filter-date,
    .filter-dropdown-toggle { min-width: 0; width: 100%; }
    /* On a phone the newspaper filter sits in the right-hand column, so a panel hanging from the
       toggle's left edge ran past the screen. Anchored to the whole filter bar instead, it spans
       the bar's width and always stays on screen. */
    .filter-bar { position: relative; }
    .filter-dropdown { position: static; }
    .filter-dropdown-panel { left: 0; right: 0; min-width: 0; max-width: none; }

    .newspaper-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem; }
    .tile-thumb { height: 150px; }

    /* Edition cards: thumbnail on top, hit rows stack */
    .edition-card { flex-direction: column; gap: 0.6rem; }
    .edition-thumb { flex-basis: auto; }
    .edition-thumb img { width: 110px; height: 154px; }
    .edition-hit a { flex-direction: column; gap: 0.1rem; }
    .hit-page { flex-basis: auto; }

    .newspaper-pagination { flex-wrap: wrap; }

    /* Viewer nav wraps instead of overflowing */
    .viewer-nav { flex-wrap: wrap; gap: 0.5rem; }

    /* PDF reader on phones: the bar keeps what a finger cannot do better itself — back, edition,
       page field, search toggle, fullscreen, download. Zoom is the pinch gesture and two broadsheet
       pages side by side are unreadable on a phone, so both groups give way (pdfViewer.js forces
       the single-page layout below the breakpoint). The in-document search would eat the whole
       row, so it stays folded until the toggle opens it as a row of its own below the controls. */
    .reader-zoom, .reader-find, .reader-spread { display: none; }
    /* The page arrows give way too, so the bar fits one row: a phone turns pages by swiping, and the
       page field still jumps anywhere. */
    #pdf-prev, #pdf-next { display: none; }
    /* Date under the wordmark instead of beside it — the edition line is the bar's widest item. */
    .reader-ident .viewer-meta { display: flex; flex-direction: column; line-height: 1.15; }
    .reader-ident .viewer-meta-sep { display: none; }
    .reader-ident .reader-logo { height: 18px; max-width: 8rem; object-position: left; }
    .reader-ident .viewer-date { font-size: 0.7rem; }
    .reader-find-toggle { display: inline-block; }
    .reader-chrome.is-find-open .reader-find,
    body.is-pdf-immersive .reader-chrome.is-find-open .reader-find {
        display: flex;
        order: 10;
        flex: 1 1 100%;
        max-width: none;
    }
    /* 16px: below that iOS Safari zooms the whole page in when the field takes focus. */
    .reader-find-input { font-size: 16px; }
    .reader-download-text, .reader-back-text { display: none; }
    .reader-download { margin-left: auto; }
    .reader-chrome { font-size: 0.75rem; gap: 0.4rem; }
    .reader-ident { gap: 0.4rem; flex: 0 1 auto; }
    .reader-back .icon { margin-right: 0; }
    #pdf-page-input { width: 2.6rem; }
}

@media (max-width: 400px) {
    .newspaper-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Admin backend (F-08) ────────────────────────────────────────── */
.admin-shell { width: 100%; margin: 1.5rem auto; padding: 0; }
.admin-title { font-family: var(--font-head); font-size: 1.8rem; margin: 0.5rem 0 1rem; }
.admin-h2 { font-size: 1.1rem; margin: 0 0 0.6rem; border-bottom: 2px solid var(--ink); padding-bottom: 0.2rem; }
.admin-h3 { font-size: 0.95rem; margin: 1rem 0 0.4rem; }

/* Sub-navigation as connected tabs (shared by Admin + Settings): each tab is a square, print-style
   box; the active one merges with the rule below it so it reads as joined to the content panel. */
.admin-nav {
    display: flex;
    gap: 0.25rem;
    align-items: flex-end;
    /* The rule under the tabs is an inset shadow, not a border: the active tab paints over it from
       inside the bar. Tabs overlapping a real border by -1px made the bar 1px taller than its box,
       and since overflow-x: auto turns overflow-y into auto as well, every browser with classic
       scrollbars (Windows) showed a tiny vertical scrollbar at the end of the bar. */
    box-shadow: inset 0 -1px 0 var(--rule-dark);
    margin-bottom: 1rem;
    overflow-x: auto;
    overflow-y: hidden;
}
.admin-nav-link {
    color: var(--ink-soft);
    text-decoration: none;
    font-family: var(--font-body);
    font-size: 0.8rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
    padding: 0.45rem 0.9rem;
    border: 1px solid transparent;
    border-bottom: none;
    transition: background 0.1s, color 0.1s;
}
.admin-nav-link:hover { color: var(--ink); background: var(--rule); }
.admin-nav-link.is-active {
    color: var(--ink);
    font-weight: 600;
    background: var(--paper);
    border-color: var(--rule-dark);        /* top + left + right */
    border-bottom: 1px solid var(--paper); /* covers the bar's rule → the tab joins the content */
}

/* Tab bar docked in the sticky header (NF-09): aligns with the content column and stays put while
   the page scrolls (it lives inside .site-top). */
.subnav-bar {
    background: var(--paper);
    max-width: var(--content-max);
    margin: 0 auto;
    padding: 0 clamp(1rem, 3vw, 3rem);
}
.subnav-bar .admin-nav { margin-bottom: 0; }

.admin-section { margin: 1.75rem 0; }

.admin-cards { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.admin-card {
    display: flex; flex-direction: column; gap: 0.2rem;
    min-width: 130px; padding: 0.8rem 1rem;
    border: 1px solid var(--rule); background: #fff;
}
.admin-card-num { font-size: 1.5rem; font-weight: 700; }
.admin-card-label { font-size: 0.75rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: 0.03em; }
.admin-health-green { color: #1a7f37; }
.admin-health-yellow { color: #9a6700; }
.admin-health-red { color: #b00020; }

/* Wrapper lets wide admin tables scroll horizontally on narrow screens instead of
   overflowing the page (the publisher/newspaper tables carry several columns). */
.admin-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.admin-table { width: 100%; min-width: 34rem; border-collapse: collapse; font-size: 0.9rem; }
/* Text cells wrap; only the headers and the action buttons stay on one line. Keeping EVERY cell on
   one line made the newspaper, API-consumer and search-alert tables 140–340px wider than the admin
   column on a 1080p screen, so their action buttons sat off-screen behind a sideways scroll
   (measured 24.09.2026). */
.admin-table th, .admin-table td { text-align: left; padding: 0.45rem 0.6rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
.admin-table th { white-space: nowrap; }
/* The action column shrinks to its buttons, keeps them in one row and leaves the width to the
   text columns. */
.admin-table td.admin-actions { width: 1%; white-space: nowrap; }
.admin-table th { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-soft); }
.admin-error { color: #b00020; font-size: 0.8rem; max-width: 360px; overflow-wrap: anywhere; } /* long file paths */

.admin-muted { color: var(--ink-soft); font-size: 0.8rem; }
.admin-warn { color: #b45309; font-size: 0.8rem; font-weight: 600; }

.admin-inline-actions { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; margin-top: 0.4rem; }
.admin-inline-form { display: inline-flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; margin: 0; }

/* Auto-download status is a full-width sub-row under each newspaper: the main row carries no bottom
   border so the two read as one block, and the sub-row lays the facts out horizontally. */
.admin-table tr.admin-mainrow > td { border-bottom: 0; }
.admin-subrow > td { padding-top: 0; }
.admin-download-row { display: flex; align-items: baseline; gap: 0.5rem 1rem; flex-wrap: wrap; }
.admin-download-facts { display: flex; align-items: baseline; gap: 0.2rem 1.25rem; flex-wrap: wrap; font-size: 0.85rem; }
.admin-download-row .admin-inline-actions { margin-top: 0; margin-left: auto; }
.admin-add { margin-top: 0.75rem; }
.admin-input {
    font-family: var(--font-body, inherit); font-size: 0.9rem;
    padding: 0.3rem 0.5rem; border: 1px solid var(--rule-dark); background: #fff;
}
/* Action cells are real table cells — flex on the td itself would decouple its height and
   bottom border from the row (the separators visibly drifted next to tall or empty cells,
   e.g. the local admin's password form and the "you" row without buttons). The forms and
   buttons inside flow inline and wrap on their own. */
.admin-actions > * {
    display: inline-flex;
    gap: 0.4rem;
    align-items: center;
    vertical-align: top;
    margin: 0 0.3rem 0.25rem 0;
}
.admin-actions > *:last-child { margin-right: 0; }

.admin-btn {
    border: 1px solid var(--rule-dark); background: transparent; color: var(--ink-soft);
    font-size: 0.8rem; padding: 0.3rem 0.6rem; cursor: pointer; font-family: var(--font-body, inherit);
    /* inline-flex + gap spaces an icon from its label, and leaves icon-only buttons centred. */
    display: inline-flex; align-items: center; gap: 0.4em;
}
.admin-btn:hover { background: var(--rule); color: var(--ink); }
.admin-btn-primary { border-color: var(--ink); color: var(--ink); font-weight: 600; }
.admin-btn-danger:hover { background: #b00020; color: #fff; border-color: #b00020; }

.admin-flash { padding: 0.5rem 0.75rem; font-size: 0.85rem; margin: 0.75rem 0; border-left: 3px solid; }
.admin-flash-ok { border-color: #1a7f37; background: #f0fbf3; }
.admin-flash-err { border-color: #b00020; background: #fdf0f1; }

.admin-badge { font-size: 0.72rem; padding: 0.1rem 0.4rem; border: 1px solid; }
.admin-badge-on { color: #1a7f37; border-color: #1a7f37; }
.admin-badge-off { color: #b00020; border-color: #b00020; }

/* ── User menu (logged-in dropdown) & masthead admin link ────────── */
.utility-bar .user-menu { margin-right: auto; position: relative; }

.user-menu-toggle {
    cursor: pointer;
    list-style: none;
    color: var(--ink-soft);
}
.user-menu-toggle::-webkit-details-marker { display: none; }
.user-menu-toggle::marker { content: ''; }
.user-menu-toggle::after { content: ' ▾'; opacity: 0.5; }
.user-menu[open] .user-menu-toggle::after { content: ' ▴'; }

.user-menu-panel {
    position: absolute;
    left: 0;
    top: calc(100% + 4px);
    min-width: 170px;
    background: var(--paper);
    border: 1px solid var(--rule-dark);
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);
    z-index: 200;
    display: flex;
    flex-direction: column;
}
.user-menu-panel form { margin: 0; }

.user-menu-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 0.5rem 0.85rem;
    font-size: 0.8rem;
    font-family: var(--font-body);
    color: var(--ink-soft);
    background: transparent;
    border: none;
    cursor: pointer;
    text-decoration: none;
}
.user-menu-item:hover { background: var(--rule); color: var(--ink); text-decoration: none; }
.user-menu-logout { border-top: 1px solid var(--rule); }

/* ── Admin: section heads, + buttons, edit/delete actions, modal dialogs ───────────── */
.admin-section-head {
    display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
    margin-bottom: 0.6rem;
}
.admin-section-head .admin-h2 { margin: 0; flex: 1; }
.admin-add-btn {
    flex: 0 0 auto; width: 1.9rem; height: 1.9rem; padding: 0; line-height: 1;
    font-size: 1.2rem; border-radius: 50%; display: inline-flex;
    align-items: center; justify-content: center;
}

.admin-dialog {
    border: 1px solid var(--rule-dark); padding: 0; max-width: 30rem; width: calc(100% - 2rem);
    background: #fff; color: var(--ink); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.18);
}
.admin-dialog::backdrop { background: rgba(0, 0, 0, 0.4); }
/* .admin-field's own display would beat the UA [hidden] rule, silently breaking every conditional
   row (login fields, week-of-month). Re-assert it for everything inside a dialog. */
.admin-dialog [hidden] { display: none !important; }
.admin-dialog-form { display: flex; flex-direction: column; gap: 0.9rem; padding: 1.25rem; }
/* Standalone (non-dialog) settings forms: same column layout, capped width, no dialog padding. */
.admin-form-narrow { max-width: 32rem; }
.admin-dialog-title { font-size: 1.05rem; margin: 0; border-bottom: 2px solid var(--ink); padding-bottom: 0.3rem; }
.admin-field { display: flex; flex-direction: column; gap: 0.25rem; }
.admin-field > span { font-size: 0.8rem; color: var(--ink-soft); }
.admin-field .admin-input { width: 100%; box-sizing: border-box; }
.admin-field .admin-muted { font-size: 0.72rem; }
.admin-dialog-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 0.25rem; }

/* Wide dialog (newspaper edit): two-column field grid; full-width rows opt in explicitly.
   Collapses back to one column on narrow screens. */
.admin-dialog--wide { max-width: 46rem; }

/* Download-history dialog: wide table (5 columns incl. error text), capped height, pager below. */
.admin-dialog--history { max-width: 60rem; }
.admin-history-scroll { max-height: 55vh; overflow-y: auto; }
.admin-pager { display: flex; align-items: center; justify-content: center; gap: 0.75rem; }
.admin-pager .admin-btn:disabled { opacity: 0.35; cursor: default; }
.admin-field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.9rem 1.25rem; align-items: start; }
.admin-field-grid .admin-field-full { grid-column: 1 / -1; }
@media (max-width: 640px) {
    .admin-field-grid { grid-template-columns: 1fr; }
}

/* The download-config group: a quiet framed section instead of the browser-default fieldset. */
.admin-fieldset {
    border: 1px solid var(--rule, #d8d2c4); padding: 0.9rem 1rem 1rem; margin: 0;
}
.admin-fieldset > legend {
    font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--ink-soft); padding: 0 0.4rem;
}
.admin-input-mono { font-family: var(--font-mono, monospace); font-size: 0.8rem; }
textarea.admin-input { resize: vertical; }
.admin-input[readonly] { background: var(--rule, #eee); color: var(--ink-soft); cursor: not-allowed; }

/* Newspaper logo (F-20): small thumbnail in the table, larger preview in the edit dialog. */
.admin-logo-cell { width: 3rem; }
.admin-logo-thumb { max-width: 2.5rem; max-height: 2.5rem; object-fit: contain; display: block; }
.admin-logo-preview { max-width: 8rem; max-height: 5rem; object-fit: contain; border: 1px solid var(--rule, #ccc); padding: 0.2rem; background: #fff; }
.admin-checkbox { flex-direction: row; align-items: center; gap: 0.4rem; font-size: 0.8rem; }

/* Publication-day checkboxes (F-10): a wrapping row of weekday toggles. */
.admin-weekday-set { border: none; margin: 0; padding: 0; }
.admin-weekday-set > legend { font-size: 0.8rem; color: var(--ink-soft); padding: 0; }
.admin-weekday-options { display: flex; flex-wrap: wrap; gap: 0.35rem 0.9rem; margin: 0.25rem 0; }
.admin-weekday-option { white-space: nowrap; }

/* ── Main navigation (always visible; wraps to multiple rows on small screens) ──────── */
.main-nav { margin-top: 0.5rem; }
/* On compact pages (and the scrolled search page) the dateline and its double rule collapse, so the
   title↔menu separator would vanish. Restore it as a top rule on the menu itself — the same 3px
   double rule as the search-page dateline. */
.is-scrolled .main-nav {
    margin-top: 0;
    padding-top: 0.4rem;
    border-top: 3px double var(--ink);
}
.main-nav-list {
    list-style: none; margin: 0; padding: 0.45rem 0.5rem;
    display: flex; justify-content: center; flex-wrap: wrap; gap: 0.3rem 1.6rem;
}
.main-nav-link {
    font-family: var(--font-body); font-size: 0.74rem; letter-spacing: 0.18em;
    text-transform: uppercase; color: var(--ink-soft); text-decoration: none;
    padding: 0.15rem 0.1rem; border-bottom: 2px solid transparent; white-space: nowrap;
}
.main-nav-link:hover { color: var(--ink); border-bottom-color: var(--rule); }
.main-nav-link.is-active { color: var(--accent); border-bottom-color: var(--accent); }

@media (max-width: 640px) {
    /* The sticky header took a quarter of a phone screen (184px: utility bar, title, dateline and
       a menu wrapping onto two rows), and a scrolled-to hit landed behind it. On a phone the
       dateline goes and the menu stays on one row that scrolls sideways; scroll-padding keeps
       anchored targets clear of what remains. */
    .masthead-dateline { display: none; }
    .main-nav-list {
        gap: 0 1.1rem; flex-wrap: nowrap; justify-content: flex-start;
        overflow-x: auto; scrollbar-width: none;
    }
    .main-nav-list::-webkit-scrollbar { display: none; }
    /* Centred while it fits, scrollable from the first item once it does not (plain centring
       would push the first links past the left edge, out of scroll reach). */
    .main-nav-list > li:first-child { margin-left: auto; }
    .main-nav-list > li:last-child { margin-right: auto; }
    .main-nav-link { font-size: 0.7rem; letter-spacing: 0.12em; display: inline-block; padding: 0.45rem 0.1rem; }
    html { scroll-padding-top: 8.5rem; }
}

/* "Why the Morgue?" (overview) — text beside the 1942 NYT-morgue photograph; stacks on narrow screens. */
.about-morgue { display: flex; gap: 1.75rem; align-items: flex-start; }
.about-morgue-text { flex: 1 1 24rem; line-height: 1.7; }
.about-morgue-text p { margin: 0 0 0.9rem; }
.about-morgue-figure { flex: 0 1 18rem; margin: 0; }
.about-morgue-figure img {
    width: 100%; height: auto; display: block;
    border: 1px solid var(--rule-dark, #999); filter: sepia(0.15);
}
.about-morgue-figure figcaption {
    margin-top: 0.4rem; font-size: 0.72rem; font-style: italic;
    color: var(--ink-soft); line-height: 1.45;
}
@media (max-width: 640px) {
    .about-morgue { flex-direction: column; }
    .about-morgue-figure { flex-basis: auto; max-width: 24rem; }
}

/* ── Search-alert subscriptions (F-15) ───────────────────────────── */
.subscription-form {
    border: 1px solid var(--rule);
    padding: 0.8rem 1rem;
    margin-bottom: 0.8rem;
}

.subscription-exclusions summary {
    cursor: pointer;
    color: var(--accent);
    font-size: 0.9rem;
}

.subscription-exclusion {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin: 0.25rem 1rem 0.25rem 0;
    font-size: 0.9rem;
}

/* Recipient choice (own account address vs. a typed foreign one). */
.subscription-recipient > span {
    display: block;
    font-size: 0.9rem;
    margin-bottom: 0.25rem;
}

.subscription-recipient-choice {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0.2rem 0;
    font-size: 0.9rem;
}

.subscription-recipient-choice .admin-input {
    flex: 0 1 22rem;
    min-width: 0;
}

.subscription-test-hit {
    border-left: 3px solid var(--rule-dark);
    padding-left: 1rem;
    margin-bottom: 0.9rem;
}

.subscription-test-fragment {
    margin: 0.15rem 0;
    color: var(--ink);
    font-size: 0.92rem;
}

/* One-off historic backfill, opened from a newspaper row (F-10). A <details> panel rather than a
   dialog: it needs no JavaScript, and the action is a plain POST like every other admin form. */
.admin-backfill { position: relative; display: inline-block; }
.admin-backfill > summary { list-style: none; cursor: pointer; }
.admin-backfill > summary::-webkit-details-marker { display: none; }
.admin-backfill-panel {
    position: absolute;
    top: calc(100% + 2px);
    right: 0;
    z-index: 200;
    min-width: 15rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.6rem;
    text-align: left;
    background: var(--paper);
    border: 1px solid var(--rule-dark);
    box-shadow: 0 4px 16px rgba(0,0,0,0.12);
}
.admin-backfill-form { display: flex; flex-direction: column; gap: 0.3rem; }

/* Interfaces section on the overview page (API + MCP). */
.interface-text {
    margin: 0.2rem 0 0.3rem;
    font-size: 0.9rem;
    color: var(--ink);
}
.interface-url {
    margin: 0 0 0.8rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85rem;
    /* Long enough to overflow a phone; break rather than widen the page. */
    overflow-wrap: anywhere;
}

/* Changelog on the overview page (one list per release). */
.changelog-list {
    margin: 0.2rem 0 0.6rem 1.2rem;
    padding: 0;
    font-size: 0.9rem;
    color: var(--ink);
}
.changelog-list li { margin: 0.2rem 0; }

/* ── Image quality (F-22) ─────────────────────────────────────────── */
.quality-picker { margin-bottom: 1rem; }
.quality-next code { font-size: 1.3rem; padding: 0.2rem 0.5rem; background: var(--card-bg); border: 1px solid var(--rule); user-select: all; }
.quality-table tr.quality-weak td:nth-child(4) { font-weight: 700; }

/* Keyboard focus must stay visible: the filter controls and the reader's find field reset their
   focus styling to a mere border colour, which a keyboard user could not make out. */
.filter-select:focus-visible,
.filter-date:focus-visible,
.filter-dropdown-toggle:focus-visible,
#pdf-find-input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ── Touch screens ────────────────────────────────────────────────── */
/* Fingers need about 40px: the reader bar (Back 30x21, page arrows 32x25), the language links
   (21px), the user menu, the pager and the dropdown entries were sized for a mouse. Keyed on the
   pointer, not the width, so a narrow desktop window keeps its compact bars. */
@media (pointer: coarse) {
    .reader-btn,
    .reader-back,
    .reader-download-toggle,
    #pdf-page-input {
        min-height: 40px;
        min-width: 40px;
    }
    .lang-link,
    .user-menu-toggle,
    .user-menu-item,
    .main-nav-link,
    .newspaper-pagination .page-link,
    .filter-dropdown-item,
    .filter-reset,
    .admin-btn,
    .admin-nav-link {
        min-height: 40px;
        display: inline-flex;
        align-items: center;
    }
    .lang-link,
    .newspaper-pagination .page-link { min-width: 40px; justify-content: center; }
    .filter-dropdown-item { display: flex; }
    /* The utility bar sits in the sticky header: full 40px targets there cost the header its space
       again, so its links take 36px — still well above the 24px minimum — and readable text. */
    .utility-bar { font-size: 0.8rem; }
    .lang-link,
    .user-menu-toggle { min-height: 36px; }
    .lang-link { min-width: 36px; }
}

/* iOS Safari zooms into any focused field with text under 16px and does not zoom back out. On
   phones every form field gets 16px: the date filters (12px), the admin inputs (13.5px), the login
   (15px) and the reader's find field in landscape (12.8px) all triggered it. */
@media (max-width: 640px), (pointer: coarse) and (max-height: 500px) {
    input:not([type=checkbox]):not([type=radio]),
    select,
    textarea,
    .filter-date,
    .filter-select,
    .admin-input,
    .form-control,
    #pdf-find-input {
        font-size: 16px;
    }
}
/* On a phone the tap targets carry the height of the header rows themselves; the rows' own padding
   on top of them would grow the sticky header back to a quarter of the screen. */
@media (pointer: coarse) and (max-width: 640px) {
    .utility-bar { padding-top: 0; padding-bottom: 0; }
    .main-nav { margin-top: 0; }
    .main-nav-list { padding-top: 0; padding-bottom: 0; }
    .main-nav-link { padding-top: 0; padding-bottom: 0; }
}
