/**
 * DRE Visualizations — shared styles.
 *
 * DESIGN CONTRACT — follow the DRE theme.
 * ----------------------------------------------------------------------------
 * This module is styled to match the Africa Multiple "Digital Research
 * Environment" Omeka S theme:
 *   https://github.com/AM-Digital-Research-Environment/DRE-theme
 *
 * Every colour, surface, border, radius, shadow and spacing value is taken
 * from the theme's CSS custom properties (design tokens) rather than being
 * hard-coded. The theme defines those tokens on :root (light) and re-defines
 * them under `body[data-theme="dark"]` and `@media (prefers-color-scheme:dark)`,
 * so by *referencing* them the module automatically follows the active light /
 * dark theme — including the live theme toggle — with no extra JS.
 *
 * The `--rv-*` aliases below are the ONLY place this module names a colour.
 * They are declared on `body` (not :root) on purpose: the theme flips its
 * tokens at `body[data-theme=...]`, and declaring the aliases on `body` lets
 * each alias resolve the *active* token from the body's own cascade.
 *
 * The hard-coded values in `var(--token, fallback)` are fallbacks for when the
 * module is used in a theme that does NOT provide the DRE tokens. When the DRE
 * theme is present its token always wins, so the fallback is ignored.
 *
 * ►► When adding new styles, ALWAYS reference a `--rv-*` alias (or a DRE theme
 *    token directly). Never introduce a raw hex / rgb colour. ◄◄
 */

/* ------------------------------------------------------------------ */
/*  Design tokens — aliases onto the DRE theme variables               */
/* ------------------------------------------------------------------ */

