﻿/* ICO Coffee Trade Globe â€” chrome.
 * House style: sections are separated by hairline rules and mono microcaps,
 * not by boxes. Panels are opaque (no glass). Selection inverts to ink.
 * Colour means data; chrome is paper and rules.
 */

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  /* Header then content, with the content taking whatever is left. Replaces
   * `height: calc(100% - 56px)` on the layouts, which assumed a header that
   * never wrapped. */
  display: flex;
  flex-direction: column;
  font: 400 var(--fs)/1.45 var(--font-ui);
  background: var(--bg);
  color: var(--ink);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.spacer { flex: 1; }
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- microcap: the single label role ---------- */
.cap {
  margin: 0;
  font: 400 var(--fs-cap) var(--font-mono);
  letter-spacing: var(--track-cap-wide);
  text-transform: uppercase;
  color: var(--ink-dim);
}

/* ---------- masthead ---------- */
/* min-height, not height: below ~1100px the tab strip and the platform bar
 * cannot both fit on one row, and navigation wrapping onto a second row is
 * the correct outcome — clipping tabs off the right edge is not. The shell is
 * a flex column (body, below) precisely so the header may grow. */
.masthead {
  min-height: 56px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-8) var(--sp-12);
  padding: var(--sp-6) var(--sp-16);
  border-bottom: 1px solid var(--line-strong);
  background: var(--panel);
  flex: none;
}
/* The mark is a flat alpha shape, so masking it recolours it exactly â€” one
 * asset serves both themes (true black on cream / cream on coffee). */
.masthead .logo {
  /* Box matches the asset's 532:180 ratio (PNG is trimmed to the artwork),
   * so the mark fills it edge to edge. At 60px tall the bar grows past its
   * 56px minimum — deliberate: the mark now leads the masthead. */
  width: 178px; height: 60px; flex: none; background: var(--logo-ink);
  -webkit-mask: url('../img/logo-cream.png') left center / contain no-repeat;
  mask: url('../img/logo-cream.png') left center / contain no-repeat;
}
.wordmark { margin: 0; display: flex; flex-direction: column; gap: 1px; padding-left: var(--sp-12); border-left: 1px solid var(--line); }
.wordmark b { font: 600 var(--fs-lg)/1.1 var(--font-ui); letter-spacing: -0.01em; }
.wordmark span {
  font: 400 var(--fs-cap-sm) var(--font-mono);
  letter-spacing: var(--track-cap);
  text-transform: uppercase;
  color: var(--ink-mute);
}
.source {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  font: 400 var(--fs-cap) var(--font-mono);
  letter-spacing: 0.04em;
  color: var(--ink-dim);
  white-space: nowrap;
}
.source .s { width: 9px; height: 9px; color: var(--ok); }
.source.err .s { color: var(--danger); }

/* ---------- layout ---------- */
/* The three column widths are NAMED, not written into the template, because
 * four different things want to change one of them: the breakpoint, the
 * production view (no dossier) and the two fold handles. One template plus a
 * variable each means every combination of those falls out on its own. */
.layout {
  --rail-w: 272px;
  --detail-w: 324px;
  display: grid; grid-template-columns: var(--rail-w) 1fr var(--detail-w);
  flex: 1; min-height: 0;
}
.rail, .detail { overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; background: var(--panel); }
.rail { border-right: 1px solid var(--line-strong); }
.detail { border-left: 1px solid var(--line-strong); }

/* Sections: a rule above, a microcap, then content. No boxes. */
.sect { padding: var(--sp-16); border-bottom: 1px solid var(--line); display: flex; flex-direction: column; gap: var(--sp-10); }
/* `display:flex` outranks the UA stylesheet's `[hidden] { display:none }`, so
 * without this a hidden card still takes its full height and the rail scrolls
 * for content nobody can see. Every display rule below must respect [hidden]. */
[hidden] { display: none !important; }
.sect:last-child { border-bottom: 0; }
.sect.grow { flex: 1; }
.note { margin: 0; font-size: var(--fs-sm); line-height: 1.5; color: var(--ink-mute); }

/* ---------- buttons ---------- */
.btn-row { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
.btn-row.stack { flex-direction: column; align-items: stretch; }
.btn {
  font: 500 var(--fs) var(--font-ui);
  color: var(--ink-dim);
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  padding: 6px 10px;
  cursor: pointer;
  text-align: left;
  transition: color var(--dur-row) ease, border-color var(--dur-row) ease,
              background-color var(--dur-row) ease;
}
.btn:hover { color: var(--ink); border-color: var(--bean); }
/* Selection inverts to ink â€” legible in both themes by construction. */
.btn.active { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; color: var(--ink-faint); border-color: var(--line); }
.btn:disabled:hover { color: var(--ink-faint); border-color: var(--line); }
.btn.icon { padding: 6px; display: inline-flex; align-items: center; justify-content: center; }
.i { width: 16px; height: 16px; display: block; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
:where(button, [tabindex], input):focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--r); }

