/* ==========================================================================
   Glottoviz theme
   Cohesive "earth & language" palette that matches the green tree/map colours
   already used in the visualizations. Loaded after Bootstrap so the variable
   overrides below re-skin Bootstrap components (buttons, etc.) in place.
   ========================================================================== */

:root {
    /* Surfaces & ink — neutral cool grey */
    --gv-bg-1: #f7f8fa;
    --gv-bg-2: #eef0f3;
    --gv-bg-3: #e4e7ec;
    --gv-surface: #ffffff;
    --gv-surface-muted: #f4f5f7;
    --gv-ink: #1f2430;
    --gv-ink-soft: #6b7280;
    --gv-border: #e2e5ea;

    /* Accent — graphite (monochrome, used for active/primary states) */
    --gv-accent: #3a414c;
    --gv-accent-strong: #232831;
    --gv-accent-soft: #eef0f3;

    /* Elevation */
    --gv-shadow-sm: 0 1px 2px rgba(20, 24, 31, 0.05), 0 2px 6px rgba(20, 24, 31, 0.06);
    --gv-shadow-md: 0 12px 30px rgba(20, 24, 31, 0.10);
    --gv-radius: 14px;

    /* Tint Bootstrap's primary colour (links, focus rings) graphite. */
    --bs-primary: var(--gv-accent);
    --bs-primary-rgb: 58, 65, 76;

    /* Hand-tuned to the single-row header (brand + buttons) + main padding, so
       viewport-height panels (#map, .viz5-panel) fill the screen without scrolling. */
    --gv-header-offset: 90px;
}

/* Re-skin Bootstrap primary buttons (the map toolbar's Adaptive Zoom / Tight
   borders) in grey tones via Bootstrap's own per-component variables, so the
   .btn-primary <-> .btn-outline-secondary toggling in controller.js keeps
   working. The .chart-tabs rules below have higher specificity, so the main
   tab bar is unaffected by this. */
.btn-primary {
    --bs-btn-bg: var(--gv-accent);
    --bs-btn-border-color: var(--gv-accent);
    --bs-btn-hover-bg: var(--gv-accent-strong);
    --bs-btn-hover-border-color: var(--gv-accent-strong);
    --bs-btn-active-bg: var(--gv-accent-strong);
    --bs-btn-active-border-color: var(--gv-accent-strong);
    --bs-btn-disabled-bg: var(--gv-accent);
    --bs-btn-disabled-border-color: var(--gv-accent);
}

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    min-height: 100vh;
    color: var(--gv-ink);
    font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    background:
        radial-gradient(1200px 600px at 12% -10%, var(--gv-bg-1) 0%, transparent 60%),
        radial-gradient(1000px 700px at 110% 0%, #f0f2f5 0%, transparent 55%),
        linear-gradient(160deg, var(--gv-bg-2) 0%, var(--gv-bg-3) 100%);
    background-attachment: fixed; /* Trying to avoid scrolling all together. Failsafe.*/
}

/* --------------------------------------------------------------------------
   Header
   -------------------------------------------------------------------------- */
.app-header {
    position: relative;
    background: linear-gradient(120deg, #ffffff 0%, #f6f7f9 100%);
    border-bottom: 1px solid var(--gv-border);
    box-shadow: var(--gv-shadow-sm);
}

/* thin accent strip along the very top */
.app-header::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: linear-gradient(90deg, var(--gv-accent-strong), var(--gv-accent) 45%, #9aa1ad 100%);
}

.app-header__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 20px;
    padding: 7px 12px;
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: inherit;
}

/* .brand__mark {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: var(--gv-surface);
    border: 1px solid var(--gv-border);
    box-shadow: var(--gv-shadow-sm);
    flex: none;
    overflow: hidden;
} */

.brand__mark img {
    width: 26px;
    height: 26px;
    object-fit: contain;
    display: block;
}

/* Title + subtitle share one baseline-aligned row, so the subtitle costs no
   extra vertical height. */
.brand__text {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    column-gap: 10px;
    line-height: 1.1;
}

