/* ── World overview ────────────────────────────────────────────────────────
   The map is server-rendered: every country path carries its two counts in
   --eco/--social and colours itself by whichever metric is selected —
   ecological overshoot by default, social foundations met on request. The
   region pills frame the map; the search dims it. Colour is never the only
   encoding — the hover tooltip, the legend and each country link's label all
   carry the numbers. */

/* One viewport, no document scroll (same shell as the country page). The
   header, the controls and the legend keep their natural height; the map takes
   everything that is left and letterboxes itself inside that box, because the
   SVG keeps the aspect ratio of its viewBox. The transport is the country
   page's footer, a sibling of the page, so the two overlap nothing. */
body.is-world {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
}

.worldpage {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  flex: 1 1 auto;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-5);
}

/* Without JavaScript the transport could not move the year, and the page is
   already the server-rendered 2015 map — so it is not rendered at all, the same
   rule the language switch follows. */
body.is-world > .transport { flex: none; }

@media (scripting: none) {
  body.is-world > .transport { display: none; }
}

.world-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-5);
  flex: none;
  flex-wrap: wrap;
}

.world-head h1 {
  font-family: var(--font-display);
  font-size: var(--step-3);
  line-height: 1.02;
  letter-spacing: -0.02em;
  margin: 0;
}

.world-head .subtitle {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 6px;
}

.world-head .finding {
  margin: var(--space-2) 0 0;
  max-width: 46ch;
  font-size: var(--step-1);
  color: var(--ink-soft);
}

.world-head .credit {
  margin: var(--space-2) 0 0;
  font-size: var(--step--1);
  color: var(--muted);
}

.world-head .credit a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

.world-head .toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* The language switch is shared with the country pages: see .lang-switch in
   app.css (a dropdown, not a row of links). */

/* ── Controls ─────────────────────────────────────────────────────────── */

/* The metric switch takes the left of the row; the two filters stack on the
   right, the continent pills first and the search directly under them. */
.world-controls {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3) var(--space-4);
  flex: none;
  flex-wrap: wrap;
  margin: var(--space-4) 0 var(--space-3);
}

.metric-switch { align-self: flex-start; }

.filters {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
  min-width: 0;
  margin-left: auto;
}

.filters .regions { justify-content: flex-end; }

.search { display: inline-flex; align-items: center; width: 100%; }
.search input {
  font: inherit;
  font-size: var(--step-0);
  padding: 9px 12px;
  width: 100%;
  min-width: 15rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--ink);
}
.search input:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Search feedback. Hidden until the query needs it, so it costs no height in
   the viewport-locked shell. It answers the box above it, which sits at the
   right edge of the controls. The off-map countries have no path to dim, so
   they are listed here as links. */
.search-status {
  flex: none;
  margin: 0;
  text-align: right;
  font-size: var(--step--1);
  color: var(--muted);
}
.search-status a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 2px;
}
.search-status a:hover { text-decoration-color: var(--accent); }
.search-hits-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-right: var(--space-2);
}

/* ── Map ──────────────────────────────────────────────────────────────── */

/* The map is the flexible pane: it owns whatever height the chrome does not
   need, and the SVG fits its viewBox inside that box (preserveAspectRatio
   defaults to xMidYMid meet). A region zoom therefore shrinks to fit instead
   of growing the document — Africa's box is taller than it is wide. */
.map-wrap {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-height: 0;
}

.world {
  display: block;
  flex: 1 1 0;
  width: 100%;
  height: 100%;
  min-height: 0;
}

.world path.c {
  fill: var(--missing);
  stroke: var(--paper);
  stroke-width: 0.7;
  vector-effect: non-scaling-stroke;
  transition: fill-opacity var(--dur-fast) var(--ease-out);
}

/* ── Hatch ────────────────────────────────────────────────────────────────
   The texture says the reading rests on a short denominator, never the shade.
   Each hatch pattern carries its own ramp step, so a hatched country keeps the
   colour it would have unhatched; the tile's size is set in screen pixels by
   world.js and these attributes are the world-view default. A country with no
   data at all is grey under the same hatch. */
[id^="hatchE"] .hatch-bg { fill: color-mix(in oklab, var(--ring), var(--alarm) calc(var(--v) * 100%)); }
[id^="hatchS"] .hatch-bg { fill: color-mix(in oklab, var(--alarm), var(--ring) calc(var(--v) * 100%)); }
#hatchMissing .hatch-bg { fill: var(--missing); }
.hatch-line { stroke: var(--ink); opacity: 0.3; }