/* Form lens: a letter, a name, and the world share it accounts for. */
.btn.form { display: grid; grid-template-columns: 16px 1fr auto; align-items: center; gap: var(--sp-8); border-left: 3px solid var(--sw); }
.btn.form .k { font: 500 var(--fs-lg) var(--font-mono); color: var(--sw); }
.btn.form .l { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.btn.form .p { font: 400 var(--fs-cap-lg) var(--font-mono); color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.btn.form.active .k, .btn.form.active .p { color: var(--bg); }
.btn.form:not(.active) { opacity: 0.55; }
.hud .warn { color: var(--danger); }

/* Export & share */
.btn.wide { display: flex; align-items: center; gap: var(--sp-8); width: 100%; }
.btn.wide .i { flex: none; }
.status { margin: 0; min-height: 1em; font: 400 var(--fs-cap-lg) var(--font-mono); color: var(--ok); }
.copy-field {
  width: 100%; margin-top: var(--sp-4); padding: var(--sp-6);
  font: 400 var(--fs-cap-lg) var(--font-mono); color: var(--ink);
  background: var(--bg-2); border: 1px solid var(--line-strong); border-radius: var(--r);
}
.note.cite {
  font-size: var(--fs-cap-lg); line-height: 1.5; color: var(--ink-faint);
  border-top: 1px solid var(--line); padding-top: var(--sp-8);
  overflow-wrap: anywhere;
}

/* Indicator-group rows carry a second line: implied vs published price. */
.rank-row.grp { grid-template-columns: 1fr auto; align-items: start; padding: var(--sp-8) var(--sp-6); }
.rank-row.grp .name, .rank-row.grp .val { display: flex; flex-direction: column; gap: 1px; }
.rank-row.grp .val { text-align: right; }
.sub2 { font: 400 var(--fs-cap-sm) var(--font-mono); color: var(--ink-mute); letter-spacing: 0.02em; }

/* Flow buttons carry their hue as an edge, not a fill. */
#flowBar .btn { border-left-width: 3px; }
#flowBar .btn[data-flow='export'] { border-left-color: var(--viz-origin); }
#flowBar .btn[data-flow='import'] { border-left-color: var(--viz-importer); }
#flowBar .btn[data-flow='reexport'] { border-left-color: var(--viz-reexport); }

/* ---------- stage ---------- */
.stage { position: relative; overflow: hidden; background: var(--bg); container-type: inline-size; }
#globe { position: absolute; inset: 0; width: 100%; height: 100%; cursor: grab; touch-action: none; display: block; }
#globe.dragging { cursor: grabbing; }
.countries path { transition: opacity var(--dur-fill) linear; }
/* NEVER transition `fill` here â€” the d3 fill tween owns that attribute. */
.countries path.hv { filter: brightness(var(--hover-bright)); }
.globe-label {
  font: 500 10px var(--font-mono);
  letter-spacing: 0.1em;
  fill: var(--label-ink);
  stroke: var(--label-halo);
  stroke-width: 3;
  stroke-linejoin: round;
  paint-order: stroke fill;
  pointer-events: none;
}
/* The selected country's ICO code, hung under its name. Subordinate by
 * design — the name answers "where", the code is the identifier you copy —
 * so it is set smaller and lighter, but keeps the same halo so it stays
 * readable over land, sea and any choropleth fill underneath. */
.globe-label .code {
  font-size: 9px;
  fill-opacity: 0.72;
}
g.labels, g.parts, g.arrows, g.ends, g.region-marks { pointer-events: none; }

/* The coffee belt (globeView.setBelt): a tint UNDER the land, so it colours
 * the sea alone and never muddies a choropleth, and the two tropic lines
 * ABOVE it, so an edge stays legible across a continent. Solid, not dashed:
 * a dash pattern restarts at the limb and crawls while the globe turns. */
.belt-band { fill: var(--belt-fill); stroke: none; pointer-events: none; }
.belt-line { fill: none; stroke: var(--belt-line); stroke-width: 1.2; pointer-events: none; }
.belt-labels { pointer-events: none; }
.belt-label {
  font: 500 8.5px var(--font-mono);
  letter-spacing: 0.14em;
  text-anchor: middle;
  fill: var(--belt-ink);
  stroke: var(--label-halo);
  stroke-width: 3;
  stroke-linejoin: round;
  paint-order: stroke fill;
}

/* Regional balance callouts. Same halo trick as .globe-label so they stay
 * readable over land, sea and any choropleth fill underneath. The figure is
 * set larger than the region name because the figure is the point. */
.rmark-dot {
  fill: var(--bg);
  stroke: var(--ink);
  stroke-width: 1.5;
}
.rmark[data-sign='pos'] .rmark-dot { stroke: var(--viz-origin); }
.rmark[data-sign='neg'] .rmark-dot { stroke: var(--viz-importer); }
/* Drawn only when a callout has been pushed off its dot's row by a neighbour. */
.rmark-leader { stroke: var(--ink-dim); stroke-width: 1; stroke-linecap: round; }
.rmark-title, .rmark-val {
  stroke: var(--label-halo);
  stroke-width: 3.5;
  stroke-linejoin: round;
  paint-order: stroke fill;
}
.rmark-title {
  font: 500 9.5px var(--font-mono);
  letter-spacing: var(--track-cap);
  text-transform: uppercase;
  fill: var(--ink-dim);
}
.rmark-val {
  font: 500 14px var(--font-mono);
  font-variant-numeric: tabular-nums;
  fill: var(--ink);
}
.rmark[data-sign='pos'] .rmark-val { fill: var(--viz-origin); }
.rmark[data-sign='neg'] .rmark-val { fill: var(--viz-importer); }
.rmark[data-sign='none'] .rmark-val { fill: var(--ink-mute); font-size: 10px; }

.hud {
  position: absolute; top: var(--sp-16); left: var(--sp-16);
  font: 400 var(--fs-cap) var(--font-mono);
  letter-spacing: var(--track-cap);
  text-transform: uppercase;
  color: var(--ink-mute);
  pointer-events: none;
}
.hud b { color: var(--ink-dim); font-weight: 500; }

/* Floating map chrome: the 1.5px bean border is the "important chrome" tier. */
.search, .globe-ctrls .btn, .timeline, .legend {
  background: var(--panel);
  border: 1.5px solid var(--bean);
  border-radius: var(--r);
}
.search {
  position: absolute; top: var(--sp-12); right: var(--sp-16); width: 224px;
  display: flex; align-items: center; gap: var(--sp-6); padding: 0 var(--sp-8);
}
.search .i { color: var(--ink-mute); flex: none; }
.search input {
  flex: 1; min-width: 0; border: 0; background: transparent; color: var(--ink);
  font: 400 var(--fs) var(--font-ui); padding: 7px 0; outline: none;
}
.search kbd {
  font: 400 var(--fs-cap-sm) var(--font-mono); color: var(--ink-mute);
  border: 1px solid var(--line); border-radius: var(--r-sm); padding: 1px 4px;
}
.search:focus-within { box-shadow: var(--focus-ring); }
.results {
  position: absolute; top: calc(100% + 4px); left: -1.5px; right: -1.5px;
  max-height: 260px; overflow-y: auto; display: none;
  background: var(--panel); border: 1.5px solid var(--bean); border-radius: var(--r);
  box-shadow: var(--shadow); z-index: 12;
}
.results.open { display: block; }
.results button {
  display: flex; justify-content: space-between; gap: var(--sp-8); width: 100%;
  border: 0; background: transparent; color: var(--ink); cursor: pointer; text-align: left;
  font: 400 var(--fs) var(--font-ui); padding: 6px var(--sp-10);
  transition: padding-left var(--dur-row) ease;
}
.results button:hover, .results button.hl { background: var(--bg-2); padding-left: var(--sp-16); }
.results button span { font: 400 var(--fs-cap) var(--font-mono); color: var(--ink-mute); }

/* --ctrl-bottom is set by app.js from the dock's measured height; the fallback
 * is the single-row dock. */
.globe-ctrls { position: absolute; right: var(--sp-16); bottom: var(--ctrl-bottom, 92px); display: flex; flex-direction: column; gap: var(--sp-4); }
.globe-ctrls .btn { color: var(--ink-dim); }
.globe-ctrls .btn:hover { color: var(--ink); }
/* A latched control (the coffee belt) inverts to ink like every other active
 * button. Restated here because the floating-chrome rule above paints these
 * buttons on the panel colour and outranks .btn.active by source order. */
.globe-ctrls .btn.active, .globe-ctrls .btn.active:hover {
  background: var(--ink); border-color: var(--ink); color: var(--bg);
}

/* ---------- bottom dock ----------
 * The legend and timeline used to be independently absolutely-positioned and
 * collided on a wide stage. One grid owns the bottom band: the timeline is
 * centred by the symmetric 1fr columns, the legend takes the left column and
 * shrinks, and neither can ever be drawn over the other.
 */
/* Stacked, not side by side: a full map key plus a centred 64-year timeline
 * does not fit on one row even at 1300px, and a squeezed legend is worse than
 * a taller dock. The globe reserves the dock's measured height, so the cost of
 * the extra row is paid for automatically. */
.dock {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-8);
  padding: var(--sp-12) var(--sp-16);
  pointer-events: none; /* the globe stays draggable around the panels */
}
.dock > * { pointer-events: auto; }

.timeline {
  min-width: 0; max-width: 100%;
  display: flex; align-items: center; gap: var(--sp-8); padding: var(--sp-6) var(--sp-10);
}
.timeline .btn.icon { border: 0; flex: none; }
/* min-width:0 is load-bearing: without it the flex item's automatic minimum
 * size is its 64-tick content width, which overflows the dock no matter what
 * overflow rule is set here. */
.ticks {
  display: flex; align-items: center; gap: 3px;
  min-width: 0; overflow-x: auto; scroll-behavior: smooth;
  scrollbar-width: none; -ms-overflow-style: none;
}
.ticks::-webkit-scrollbar { display: none; }
.tick {
  font: 400 var(--fs-cap-lg) var(--font-mono);
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 3px 5px;
  cursor: pointer;
  line-height: 1;
  transition: color var(--dur-row) ease, background-color var(--dur-row) ease;
}
.tick:hover { color: var(--ink); border-color: var(--line-strong); }
.tick.on { background: var(--ink); color: var(--bg); font-weight: 500; }
.tick.dec { color: var(--ink-dim); }
.tick.gap { pointer-events: none; color: var(--ink-faint); opacity: 0.5; padding: 3px 1px; }
.cov { font: 400 var(--fs-cap-sm) var(--font-mono); color: var(--ink-mute); white-space: nowrap; padding-left: var(--sp-6); border-left: 1px solid var(--line); flex: none; }
.tick { flex: none; }

/* ---------- legend ---------- */
.legend {
  align-self: flex-start; min-width: 0; max-width: 100%;
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-4) var(--sp-12);
  padding: var(--sp-6) var(--sp-12);
  font-size: var(--fs-sm); color: var(--ink-dim);
}
.legend::-webkit-scrollbar { display: none; }
.legend > * { flex: none; }
.legend .lg-title {
  font: 400 var(--fs-cap) var(--font-mono); letter-spacing: var(--track-cap-wide);
  text-transform: uppercase; color: var(--ink-dim);
  padding-right: var(--sp-12); border-right: 1px solid var(--line); white-space: nowrap;
}
.legend .item { display: flex; align-items: center; gap: var(--sp-6); white-space: nowrap; }
.legend .ramp { width: 110px; height: 8px; border-radius: var(--r-sm); border: 1px solid var(--line); }
.legend .t { font: 400 var(--fs-cap-sm) var(--font-mono); color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.legend .arckey { display: flex; flex-direction: column; gap: 3px; }
.legend .arckey i { display: block; width: 26px; background: currentColor; border-radius: 1px; }
.legend .nodata { width: 12px; height: 12px; border-radius: var(--r-sm); background: var(--country-fill); border: 1px solid var(--country-line); }
.legend .belt-key {
  width: 26px; height: 10px; flex: none;
  background: var(--belt-fill);
  border-top: 1.5px solid var(--belt-line); border-bottom: 1.5px solid var(--belt-line);
}
/* The one-colour switch rides in the key it changes, in the key's own small
 * type; pressed, it inverts to ink like every other active button. */
.legend .lg-toggle {
  font: 400 var(--fs-cap-sm) var(--font-mono); letter-spacing: 0.04em;
  padding: 3px 7px; white-space: nowrap;
}

/* ---------- tooltip ---------- */
#tip {
  position: absolute; pointer-events: none; z-index: 30; max-width: 230px;
  background: var(--panel-tooltip); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: var(--r);
  padding: var(--sp-8) var(--sp-10); box-shadow: var(--shadow-tooltip);
  opacity: 0; transition: opacity var(--dur-row) ease;
}
#tip.show { opacity: 1; }
#tip .t-name { font: 600 var(--fs) var(--font-ui); margin-bottom: var(--sp-4); }
#tip .t-iso { font: 400 var(--fs-cap-sm) var(--font-mono); color: var(--ink-mute); letter-spacing: 0.08em; }
#tip .t-row { display: flex; justify-content: space-between; gap: var(--sp-12); font-size: var(--fs-sm); color: var(--ink-dim); }
#tip .t-row b { color: var(--ink); font: 500 var(--fs-sm) var(--font-mono); }

