* {
    box-sizing: border-box;
}

/* self-hosted fonts (fonts/ dir) — no third-party requests, no JS.
latin-ext subsets carry the accented country names (Gjirokastër, Curaçao…). */
@font-face {
    font-family: 'Inter';
    src: url('fonts/inter-400.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Inter';
    src: url('fonts/inter-400-ext.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Inter';
    src: url('fonts/inter-600.woff2') format('woff2');
    font-weight: 600;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Inter';
    src: url('fonts/inter-600-ext.woff2') format('woff2');
    font-weight: 600;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Inter';
    src: url('fonts/inter-700.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* design tokens.
   score band colours are ordered light-to-dark as well as green-to-red so the
   scale still reads for colour-blind visitors; everything else stays neutral
   so the data is the only colour on the page. */
:root {
    --best: #00a878;
    --good: #93d772;
    --ok: #f5e169;
    --risky: #f09d4d;
    --danger: #e4572e;
    --unknown: #45484d;

    /* permanent dark theme: strictly neutral cool greys, so green belongs
       to the data and the brand moments (ribbon, accent) alone —
       a green-tinted ground made the green score cells read as wallpaper */
    --bg: #131417;
    --surface: #1b1d20;
    --ink: #e8eaed;
    --muted: #989ea6;
    --line: #2a2d31;
    --line-strong: #3b3f45;   /* hover borders, header rules */
    --soft-ink: #c6cad0;      /* long-form notes, a step quieter than ink */
    --accent: #2fb388;        /* brightened so it carries on dark */
    --on-accent: #0d130c;     /* text on accent-filled chips/buttons */
}

body {
    margin: 0;
    padding: 0;
    background-color: var(--bg);
    color: var(--ink);
    font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    -webkit-text-size-adjust: none;
    text-size-adjust: none;
}

/* brand ribbon: the metric scale gradient, worst -> best */
body::before {
    content: "";
    display: block;
    height: 4px;
    background: linear-gradient(90deg, var(--danger), var(--risky), var(--ok), var(--good), var(--best));
}

a {
    color: var(--accent);
    transition: color 0.15s ease, background-color 0.15s ease;
}

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

::selection {
    background: #2b5c48;
}

p {
    line-height: inherit;
}

/* --- header: annual-report masthead --- */
.banner {
    padding: 40px 20px 0;
    border-bottom: 1px solid var(--line);
    text-align: center;
}

.banner h1 {
    margin: 0;
    color: var(--ink);
    /* spaced capitals, in the voice of the annual indices the site sits
       alongside; text-indent rebalances the trailing letter-space */
    font-size: 24px;
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: 0.24em;
    text-indent: 0.24em;
    text-transform: uppercase;
}

.banner h1 a {
    color: inherit;
    text-decoration: none;
}

/* the year as a badge — it changes annually, so it isn't part of the name */
.banner .year {
    display: inline-block;
    vertical-align: 4px;
    margin-left: 12px;
    padding: 2px 9px;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    color: var(--muted);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-indent: 0;
}

.banner .tagline {
    margin: 8px 0 0;
    color: var(--muted);
    font-size: 14px;
}

.banner nav {
    padding: 14px 0 0;
}

.banner nav a {
    display: inline-block;
    padding: 10px 16px 12px;
    color: var(--muted);
    font-size: 15px;
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;    /* the active border sits on the header hairline */
}

.banner nav a:hover {
    color: var(--ink);
}

.banner nav a.current {
    color: var(--ink);
    border-bottom-color: var(--accent);
}

/* --- page content --- */
.page {
    max-width: 1024px;
    margin: 0 auto;
    padding: 40px 20px 20px;
}

.intro {
    margin: 0 auto 36px;
    max-width: 640px;
    color: var(--muted);
    font-size: 17px;
    line-height: 1.6;
    text-align: center;
}

.list-wrap {
    /* visible (not a scroll container) on every width, so the sticky header
       sticks to the viewport — the fixed-layout table already fits, so there's
       nothing to scroll sideways */
    overflow-x: visible;
    margin: 0 0 8px;
}

.notes {
    margin: 0 auto 25px;
    font-size: 15px;
    line-height: 1.7;
    text-align: center;
}

.notes p {
    margin: 0;
}

/* --- verdict chips + legend --- */
.verdict,
.legend {
    margin: 0 auto 32px;
    max-width: 640px;
    text-align: center;
}

.legend {
    max-width: 1024px;
    margin-bottom: 18px;
}

.chip {
    display: inline-block;
    margin: 0 3px 8px;
    padding: 5px 13px;
    border-radius: 6px;
    color: #10130f;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
}

.chip.best { background-color: var(--best); }
.chip.good { background-color: var(--good); }
.chip.ok { background-color: var(--ok); }
.chip.risky { background-color: var(--risky); }
.chip.danger { background-color: var(--danger); }

/* the unknown fill is dark (unlike the band colours), so its text flips light */
.chip.unknown {
    background-color: var(--unknown);
    color: #c9ccd0;
}

/* --- CSS-only table filters ---
   One hidden checkbox per band sits as a direct child of .page, before the chip
   labels and .list-wrap, so `:checked ~` reaches both. build.py stamps each row
   with a v-<verdict> class and each section with has-<band>; the rules below do
   the rest — every band is an independent toggle, all on by default. */
/* the map reuses .filter-band for its bands and .metric-toggle for its metrics */
.filter-band,
.metric-toggle {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
    pointer-events: none;
}

label.chip {
    cursor: pointer;
    user-select: none;
    transition: background-color 0.15s ease, border-color 0.15s ease,
                color 0.15s ease, opacity 0.15s ease;
}

/* legend chips: outline, colour dot, live count. The solid fills above stay
   on the country-page verdict chips, where the colour IS the message; here
   the chips are controls, so they stay quiet and the table keeps the colour */
.legend .chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 4px 12px;              /* the border adds the missing 1px back */
    background-color: var(--surface);
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--ink);
}

.legend .chip::before {
    content: "";
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background-color: var(--accent);       /* the "All" dot */
}

.legend .chip.best::before { background-color: var(--best); }
.legend .chip.good::before { background-color: var(--good); }
.legend .chip.ok::before { background-color: var(--ok); }
.legend .chip.risky::before { background-color: var(--risky); }
.legend .chip.danger::before { background-color: var(--danger); }

.legend .chip:hover {
    border-color: var(--line-strong);
}

.legend .count {
    color: var(--muted);
    font-weight: 400;
    font-variant-numeric: tabular-nums;
}

/* each band chip is its own toggle (all on by default): a checked chip wears the
   accent ring at full strength, an unchecked one goes quiet and dimmed */
#b-best:not(:checked) ~ .legend [for="b-best"],
#b-good:not(:checked) ~ .legend [for="b-good"],
#b-ok:not(:checked) ~ .legend [for="b-ok"],
#b-risky:not(:checked) ~ .legend [for="b-risky"],
#b-danger:not(:checked) ~ .legend [for="b-danger"] {
    opacity: 0.4;
}

#b-best:checked ~ .legend [for="b-best"],
#b-good:checked ~ .legend [for="b-good"],
#b-ok:checked ~ .legend [for="b-ok"],
#b-risky:checked ~ .legend [for="b-risky"],
#b-danger:checked ~ .legend [for="b-danger"] {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
}

