/* =========================================================================
   Styling for the Sudan & Neighbouring countries dashboard.
   =========================================================================

   Everything visual lives in this one file. It is organised in the same order
   as the page itself: colours first, then header, layout, map, timeline and
   the two side panels.

   The block below is the ONE place colour is defined for this dashboard.
   Everything else in this file, in app.js and in index.html refers back to
   it as var(--name), so a rebrand is an edit to this block and nothing else.
   (app.js reads these same properties at start-up - see readPalette() there.)

   It has three layers, in order:
     1. the IFRC brand palette, exactly as published on brand.ifrc.org
     2. data-mark steps, where a brand colour was too dark or too pale to
        read as a mark and needed a lighter step of the same hue
     3. semantic roles - what this dashboard uses each colour FOR
   ========================================================================= */
:root {
  /* --- 1. IFRC brand palette, verbatim ---------------------------------
     Source: brand.ifrc.org, "Colour" - the six published groups. Do not
     edit these to restyle; change the semantic roles in section 3 instead. */

  /* Primary brand colours */
  --ifrc-red:            #ee2435;
  --ifrc-dark-blue:      #011e41;
  --ifrc-black:          #000000;
  --ifrc-white:          #ffffff;
  /* Climate and environment */
  --ifrc-green-dark:     #009775;
  --ifrc-green-medium:   #00ab84;
  --ifrc-green-light:    #47d7ac;
  /* Health and wellbeing */
  --ifrc-blue-dark:      #0f3a50;
  --ifrc-blue-medium:    #8dcde2;
  --ifrc-blue-light:     #ccf5fc;
  /* Migration and identity */
  --ifrc-orange-medium:  #ff8200;
  --ifrc-orange-light:   #ffb25b;
  /* Values, power and inclusion */
  --ifrc-purple-dark:    #2e1a47;
  --ifrc-purple-medium:  #512d6d;
  --ifrc-purple-light:   #958dbe;
  /* Disasters and crises */
  --ifrc-red-dark:       #d22730;

  /* --- 2. data-mark steps ----------------------------------------------
     The four narrative dimensions, each given the IFRC thematic palette that
     means the same thing. All four are drawn on the map at the same time,
     so they were checked as a set against all pairs, for normal colour vision
     and for the three kinds of colour blindness (protan/deutan/tritan).

     Two are the brand colour unchanged. Two needed a lighter step of the
     same hue: IFRC's purple and blue are both deep and, at their published
     lightness, indistinguishable to a deuteranope (deltaE 3.0 - effectively
     the same colour). Lifting them apart brings that to 8.0, the accepted
     floor when identity is ALSO carried by something other than colour -
     which it is here: every swatch in the legend and the sidebar is labelled
     in text, and every map bubble has a popup naming its dimension. Keep the
     labels if you change these. */
  --dim-hazard:        #d22730;   /* Disasters and crises   - brand, unchanged */
  --dim-vulnerability: #6b3a90;   /* Values/power/inclusion - #512d6d lifted */
  --dim-coping:        #009775;   /* Climate and environment- brand, unchanged */
  --dim-need:          #1874a0;   /* Health and wellbeing   - #0f3a50 lifted */
  /* NOT a dimension any more. Movement patterns was the fifth pillar until the
     Movement trends graph took over saying what this dashboard knows about
     movement - in people rather than in mentions. The token stays because the
     seasonal calendar borrows it below for livestock migration, which is a
     movement and should go on being drawn in the movement colour. */
  --dim-movement:      #ff8200;   /* Migration and identity - brand, unchanged */

  /* The seasonal calendar's bands. ONE COLOUR PER KIND OF SEASON, and the same
     colour in every country - a reader who learns that pale blue is the rains
     in Sudan can then read Chad's strip without consulting a key again. That is
     the whole reason colour is derived from the normalised season_type rather
     than from anything a publisher supplies.

     Four are the agricultural year, which no map pillar describes, so they take
     brand colours of their own. Planting and harvest are deliberately two steps
     of ONE hue: they are consecutive stages of a single cycle, and shading them
     as a sequence says so.

     Three DELIBERATELY borrow a colour from the block above, because they mean
     the same thing the map means by it - a seasonal hazard is a hazard, and the
     lean season is the window when vulnerability peaks. The echo is the point:
     the calendar and the map should not use two different reds for danger. If
     you restyle the dimensions, restyle these with them or the link silently
     breaks. (Livestock keeps --dim-movement, which the map no longer draws -
     see the note on that token.) */
  --season-rain:      var(--ifrc-blue-medium);    /* #8dcde2 water     - brand */
  --season-dry:       var(--ifrc-orange-light);   /* #ffb25b sun/arid  - brand */
  --season-planting:  var(--ifrc-green-light);    /* #47d7ac early growth - brand */
  --season-harvest:   var(--ifrc-green-dark);     /* #009775 mature crop  - brand */
  --season-lean:      var(--dim-vulnerability);   /* borrowed, see above */
  --season-hazard:    var(--dim-hazard);          /* borrowed, see above */
  --season-livestock: var(--dim-movement);        /* borrowed, see above */
  /* A category ACAPS adds that we have not classified yet. Grey on purpose: an
     unclassified band must not be able to pass for a real season. */
  --season-other:     #bdbdbd;

  /* --- 3. semantic roles -----------------------------------------------
     What this dashboard uses each colour for. Restyle here. */
  --bg: #f5f5f5;                        /* the page behind the panels */
  --panel: var(--ifrc-white);
  --panel2: var(--ifrc-white);
  --border: #e0e0e0;
  --text: var(--ifrc-dark-blue);        /* body text - 16.6:1 on white */
  --muted: #6c757d;                     /* secondary text  - 4.7:1 on white */
  --accent: var(--ifrc-red);            /* headings, highlights, major events */
  --header: var(--ifrc-red);            /* the top bar */
  --out: var(--ifrc-orange-medium);     /* movement out of Sudan */
  --return: var(--ifrc-green-dark);     /* movement back into Sudan */
  /* Key events are a separate layer, not a sixth dimension, so what matters
     is that they are distinct from all five above. The disasters red would be
     the obvious choice and is exactly wrong: it IS --dim-hazard, deltaE 0.0.
     IFRC dark blue is 18.8 away from the nearest dimension and is itself a
     primary brand colour. */
  --event: var(--ifrc-dark-blue);       /* key events */
  /* The MCP data layers get their own colours. They are dots in the same
     legend as the four dimensions, so they must be separable from those four,
     from the key-event navy and from each other - every pair above deltaE 8.
     (Influx arrows deliberately have no token of their own: they are movement
     INTO Sudan and share --return with the other return flows.) */
  --idp-locality: var(--ifrc-orange-light);   /* IDP admin2 - Migration family */
  --idp-state:    var(--ifrc-purple-light);   /* IDP admin1 - the coarser aggregate */
  --refugees:     var(--ifrc-blue-medium);    /* host-country totals and camps */
  --hsp:          #7b4ea3;                    /* humanitarian service points */
  /* Returnee localities. Green because --return (dark green) is already
     "movement back into Sudan" for the corridor arrows; this is that same
     movement at its DESTINATION, one step lighter so the two read as related
     and still separate from each other. */
  --returnee:     var(--ifrc-green-medium);   /* returnees by locality (IOM-DTM) */
  /* Rivers and lakes. Not a brand colour: it has to read as water, and it has
     to stay separate from --refugees, which is the other blue on this map. */
  --water:        #1f9ad6;
  --on-header: var(--ifrc-white);       /* text sitting on the header bar */
  /* Panels on the header bar are DARKENED, not lightened: white 10px text
     needs 4.5:1, and lightening the red drops it to 3.6:1. */
  --header-panel: rgba(0,0,0,.15);
  --header-panel-edge: rgba(255,255,255,.38);
  --hover: rgba(0,0,0,.045);
  --hover2: rgba(0,0,0,.07);
}
/* A "reset": browsers add their own spacing to headings, lists and so on.
   Clearing it first means the rules below start from a blank slate. */
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }

/* ---------- --ui-scale: the whole page, drawn one size down --------------
   THE SINGLE KNOB FOR HOW BIG THIS DASHBOARD IS. Every length in this file is
   in px, and they were all chosen while reading the page at 80% browser zoom -
   which is the only size at which the header sits on one line, the sidebar
   panels are all visible without scrolling, and the map has room to be a map.
   Nobody using this dashboard is going to reach for Ctrl+Minus first, so the
   page has to open at that size on its own.

   zoom (not transform: scale) because zoom RE-LAYS-OUT: the header unwraps,
   the map pane genuinely gets wider, and percentage heights still resolve
   against the viewport, so body height:100% keeps filling the screen exactly.
   MapLibre already divides pointer coordinates by getBoundingClientRect().width
   / offsetWidth, so clicks, drags and wheel-zoom land where the cursor is.

   Two things do NOT come along for the ride, because they are measured against
   the real viewport rather than the laid-out page, and both are corrected by
   hand below - search this file for --ui-scale to find them:
     * vh units      - see .hover-popup, which divides by the scale
     * media queries - they cannot read a custom property; the breakpoints at
                       the end of this file carry the multiplication instead
   Setting --ui-scale back to 1 restores the browser's own idea of 100%. The vh
   correction looks after itself when you do (dividing by 1 changes nothing);
   the breakpoints have to be re-multiplied by hand. */
:root { --ui-scale: .8; }

/* The whole page is one vertical column: header, then the three-column
   layout, then the timeline. overflow:hidden stops the page itself from
   scrolling - each panel scrolls on its own instead. */
body {
  zoom: var(--ui-scale);
  font-family: "Open Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  background: var(--bg); color: var(--text);
  display: flex; flex-direction: column; overflow: hidden;
}
h1, h2, h3, .panel h2, .stat-value, #current-date, .theme-group h3 {
  font-family: "Montserrat", "Open Sans", Arial, sans-serif;
}

/* ---------- header: IFRC red bar with the four headline figures -------- */
#topbar {
  display: flex; align-items: center; justify-content: space-between;
  /* NO PADDING ON THE LEFT, and 166px of it instead: the logo plate below is
     taken out of the flex flow and pinned into the corner, so the bar has to
     hold that width open for it (150px of plate + 16px before the title). The
     18px of red that used to sit left of the logo is exactly what made the
     logo look like it was floating in the middle of the bar. */
  padding: 10px 18px 10px 166px; background: var(--header); color: var(--on-header);
  box-shadow: 0 2px 6px rgba(0,0,0,.18);
  gap: 16px; flex-wrap: wrap; z-index: 1000; position: relative;
}
#title-block { display: flex; align-items: center; gap: 16px; }

/* ---------- the logo plate --------------------------------------------
   A LOGO THAT BRINGS ITS OWN BACKGROUND. IFRC's Logo-Horizontal-RGB.svg has a
   viewBox of 841.89 x 380.21 and paints a white rectangle at (94.73, 93.38)
   measuring 652.43 x 193.44 - so the white is 77.50% of the file's width and
   50.88% of its height, centred, with transparent margin all round. Dropped
   straight onto the red bar, that margin shows as red and the white reads as a
   chip hovering in the header rather than as part of it.

   So the plate, not the file, is the thing you see. This box IS white, is
   pinned flush into the top-left corner, and runs the full height of the bar -
   ABSOLUTELY POSITIONED for that last part, because #topbar wraps onto two
   lines at laptop widths and a flex item can only stretch to its own line,
   which left red showing under the logo on exactly the screens most people use.

   Inside it, the image is blown up and pushed left until its white rectangle
   spans the box exactly - width 1/0.7750 = 129.03%, left -(94.73/841.89) of
   that = -14.52%, both measured against the box width, so they hold at any
   header height. Vertically it is simply centred: the box is already the same
   white, so however much of it the logo does not cover reads as margin rather
   than as a seam. */