body {
    /* Surfaces — warm stone (mirrors the DRE light theme, never cold grey) */
    --rv-bg:              var(--surface, #fdfcfa);          /* cards, panels      */
    --rv-bg-raised:       var(--surface-raised, #fffefb);   /* hovered / floating */
    --rv-bg-sunken:       var(--surface-sunken, #f1ede6);   /* wells, chips       */
    --rv-overlay:         var(--surface-overlay, rgba(253, 252, 250, 0.92));

    /* Borders — warm low-chroma neutrals */
    --rv-border:          var(--border, #dcd6cb);
    --rv-border-light:    var(--border-light, #eae5dd);
    --rv-border-strong:   var(--border-strong, #c3b9a9);

    /* Text — warm near-neutral inks */
    --rv-heading-color:   var(--ink-strong, #33291f);
    --rv-text-strong:     var(--ink, #473e33);
    --rv-text-color:      var(--ink-light, #6c6357);

    /* Brand accent (Uni-Grün via the theme; the fallback IS Uni-Grün) */
    --rv-accent:          var(--primary, #007a50);
    --rv-accent-hover:    var(--primary-hover, #00633f);
    --rv-accent-contrast: var(--primary-contrast, #fffef9);

    /* Radii, shadows, focus — straight from the theme's token set */
    --rv-radius:          var(--radius-lg, 12px);
    --rv-radius-sm:       var(--radius-sm, 6px);
    --rv-shadow:          var(--shadow-lg, 0 4px 16px rgba(0, 0, 0, 0.12));
    --rv-shadow-sm:       var(--shadow-sm, 0 1px 3px rgba(0, 0, 0, 0.12));
    --rv-focus-ring:      var(--ring-focus, 0 0 0 3px rgba(0, 122, 80, 0.32));

    /* Type scale — rides the theme's modular --text-* scale (DESIGN.md §9 rule 4:
       "Don't invent parallel scales"). --text-2xs was added to the theme in
       v2.22 precisely because this module was already reaching for it, so every
       step below now resolves to a real theme token and the literals are pure
       fallbacks for a page rendered without the theme. */
    --rv-text-2xs:   var(--text-2xs, 0.6875rem);  /* 11px — legends, micro-captions */
    --rv-text-xs:    var(--text-xs, 0.8125rem);   /* 13px — captions, chips, fine print */
    --rv-text-sm:    var(--text-sm, 0.9375rem);   /* 15px — controls, labels, list items */
    --rv-text-base:  var(--text-base, 1.0625rem); /* 17px — body, detail titles */
    --rv-text-lg:    var(--text-lg, 1.1875rem);   /* 19px — card / panel titles */
    --rv-text-xl:    var(--text-xl, 1.5rem);      /* 24px — compare stat value */
    --rv-text-2xl:   var(--text-2xl, 1.875rem);   /* 30px — overview stat value */

    /* Spacing — rides the theme's 4 pt --space-* grid (same rule). The photo
       browser's px hairline gaps (2/6/10/14/18px) are a separate sub-grid idiom
       and stay in px; em paddings (pill badges) scale with their own text. */
    --rv-space-1:    var(--space-1, 0.25rem);     /* 4px  */
    --rv-space-2:    var(--space-2, 0.5rem);      /* 8px  */
    --rv-space-3:    var(--space-3, 0.75rem);     /* 12px */
    --rv-space-4:    var(--space-4, 1rem);        /* 16px */
    --rv-space-5:    var(--space-5, 1.25rem);     /* 20px */
    --rv-space-6:    var(--space-6, 1.5rem);      /* 24px */
    --rv-space-8:    var(--space-8, 2rem);        /* 32px */

    /* Radius — adds the full round to the existing --rv-radius (12px panels) /
       --rv-radius-sm (6px); F3 collapses the stray 3/4/6/12px radius spread onto
       these and unifies count/tag chips on the pill. */
    --rv-radius-full: var(--radius-full, 9999px);

    /* Motion — rides the theme's --transition-* (duration + ease-out-quart) and the
       hover-lift tokens, so the module animates with the theme's one motion vocab
       instead of hand-set 0.15s/0.18s/0.2s/120ms durations and raw translateY. */
    --rv-transition-fast: var(--transition-fast, 150ms cubic-bezier(0.25, 1, 0.5, 1));
    --rv-transition-base: var(--transition-base, 200ms cubic-bezier(0.25, 1, 0.5, 1));
    --rv-lift-xs:    var(--lift-xs, -0.125rem);   /* -2px — card hover lift */
    --rv-lift-sm:    var(--lift-sm, -0.25rem);    /* -4px */
}

/* ------------------------------------------------------------------ */
/*  Semantic map + cross-type recommendations                          */
/* ------------------------------------------------------------------ */

.semantic-map-intro,
.semantic-similar-intro {
    max-width: 72ch;
    margin: 0 0 var(--rv-space-5);
    color: var(--rv-text-color);
}

.semantic-map-toolbar {
    position: relative;
    z-index: 2;
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(16rem, 28rem);
    align-items: end;
    justify-content: space-between;
    gap: var(--rv-space-4);
    margin-bottom: var(--rv-space-4);
}

.semantic-map-mode {
    display: inline-flex;
    width: fit-content;
    padding: var(--rv-space-1);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-full);
    background: var(--rv-bg-sunken);
}

button.semantic-map-mode__button {
    min-height: 2.5rem;
    padding: var(--rv-space-2) var(--rv-space-4);
    border: 0;
    border-radius: var(--rv-radius-full);
    background: transparent;
    box-shadow: none;
    color: var(--rv-text-color);
    font: inherit;
    font-size: var(--rv-text-sm);
    font-weight: 700;
    cursor: pointer;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast), box-shadow var(--rv-transition-fast);
}

button.semantic-map-mode__button:hover {
    color: var(--rv-text-strong);
}

button.semantic-map-mode__button.is-active {
    background: var(--rv-bg-raised);
    box-shadow: var(--rv-shadow-sm);
    color: var(--rv-accent);
}

button.semantic-map-mode__button:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
}

.semantic-map-search {
    position: relative;
}

.semantic-map-search__label {
    display: grid;
    gap: var(--rv-space-1);
    color: var(--rv-text-strong);
    font-size: var(--rv-text-xs);
    font-weight: 700;
}

.semantic-map-search__input {
    width: 100%;
    min-height: 2.75rem;
    padding: var(--rv-space-2) var(--rv-space-3);
    border: 1px solid var(--rv-border-strong);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    color: var(--rv-text-strong);
    font: inherit;
    font-size: var(--rv-text-sm);
}

.semantic-map-search__input:focus-visible {
    outline: none;
    border-color: var(--rv-accent);
    box-shadow: var(--rv-focus-ring);
}

.semantic-map-search__results {
    position: absolute;
    top: calc(100% + var(--rv-space-1));
    right: 0;
    left: 0;
    z-index: 20;
    max-height: 20rem;
    overflow-y: auto;
    margin: 0;
    padding: var(--rv-space-1);
    list-style: none;
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg-raised);
    box-shadow: var(--rv-shadow);
}

.semantic-map-search__result {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    gap: var(--rv-space-3);
    border-radius: var(--rv-radius-sm);
}

.semantic-map-search__result a {
    padding: var(--rv-space-2) var(--rv-space-3);
    color: var(--rv-text-strong);
    font-size: var(--rv-text-sm);
    font-weight: 650;
    text-decoration: none;
}

.semantic-map-search__result:has(a:hover),
.semantic-map-search__result:has(a:focus-visible) {
    background: var(--rv-bg-sunken);
}

.semantic-map-search__result a:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
}

.semantic-map-search__type {
    padding-right: var(--rv-space-3);
    color: var(--rv-text-color);
    font-size: var(--rv-text-2xs);
    white-space: nowrap;
}

.semantic-map-search__empty {
    padding: var(--rv-space-3);
    color: var(--rv-text-color);
    font-size: var(--rv-text-sm);
}

.semantic-map-panel {
    min-width: 0;
}

.chart-container.semantic-map-chart {
    height: 38rem;
    min-height: 28rem;
}

.semantic-map-status,
.semantic-map-note {
    margin: var(--rv-space-2) 0 0;
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
}

.semantic-map-status {
    color: var(--rv-text-strong);
    font-weight: 700;
}

.semantic-similar-list {
    margin: 0;
    padding: 0;
    list-style: none;
    border-top: 1px solid var(--rv-border);
}

.semantic-similar-item {
    border-bottom: 1px solid var(--rv-border-light);
}

.semantic-similar-link {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--rv-space-4);
    padding: var(--rv-space-3) var(--rv-space-2);
    border-radius: var(--rv-radius-sm);
    color: var(--rv-text-strong);
    text-decoration: none;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast);
}

.semantic-similar-link:hover {
    background: var(--rv-bg-sunken);
    color: var(--rv-accent);
}

.semantic-similar-link:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
}

.semantic-similar-copy {
    display: grid;
    justify-items: start;
    gap: var(--rv-space-1);
    min-width: 0;
}

.semantic-similar-type {
    display: inline-block;
    padding: 0.15em 0.65em;
    border-radius: var(--rv-radius-full);
    background: var(--rv-bg-sunken);
    color: var(--rv-text-color);
    font-size: var(--rv-text-2xs);
    font-weight: 750;
    line-height: 1.45;
}

.semantic-similar-title {
    overflow-wrap: anywhere;
    font-size: var(--rv-text-base);
    font-weight: 700;
    line-height: 1.35;
}

.semantic-similar-score {
    min-width: 3.4rem;
    color: var(--rv-accent);
    font-variant-numeric: tabular-nums;
    font-weight: 800;
    text-align: right;
}

@media (max-width: 700px) {
    .semantic-map-toolbar {
        grid-template-columns: minmax(0, 1fr);
        align-items: stretch;
    }

    .semantic-map-mode {
        width: 100%;
    }

    button.semantic-map-mode__button {
        flex: 1;
        padding-inline: var(--rv-space-2);
    }

    .chart-container.semantic-map-chart {
        height: 29rem;
        min-height: 24rem;
    }

    .semantic-similar-link {
        gap: var(--rv-space-2);
        padding-inline: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    button.semantic-map-mode__button,
    .semantic-similar-link {
        transition: none;
    }
}

/*
 * Dark fallbacks for NON-DRE host themes only. When the DRE theme is active it
 * already supplies dark values for --surface / --border / --ink-* / --primary,
 * so `var(--token, …)` resolves to the theme token and these fallbacks are
 * never used. They simply keep the module usable (and dark-aware) if dropped
 * into a theme that lacks the DRE token set but honours prefers-color-scheme.
 *
 * ►► DELIBERATELY FROZEN — DO NOT "tidy" or sync to the theme. ◄◄
 * These literals are a deliberately frozen snapshot of the DRE forest-dark
 * surfaces, kept ONLY as the dark fallback after each `var(--token, …)` for a
 * non-DRE host. Under the DRE theme they are 100% inert (the token always wins),
 * so they CANNOT drift the live UI — the only thing that "drifts" is what an
 * isolated/non-DRE dark render shows, which is cosmetic. Resist the urge to
 * delete the block as "duplication" (a non-DRE dark host would then get the
 * light fallbacks) or to chase the theme's exact dark hexes (pointless churn on
 * inert values). They live in `var()` fallback position, so the token linter
 * already permits them — no lint exception is required. This is the same
 * fallback-on-brand contract as DESIGN.md §9 rule 3, doubled for dark.
 */
@media (prefers-color-scheme: dark) {
    body {
        /* Forest-dark (mirrors the DRE dark theme), lifted green accent. */
        --rv-bg:            var(--surface, #1b211e);
        --rv-bg-raised:     var(--surface-raised, #212724);
        --rv-bg-sunken:     var(--surface-sunken, #161b18);
        --rv-overlay:       var(--surface-overlay, rgba(27, 33, 30, 0.92));

        --rv-border:        var(--border, #39423d);
        --rv-border-light:  var(--border-light, #2c3531);
        --rv-border-strong: var(--border-strong, #4c554f);

        --rv-heading-color: var(--ink-strong, #f3f1ec);
        --rv-text-strong:   var(--ink, #e3e0d9);
        --rv-text-color:    var(--ink-light, #aaa498);

        --rv-accent:          var(--primary, #3fb488);
        --rv-accent-hover:    var(--primary-hover, #5cc49d);
        --rv-accent-contrast: var(--primary-contrast, #14201a);

        --rv-shadow:    var(--shadow-lg, 0 4px 16px rgba(0, 0, 0, 0.5));
        --rv-shadow-sm: var(--shadow-sm, 0 1px 3px rgba(0, 0, 0, 0.45));
    }
}

/* ------------------------------------------------------------------ */
/*  Block wrapper                                                      */
/* ------------------------------------------------------------------ */

.resource-vis-block {
    margin: var(--rv-space-8) 0;
}

.resource-vis-block h2 {
    /* Block-level section titles. Inherit the host theme's global h2 size/weight
       (DRE theme: 1.875rem, fluid to 2.75rem from >=768px, weight 800) so the
       module's blocks read as proper page sections under the page/item <h1> and
       match sibling sections such as the theme's "Linked resources" disclosure.
       Visualisation titles inside a block are <h3>, so they step cleanly down. */
    color: var(--rv-heading-color);
    margin: 0 0 var(--rv-space-3);
}

/* ------------------------------------------------------------------ */
/*  Collapsible section                                                 */
/*                                                                      */
/*  Mirrors the DRE theme's "Linked resources" disclosure: a native     */
/*  <details> with a summary head (heading + chevron) over a panel.     */
/*  Used by the Knowledge Graph block and the async dashboards so the    */
/*  heavy visualizations can be collapsed. Expanded by default; charts   */
/*  re-fit on re-expand (see the `toggle` handler in dashboard-core.js). */
/* ------------------------------------------------------------------ */

details.rv-collapsible {
    display: block;
}

/* Summary head — heading on the left, a chevron that flips on the right.
   The whole bar toggles the panel. */
.rv-collapsible__head {
    list-style: none;               /* strip the native disclosure triangle… */
    display: flex;                  /* …and flex display drops the list marker */
    align-items: center;
    gap: var(--rv-space-4);
    margin: 0;
    padding: var(--rv-space-2) 0;
    cursor: pointer;
    border-bottom: 1px solid var(--rv-border);
}

.rv-collapsible__head::-webkit-details-marker {
    display: none;
}

.rv-collapsible__head:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
    border-radius: var(--rv-radius-sm);
}

/* Reuse the block heading style; just neutralise its margin in the flex head. */
.rv-collapsible__head h2 {
    margin: 0;
    transition: color var(--rv-transition-fast);
}

.rv-collapsible__head:hover h2 {
    color: var(--rv-accent);
}

/* CSS chevron: points down when closed, flips up when open. */
.rv-collapsible__chevron {
    flex-shrink: 0;
    margin-left: auto;
    width: 0.6rem;
    height: 0.6rem;
    border-right: 2px solid var(--rv-text-color);
    border-bottom: 2px solid var(--rv-text-color);
    transform: translateY(-2px) rotate(45deg);
    transition: transform var(--rv-transition-base), border-color var(--rv-transition-fast);
}

.rv-collapsible__head:hover .rv-collapsible__chevron {
    border-color: var(--rv-accent);
}

details.rv-collapsible[open] > .rv-collapsible__head .rv-collapsible__chevron {
    transform: translateY(1px) rotate(225deg);
}

.rv-collapsible__panel {
    padding-top: var(--rv-space-5);
}

@media (prefers-reduced-motion: reduce) {
    .rv-collapsible__chevron,
    .rv-collapsible__head h2 {
        transition: none;
    }
}

/* ------------------------------------------------------------------ */
/*  Knowledge graph                                                    */
/* ------------------------------------------------------------------ */

.knowledge-graph-block {
    position: relative;
}

.knowledge-graph-toolbar {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 10;
    display: flex;
    gap: var(--rv-space-1);
}

.knowledge-graph-container {
    width: 100%;
    height: 520px;
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    background: var(--rv-bg);
}

/* ------------------------------------------------------------------ */
/*  Force-graph canvas stage (graph-canvas.js / graph-force.js)         */
/* ------------------------------------------------------------------ */

/* The stage is the positioning context for the canvas, the tooltip and the
   live region. `overflow: hidden` keeps the canvas inside the rounded corner. */
.rv-graph-stage {
    position: relative;
    overflow: hidden;
}

/* touch-action: none — the graph owns its gestures (one finger drags a node or
   pans, two fingers pinch), matching the module's MapLibre surfaces. The page is
   still scrollable everywhere outside the canvas. */
/* position: absolute is load-bearing, not cosmetic. resize() measures the stage and
   writes the result back as an inline px height on this canvas; if the canvas were
   in flow, that height would feed the stage's own content height and the two would
   ratchet upward forever under the ResizeObserver — which is exactly what happened
   in fullscreen, where the stage is flex-sized rather than a fixed 520px.
   Out of flow, the canvas can never influence its container's size. */
.rv-graph-canvas {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    touch-action: none;
    cursor: grab;
}

.rv-graph-canvas:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
    border-radius: var(--rv-radius);
}

/* Tooltip: DOM, not canvas, so it stays selectable and theme-styled. Confined to
   the stage by graph-force.js, which flips it near an edge. */
.rv-graph-tooltip {
    position: absolute;
    z-index: 2;
    display: flex;
    flex-direction: column;
    gap: var(--rv-space-1);
    max-width: 15rem;
    padding: var(--rv-space-2) var(--rv-space-3);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-overlay);
    box-shadow: var(--rv-shadow-sm);
    color: var(--rv-text-strong);
    font-size: var(--rv-text-xs);
    line-height: 1.45;
    pointer-events: none;
}

.rv-graph-tooltip[hidden] {
    display: none;
}

.rv-kg-tip-cat {
    font-weight: 600;
}

.rv-kg-tip-meta {
    color: var(--rv-text-color);
    font-size: var(--rv-text-2xs);
}

/* Detail card for the selected entity. Bottom-left, so it never sits under the
   toolbar (top-right); pointer-events stay on so its link is clickable, unlike the
   tooltip above. Capped so it can never cover the whole graph on a phone. */
.rv-kg-card {
    position: absolute;
    z-index: 3;
    bottom: var(--rv-space-3);
    left: var(--rv-space-3);
    width: max-content;
    max-width: min(20rem, calc(100% - 2 * var(--rv-space-3)));
    max-height: calc(100% - 2 * var(--rv-space-3));
    overflow-y: auto;
    padding: var(--rv-space-3) var(--rv-space-4);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-overlay);
    box-shadow: var(--rv-shadow);
}

.rv-kg-card[hidden] {
    display: none;
}

.rv-kg-card-close {
    position: absolute;
    top: var(--rv-space-1);
    right: var(--rv-space-1);
}

.rv-kg-card-title {
    margin: 0 var(--rv-space-6) var(--rv-space-1) 0;
    color: var(--rv-heading-color);
    font-size: var(--rv-text-sm);
    line-height: 1.3;
}

.rv-kg-card-type {
    margin: 0 0 var(--rv-space-2);
    font-size: var(--rv-text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.rv-kg-card-meta {
    margin: 0;
    padding: 0;
    list-style: none;
    color: var(--rv-text-color);
    font-size: var(--rv-text-2xs);
}

.rv-kg-card-rels {
    margin: var(--rv-space-2) 0 0;
    color: var(--rv-text-color);
    font-size: var(--rv-text-2xs);
}

/* The one way out of the graph to the record — deliberately a real link. */
.rv-kg-card-link {
    display: inline-block;
    margin-top: var(--rv-space-3);
    font-size: var(--rv-text-xs);
    font-weight: 600;
}

.rv-kg-card-link[hidden] {
    display: none;
}

/* Screen-reader live region announcing the keyboard-focused node. Visually
   hidden rather than display:none, which would stop it being announced. */
.rv-graph-status {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ------------------------------------------------------------------ */
/*  Knowledge-graph legend, hint and text alternative                  */
/* ------------------------------------------------------------------ */

/* Below the stage, never over it — the same rule the module's map legends
   follow, so a legend can never cover the data it describes. */
.rv-kg-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--rv-space-1) var(--rv-space-3);
    margin-top: var(--rv-space-3);
}

.rv-kg-legend-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-2);
    padding: var(--rv-space-1) var(--rv-space-2);
    border: 1px solid transparent;
    border-radius: var(--rv-radius-full);
    background: none;
    color: var(--rv-text-strong);
    font-size: var(--rv-text-xs);
    cursor: pointer;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast);
}

.rv-kg-legend-chip:hover {
    background: var(--rv-bg-sunken);
}

.rv-kg-legend-chip:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
    border-color: var(--rv-accent);
}

.rv-kg-legend-off {
    color: var(--rv-text-color);
    opacity: 0.55;
    text-decoration: line-through;
}

.rv-kg-legend-swatch {
    width: 0.625em;
    height: 0.625em;
    border-radius: var(--rv-radius-full);
    flex: none;
}

.rv-kg-hint {
    margin: var(--rv-space-2) 0 0;
    color: var(--rv-text-color);
    font-size: var(--rv-text-2xs);
}

.rv-kg-list {
    margin-top: var(--rv-space-2);
    font-size: var(--rv-text-xs);
}

.rv-kg-list > summary {
    color: var(--rv-text-color);
    cursor: pointer;
}

.rv-kg-list > summary:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
    border-radius: var(--rv-radius-sm);
}

.rv-kg-list-head {
    margin: var(--rv-space-3) 0 var(--rv-space-1);
    color: var(--rv-heading-color);
    font-size: var(--rv-text-xs);
}

.rv-kg-list-items {
    margin: 0;
    padding-left: var(--rv-space-5);
    columns: 16rem 2;
}

.rv-kg-list-items > li {
    break-inside: avoid;
}

.rv-kg-list-meta {
    color: var(--rv-text-color);
}

/* Fullscreen mode.
   z-index scale used across the module: 1–2 in-panel layering; 10/20/30
   toolbars → filter panels → combobox lists; 1000 modals (photo lightbox,
   issue TOC); 9999 fullscreen — deliberately above everything, including any
   sticky theme chrome, since fullscreen must own the whole viewport. */
.knowledge-graph-block.rv-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 9999;
    margin: 0;
    padding: var(--rv-space-4);
    background: var(--rv-bg);
}

/* Fullscreen lays the PANEL out as a column so the stage takes whatever the legend
   and hint leave — they can wrap on a narrow window without cropping the graph.
   The definite height goes on the panel, NOT via flex from the <details> block:
   modern Chromium wraps a <details> element's content in an internal
   ::details-content box, so `display: flex` on the block never reaches the panel
   and the stage would collapse to its min-height. 2rem = the block's own padding. */
.knowledge-graph-block.rv-fullscreen .rv-collapsible__panel {
    display: flex;
    flex-direction: column;
    height: calc(100vh - 2rem);
    min-height: 0;
}

/* flex-basis 0, not auto: the stage takes the space the flex line has left over
   instead of being sized by its content. Combined with the out-of-flow canvas above
   this makes the height flow strictly one way — container decides, canvas follows —
   so there is no cycle for the ResizeObserver to amplify. */
.knowledge-graph-block.rv-fullscreen .knowledge-graph-container {
    flex: 1 1 0;
    height: auto;
    min-height: 8rem;
}

.knowledge-graph-block.rv-fullscreen .knowledge-graph-toolbar {
    top: 1rem;
    right: 1rem;
}

/* Fullscreen is for the graph. The location map and the text alternative are
   reading aids for the page, and squeezing them into the viewport would only take
   height away from the thing the reader just asked to see. */
.knowledge-graph-block.rv-fullscreen .rv-item-map-panel,
.knowledge-graph-block.rv-fullscreen .rv-kg-list {
    display: none;
}

/* Collapsible knowledge graph — the summary head sits at the top, so the
   floating toolbar is positioned within the panel (over the graph's corner),
   and fullscreen hides the head so the graph fills the screen. */
.knowledge-graph-block .rv-collapsible__panel {
    position: relative;
}

.knowledge-graph-block .knowledge-graph-toolbar {
    top: 1.6rem;
    right: 0.5rem;
}

.knowledge-graph-block.rv-fullscreen > .rv-collapsible__head {
    display: none;
}

.knowledge-graph-block.rv-fullscreen .rv-collapsible__panel {
    padding-top: 0;
}

/* ------------------------------------------------------------------ */
/*  Knowledge-graph filter panel                                       */
/* ------------------------------------------------------------------ */

.rv-kg-filters {
    position: relative;
}

.rv-kg-filters-toggle.rv-btn-active {
    background: var(--rv-accent);
    color: var(--rv-accent-contrast);
    border-color: var(--rv-accent);
}

.rv-kg-filters-panel {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: var(--rv-space-2);
    padding: var(--rv-space-3) var(--rv-space-4);
    width: 300px;
    background: var(--rv-bg);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    box-shadow: var(--rv-shadow);
    z-index: 20;
}

/* Each slider block: label row on top, slider row below, description last. */
.rv-kg-slider {
    margin-bottom: var(--rv-space-3);
    padding-bottom: var(--rv-space-3);
    border-bottom: 1px solid var(--rv-border);
}

.rv-kg-slider:last-child {
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: none;
}

/* Label sits on its own line above the slider. */
.rv-kg-slider label {
    display: block;
    font-size: var(--rv-text-xs);
    color: var(--rv-heading-color);
    width: 100%;
}

.rv-kg-slider-label {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: var(--rv-space-1);
    font-weight: 600;
}

/* Slider sits on its own row, full width. */
.rv-kg-slider input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    display: block;
    width: 100%;
    height: 6px;
    margin: var(--rv-space-1) 0 0;
    padding: 0;
    border: none;
    border-radius: var(--rv-radius-sm);
    background: var(--rv-border);
    outline: none;
    cursor: pointer;
}

.rv-kg-slider input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--rv-accent);
    border: 2px solid var(--rv-bg);
    box-shadow: var(--rv-shadow-sm);
    cursor: pointer;
}

.rv-kg-slider input[type="range"]::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--rv-accent);
    border: 2px solid var(--rv-bg);
    box-shadow: var(--rv-shadow-sm);
    cursor: pointer;
}

.rv-kg-slider input[type="range"]::-moz-range-track {
    height: 6px;
    border-radius: var(--rv-radius-sm);
    background: var(--rv-border);
}

.rv-kg-slider-value {
    font-size: var(--rv-text-xs);
    font-variant-numeric: tabular-nums;
    color: var(--rv-accent);
    font-weight: 700;
}

.rv-kg-slider-desc {
    display: block;
    font-size: var(--rv-text-2xs);
    color: var(--rv-text-color);
    margin-top: var(--rv-space-1);
    line-height: 1.35;
}

/* Reset action at the foot of the knowledge-graph filter panel. A text button
 * (the icon-only .rv-btn is the wrong shape for a label), styled to match. */
.rv-kg-filters-actions {
    margin-top: var(--rv-space-2);
    display: flex;
    justify-content: flex-end;
}
.rv-kg-filters-reset {
    padding: var(--rv-space-1) var(--rv-space-3);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
    cursor: pointer;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast), border-color var(--rv-transition-fast);
}
.rv-kg-filters-reset:hover { background: var(--rv-bg-raised); color: var(--rv-accent); border-color: var(--rv-accent); }
.rv-kg-filters-reset:focus-visible { outline: none; box-shadow: var(--rv-focus-ring); border-color: var(--rv-accent); }
.rv-kg-filters-reset:disabled { opacity: 0.45; cursor: default; }
.rv-kg-filters-reset:disabled:hover { background: var(--rv-bg); color: var(--rv-text-color); border-color: var(--rv-border); }

/* ------------------------------------------------------------------ */
/*  Item location map                                                  */
/* ------------------------------------------------------------------ */

.rv-item-map-panel {
    margin-top: var(--rv-space-5);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    padding: var(--rv-space-4);
    background: var(--rv-bg);
}

.rv-item-map-panel h3 {
    font-size: var(--rv-text-sm);
    font-weight: 600;
    color: var(--rv-heading-color);
    margin: 0 0 var(--rv-space-2);
}

.rv-item-map-legend {
    display: flex;
    gap: var(--rv-space-4);
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
    margin-bottom: var(--rv-space-2);
}

.rv-legend-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    margin-right: var(--rv-space-1);
    vertical-align: middle;
}

.rv-item-map-container {
    width: 100%;
    height: 300px;
    border-radius: var(--rv-radius-sm);
}

/* ------------------------------------------------------------------ */
/*  Dashboard                                                          */
/* ------------------------------------------------------------------ */

.dashboard-header {
    display: flex;
    align-items: baseline;
    gap: var(--rv-space-4);
    margin-bottom: var(--rv-space-4);
}

.dashboard-header h2 {
    /* Inherit the host theme's global h2 scale -- see .resource-vis-block h2. */
    margin: 0;
    color: var(--rv-heading-color);
}

/* Inline count/meta badge used by the Discursive Communities, What's New and
   item-page dashboard headers (the Collection overview header no longer shows
   one). */
.dashboard-total {
    font-size: var(--rv-text-sm);
    color: var(--rv-text-color);
    background: var(--rv-bg);
    padding: 0.15em 0.6em;
    border-radius: var(--rv-radius-full);
    border: 1px solid var(--rv-border);
    font-variant-numeric: tabular-nums;
}

.dashboard-charts {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--rv-space-5);
}