/* keyboard focus on a hidden checkbox shows on its chip */
#b-best:focus-visible ~ .legend [for="b-best"],
#b-good:focus-visible ~ .legend [for="b-good"],
#b-ok:focus-visible ~ .legend [for="b-ok"],
#b-risky:focus-visible ~ .legend [for="b-risky"],
#b-danger:focus-visible ~ .legend [for="b-danger"] {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* the filtering: an unchecked band hides its rows */
#b-best:not(:checked) ~ .list-wrap tbody tr.v-best,
#b-good:not(:checked) ~ .list-wrap tbody tr.v-good,
#b-ok:not(:checked) ~ .list-wrap tbody tr.v-ok,
#b-risky:not(:checked) ~ .list-wrap tbody tr.v-risky,
#b-danger:not(:checked) ~ .list-wrap tbody tr.v-danger {
    display: none;
}

/* a continent shows only while it still has a country in some checked band
   (rows/sections with no data — v-unknown / has-unknown — always show) */
.list-wrap .continent {
    display: none;
    /* the desktop jump-nav anchors land here; offset for the sticky metric
       header so a heading isn't hidden behind it */
    scroll-margin-top: 56px;
}
#b-best:checked ~ .list-wrap .continent.has-best,
#b-good:checked ~ .list-wrap .continent.has-good,
#b-ok:checked ~ .list-wrap .continent.has-ok,
#b-risky:checked ~ .list-wrap .continent.has-risky,
#b-danger:checked ~ .list-wrap .continent.has-danger,
.list-wrap .continent.has-unknown {
    display: block;
}

/* continent section headings on the by-continent view */
.continent-head {
    margin: 36px 0 8px;
    font-size: 19px;
    text-align: center;
}

/* the shared metric header sits above the first continent; a small gap sets
   the first heading (Europe) off it, not the full inter-continent gap */
.list-wrap .continent:first-of-type .continent-head {
    margin-top: 14px;
}

/* --- scores table --- */
.scores {
    /* separate borders (not collapse) so sticky cells keep theirs */
    border-collapse: separate;
    border-spacing: 0;
    width: 100%;
    margin: 0 auto;
    font-variant-numeric: tabular-nums;
    /* fixed layout so every continent table shares identical column widths */
    table-layout: fixed;
}

/* colgroup sets the widths, so the shared header table and every headerless
   continent table line up: country 30%, metrics split the rest */
.scores .cg-label {
    width: 30%;
}

.scores th:first-child {
    width: 30%;      /* the rest is split equally between the metric columns */
}

/* the one shared header table above the list — its own element, no body rows */
/* the one shared header sticks to the top as you scroll the list, on desktop
   and mobile — so the metric headings are always in view. It's the whole
   table that sticks (a header-only table's th can't span the rows below). */
.scores-head {
    position: sticky;
    top: 0;
    z-index: 6;
    background: var(--bg);
    margin-bottom: 4px;
}


.scores th,
.scores td {
    padding: 11px 10px;
    font-size: 14px;
    text-align: center;
    vertical-align: middle;
}

.scores th {
    border-bottom: 1px solid var(--line);
}

/* zebra striping carries row separation instead of a hairline under every
   row — reads calmer once the score cells are solid colour blocks */
.scores tbody tr:nth-child(even) td {
    background-color: color-mix(in srgb, var(--line) 45%, transparent);
}

/* score cells get less side padding so the swatch below can fill the cell */
.scores td.score {
    padding: 5px 4px;
}