.app-title {
    margin: 0;
    font-family: 'Fraunces', Georgia, "Times New Roman", serif;
    font-weight: 600;
    font-size: clamp(1.3rem, 1.6vw, 1.55rem);
    letter-spacing: 0.1em;
    background: linear-gradient(120deg, var(--gv-accent-strong), #525a67 75%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: var(--gv-accent-strong);
    /* transition: letter-spacing 0.15s ease-out; */

}

/* .app-title:hover {
    letter-spacing: -0.01em;
    transition: letter-spacing 0.15s ease-in;
} */

.app-subtitle {
    margin: 0;
    font-size: 0.82rem;
    font-weight: 400;
    letter-spacing: 0.01em;
    color: var(--gv-ink-soft);
}

/* On narrow screens drop the subtitle so the header stays compact. */
/* Not coherently created a version for small screens, so dropping this. */
/* @media (max-width: 767.98px) {
    .app-subtitle {
        display: none;
    }
} */

/* --------------------------------------------------------------------------
   Main / layout
   -------------------------------------------------------------------------- */
.app-main {
    /* Full-width, no centred gutters (dropped Bootstrap .container). Small padding so content
       isn't flush to the window edges. */
    max-width: none;
    width: 100%;
    padding: 5px 12px;
}

/* --------------------------------------------------------------------------
   Visualization tab bar (#chartControls)
   Restyled as a segmented "pill" control. The buttons keep their Bootstrap
   .btn / .active classes (controller.js toggles .active), we just override
   the look here.
   -------------------------------------------------------------------------- */
.chart-tabs {
    display: inline-flex;
    flex-wrap: wrap;
    /* gap: 4px;
    padding: 5px; */
    margin-bottom: 0;
    background: var(--gv-surface);
    border: 1px solid var(--gv-border);
    border-radius: 999px;
    /* box-shadow: var(--gv-shadow-sm); */
}

.chart-tabs .btn {
    --bs-btn-bg: transparent;
    --bs-btn-border-color: transparent;
    border: none;
    border-radius: 999px;
    padding: 0.5rem 1.1rem;
    font-size: 0.92rem;
    font-weight: 500;
    color: var(--gv-ink-soft);
    background: transparent;
    box-shadow: none;
    transition: color 0.15s ease, background-color 0.15s ease;
}

.chart-tabs .btn:hover,
.chart-tabs .btn:focus-visible {
    color: var(--gv-accent-strong);
    background: var(--gv-accent-soft);
}

.chart-tabs .btn.active {
    color: #fff;
    background: linear-gradient(145deg, var(--gv-accent) 0%, var(--gv-accent-strong) 100%);
    /* box-shadow: 0 4px 12px rgba(20, 24, 31, 0.25); */
}

/* --------------------------------------------------------------------------
   Content surfaces
   -------------------------------------------------------------------------- */
#data-container,
#chart1-container {
    background: var(--gv-surface);
    border: 1px solid var(--gv-border);
    border-radius: var(--gv-radius);
    box-shadow: var(--gv-shadow-md);
    padding: 20px 24px;
}

#data-container h3 {
    font-family: Georgia, serif;
    font-weight: 600;
    color: var(--gv-accent-strong);
}

/* Map host: round + lift the Leaflet canvas. Definite height so the map renders in every tab
   (a percentage chain collapses without a definite ancestor height). See --gv-header-offset. */
#map {
    height: calc(100vh - var(--gv-header-offset));
    min-height: 560px;
    border-radius: var(--gv-radius);
    border: 1px solid var(--gv-border);
    box-shadow: var(--gv-shadow-md);
    overflow: hidden;
} 

/* --------------------------------------------------------------------------
   Visualization 5: filter side panel beside the shared map.
   The panel is a flex item that Bootstrap's `d-none` removes when Vis 5 is not
   active; the map wrap then flex-grows back to full width.
   -------------------------------------------------------------------------- */
.viz5-row {
    display: flex;
    gap: 0.75rem;
    align-items: stretch;
}

.viz5-map-wrap {
    flex: 1 1 auto;
    min-width: 0;
    /* allow the map to shrink instead of overflowing the row */
}

.viz5-panel {
    flex: 0 0 260px;
    max-height: calc(100vh - var(--gv-header-offset));
    overflow-y: auto;
    padding: 0.75rem;
    padding-bottom: 0;
    padding-top: 0;
    background: #fff;
    border: 1px solid var(--gv-border);
    border-radius: var(--gv-radius);
    box-shadow: var(--gv-shadow-md);
}

/* Sticky action bar at the bottom of the Vis 5 filter pane: Reset / Apply + the request status stay
   pinned to the bottom of the scrolling panel so they're always reachable. Bleeds to the panel edges
   (like .left-tabs at the top) with an opaque background covering content that scrolls behind it. */