/* ---------- loader ---------- */
.loader {
  position: absolute; inset: 0; z-index: 40; display: flex; align-items: center; justify-content: center;
  gap: var(--sp-12); background: var(--bg); transition: opacity 400ms ease;
}
.loader.done { opacity: 0; pointer-events: none; }
.loader .spin {
  width: 12px; height: 12px; border-radius: 50%;
  border: 1.5px solid var(--line-strong); border-top-color: var(--bean);
  animation: spin 900ms linear infinite;
}
.loader .msg {
  font: 400 var(--fs-sm) var(--font-mono); letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--ink-dim);
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- lists ---------- */
.rank-list { display: flex; flex-direction: column; }
.rank-row, .corridor-row {
  display: grid; align-items: center; gap: var(--sp-8);
  width: 100%; border: 0; background: transparent; color: inherit; cursor: pointer;
  font: 400 var(--fs) var(--font-ui); text-align: left;
  padding: 5px var(--sp-6); border-bottom: 1px solid var(--line);
  transition: padding-left var(--dur-row) ease, background-color var(--dur-row) ease;
}
.rank-row { grid-template-columns: 16px 1fr auto; position: relative; }
.corridor-row { grid-template-columns: 1fr auto; }
.rank-row:last-child, .corridor-row:last-child { border-bottom: 0; }
.rank-row:hover, .corridor-row:hover { background: var(--bg-2); padding-left: var(--sp-12); }
.rank-row .n { font: 400 var(--fs-cap) var(--font-mono); color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.rank-row .name, .corridor-row .route { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-row .val, .corridor-row .val { font: 400 var(--fs-cap-lg) var(--font-mono); color: var(--ink-dim); }
.corridor-row .arrow { color: var(--ink-mute); margin: 0 4px; }
/* magnitude track + bar, flush to the row's bottom rule */
.rank-row .track { position: absolute; left: var(--sp-6); right: var(--sp-6); bottom: 2px; height: 2px; background: var(--line); border-radius: 1px; }
.rank-row .bar { position: absolute; left: 0; top: 0; bottom: 0; background: var(--bar, var(--bean)); border-radius: 1px; }

/* ---------- regional balance ----------
 * Surplus wears the origin amber and deficit the importer green — the same two
 * hues the rest of the app uses for supply and demand, so the rail agrees with
 * the map. Deliberately NOT red/green: a coffee deficit is the normal, intended
 * state of every consuming region, and a red number would call it a failure.
 * The sign is always written, so the hue never has to carry it alone.
 */
.pos { color: var(--viz-origin); }
.neg { color: var(--viz-importer); }

/* One key for the list, not a label on every bar — fourteen repetitions of
 * "PRODUCTION / CONSUMPTION" cost more vertical space than they explain. */
.reg-key {
  display: flex; gap: var(--sp-12); padding: 0 var(--sp-6) var(--sp-6);
  font: 400 var(--fs-cap-xs) var(--font-mono);
  letter-spacing: var(--track-cap); text-transform: uppercase; color: var(--ink-mute);
}
.reg-key span { display: inline-flex; align-items: center; gap: 5px; }
.reg-key i { width: 9px; height: 5px; border-radius: 1px; display: block; }

.reg-row {
  position: relative;
  padding: 4px var(--sp-6) 6px;
  border-bottom: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 3px;
}
.reg-row:last-child { border-bottom: 0; }
.reg-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-8); line-height: 1.25; }
.reg-head .name { font-size: var(--fs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.reg-head .val { font: 500 var(--fs-cap-lg) var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.reg-row.pos .reg-head .val { color: var(--viz-origin); }
.reg-row.neg .reg-head .val { color: var(--viz-importer); }

/* Two bars, ONE scale: the gap between them is the balance, so the number in
 * the header and the picture below it are the same fact twice. */
.reg-bars {
  display: grid; grid-template-columns: 1fr auto;
  align-items: center; gap: 3px var(--sp-6);
}
.reg-bars .track { height: 6px; background: var(--bg-3); border-radius: 2px; overflow: hidden; }
.reg-bars .track i { display: block; height: 100%; border-radius: 2px; }
/* line-height 1 so the row is as tall as the bar, not as tall as the glyphs.
 * The fixed number column keeps every region's figures on one vertical rule. */
.reg-bars .num {
  font: 400 var(--fs-cap) / 1 var(--font-mono);
  font-variant-numeric: tabular-nums; color: var(--ink-dim);
  text-align: right; min-width: 52px;
}

/* The net magnitude, flush to the row's rule — same device as .rank-row .track. */
.reg-net { position: absolute; left: var(--sp-6); right: var(--sp-6); bottom: 2px; height: 2px; }
.reg-net i { display: block; height: 100%; border-radius: 1px; background: var(--region, var(--ink-faint)); }

/* The region's identity dot — one mark shared by the rail rows, the ledger
 * table, the legend and the dossier, always in the region's map colour. */
.reg-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  margin-right: 6px; vertical-align: baseline; flex: none;
}
.legend .item .reg-dot { margin-right: 0; }

/* Non-interactive variant of a rank row — the hub list is read, not clicked. */
.rank-row.static { cursor: default; display: grid; grid-template-columns: 1fr auto; align-items: start; }
.rank-row.static:hover { background: transparent; padding-left: var(--sp-6); }
.rank-row.static .name { display: flex; flex-direction: column; gap: 1px; }

/* Sized to FIT the 324px rail — five columns with an invisible horizontal
 * scrollbar were cutting the residual column off entirely. Tight padding,
 * cap-size figures, and headers that may wrap ("Net trade" on two lines
 * costs a taller header row; a hidden column costs the data). */
.bal-table { overflow-x: auto; }
.bal-table table { width: 100%; border-collapse: collapse; }
.bal-table th, .bal-table td { padding: 4px 3px; border-bottom: 1px solid var(--line); text-align: right; }
.bal-table thead th {
  font: 400 var(--fs-cap-xs) var(--font-mono); letter-spacing: 0.03em;
  text-transform: uppercase; color: var(--ink-mute); border-bottom: 1px solid var(--line-strong);
  white-space: normal; vertical-align: bottom;
}
.bal-table th[scope='row'], .bal-table thead th:first-child { text-align: left; white-space: normal; font-weight: 400; }
.bal-table th[scope='row'] { font-size: var(--fs-cap-lg); line-height: 1.25; }
.bal-table th[scope='row'] .reg-dot { width: 7px; height: 7px; margin-right: 4px; }
.bal-table td { font: 400 var(--fs-cap) var(--font-mono); font-variant-numeric: tabular-nums; color: var(--ink-dim); white-space: nowrap; }
.bal-table td.mute { color: var(--ink-mute); }
.bal-table tbody tr:last-child th, .bal-table tbody tr:last-child td { border-bottom: 0; }

.note.warn-note { color: var(--danger); }

/* ---------- right rail ---------- */
.hero { padding: var(--sp-16); border-bottom: 1px solid var(--line-strong); background: var(--panel-strong); }
.hero .fig { font: 300 var(--fs-display-lg)/1 var(--font-ui); letter-spacing: -0.015em; margin-top: var(--sp-6); }
.hero .fig em { font-style: normal; font-size: var(--fs-display-sm); color: var(--ink-dim); }
.hero .sub { margin-top: var(--sp-6); font-size: var(--fs-sm); color: var(--ink-mute); }

.dossier .d-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-8); }
.dossier h3 { margin: 0; font: 500 var(--fs-display-sm)/1.15 var(--font-ui); letter-spacing: -0.01em; }
.dossier .d-close { border: 0; background: transparent; color: var(--ink-faint); cursor: pointer; padding: 2px; }
.dossier .d-close:hover { color: var(--ink); }
.dossier .d-meta { font: 400 var(--fs-cap-sm) var(--font-mono); letter-spacing: 0.08em; color: var(--ink-mute); }
/* 1px-gap-as-border: the gap IS the divider */
.d-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
  background: var(--line); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
}
.d-grid .stat { background: var(--panel); padding: var(--sp-8) var(--sp-10); }
.d-grid .stat:last-child:nth-child(odd) { grid-column: 1 / -1; }
.stat .k { font: 400 var(--fs-cap-xs) var(--font-mono); letter-spacing: var(--track-cap); text-transform: uppercase; color: var(--ink-mute); }
.stat .v { font: 400 var(--fs-lg) var(--font-mono); margin-top: 2px; }
.share-bar { height: 6px; border-radius: var(--r-sm); overflow: hidden; display: flex; }
.share-bar i { display: block; height: 100%; }
.spark svg { width: 100%; height: 44px; display: block; }
.spark .cap { margin-top: 2px; }
.empty { font-size: var(--fs-sm); color: var(--ink-mute); font-style: normal; }