.scores thead th {
    background-color: var(--bg);
    border-bottom: 1px solid var(--line-strong);
    color: var(--muted);
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* metric headings — <button>s the client-side script toggles: click to drop a
   column from the ranking, click again to bring it back. An enabled column
   wears the accent border + inset ring; a disabled one (the .off class the
   script adds) goes a quiet grey. */
.col-toggle {
    display: inline-block;
    padding: 4px 12px;
    font-family: inherit;                       /* it's a <button> now, not a link */
    font-size: 13px;
    font-weight: 600;
    text-transform: none;
    letter-spacing: normal;
    border: 1px solid var(--accent);            /* active: green border + ring */
    border-radius: 999px;
    background-color: var(--surface);
    box-shadow: inset 0 0 0 1px var(--accent);
    color: var(--ink);
    text-decoration: none;
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, color 0.15s ease;
}

.col-toggle.off {
    border-color: var(--line);                  /* inactive: no green */
    box-shadow: none;
    color: var(--muted);
    text-decoration: none;
}

.col-toggle.off:hover {
    border-color: var(--line-strong);
    color: var(--ink);
}

/* --- mobile filter drawer (phones only) ---
   Hidden on desktop, which keeps its legend + header toggle pills untouched.
   The @media block near the bottom of this file reveals the toolbar and slides
   the drawer in with the #drawer checkbox (CSS only, like the verdict radios). */
.toolbar,
.mobile-drawer,
.drawer-backdrop,
.deskrail,
.leftrail {
    display: none;
}

/* the two-column desktop layout gives each continent table its own metric
   header; the single-column / mobile view keeps the one shared scores-head, so
   hide these per-table headers everywhere but the desktop block below */
.scores thead.plainhead {
    display: none;
}

/* a row or continent section the search has filtered out. !important so it wins
   over the band filter's section show-rules — a search miss stays hidden even if
   its band is on (search box lives in the mobile toolbar; desktop is unaffected) */
.search-hide {
    display: none !important;
}

.list-empty {
    margin: 24px auto;
    text-align: center;
    color: var(--muted);
    font-size: 15px;
}

.scores td:first-child,
.scores th:first-child {
    position: sticky;       /* country name stays put on horizontal scroll */
    left: 0;
    z-index: 2;
    background-color: var(--bg);
    text-align: left;
}

.scores thead th:first-child {
    z-index: 4;
}

/* flag leads the name; flex (not text-indent) so the flag can't get
   clipped, and a wrapped long name hangs under its own first letter,
   not under the flag. */
.scores .name-row {
    display: flex;
    align-items: baseline;
    gap: 0.35em;
}

.scores .flag {
    flex: 0 0 auto;
}

.scores .name {
    flex: 1 1 auto;
    min-width: 0;
}

.country-link {
    color: var(--ink);
    text-decoration-color: #4b4f55;
    text-underline-offset: 3px;
    transition: text-decoration-color 0.15s ease;
}

/* the chevron says "this opens" where an underline alone no longer does */
.country-link .name::after {
    content: "\00a0›";
    color: var(--muted);
    transition: color 0.15s ease;
}

/* hovering anywhere in the row (not just the name itself) lights up the
   link — the swatches are the row's colour now, so the name is the one
   thing left to signal "this is what you click" */
.scores tbody tr:hover .country-link {
    text-decoration-color: var(--accent);
}

.scores tbody tr:hover .country-link .name::after {
    color: var(--accent);
}

/* screen-reader only: present in the accessibility tree, invisible on screen */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.pill {
    display: inline-block;
    min-width: 52px;
    padding: 4px 8px;
    border-radius: 6px;
    color: #10130f;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
}

/* index/columns tables: the swatch fills its cell like a heatmap tile and
   carries the country's rank for that metric (1st); the exact value stays a
   hover (title) or a tap-through away. */
.pill.swatch {
    position: relative;
    display: block;
    width: 100%;
    min-width: 0;
    height: 26px;
    padding: 0;
    border-radius: 6px;
}

.pill.swatch .rank {
    display: block;
    height: 100%;
    line-height: 26px;
    text-align: center;
    font-size: 12px;
    font-weight: 600;
}

.pill.swatch .rank sup {
    font-size: 0.65em;
    line-height: 0;              /* keep the sup from stretching the line box */
    vertical-align: super;
}

.pill.best { background-color: var(--best); }
.pill.good { background-color: var(--good); }
.pill.ok { background-color: var(--ok); }
.pill.risky { background-color: var(--risky); }
.pill.danger { background-color: var(--danger); }
.pill.unknown { background-color: var(--unknown); }

/* a column switched off by the toggle script: everything grey, no rank badge,
   and inert (so a greyed swatch can't open the popup) */
.pill.off {
    background-color: var(--unknown);
    color: #a6aab0;
}

.pill.swatch.off .rank { display: none; }
.pill.swatch.off { pointer-events: none; }

/* enabled swatches are links into that metric's ranking page; the ring on
   hover is what says "this one answers a click" */
a.pill.swatch {
    text-decoration: none;
    transition: box-shadow 0.15s ease, filter 0.15s ease;
}

a.pill.swatch:hover {
    box-shadow: 0 0 0 1px var(--bg), 0 0 0 3px var(--line-strong);
    filter: brightness(1.1);
}

/* --- country detail pages --- */
.country-title {
    margin: 0 0 24px;
    color: var(--ink);
    font-size: 30px;
    font-weight: 700;
    text-align: center;
}

.country-blurb {
    max-width: 640px;
    margin: 0 auto 40px;
    font-size: 16.5px;
    line-height: 1.65;
    text-align: center;
}

.country-blurb p {
    margin: 0;
}

.metric {
    position: relative;
    overflow: hidden;
    max-width: 680px;
    margin: 0 auto 22px;
    scroll-margin-top: 16px;     /* landing from an index pill (…#peace) */
    padding: 30px 28px 20px;
    background-color: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

/* the colour band, reduced to a quiet accent bar along the top */
.metric::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
}

.metric.best::before { background-color: var(--best); }
.metric.good::before { background-color: var(--good); }
.metric.ok::before { background-color: var(--ok); }
.metric.risky::before { background-color: var(--risky); }
.metric.danger::before { background-color: var(--danger); }
.metric.unknown::before { background-color: var(--unknown); }

.metric h2 {
    margin: 0 0 4px;
    color: var(--ink);
    font-size: 15px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-align: center;
}

.metric-desc {
    margin: 0 0 22px;
    color: var(--muted);
    font-size: 13.5px;
    text-align: center;
}

.scale {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 0 8px;
}

.scale-min,
.scale-max {
    color: var(--muted);
    font-size: 13px;
    white-space: nowrap;
    text-align: center;
}

.scale-min small,
.scale-max small {
    display: block;
    color: var(--muted);
    font-size: 11px;
}

.scale-line {
    position: relative;
    flex: 1;
    height: 6px;
    /* every scale reads worst (left) -> best (right), so one gradient fits all */
    background: linear-gradient(90deg, var(--danger), var(--risky), var(--ok), var(--good), var(--best));
    border-radius: 3px;
}

.scale-dot {
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 16px;
    height: 16px;
    border: 2px solid var(--ink);
    border-radius: 50%;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55);
}

.scale-dot.best { background-color: var(--best); }
.scale-dot.good { background-color: var(--good); }
.scale-dot.ok { background-color: var(--ok); }
.scale-dot.risky { background-color: var(--risky); }
.scale-dot.danger { background-color: var(--danger); }
.scale-dot.unknown { background-color: var(--unknown); }

.scale-value {
    margin: 0 0 4px;
    font-size: 15px;
    font-variant-numeric: tabular-nums;
    text-align: center;
}

/* the plain-English rank line, under the precise value */
.scale-rank {
    margin: 0 0 16px;
    color: var(--muted);
    font-size: 14px;
    text-align: center;
}

.metric .notes {
    font-size: 15px;
    color: var(--soft-ink);
}

/* --- about page cards --- */
/* each card's top strip is that metric's real distribution of countries
   across the bands (injected by build.py), so every card reads differently */
.metric.about::before {
    content: none;
}

.metric-strip {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
}

/* about cards carry an icon and stand alone (no card below explains them
   further), so the heading runs bigger and further from the description */
.metric.about h2 {
    margin: 4px 0 10px;
    font-size: 20px;
    letter-spacing: 0.02em;
}

.metric.about .metric-desc {
    margin-bottom: 26px;
}

/* the metric's icon, in the same muted voice as the banner fleet */
.metric.about h2 svg {
    width: 22px;
    height: 22px;
    margin-right: 9px;
    vertical-align: -4.5px;
    color: var(--muted);
}