#ifrc-logo-plate {
  position: absolute; left: 0; top: 0; bottom: 0; width: 150px;
  overflow: hidden; background: var(--ifrc-white);
}
#ifrc-logo {
  position: absolute; left: -14.52%; width: 129.03%; height: auto;
  top: 50%; transform: translateY(-50%);
  max-width: none; display: block;
}
#title-block h1 { font-size: 19px; letter-spacing: 1px; color: var(--on-header); }
#title-block h1 span { color: rgba(255,255,255,.88); font-weight: 400; }
#page-footer {
  font-size: 11.5px; color: var(--muted, rgba(255,255,255,.6)); text-align: center;
  padding: 7px 18px; background: var(--panel); border-top: 1px solid var(--border);
}
#stat-cards { display: flex; gap: 10px; flex-wrap: wrap; }
/* A filtered card is not the published headline figure, so it says so. */
.stat-filtered { border-style: dashed; }
.stat-filtered .stat-label::after {
  content: " (filtered)"; opacity: .75; font-style: italic; text-transform: none;
}
.stat {
  background: var(--header-panel); border: 1px solid var(--header-panel-edge); border-radius: 20px;
  padding: 5px 16px; min-width: 124px; text-align: center;
}
.stat-value { font-size: 20px; font-weight: 800; color: var(--on-header); font-variant-numeric: tabular-nums; }
.stat-return .stat-value { color: var(--on-header); }
.stat-label { font-size: 10px; color: var(--on-header); text-transform: uppercase; letter-spacing: .4px; }

/* ---------- layout: left sidebar | map | right sidebar ----------------
   #layout is a flex row. The two sidebars have a fixed width and the map
   takes whatever is left (flex: 1). min-height/min-width: 0 are the small
   incantations that let a flex child scroll instead of stretching forever.
   ---------------------------------------------------------------------- */
#layout { flex: 1; display: flex; min-height: 0; }
#sidebar {
  width: 320px; min-width: 320px; background: var(--panel);
  border-right: 1px solid var(--border); overflow-y: auto; padding: 12px;
  display: flex; flex-direction: column; gap: 12px;
  box-shadow: 2px 0 6px rgba(0,0,0,.05);
}
.panel { background: var(--panel2); border: 1px solid var(--border); border-radius: 10px; padding: 12px; }
.panel h2 {
  font-size: 11px; text-transform: uppercase; letter-spacing: 1px;
  color: var(--muted); margin-bottom: 10px; font-weight: 700;
}

/* The layer switches. The real checkbox is hidden and a small sliding pill
   is drawn in its place, which is why there are so many rules here. */
.layer-toggle { display: flex; align-items: center; gap: 9px; font-size: 13px; padding: 5px 0; cursor: pointer; }
input.switch {
  appearance: none; width: 34px; height: 18px; border-radius: 10px;
  background: #cfd6df; position: relative; cursor: pointer; flex-shrink: 0;
  transition: background .18s;
}
input.switch::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px;
  border-radius: 50%; background: var(--ifrc-white); transition: left .18s; box-shadow: 0 1px 2px rgba(0,0,0,.25);
}
input.switch:checked { background: var(--return); }
input.switch:checked::after { left: 18px; background: var(--ifrc-white); }
.swatch { display: inline-block; }
.arrow-swatch { font-weight: 800; font-size: 15px; line-height: 1; }
.dot-swatch { width: 10px; height: 10px; border-radius: 50%; }

/* ---------- the country filter, first panel in the sidebar --------------
   It governs every other panel, so it sits above the layer switches. The
   dropdown itself is the shared component below; only the chips and the
   one-line description of what the map is doing are specific to it. */
#filter-bar { display: flex; flex-direction: column; gap: 8px; }
#country-filter .dd { margin-bottom: 0; }

/* The chips: one per selected country, each removable, plus a Clear all. */
#country-chips { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
#country-chips:empty { display: none; }
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--accent); color: var(--ifrc-white);
  border: none; border-radius: 12px; cursor: pointer;
  font-family: inherit; font-size: 10.5px; font-weight: 700;
  letter-spacing: .3px; padding: 3px 9px;
}
.chip span { font-size: 13px; line-height: 1; opacity: .7; }
.chip:hover span { opacity: 1; }
.chip-clear { background: transparent; color: var(--muted); border: 1px solid var(--border); }
.chip-clear:hover { background: var(--hover); color: var(--text); }

/* Says in words what the map is doing: "Split view: Chad | Egypt". */
#filter-mode {
  font-size: 10px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--muted); line-height: 1.4;
}
/* The dropdown panel must be allowed to hang outside this panel's box. */
#country-panel { overflow: visible; }

/* ---------- the dropdown filter -----------------------------------------
   ONE component, used five times: the country filter in the header and the
   four theme dimensions in the sidebar. app.js builds the markup
   (makeDropdown); everything below is only how it looks.

   The panel is absolutely positioned and revealed by the class "open", which
   app.js puts on .dd. Its parent must therefore not clip it - see the
   overflow rules on #themes-panel and #filter-bar further down.
   ------------------------------------------------------------------------ */
.dd { position: relative; margin-bottom: 6px; }
.dd-trigger {
  display: flex; align-items: center; gap: 7px; width: 100%; text-align: left;
  background: var(--panel2); border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); font-family: inherit; font-size: 12.5px;
  padding: 7px 10px; cursor: pointer; transition: border-color .15s, background .15s;
}
.dd-trigger:hover { background: var(--hover); }
.dd.open > .dd-trigger { border-color: var(--accent); background: var(--hover); }
.dd.has-selection > .dd-trigger { border-color: var(--accent); }
.dd-label { white-space: nowrap; }
.dd-value {
  margin-left: auto; color: var(--muted); font-size: 11px;
  max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dd-value.on { color: var(--accent); font-weight: 700; }
.dd-caret { color: var(--muted); font-size: 10px; flex-shrink: 0; }
.dd.open .dd-caret { transform: rotate(180deg); }
.dd-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }

.dd-panel {
  display: none; position: absolute; z-index: 1200; top: calc(100% + 4px); left: 0;
  width: 100%; min-width: 232px; background: var(--panel);
  border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 22px rgba(0,0,0,.18); overflow: hidden;
}
.dd.open .dd-panel { display: block; }
.dd-search { padding: 8px 8px 6px; border-bottom: 1px solid var(--border); }
.dd-search input, .panel-search input {
  width: 100%; box-sizing: border-box; font-family: inherit; font-size: 12px;
  padding: 6px 9px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--panel2); color: var(--text);
}
.dd-search input:focus, .panel-search input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.dd-actions {
  display: flex; align-items: center; gap: 8px; padding: 6px 8px;
  border-bottom: 1px solid var(--border);
}
.dd-actions button {
  font-family: inherit; font-size: 10.5px; text-transform: uppercase; letter-spacing: .4px;
  background: none; border: none; color: var(--accent); cursor: pointer; padding: 2px;
  font-weight: 700;
}
.dd-actions button:hover { text-decoration: underline; }
.dd-shown { margin-left: auto; font-size: 10px; color: var(--muted); }
.dd-list { max-height: 240px; overflow-y: auto; padding: 4px; }
.dd-opt {
  display: flex; align-items: center; gap: 8px; font-size: 12px;
  padding: 5px 6px; border-radius: 5px; color: var(--text);
}
.dd-opt:hover { background: var(--hover); }
.dd-opt.on { background: var(--hover2); }
.dd-opt .dd-cb { accent-color: var(--accent); cursor: pointer; flex-shrink: 0; margin: 0; }
/* Clicking the NAME picks only this one; clicking the box adds or removes it. */
.dd-opt .dd-text { flex: 1; cursor: pointer; }
.dd-opt .dd-text:hover { text-decoration: underline; text-decoration-style: dotted; }
.dd-empty { font-size: 11.5px; color: var(--muted); padding: 10px 8px; }

/* The theme dropdowns' panels must be allowed to hang outside their panel. */
#themes-panel { overflow: visible; }
#dim-groups { position: relative; }

#theme-detail { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.theme-group h3 { font-size: 12px; color: var(--accent); margin: 6px 0 4px; }
.tag-row {
  display: flex; justify-content: space-between; gap: 8px; font-size: 12px;
  padding: 3px 6px; border-radius: 4px; cursor: pointer;
}
.tag-row:hover { background: var(--hover); }
.tag-row .cnt { color: var(--muted); font-variant-numeric: tabular-nums; }
.tag-snippet {
  font-size: 11px; color: var(--muted); border-left: 2px solid var(--accent);
  padding: 4px 8px; margin: 2px 0 6px; font-style: italic; display: none;
}
.tag-snippet.open { display: block; }

/* The timeline list. Events extracted automatically are shown in grey
   italics so a reader can tell them apart from hand-checked milestones. */
#event-list { display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; }
.event-item {
  border-left: 3px solid var(--event); padding: 5px 9px; background: var(--hover);
  border-radius: 0 6px 6px 0; cursor: pointer; transition: background .15s;
}
.event-item:hover { background: var(--hover2); }
.event-item.major { border-left-color: var(--accent); }
.event-item .ev-date { font-size: 10px; color: var(--muted); }
.event-item .ev-title { font-size: 12px; line-height: 1.35; }
.event-item.extracted .ev-title { color: var(--muted); font-style: italic; }

/* ---------- the map, its event banner and its popups ------------------- */
/* A column, not a stack of overlays: the map panes take the height that is
   going and the legend sits under them as the map's bottom edge. position is
   still relative because the event banner is absolutely placed against it. */
#map-wrap { flex: 1; position: relative; min-width: 0; display: flex; flex-direction: column; }
/* One pane normally. When app.js adds "split" to #map-wrap the second pane
   appears and the two share the width, each with the country name written
   across its top. The gap is a hairline of page background, which reads as a
   divider without needing a border on either map. */
#map-panes { position: relative; flex: 1; min-height: 0; display: flex; gap: 0; }
.map-pane { position: relative; flex: 1; min-width: 0; }
.map-canvas { position: absolute; inset: 0; }
#pane-b { display: none; }
#map-wrap.split #pane-b { display: block; }
#map-wrap.split #map-panes { gap: 2px; background: var(--border); }
.pane-label {
  display: none; position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
  z-index: 5; background: var(--header); color: var(--on-header);
  font-family: "Montserrat", "Open Sans", Arial, sans-serif;
  font-size: 11px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  padding: 4px 14px; border-radius: 12px; box-shadow: 0 2px 6px rgba(0,0,0,.25);
  pointer-events: none; white-space: nowrap;
}
#map-wrap.split .pane-label { display: block; }
/* Panning is switched off in split view, so the grab hand would be a lie. */
.pan-locked .maplibregl-canvas-container.maplibregl-interactive,
.pan-locked .maplibregl-canvas-container.maplibregl-interactive:active { cursor: default; }
#event-banner {
  position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
  max-width: 620px; background: rgba(255,255,255,.97); border: 1px solid var(--event);
  border-radius: 10px; padding: 10px 16px; font-size: 13px; z-index: 5; color: var(--text);
  box-shadow: 0 4px 20px rgba(0,0,0,.18);
}
#event-banner .eb-date { color: var(--event); font-size: 11px; font-weight: 700; }
#event-banner.hidden { display: none; }
.maplibregl-popup-content {
  background: var(--ifrc-white) !important; color: var(--text) !important;
  border: 1px solid var(--border); border-radius: 8px !important; font-size: 12px;
  max-width: 300px; box-shadow: 0 4px 16px rgba(0,0,0,.18);
}
.maplibregl-popup-tip { border-top-color: var(--ifrc-white) !important; border-bottom-color: var(--ifrc-white) !important; }
/* ---------- the combined detail bubble ----------------------------------
   One bubble carries a section per layer under the cursor. While it merely
   follows the mouse it must NOT take the pointer: moving onto it would count
   as leaving the shape underneath, and it would flicker in and out.

   Clicking pins it. A pinned bubble is a thing you read rather than a thing
   that follows you, so it takes the pointer back, gains a close button and
   scrolls - which is the whole point when five layers overlap. */