.chart-panel {
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    padding: var(--rv-space-4);
    background: var(--rv-bg);
    overflow: hidden;
    min-width: 0;
}

.chart-panel-wide {
    grid-column: 1 / -1;
}

.chart-panel h3 {
    display: flex;
    align-items: center;
    gap: var(--rv-space-2);
    font-size: var(--rv-text-sm);
    font-weight: 600;
    color: var(--rv-heading-color);
    margin: 0 0 var(--rv-space-2);
    overflow-wrap: break-word;
    word-break: break-word;
}

/* Chart-level toolbar (save button, inline with title). */
.rv-chart-toolbar {
    margin-left: auto;
    display: flex;
    gap: var(--rv-space-1);
    flex-shrink: 0;
}

/* Dashboard toolbar buttons are slightly smaller than knowledge graph ones. */
.rv-toolbar-btn {
    width: 1.6rem;
    height: 1.6rem;
}

.dashboard-header h2,
.resource-vis-block h2 {
    overflow-wrap: break-word;
    word-break: break-word;
}

.chart-description {
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
    margin: 0 0 var(--rv-space-2);
    line-height: 1.4;
}

/* Word-count slider for wordcloud panels. */
.rv-word-slider {
    display: flex;
    align-items: center;
    margin: 0 0 var(--rv-space-2);
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
}

.rv-word-slider label {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-2);
}

.rv-word-slider-caption {
    color: var(--rv-text-color);
    white-space: nowrap;
}

.rv-word-slider input[type="range"] {
    /* A compact, fixed-width control — the slider is not meant to span the whole
       panel (a full-width track reads as a dead gap on the right). */
    width: 220px;
    max-width: 100%;
    height: 4px;
    accent-color: var(--rv-accent);
    cursor: pointer;
}

.rv-word-slider-value {
    min-width: 3ch;
    text-align: left;
    font-variant-numeric: tabular-nums;
}

/* Word-cloud language toggle — segmented buttons above the cloud (shown only
   when the precomputed input carries more than one language). */
.rv-word-langs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--rv-space-1);
    margin: 0 0 var(--rv-space-2);
}

.rv-word-lang {
    padding: 2px var(--rv-space-2);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
    font-weight: 600;
    cursor: pointer;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast), border-color var(--rv-transition-fast);
}

.rv-word-lang:hover {
    background: var(--rv-bg-raised);
    color: var(--rv-accent);
    border-color: var(--rv-accent);
}

.rv-word-lang.is-active {
    background: var(--rv-accent);
    color: var(--rv-accent-contrast);
    border-color: var(--rv-accent);
}

.rv-word-lang:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
}

.chart-container {
    width: 100%;
    max-width: 100%;
    height: 350px;
}

.chart-container-tall {
    height: 420px;
}

/* ------------------------------------------------------------------ */
/*  Single-visualization embed (/dre-embed/<block>/<viz>)              */
/* ------------------------------------------------------------------ */

/* The bare embed route renders one chart full-bleed: drop the 2-column grid and
   the panel chrome so the chart meets the frame edges, and give it room. The
   iframe auto-resizes to this height via the embed layout's postMessage
   reporter, so a stable pixel height (not vh) is what the host reads. */
.dre-embed-single {
    margin: 0;
}

.dre-embed-single .dashboard-charts--single {
    display: block;
    gap: 0;
}

.dre-embed-single .chart-panel {
    border: 0;
    border-radius: 0;
    padding: var(--rv-space-3);
    background: transparent;
}

.dre-embed-single .chart-container,
.dre-embed-single .chart-container-tall {
    height: 480px;
}

/* Quiet fallback when a single-chart embed has no data for that key. */
.rv-embed-empty {
    margin: 0;
    padding: var(--rv-space-8) var(--rv-space-4);
    text-align: center;
    color: var(--rv-text-color);
    font-size: var(--rv-text-sm);
}

/* Copy-embed-code buttons. The icon-only form rides the .rv-toolbar-btn skin in
   the chart toolbars; the labelled form is the single-widget block's button.
   Double-class selector so `width: auto` outranks the `button.rv-toolbar-btn`
   reset below (an element+class selector that would otherwise pin it to 2rem and
   clip the "Embed" label). */
.rv-embed-btn.rv-embed-btn--labeled {
    width: auto;
    gap: var(--rv-space-1);
    padding: 0 var(--rv-space-2);
    font-size: var(--rv-text-xs);
    font-weight: 600;
}

/* Right-aligned strip holding a single-widget block's whole-block embed button.
   Mounted on the outer .resource-vis-block wrapper, above the visualization, so
   the block's own controller (which rebuilds the inner container) never wipes it
   and it never overlaps a map's own controls. */
.rv-embed-block-toolbar {
    display: flex;
    justify-content: flex-end;
    margin-bottom: var(--rv-space-2);
}

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

/* Override a host theme's bare-button skin (e.g. Lively sets padding/border-radius/bg
   on all <button>s). Double class selector for specificity over a theme's `button`
   rule. (The DRE theme only skins `.button` as of theme v2.13.0 — which these
   controls don't use — so under DRE this is a harmless no-op.) */
button.rv-btn,
button.rv-toolbar-btn,
.rv-btn,
.rv-toolbar-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    min-width: 0;
    padding: 0;
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    cursor: pointer;
    font-size: var(--rv-text-base);
    color: var(--rv-text-color);
    line-height: 1;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast), border-color var(--rv-transition-fast);
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    text-decoration: none;
}

button.rv-btn:hover,
button.rv-toolbar-btn:hover,
.rv-btn:hover,
.rv-toolbar-btn:hover {
    background: var(--rv-bg-raised);
    color: var(--rv-accent);
    border-color: var(--rv-accent);
}

button.rv-btn:focus-visible,
button.rv-toolbar-btn:focus-visible,
.rv-btn:focus-visible,
.rv-toolbar-btn:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
    border-color: var(--rv-accent);
}

.rv-toolbar-btn-active {
    background: var(--rv-accent) !important;
    color: var(--rv-accent-contrast) !important;
    border-color: var(--rv-accent) !important;
}

/* Coarse-pointer (touch) tap targets — the dense chart + knowledge-graph toolbar
   icon buttons stay visually compact (26–32px) but gain a centred ≥44px invisible
   hit area (the theme's --size-control-lg WCAG ambition) so they are comfortable
   to tap. Fine-pointer (mouse) layout is unchanged. (F4) */
@media (pointer: coarse) {
    .rv-btn,
    .rv-toolbar-btn {
        position: relative;
    }

    .rv-btn::before,
    .rv-toolbar-btn::before {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        width: max(100%, var(--size-control-lg, 2.75rem));
        height: max(100%, var(--size-control-lg, 2.75rem));
        transform: translate(-50%, -50%);
    }
}

/* ------------------------------------------------------------------ */
/*  Loading / error states                                             */
/* ------------------------------------------------------------------ */