/* one row per band: the pill, then what it actually means */
.threshold-table {
    width: 100%;
    margin: 0 0 14px;
    border-collapse: collapse;
}

.threshold-table td {
    padding: 9px 0;
    border-bottom: 1px solid var(--line);
    color: var(--soft-ink);
    font-size: 14.5px;
    line-height: 1.55;
    text-align: left;
    vertical-align: top;
}

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

.threshold-table .t-pill {
    width: 118px;
    padding-right: 14px;
    white-space: nowrap;
}

/* contact page email call-to-action */
.contact-cta {
    margin: 8px 0 4px;
    text-align: center;
    font-size: 15px;
    font-weight: 600;
}

.contact-cta a {
    display: inline-block;
    padding: 8px 16px;
    border-radius: 7px;
    background: var(--accent);
    color: var(--on-accent);
    text-decoration: none;
}

.contact-cta a:hover {
    background: var(--ink);
    color: var(--bg);
}

/* per-metric source credit, quiet, at the foot of each about card */
.metric-source {
    margin: 16px 0 0;
    padding-top: 14px;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: 12.5px;
    text-align: center;
}

.timestamp {
    margin: 12px 0 0;
    color: var(--muted);
    font-size: 12px;
    text-align: center;
}

.country-nav {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 15px;
    max-width: 680px;
    margin: 36px auto 10px;
    font-size: 15px;
}

/* --- verdict map (map.html) ---
   The verdict highlight is the same hidden-radio trick as the index filters;
   pan/zoom is the small script in map.html driving the svg's viewBox (drag,
   wheel and pinch don't exist in CSS). The svg is vector, so every zoom
   level stays razor sharp. Without JS the map is a fitted, clickable world
   map and the zoom buttons stay hidden. */
.map-frame {
    position: relative;          /* anchors the country popup */
    margin: 0 0 14px;
    background-color: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    overflow: hidden;
}

/* the only chrome over the map: a full-screen toggle in the top-right
   corner. Hidden until the script wires it (.pz), like the old zoom bar. */
.map-full {
    display: none;
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 6;                          /* over the map, under the popup (50) */
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;
    border: 1px solid var(--line-strong);
    border-radius: 8px;
    background-color: color-mix(in srgb, var(--bg) 82%, transparent);
    color: var(--ink);
    font-family: inherit;
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}

.map-frame.pz .map-full {
    display: flex;
}

.map-full:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/* the pan/zoom surface: the svg always fills it, the viewBox does the rest */
.map-scroll {
    overflow: hidden;
    aspect-ratio: 2000 / 993.7;          /* the basemap's frame */
    max-height: 74vh;
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
}

.map-scroll:active {
    cursor: grabbing;
}

.map-scroll .worldmap {
    display: block;
    width: 100%;
    height: 100%;
}

/* the metric toggles, sitting just below the map as the verdict legend sits
   just above it — individual pills, spaced like that legend (not joined into a
   track), and styled exactly like the list-page headings: an enabled column
   wears the accent-washed fill, a disabled one the dashed, struck ghost.
   Hidden without JS (like the full-screen button): they only mean anything
   once the script can recolour the map. */
.map-cols {
    display: none;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    margin: 18px 0 0;         /* mirrors the legend gap above the map */
}

.map-frame.pz ~ .map-cols {
    display: flex;
}

/* the pills are the shared .col-toggle — green border when the metric is on,
   grey when its checkbox is cleared. The checkboxes live up top now, so the
   labels (in .map-cols and in the drawer) are reached by ~ + :is(). */
.map-cols .col-toggle {
    cursor: pointer;
    user-select: none;
}

#c-homicide:not(:checked) ~ :is(.map-cols, .mobile-drawer, .deskrail) [for="c-homicide"],
#c-peace:not(:checked) ~ :is(.map-cols, .mobile-drawer, .deskrail) [for="c-peace"],
#c-freedom:not(:checked) ~ :is(.map-cols, .mobile-drawer, .deskrail) [for="c-freedom"],
#c-lgbt:not(:checked) ~ :is(.map-cols, .mobile-drawer, .deskrail) [for="c-lgbt"] {
    border-color: var(--line);
    box-shadow: none;
    color: var(--muted);
}

#c-homicide:focus-visible ~ :is(.map-cols, .mobile-drawer, .deskrail) [for="c-homicide"],
#c-peace:focus-visible ~ :is(.map-cols, .mobile-drawer, .deskrail) [for="c-peace"],
#c-freedom:focus-visible ~ :is(.map-cols, .mobile-drawer, .deskrail) [for="c-freedom"],
#c-lgbt:focus-visible ~ :is(.map-cols, .mobile-drawer, .deskrail) [for="c-lgbt"] {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* full screen: the whole stage (legend + map + toggles) goes full screen, so
   both pill rows show around the map — the native API where allowed, a fixed
   overlay (.fs) on iPhones. The map frame flexes to take every spare pixel;
   the legend and toggles keep their natural height above and below it. */
.map-stage:fullscreen,
.map-stage.fs {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 12px;
    padding: 16px;
    background-color: var(--bg);
}

.map-stage.fs {
    position: fixed;
    inset: 0;
    z-index: 50;
    overflow: auto;
}

.map-stage:fullscreen .legend,
.map-stage.fs .legend,
.map-stage:fullscreen .map-cols,
.map-stage.fs .map-cols {
    flex: 0 0 auto;
    /* stretch full width so the centred chips land mid-screen — the 1024px
       max-width would otherwise sit the legend left of centre */
    max-width: none;
    margin: 0;
}

.map-stage:fullscreen .map-frame,
.map-stage.fs .map-frame {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
    margin: 0;
    border: none;
    border-radius: 0;
    background-color: var(--bg);
}

.map-stage:fullscreen .map-scroll,
.map-stage.fs .map-scroll {
    flex: 1 1 auto;
    aspect-ratio: auto;
    max-height: none;
    min-height: 0;
}

/* in full screen the frame sits below the legend, so anchor the button to the
   screen corner (fixed = the fullscreen viewport) instead of the frame */
.map-stage:fullscreen .map-full,
.map-stage.fs .map-full {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 60;
}

/* countries wear their verdict colour; unranked territories stay a grey a
   step darker than the unknown band, so they read as background.
   --sw/--dotr/--dotop come from the script: borders thin and the microstate
   dots shrink, then fade, as the zoom grows. */