.hover-popup { pointer-events: none; }
.hover-popup .maplibregl-popup-content {
  box-shadow: 0 4px 14px rgba(0,0,0,.22);
  /* 46% of the window's height. Divided by --ui-scale because vh is measured
     against the real viewport and then shrunk with the rest of the page - left
     as a bare 46vh it would cap the bubble at 37% of the screen. */
  max-height: calc(46vh / var(--ui-scale)); overflow: hidden; padding: 10px 12px;
}
.hover-popup.pinned { pointer-events: auto; }
.hover-popup.pinned .maplibregl-popup-content {
  overflow-y: auto; box-shadow: 0 10px 28px rgba(0,0,0,.3);
  border: 1px solid var(--accent); padding-top: 22px;
}
.pp-close {
  position: absolute; top: 4px; right: 6px; z-index: 2;
  background: none; border: none; cursor: pointer;
  font-size: 17px; line-height: 1; color: var(--muted); padding: 2px 5px;
}
.pp-close:hover { color: var(--accent); }
/* How many layers are stacked here - shown only when there is more than one. */
.pp-count {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .6px;
  color: var(--muted); margin-bottom: 6px;
}
.pp-sec + .pp-sec { border-top: 1px solid var(--border); margin-top: 8px; padding-top: 8px; }
.pp-title {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .6px;
  color: var(--muted); font-weight: 700; margin-bottom: 3px;
}
.pp-head { margin-bottom: 5px; }
.pp-sub { display: block; font-size: 10.5px; color: var(--muted); font-weight: 400; }
/* One block per pillar, edged in that pillar's colour so the bubble and the
   bands on the map are colour-matched. */
.pp-dim { border-left: 3px solid; padding: 2px 0 2px 7px; margin: 6px 0; }
.pp-dim-h { font-size: 11px; font-weight: 700; display: flex; justify-content: space-between; gap: 8px; }
.pp-row { display: flex; justify-content: space-between; gap: 10px; font-size: 11.5px; padding: 1px 0; }
.pp-n { color: var(--muted); font-variant-numeric: tabular-nums; font-weight: 400; }
.pp-quote { font-size: 10.5px; font-style: italic; color: var(--muted); margin-top: 3px; line-height: 1.4; }
/* A labelled fact: "Hazard", "Sub-hazard", "Where", "When" in the gutter and
   the answer beside it. The gutter is a fixed width so the answers line up
   down the bubble however many hazards are stacked in it. */
.pp-kv { display: flex; gap: 8px; font-size: 11.5px; padding: 1px 0; align-items: baseline; }
.pp-k {
  flex: 0 0 78px; color: var(--muted); font-weight: 700; white-space: nowrap;
  font-size: 9px; text-transform: uppercase; letter-spacing: .5px;
}
.pp-v { flex: 1 1 auto; font-weight: 600; min-width: 0; }
/* What a dated event actually carries. Indented one step rather than aligned
   under the value column: the bubble is capped at 330px, and an 86px gutter
   left the impact figures wrapping every line. */
.pp-detail {
  font-size: 10.5px; color: var(--muted); line-height: 1.45;
  margin: 1px 0 6px 14px;
}
.pp-quote.pp-linked { margin-left: 14px; }
.pp-water { font-size: 10.5px; color: var(--water); margin-top: 6px; }
/* Says plainly what the numbers are, so a mention count is never read as a
   population figure. */
.pp-note { font-size: 9.5px; color: var(--muted); margin-top: 7px; line-height: 1.4; }
.pp-hint {
  font-size: 9px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--muted); margin-top: 7px; text-align: right; opacity: .8;
}

/* ---------- the timeline: play button, slider and the date -------------
   The trend chart used to be drawn on a canvas behind the slider. It is its
   own panel in the right sidebar now (#trends-graph-panel), which is why this
   bar has nothing in it but the controls for moving through time.
   ---------------------------------------------------------------------- */
#timebar {
  display: flex; align-items: center; gap: 14px; padding: 10px 18px 14px;
  background: var(--panel); border-top: 1px solid var(--border);
}
#play-btn {
  width: 44px; height: 44px; border-radius: 50%; border: 2px solid var(--accent);
  background: transparent; color: var(--accent); font-size: 16px; cursor: pointer;
  flex-shrink: 0; transition: all .15s;
}
#play-btn:hover { background: var(--accent); color: #fff; }
/* The two date pickers that bound the slider. They sit in the bar rather than
   in a panel because they change what the slider IS, not what the map draws,
   and a control that reshapes the timeline belongs next to it. Fixed width so
   the slider does not jump a few pixels when a date goes from 1 to 11. */
.range-field {
  display: flex; flex-direction: column; gap: 2px; flex-shrink: 0;
  font-size: 10px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.range-field input[type="date"] {
  width: 128px; padding: 4px 6px; border: 1px solid var(--border); border-radius: 5px;
  background: var(--bg); color: var(--accent);
  font: 600 12px "Open Sans", Arial, sans-serif; font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.range-field input[type="date"]:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
#slider-wrap { flex: 1; position: relative; }
#time-slider { width: 100%; accent-color: var(--accent); cursor: pointer; display: block; }
#slider-ticks { display: flex; justify-content: space-between; font-size: 10px; color: var(--muted); margin-top: 1px; }
#current-date {
  min-width: 110px; text-align: center; font-size: 15px; font-weight: 700;
  color: var(--accent); font-variant-numeric: tabular-nums;
}

/* ---------- right panel: insights, quotes and hazard bars -------------- */
#rightbar {
  width: 290px; min-width: 290px; background: var(--panel);
  border-left: 1px solid var(--border); overflow-y: auto; padding: 12px;
  display: flex; flex-direction: column; gap: 12px;
  box-shadow: -2px 0 6px rgba(0,0,0,.05);
}
.insight {
  display: flex; gap: 9px; font-size: 12.5px; line-height: 1.45;
  padding: 8px 9px; background: var(--hover); border-radius: 8px; margin-bottom: 6px;
}
.insight .ico { font-size: 15px; }
.insight b { color: var(--accent); }
.insight.good b { color: var(--return); }
.voice {
  border-left: 3px solid var(--border); padding: 6px 10px; margin-bottom: 8px;
  font-size: 12px; font-style: italic; color: var(--muted); line-height: 1.45;
}
.voice .v-meta { font-style: normal; font-size: 10.5px; color: var(--muted); margin-top: 3px; }
/* One badge per coding on a community/API card - a statement can carry
   several themes and each needs its own dimension colour, unlike the single
   "Dimension · tag" text an analyst snippet still prints inline. */