.viz5-actions {
    position: sticky;
    bottom: 0;
    z-index: 2;
    margin: 0.25rem -0.75rem -0.75rem;
    padding: 0.5rem 0.75rem 0.75rem;
    background: var(--gv-surface);
    border-top: 1px solid var(--gv-border);
}

/* The status box brings its own sticky behaviour (below) for the KelpFusion pane; inside the filter
   pane's already-sticky action bar it should just sit in normal flow. */
.viz5-actions .gv-status {
    position: static;
    margin-top: 8px;
}

/* --------------------------------------------------------------------------
   Request status indicator + live elapsed timer (status.js).
   Mounted at the bottom of a pane; sticky so it stays visible while the pane
   scrolls. Hidden until the first request, then persists (showing the last
   run's frozen time) until the next request starts.
   -------------------------------------------------------------------------- */
.gv-status {
    position: sticky;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    padding: 8px 10px;
    border-radius: 10px;
    background: var(--gv-surface-muted);
    border: 1px solid var(--gv-border);
    font-size: 0.78rem;
    color: var(--gv-ink);
}

.gv-status.is-idle {
    display: none;
}

.gv-status__spinner {
    flex: none;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    box-sizing: border-box;
}

.gv-status.is-running .gv-status__spinner {
    border: 2px solid var(--gv-border);
    border-top-color: var(--gv-accent);
    animation: gv-spin 0.7s linear infinite;
}

.gv-status.is-done .gv-status__spinner {
    background: #3f9d6d;
}

.gv-status.is-error .gv-status__spinner {
    background: #c0435a;
}

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

@media (prefers-reduced-motion: reduce) {
    .gv-status.is-running .gv-status__spinner {
        animation: none;
    }
}

.gv-status__body {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
    min-width: 0;
}

.gv-status__label {
    font-weight: 600;
}

.gv-status__meta {
    color: var(--gv-ink-soft);
}

/* Tabular figures so the ticking timer doesn't jitter its width. */
.gv-status__time {
    font-variant-numeric: tabular-nums;
}

.viz5-panel .fw-semibold {
    color: var(--gv-accent-strong);
}

/* Sticky host for the pane switcher: keeps Filters | KelpFusion pinned while a long pane scrolls.
   The negative margins bleed it across the panel's 0.75rem padding so scrolled content passes
   cleanly underneath instead of peeking out at the sides or above the pill. */
.left-tabs {
    position: sticky;
    top: 0;
    z-index: 2;
    /* Bleed to the panel edges; a small top strip is all that's needed to cover content scrolling
       up behind the (opaque) pill, so the two pane buttons sit close to the panel's top edge. */
    margin: -0.75rem -0.75rem 0.5rem;
    padding: 0.3rem 0.75rem 0.4rem;
    background: var(--gv-surface);
}

/* Pane switcher at the top of the shared left column (Filters | KelpFusion). Same pill treatment as
   the header's .chart-tabs, but sized for a 260px column: full width, equal halves, smaller type. */
.viz5-tabs {
    display: flex;
    width: 100%;
    background: var(--gv-surface);
    border: 1px solid var(--gv-border);
    border-radius: 999px;
}

.viz5-tabs .btn {
    --bs-btn-bg: transparent;
    --bs-btn-border-color: transparent;
    flex: 1 1 0;
    border: none;
    border-radius: 999px;
    padding: 0.25rem 0.5rem;
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--gv-ink-soft);
    background: transparent;
    box-shadow: none;
    transition: color 0.15s ease, background-color 0.15s ease;
}

.viz5-tabs .btn:hover,
.viz5-tabs .btn:focus-visible {
    color: var(--gv-accent-strong);
    background: var(--gv-accent-soft);
}

.viz5-tabs .btn.active {
    color: #fff;
    background: linear-gradient(145deg, var(--gv-accent) 0%, var(--gv-accent-strong) 100%);
}

/* Scrollable option lists (top-level families, MED categories) inside the panel. */
.viz5-scroll {
    max-height: 180px;
    overflow-y: auto;
    border: 1px solid var(--gv-border);
    border-radius: 8px;
    padding: 0.35rem 0.5rem;
}

/* Collapsible filter groups
   ------------------------------------------------------------------------------------------
   With seven groups the pane no longer fits on a screen, so each one collapses to its header.
   The header keeps a status ("All" / "3 selected"), which is what makes a collapsed group
   readable at a glance. */
.gv-section { border-bottom: 1px solid var(--gv-border); }
.gv-section:last-of-type { border-bottom: 0; }