/* ---------- prices ---------- */
.price-view {
  position: absolute; inset: 0; z-index: 20; display: none;
  flex-direction: column; gap: var(--sp-12); padding: var(--sp-20) var(--sp-24); background: var(--bg);
}
.price-view.on { display: flex; }
.pv-head { display: flex; align-items: baseline; gap: var(--sp-16); }
.pv-now { font: 300 var(--fs-display) var(--font-ui); letter-spacing: -0.01em; margin-top: var(--sp-4); }
.pv-delta { font: 400 var(--fs-sm) var(--font-mono); }
.pv-delta.up { color: var(--ok); }
.pv-delta.down { color: var(--danger); }
.pv-leg { display: flex; gap: var(--sp-16); flex-wrap: wrap; font-size: var(--fs-sm); color: var(--ink-dim); }
.pv-leg .row { display: flex; align-items: center; gap: var(--sp-6); }
/* The legend is the series switch. A row is a button, dressed as the label it replaced. */
.pv-leg button.row { font: inherit; color: inherit; background: none; border: 0; padding: 2px 0; cursor: pointer; }
.pv-leg button.row:hover { color: var(--ink); }
.pv-leg button.row[aria-pressed='false'] { color: var(--ink-faint); }
.pv-leg button.row[aria-pressed='false'] .sw { background: var(--ink-faint) !important; opacity: 0.5; }
/* On, but nothing recorded in the range on screen (the older futures in a recent range). */
.pv-leg button.row.none[aria-pressed='true'] { opacity: 0.45; }
.pv-leg button.row:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--r-sm); }
/* A range that reaches before the open year: the same dress as a locked tab. */
#pvRanges .btn.locked { color: var(--ink-mute); border-style: dashed; }
#pvRanges .btn.locked::after { content: '·'; margin-left: 2px; color: var(--gold); }
#pvRanges .btn.locked:hover { color: var(--ink); border-color: var(--bean); }
#pvCsv { display: inline-flex; align-items: center; gap: var(--sp-6); }
#pvCsv[hidden] { display: none; }
.pv-leg .sw { width: 14px; height: 3px; border-radius: 1px; }
/* The legend and the two switches share a row: the series on the left, how
 * they are expressed on the right, wrapping together on a narrow screen. */