.v-chip {
  display: inline-block; border: 1px solid var(--border); border-radius: 9px;
  padding: 0 6px; margin: 2px 3px 0 0; font-size: 10px; line-height: 1.6;
}
.v-chip-uncoded { color: var(--muted); font-style: italic; }
/* A search result is clickable - it takes the timeline to that month. */
.voice-hit { cursor: pointer; transition: background .15s; }
.voice-hit:hover { background: var(--hover); }
.voice mark { background: rgba(255,130,0,.3); color: inherit; border-radius: 2px; padding: 0 1px; }
.voices-empty { font-size: 12px; color: var(--muted); }

/* Search boxes that sit inside a panel heading, and the small grey count
   beside a heading that says how many results a search found. */
.panel-search { margin-bottom: 8px; }
.panel-count { float: right; font-size: 10px; color: var(--muted); font-weight: 400; text-transform: none; letter-spacing: 0; }
/* Shown when a panel has nothing to say under the current filter. */
.panel-note { font-size: 11.5px; color: var(--muted); line-height: 1.45; }
.trigger-row { margin-bottom: 7px; }
.trigger-row .t-label { display: flex; justify-content: space-between; font-size: 11.5px; margin-bottom: 3px; }
.trigger-row .t-label .t-n { color: var(--muted); }
.trigger-bar { height: 6px; border-radius: 3px; background: rgba(0,0,0,.08); overflow: hidden; }
.trigger-bar > div { height: 100%; border-radius: 3px; background: var(--out); }

/* ---------- the disclaimer chip in the map's bottom-left corner --------
   A small red pill over the map that opens a paragraph when it is hovered,
   focused or tapped. The wording is in index.html, not here and not in
   app.js.

   Two things in here are deliberate and easy to undo by accident:

     * The gap between the panel and the chip is the panel's own transparent
       padding-bottom, NOT a margin. A margin would leave a dead strip that
       the pointer crosses on its way up, closing the panel before it could
       be read.

     * The whole thing is one hover target, #map-disclaimer, which is exactly
       the size of the chip until the panel opens; the panel is absolutely
       placed out of the flow, so a closed disclaimer never covers the map
       beyond its own pill. */
#map-disclaimer {
  position: absolute; bottom: 14px; left: 14px;
  /* Above the legend (4) and the pane labels and event banner (5). */
  z-index: 6;
}
.disclaimer-chip {
  display: flex; align-items: center; gap: 6px;
  background: var(--accent); color: var(--ifrc-white);
  border: none; border-radius: 14px; padding: 5px 12px 5px 9px;
  font-family: "Open Sans", Arial, sans-serif; font-size: 11.5px; font-weight: 600;
  letter-spacing: .2px; cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,.28);
}
.disclaimer-chip svg { width: 15px; height: 15px; fill: currentColor; flex-shrink: 0; }
.disclaimer-panel {
  position: absolute; bottom: 100%; left: 0;
  /* A flat width, deliberately not a vw one: a media query and a vw unit are
     both measured against the REAL viewport, and this page is laid out at
     1/--ui-scale of it, so "46vw" here would be 57 layout-vw and would not
     hold the panel where it was told to. 320px is narrower than the map at
     every width the page is used at. */
  width: 320px;
  /* The bottom padding is the gap to the chip - see the note above. */
  padding-bottom: 8px;
  visibility: hidden; opacity: 0; transition: opacity .12s ease, visibility .12s;
}
.disclaimer-panel p {
  margin: 0; background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px;
  font-size: 11.5px; line-height: 1.5;
  box-shadow: 0 6px 22px rgba(0,0,0,.22);
}
#map-disclaimer:hover .disclaimer-panel,
#map-disclaimer:focus-within .disclaimer-panel { visibility: visible; opacity: 1; }

/* ---------- the legend strip along the bottom of the map --------------- */
/* The chip above floats over this strip's top edge; if either moves, check
   they still clear one another.
   It used to float over the map as a card, which covered whatever was beneath
   it - and what is beneath the middle-bottom of this map is southern Sudan.
   It is now the map's bottom boundary: a full-width strip in the flow, so it
   hides nothing and the map ends where the legend begins. It wraps to as many
   rows as it needs and the map gives up that height. */
#map-legend {
  flex-shrink: 0;
  display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; align-items: center;
  background: var(--panel); border-top: 1px solid var(--border);
  padding: 7px 14px; font-size: 11px; color: var(--muted); z-index: 4;
}
.leg { display: flex; align-items: center; gap: 5px; }
.leg-arrow { font-weight: 800; letter-spacing: -1px; }
.leg-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
/* A pillar is drawn as a FILLED area with a dark hairline, so its key is a
   filled box with the same hairline - it used to be a hollow ring, which was
   right when the pillars were drawn as bands around an area and became a lie
   the moment they became fills. */
.leg-band {
  width: 11px; height: 11px; border-radius: 2px; display: inline-block;
  border: 0.5px solid rgba(14, 17, 22, .55);
}
.leg-tint { width: 11px; height: 11px; border-radius: 2px; display: inline-block;
            background: rgba(1,30,65,.18); }
.leg-water { width: 14px; height: 3px; border-radius: 2px; display: inline-block;
             background: var(--water); box-shadow: 0 0 4px var(--water); }

/* ---------- the Movement trends graph and its key ---------------------
   The canvas is sized in JS from its own offsetWidth. The height is stated
   here as well as in JS on purpose: without it the canvas keeps its default
   300x132 aspect ratio until the first draw, so the panel would be laid out
   short and then jump taller a frame later. Keep this in step with CSS_H in
   drawMovementTrends().
   ---------------------------------------------------------------------- */
/* Taller than it was, to pay for the axis gutters - see TREND_CSS_H in
   app.js, which MUST match this height. Crosshair because the graph is
   clickable: clicking it moves the timeline, and the whole page with it. */
#movement-trends { width: 100%; height: 176px; display: block; cursor: crosshair; }
#trend-key { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.tk-row { display: flex; align-items: center; gap: 7px; font-size: 11.5px; }
/* A short bar rather than a dot: it is a LINE on the graph, and the key should
   look like the thing it points at. */