/* Loading state — a token-driven skeleton, not a bare "Loading…" line. The
   container is a --rv-bg-sunken block at the chart's reserved height with a slow
   shimmer sweep; the spinner + label sit centred on top. One grammar for every
   chart block (dashboards, knowledge graph, maps, compare, …) so the whole
   module loads with one composed voice. Shimmer + spin are both suppressed
   under prefers-reduced-motion (see the media query below). */
.rv-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    min-height: 200px;              /* reserve an area when the parent sets no height */
    gap: var(--rv-space-3);
    color: var(--rv-text-color);
    font-size: var(--rv-text-sm);
    border-radius: var(--rv-radius);
    background-color: var(--rv-bg-sunken);
    background-image: linear-gradient(100deg,
        transparent 30%,
        var(--rv-bg) 50%,
        transparent 70%);
    background-size: 200% 100%;
    background-repeat: no-repeat;
    animation: rv-shimmer 1.6s ease-in-out infinite;
}

.rv-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid var(--rv-border);
    border-top-color: var(--rv-accent);
    border-radius: 50%;
    animation: rv-spin 0.8s linear infinite;
}

@keyframes rv-spin {
    to { transform: rotate(360deg); }
}

@keyframes rv-shimmer {
    from { background-position: 150% 0; }
    to   { background-position: -50% 0; }
}

/* Empty + error states: a quiet composed panel (not bare centred text), on the
   same sunken surface and reserved height as the skeleton — no shimmer, since
   nothing is loading. */
.rv-no-data,
.rv-error {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    min-height: 200px;
    margin: 0;
    padding: var(--rv-space-6);
    text-align: center;
    color: var(--rv-text-color);
    font-size: var(--rv-text-sm);
    background-color: var(--rv-bg-sunken);
    border-radius: var(--rv-radius);
}

/* Honour reduced-motion: no shimmer sweep, no spin — static sunken panels. */
@media (prefers-reduced-motion: reduce) {
    .rv-loading {
        animation: none;
        background-image: none;
    }

    .rv-spinner {
        animation: none;
    }
}

/* ------------------------------------------------------------------ */
/*  Map attribution                                                    */
/* ------------------------------------------------------------------ */

.chart-panel .maplibregl-ctrl-attrib:not(.maplibregl-compact-show) {
    background: none;
    padding: 0;
}

.chart-panel .maplibregl-ctrl-attrib:not(.maplibregl-compact-show) a,
.chart-panel .maplibregl-ctrl-attrib:not(.maplibregl-compact-show) span {
    display: none;
}

/* ------------------------------------------------------------------ */
/*  Map popups                                                         */
/* ------------------------------------------------------------------ */

/* Theme the MapLibre popup surface so it matches light / dark like the rest
   of the module (MapLibre's stylesheet defaults to a white card). */
.resource-vis-block .maplibregl-popup-content,
.rv-item-map-panel .maplibregl-popup-content {
    background: var(--rv-bg);
    color: var(--rv-text-strong);
    box-shadow: var(--rv-shadow);
}

.resource-vis-block .maplibregl-popup-anchor-top .maplibregl-popup-tip,
.rv-item-map-panel .maplibregl-popup-anchor-top .maplibregl-popup-tip {
    border-bottom-color: var(--rv-bg);
}

.resource-vis-block .maplibregl-popup-anchor-bottom .maplibregl-popup-tip,
.rv-item-map-panel .maplibregl-popup-anchor-bottom .maplibregl-popup-tip {
    border-top-color: var(--rv-bg);
}

.resource-vis-block .maplibregl-popup-anchor-left .maplibregl-popup-tip,
.rv-item-map-panel .maplibregl-popup-anchor-left .maplibregl-popup-tip {
    border-right-color: var(--rv-bg);
}

.resource-vis-block .maplibregl-popup-anchor-right .maplibregl-popup-tip,
.rv-item-map-panel .maplibregl-popup-anchor-right .maplibregl-popup-tip {
    border-left-color: var(--rv-bg);
}

/* Consistent close button for ALL MapLibre popups in the module. */
.resource-vis-block .maplibregl-popup-close-button,
.rv-item-map-panel .maplibregl-popup-close-button {
    font-size: var(--rv-text-lg);
    font-weight: bold;
    width: 24px;
    height: 24px;
    line-height: 24px;
    padding: 0;
    color: var(--rv-text-color);
    background: transparent;
    border: none;
    border-radius: var(--rv-radius-sm);
}

.resource-vis-block .maplibregl-popup-close-button:hover,
.rv-item-map-panel .maplibregl-popup-close-button:hover {
    color: var(--rv-heading-color);
    background: var(--rv-bg-sunken);
}

/* Map legend — rendered BELOW the map (appended to the .chart-panel by
   ns.mountMapLegend), never overlaid on the basemap, so it can't cover
   countries, markers or their labels. The cluster-partner map's toggleable
   legend (.rv-cluster-legend) uses the same below-the-map placement. */
.rv-map-legend {
    display: inline-flex;
    flex-direction: column;
    gap: 2px;
    margin-top: var(--rv-space-2);
    max-width: 100%;
    background: var(--rv-bg-sunken);
    color: var(--rv-text-strong);
    border-radius: var(--rv-radius-sm);
    padding: 6px 10px;
    font-size: var(--rv-text-xs);
    line-height: 1.6;
    font-variant-numeric: tabular-nums;   /* choropleth min/max ramp labels align */
}

.rv-map-legend-row {
    display: flex;
    align-items: center;
    gap: 6px;
}

.rv-map-legend-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 1px solid var(--rv-border-strong);
    flex-shrink: 0;
}

.rv-map-legend-line {
    width: 16px;
    height: 2px;
    flex-shrink: 0;
    opacity: 0.6;
}

/* Choropleth legend: a flush row of ramp swatches between min/max labels. */
.rv-map-legend-swatch {
    display: inline-block;
    width: 16px;
    height: 10px;
    flex-shrink: 0;
}

.rv-choropleth-legend .rv-map-legend-row {
    gap: 0;
}

.rv-choropleth-legend .rv-map-legend-row > span:first-child,
.rv-choropleth-legend .rv-map-legend-row > span:last-child {
    margin: 0 4px;
}

.rv-map-legend-caption {
    margin-top: 2px;
    color: var(--rv-text-color);
    opacity: 0.75;
}

/* Co-author network relationship key — the colour legend for the edges, mounted
   below the graph (in the .chart-panel) like the map legends. Reuses
   .rv-map-legend-row / .rv-map-legend-line for each swatch, but lays them out in
   one horizontal, wrapping row. */
.rv-edge-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 16px;
    margin-top: var(--rv-space-2);
    padding: 6px 10px;
    background: var(--rv-bg-sunken);
    color: var(--rv-text-strong);
    border-radius: var(--rv-radius-sm);
    font-size: var(--rv-text-xs);
    line-height: 1.6;
}

.rv-edge-legend .rv-map-legend-line {
    width: 18px;
    height: 3px;
    opacity: 1;
}

.rv-map-popup {
    max-width: 320px !important;
}

.rv-map-popup .maplibregl-popup-content {
    max-height: 350px;
    overflow-y: auto;
}

.rv-popup-content {
    font-size: var(--rv-text-xs);
    line-height: 1.4;
    max-width: 300px;
}

.rv-popup-content strong {
    font-size: var(--rv-text-sm);
}

.rv-popup-count {
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
    font-variant-numeric: tabular-nums;
}

.rv-popup-sub {
    margin-top: 2px;
    color: var(--rv-text-color);
    opacity: 0.8;
    font-size: var(--rv-text-xs);
}

/* Cluster-partner map (Collection Overview): colour-coded dot markers with a
   toggleable legend. Colours are set inline per category by the builder
   (dashboard-charts-cluster-map.js); shape/stroke live here. */
.rv-cluster-marker {
    border-radius: 50%;
    border: 2px solid var(--rv-border);
    box-shadow: var(--rv-shadow-sm);
    cursor: pointer;
    transition: transform var(--rv-transition-fast);
}

.rv-cluster-marker:hover {
    transform: scale(1.15);
}

.rv-cluster-legend {
    /* Sits below the map (within the panel) so it never covers markers. */
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 4px 14px;
    margin-top: var(--rv-space-2);
    padding: 6px 12px;
}

.rv-cluster-legend__item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 4px;
    border: 0;
    background: none;
    color: var(--rv-text-strong);
    font: inherit;
    font-size: var(--rv-text-xs);
    line-height: 1.4;
    cursor: pointer;
    border-radius: var(--rv-radius-sm);
    transition: opacity var(--rv-transition-fast), color var(--rv-transition-fast);
}

.rv-cluster-legend__item:hover {
    color: var(--rv-accent);
}

.rv-cluster-legend__item:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
}

.rv-cluster-legend__item.is-off {
    opacity: 0.45;
}

.rv-cluster-legend__item.is-off .rv-cluster-legend__label {
    text-decoration: line-through;
}

.rv-cluster-legend__dot {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    border: 1px solid var(--rv-border-strong);
    flex-shrink: 0;
}

.rv-popup-items {
    list-style: none;
    margin: var(--rv-space-2) 0;
    padding: 0;
    max-height: 220px;
    overflow-y: auto;
}

.rv-popup-items li {
    padding: var(--rv-space-1) 0;
    border-bottom: 1px solid var(--rv-border-light);
}

.rv-popup-items li:last-child {
    border-bottom: none;
}

.rv-popup-items a {
    color: var(--rv-accent);
    text-decoration: none;
    font-size: var(--rv-text-xs);
}

.rv-popup-items a:hover {
    text-decoration: underline;
}

.rv-popup-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--rv-space-2);
    margin: var(--rv-space-2) 0;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
}

.rv-popup-pagination button {
    background: var(--rv-bg-raised);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    cursor: pointer;
    padding: var(--rv-space-1) var(--rv-space-2);
    font-size: var(--rv-text-sm);
    line-height: 1;
    min-width: 28px;
    color: var(--rv-text-strong);
}

.rv-popup-pagination button:hover {
    background: var(--rv-bg-sunken);
}

.rv-popup-location-link {
    display: block;
    margin-top: var(--rv-space-1);
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
    text-decoration: none;
}

.rv-popup-location-link:hover {
    color: var(--rv-accent);
}

/* ------------------------------------------------------------------ */
/*  Compare Projects                                                   */
/* ------------------------------------------------------------------ */

.compare-selectors {
    display: flex;
    align-items: flex-end;
    gap: var(--rv-space-4);
    margin-bottom: var(--rv-space-6);
    flex-wrap: wrap;
}

.compare-selector {
    flex: 1;
    min-width: 250px;
}

.compare-selector-label {
    display: block;
    font-size: var(--rv-text-xs);
    font-weight: 600;
    color: var(--rv-heading-color);
    margin-bottom: var(--rv-space-1);
}

.compare-select {
    width: 100%;
    padding: var(--rv-space-2) var(--rv-space-3);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    background: var(--rv-bg);
    color: var(--rv-heading-color);
    font-size: var(--rv-text-sm);
    cursor: pointer;
    appearance: auto;
}

.compare-select:focus {
    outline: none;
    border-color: var(--rv-accent);
    box-shadow: var(--rv-focus-ring);
}

.compare-vs {
    font-size: var(--rv-text-sm);
    font-weight: 600;
    color: var(--rv-text-color);
    flex-shrink: 0;
    align-self: flex-end;
    padding-bottom: var(--rv-space-2);
}

/* Searchable entity picker (Compare) — an ARIA combobox replacing the native
   <select>: type to filter the index, arrow keys to navigate, Enter to choose. */
.rv-combobox {
    position: relative;
}

.rv-combobox-input {
    width: 100%;
    padding: var(--rv-space-2) var(--rv-space-3);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    background: var(--rv-bg);
    color: var(--rv-heading-color);
    font-size: var(--rv-text-sm);
    font-family: inherit;
}

.rv-combobox-input::placeholder {
    color: var(--rv-text-color);
    opacity: 0.85;
}

.rv-combobox-input:focus {
    outline: none;
    border-color: var(--rv-accent);
    box-shadow: var(--rv-focus-ring);
}

.rv-combobox-list {
    position: absolute;
    z-index: 30;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    margin: 0;
    padding: var(--rv-space-1) 0;
    list-style: none;
    max-height: 18rem;
    overflow-y: auto;
    background: var(--rv-bg-raised);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    box-shadow: var(--rv-shadow);
}

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