.gv-section__header {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    width: 100%;
    padding: 0.4rem 0;
    background: none;
    border: 0;
    text-align: left;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--gv-accent-strong);
}
.gv-section__header:hover .gv-section__title { text-decoration: underline; }
.gv-section__title { flex: 1 1 auto; min-width: 0; }

/* Status: muted when the group filters nothing, accented once it does. */
.gv-section__status {
    flex: 0 0 auto;
    font-weight: 400;
    font-size: 0.75rem;
    color: var(--gv-ink-soft);
}
.gv-section__status.is-active { color: var(--gv-accent); font-weight: 600; }

/* A CSS triangle rather than a glyph, so it is the same shape in every font. */
.gv-section__caret {
    flex: 0 0 auto;
    width: 0;
    height: 0;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    border-left: 6px solid currentColor;
    transition: transform 0.18s ease;
}
.gv-section.is-open .gv-section__caret { transform: rotate(90deg); }

/* Animating to the content's natural height without measuring it in JS: a one-row grid whose
   track goes 0fr -> 1fr. The child's overflow:hidden and min-height:0 are both required - the
   track collapses but the content refuses to clip without them. */
.gv-section__body {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.18s ease;
}
.gv-section.is-open .gv-section__body { grid-template-rows: 1fr; }
.gv-section__inner {
    overflow: hidden;
    min-height: 0;
}
/* Breathing room only while open, so a collapsed group is exactly its header. */
.gv-section.is-open .gv-section__inner { padding-bottom: 0.5rem; }

/* Search + Clear all + Show selected, above the long option lists. */
.viz5-list-controls {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin-bottom: 0.35rem;
}
.viz5-list-controls .form-control-sm { font-size: 0.75rem; }
.viz5-list-controls .btn { font-size: 0.7rem; }

/* Geographic filter overlays (drawn boxes/polygons and the hovered country outline)
   ------------------------------------------------------------------------------------------
   Leaflet renders these as SVG <path> elements, and CSS beats the presentation attributes it
   sets, so the whole appearance lives here rather than in style objects in geoFilter.js.
   pointer-events:none keeps a shape from stealing hover from the points drawn above it. */
.gv-shape {
    stroke: #1d4ed8;
    stroke-width: 2;
    fill: none;
    pointer-events: none;
    opacity: 1;
    /* stroke and stroke-width are in here too, so a flash settles back to the resting colour
       gradually instead of snapping the moment the class comes off. */
    transition: opacity 0.35s ease, stroke 0.6s ease, stroke-width 0.6s ease;
}

/* Flash: a shape being pointed at, either by hovering its row or by clicking it in the list.
   Also the state that survives "Hide borders & boxes", so you can still find a shape there. */
.gv-shape--flash {
    stroke: #f59e0b;
    stroke-width: 4;
}

/* The hovered country's outline gets its own pane so the whole outline - which is dozens of
   separate island paths - can be faded as one thing by animating a single element's opacity.
   Fading the layer itself is not an option: taking it off the map deletes the SVG immediately,
   and a deleted element cannot transition.

   Asymmetric on purpose: appearing is instant, so the outline feels like it is tracking the
   pointer, while leaving fades. A transition belongs to the state being transitioned *to*, so
   putting the duration on .is-fading fades on the way out, and dropping the class snaps back.

   The 0.6s here is the single source of truth - geoFilter.js reads it back off the pane with
   getComputedStyle to know when the element is safe to remove. */
.gv-outline-pane {
    opacity: 1;
    transition: opacity 0s;
}
.gv-outline-pane.is-fading {
    opacity: 0;
    transition: opacity 0.6s ease;
}

/* "Hide borders & boxes": purely visual, nothing leaves the map and no filter changes. */
.gv-overlays-hidden .gv-shape { opacity: 0; }
.gv-overlays-hidden .gv-shape--flash { opacity: 1; }

/* Hover tooltip for map points. Positioned inside .viz5-map-wrap (which is position:relative),
   above the chart pane, and non-interactive so it never blocks the points. */