.pv-bar { display: flex; align-items: center; gap: var(--sp-16); flex-wrap: wrap; }
.pv-bar .pv-leg { flex: 1 1 24ch; }
.pv-opts { display: flex; gap: var(--sp-8); align-items: center; }
.pv-opts[hidden] { display: none; }
/* A compact segmented switch — the rail's .seg, sized for this row. */
.pv-seg { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--line-strong); border-radius: var(--r); background: var(--bg-2); }
.pv-seg[hidden] { display: none; }
.pv-seg .seg-btn { font: 500 var(--fs-sm) var(--font-ui); padding: 4px 9px; }
.pv-seg .seg-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.pv-seg .seg-btn:disabled:hover { color: var(--ink-dim); background: transparent; }
.pv-chart { flex: 1; min-height: 0; }
.pv-chart svg { width: 100%; height: 100%; display: block; }
/* Fetching the long record: hold the frame, no skeleton, no jump. */
.pv-chart.loading { opacity: 0.45; transition: opacity var(--dur-panel, 200ms); }

/* ---------- motion ---------- */
@keyframes riseIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
[data-anim] > .sect, [data-anim] > .hero, [data-anim] > .dossier { animation: riseIn var(--dur-panel) var(--ease-panel) both; }
[data-anim] .rank-row, [data-anim] .corridor-row {
  animation: riseIn var(--dur-row) var(--ease-panel) both;
  animation-delay: calc(var(--i, 0) * 16ms);
}
.dossier { animation: riseIn var(--dur-panel) var(--ease-enter) both; }