.rv-combobox-option {
    padding: var(--rv-space-2) var(--rv-space-3);
    font-size: var(--rv-text-xs);
    color: var(--rv-text-strong);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rv-combobox-option.is-active,
.rv-combobox-option:hover {
    background: var(--rv-bg-sunken);
    color: var(--rv-heading-color);
}

.rv-combobox-option[aria-selected="true"] {
    font-weight: 600;
    color: var(--rv-accent);
}

.rv-combobox-group {
    position: sticky;
    top: 0;
    padding: var(--rv-space-2) var(--rv-space-3) var(--rv-space-1);
    font-size: var(--rv-text-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--rv-text-color);
    background: var(--rv-bg-raised);
}

.rv-combobox-empty,
.rv-combobox-more {
    padding: var(--rv-space-2) var(--rv-space-3);
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
    font-style: italic;
}

/* Compare: in-page entity-type switcher (Compare any-entity block) — a row of
   icon + label pills; the active type is a filled accent pill. */
.compare-type-switcher {
    display: flex;
    flex-wrap: wrap;
    gap: var(--rv-space-2);
    margin-bottom: var(--rv-space-5);
}

.compare-type-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-2);
    padding: var(--rv-space-2) var(--rv-space-3);
    font-size: var(--rv-text-xs);
    font-weight: 500;
    line-height: 1.2;
    color: var(--rv-text-strong);
    background: var(--rv-bg);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    cursor: pointer;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast), border-color var(--rv-transition-fast);
    appearance: none;
    -webkit-appearance: none;
}

.compare-type-btn:hover {
    background: var(--rv-bg-raised);
    border-color: var(--rv-border-strong);
    color: var(--rv-heading-color);
}

.compare-type-btn:disabled {
    cursor: not-allowed;
    opacity: 0.45;
}

.compare-type-btn:disabled:hover {
    background: var(--rv-bg);
    border-color: var(--rv-border);
    color: var(--rv-text-strong);
}

.compare-type-btn:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
}

.compare-type-btn.is-active {
    background: var(--rv-accent);
    border-color: var(--rv-accent);
    color: var(--rv-accent-contrast);
}

.compare-type-icon {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
}

/* Compare: overlaid A/B radar headline */
.compare-radar-row {
    display: flex;
    justify-content: center;
    margin-bottom: var(--rv-space-6);
}

.compare-radar-panel {
    max-width: 560px;
    width: 100%;
}

/* Project Explorer */
.explorer-controls {
    margin-bottom: var(--rv-space-6);
}

.explorer-selector {
    max-width: 480px;
}

.explorer-selector-label {
    display: block;
    font-size: var(--rv-text-xs);
    font-weight: 600;
    color: var(--rv-heading-color);
    margin-bottom: var(--rv-space-1);
}

.explorer-content {
    min-height: 120px;
}

/* Collapsible project abstract (dcterms:abstract), between the selector and the
   dashboard. Empty until a project with an abstract is selected — :not(:empty)
   keeps it from adding a gap when there is nothing to show. */
.explorer-abstract:not(:empty) {
    margin-bottom: var(--rv-space-6);
}

.explorer-abstract-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-1);
    padding: var(--rv-space-1) var(--rv-space-3);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    color: var(--rv-text-color);
    font-size: var(--rv-text-sm);
    font-weight: 600;
    cursor: pointer;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast), border-color var(--rv-transition-fast);
}

.explorer-abstract-toggle:hover {
    background: var(--rv-bg-raised);
    color: var(--rv-accent);
    border-color: var(--rv-accent);
}

.explorer-abstract-toggle:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
}

.explorer-abstract-body[hidden] {
    display: none;
}

.explorer-abstract-body {
    margin-top: var(--rv-space-3);
    max-width: 70ch;
    font-size: var(--rv-text-sm);
    line-height: 1.65;
    color: var(--rv-text-color);
}

.explorer-abstract-body p {
    margin: 0 0 var(--rv-space-3);
}

.explorer-abstract-body p:last-child {
    margin-bottom: 0;
}

/* What's New */
.whats-new-windows {
    margin-bottom: var(--rv-space-5);
}

.whats-new-heading {
    /* A sub-section divider (the new-items count) sitting beside the chart
       panel inside the What's New block. Now an <h3> for outline correctness,
       but pinned small so it doesn't inherit the theme's full h3 scale. */
    margin: var(--rv-space-6) 0 var(--rv-space-3);
    color: var(--rv-heading-color);
    font-size: var(--rv-text-lg);
}

.whats-new-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--rv-space-2);
}

.whats-new-card {
    display: flex;
    flex-direction: column;
    gap: var(--rv-space-1);
    padding: var(--rv-space-2) var(--rv-space-3);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg-raised);
    text-decoration: none;
    transition: border-color var(--rv-transition-fast), box-shadow var(--rv-transition-fast);
}

.whats-new-card:hover {
    border-color: var(--rv-accent);
    box-shadow: var(--rv-shadow-sm);
}

.whats-new-card-title {
    color: var(--rv-heading-color);
    font-weight: 600;
    font-size: var(--rv-text-sm);
    line-height: 1.3;
}

.whats-new-card-date {
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
    font-variant-numeric: tabular-nums;
}

/* Sibling-items sparkline (item pages) */
.sibling-sparkline-block {
    margin: var(--rv-space-4) 0;
}

.sibling-sparkline-head h2 {
    /* A minor resource-page block: keep the heading at the page outline's h2
       level (sibling to the other item-page blocks) but pin it small so the
       compact sparkline caption doesn't inherit the theme's full h2 scale. */
    margin: 0 0 var(--rv-space-2);
    color: var(--rv-heading-color);
    font-size: var(--rv-text-sm);
}

.sibling-sparkline-link {
    cursor: pointer;
}

.sibling-sparkline-link:hover {
    color: var(--rv-accent);
}

.sibling-sparkline-chart {
    height: 150px;
    width: 100%;
}

/* Stats cards */
.compare-stats {
    display: grid !important;
    grid-template-columns: 1fr 1fr 1fr;
    gap: var(--rv-space-4);
    margin-bottom: var(--rv-space-5);
    align-items: stretch;
}

.compare-stat-card {
    flex: 1;
    min-width: 120px;
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    padding: var(--rv-space-3) var(--rv-space-4);
    background: var(--rv-bg);
    text-align: center;
}

.compare-stat-card.compare-stat-accent {
    border-color: var(--rv-accent);
}

.compare-stat-value {
    display: block;
    font-size: var(--rv-text-xl);
    font-weight: 700;
    color: var(--rv-heading-color);
    line-height: 1.2;
    font-variant-numeric: tabular-nums;
}

.compare-stat-accent .compare-stat-value {
    color: var(--rv-accent);
}

.compare-stat-label {
    display: block;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
    margin-top: var(--rv-space-1);
}

.compare-stat-label small {
    font-size: var(--rv-text-xs);
    opacity: 0.8;
}

/* Shared subjects badges */
.compare-shared {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rv-space-2);
    margin-bottom: var(--rv-space-5);
}

.compare-shared-label {
    font-size: var(--rv-text-xs);
    font-weight: 600;
    color: var(--rv-heading-color);
    margin-right: var(--rv-space-1);
}

.compare-badge {
    display: inline-block;
    font-size: var(--rv-text-xs);
    color: var(--rv-heading-color);
    background: var(--rv-bg);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-full);
    padding: 0.15em 0.6em;
}

.compare-badge-muted {
    color: var(--rv-text-color);
    font-style: italic;
}

/* Chart pair rows — always 2 columns for side-by-side comparison */
.compare-chart-row {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: var(--rv-space-4);
    margin-bottom: var(--rv-space-5);
}

.compare-chart-row > .compare-chart-panel {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    width: auto;
}

.compare-chart-panel .chart-container {
    width: 100%;
    max-width: 100%;
}

.compare-content .rv-no-data {
    padding: var(--rv-space-8) 0;
}

/* ------------------------------------------------------------------ */
/*  Collection Overview — summary stat cards (amira-style)             */
/* ------------------------------------------------------------------ */

/* The Collection Overview / Publications blocks lead with the stat-card grid
   directly under the page-title block, so the module's default 2rem top margin
   stacks with the heading and leaves too big a gap. Drop it for those blocks
   only (embedded item-page dashboards have no .rv-stat-cards, so are untouched). */
.resource-vis-block:has(.rv-stat-cards) {
    margin-top: 0;
}

.rv-stat-cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--rv-space-4);
    margin-bottom: var(--rv-space-6);
}

.rv-stat-card {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--rv-space-3);
    padding: var(--rv-space-4) var(--rv-space-5);
    background: var(--rv-bg);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    box-shadow: var(--rv-shadow-sm);
    transition: border-color var(--rv-transition-fast), box-shadow var(--rv-transition-base), transform var(--rv-transition-base);
}

.rv-stat-card:hover {
    border-color: var(--rv-accent);
    box-shadow: var(--rv-shadow);
    transform: translateY(var(--rv-lift-xs));
}

.rv-stat-body {
    min-width: 0;
}

.rv-stat-label {
    margin: 0;
    font-size: var(--rv-text-xs);
    font-weight: 500;
    color: var(--rv-text-color);
}

.rv-stat-value {
    margin: var(--rv-space-1) 0 0;
    font-size: var(--rv-text-2xl);
    font-weight: 700;
    line-height: 1.1;
    color: var(--rv-heading-color);
    font-variant-numeric: tabular-nums;
}

.rv-stat-sub {
    margin: var(--rv-space-1) 0 0;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
}

.rv-stat-badge {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg-sunken);
    color: var(--rv-accent);
}

.rv-stat-icon {
    width: 1.25rem;
    height: 1.25rem;
}

@media (max-width: 768px) {
    .rv-stat-cards {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 420px) {
    .rv-stat-cards {
        grid-template-columns: 1fr;
    }
}

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

@media (max-width: 600px) {
    .dashboard-charts {
        grid-template-columns: 1fr;
    }

    .knowledge-graph-container {
        height: 350px;
    }

    .rv-kg-filters-panel {
        width: 250px;
    }

    .chart-container {
        height: 280px;
    }

    .compare-chart-row {
        grid-template-columns: 1fr;
    }

    .compare-selectors {
        flex-direction: column;
    }

    .compare-vs {
        text-align: center;
        padding: 0;
    }
}

/* ------------------------------------------------------------------ */
/*  Photo Browsing block (masonry / map / timeline + lightbox)         */
/* ------------------------------------------------------------------ */

.rv-empty {
    padding: 24px;
    text-align: center;
    color: var(--rv-text-color);
    font-size: var(--rv-text-sm);
}

.photo-browse-heading {
    margin: 0 0 14px;
    color: var(--rv-heading-color);
}

.photo-browse-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

/* Segmented view switch — a sunken track that holds pill-shaped tabs, with the
   active view floating on an accent pill. These are <button>s, so a host theme
   that skins every <button> (e.g. Lively: fill + lift + glow on :hover) would
   otherwise hijack them; the interactive rules below are scoped through
   .photo-view-toggle (4+ selectors) so they out-rank any such rule and the
   control keeps its own quiet, intentional look — colour transitions only, never
   a lift. (The DRE theme no longer skins bare <button>s as of theme v2.13.0, so
   under DRE this scoping is simply defensive.) */
.photo-view-toggle {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    background: var(--rv-bg-sunken);
    border: 1px solid var(--rv-border-light);
    border-radius: var(--rv-radius-full);
}

.photo-browse-toolbar .photo-view-toggle .photo-view-btn {
    margin: 0;
    border: 0;
    border-radius: var(--rv-radius-full);
    background: transparent;
    box-shadow: none;
    color: var(--rv-text-color);
    padding: 6px 16px;
    font-size: var(--rv-text-xs);
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0.01em;
    cursor: pointer;
    transition: background-color var(--rv-transition-base), color var(--rv-transition-base);
}

.photo-browse-toolbar .photo-view-toggle .photo-view-btn:hover:not(.is-active),
.photo-browse-toolbar .photo-view-toggle .photo-view-btn:focus-visible:not(.is-active) {
    background: color-mix(in oklab, var(--rv-accent) 12%, transparent);
    color: var(--rv-text-strong);
    box-shadow: none;
    transform: none;
}

.photo-browse-toolbar .photo-view-toggle .photo-view-btn.is-active {
    background: var(--rv-accent);
    color: var(--rv-accent-contrast);
    box-shadow: var(--rv-shadow-sm);
    transform: none;
}

.photo-browse-toolbar .photo-view-toggle .photo-view-btn:active {
    transform: none;
    box-shadow: none;
}

.photo-browse-toolbar .photo-view-toggle .photo-view-btn:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
}

.photo-browse-count {
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
    font-variant-numeric: tabular-nums;
}

.photo-browse-stage {
    position: relative;
    min-height: 200px;
}

/* `hidden` alone loses to a view's own display (e.g. masonry's flex) — force it off. */
.photo-browse-stage .photo-view[hidden] {
    display: none !important;
}

/* --- Masonry (responsive round-robin columns, built by the engine) --- */
.photo-masonry {
    display: flex;
    align-items: flex-start;
    gap: 16px;
}