.worldmap path,
.worldmap .dot {
    stroke: var(--bg);
    stroke-width: var(--sw, 0.5px);
}

.worldmap .terr { fill: #26282c; }
.worldmap .unknown { fill: var(--unknown); }
.worldmap .best { fill: var(--best); }
.worldmap .good { fill: var(--good); }
.worldmap .ok { fill: var(--ok); }
.worldmap .risky { fill: var(--risky); }
.worldmap .danger { fill: var(--danger); }

/* search highlight: the found country wears a bright outline that pulses; the
   script clears it after a moment (or when the next search lands) */
.worldmap .cty.found path,
.worldmap .cty.found .dot {
    stroke: #ffffff;
    stroke-width: calc(var(--sw, 0.5px) * 4);
    animation: found-pulse 1.4s ease-in-out;
}
@keyframes found-pulse {
    0%, 100% { stroke-opacity: 1; }
    50%      { stroke-opacity: 0.25; }
}

/* island microstates: a dot marks (and clicks for) what one pixel can't */
.worldmap .dot {
    r: var(--dotr, 4.5px);
    opacity: var(--dotop, 1);
}

a.cty {
    cursor: pointer;
}

a.cty:hover path,
a.cty:hover .dot,
a.cty:focus-visible path,
a.cty:focus-visible .dot {
    stroke: var(--ink);
    stroke-width: calc(var(--sw, 0.5px) * 2);
    filter: brightness(1.12);
}

a.cty:focus-visible {
    outline: none;
}

/* multi-toggle band highlight: an unchecked band fades its own countries
   (all on by default); territories stay neutral either way */
#m-best:not(:checked) ~ .map-frame .worldmap .cty.best,
#m-good:not(:checked) ~ .map-frame .worldmap .cty.good,
#m-ok:not(:checked) ~ .map-frame .worldmap .cty.ok,
#m-risky:not(:checked) ~ .map-frame .worldmap .cty.risky,
#m-danger:not(:checked) ~ .map-frame .worldmap .cty.danger {
    opacity: 0.15;
}

/* the band chips — the legend on desktop / full screen, the drawer's Rank chips
   on mobile (labels sit in both, so :is() reaches either). Checked wears the
   accent ring, unchecked goes quiet and dimmed, exactly like the list. */
#m-best:checked ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-best"],
#m-good:checked ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-good"],
#m-ok:checked ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-ok"],
#m-risky:checked ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-risky"],
#m-danger:checked ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-danger"] {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
}

#m-best:not(:checked) ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-best"],
#m-good:not(:checked) ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-good"],
#m-ok:not(:checked) ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-ok"],
#m-risky:not(:checked) ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-risky"],
#m-danger:not(:checked) ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-danger"] {
    opacity: 0.4;
}

#m-best:focus-visible ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-best"],
#m-good:focus-visible ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-good"],
#m-ok:focus-visible ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-ok"],
#m-risky:focus-visible ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-risky"],
#m-danger:focus-visible ~ :is(.legend, .mobile-drawer, .deskrail) [for="m-danger"] {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* --- info popup (the map's country card and the index's pill card, filled
   by their pages' inline scripts) --- */
.info-pop {
    width: 224px;
    padding: 14px 16px 12px;
    background-color: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: 10px;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55);
}

.info-pop[hidden] {
    display: none;
}

.map-pop {
    position: absolute;      /* floats inside the map frame */
    z-index: 5;
}

.list-pop {
    position: fixed;         /* floats over the score table */
    z-index: 50;
}

.pop-close {
    position: absolute;
    top: 5px;
    right: 5px;
    width: 26px;
    height: 26px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: none;
    color: var(--muted);
    font-family: inherit;
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
}

.pop-close:hover {
    color: var(--ink);
    background-color: var(--line);
}

.pop-head {
    display: flex;
    align-items: baseline;
    gap: 7px;
    margin: 0 14px 1px 0;
    font-size: 15.5px;
    font-weight: 600;
}

.pop-cont {
    margin: 0 0 10px;
    color: var(--muted);
    font-size: 12.5px;
}

.pop-scores {
    width: 100%;
    border-collapse: collapse;
}