/* Narrow stage: stack the dock instead of squeezing two panels side by side. */
@container (max-width: 780px) {
  .legend { align-self: stretch; }
  .cov { display: none; } /* the year is already on the active tick */
  .globe-ctrls { bottom: auto; top: var(--sp-16); }
}

/* ---------- responsive ---------- */
.rail-toggle {
  display: none; position: fixed; left: var(--sp-12); bottom: var(--sp-12); z-index: 50;
  font: 500 var(--fs) var(--font-ui); color: var(--bg); background: var(--ink);
  border: 0; border-radius: var(--r-pill); padding: 9px var(--sp-16); cursor: pointer;
  box-shadow: var(--shadow);
}
@media (max-width: 1180px) { .layout { --rail-w: 240px; --detail-w: 296px; } }
@media (max-width: 960px) {
  .layout { grid-template-columns: 1fr; }
  .detail { display: none; }
  .rail-toggle { display: block; }
  /* Bottom sheet replaces the hidden rail â€” controls stay reachable.
   * The globe's rail ONLY: it slides up beside a map that is still visible.
   * The sub-pages' rail covers their document instead (country.css), and
   * without this exclusion it would inherit a translated-off-screen sheet. */
  .rail:not(.country-rail) {
    position: fixed; left: 0; right: 0; bottom: 0; max-height: 62vh; z-index: 49;
    border-top: 1.5px solid var(--bean); border-right: 0;
    transform: translateY(100%); transition: transform var(--dur-panel) var(--ease-panel);
  }
  .rail:not(.country-rail).open { transform: none; }
  .legend { max-width: calc(100% - 32px); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* Links styled as buttons: kill the underline and keep the label on one line,
 * so a two-word action ("Country analysis") does not wrap the control. */
a.btn { text-decoration: none; white-space: nowrap; }

/* ---------- segmented view switch ----------
 * Map/Table was a pair of plain buttons and read as just more mode chips, so
 * it got missed. A labelled segmented control with icons and a filled active
 * segment reads as a switch between two ways of seeing the same thing.
 */
.seg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px;
  padding: 3px;
  border: 1.5px solid var(--bean);
  border-radius: var(--r);
  background: var(--bg-2);
  position: relative;
}
.seg-label {
  grid-column: 1 / -1;
  font: 400 var(--fs-cap-xs) var(--font-mono);
  letter-spacing: var(--track-cap);
  text-transform: uppercase;
  color: var(--ink-mute);
  padding: 2px 4px 3px;
}
.seg-btn {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-6);
  font: 500 var(--fs) var(--font-ui);
  color: var(--ink-dim);
  background: transparent;
  border: 0; border-radius: calc(var(--r) - 2px);
  padding: 7px 8px; cursor: pointer;
  transition: background-color var(--dur-row) ease, color var(--dur-row) ease;
}
.seg-btn:hover { color: var(--ink); background: var(--panel); }
.seg-btn[aria-pressed='true'] { background: var(--ink); color: var(--bg); }
.seg-btn[aria-pressed='true'] .i { color: var(--bg); }