.photo-masonry-col {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* Carded tile shared by masonry + timeline. */
.photo-card {
    display: block;
    width: 100%;
    margin: 0;
    padding: 0;
    text-align: left;
    background: var(--rv-bg);
    color: var(--rv-text-strong);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    overflow: hidden;
    cursor: pointer;
    transition: transform var(--rv-transition-base), box-shadow var(--rv-transition-base), border-color var(--rv-transition-base);
}

.photo-card:hover,
.photo-card:focus-visible {
    transform: translateY(var(--rv-lift-xs));
    border-color: var(--rv-accent);
    box-shadow: var(--rv-shadow);
    outline: none;
}

/* Image frame — reserves the box with a muted placeholder BEFORE the image
   loads, so lazy images never reflow or flash in; it then settles from the 4:3
   placeholder to the image's natural ratio (set inline by the engine on load). */
.photo-card-frame {
    position: relative;
    width: 100%;
    background: var(--rv-bg-sunken);
    overflow: hidden;
    display: block;
    transition: aspect-ratio var(--rv-transition-base)-out;
}

.photo-card-frame.is-broken {
    aspect-ratio: 4 / 3;
}
.photo-card-frame.is-broken .photo-card-img {
    display: none;
}

.photo-card-img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
    /* Fade the image in once it has decoded (engine adds .is-loaded) so it
       eases over the muted placeholder instead of popping. */
    opacity: 0;
    transition: opacity 0.4s ease, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.photo-card-img.is-loaded {
    opacity: 1;
}
.photo-card:hover .photo-card-img.is-loaded {
    transform: scale(1.03);
}

.photo-card-body {
    padding: 10px 12px 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.photo-card-title {
    margin: 0;
    font-size: var(--rv-text-xs);
    font-weight: 600;
    line-height: 1.3;
    color: var(--rv-heading-color);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.photo-card-subtitle {
    margin: 0;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
}

.photo-card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.photo-card-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--rv-text-2xs);
    color: var(--rv-text-color);
    background: var(--rv-bg-sunken);
    border-radius: var(--rv-radius-full);
    padding: 2px 8px;
    max-width: 100%;
    overflow: hidden;
    white-space: nowrap;
}
.photo-card-chip > * { flex: none; }

.photo-chip-icon {
    width: 12px;
    height: 12px;
    flex: none;
}

/* --- Map --- */
.photo-map {
    height: min(70vh, 560px);
    min-height: 360px;
    border-radius: var(--rv-radius);
    overflow: hidden;
    border: 1px solid var(--rv-border);
}

/* --- Timeline (chronological grid that fills the width, grouped by year) ---
   A responsive auto-fill grid, NOT a thin horizontal strip: a collection with
   few years (or a single year) fills the row instead of leaving it mostly
   blank. The year is a tick above the first cell of each year; a single-year
   collection shows one header (.is-single-year) instead of repeating it. */
.photo-timeline-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(116px, 1fr));
    gap: 18px 14px;
    align-items: start;
}

/* Single-year header: one dated marker above a plain grid (no per-cell ticks). */
.photo-timeline-solo {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 18px;
    padding-bottom: 6px;
    font-weight: 700;
    font-size: var(--rv-text-sm);
    color: var(--rv-heading-color);
    border-bottom: 2px solid var(--rv-accent);
    font-variant-numeric: tabular-nums;
}
.photo-timeline-solo::before {
    content: "";
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--rv-accent);
    flex: none;
}

.photo-timeline-cell {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* Year tick above the first cell of each year. Continuation cells keep an empty
   head of the same height so the covers stay aligned across the row. */
.photo-timeline-head {
    height: 22px;
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
}
.photo-timeline-head.is-year::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--rv-accent);
    flex: none;
}
.photo-timeline-y {
    font-weight: 700;
    font-size: var(--rv-text-xs);
    color: var(--rv-heading-color);
    font-variant-numeric: tabular-nums;
}

/* Square cover thumbnail (reuses .photo-card-frame from makeFrame). */
.photo-timeline-thumb {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    margin: 0;
    padding: 0;
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    overflow: hidden;
    background: var(--rv-bg-sunken);
    cursor: pointer;
    transition: transform var(--rv-transition-base), box-shadow var(--rv-transition-base), border-color var(--rv-transition-base);
}
.photo-timeline-thumb:hover,
.photo-timeline-thumb:focus-visible {
    transform: translateY(var(--rv-lift-xs));
    border-color: var(--rv-accent);
    box-shadow: var(--rv-shadow);
    outline: none;
}
.photo-timeline-thumb .photo-card-frame {
    height: 100%;
    border-radius: 0;
    background: transparent;
}

/* Caption under the cover (issue collections: "Vol. N No. M"). */
.photo-timeline-cap {
    margin-top: 5px;
    font-size: var(--rv-text-2xs);
    line-height: 1.3;
    color: var(--rv-text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- Lightbox --- */
body.photo-lightbox-open {
    overflow: hidden;
}

.photo-lightbox {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
}

.photo-lightbox[hidden] {
    display: none;
}

.photo-lightbox-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.82);
}

.photo-lightbox-body {
    position: relative;
    z-index: 1;
    display: flex;
    max-width: 94vw;
    max-height: 92vh;
    background: var(--rv-bg-raised);
    border-radius: var(--rv-radius);
    overflow: hidden;
    box-shadow: var(--rv-shadow);
}

.photo-lightbox-figure {
    margin: 0;
    background: #000;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1 1 auto;
    min-width: 0;
}

.photo-lightbox-figure img {
    max-width: 72vw;
    max-height: 92vh;
    width: auto;
    height: auto;
    display: block;
    object-fit: contain;
}

.photo-lightbox-meta {
    flex: 0 0 280px;
    width: 280px;
    padding: 20px 22px;
    overflow-y: auto;
}

.photo-lightbox-title {
    margin: 0 0 14px;
    color: var(--rv-heading-color);
    font-size: var(--rv-text-base);
    line-height: 1.35;
}

.photo-lightbox-fields {
    margin: 0 0 18px;
}

.photo-lightbox-fields dt {
    color: var(--rv-text-color);
    font-size: var(--rv-text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-top: 10px;
}

.photo-lightbox-fields dd {
    margin: 2px 0 0;
    color: var(--rv-text-strong);
}

.photo-lightbox-link {
    display: inline-block;
    color: var(--rv-accent);
    font-weight: 600;
    text-decoration: none;
}

.photo-lightbox-link:hover {
    color: var(--rv-accent-hover);
    text-decoration: underline;
}

.photo-lightbox-close,
.photo-lightbox-nav {
    position: absolute;
    z-index: 2;
    margin: 0;
    /* Shed any host-theme button padding — under border-box it would overflow the
       fixed width and warp the circle to an ellipse. These are icon buttons;
       width/height set the size. */
    padding: 0;
    box-sizing: border-box;
    border: 1px solid color-mix(in oklab, #fff 24%, transparent);
    background: color-mix(in oklab, #000 52%, transparent);
    -webkit-backdrop-filter: blur(8px) saturate(1.4);
    backdrop-filter: blur(8px) saturate(1.4);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
    /* `scale` (not `transform`) carries the hover growth — see the nav note. */
    transition: background-color var(--rv-transition-base), border-color var(--rv-transition-base),
        box-shadow 0.18s ease, scale 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Hover/active/focus skin. Scoped 4 selectors deep (.photo-browse-container
   .photo-lightbox …:hover) so it out-ranks a host theme's global button:hover
   (e.g. Lively), which would otherwise turn these controls green, add a glow, and
   apply a translateY lift that fights the arrows' centring. We grow with `scale`
   and reset its `transform` to none. (The DRE theme stopped skinning bare
   <button>s in theme v2.13.0; this stays for non-DRE hosts.) */
.photo-browse-container .photo-lightbox .photo-lightbox-close:hover,
.photo-browse-container .photo-lightbox .photo-lightbox-nav:hover,
.photo-browse-container .photo-lightbox .photo-lightbox-close:focus-visible,
.photo-browse-container .photo-lightbox .photo-lightbox-nav:focus-visible {
    background: color-mix(in oklab, #000 72%, transparent);
    border-color: color-mix(in oklab, #fff 44%, transparent);
    box-shadow: 0 8px 26px rgba(0, 0, 0, 0.45);
    color: #fff;
    transform: none;
    scale: 1.07;
}

.photo-browse-container .photo-lightbox .photo-lightbox-close:active,
.photo-browse-container .photo-lightbox .photo-lightbox-nav:active {
    transform: none;
    scale: 0.94;
}

.photo-browse-container .photo-lightbox .photo-lightbox-close:focus-visible,
.photo-browse-container .photo-lightbox .photo-lightbox-nav:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.92);
    outline-offset: 2px;
}

.photo-lightbox-close {
    top: 16px;
    right: 16px;
    width: 40px;
    height: 40px;
}

/* Centring lives on `translate`, an independent property the hover `scale` can
   never overwrite — so the arrows grow in place instead of jumping. */
.photo-lightbox-nav {
    top: 50%;
    translate: 0 -50%;
    width: 48px;
    height: 48px;
}

.photo-lightbox-prev { left: 18px; }
.photo-lightbox-next { right: 18px; }

.photo-lightbox-close svg { width: 20px; height: 20px; }
.photo-lightbox-nav svg { width: 26px; height: 26px; }

@media (max-width: 680px) {
    .photo-lightbox-body {
        flex-direction: column;
        max-height: 94vh;
    }

    .photo-lightbox-figure img {
        max-width: 94vw;
        max-height: 58vh;
    }

    .photo-lightbox-meta {
        flex-basis: auto;
        width: auto;
        max-height: 34vh;
    }

    .photo-lightbox-nav {
        width: 40px;
        height: 40px;
    }

    .photo-lightbox-nav svg { width: 22px; height: 22px; }
}

/* ------------------------------------------------------------------ */
/*  Partner credit (featured-collection galleries)                     */
/* ------------------------------------------------------------------ */

.photo-browse-partner {
    margin: -6px 0 14px;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
}

/* ------------------------------------------------------------------ */
/*  Issue table-of-contents modal (ILAM journal issues)               */
/* ------------------------------------------------------------------ */

.photo-toc {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: clamp(16px, 5vh, 64px) 16px;
    overflow-y: auto;
}
.photo-toc[hidden] { display: none; }

.photo-toc-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
}

.photo-toc-frame {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: minmax(200px, 260px) minmax(0, 1fr);
    width: 100%;
    max-width: 920px;
    background: var(--rv-bg);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    overflow: hidden;
    box-shadow: var(--rv-shadow);
    animation: photo-toc-in 0.22s cubic-bezier(0.33, 1, 0.68, 1);
}
@keyframes photo-toc-in {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .photo-toc-frame { animation: none; }
}

.photo-toc-cover {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    background: var(--rv-bg-sunken);
    padding: 18px;
}
.photo-toc-cover img {
    max-width: 100%;
    max-height: 340px;
    width: auto;
    height: auto;
    display: block;
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
}

.photo-toc-body {
    padding: 20px 22px;
    min-width: 0;
}

.photo-toc-header {
    padding-bottom: 12px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--rv-border);
}

.photo-toc-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 6px;
    font-size: var(--rv-text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--rv-text-color);
}
.photo-toc-eyebrow-icon { width: 14px; height: 14px; }

.photo-toc-title {
    margin: 0;
    font-size: var(--rv-text-lg);
    font-weight: 700;
    color: var(--rv-heading-color);
}
.photo-toc-count {
    margin: 4px 0 0;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
}

.photo-toc-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.photo-toc-item-link {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid transparent;
    border-radius: var(--rv-radius-sm);
    text-decoration: none;
    color: inherit;
    transition: background var(--rv-transition-fast), border-color var(--rv-transition-fast);
}
.photo-toc-item-link:hover {
    background: var(--rv-bg-sunken);
    border-color: var(--rv-border);
}

.photo-toc-item-main { flex: 1; min-width: 0; }
.photo-toc-item-title {
    margin: 0;
    font-size: var(--rv-text-xs);
    font-weight: 500;
    color: var(--rv-text-strong);
    line-height: 1.3;
}
.photo-toc-item-creator {
    margin: 2px 0 0;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
}
.photo-toc-item-meta {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: none;
    font-variant-numeric: tabular-nums;
}
.photo-toc-item-pages {
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
    white-space: nowrap;
}
.photo-toc-item-arrow { display: inline-flex; color: var(--rv-text-color); }
.photo-toc-item-arrow svg { width: 16px; height: 16px; }
.photo-toc-item-link:hover .photo-toc-item-arrow { color: var(--rv-accent); }

.photo-toc-close {
    position: absolute;
    top: 10px;
    right: 12px;
    z-index: 2;
    width: 32px;
    height: 32px;
    border: 0;
    border-radius: 50%;
    background: var(--rv-bg-sunken);
    color: var(--rv-text-strong);
    cursor: pointer;
    font-size: var(--rv-text-base);
    line-height: 1;
}
.photo-toc-close:hover { background: var(--rv-border); }

@media (max-width: 720px) {
    .photo-toc-frame { grid-template-columns: 1fr; }
    .photo-toc-cover { max-height: 240px; }
}

/* ------------------------------------------------------------------ */
/*  Reveal-on-scroll (driven by dashboard-core.js)                     */
/* ------------------------------------------------------------------ */

[data-reveal="hidden"] {
    opacity: 0;
    transform: translateY(12px);
    transition:
        opacity 0.5s cubic-bezier(0.19, 1, 0.22, 1),
        transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
    will-change: opacity, transform;
}
[data-reveal="shown"] {
    opacity: 1;
    transform: none;
}
@media (prefers-reduced-motion: reduce) {
    [data-reveal] {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
}

/* ------------------------------------------------------------------ */
/*  Featured collections landing grid                                  */
/* ------------------------------------------------------------------ */

.featured-collections {
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
}
@media (min-width: 720px) {
    .featured-collections { grid-template-columns: repeat(2, 1fr); }
}

.collection-card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    background: var(--rv-bg);
    color: var(--rv-text-strong);
    text-decoration: none;
    transition: transform var(--rv-transition-base), box-shadow var(--rv-transition-base), border-color var(--rv-transition-base);
}
.collection-card:hover {
    transform: translateY(var(--rv-lift-xs));
    border-color: var(--rv-accent);
    box-shadow: var(--rv-shadow);
}
.collection-card:focus-visible {
    outline: none;
    box-shadow: var(--rv-focus-ring);
}

.collection-card-cover {
    aspect-ratio: 16 / 9;
    background: var(--rv-bg-sunken);
    overflow: hidden;
}
.collection-card-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.4s ease;
}
.collection-card:hover .collection-card-cover img {
    transform: scale(1.03);
}