.tk-line { width: 14px; height: 3px; border-radius: 2px; flex-shrink: 0; }
.tk-label { color: var(--muted); flex: 1; min-width: 0; }
/* Tabular figures so the numbers line up as the timeline moves and they change
   width - a column that jitters is hard to read a trend off. */
.tk-val { font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.graph-note { font-size: 9.5px; color: var(--muted); margin-top: 8px; line-height: 1.4; }

/* stat trend arrows */
.stat-value .trend { font-size: 12px; margin-left: 4px; vertical-align: 2px; }

/* responsive
   THE BREAKPOINTS CARRY --ui-scale MULTIPLIED IN, because a media query is
   evaluated against the real viewport and cannot read a custom property. What
   these two rules are really asking is "is there room for the right sidebar /
   for both sidebars", and the page is laid out at 1/--ui-scale the viewport's
   width, so a 1024px window has 1280px of room in it. The widths below are the
   layout widths these rules were written for, times .8:
     1280 -> 1024   the right sidebar no longer fits
      900 ->  720   neither sidebar fits
   Change --ui-scale and these have to be re-multiplied by hand. */
@media (max-width: 1024px) {
  /* The grip goes with the panel it resizes - otherwise it is left floating
     over the map with nothing behind it to drag. */
  #rightbar, .side-grip[data-for="rightbar"] { display: none; }
}
@media (max-width: 720px) {
  #sidebar, .side-grip[data-for="sidebar"] { display: none; }
  #stat-cards .stat { min-width: 100px; }
  /* Two maps side by side need width there is not any of, so stack them. */
  #map-wrap.split #map-panes { flex-direction: column; }
}

/* --- MCP data layers: toggles, seasonal calendar --- */
.swatch { width: 16px; height: 10px; border-radius: 2px; flex-shrink: 0; }
.dot-swatch { width: 10px; height: 10px; border-radius: 50%; }
.layer-sep {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: 1px;
  color: var(--muted); margin: 10px 0 4px; padding-top: 8px; border-top: 1px solid var(--border);
}
.src-tag {
  font-size: 8.5px; background: #f0f0f0; color: var(--muted);
  border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; margin-left: 6px;
  letter-spacing: .5px; vertical-align: middle;
}
#seasonal-cal { display: flex; flex-direction: column; gap: 2px; }
.cal-row { display: grid; grid-template-columns: 96px repeat(12, 1fr); gap: 2px; align-items: center; }
.cal-label { font-size: 10px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-cell { height: 12px; border-radius: 2px; background: #ececec; font-size: 8px; color: var(--muted);
  text-align: center; line-height: 12px; }
.cal-head .cal-cell { background: transparent; }

/* A month this event is IN. The colour comes from the season's kind, so it is
   the same in every country - see the --season-* block at the top. */
.cal-cell[data-season="Rainy"]     { background: var(--season-rain); }
.cal-cell[data-season="Dry"]       { background: var(--season-dry); }
.cal-cell[data-season="Planting"]  { background: var(--season-planting); }
.cal-cell[data-season="Harvest"]   { background: var(--season-harvest); }
.cal-cell[data-season="Lean"]      { background: var(--season-lean); }
.cal-cell[data-season="Hazard"]    { background: var(--season-hazard); }
.cal-cell[data-season="Livestock"] { background: var(--season-livestock); }
.cal-cell[data-season="Other"]     { background: var(--season-other); }

/* The month the timeline is in. It used to be a red outline, which disappeared
   the moment it landed on the red Hazard band - exactly the month a reader is
   most likely to be looking at. Two tones instead: a white inner hairline and a
   dark outer one, so one of the pair contrasts against every band above and
   against the empty grey of a month with nothing in it. */
.cal-now {
  outline: 1.5px solid var(--ifrc-dark-blue); outline-offset: -1px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .9);
}

/* IPC legend swatches */
.leg-ipc { display: flex; align-items: center; gap: 4px; }
.ipc-sw { display: inline-block; width: 11px; height: 11px; border-radius: 2px; margin-left: 2px; }

/* The seasonal panel now carries one strip per selected country, so each needs
   a heading. Small and quiet: the strip itself is the thing to read. */
.cal-country {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px;
  color: var(--ink); margin: 8px 0 3px;
}
.cal-country:first-child { margin-top: 0; }
#seasonal-panel .panel-note { margin-top: 8px; }

/* ACAPS groups its events, and the grouping is most of what makes the calendar
   readable - so the group headings are visible but quiet, and the strip stays
   the thing being read. */
.cal-group {
  font-size: 9px; text-transform: uppercase; letter-spacing: .7px;
  color: var(--muted); margin: 6px 0 2px; padding-top: 4px;
  border-top: 1px dashed var(--border);
}
.cal-src {
  font-size: 8.5px; font-weight: 600; letter-spacing: .5px;
  color: var(--muted); border: 1px solid var(--border); border-radius: 4px;
  padding: 1px 5px; margin-left: 6px; text-transform: none;
}
/* An event that applies to named regions rather than the whole country. The
   dotted underline is the hint that the tooltip names them. */
.cal-label.cal-partial { border-bottom: 1px dotted var(--muted); cursor: help; }

/* The key to the bands. ONE per panel, under all the country strips rather
   than repeated above each - the colours mean the same thing in all of them,
   which is the point being made. */
.cal-legend {
  display: flex; flex-wrap: wrap; gap: 3px 10px;
  margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border);
}
.cal-key { display: flex; align-items: center; gap: 4px; font-size: 9.5px; color: var(--muted); }
.cal-key i {
  width: 14px; height: 9px; border-radius: 2px; flex-shrink: 0;
  display: inline-block; font-style: normal;
}
#seasonal-panel .panel-note a { color: var(--ifrc-red, #d22730); }

/* ---------- resizable sidebars ----------------------------------------
   The panels hold long place names, quotes and seasonal descriptions, and at
   320px those wrap hard. Each sidebar can be dragged wider; the width it
   starts at is the width it has always had, so nothing moves until someone
   decides it should.

   The width lives in a custom property rather than on the element, because
   both the panel and its drag handle need it and CSS should only be told once.
   -------------------------------------------------------------------------- */