/* Inapplicable controls are hidden ([hidden] on the .sect), not greyed:
 * a stack of dimmed cards is noise that pushes the applicable ones down. */
.sect .why {
  margin: 0; font-size: var(--fs-sm); line-height: 1.5;
  color: var(--ink-mute); font-style: italic;
}

/* ---------- entitlement ---------- */
.tick.locked { opacity: 0.42; }
.tick.locked::after { content: '·'; margin-left: 1px; color: var(--gold); }

/* A year whose supply vintage is still being collected. Trade runs to 2026 but
 * production does not, so these ticks look ordinary and behave very
 * differently — an amber underscore is the cheapest honest warning. */
.tick.prov { position: relative; }
.tick.prov::before {
  content: ''; position: absolute; left: 2px; right: 2px; bottom: 1px;
  height: 2px; border-radius: 1px; background: var(--gold);
}
.tick.prov.on::before { background: var(--bg); }

/* The way out of an empty balance. */
.bal-jump { margin: 0 var(--sp-6) var(--sp-8); width: auto; }
.tick.locked:hover { color: var(--ink-mute); border-color: var(--line); }
.paywall {
  border: 1.5px solid var(--bean); border-radius: var(--r);
  background: var(--panel); color: var(--ink);
  padding: 0; max-width: 420px; box-shadow: var(--shadow);
}
.paywall::backdrop { background: rgba(42, 22, 18, 0.42); }
.pw-inner { padding: var(--sp-20); display: flex; flex-direction: column; gap: var(--sp-12); }
.pw-body { margin: 0; font-size: var(--fs-lg); line-height: 1.55; }
.pw-actions { display: flex; flex-wrap: wrap; gap: var(--sp-8); }
.btn.primary { background: var(--ink); color: var(--bg); border-color: var(--ink); }

/* ---------- app tab strip ----------
 * The three pages of this app, shown on all three of them (ui/appTabs.js).
 * `flex-wrap` and the masthead's own wrapping are what deliver the rule that
 * every tab is reachable at every window size: they rewrap, they never hide.
 */
.app-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
/* When the header runs out of room, the wordmark yields before the tabs do:
 * the organisation's name is decoration beside the logo, navigation is not. */
@media (max-width: 1400px) { .wordmark span { display: none; } }
@media (max-width: 900px) { .wordmark { display: none; } }
.btn.tab { display: inline-flex; align-items: center; gap: var(--sp-6); }
.btn.tab.active {
  background: var(--ink); color: var(--bg); border-color: var(--ink);
  pointer-events: none; /* the page you are already on */
}
/* Locked reads as available-but-not-yours: dimmed, never disabled. It stays
 * clickable because the click is what explains the lock. */
.btn.tab.locked { color: var(--ink-mute); border-style: dashed; }
.btn.tab.locked:hover { color: var(--ink); border-color: var(--bean); }
.btn.tab .lock { font-size: 10px; line-height: 1; opacity: 0.9; }

/* The language picker is now the .ico-lang pill toggle inside .ico-nav —
 * styled by vendor/ico-nav/ico-nav.css, same control as every platform page. */

/* ---------- table view ----------
 * An equal view of the same data, not a fallback: the globe is unusable with
 * a screen reader and awkward for anyone who just wants the numbers.
 */
.table-view {
  position: absolute; inset: 0; z-index: 22; overflow: auto;
  background: var(--bg); padding: var(--sp-16);
  display: flex; flex-direction: column; gap: var(--sp-12);
}
.table-view[hidden] { display: none; }
.table-tools { display: flex; align-items: center; gap: var(--sp-8); flex: none; }
.table-scroll { overflow: auto; border: 1px solid var(--line-strong); border-radius: var(--r); }
.table-view table { width: 100%; border-collapse: collapse; font-size: var(--fs); }
.table-view caption {
  text-align: left; padding: var(--sp-10) var(--sp-12);
  font: 400 var(--fs-cap) var(--font-mono); letter-spacing: var(--track-cap);
  text-transform: uppercase; color: var(--ink-dim);
}
.table-view caption .cap-note { display: block; text-transform: none; letter-spacing: 0; color: var(--ink-faint); margin-top: 2px; }
.table-view thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--panel-strong); text-align: left;
  border-bottom: 1px solid var(--line-strong);
  padding: 0; white-space: nowrap;
  font: 400 var(--fs-cap) var(--font-mono); letter-spacing: var(--track-cap);
  text-transform: uppercase; color: var(--ink-dim);
}
.table-view thead th.num { text-align: right; }
.th-sort {
  width: 100%; display: inline-flex; align-items: center; gap: 4px; justify-content: inherit;
  border: 0; background: transparent; color: inherit; font: inherit; letter-spacing: inherit;
  text-transform: inherit; padding: var(--sp-8) var(--sp-10); cursor: pointer;
}
.table-view thead th.num .th-sort { justify-content: flex-end; }
.th-sort:hover { color: var(--ink); }
.th-sort .arrow { color: var(--bean); }
/* Inset ring: the scroller clips outer box-shadows, so an outside ring on a
 * sticky header cell would be invisible exactly where it is needed. */