.pop-scores th {
    padding: 3px 0;
    color: var(--muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-align: left;
    text-transform: uppercase;
}

.pop-scores td {
    padding: 3px 0;
    text-align: right;
}

.pop-scores .pill {
    min-width: 64px;
    padding: 2px 8px;
    font-size: 12.5px;
}

/* the index pill card's one-liner: the value pill, then the rank sentence */
.pop-rank {
    margin: 0;
    font-size: 13.5px;
    line-height: 1.55;
}

.pop-rank .pill {
    min-width: 52px;
    margin-right: 6px;
    padding: 2px 8px;
    font-size: 12.5px;
}

.pop-link {
    margin: 10px 0 0;
    font-size: 13.5px;
    text-align: right;
}

/* on a phone the popup tucks into the bottom-left corner (of the map frame
   or the screen), clear of the finger that just tapped — same compact card
   as desktop, not a full-width bar; the scripts skip their pointer
   positioning there */
@media (max-width: 899px) {
    .info-pop {
        top: auto;
        right: auto;
        bottom: 10px;
        left: 10px;
        width: 224px;
        max-width: calc(100% - 20px);
    }
}

/* --- footer --- */
.footer {
    margin-top: 28px;
    padding: 26px 20px 36px;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: 14px;
    text-align: center;
}

.footer nav {
    margin-bottom: 10px;
}

.footer nav a {
    margin: 0 10px;
    color: var(--muted);
    text-decoration: none;
}

.footer nav a:hover {
    color: var(--ink);
}

.footer p {
    margin: 4px 0;
}

.footer a {
    color: var(--muted);
}

.footer a:hover {
    color: var(--ink);
}

/* invisible visit-counter beacon (index only) — counts without showing a badge */
.tracker {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* --- mobile --- */
@media (max-width: 899px) {
    .banner {
        padding: 30px 12px 0;
    }

    /* tighter tracking so the caps fit a portrait phone on one line */
    .banner h1 {
        font-size: 17px;
        letter-spacing: 0.18em;
        text-indent: 0.18em;
    }

    .banner .year {
        margin-left: 9px;
        vertical-align: 2px;
    }

    .banner .tagline {
        font-size: 13px;
    }

    .banner nav a {
        padding: 10px 10px 12px;
        font-size: 14px;
    }

    .page {
        position: relative;          /* anchors the filter popover under the toolbar */
        padding: 28px 14px 10px;
    }

    /* six columns fit a portrait phone: compact cells instead of sideways scroll */
    .scores th,
    .scores td {
        padding: 9px 2px;
        font-size: 13px;
    }

    .scores thead th {
        font-size: 9px;
        letter-spacing: 0.03em;
    }

    /* on a phone the metric toggles move into the filter drawer, so the sticky
       header is just plain labels again — no pill borders to collide with the
       country names, and no taps (toggle from the drawer instead) */
    .scores-head .col-toggle,
    .scores-head .col-toggle.off {
        display: inline;
        padding: 0;
        border: 0;
        box-shadow: none;
        background: none;
        color: var(--muted);
        font: inherit;
        font-weight: 600;
        text-transform: uppercase;
        pointer-events: none;
    }

    .pill.swatch {
        height: 20px;
    }

    .pill.swatch .rank {
        line-height: 20px;
        font-size: 11px;
    }

    .scores td.score {
        padding: 4px 2px;
    }

    .country-title {
        font-size: 24px;
    }

    .metric {
        padding: 26px 18px 16px;
    }

    /* legend chips (index, columns and map): one centred, evenly-gapped row on
       every page — the coloured chips drop their word (dot + count still read
       them) so all six fit; only "All" keeps its label. The flex + gap here is
       what makes the list and map legends space identically. */
    .legend {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        gap: 5px;
    }

    .legend .chip {
        flex: 0 0 auto;
        gap: 5px;
        margin: 0;
        padding: 4px 9px;
    }

    .legend .chip-word {
        display: none;
    }
}

/* --- mobile filter drawer: the toolbar, the slide-in panel and its controls.
   Everything here is phones-only; desktop keeps its legend + header pills. --- */
@media (max-width: 899px) {
    /* the legend becomes the drawer's Rank section on the list pages; the map
       has no drawer, so keep its legend visible there */
    body:not(.mappage) .legend {
        display: none;
    }

    .toolbar {
        display: flex;
        align-items: center;
        gap: 8px;
        margin: 0 0 16px;
    }

    .searchbox {
        flex: 1 1 auto;
        display: flex;
        align-items: center;
        gap: 7px;
        min-width: 0;
        padding: 8px 12px;
        background-color: var(--surface);
        border: 1px solid var(--line);
        border-radius: 999px;
        color: var(--muted);
    }

    .searchbox svg {
        flex: 0 0 auto;
    }

    .list-search,
    .map-search {
        flex: 1 1 auto;
        min-width: 0;
        background: none;
        border: 0;
        outline: none;
        color: var(--ink);
        font: inherit;
        font-size: 13px;
        padding: 0;
    }

    .list-search::placeholder,
    .map-search::placeholder {
        color: var(--muted);
    }

    .btn-filter {
        flex: 0 0 auto;
        display: inline-flex;
        align-items: center;
        gap: 6px;
        padding: 8px 14px;
        background-color: var(--surface);
        border: 1px solid var(--line-strong);
        border-radius: 999px;
        color: var(--ink);
        font-size: 13px;
        font-weight: 600;
        cursor: pointer;
    }

    /* the filter is a compact popover that grows out of the Filter button
       (top-right), not a full-height side sheet. The #drawer checkbox (a ~
       sibling of both) scales it in; a transparent full-screen label sits
       behind it so an outside tap closes it. The container is positioned
       (.page / .map-stage, below) so the popover anchors under the toolbar. */
    .drawer-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 40;
        background: transparent;
        pointer-events: none;
    }

    .mobile-drawer {
        display: block;
        position: absolute;
        top: 40px;                      /* just below the toolbar / Filter button */
        right: 0;
        z-index: 41;
        width: max-content;             /* hug the pills — no empty panel */
        max-width: min(80vw, 260px);
        max-height: 82vh;
        background-color: var(--surface);
        border: 1px solid var(--line-strong);
        border-radius: 14px;
        box-shadow: 0 14px 40px rgba(0, 0, 0, 0.3);
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        transform-origin: top right;    /* expand from the button corner */
        transform: scale(0.9);
        opacity: 0;
        pointer-events: none;
        transition: transform 0.18s cubic-bezier(0.22, 1, 0.36, 1),
                    opacity 0.16s ease;
    }

    /* the list drawer lives in .page (which has top padding), so it sits a
       touch lower than the map's, whose container (.map-stage) starts flush */
    body:not(.mappage) .mobile-drawer {
        top: 66px;
    }

    #drawer:checked ~ .drawer-backdrop {
        pointer-events: auto;
    }

    #drawer:checked ~ .mobile-drawer {
        transform: scale(1);
        opacity: 1;
        pointer-events: auto;
    }

    .drawer-head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 14px 16px;
        border-bottom: 1px solid var(--line);
    }

    .drawer-head strong {
        font-size: 15px;
    }

    .drawer-x {
        cursor: pointer;
        color: var(--muted);
        font-size: 17px;
        line-height: 1;
        padding: 2px 6px;
        user-select: none;
    }

    .mobile-drawer .fgroup {
        padding: 14px 16px;
        border-bottom: 1px solid var(--line);
    }

    .mobile-drawer .fgroup:last-child {
        border-bottom: 0;
    }

    .mobile-drawer h4 {
        margin: 0 0 11px;
        font-size: 10px;
        letter-spacing: 0.12em;
        text-transform: uppercase;
        color: var(--muted);
        font-weight: 600;
    }

    /* Rank chips and Metric toggles: stacked, each sized to its own content */
    .drawer-rank,
    .drawer-metrics {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 9px;
    }

    /* Rank chips reuse the legend look (the .legend-scoped rules don't reach
       here). Colour dot + count; the active one wears the accent ring below. */
    .drawer-rank .chip {
        display: inline-flex;
        align-items: center;
        gap: 7px;
        margin: 0;
        box-sizing: border-box;
        min-width: 128px;               /* one neat width for every pill; the
                                           widest label ("Caution 40") is ~111px */
        padding: 4px 12px;
        background-color: var(--surface);
        border: 1px solid var(--line);
        border-radius: 999px;
        color: var(--ink);
        font-size: 12.5px;
        font-weight: 600;
        cursor: pointer;
        user-select: none;
    }

    .drawer-rank .chip::before {
        content: "";
        width: 9px;
        height: 9px;
        border-radius: 50%;
        background-color: var(--accent);
    }

    .drawer-rank .chip.best::before { background-color: var(--best); }
    .drawer-rank .chip.good::before { background-color: var(--good); }
    .drawer-rank .chip.ok::before { background-color: var(--ok); }
    .drawer-rank .chip.risky::before { background-color: var(--risky); }
    .drawer-rank .chip.danger::before { background-color: var(--danger); }

    .drawer-rank .count {
        margin-left: auto;              /* dot + label stay left, count to the right edge */
        padding-left: 10px;
        color: var(--muted);
        font-weight: 400;
        font-variant-numeric: tabular-nums;
    }

    /* each rank chip is its own toggle (mirrors the legend): checked wears the
       accent ring, unchecked goes quiet and dimmed */
    #b-best:checked ~ .mobile-drawer [for="b-best"],
    #b-good:checked ~ .mobile-drawer [for="b-good"],
    #b-ok:checked ~ .mobile-drawer [for="b-ok"],
    #b-risky:checked ~ .mobile-drawer [for="b-risky"],
    #b-danger:checked ~ .mobile-drawer [for="b-danger"] {
        border-color: var(--accent);
        box-shadow: inset 0 0 0 1px var(--accent);
    }

    #b-best:not(:checked) ~ .mobile-drawer [for="b-best"],
    #b-good:not(:checked) ~ .mobile-drawer [for="b-good"],
    #b-ok:not(:checked) ~ .mobile-drawer [for="b-ok"],
    #b-risky:not(:checked) ~ .mobile-drawer [for="b-risky"],
    #b-danger:not(:checked) ~ .mobile-drawer [for="b-danger"] {
        opacity: 0.4;
    }

    /* Metric toggles keep the shared .col-toggle look (green ring on, grey off),
       but reset the phone header override above back to a real pill */
    .drawer-metrics .col-toggle,
    .drawer-metrics .col-toggle.off {
        display: inline-flex;
        justify-content: center;
        box-sizing: border-box;
        min-width: 128px;               /* same neat width as the Rank chips */
        padding: 6px 14px;
        font: inherit;
        font-size: 12.5px;
        font-weight: 600;
        text-transform: none;
        pointer-events: auto;
    }

    .drawer-metrics .col-toggle {
        border: 1px solid var(--accent);
        box-shadow: inset 0 0 0 1px var(--accent);
        background-color: var(--surface);
        color: var(--ink);
    }

    .drawer-metrics .col-toggle.off {
        border: 1px solid var(--line);
        box-shadow: none;
        color: var(--muted);
    }
}