:root { --sidebar-w: 320px; --rightbar-w: 290px; }

#sidebar  { width: var(--sidebar-w);  min-width: var(--sidebar-w); }
#rightbar { width: var(--rightbar-w); min-width: var(--rightbar-w); }

/* The handle is a zero-width flex item sitting between a panel and the map,
   a sibling of the panel rather than a child of it. That is the whole trick,
   and it is worth saying why: inside a panel that scrolls, an absolutely
   positioned handle is laid out against the padding box at the scroll origin,
   so it is one screenful tall and it scrolls away with the content. The left
   sidebar is the one with enough in it to scroll, which is why its grip was
   the one that stopped answering partway down. As a sibling of the panel the
   handle is stretched by the flex row itself: full height, at any scroll
   position, on both sides from the same rule.

   Width zero so it takes nothing from the layout. The 6px hit area is drawn
   by ::before, centred on the seam and overhanging three pixels into each
   neighbour - which it can do here, where the old handle could not: a panel
   with overflow-y: auto also clips horizontally, so the overhang inside the
   sidebar was never really there and the target was half the width it claimed.
   Invisible until pointed at; a visible divider would be one more line on a
   page that already has plenty. */
.side-grip {
  flex: 0 0 0px; width: 0; align-self: stretch;
  position: relative; z-index: 5;
  cursor: col-resize; touch-action: none;
}
.side-grip::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: -3px; width: 6px;
  background: transparent; transition: background .12s ease;
}
.side-grip:hover::before, .side-grip.dragging::before {
  background: var(--accent, #d22730); opacity: .55;
}
/* These two were added for the grip when it lived inside the panels. Nothing
   now depends on them - the dropdowns carry their own containing block on .dd -
   but removing a containing block is a layout change this fix has no reason to
   make, so they stay. */
#sidebar  { position: relative; }
#rightbar { position: relative; }

/* While dragging, stop the pointer selecting text across the whole page. */
body.resizing, body.resizing * { user-select: none !important; cursor: col-resize !important; }

/* The seasonal strip has a fixed label column that squeezes the months at
   narrow widths. Give it room to grow with the panel instead. */
.cal-row { grid-template-columns: minmax(84px, 1.6fr) repeat(12, 1fr); }

/* Visually hidden but still reachable by screen readers - used for the
   Community Insights search label, which would otherwise print its own text
   above the search box. */
.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;
}

/* Community Insights: three tabs over one list. The active tab decides both
   what is listed and what the search box searches. */
.voice-tabs { display: flex; gap: 2px; margin: 6px 0; }
.voice-tab {
  flex: 1; padding: 4px 6px; font-size: 11px; cursor: pointer;
  border: 1px solid var(--ifrc-red); background: var(--ifrc-red); color: #fff;
}
.voice-tab:hover { filter: brightness(1.08); }
.voice-tab.is-active {
  background: var(--ifrc-dark-blue); border-color: var(--ifrc-dark-blue);
  color: #fff; font-weight: 600;
}
/* #voice-search lost its .panel-search wrapper when the tabs were added, so
   it needs the same look .panel-search input gave it, applied directly. */
#voice-search {
  width: 100%; box-sizing: border-box; font-family: inherit; font-size: 12px;
  padding: 6px 9px; margin-bottom: 8px; border: 1px solid var(--border);
  border-radius: 6px; background: var(--panel2); color: var(--text);
}
#voice-search:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.voice .v-voice {
  font-size: 10px; text-transform: uppercase; letter-spacing: .04em; opacity: .65;
}
/* 5px, not the base rule's 3px: telling the two voices apart at a glance is
   the whole point of the tabs, and 3px here was a no-op - the base .voice
   rule already sets 3px, so this changed nothing until now. */
.voice.is-community { border-left-width: 5px; }
.voice .v-full { white-space: pre-wrap; }
.voice .v-translated { opacity: .75; font-style: italic; margin-top: 4px; }
.voices-unmapped { font-size: 11px; opacity: .7; margin-top: 6px; }
/* Highlighted by a click on its map bubble (Task 12 R5). */
.voice.is-selected { outline: 2px solid var(--accent); outline-offset: -1px; }

/* Community-insight map popup body (Task 12 R4). Reuses the same combined
   bubble as every other layer - see "ONE BUBBLE, NOT FIVE" in app.js - so
   only the section content is styled here. */
.insight-popup blockquote {
  margin: 4px 0; font-size: 12px; line-height: 1.45; white-space: pre-wrap;
}
.insight-popup .ip-voice {
  font-size: 10px; text-transform: uppercase; letter-spacing: .04em; opacity: .65;
}
.insight-popup .ip-place { font-size: 11px; opacity: .8; }
.insight-popup .ip-meta { font-size: 10px; opacity: .6; margin-top: 4px; }
/* SEVERAL STATEMENTS IN ONE BUBBLE. A statement is located to an admin area
   and drawn at that area's label point, so one point routinely holds a dozen
   of them - see renderInsightBubbles(). They are ruled off from each other,
   in the same way .pp-sec separates one LAYER from the next, because the
   thing a reader must not do here is run two people's words together. */
.insight-popup .ip-count {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .6px;
  color: var(--muted); font-weight: 700; margin-bottom: 4px;
}
.insight-popup .ip-item + .ip-item {
  border-top: 1px dashed var(--border); margin-top: 7px; padding-top: 7px;
}
/* Only on the hovering bubble, which cannot be scrolled: it shows the first
   few and this says how to reach the rest. */
.insight-popup .ip-more {
  font-size: 10px; color: var(--muted); font-style: italic; margin-top: 7px;
}

/* The banner over a sidebar narrowed by a click on a map bubble, and its way
   out. A selection is the one panel mode a person can enter by accident, so it
   names itself and always offers the month back. */
.voices-selected {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 11px; color: var(--muted); line-height: 1.4;
  padding: 5px 7px; margin-bottom: 8px;
  border-left: 3px solid var(--accent); background: var(--hover);
}
.voices-clear {
  background: none; border: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--accent); text-decoration: underline;
}
.voices-clear:hover { text-decoration: none; }