.collection-card-mosaic {
    display: grid;
    width: 100%;
    height: 100%;
    gap: 2px;
}
.collection-card-mosaic[data-count="1"] { grid-template: 1fr / 1fr; }
.collection-card-mosaic[data-count="2"] { grid-template: 1fr / 1fr 1fr; }
.collection-card-mosaic[data-count="3"] { grid-template: 1fr 1fr / 1fr 1fr; }
.collection-card-mosaic[data-count="3"] img:first-child { grid-row: 1 / span 2; }
.collection-card-mosaic[data-count="4"] { grid-template: 1fr 1fr / 1fr 1fr; }
.collection-card-mosaic img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.collection-card-placeholder {
    display: flex;
    width: 100%;
    height: 100%;
    align-items: center;
    justify-content: center;
    color: var(--rv-text-color);
    background: var(--rv-bg-sunken);
}
.collection-card-placeholder svg { width: 40px; height: 40px; opacity: 0.4; }

.collection-card-body {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 18px 20px 20px;
}
.collection-card-title {
    margin: 0;
    font-size: var(--rv-text-lg);
    font-weight: 700;
    line-height: 1.25;
    color: var(--rv-heading-color);
}
.collection-card-tagline {
    margin: 0;
    font-size: var(--rv-text-xs);
    font-weight: 500;
    color: var(--rv-accent);
}
.collection-card-desc {
    margin: 0;
    font-size: var(--rv-text-xs);
    line-height: 1.5;
    color: var(--rv-text-color);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.collection-card-partner {
    align-self: flex-start;
    margin: 4px 0 0;
    padding: 2px 10px;
    font-size: var(--rv-text-2xs);
    color: var(--rv-text-color);
    background: var(--rv-bg-sunken);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-full);
}
.collection-card-footer {
    margin-top: 10px;
    padding-top: 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    border-top: 1px solid var(--rv-border);
}
.collection-card-stats {
    font-size: var(--rv-text-xs);
    color: var(--rv-text-strong);
    font-variant-numeric: tabular-nums;
}
.collection-card-stats strong { font-weight: 700; }
.collection-card-stats-alt { color: var(--rv-text-color); }
.collection-card-cta {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--rv-text-xs);
    font-weight: 600;
    color: var(--rv-accent);
    white-space: nowrap;
}
.collection-card-cta svg { width: 16px; height: 16px; transition: transform var(--rv-transition-fast); }
.collection-card:hover .collection-card-cta svg { transform: translateX(2px); }

/* ------------------------------------------------------------------ */
/*  Entity Network (MapLibre) block                                    */
/*  Multi-entity co-occurrence graph — see asset/js/entity-graph.js.   */
/* ------------------------------------------------------------------ */

.dre-entity-graph { width: 100%; min-height: 480px; }
.dre-entity-graph > .rv-loading { min-height: 480px; }

/* Toolbar
 * ----------------------------------------------------------------------------
 * Every control in this row sits on ONE height (--deg-control-h) with one
 * radius language: rectangular controls use --rv-radius-sm, the type chips use
 * a pill. The search field and the min-link control are a native <input> and
 * <select>, so the DRE theme's GLOBAL form skin (44px-tall selects, width:100%,
 * margin-bottom, appearance:none + a background arrow) cascades in — each such
 * property is reset below so the controls line up flush on the shared row,
 * exactly as the module's .rv-btn / .compare-select already do.
 *
 * Font sizes follow the module's two-step convention: fields you type into or
 * pick from are 0.85rem; compact toggles and field captions are 0.8rem. */
.deg-toolbar {
    --deg-control-h: 2rem;          /* shared height for the whole control row */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rv-space-2) var(--rv-space-3);
    margin: 0 0 var(--rv-space-3);
}
.deg-spacer { flex: 1 1 auto; }

/* Search */
.deg-search { position: relative; flex: 0 1 240px; min-width: 160px; }
.deg-search-input {
    width: 100%;
    height: var(--deg-control-h);
    margin: 0;                      /* reset the theme's form-field margin-bottom */
    padding: 0 var(--rv-space-2);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    color: var(--rv-text-strong);
    font-size: var(--rv-text-xs);
}
.deg-search-input:focus-visible { outline: none; border-color: var(--rv-accent); box-shadow: var(--rv-focus-ring); }
.deg-search-results {
    position: absolute;
    z-index: 30;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    background: var(--rv-bg-raised);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    box-shadow: var(--rv-shadow);
    max-height: 260px;
    overflow-y: auto;
}
.deg-search-hit {
    display: flex;
    align-items: center;
    gap: var(--rv-space-2);
    width: 100%;
    padding: var(--rv-space-2) var(--rv-space-2);
    border: 0;
    background: transparent;
    color: var(--rv-text-strong);
    font-size: var(--rv-text-xs);
    text-align: left;
    cursor: pointer;
}
.deg-search-hit:hover { background: var(--rv-bg-sunken); }
.deg-search-hit-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Type-filter chips — a multi-toggle group whose colour dot doubles as the
   graph legend. The OFF state mirrors the module's other category toggle, the
   cluster-map legend (.rv-cluster-legend__item.is-off): dimmed + struck out. */
.deg-chips { display: flex; flex-wrap: wrap; gap: var(--rv-space-2); }
.deg-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-2);
    height: var(--deg-control-h);
    padding: 0 var(--rv-space-3);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-full);
    background: var(--rv-bg);
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
    cursor: pointer;
    transition: opacity var(--rv-transition-fast), background var(--rv-transition-fast), color var(--rv-transition-fast), border-color var(--rv-transition-fast);
}
.deg-chip:hover { background: var(--rv-bg-raised); border-color: var(--rv-accent); }
.deg-chip:focus-visible { outline: none; box-shadow: var(--rv-focus-ring); }
.deg-chip:not(.deg-chip-on) { opacity: 0.45; }
.deg-chip:not(.deg-chip-on) span:not(.deg-swatch) { text-decoration: line-through; }

/* Min-link <select> — compacted onto the shared row height. Reset the theme's
   global select skin (height:44px, width:100%, margin, appearance:none + arrow
   image) and restore a native dropdown arrow, matching .compare-select. */
.deg-weight {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-2);
    flex: none;
    white-space: nowrap;            /* keep the "Min. link" caption on one line */
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
}
.deg-weight-select {
    height: var(--deg-control-h);
    width: auto;
    margin: 0;
    padding: 0 var(--rv-space-2);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    color: var(--rv-text-strong);
    font-size: var(--rv-text-xs);
    line-height: 1.2;
    cursor: pointer;
    appearance: auto;
    -webkit-appearance: auto;
    -moz-appearance: auto;
}
.deg-weight-select:focus-visible { outline: none; border-color: var(--rv-accent); box-shadow: var(--rv-focus-ring); }

/* Text button (colour-mode toggle) */
.deg-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-1);
    height: var(--deg-control-h);
    padding: 0 var(--rv-space-3);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast), border-color var(--rv-transition-fast);
}
.deg-btn:hover { background: var(--rv-bg-raised); color: var(--rv-accent); border-color: var(--rv-accent); }
.deg-btn:focus-visible { outline: none; box-shadow: var(--rv-focus-ring); }
.deg-btn.is-on { background: var(--rv-accent); color: var(--rv-accent-contrast); border-color: var(--rv-accent); }
.deg-btn:disabled { opacity: 0.45; cursor: default; }
.deg-btn:disabled:hover { background: var(--rv-bg); color: var(--rv-text-color); border-color: var(--rv-border); }

/* Colour-by control: a short label followed by the mode buttons (Type/Cluster/Section) */
.deg-colormode { display: inline-flex; align-items: center; gap: var(--rv-space-1); }
.deg-colormode-label { font-size: var(--rv-text-xs); color: var(--rv-text-color); }

/* Colour swatch dot (chips, legend, sidebar) */
.deg-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }

/* Stage: canvas + sidebar */
.deg-stage {
    display: grid;
    grid-template-columns: 1fr minmax(220px, 280px);
    gap: var(--rv-space-3);
    height: clamp(460px, 68vh, 760px);
}
.deg-canvas {
    position: relative;
    min-width: 0;
    height: 100%;
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    background: var(--rv-bg);
    overflow: hidden;
}
/* MapLibre turns .deg-canvas into the map container and absolute-fills it via
   maplibre-gl.css; the rounded, overflow-hidden .deg-canvas clips the corners. */
.deg-canvas .maplibregl-canvas { outline: none; }
.deg-popup-meta {
    display: block;
    margin-top: 2px;
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
}
.deg-sidebar {
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    background: var(--rv-bg);
    padding: var(--rv-space-4);                  /* match the module's panel padding (.chart-panel) */
    overflow-y: auto;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-strong);
}

/* Sidebar — overview */
.deg-side-title { font-weight: 600; color: var(--rv-heading-color); margin-bottom: var(--rv-space-2); }
.deg-type-counts { display: flex; flex-direction: column; gap: var(--rv-space-1); }
.deg-type-row { display: flex; align-items: center; gap: var(--rv-space-2); }
.deg-type-name { flex: 1 1 auto; }
.deg-type-val { font-variant-numeric: tabular-nums; color: var(--rv-text-color); }
.deg-side-hint { margin: var(--rv-space-3) 0 0; color: var(--rv-text-color); font-size: var(--rv-text-xs); line-height: 1.4; }

/* Sidebar — selected entity */
.deg-detail-type { font-size: var(--rv-text-2xs); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; }
.deg-detail-title {
    margin: var(--rv-space-1) 0 var(--rv-space-1);
    font-size: var(--rv-text-base);
    font-weight: 600;
    color: var(--rv-heading-color);
    overflow-wrap: break-word;
}
.deg-detail-title a { color: inherit; text-decoration: none; }
.deg-detail-title a:hover { color: var(--rv-accent); text-decoration: underline; }
.deg-detail-stats { color: var(--rv-text-color); font-size: var(--rv-text-xs); margin-bottom: var(--rv-space-3); }
.deg-detail-subhead { font-size: var(--rv-text-xs); font-weight: 600; color: var(--rv-text-color); margin-bottom: var(--rv-space-1); }
.deg-neighbors { display: flex; flex-direction: column; gap: var(--rv-space-1); }
.deg-neighbor {
    display: flex;
    align-items: center;
    gap: var(--rv-space-2);
    width: 100%;
    padding: var(--rv-space-1) var(--rv-space-1);
    border: 0;
    border-radius: var(--rv-radius-sm);
    background: transparent;
    color: var(--rv-text-strong);
    text-align: left;
    font-size: var(--rv-text-xs);
    cursor: pointer;
}
.deg-neighbor:hover { background: var(--rv-bg-sunken); }
.deg-neighbor-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deg-neighbor-w { font-variant-numeric: tabular-nums; color: var(--rv-text-color); font-size: var(--rv-text-xs); }
.deg-open {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-top: var(--rv-space-3);
    padding: var(--rv-space-2) var(--rv-space-3);
    border: 1px solid var(--rv-accent);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-accent);
    color: var(--rv-accent-contrast);
    text-decoration: none;
    font-size: var(--rv-text-xs);
    font-weight: 600;
}
.deg-open:hover { background: var(--rv-accent-hover); border-color: var(--rv-accent-hover); }