.map-tooltip {
    display: none;
    position: absolute;
    z-index: 500;
    pointer-events: none;
    max-width: 240px;
    padding: 3px 8px;
    font-size: 0.8rem;
    color: var(--gv-ink);
    background: var(--gv-surface);
    border: 1px solid var(--gv-border);
    border-radius: 8px;
    box-shadow: var(--gv-shadow-sm);
    white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Leaflet polish
   -------------------------------------------------------------------------- */
.leaflet-container {
    font-family: "Inter", system-ui, sans-serif;
}

.leaflet-bar a,
.leaflet-bar a:hover {
    color: var(--gv-accent-strong);
}

.leaflet-popup-content-wrapper {
    border-radius: 12px;
    box-shadow: var(--gv-shadow-md);
}

.leaflet-popup-content {
    font-size: 0.86rem;
    line-height: 1.45;
    color: var(--gv-ink);
}

/* The Vis-2 contour toolbar still sits over the map: give its buttons a softer card look. */
#chart2-toolbar .btn {
    border-radius: 12px;
}

/* --------------------------------------------------------------------------
   Visualization 4 (KelpFusion): right-hand set legend column (kelpLegend.js).
   Shares the .viz5-panel card look; laid out as a flex column so the set list
   scrolls and the languoid lookup takes the lower portion when open.
   -------------------------------------------------------------------------- */
.kelp-legend {
    flex: 0 0 300px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* the inner lists scroll, not the panel */
}

.kelp-set-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.kelp-card {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    padding: 0.4rem;
    border: 1px solid var(--gv-border);
    border-radius: 10px;
    background: #fff;
    cursor: pointer;
}

.kelp-card:hover {
    background: var(--gv-surface-muted);
}

.kelp-card--selected {
    border-color: var(--gv-accent-strong);
    box-shadow: 15px 1px 10px 1px var(--gv-accent);
}

.kelp-card--hidden {
    opacity: 0.5;
}

.kelp-minimap {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    background: var(--gv-surface-muted);
    border: 1px solid var(--gv-border);
    border-radius: 8px;
}

.kelp-card__meta {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.kelp-card__title {
    font-weight: 600;
    font-size: 0.85rem;
    color: var(--gv-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    /* white-space: nowrap;  */
}

.kelp-card__controls {
    display: flex;
    gap: 0.35rem;
    margin-top: 0.2rem;
}

.kelp-card__controls .btn {
    padding: 0.1rem 0.4rem;
    font-size: 0.72rem;
}

/* Languoid lookup: appears below the set list, sharing the column's height. */
.kelp-lookup {
    flex: 0 0 auto;
    max-height: 45%;
    min-height: 0;
    display: flex;
    flex-direction: column;
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--gv-border);
}

.kelp-lookup__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.35rem;
}

.kelp-lookup__header .btn-close {
    font-size: 0.6rem;
}

.kelp-lookup__list {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    /* fill the lookup area instead of viz5-scroll's 180px cap */
}

.kelp-lookup__row {
    padding: 0.2rem 0.35rem;
    border-radius: 6px;
    font-size: 0.82rem;
    cursor: default;
}

.kelp-lookup__row:hover {
    background: var(--gv-accent-soft);
}

/* Hover emphasis for a languoid picked from the legend's lookup list. Set as a class rather than a
   fill attribute so each host's resting color (Vis 4's white dots, Vis 5's green ones) is restored
   simply by removing it. */
.kelp-node--highlighted {
    fill: #d84d46;
}

/* Popup nudge over the map: shown while hidden sets are still part of the KelpFusion layout. */
.kelp-redraw-hint {
    position: absolute;
    top: 0.75rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1000;
    max-width: 420px;
    padding: 0.4rem 0.7rem;
    background: var(--gv-surface);
    color: var(--gv-ink);
    border: 1px solid var(--gv-border);
    border-radius: 10px;
    box-shadow: var(--gv-shadow-md);
    font-size: 0.82rem;
    line-height: 1.35;
    text-align: center;
}

kbd {
    /* padding: 0.05rem 0.35rem;
    border-radius: 5px;
    background: var(--gv-accent);
    border: 1px solid var(--gv-border);
    font-size: 0.78rem; */
    /* Copied from https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/kbd */
    background-color: #eeeeee;
    border-radius: 3px;
    border: 1px solid #b4b4b4;
    box-shadow:
        0 1px 1px rgb(0 0 0 / 0.2),
        0 2px 0 0 rgb(255 255 255 / 0.7) inset;
    color: #333333;
    /* display: inline-block; */
    font: inherit;
    font-size: 0.85em;
    font-weight: bold;
    line-height: 1;
    padding: 2px 4px;
    white-space: nowrap;

}

/* --------------------------------------------------------------------------
   Header actions (visualization tabs + About button) and the About modal
   -------------------------------------------------------------------------- */