/* --- the map page on a phone ---
   It scrolls like every other page (same banner, same footer). The map frame
   keeps the basemap's own ~2:1 ratio at every width (the base .map-scroll rule),
   so the map fills it edge to edge instead of floating in a tall fixed slice —
   the same shape as desktop. The map still owns its drags — touch-action:none
   (set in the script) captures pan/zoom, so it never fights the page scroll —
   and the corner button goes truly full screen for an immersive view. */
@media (max-width: 899px) {
    /* phone: the map's inline legend + metric pills always live in the Filter
       popover — outside full screen AND in it (Option A). So hide both rows and
       show the Search + Filter toolbar in every state; only the popover's anchor
       offsets differ between the two. Desktop (outside this media query) is
       untouched: it keeps the floating legend + pills, in and out of full screen. */
    body.mappage .map-stage:not(.fs) {
        position: relative;          /* anchors the popover; .fs keeps position:fixed */
    }
    body.mappage .map-stage .legend,
    body.mappage .map-stage .map-cols {
        display: none;
    }
    body.mappage .map-toolbar {
        display: flex;
    }
    /* in full screen the stage gains 16px of padding and the exit button (⛶)
       floats in the top-right corner, so hold the toolbar clear of it and drop
       the popover a little lower to clear the taller toolbar row */
    body.mappage .map-stage:is(:fullscreen, .fs) .map-toolbar {
        margin: 0 46px 0 0;
    }
    body.mappage .map-stage:is(:fullscreen, .fs) .mobile-drawer {
        top: 58px;
        right: 16px;
    }
}

/* ============================================================================
   DESKTOP (>=900px): the list becomes [ left rail | list | filter rail ]. It
   stays a single column of continents (the sticky metric header comes back),
   flanked by a jump-to-continent nav + at-a-glance on the left and the filters
   on the right. Below 900px the compact layout (toolbar + search + popover,
   single column) runs all the way down to the phone, with no gap. */