/* Legend */
.deg-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--rv-space-2) var(--rv-space-4);
    margin-top: var(--rv-space-2);
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
}
.deg-legend-item { display: inline-flex; align-items: center; gap: var(--rv-space-2); }
.deg-legend-note { font-style: italic; }

/* The cluster select can hold a long anchor name ("Fatou Sow (34)"), so cap it
   rather than let one option stretch the whole control row. */
.deg-cluster-select { max-width: 12rem; }

/* Keyboard: the stage is the focusable thing (entity-graph.js takes the arrow keys
   off MapLibre to walk between entities), so it needs a visible ring of its own. */
.deg-canvas:focus-visible {
    outline: none;
    border-color: var(--rv-accent);
    box-shadow: var(--rv-focus-ring);
}

/* Live region announcing the keyboard-focused entity. Visually hidden rather than
   display:none, which would stop it being announced at all. */
.deg-status {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* The highlighted search option. Driven by aria-activedescendant, so focus stays in
   the input and typing keeps working while the highlight moves. */
.deg-search-hit.is-active {
    background: var(--rv-bg-sunken);
    box-shadow: inset 2px 0 0 var(--rv-accent);
}

/* Text alternative: a swatch beside each type heading ties the list back to the
   colours on the map. */
.deg-list .rv-kg-list-head {
    display: flex;
    align-items: center;
    gap: var(--rv-space-2);
}

/* Fullscreen. Same convention (and the same 9999 layer) as the knowledge graph:
   the whole block expands, because the toolbar, sidebar and legend are how this
   graph is driven — MapLibre's own control would take only the canvas. */
.communities-block.rv-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 9999;
    margin: 0;
    padding: var(--rv-space-4);
    background: var(--rv-bg);
    overflow: hidden;
}

/* The definite height goes on the graph container, which then hands the stage
   whatever the header, toolbar and legend leave. 2rem = the block's own padding. */
.communities-block.rv-fullscreen .dre-entity-graph {
    display: flex;
    flex-direction: column;
    height: calc(100vh - 2rem);
    min-height: 0;
}

/* flex-basis 0, not auto: the stage takes the leftover space instead of being
   sized by its content, so height flows strictly one way and the stage's
   ResizeObserver has no cycle to amplify. */
.communities-block.rv-fullscreen .deg-stage {
    flex: 1 1 0;
    height: auto;
    min-height: 8rem;
}

/* Fullscreen is for the graph. The prose description and the text alternative are
   reading aids for the page; squeezing them in would only take height from the
   thing the reader just asked to see. */
.communities-block.rv-fullscreen .chart-description,
.communities-block.rv-fullscreen .deg-list {
    display: none;
}

/* Stack the sidebar under the canvas on narrow viewports */
@media (max-width: 720px) {
    .deg-stage { grid-template-columns: 1fr; height: auto; }
    .deg-canvas { height: 60vh; }
    .deg-sidebar { max-height: 280px; }
    .deg-search { flex-basis: 100%; }
}

/* ------------------------------------------------------------------ */
/*  Spatial Exploration (MapLibre) block                               */
/*  Collection-wide places map + entity picker — spatial-exploration.js */
/* ------------------------------------------------------------------ */

.dre-spatial-exploration { width: 100%; min-height: 520px; }
.dre-spatial-exploration > .rv-loading { min-height: 520px; }

/* Stage: picker sidebar (left) + map column (right) */
.rv-spatial-stage {
    display: grid;
    grid-template-columns: minmax(220px, 300px) 1fr;
    gap: var(--rv-space-3);
    height: clamp(480px, 70vh, 780px);
    margin-top: var(--rv-space-3);
}
.rv-spatial-sidebar {
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    background: var(--rv-bg);
    padding: var(--rv-space-4);
    overflow-y: auto;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-strong);
}
.rv-spatial-main { display: flex; flex-direction: column; min-width: 0; gap: var(--rv-space-2); }
.rv-spatial-canvas {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius);
    background: var(--rv-bg);
    overflow: hidden;
}
.rv-spatial-canvas .maplibregl-canvas { outline: none; }

/* Toolbar: country focus + status line.
 * The <select> is a native control, so the DRE theme's GLOBAL form skin
 * (44px height, width:100%, margin-bottom, appearance:none + an arrow image)
 * cascades in — reset each property so it sits flush on the row, exactly as
 * .deg-weight-select / .compare-select do. */
.rv-spatial-toolbar {
    --rv-spatial-control-h: 2rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rv-space-2) var(--rv-space-3);
}
.rv-spatial-focus {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-2);
    flex: none;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
    white-space: nowrap;
}
.rv-spatial-focus-select {
    height: var(--rv-spatial-control-h);
    width: auto;
    max-width: 16rem;
    margin: 0;
    padding: 0 var(--rv-space-2);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    color: var(--rv-text-strong);
    font-size: var(--rv-text-xs);
    line-height: 1.2;
    cursor: pointer;
    appearance: auto;
    -webkit-appearance: auto;
    -moz-appearance: auto;
}
.rv-spatial-focus-select:focus-visible { outline: none; border-color: var(--rv-accent); box-shadow: var(--rv-focus-ring); }
.rv-spatial-status {
    margin-left: auto;
    font-size: var(--rv-text-xs);
    color: var(--rv-text-color);
    font-variant-numeric: tabular-nums;
}

/* Legend chips — double as show/hide toggles for the origin / current layers
 * (off = dimmed + struck through, like .deg-chip and the cluster-map legend). */
.rv-spatial-legend { display: inline-flex; flex-wrap: wrap; gap: var(--rv-space-1); }
.rv-spatial-legend-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-2);
    height: var(--rv-spatial-control-h);
    padding: 0 var(--rv-space-2);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-full);
    background: var(--rv-bg);
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
    cursor: pointer;
    transition: opacity var(--rv-transition-fast), background var(--rv-transition-fast), border-color var(--rv-transition-fast);
}
.rv-spatial-legend-chip:hover { background: var(--rv-bg-raised); border-color: var(--rv-accent); }
.rv-spatial-legend-chip:focus-visible { outline: none; box-shadow: var(--rv-focus-ring); }
.rv-spatial-legend-chip.is-off { opacity: 0.45; }
.rv-spatial-legend-chip.is-off span:not(.rv-spatial-swatch) { text-decoration: line-through; }
.rv-spatial-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }

/* Sidebar groups + labels */
.rv-spatial-group { margin-bottom: var(--rv-space-4); }
.rv-spatial-label {
    font-size: var(--rv-text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 700;
    color: var(--rv-text-color);
    margin-bottom: var(--rv-space-2);
}

/* Entity-type tabs — single-select pills (active = filled accent) */
.rv-spatial-tabs { display: flex; flex-wrap: wrap; gap: var(--rv-space-1); }
.rv-spatial-tab {
    padding: var(--rv-space-1) var(--rv-space-2);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-full);
    background: var(--rv-bg);
    color: var(--rv-text-color);
    font-size: var(--rv-text-xs);
    cursor: pointer;
    transition: background var(--rv-transition-fast), color var(--rv-transition-fast), border-color var(--rv-transition-fast);
}
.rv-spatial-tab:hover { background: var(--rv-bg-raised); border-color: var(--rv-accent); }
.rv-spatial-tab:focus-visible { outline: none; box-shadow: var(--rv-focus-ring); }
.rv-spatial-tab.is-active { background: var(--rv-accent); color: var(--rv-accent-contrast); border-color: var(--rv-accent); }

/* Search field — reset the theme's global form skin (see toolbar note). */
.rv-spatial-search {
    width: 100%;
    height: 2rem;
    margin: 0;
    padding: 0 var(--rv-space-2);
    border: 1px solid var(--rv-border);
    border-radius: var(--rv-radius-sm);
    background: var(--rv-bg);
    color: var(--rv-text-strong);
    font-size: var(--rv-text-xs);
}
.rv-spatial-search:focus-visible { outline: none; border-color: var(--rv-accent); box-shadow: var(--rv-focus-ring); }

/* Result + top-places lists */
.rv-spatial-list {
    margin-top: var(--rv-space-2);
    max-height: 16rem;
    overflow-y: auto;
    border: 1px solid var(--rv-border-light);
    border-radius: var(--rv-radius-sm);
    padding: var(--rv-space-1);
}
.rv-spatial-top-list { list-style: none; margin: 0; padding: 0; }
.rv-spatial-item {
    display: flex;
    align-items: baseline;
    gap: var(--rv-space-2);
    width: 100%;
    padding: var(--rv-space-1) var(--rv-space-2);
    border: 0;
    border-radius: var(--rv-radius-sm);
    background: transparent;
    color: var(--rv-text-strong);
    text-align: left;
    font-size: var(--rv-text-xs);
    cursor: pointer;
}
.rv-spatial-item:hover { background: var(--rv-bg-sunken); }
.rv-spatial-item.is-active { background: var(--rv-accent); color: var(--rv-accent-contrast); }
.rv-spatial-item-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rv-spatial-item-count { font-variant-numeric: tabular-nums; color: var(--rv-text-color); font-size: var(--rv-text-xs); }
.rv-spatial-item.is-active .rv-spatial-item-count { color: var(--rv-accent-contrast); }
.rv-spatial-muted { padding: var(--rv-space-2) var(--rv-space-2); color: var(--rv-text-color); font-size: var(--rv-text-xs); }

/* Selection chip + summary */
.rv-spatial-selection { margin-bottom: var(--rv-space-4); }
.rv-spatial-hint { margin: 0; color: var(--rv-text-color); font-size: var(--rv-text-xs); line-height: 1.4; }
.rv-spatial-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--rv-space-1);
    padding: var(--rv-space-1) var(--rv-space-2);
    border: 1px solid var(--rv-accent);
    border-radius: var(--rv-radius-full);
    background: color-mix(in oklab, var(--rv-accent) 14%, var(--rv-bg));
    color: var(--rv-text-strong);
    font-size: var(--rv-text-xs);
    cursor: pointer;
}
.rv-spatial-chip:hover { background: color-mix(in oklab, var(--rv-accent) 24%, var(--rv-bg)); }
.rv-spatial-chip:focus-visible { outline: none; box-shadow: var(--rv-focus-ring); }
.rv-spatial-summary { margin: var(--rv-space-2) 0 var(--rv-space-1); color: var(--rv-text-color); font-size: var(--rv-text-xs); }
.rv-spatial-link { font-size: var(--rv-text-xs); color: var(--rv-accent); text-decoration: none; }
.rv-spatial-link:hover { text-decoration: underline; }

/* Stack the sidebar above the map on narrow viewports */
@media (max-width: 720px) {
    .rv-spatial-stage { grid-template-columns: 1fr; height: auto; }
    .rv-spatial-sidebar { max-height: 320px; order: 2; }
    .rv-spatial-main { order: 1; }
    .rv-spatial-canvas { height: 60vh; }
}

/* ==================================================================
   Reduced motion — hover lifts and grows
   The per-feature reduced-motion blocks above stop the animations;
   this stops the remaining hover transforms so pointing at a card
   never moves content for users with a reduced-motion preference.
   ================================================================== */
@media (prefers-reduced-motion: reduce) {
    .rv-stat-card:hover,
    .photo-card:hover,
    .photo-card:focus-visible,
    .photo-timeline-thumb:hover,
    .photo-timeline-thumb:focus-visible,
    .collection-card:hover,
    .whats-new-card:hover,
    .rv-cluster-marker:hover {
        transform: none;
    }
    .photo-card:hover .photo-card-img.is-loaded { transform: none; }
    .photo-lightbox-nav:hover { scale: none; }
}

/* ==================================================================
   Print — dashboards on paper
   Keep each panel/card on one page and drop the interactive chrome
   (toolbars, embed buttons, window switchers, lightbox overlays)
   that means nothing in print. Charts and maps print at their last
   rendered frame.
   ================================================================== */
@media print {
    .chart-panel,
    .rv-stat-card,
    .whats-new-card,
    .collection-card {
        break-inside: avoid;
        page-break-inside: avoid;
    }
    .rv-chart-toolbar,
    .rv-toolbar-btn,
    .rv-embed-btn,
    .rv-embed-block-toolbar,
    .knowledge-graph-toolbar,
    .photo-lightbox,
    .photo-toc,
    .compare-type-switcher,
    .whats-new-windows,
    .rv-combobox-list {
        display: none !important;
    }
}