.th-sort:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--bean); border-radius: var(--r-sm); }
.table-view tbody tr:focus-within { background: var(--bg-2); }
.table-view tbody th, .table-view tbody td {
  padding: 6px var(--sp-10); border-bottom: 1px solid var(--line);
  text-align: left; font-weight: 400;
}
.table-view tbody td.num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.table-view tbody td.rank { color: var(--ink-faint); }
.table-view tbody tr:hover { background: var(--bg-2); cursor: pointer; }
.table-view tbody tr:last-child th, .table-view tbody tr:last-child td { border-bottom: 0; }
/* The globe keeps running underneath; hide it so it cannot be tabbed into. */
.stage.as-table > svg#globe,
.stage.as-table > .dock,
.stage.as-table > .globe-ctrls,
.stage.as-table > .search,
.stage.as-table > .hud { visibility: hidden; pointer-events: none; }

/* ---------- numerals ----------
 * MUST be last: the `font:` shorthand resets font-variant-numeric to normal,
 * so this has to win on source order over every rule that uses it above.
 * Figures that get compared must share a column width.
 */
.rank-row .val, .rank-row .n, .corridor-row .val, .stat .v, .hero .fig,
.pv-now, .pv-delta, .tick, .cov, #tip .t-row b, .source, .legend .t, .d-meta {
  font-variant-numeric: tabular-nums;
}

/* ── The two finer export lenses (2026-08-10) ──────────────────────────── */
.combo-select {
  width: 100%; padding: 7px 8px; border-radius: 7px;
  border: 1px solid var(--line-strong); background: var(--panel-2, var(--panel));
  color: var(--ink); font: 500 11.5px/1.3 inherit; cursor: pointer;
}
.combo-select:focus-visible { outline: 2px solid var(--bean, #c89669); outline-offset: 1px; }

/* ── Timeline tuck-away: the control sits UNDER the slider in the dock ─── */
.tl-hide {
  align-self: flex-end; margin-top: -2px;
  border: 0; background: transparent; cursor: pointer;
  font: 600 10px/1 inherit; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-dim, currentColor); opacity: 0.65; padding: 3px 8px; border-radius: 6px;
}
.tl-hide:hover { opacity: 1; background: color-mix(in srgb, currentColor 8%, transparent); }

/* ── Production mode carries no right-hand dossier (owner call 2026-08-10) ─ */
/* Naming the width rather than restating the template also keeps this rule
 * out of the breakpoints' way: as a full template it outranked both of them
 * by specificity, so production mode kept a 272px rail at 1180px and reserved
 * a 272px column for a rail that is a bottom sheet below 960px. */
.layout.no-detail { --detail-w: 0px; }
.layout.no-detail .detail { display: none; }

/* ── Folding the side panels away ─────────────────────────────────────────
 * A handle per side, sitting on the STAGE and not in the panel it controls:
 * a button inside a folded panel is a button nobody can reach.
 *
 * Folding changes the column width and nothing else. The panel keeps its DOM,
 * its scroll position and its subscriptions, so it returns exactly as it
 * left, and the globe re-fits itself off the ResizeObserver it already has.
 *
 * Above the breakpoint only: below it the left rail is already a bottom sheet
 * on the Controls button and the right one is not rendered at all, so there
 * is nothing down there to fold.
 */
.rail-fold {
  position: absolute; top: 50%; transform: translateY(-50%);
  z-index: 24; /* over the table and prices views, under the tooltip */
  display: flex; align-items: center; justify-content: center;
  width: 15px; height: 60px; padding: 0; cursor: pointer;
  color: var(--ink-mute); background: var(--panel);
  border: 1.5px solid var(--bean);
  transition: color var(--dur-row) ease, background-color var(--dur-row) ease;
}
.rail-fold:hover { color: var(--ink); background: var(--bg-2); }
.rail-fold .i { width: 12px; height: 12px; }
/* Each tab is open on the side it is attached to, so it reads as part of the
 * panel edge rather than as a floating button. */
.rail-fold.left { left: 0; border-left: 0; border-radius: 0 var(--r) var(--r) 0; }
.rail-fold.right { right: 0; border-right: 0; border-radius: var(--r) 0 0 var(--r); }
/* Production carries no dossier at all, so there is nothing to fold away. */
.layout.no-detail .rail-fold.right { display: none; }

@media (min-width: 961px) {
  .layout.fold-rail { --rail-w: 0px; }
  .layout.fold-detail { --detail-w: 0px; }
  /* visibility, not display: the panel stays a grid item, so the columns keep
   * their order, but it leaves the tab order and the accessibility tree. */
  .layout.fold-rail > .rail, .layout.fold-detail > .detail { visibility: hidden; }
}
@media (max-width: 960px) { .rail-fold { display: none; } }
