    /* ============================================================
       Dense data explorer over the ~27k-event corpus.
       Self-contained static page: fetches data_processed.json, holds
       all rows as JS records, and windows the DOM — only the visible
       slice of rows (plus a small buffer) is ever rendered. Filtering
       and sorting operate on the array, then repaint the window, so a
       filter click stays cheap regardless of corpus size.
       ============================================================ */

    :root {
        --bg:        #f4f4f2;
        --panel:     #ffffff;
        --rail:      #fafaf8;
        --ink:       #1c1c1e;
        --ink-dim:   #6b6b70;
        /* faint still reads as "secondary" but clears ~3.3:1 on --bg — the old
           #a9a9ae (~2.2:1) was used for real content (counts, markers, notes) */
        --ink-faint: #85858c;
        --line:      #e4e4e0;
        --line-soft: #efefec;
        --cmap-land: #d4d4cd;   /* map: country with NO events (darker than --bg so shapes read) */
        --cmap-sel:  #c4c2e6;   /* map: country WITH events — a soft indigo wash, not the loud accent */
        --cmap-dot:  #e8590c;   /* map: event marker — warm ember, pops on the cool land */
        --accent:    #4f46b8;
        --accent-bg: #ecebf8;
        --visited:   #8a5fb0;   /* clicked-through links (Wikipedia / Google Translate) */
        --row-hover: #f1f0fb;
        --row-sel:   #ecebf8;

        --sev-0:  #c4c4c8;
        --sev-1:  #6fae6f;
        --sev-2:  #e0a83a;
        --sev-3:  #d9772e;
        --sev-4:  #c0392b;

        --row-h: 30px;
        --mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
        --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    }

    [data-theme="dark"] {
        --bg:        #161618;
        --panel:     #1c1c1f;
        --rail:      #1a1a1d;
        --ink:       #e9e9ea;
        --ink-dim:   #9a9aa0;
        --ink-faint: #7c7c84;   /* was #66666c (~3.2:1); ~4.4:1 on the dark --bg */
        --line:      #2c2c30;
        --line-soft: #242427;
        --cmap-land: #34343a;   /* map: country with NO events (lighter than --bg so shapes read) */
        --cmap-sel:  #423e6a;   /* map: country WITH events — a muted indigo, not the loud accent */
        --cmap-dot:  #fb923c;   /* map: event marker — warm amber, glows on the dark land */
        --accent:    #8b83f0;
        --accent-bg: #2a2740;
        --visited:   #c2a8e6;   /* clicked-through links (Wikipedia / Google Translate) */
        --row-hover: #25243a;
        --row-sel:   #2a2740;
    }

    * { box-sizing: border-box; }
    html, body { height: 100%; margin: 0; }

    body {
        font-family: var(--sans);
        font-size: 17px;
        color: var(--ink);
        background: var(--bg);
        -webkit-font-smoothing: antialiased;
        display: flex;
        flex-direction: column;
        height: 100vh;
        height: 100dvh;   /* dynamic viewport: excludes iOS Safari's retracting URL bar */
        overflow: hidden;
    }

    .num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }

    /* ---------- top bar ---------- */
    .topbar {
        flex: 0 0 auto;
        display: flex;
        align-items: center;
        gap: 16px;
        /* respect the notch in landscape; falls back to 16px on devices with no inset */
        padding: 0 max(16px, env(safe-area-inset-right)) 0 max(16px, env(safe-area-inset-left));
        height: 48px;
        background: var(--panel);
        border-bottom: 1px solid var(--line);
        position: relative;   /* anchors the mobile ⋯ overflow dropdown */
    }
    .brand {
        font-weight: 700;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        font-size: 15.5px;
        white-space: nowrap;
        cursor: pointer;
        user-select: none;
    }
    .brand .dot { color: var(--accent); }

    .search { flex: 1 1 auto; position: relative; max-width: 560px; }
    .search input {
        width: 100%; height: 30px;
        padding: 0; padding-inline: 30px 28px;
        font-family: var(--sans); font-size: 17px;
        color: var(--ink); background: var(--bg);
        border: 1px solid var(--line); border-radius: 6px; outline: none;
    }
    .search input:focus { border-color: var(--accent); }
    .search .icon {
        position: absolute; inset-inline-start: 9px; top: 50%;
        transform: translateY(-50%);
        color: var(--ink-faint); font-size: 17px;
    }
    .search .clear {
        position: absolute; inset-inline-end: 5px; top: 50%;
        transform: translateY(-50%);
        width: 19px; height: 19px; padding: 0;
        display: none; align-items: center; justify-content: center;
        border: none; border-radius: 50%; background: transparent;
        color: var(--ink-faint); font-size: 14px; line-height: 1; cursor: pointer;
    }
    .search .clear:hover { color: var(--ink); background: var(--line-soft); }
    .search .clear.show { display: flex; }

    .topbar-right { margin-inline-start: auto; display: flex; align-items: center; gap: 14px; white-space: nowrap; }
    /* The ⋯ overflow button only appears on phones (see the card media query). */
    .more-btn { display: none; margin-inline-start: auto; font-size: 20px; line-height: 1; padding: 0 12px; }
    .count { font-size: 15.5px; color: var(--ink-dim); }
    .count b { color: var(--ink); font-weight: 600; }
    .count .note { color: var(--ink-faint); }
    .topbtn {
        font-family: var(--sans); font-size: 15.5px;
        color: var(--ink-dim); background: transparent;
        border: 1px solid var(--line); border-radius: 6px;
        height: 28px; padding: 0 10px; cursor: pointer;
    }
    .topbtn:hover { color: var(--ink); border-color: var(--ink-faint); }
    .topbtn.on { color: var(--ink); border-color: var(--accent, var(--ink-faint)); }
    /* The language dropdown is a <select> wearing the .topbtn look. */
    select.topbtn { -webkit-appearance: none; appearance: none; padding-inline-end: 22px;
        background-image: linear-gradient(45deg, transparent 50%, var(--ink-dim) 50%),
            linear-gradient(135deg, var(--ink-dim) 50%, transparent 50%);
        background-position: calc(100% - 11px) 12px, calc(100% - 6px) 12px;
        background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
    /* RTL: the ▾ moves to the button's inline-end, which is the LEFT edge. */
    [dir="rtl"] select.topbtn { background-position: 6px 12px, 11px 12px; }
    select.topbtn option { color: var(--ink); background: var(--bg); }


    /* ---------- active-filter chips ----------
       A strip below the top bar that mirrors every active filter as a
       removable pill (one click removes that one filter), with a trailing
       "clear all". Hidden entirely when nothing is filtered. Built by
       renderChips() on every recompute, dispatched by data-kind/data-val. */
    /* One-line self-introduction: what noir.wiki is + the key interaction
       (rows expand). Muted, matches the topbar/thead panel; hidden on narrow
       screens (1000px query below), where space is tight and tap-to-expand is
       already the norm. */
    .masthead {
        flex: 0 0 auto;
        padding: 7px 16px;
        font-size: 13.5px; line-height: 1.3;
        color: var(--ink-dim);
        background: var(--panel);
        border-bottom: 1px solid var(--line-soft);
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .masthead .mh-hint { color: var(--ink-faint); }

    /* Language nudge bar (shown only for a non-English target language). */
    .langnudge {
        flex: 0 0 auto;
        display: flex; align-items: center; flex-wrap: wrap; gap: 7px 10px;
        padding: 8px 16px;
        font-size: 13.5px; line-height: 1.35; color: var(--ink);
        background: var(--accent-bg); border-bottom: 1px solid var(--accent);
    }
    .langnudge[hidden] { display: none; }
    .langnudge .ln-act {
        font: inherit; cursor: pointer; color: var(--accent);
        background: transparent; border: 1px solid var(--accent);
        border-radius: 12px; padding: 2px 10px; line-height: 1.4; white-space: nowrap;
    }
    .langnudge .ln-act:hover { background: var(--accent); color: #fff; }
    .langnudge .ln-select {
        font: inherit; color: var(--ink); background: var(--bg);
        border: 1px solid var(--accent); border-radius: 8px;
        padding: 1px 4px; cursor: pointer;
    }
    .langnudge .ln-select option { color: var(--ink); background: var(--bg); }
    .langnudge .ln-x {
        margin-inline-start: auto; flex: 0 0 auto;
        font-size: 14px; color: var(--ink-dim);
        background: transparent; border: 0; cursor: pointer; padding: 2px 4px;
    }
    .langnudge .ln-x:hover { color: var(--accent); }

    .chipbar {
        flex: 0 0 auto; display: none;
        flex-wrap: wrap; align-items: center; gap: 6px;
        padding: 7px 16px;
        background: var(--panel); border-bottom: 1px solid var(--line);
    }
    .chip {
        display: inline-flex; align-items: center; gap: 6px;
        height: 24px; padding: 0; padding-inline: 9px 6px; max-width: 260px;
        font-size: 14px; color: var(--ink);
        background: var(--accent-bg); border: 1px solid var(--accent);
        border-radius: 12px; cursor: pointer; user-select: none;
    }
    .chip .chip-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .chip .x { flex: 0 0 auto; color: var(--accent); font-size: 12px; line-height: 1; }
    .chip:hover { background: var(--accent); color: #fff; }
    .chip:hover .x { color: #fff; }
    .chip-clear { margin-inline-start: 4px; font-size: 14px; color: var(--ink-dim); cursor: pointer; }
    .chip-clear:hover { color: var(--accent); text-decoration: underline; }

    /* ---------- body split ---------- */
    .body { flex: 1 1 auto; display: flex; min-height: 0; position: relative; }

    /* ---------- facet rail ---------- */
    .rail {
        flex: 0 0 310px;
        background: var(--rail);
        border-inline-end: 1px solid var(--line);
        overflow-y: auto;
        padding: 4px 0 24px;
    }
    .facet { padding: 14px 16px 6px; }
    .facet + .facet { border-top: 1px solid var(--line-soft); }
    .facet-head {
        display: flex; align-items: baseline; justify-content: space-between;
        margin-bottom: 8px; user-select: none;
    }
    .facet-title {
        font-size: 13.5px; font-weight: 700;
        letter-spacing: 0.1em; text-transform: uppercase;
        color: var(--ink-dim); cursor: pointer;
    }
    /* fold indicator + collapse: tap the header to hide the facet body */
    .facet-title::before {
        content: '▾'; display: inline-block; width: 0.9em; margin-inline-end: 3px;
        font-size: 9px; vertical-align: 1px; color: var(--ink-faint);
        transition: transform 0.12s ease;
    }
    .facet.collapsed .facet-title::before { transform: rotate(-90deg); }
    .facet.collapsed > :not(.facet-head) { display: none; }
    .facet.collapsed .facet-head { margin-bottom: 0; }
    .facet-clear { font-size: 14px; color: var(--accent); cursor: pointer; visibility: hidden; }
    .facet-clear.show { visibility: visible; }
    .facet-actions { display: flex; align-items: baseline; gap: 12px; }
    .facet-sort {
        font-size: 11px; color: var(--ink-dim); cursor: pointer;
        letter-spacing: 0.02em; white-space: nowrap;
    }
    .facet-sort:hover { color: var(--accent); }

    .opt {
        display: flex; align-items: center; gap: 8px;
        height: 24px; cursor: pointer; color: var(--ink); user-select: none;
    }
    .opt:hover { color: var(--accent); }
    .opt .box {
        width: 13px; height: 13px;
        border: 1.5px solid var(--ink-faint);
        border-radius: 3px; flex: 0 0 auto;
    }
    .opt.on .box { background: var(--accent); border-color: var(--accent); position: relative; }
    .opt.on .box::after {
        content: ""; position: absolute; left: 3px; top: 0px;
        width: 4px; height: 8px;
        border: solid #fff; border-width: 0 2px 2px 0;
        transform: rotate(45deg);
    }
    .opt .label { flex: 1 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .opt .cnt { color: var(--ink-faint); font-size: 14px; }
    .opt.on .cnt { color: var(--accent); }
    .opt.empty { opacity: 0.4; }
    .facet-empty { display: none; padding: 5px 2px 2px; color: var(--ink-faint); font-size: 14px; font-style: italic; }

    .sev-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }

    .mini-search {
        width: 100%; height: 24px; margin-bottom: 6px;
        padding: 0 8px; font-size: 15.5px; font-family: var(--sans);
        color: var(--ink); background: var(--panel);
        border: 1px solid var(--line); border-radius: 5px; outline: none;
    }
    .mini-search:focus { border-color: var(--accent); }
    .country-list { max-height: 260px; overflow-y: auto; }
    .type-list { max-height: 300px; overflow-y: auto; }

    /* selected-countries mini-map (Place facet); hidden until ≥1 selection
       maps to a geojson feature. Highlights the selection, zooms to fit it. */
    /* placeholder reserves the collapsed footprint so the rail layout never
       shifts; the inner box is what actually grows (and pops out, fixed) on hover. */
    /* touch-action:none -> the browser hands pan/pinch gestures to our
       pointer handlers instead of scrolling/zooming the page. */
    .country-map { direction: ltr; position: relative; margin-top: 10px; height: 170px; touch-action: none; }
    .country-map.empty { display: none; }
    .cmap-inner {
        position: absolute; inset: 0;
        border: 1px solid var(--line-soft); border-radius: 6px;
        overflow: hidden; background: var(--bg);
        z-index: 1; transition: box-shadow .16s ease;
    }
    .cmap-inner.expanded {           /* fixed -> escapes the rail's overflow clip */
        position: fixed; inset: auto; z-index: 80;
        border-color: var(--line);
        box-shadow: 0 10px 34px rgba(0,0,0,.30);
    }
    #cmap-svg { display: block; width: 100%; height: 100%; }
    /* overlay svg for the hovered row's country outline (above the map, below the dot) */
    .cmap-overlay { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 3; }
    #cmap-hl-country { fill: rgba(255, 214, 0, 0.16); stroke: #ffd400; stroke-width: 1.4px; stroke-linejoin: round; }
    /* pulsing marker shown when a table row is hovered, at that event's coord */
    .cmap-hl {
        position: absolute; display: none; z-index: 5; pointer-events: none;
        width: 9px; height: 9px; border-radius: 50%;
        background: #ffe600; border: 1.5px solid #1c1c1e;
        transform: translate(-50%, -50%);
        animation: cmap-pulse .9s ease-out infinite;
    }
    @keyframes cmap-pulse {
        0%   { box-shadow: 0 0 0 0 rgba(255, 214, 0, 0.7); }
        70%  { box-shadow: 0 0 0 13px rgba(255, 214, 0, 0); }
        100% { box-shadow: 0 0 0 0 rgba(255, 214, 0, 0); }
    }
    .cmap-land { fill: var(--cmap-land); stroke: var(--panel); stroke-width: 0.4px; }

    /* "Enlarge map" toggle (top-right of the mini-map) — always visible on both
       mouse and touch, a persistent cue that the map zooms. Click opens the map
       enlarged and PINNED (JS); the icon swaps to ✕ while open. */
    .cmap-zoom {
        display: inline-flex; align-items: center; justify-content: center;
        position: absolute; top: 5px; right: 5px; z-index: 6;
        width: 32px; height: 32px; padding: 0; line-height: 1; font-size: 16px;
        color: var(--ink-dim); background: var(--panel); cursor: pointer;
        border: 1px solid var(--line); border-radius: 6px; opacity: 0.88;
        transition: opacity .12s ease, color .12s ease;
    }
    .cmap-zoom:hover { opacity: 1; color: var(--ink); }
    .cmap-sel  { fill: var(--cmap-sel); stroke: var(--panel); stroke-width: 0.4px; }
    .cmap-dot  { fill: var(--cmap-dot); fill-opacity: 0.85; stroke: #fff; stroke-width: 0.3px; stroke-opacity: 0.5; cursor: pointer; }
    .opt .emoji { margin-inline-end: 6px; width: 1.1em; text-align: center; flex: 0 0 auto; }

    /* Year distribution histogram + dual-handle range brush (above the inputs).
       Bars show the event count per decade (sqrt-scaled); the brush over the
       track selects a sub-range and is two-way synced with the from/to boxes. */
    .yearhist { direction: ltr; margin: 2px 0 9px; user-select: none; }
    .yh-bars {
        display: flex; align-items: flex-end; gap: 1px;
        height: 38px; padding: 0 1px;
    }
    .yh-bar {
        flex: 1 1 0; min-width: 0; min-height: 0;
        background: var(--ink-faint); border-radius: 1px 1px 0 0;
        transition: background .08s, opacity .08s;
        cursor: pointer;
    }
    .yh-bar:hover { background: var(--accent); opacity: .7; }
    /* when a range is active, in-range bars take the accent, out-of-range fade */
    .yearhist.sel .yh-bar { background: var(--accent); }
    .yh-bar.dim { background: var(--ink-faint); opacity: .35; }
    .yh-track {
        position: relative; height: 16px; margin-top: 3px;
        border-top: 1px solid var(--line);
    }
    .yh-track::before {           /* the unselected baseline rail */
        content: ""; position: absolute; left: 0; right: 0; top: 7px; height: 3px;
        background: var(--line); border-radius: 2px;
    }
    .yh-range {        /* blends with the baseline until a range is set */
        position: absolute; top: 7px; height: 3px;
        background: var(--line); border-radius: 2px; pointer-events: none;
    }
    .yearhist.sel .yh-range { background: var(--accent); }
    .yh-handle {
        position: absolute; top: 8.5px; left: 0;
        width: 13px; height: 13px; margin-left: -6.5px;
        background: var(--panel); border: 2px solid var(--accent);
        border-radius: 50%; cursor: ew-resize; transform: translateY(-50%);
        box-shadow: 0 1px 2px rgba(0,0,0,.2); touch-action: none;
    }
    .yh-handle:hover, .yh-handle:active { background: var(--accent); }

    .yearrange { display: flex; align-items: center; gap: 6px; }
    .yearrange input {
        width: 100%; height: 26px; padding: 0 8px;
        font-family: var(--mono); font-variant-numeric: tabular-nums;
        font-size: 15.5px; color: var(--ink); background: var(--panel);
        border: 1px solid var(--line); border-radius: 5px; outline: none; text-align: center;
    }
    .yearrange input:focus { border-color: var(--accent); }
    .yearrange .sep { color: var(--ink-faint); }

    /* ---------- popularity facet (dual-handle range slider) ----------
       Two overlaid native range inputs share one track: the inputs are
       transparent + pointer-events:none, only their thumbs are interactive, and
       a .pop-fill bar (positioned by JS) shows the selected span. */
    .poprange { display: flex; align-items: center; gap: 10px; margin: 8px 0 2px; }
    .pop-slider { direction: ltr; position: relative; flex: 1 1 auto; height: 16px; }
    .pop-track, .pop-fill { position: absolute; top: 6px; height: 4px; border-radius: 3px; }
    .pop-track { left: 0; right: 0; background: var(--line); }
    .pop-fill  { left: 0; right: 0; background: var(--accent); }
    .pop-slider input[type=range] {
        -webkit-appearance: none; appearance: none;
        position: absolute; top: 0; left: 0; width: 100%; height: 16px;
        margin: 0; background: transparent; pointer-events: none;
    }
    .pop-slider #pop-min { z-index: 3; }
    .pop-slider #pop-max { z-index: 4; }
    .pop-slider input[type=range]::-webkit-slider-thumb {
        -webkit-appearance: none; appearance: none;
        width: 14px; height: 14px; border-radius: 50%;
        background: var(--accent); border: 2px solid var(--panel);
        box-shadow: 0 0 0 1px var(--line); cursor: pointer; pointer-events: auto;
    }
    .pop-slider input[type=range]::-moz-range-thumb {
        width: 14px; height: 14px; border-radius: 50%;
        background: var(--accent); border: 2px solid var(--panel);
        box-shadow: 0 0 0 1px var(--line); cursor: pointer; pointer-events: auto;
    }
    .pop-slider input[type=range]::-moz-range-track { background: transparent; }
    .pop-val {
        flex: 0 0 auto; min-width: 44px; text-align: end;
        font-family: var(--mono); font-variant-numeric: tabular-nums;
        font-size: 14px; color: var(--ink-dim);
    }
    .pop-val.on { color: var(--ink); }

    .lang-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 4px 6px; }
    .lang-chip {
        display: flex; align-items: center; justify-content: center; gap: 4px;
        height: 24px; font-size: 14px; font-weight: 600;
        border: 1px solid var(--line); border-radius: 5px; cursor: pointer;
        background: var(--panel); color: var(--ink-dim); user-select: none;
    }
    .lang-chip.on { background: var(--accent-bg); border-color: var(--accent); color: var(--accent); }
    .lang-chip .lc { color: var(--ink-faint); font-size: 12.5px; font-variant-numeric: tabular-nums; }
    .lang-chip.on .lc { color: var(--accent); }

    .lang-sel { font-size: 14px; white-space: nowrap; }
    .lang-sel [data-langsel] { color: var(--accent); cursor: pointer; }
    .lang-sel [data-langsel]:hover { text-decoration: underline; }
    .lang-sel .sep { color: var(--ink-faint); margin: 0 4px; }

    /* ---------- table area ---------- */
    .table-wrap { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; background: var(--panel); }

    .grid-row { display: grid; grid-template-columns: 130px 190px 1fr 90px 84px 104px; align-items: center; }

    .thead { flex: 0 0 auto; border-bottom: 1px solid var(--line); background: var(--panel); }
    .th {
        display: flex; align-items: center; gap: 4px;
        height: 34px; padding: 0 12px;
        font-size: 13.5px; font-weight: 700;
        letter-spacing: 0.08em; text-transform: uppercase;
        color: var(--ink-dim); cursor: pointer; user-select: none; white-space: nowrap;
    }
    .th:hover { color: var(--ink); }
    .th.num-col { justify-content: flex-end; }
    .th .caret { font-size: 12px; opacity: 0; }
    .th.sorted { color: var(--accent); }
    .th.sorted .caret { opacity: 1; color: var(--accent); }

    .tbody { flex: 1 1 auto; overflow-y: auto; }

    .row { height: var(--row-h); border-bottom: 1px solid var(--line-soft); cursor: pointer; }
    .row:hover { background: var(--row-hover); }
    .row.sel { background: var(--row-sel); }
    /* Disclosure affordance: a faint caret in the left gutter (inside the date
       cell's padding, so it never overlaps text) that reveals on row hover and
       rotates to point down when the row is expanded — teaching that a row opens
       a detail panel (distinct from the title, which links out to the article).
       Pure CSS off .row state; pointer-events:none so clicks fall through to the
       row. The row is position:absolute (set inline), so this anchors to it. */
    .row::before {
        content: '\203A';   /* › */
        position: absolute; inset-inline-start: 1px; top: 0; height: 100%; width: 11px;
        display: flex; align-items: center; justify-content: center;
        color: var(--ink-faint); font-size: 14px; line-height: 1;
        opacity: 0; transform-origin: center;
        transition: opacity .12s ease, transform .12s ease;
        pointer-events: none;
    }
    .row:hover::before { opacity: .55; }
    .row.sel::before { opacity: .8; transform: rotate(90deg); }
    /* RTL: the expand caret points INTO the row (‹), and must rotate the other
       way to end up pointing down when open. */
    [dir="rtl"] .row::before { content: '\2039'; }
    [dir="rtl"] .row.sel::before { transform: rotate(-90deg); }
    .row.loading::before { display: none; }
    /* Not-yet-loaded row (API/thin-client mode): a faint pulsing bar where the
       event title will land, so a fast scroll shows skeletons, not blank gaps. */
    .row.loading { cursor: default; }
    .row.loading:hover { background: none; }
    .ph-bar { display: inline-block; width: 42%; height: 9px; border-radius: 4px;
        background: var(--line); opacity: .5; animation: ph-pulse 1.1s ease-in-out infinite; }
    @keyframes ph-pulse { 0%,100% { opacity: .25; } 50% { opacity: .6; } }

    .cell { padding: 0 12px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

    .c-date { color: var(--ink); font-variant-numeric: tabular-nums; }
    .c-date .bc { font-size: 13px; color: var(--ink-faint); margin-inline-start: 2px; }
    .c-date .pre { color: var(--ink-faint); font-size: 14px; margin-inline-end: 4px; }
    .c-date .coarse { color: var(--ink-dim); font-style: italic; }

    .c-place { color: var(--ink-dim); }
    .c-place .flag { margin-inline-end: 6px; }
    /* SVG country flags (the flags.svg sprite; emoji fallback needs none of
       this): text-sized, with a hairline inset edge so white flag fields
       (Japan, …) keep a boundary against the panel background. The edge is a
       translucent mid-grey so it reads on both themes without theme vars. */
    .flag-svg { width: 1.24em; height: 0.93em; vertical-align: -0.12em;
        border-radius: 2px; box-shadow: inset 0 0 0 1px rgba(128, 128, 128, 0.35); flex: none; }

    .c-event { display: flex; align-items: center; gap: 8px; }
    .c-event .sev { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
    .c-event a { color: var(--ink); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .c-event a:visited { color: var(--visited); }
    .row:hover .c-event a { color: var(--accent); text-decoration: underline; }
    .type-badge { flex: 0 0 auto; font-size: 13px; line-height: 1; cursor: default;
        filter: saturate(0.85); opacity: 0.9; }
    .type-badge.none { opacity: 0.3; }
    /* Monochrome type-badge icons (the type-icons.svg sprite; the emoji fallback
       needs none of this). Text-sized, muted so the badge reads as a signal, not
       a sticker; currentColor keeps it theme-aware. */
    .type-icon { width: 1em; height: 1em; vertical-align: -0.15em;
        display: inline-block; color: var(--ink-dim); }
    .c-event .book-flag { flex: 0 0 auto; font-size: 12px; line-height: 1; cursor: default;
        opacity: 0.85; margin-inline-start: -2px; }

    .c-deaths { text-align: end; font-variant-numeric: tabular-nums; font-family: var(--mono); }
    .c-deaths.none { color: var(--ink-faint); }
    .c-deaths .big { color: var(--sev-4); font-weight: 600; }

    /* Popularity (pageviews_score, 0-100): a right-aligned number over a faint
       bar whose width tracks the score, so the column scans as a magnitude. */
    .c-views { position: relative; text-align: end; font-variant-numeric: tabular-nums;
        font-family: var(--mono); color: var(--ink-dim); }
    .c-views.none { color: var(--ink-faint); }
    .c-views .pv-bar { position: absolute; inset-inline-start: 8px; top: 50%; transform: translateY(-50%);
        height: 15px; border-radius: 2px; background: var(--accent); opacity: 0.16; z-index: 0; }
    .c-views .pv-num { position: relative; z-index: 1; }

    .c-langs { display: flex; gap: 3px; justify-content: flex-end; align-items: center; }
    .lng {
        font-size: 12.5px; font-weight: 700; padding: 1px 3px; border-radius: 3px;
        background: var(--line-soft); color: var(--ink-dim); text-transform: uppercase;
    }
    .lng.all { background: var(--accent-bg); color: var(--accent); }
    /* the "+N" overflow chip when >3 editions: outlined, not filled, so it
       reads as "more of these" rather than a language code */
    .lng.more { background: transparent; box-shadow: inset 0 0 0 1px var(--line);
        color: var(--ink-faint); }

    .missing { color: var(--ink-faint); }

    /* ---------- inline detail strip ---------- */
    .detail {
        background: var(--rail);
        border-bottom: 1px solid var(--line);
        padding: 14px 16px 16px; padding-inline-start: 28px;
    }
    .detail h4 { margin: 0 0 10px; font-size: 18px; font-weight: 600; }
    /* faint ⚙ after a machine-translated English headline in the dense table row */
    .c-event .mt-flag { color: var(--ink-faint); font-size: 11px; margin-inline-start: 5px; cursor: help; flex: none; }
    .detail .desc { margin: 0 0 12px; font-size: 15.5px; line-height: 1.45; color: var(--ink); max-width: 78ch; }
    .detail .desc-mt {
        font-size: 12px; font-weight: 600; color: var(--ink-faint); cursor: help;
        border: 1px solid var(--line); border-radius: 4px; padding: 0 5px;
        margin-inline-start: 6px; white-space: nowrap; vertical-align: middle;
    }
    /* primary "read the article" action — filled so it reads as the main CTA.
       Fill is pinned to the deep-indigo accent (not var(--accent), whose dark-
       theme value is too light for legible white text) so #fff clears ~7:1 in
       both themes while still standing out against the near-black dark bg. */
    .detail .read-cta {
        display: inline-flex; align-items: center; gap: 7px;
        margin: 0 0 14px; padding: 8px 15px;
        font-size: 15.5px; font-weight: 600; line-height: 1;
        color: #fff; background: #4f46b8; border-radius: 7px;
        text-decoration: none; white-space: nowrap;
    }
    .detail .read-cta:hover { filter: brightness(1.1); }
    .detail .read-cta:visited { color: #fff; }
    .detail .meta-line { display: flex; flex-wrap: wrap; gap: 18px; margin-bottom: 12px; font-size: 15.5px; color: var(--ink-dim); }
    .detail .meta-line b { color: var(--ink); font-weight: 600; }
    .detail .ext { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 12px; font-size: 15.5px; }
    .detail .ext a { color: var(--accent); text-decoration: none; }
    .detail .ext a:visited { color: var(--visited); }
    .detail .ext a:hover { text-decoration: underline; }
    .detail .ext button.share-link { color: var(--accent); background: none; border: 0; padding: 0;
        font: inherit; font-size: 15.5px; cursor: pointer;
        display: inline-flex; align-items: center; gap: 5px; vertical-align: baseline; }
    /* underline the label only — an underlined inline <svg> box looks broken */
    .detail .ext button.share-link:hover .sl-label { text-decoration: underline; }
    /* taps land on the button, never on an <svg> child (closest() would still
       resolve, but this keeps the hit area uniform) */
    .detail .ext button.share-link .sl-icon { flex: none; pointer-events: none; }
    /* touch: the 44px minimum the top bar, facet rows and language chips honour.
       This is the row's one real action — on a phone it opens the share sheet. */
    @media (pointer: coarse) {
        .detail .ext button.share-link { min-height: 44px; padding-inline-end: 6px; }
    }

    /* ---------- recommended-books strip ---------- */
    .detail .books { margin-top: 14px; }
    .detail .books-h { font-size: 12.5px; font-weight: 700; text-transform: uppercase;
        letter-spacing: .04em; color: var(--ink-faint); margin-bottom: 8px; }
    .detail .book-grid { display: flex; flex-wrap: wrap; gap: 10px; }
    .detail .book {
        display: flex; gap: 9px; width: 260px; padding: 7px 9px;
        border: 1px solid var(--line); border-radius: 7px; background: var(--panel);
        color: var(--ink); text-decoration: none; align-items: center;
    }
    .detail .book:hover { border-color: var(--accent); }
    .detail .book-cover {
        flex: 0 0 auto; width: 38px; height: 56px; border-radius: 3px;
        object-fit: cover; background: var(--rail); box-shadow: 0 1px 3px rgba(0,0,0,.25);
    }
    .detail .book-cover.ph { display: flex; align-items: center; justify-content: center;
        font-size: 20px; box-shadow: none; }
    .detail .book-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
    .detail .book-title { font-size: 14px; font-weight: 600; line-height: 1.25;
        overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
    .detail .book-author { font-size: 12.5px; color: var(--ink-dim);
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .detail .book-rating { font-size: 12.5px; color: var(--accent); font-weight: 600; }
    .detail .book-count { color: var(--ink-faint); font-weight: 400; padding-inline-start: 5px; font-family: var(--mono); }

    /* ---------- loading / error ---------- */
    .status { padding: 40px; text-align: center; color: var(--ink-faint); font-size: 18px; }

    /* Transient backend-error toast (API mode): floats over the table instead of
       replacing it, so one failed page fetch never blanks loaded rows. Click or
       timeout dismisses; the next scroll/filter retries the fetch anyway. */
    #err-toast {
        position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
        z-index: 90; max-width: min(560px, calc(100vw - 32px));
        padding: 10px 14px; display: none; cursor: pointer;
        font-size: 14px; line-height: 1.4;
        color: #fff; background: #b3372e;
        border: 1px solid rgba(0, 0, 0, 0.25); border-radius: 8px;
        box-shadow: 0 6px 22px rgba(0, 0, 0, 0.3);
    }

    /* ---------- keyboard focus ----------
       The rail/table controls are custom divs (focusable via tabindex); give
       them a real focus ring without affecting mouse clicks (:focus-visible). */
    .opt:focus-visible, .th:focus-visible, .lang-chip:focus-visible,
    .row:focus-visible, .chip:focus-visible, .chip-clear:focus-visible,
    .facet-title:focus-visible, .facet-clear:focus-visible,
    .facet-sort:focus-visible, [data-langsel]:focus-visible, .brand:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 1px;
        border-radius: 3px;
    }

    /* ---------- instant hover tooltip ----------
       The cells ellipsize and have overflow:hidden, which would clip a CSS
       pseudo-element tooltip, and the native `title` attribute has an
       un-overridable ~0.5–1s delay. So a single floating element, parented to
       <body> (escapes the clip), positioned at the cursor and shown with no
       delay on mouseover of any `[data-tip]` cell. pointer-events:none so it
       never eats the hover it is describing. */
    #tip {
        position: fixed; z-index: 100; pointer-events: none;   /* above the expanded map (z 80) */
        max-width: 380px; padding: 4px 9px;
        font-size: 14px; line-height: 1.35;
        background: var(--ink); color: var(--bg);
        border: 1px solid var(--line); border-radius: 5px;
        box-shadow: 0 3px 12px rgba(0, 0, 0, 0.28);
        display: none; white-space: normal; overflow-wrap: anywhere;
    }

    /* ---------- responsive: the facet rail collapses into a drawer ----------
       On wide screens the rail is the normal static sidebar. Below 1000px it
       becomes an off-canvas drawer toggled by the ☰ button, over a dimming
       scrim. The drawer rules live entirely inside the media query so the
       leftover `.open`/`.show` classes are inert once the viewport widens. */
    .rail-toggle { display: none; }
    .scrim { display: none; }

    /* Below this width the rail collapses to a drawer. 1000px (not 820) because a
       phone in LANDSCAPE is typically 844–932 CSS px, and the inline layout
       (310px rail + ~690px min table = ~1000px) overflows/zooms below that — so
       landscape phones and portrait tablets get the drawer + narrow table, and
       only true desktops (>1000px) keep the inline rail + full 6-column table. */
    @media (max-width: 1000px) {
        .masthead { display: none; }
        .topbar { gap: 10px;
            padding: 0 max(10px, env(safe-area-inset-right)) 0 max(10px, env(safe-area-inset-left)); }
        /* labelled, not a bare glyph: testers didn't realise the ☰ hid the
           filters + map, so spell it out — a labelled button reads as tappable */
        .rail-toggle { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
            font-size: 16px; }
        .rail-toggle .rt-label { font-size: 14px; font-weight: 600; letter-spacing: 0.01em; }
        .rail {
            position: absolute; top: 0; inset-inline-start: 0; bottom: 0;
            flex: none; width: 300px; max-width: 85vw; z-index: 6;
            transform: translateX(-100%); transition: transform 0.18s ease;
            box-shadow: 2px 0 14px rgba(0, 0, 0, 0.18);
        }
        /* RTL: the drawer anchors at inline-start (the RIGHT edge), so it hides
           off-screen right; .rail.open below wins in both directions. */
        [dir="rtl"] .rail {
            transform: translateX(100%);
            box-shadow: -2px 0 14px rgba(0, 0, 0, 0.18);
        }
        .rail.open { transform: translateX(0); }
        .scrim.show {
            display: block; position: absolute; inset: 0; z-index: 5;
            background: rgba(0, 0, 0, 0.4);
        }
    }

    /* ---------- responsive: the table columns ----------
       The rows are absolutely positioned (left:0/right:0) inside a
       vertical-only scroller, so the fixed-width grid can't be reached by
       horizontal scrolling on a narrow screen — it just gets clipped at the
       right edge. Instead we progressively DROP the least-essential columns
       as the viewport narrows; the dropped data (place, languages) is still
       shown in the row's expand/detail strip. Grid ignores `display:none`
       cells when placing, so hiding cells + matching the column count keeps
       every row on a single line. */
    @media (max-width: 1000px) {
        /* rail is a drawer here, so the table has the full width — drop Langs.
           Deaths gets the desktop 90px so a 5-digit toll ("73,338") isn't
           ellipsized (6-digit+ values are compacted to "830k"/"6M"). */
        .grid-row { grid-template-columns: 96px 150px 1fr 90px; }
        .c-langs, .c-views, .th[data-col="langs"], .th[data-col="views"] { display: none; }
    }

    @media (max-width: 560px) {
        /* Rows reflow to a card grid here; the absolute left-gutter caret would
           collide, and touch has no hover to reveal it — so drop it. */
        .row::before { display: none; }
        /* ---- top bar: collapse the secondary controls into a ⋯ dropdown ----
           The controls keep their real DOM/IDs/handlers — the rail just becomes
           a positioned dropdown panel instead of an inline row, so nothing is
           duplicated. The search box keeps its inline spot. */
        .more-btn { display: inline-flex; align-items: center; }
        .topbar-right {
            position: absolute; top: 100%; inset-inline-end: 8px; margin-top: 6px;
            flex-direction: column; align-items: stretch; gap: 8px;
            min-width: 210px; padding: 8px; z-index: 60;
            background: var(--bg); border: 1px solid var(--line); border-radius: 8px;
            box-shadow: 0 6px 22px rgba(0, 0, 0, 0.28);
            display: none;
        }
        .topbar-right.open { display: flex; }
        .topbar-right > * { width: 100%; }
        /* keep the live result count visible as the menu's first line */
        .topbar-right .count { padding-bottom: 4px; border-bottom: 1px solid var(--line-soft); }
        .topbar-right .topbtn, .topbar-right select.topbtn { height: 44px; text-align: start; }

        /* ---- result rows become 2-line cards ----
           The virtualizer keeps a single fixed ROWH (set for this breakpoint in
           viewer.js), so cards are uniform-height. Title wraps to two lines; a
           metadata line (date · place · deaths) sits beneath it. Only `.row`
           cells reflow — the `.thead` row is handled separately below. */
        .row.grid-row {
            grid-template-columns: auto minmax(0, 1fr) auto;
            grid-template-areas:
                "event  event  event"
                "date   place  deaths";
            align-content: center;
            row-gap: 3px;
            padding: 0 14px;
        }
        .row .c-event  { grid-area: event; align-items: flex-start; gap: 6px; min-width: 0; }
        .row .c-event a {
            flex: 1 1 auto; min-width: 0;
            white-space: normal; font-size: 15.5px; line-height: 1.25;
            display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
        }
        .row .c-event .sev { margin-top: 5px; }
        .row .c-event .type-badge { margin-top: 1px; }
        .row .c-date   { grid-area: date;   font-size: 13px; }
        .row .c-place  { grid-area: place;  font-size: 13px; min-width: 0; }
        .row .c-deaths { grid-area: deaths; font-size: 13px; }

        /* ---- sort header becomes a row of tappable sort chips ---- */
        .thead .grid-row { display: flex; flex-wrap: wrap; gap: 5px; padding: 5px 10px; }
        .thead .th {
            height: 44px; padding: 0 10px;
            border: 1px solid var(--line); border-radius: 22px;
        }
        .thead .th[data-col="views"] { display: flex; }   /* allow sort-by-popularity */

        /* ---- detail strip: stack books full-width on a narrow screen ---- */
        .detail { padding: 12px 14px 14px; }
        .detail .book { width: 100%; }
        .detail .meta-line { gap: 10px 16px; }
        /* the read CTA is the primary action here — make it a full-width,
           thumb-sized, centered button so it can't overflow or be missed */
        .detail .read-cta { display: flex; justify-content: center; white-space: normal;
            text-align: center; padding: 12px 15px; }
    }

    /* ---------- touch ergonomics (coarse pointer = finger, not mouse) ----------
       A touch device can't hover and can't hit a 24px control reliably. This block
       (1) bumps every tappable form control to >=16px so iOS Safari stops
       auto-zooming the page on focus, and (2) grows the rail's touch targets toward
       the ~44px guideline. The table rows themselves are JS-positioned by ROWH
       (viewer.js), which already switches to a taller value on a coarse pointer. */
    @media (pointer: coarse) {
        /* iOS zooms the viewport when a focused input's text is < 16px; bump them. */
        .mini-search, .yearrange input { font-size: 16px; }

        /* top-bar buttons + language dropdown: 44px touch target (iOS/Android min) */
        .topbtn { height: 44px; }
        .search input { height: 44px; }

        /* facet rows (deaths/data brackets) and their checkboxes */
        .opt { height: 44px; }
        .facet-clear, .facet-sort { padding: 6px 2px; }

        /* language chips */
        .lang-chip { height: 44px; }

        /* range-slider handles need a finger-sized hit area */
        .yh-handle { width: 22px; height: 22px; margin-left: -11px; }
        .pop-slider { height: 24px; }
        .pop-slider input[type=range] { height: 24px; }
        .pop-slider input[type=range]::-webkit-slider-thumb { width: 22px; height: 22px; }
        .pop-slider input[type=range]::-moz-range-thumb { width: 22px; height: 22px; }

        /* the map's pan/pinch handlers already own the gesture; widen the pin too */
        .cmap-dot { stroke-width: 0.5px; }
    }

    /* ---------- attribution footer ---------- */
    .site-footer {
        flex: 0 0 auto;
        /* clear the home indicator at the bottom and the notch on the sides */
        padding: 6px max(14px, env(safe-area-inset-right))
                 calc(6px + env(safe-area-inset-bottom)) max(14px, env(safe-area-inset-left));
        border-top: 1px solid var(--line);
        background: var(--rail);
        color: var(--ink-faint);
        font-size: 12px;
        line-height: 1.4;
        text-align: center;
    }
    .site-footer a { color: var(--ink-dim); text-decoration: underline; }
    .site-footer a:hover { color: var(--accent); }