@media (min-width: 900px) {
    body.listpage .page {
        max-width: 1240px;
        display: grid;
        grid-template-columns: 184px minmax(0, 1fr) 236px;
        column-gap: 40px;
        align-items: start;
    }
    body.listpage .list-wrap { grid-column: 2; grid-row: 1; }
    body.listpage .list-empty { grid-column: 2; }

    /* the filters live in the right rail now, so drop the old top legend */
    body.listpage .legend { display: none; }

    /* --- the two rails: shared look; each page sets its display + grid slot,
       so neither leaks into the map's full screen (where both stay hidden) --- */
    .deskrail,
    .leftrail {
        flex-direction: column;
        gap: 22px;
        position: sticky;
        top: 20px;
    }
    body.listpage .leftrail { display: flex; grid-column: 1; grid-row: 1; }
    body.listpage .deskrail { display: flex; grid-column: 3; grid-row: 1; }

    /* --- the filter rail --- */
    .deskrail .rail-search {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 9px 13px;
        background-color: var(--surface);
        border: 1px solid var(--line);
        border-radius: 999px;
        color: var(--muted);
    }
    .deskrail .rail-search svg { flex: 0 0 auto; }
    .deskrail .list-search,
    .deskrail .map-search {
        flex: 1 1 auto;
        min-width: 0;
        background: none;
        border: 0;
        outline: none;
        color: var(--ink);
        font: inherit;
        font-size: 13px;
        padding: 0;
    }
    .deskrail .list-search::placeholder,
    .deskrail .map-search::placeholder { color: var(--muted); }

    .deskrail .rail-group,
    .leftrail .rail-group { display: flex; flex-direction: column; gap: 10px; }
    .deskrail h4,
    .leftrail h4 {
        margin: 0;
        font-size: 10px;
        letter-spacing: 0.13em;
        text-transform: uppercase;
        color: var(--muted);
        font-weight: 700;
    }

    /* Rank: the legend chips, stacked — dot + word + count, accent ring when on */
    .deskrail .rail-rank { display: flex; flex-direction: column; gap: 8px; }
    .deskrail .rail-rank .chip {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        margin: 0;
        padding: 6px 13px;
        background-color: var(--surface);
        border: 1px solid var(--line);
        border-radius: 999px;
        color: var(--ink);
        font-size: 13px;
        font-weight: 600;
        cursor: pointer;
        transition: opacity 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
    }
    .deskrail .rail-rank .chip::before {
        content: "";
        width: 9px; height: 9px; border-radius: 50%;
        background-color: var(--accent);
    }
    .deskrail .rail-rank .chip.best::before { background-color: var(--best); }
    .deskrail .rail-rank .chip.good::before { background-color: var(--good); }
    .deskrail .rail-rank .chip.ok::before { background-color: var(--ok); }
    .deskrail .rail-rank .chip.risky::before { background-color: var(--risky); }
    .deskrail .rail-rank .chip.danger::before { background-color: var(--danger); }
    .deskrail .rail-rank .count {
        margin-left: auto;
        color: var(--muted);
        font-weight: 400;
        font-variant-numeric: tabular-nums;
    }
    #b-best:checked ~ .deskrail [for="b-best"],
    #b-good:checked ~ .deskrail [for="b-good"],
    #b-ok:checked ~ .deskrail [for="b-ok"],
    #b-risky:checked ~ .deskrail [for="b-risky"],
    #b-danger:checked ~ .deskrail [for="b-danger"] {
        border-color: var(--accent);
        box-shadow: inset 0 0 0 1px var(--accent);
    }
    #b-best:not(:checked) ~ .deskrail [for="b-best"],
    #b-good:not(:checked) ~ .deskrail [for="b-good"],
    #b-ok:not(:checked) ~ .deskrail [for="b-ok"],
    #b-risky:not(:checked) ~ .deskrail [for="b-risky"],
    #b-danger:not(:checked) ~ .deskrail [for="b-danger"] {
        opacity: 0.4;
    }

    /* Metric: the shared .col-toggle buttons, stacked full-width */
    .deskrail .rail-metrics { display: flex; flex-direction: column; gap: 8px; }
    .deskrail .rail-metrics .col-toggle { width: 100%; text-align: center; }
}

/* ============================================================================
   MAP desktop (>=900px), non-fullscreen: [ map | rail ] — the inline legend +
   metric pills move into the rail, the map fills the rest. Full screen keeps
   its own floating rows (scoped :not(:fullscreen):not(.fs)); phones keep the
   toolbar + popover. Both are untouched. The rail's own look (search, Rank
   chips, Metric labels) is the shared .deskrail styling above. */
@media (min-width: 900px) {
    body.mappage .page { max-width: 1340px; }

    body.mappage .map-stage:not(:fullscreen):not(.fs) {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 236px;
        column-gap: 40px;
        align-items: start;
    }
    body.mappage .map-stage:not(:fullscreen):not(.fs) .legend,
    body.mappage .map-stage:not(:fullscreen):not(.fs) .map-cols {
        display: none;
    }
    body.mappage .map-stage:not(:fullscreen):not(.fs) .map-frame {
        grid-column: 1;
        grid-row: 1;
        margin: 0;
        /* the map is a short ~2:1 shape; when the rail is taller, centre it so
           the leftover height splits evenly and reads as padding, not a gap */
        align-self: center;
    }
    body.mappage .map-stage:not(:fullscreen):not(.fs) .deskrail {
        display: flex;
        grid-column: 2;
        grid-row: 1;
        position: sticky;
        top: 20px;
    }
}

/* a big screen gives the map more room too */
@media (min-width: 1120px) {
    body.mappage .page { max-width: clamp(1340px, 92vw, 1600px); }
}

/* --- desktop list left rail: jump-to-continent nav + at-a-glance spread --- */
@media (min-width: 900px) {
    .leftrail .jump-nav { display: flex; flex-direction: column; }
    .leftrail .jump-nav a {
        display: flex;
        justify-content: space-between;
        gap: 10px;
        padding: 5px 0;
        color: var(--muted);
        font-size: 13px;
        text-decoration: none;
        border-bottom: 1px solid var(--line);
    }
    .leftrail .jump-nav a:last-child { border-bottom: 0; }
    .leftrail .jump-nav a:hover { color: var(--ink); }
    .leftrail .jump-nav .n { color: var(--muted); font-variant-numeric: tabular-nums; }

    /* the at-a-glance card is shared by both rails (list left, map right) */
    .glance {
        display: flex;
        flex-direction: column;
        gap: 10px;
        padding: 12px 13px;
        border: 1px solid var(--line);
        border-radius: 12px;
        background-color: var(--surface);
    }
    .glance-top { display: flex; align-items: baseline; gap: 7px; }
    .glance-big { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
    .glance-k { font-size: 11px; color: var(--muted); }
    .spread {
        display: flex;
        height: 7px;
        border-radius: 4px;
        overflow: hidden;
        background-color: var(--line);
    }
    .spread i { height: 100%; transition: width 0.2s ease; }
    .spread i[data-band="best"] { background-color: var(--best); }
    .spread i[data-band="good"] { background-color: var(--good); }
    .spread i[data-band="ok"] { background-color: var(--ok); }
    .spread i[data-band="risky"] { background-color: var(--risky); }
    .spread i[data-band="danger"] { background-color: var(--danger); }
}

/* full-screen search bar — desktop only. In full screen the rail is hidden, so
   this rides at the top with the floating legend + metric pills. Wired by the
   same script (all .map-search boxes share the one datalist). The compact
   layout's full-screen toolbar carries its own search, so this stays >=900px. */
.fs-search { display: none; }
@media (min-width: 900px) {
    .map-stage:fullscreen .fs-search,
    .map-stage.fs .fs-search {
        display: flex;
        align-items: center;
        gap: 8px;
        flex: 0 0 auto;
        align-self: center;
        width: min(340px, 90%);
        padding: 9px 14px;
        background-color: var(--surface);
        border: 1px solid var(--line-strong);
        border-radius: 999px;
        color: var(--muted);
    }
    .map-stage:fullscreen .fs-search .map-search,
    .map-stage.fs .fs-search .map-search {
        flex: 1 1 auto;
        min-width: 0;
        background: none;
        border: 0;
        outline: none;
        color: var(--ink);
        font: inherit;
        font-size: 14px;
        padding: 0;
    }
    .map-stage:fullscreen .fs-search .map-search::placeholder,
    .map-stage.fs .fs-search .map-search::placeholder { color: var(--muted); }
}