/* One sequential ramp per metric, each with its own legend and its own fill.
   Ecological (the server-rendered default) runs green — nothing overshot — to
   red — all six, left to right. Social reverses with its reading: red, no
   foundation met, to green, all eleven. The denominator is the full count
   either way; the template hands the path --eco-fill/--social-fill only when
   that metric's denominator is short, so the hatch — and the tooltip's (i) note
   — follow the pill, and a short denominator is never silently read as a low
   count. world.js sets data-metric on <body>; without it the eco colours
   stand. */
.world path.c:not(.is-nodata) {
  --v: calc(var(--eco) / 6);
  fill: var(--eco-fill, color-mix(in oklab, var(--ring), var(--alarm) calc(var(--v) * 100%)));
}
body[data-metric="social"] .world path.c:not(.is-nodata) {
  --v: calc(var(--social) / 11);
  fill: var(--social-fill, color-mix(in oklab, var(--alarm), var(--ring) calc(var(--v) * 100%)));
}
.world path.c.is-nodata { fill: url(#hatchMissing); }

.world a:hover path.c,
.world a:focus-visible path.c { stroke: var(--ink); stroke-width: 1.6; }
.world a:focus-visible { outline: none; }
.world a:focus-visible path.c { stroke: var(--accent); stroke-width: 2; }
.world a.is-dim path.c { opacity: 0.18; }
.world a.is-dim { pointer-events: none; }
/* No-data countries have no link to dim, so the search dims the path itself. */
.world path.c.is-dim { opacity: 0.18; pointer-events: none; }

/* ── Hover tooltip ────────────────────────────────────────────────────────
   The app's tooltip (app.css) already floats, fades and carries the CTA. The
   map gives it a fixed width: an absolutely positioned box sized `auto` shrinks
   to the space right of its `left`, so near the right edge it would collapse and
   grow tall. With the width fixed, the JS clamp can keep the whole box inside
   the pane without measuring anything. */
.map-wrap .tooltip {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: min(320px, calc(100% - 8px));
  max-width: none;
}
.tooltip-body { min-width: 0; }
.mini-donut { width: 44px; height: 44px; flex: none; margin-top: 1px; }
.mini-donut .is-met, .mini-donut .is-within { fill: var(--ring); }
.mini-donut .is-short, .mini-donut .is-over { fill: var(--alarm); }
.mini-donut .is-missing { fill: var(--missing); }

/* The (i) note closes the tooltip. It appears exactly when the country is
   hatched, and says what the hatch means in words — the surface texture is
   never the only encoding. `hidden` has to beat the `.t-value` display rule. */
.tooltip [hidden] { display: none !important; }
.tooltip.is-nodata .mini-donut { display: none; }
.t-note {
  display: flex;
  align-items: flex-start;
  gap: 5px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid color-mix(in oklab, currentColor 22%, transparent);
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.4;
  letter-spacing: 0.01em;
  opacity: 0.82;
}
.t-info { width: 12px; height: 12px; flex: none; margin-top: 1px; }

/* ── Legend ───────────────────────────────────────────────────────────── */

/* Both legends are rendered server-side — the numbers never travel through
   JavaScript — and the body's data-metric picks which one is shown. Every chip
   is also a filter: hovering, focusing or tapping it dims every country that
   does not share its colour (world.js). The chip the filter comes from keeps
   the ring, and the rest of the legend steps back so the eye follows one
   selection rather than seven. */
.legend {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: none;
  flex-wrap: wrap;
  margin-top: var(--space-3);
  font-size: 11px;
  color: var(--muted);
}
.legend .only-social { display: none; }
body[data-metric="social"] .legend .only-eco { display: none; }
body[data-metric="social"] .legend .only-social { display: inline-flex; }

.legend-title {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.ramp { display: inline-flex; gap: 1px; }
.ramp .legend-step {
  width: 22px;
  height: 16px;
  display: block;
  border-radius: 2px;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast);
}
.ramp.only-eco .legend-step { background: color-mix(in oklab, var(--ring), var(--alarm) calc(var(--v) * 100%)); }
.ramp.only-social .legend-step { background: color-mix(in oklab, var(--alarm), var(--ring) calc(var(--v) * 100%)); }
.legend-step:hover { transform: scaleY(1.15); }
.legend-step:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The chip the filter comes from. Two rings, paper then ink, so it reads on
   both themes and on a pale ramp step. */
.legend-step.is-active {
  box-shadow: 0 0 0 1.5px var(--paper), 0 0 0 3px var(--ink);
}
.legend.is-filtering .legend-step:not(.is-active) { opacity: 0.42; }
.legend-note {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 4px;
  border-radius: 3px;
  cursor: pointer;
  transition: opacity var(--dur-fast);
}
.swatch {
  width: 12px;
  height: 12px;
  display: inline-block;
  border-radius: 2px;
  background-repeat: repeat;
}
/* Both swatches are the map's own encoding at 12 px: the hatch over the ramp
   step (which switches with the metric) and the hatch over grey. */
.swatch.is-partial {
  background-image:
    repeating-linear-gradient(45deg, transparent 0 2.5px, color-mix(in oklab, var(--ink) 30%, transparent) 2.5px 3.5px),
    linear-gradient(90deg, var(--ring), var(--alarm));
}
body[data-metric="social"] .swatch.is-partial {
  background-image:
    repeating-linear-gradient(45deg, transparent 0 2.5px, color-mix(in oklab, var(--ink) 30%, transparent) 2.5px 3.5px),
    linear-gradient(90deg, var(--alarm), var(--ring));
}
.swatch.is-nodata {
  background-image:
    repeating-linear-gradient(45deg, transparent 0 2.5px, color-mix(in oklab, var(--ink) 30%, transparent) 2.5px 3.5px),
    linear-gradient(var(--missing), var(--missing));
}

/* The chip's own tooltip: the explanation, then how many countries it lights
   up. Same skin as the app's tooltip, but positioned above the chip and clamped
   inside the pane by world.js — the legend wraps, so a chip can sit at either
   end. It never takes the pointer: the filter ends when the pointer leaves the
   legend, and a box under the cursor would sit in the way of that. */
.legend-tip {
  position: absolute;
  z-index: 30;
  /* Explicitness beats shrink-to-fit: an absolutely positioned box sized
     `auto` narrows to the space right of its `left`, which turns a chip at the
     end of a wrapped legend into a column of single words. */
  width: max-content;
  max-width: 260px;
  padding: 7px 9px;
  border-radius: 8px;
  background: var(--tooltip-bg);
  color: var(--tooltip-ink);
  font-size: 11.5px;
  line-height: 1.35;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -100%);
  transition: opacity var(--dur-fast) var(--ease-out);
}
.legend-tip.is-visible { opacity: 1; }
.legend-tip strong { display: block; font-weight: 600; }
.legend-tip span {
  display: block;
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.02em;
  opacity: 0.78;
}

/* ── Regions ──────────────────────────────────────────────────────────── */

.regions { display: flex; gap: 6px; flex-wrap: wrap; min-width: 0; }
.regions button {
  font: inherit;
  font-size: 11px;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.regions button[aria-pressed="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }

/* ── Compaction ───────────────────────────────────────────────────────── */

@media (max-width: 640px) {
  .worldpage { padding: var(--space-3) var(--space-3); }
  .world-head h1 { font-size: var(--step-2); }
  .world-head .finding { font-size: var(--step-0); }
  /* Stacked in reading order: metric switch, continents, search. */
  .world-controls { flex-direction: column; align-items: stretch; gap: var(--space-2); margin: var(--space-3) 0 var(--space-2); }
  .metric-switch { align-self: flex-start; }
  .filters { align-items: stretch; margin-left: 0; }
  .filters .regions { justify-content: flex-start; }
  .search input { min-width: 0; padding: 7px 10px; }
  .search-status { text-align: left; }
  .ramp .legend-step { width: 16px; height: 14px; }
  .legend { gap: var(--space-2); }
  /* Compact pills so the six regions still fit in two rows. */
  .regions { gap: 4px; }
  .regions button { font-size: 10px; padding: 4px 8px; }
}

/* Short viewports trade the secondary lines for map height. The finding stays:
   it is the one sentence that says what the map means. */
@media (max-height: 640px) {
  .worldpage { padding-top: var(--space-2); padding-bottom: var(--space-2); }
  .world-head .credit { display: none; }
  .world-head .finding { margin-top: var(--space-1); font-size: var(--step-0); }
  .world-controls { margin: var(--space-3) 0 var(--space-2); }
  .search-status { margin-bottom: var(--space-2); }
}

@media (max-height: 520px) {
  .world-head .subtitle { display: none; }
}