.app-header__actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}

.about-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 0.45rem 0.9rem;
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--gv-ink-soft);
    background: var(--gv-surface);
    border: 1px solid var(--gv-border);
    border-radius: 999px;
    box-shadow: var(--gv-shadow-sm);
    cursor: pointer;
    transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
}

.about-btn:hover,
.about-btn:focus-visible {
    color: var(--gv-accent-strong);
    background: var(--gv-accent-soft);
    border-color: var(--gv-accent-soft);
}

/* Overlay. `hidden` toggles it (about.js); the rule below beats display:flex when hidden. */
.gv-modal {
    position: fixed;
    inset: 0;
    z-index: 2000;
    /* above Leaflet panes/controls and the map toolbars */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba(20, 24, 31, 0.45);
    backdrop-filter: blur(2px);
}

.gv-modal[hidden] {
    display: none;
}

.gv-modal__panel {
    display: flex;
    flex-direction: column;
    width: min(92vw, 820px);
    max-height: 86vh;
    background: var(--gv-surface);
    border: 1px solid var(--gv-border);
    border-radius: var(--gv-radius);
    box-shadow: 0 24px 60px rgba(20, 24, 31, 0.35);
    overflow: hidden;
}

.gv-modal__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--gv-border);
    background: linear-gradient(120deg, #ffffff 0%, #f6f7f9 100%);
}

.gv-modal__head h2 {
    margin: 0;
    font-family: Georgia, serif;
    font-weight: 600;
    font-size: 1.4rem;
    color: var(--gv-accent-strong);
}

.gv-modal__close {
    flex: none;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    border: 1px solid var(--gv-border);
    background: var(--gv-surface);
    color: var(--gv-ink-soft);
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
}

.gv-modal__close:hover {
    background: var(--gv-accent-soft);
    color: var(--gv-accent-strong);
}

.gv-modal__body {
    overflow-y: auto;
    padding: 20px;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--gv-ink);
}

.gv-modal__body a {
    color: var(--gv-accent-strong);
}

.about-section {
    margin-bottom: 22px;
}

.about-section:last-child {
    margin-bottom: 0;
}

.about-section h3 {
    margin: 0 0 8px;
    font-family: Georgia, serif;
    font-weight: 600;
    font-size: 1.08rem;
    color: var(--gv-accent-strong);
}

.about-section p {
    margin: 0 0 8px;
}

.about-tabs {
    margin: 0;
}

.about-tabs dt {
    margin-top: 8px;
    font-weight: 600;
    color: var(--gv-ink);
}

.about-tabs dd {
    margin: 2px 0 0;
    color: var(--gv-ink-soft);
}

/* .about-tabs kbd {
    font: inherit;
    font-size: 0.8em;
    padding: 1px 6px;
    border: 1px solid var(--gv-border);
    border-bottom-width: 2px;
    border-radius: 5px;
    background: var(--gv-surface-muted);
    color: var(--gv-ink);
} */

.about-source {
    margin: 0 0 10px;
}

.about-cite {
    font-size: 0.85rem;
    color: var(--gv-ink-soft);
}

.about-list {
    margin: 0;
    padding-left: 1.2rem;
    color: var(--gv-ink-soft);
}

.about-list li {
    margin: 2px 0;
}

.about-note {
    margin-top: 10px;
    font-size: 0.82rem;
    color: var(--gv-ink-soft);
}

/* Scrollable citation table with a sticky header. */
.about-table-wrap {
    max-height: 320px;
    overflow-y: auto;
    margin-top: 6px;
    border: 1px solid var(--gv-border);
    border-radius: 10px;
}

.about-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8rem;
}

.about-table th,
.about-table td {
    padding: 6px 10px;
    text-align: left;
    vertical-align: top;
    border-bottom: 1px solid var(--gv-border);
}

.about-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--gv-surface-muted);
    font-weight: 600;
    color: var(--gv-ink);
}

.about-table td:first-child {
    white-space: nowrap;
    font-family: "Courier New", monospace;
    font-size: 0.76rem;
    color: var(--gv-ink-soft);
}

.about-table th:last-child,
.about-table td:last-child {
    white-space: nowrap;
}

.about-table tbody tr:last-child td {
    border-bottom: none;
}

/* NonCommercial licenses stand out so the distinction isn't missed. */
.about-lic-nc {
    font-weight: 600;
    color: #b06a1b;
}