:root {
  --bg:            #070d18;
  --panel:         #0f1a2d;
  --panel-2:       #132340;
  --line:          #1e3355;
  --line-soft:     #17293f;
  --ink:           #e8eefb;
  --ink-dim:       #8ea3c4;
  --ink-faint:     #5c76a0;
  --consum:        #f2994a;
  --productie:     #58c98a;
  --sold:          #4aa8f0;
  --frecventa:     #f5d547;
  --import:        #4aa8f0;
  --export:        #58c98a;
  --idle:          #55688a;
  --land:          #111e31;
  --land-line:     #1d3452;
  --ro-land:       #21406a;
  --ro-line:       #5d95d4;
  --radius:        10px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: radial-gradient(1200px 700px at 50% -10%, #0d1a2e 0%, var(--bg) 60%);
  color: var(--ink);
  font: 400 15px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-variant-numeric: tabular-nums;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* ---------- top bar ---------- */

.topbar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  padding: 14px 22px;
  border-bottom: 1px solid var(--line-soft);
}

.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  color: var(--ink-dim);
  min-width: 0;
}

.brand-mark { width: 34px; height: 34px; flex: none; color: #6f9ad0; }

.brand-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.brand-text strong { color: var(--ink); font-size: 15px; font-weight: 600; }
.brand-text span { font-size: 12px; color: var(--ink-faint); }

.page-title {
  margin: 0;
  font-size: 27px;
  font-weight: 700;
  letter-spacing: .2px;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 12px;
}

.live-dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--productie);
  box-shadow: 0 0 0 0 rgba(88, 201, 138, .6);
  animation: pulse 2.4s ease-out infinite;
}
.live-dot.is-stale { background: var(--consum); box-shadow: none; animation: none; }
.live-dot.is-down  { background: #e05b5b; box-shadow: none; animation: none; }

@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(88, 201, 138, .55); }
  70%  { box-shadow: 0 0 0 9px rgba(88, 201, 138, 0); }
  100% { box-shadow: 0 0 0 0 rgba(88, 201, 138, 0); }
}

.topbar-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 18px;
}

.clock { color: var(--ink-dim); font-size: 15px; white-space: nowrap; }

.lang { display: flex; align-items: center; gap: 7px; color: var(--ink-faint); font-size: 13px; }
.lang button {
  background: none; border: 0; padding: 2px 1px;
  color: var(--ink-faint); font: inherit; cursor: pointer; border-radius: 3px;
}
.lang button:hover { color: var(--ink-dim); }
.lang button.is-active { color: var(--sold); font-weight: 600; }
.lang button:focus-visible,
.brand:focus-visible { outline: 2px solid var(--sold); outline-offset: 2px; }

/* ---------- banner ---------- */

.banner {
  margin: 12px 22px -4px;
  padding: 9px 14px;
  border: 1px solid #6b4a1e;
  background: #2a1e0d;
  color: #f0c489;
  border-radius: var(--radius);
  font-size: 13.5px;
}
.banner[hidden] { display: none; }

/* ---------- layout ---------- */

.layout {
  --pad: 22px;
  flex: 1;
  display: grid;
  /* Charts left, map centre, figures right. The charts moved out of a
     full-width row at the bottom because a 24 h line stretched across 1400px
     is hard to follow and pushed everything else below the fold; in a column
     the same window reads at a glance and the page stops scrolling. */
  grid-template-columns: clamp(356px, 27vw, 420px) minmax(0, 1fr) clamp(336px, 25vw, 400px);
  /* Row 2 needs a definite height, not 1fr: the exchange table is 22 rows and
     would otherwise stretch the row until the page scrolled again, which is
     the whole thing this layout exists to avoid. Given a height it scrolls
     inside its own column instead. */
  /* Deriving it from the fixed chrome rather than a vh fraction is what makes
     the page land on exactly one screen at any height: 285px is the top bar,
     the KPI strip, the footer and this grid's own padding, measured. */
  grid-template-rows: auto clamp(520px, calc(100vh - 285px), 900px);
  /* Rows are sized deliberately, so leftover height must not be handed back to
     them: the default (stretch) grew the auto row -- the KPI strip -- and left
     a band of dead space under the sparklines that pushed everything down. */
  align-content: start;
  gap: 14px;
  padding: 14px var(--pad) 18px;
}

.kpis { grid-column: 1 / -1; }

.charts {
  grid-column: 1; grid-row: 2;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
}

/* The evolution plot carries a frequency strip as well, so it gets the
   larger share of the column. */
.panel--chart { flex: 1.2 1 0; }
.panel--mix   { flex: 1 1 0; }

.panel--map { grid-column: 2; grid-row: 2; }
.side {
  grid-column: 3; grid-row: 2;
  display: flex; flex-direction: column; gap: 14px;
  min-height: 0;
  /* On a short window the exchange table would be squeezed to a couple of
     rows; give it a floor and let the whole column scroll instead. */
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: #24406a transparent;
}

.side::-webkit-scrollbar { width: 8px; }
.side::-webkit-scrollbar-thumb { background: #24406a; border-radius: 4px; }
.side::-webkit-scrollbar-track { background: transparent; }

.panel {
  background: linear-gradient(180deg, var(--panel) 0%, #0c1727 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  min-width: 0;
}

.panel--flex { flex: 1 0 auto; display: flex; flex-direction: column; min-height: 240px; }

.panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 16px;
  border-bottom: 1px solid var(--line-soft);
}
.panel-head h2 { margin: 0; font-size: 16px; font-weight: 600; }
.panel--chart .panel-head, .panel--mix .panel-head { padding: 8px 14px; }
.panel--chart .panel-head h2, .panel--mix .panel-head h2 { font-size: 15px; }
.panel-total { color: var(--productie); white-space: nowrap; }
.panel-total b { font-size: 19px; font-weight: 700; }
.panel-total i { font-style: normal; font-size: 12px; color: var(--ink-faint); margin-left: 2px; }

/* ---------- KPI strip ---------- */

.kpis {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  background: linear-gradient(180deg, var(--panel) 0%, #0c1727 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.kpi {
  padding: 11px 20px;
  border-left: 1px solid var(--line-soft);
  min-width: 0;
}

/* Consumption and generation carry no sparkline: a line with no baseline to
   read it against says nothing on its own -- the chart below does that job
   properly. Their tiles centre what is left rather than leaving the band the
   sparkline used to occupy sitting empty. */
.kpi--plain {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.kpi:first-child { border-left: 0; }

.kpi-label { display: block; font-size: 14px; font-weight: 600; margin-bottom: 3px; }
.kpi--consum    .kpi-label { color: var(--consum); }
.kpi--productie .kpi-label { color: var(--productie); }
/* White, so that blue is free to mean one thing only on this page: import.
   The direction word underneath carries the colour instead. */
.kpi--sold      .kpi-label { color: var(--ink); }
.kpi--frecventa .kpi-label { color: var(--frecventa); }

.kpi-value { display: flex; align-items: baseline; gap: 6px; white-space: nowrap; }
.kpi-value b { font-size: 31px; font-weight: 700; letter-spacing: -.4px; }
.kpi-value i { font-style: normal; font-size: 16px; color: var(--ink-dim); }
.kpi-value em { font-style: normal; font-size: 12.5px; font-weight: 600; color: var(--ink-faint); }
.kpi-value em.dir--export { color: var(--export); }
.kpi-value em.dir--import { color: var(--import); }

/* The same figure with the rooftop estimate folded in, shown only while the
   estimate is switched on somewhere. Under the headline number rather than
   replacing it: that one is metered, and a total that silently mixes a
   measurement with a model cannot be taken apart again. */
.kpi-extra {
  display: flex;
  align-items: baseline;
  gap: 5px;
  margin-top: 3px;
  font-size: 11.5px;
  color: var(--ink-faint);
  white-space: nowrap;
}
.kpi-extra[hidden] { display: none; }
/* The colon rides on the label rather than sitting beside it as its own text
   node: `.kpi-extra` is a flex row with a gap, and a loose ":" would be spaced
   off the word it belongs to. */
.prosum-tag::after { content: ':'; }
.kpi-extra b { font-size: 13.5px; font-weight: 700; color: var(--ink-dim); margin-left: 2px; }
.kpi-extra i { font-style: normal; }

.kpi-spark { display: block; position: relative; height: 30px; margin-top: 5px; cursor: crosshair; touch-action: pan-y; }
.kpi-spark svg { display: block; width: 100%; height: 30px; overflow: visible; }
.spark-line { fill: none; stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; opacity: .85; }
.spark-gap { stroke-dasharray: 2 3; opacity: .3; }
/* The reference line is the thing the series is read against -- zero for the
   balance, 50 Hz for frequency -- so it has to survive being 30px tall. */
.spark-zero { stroke: rgba(176, 206, 244, .6); stroke-width: 1; stroke-dasharray: 3 3; }
.spark-cross { stroke: rgba(160, 190, 230, .55); stroke-width: 1; }
.spark-hover-dot { stroke: #0b1626; stroke-width: 1.2; }
.spark-tip {
  position: absolute;
  right: 0;
  top: -17px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
  background: rgba(9, 18, 32, .9);
  padding: 0 3px;
  border-radius: 3px;
}
.spark-tip[hidden] { display: none; }

.kpi--consum    .kpi-value b { color: var(--consum); }
.kpi--productie .kpi-value b { color: var(--productie); }
.kpi--sold      .kpi-value b { color: var(--ink); }
.kpi--frecventa .kpi-value b { color: var(--frecventa); }

/* ---------- map ---------- */

.panel--map { position: relative; display: flex; flex-direction: column; overflow: hidden; }
.map { position: relative; flex: 1; min-height: 300px; min-width: 0; }
/* Absolute fill keeps the SVG box tied to the panel, never to its own
   intrinsic size -- otherwise the viewBox feeds back into the grid row. */
.map svg { position: absolute; inset: 10px; width: calc(100% - 20px); height: calc(100% - 20px); display: block; }

/* Stroke widths and font sizes below are viewBox units, driven by scaleMapInk()
   so they render at a constant on-screen size whatever the panel width. The
   literals are only the pre-script fallback. */
.geo-neighbour { fill: var(--land); stroke: var(--land-line); stroke-width: 1; }
.geo-ro {
  fill: var(--ro-land);
  stroke: var(--ro-line);
  stroke-width: var(--sw-ro, 1.8);
  filter: drop-shadow(0 0 14px rgba(77, 127, 184, .35));
}

.geo-county { fill: none; stroke: rgba(125, 165, 215, .22); stroke-width: var(--sw-county, 0.7); }

/* The transmission network. 400 kV is the backbone and reads warm against
   the blue landmass; 220 kV sits behind it, cooler and thinner. The blue
   needs to be well above the landmass tone or it simply disappears. */
.grid-400 { fill: none; stroke: #e0a24a; stroke-width: var(--sw-400, 1); opacity: .65; stroke-linecap: round; }
.grid-220 { fill: none; stroke: #7fc4e8; stroke-width: var(--sw-220, 0.8); opacity: .5; stroke-linecap: round; }

.geo-label {
  fill: #43597e;
  font-size: var(--fs-label, 26px);
  font-weight: 700;
  letter-spacing: .1em;
  text-anchor: middle;
  dominant-baseline: middle;
}

/* The day-ahead price, under each country's initials. Dimmer and much smaller
   than the flow captions on purpose: this view is about what is crossing the
   border, and the price is the reason rather than the subject. */
.geo-price {
  fill: #7d9ac6;
  font-size: var(--fs-price, 13px);
  font-weight: 600;
  text-anchor: middle;
  dominant-baseline: middle;
  paint-order: stroke fill;
  stroke: rgba(7, 16, 29, .8);
  stroke-width: var(--sw-halo, 3px);
  stroke-linejoin: round;
}
/* Romania's own price, set at Bucharest and written like a city label. The
   halo does what a filled plate did, without a box: paint-order puts the stroke
   behind the glyphs, so the number lifts off the counties without a frame
   around it. */
.ro-price-dot {
  fill: #b9cdeb;
  stroke: rgba(9, 20, 36, .85);
  stroke-width: var(--sw-ro, 1.4);
  /* r as a CSS property so it holds a constant on-screen size like the type;
     the attribute stays as the fallback. */
  r: var(--r-city, 3.4px);
}
.ro-price-value {
  fill: #c3d5f0;
  font-size: var(--fs-price, 13px);
  font-weight: 600;
  letter-spacing: .01em;
  dominant-baseline: middle;
  paint-order: stroke fill;
  stroke: rgba(7, 16, 29, .85);
  stroke-width: var(--sw-halo, 3px);
  stroke-linejoin: round;
}

.flow-line  { stroke-width: var(--sw-flow, 2.4); fill: none; stroke-linecap: round; }
.flow-head  { stroke: none; }
.flow-node  { fill: #0b1626; stroke-width: var(--sw-node, 2); }

.flow--import .flow-line, .flow--import .flow-node { stroke: var(--import); }
.flow--import .flow-head { fill: var(--import); }
.flow--export .flow-line, .flow--export .flow-node { stroke: var(--export); }
.flow--export .flow-head { fill: var(--export); }
.flow--idle   .flow-line, .flow--idle .flow-node   { stroke: var(--idle); }
.flow--idle   .flow-head { fill: var(--idle); }

.flow-value { font-size: var(--fs-value, 20px); font-weight: 700; }
.flow--import .flow-value { fill: var(--import); }
.flow--export .flow-value { fill: var(--export); }
.flow--idle   .flow-value { fill: var(--idle); }
.flow-unit  { fill: var(--ink-faint); font-size: var(--fs-unit, 12px); font-weight: 600; }

.legend {
  position: absolute;
  right: 14px;
  top: 14px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 11px 15px;
  background: rgba(9, 18, 32, .82);
  border: 1px solid var(--line);
  border-radius: 8px;
  font-size: 13.5px;
  color: var(--ink-dim);
  backdrop-filter: blur(3px);
}
.legend[hidden] { display: none; }
.legend > span { display: flex; align-items: center; gap: 8px; }
.legend-arrow { width: 26px; height: 10px; flex: none; }
.legend-arrow path { stroke-width: 1.8; fill: currentColor; stroke: currentColor; }
.legend-arrow--export { color: var(--export); }
.legend-arrow--import { color: var(--import); }

/* A control inside the legend rather than beside it: it turns on a layer whose
   key the legend is already carrying, and a separate strip of chrome for one
   switch is more furniture than the map can spare. */
.legend-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid transparent;
  background: none;
  padding: 2px 6px;
  margin: 0 -6px;
  border-radius: 6px;
  color: var(--ink-faint);
  font: inherit;
  font-size: inherit;
  cursor: pointer;
  text-align: left;
}
.legend-toggle[hidden] { display: none; }
.legend-toggle:hover { color: var(--ink-dim); }
.legend-toggle.is-on { color: var(--ink-dim); border-color: rgba(242, 168, 58, .45); }
.legend-swatch {
  width: 12px; height: 12px; flex: none; border-radius: 3px;
  border: 1px solid rgba(242, 168, 58, .55);
  background: rgba(242, 168, 58, .18);
}
.legend-toggle.is-on .legend-swatch { background: rgba(242, 168, 58, .6); }
.legend-toggle em { font-style: normal; color: var(--ink-faint); font-size: 11.5px; }

.legend-sep { height: 1px; background: var(--line); margin: 2px 0; }

/* ---------- map: two views ---------- */

.map-views {
  position: absolute;
  left: 14px;
  top: 14px;
  z-index: 2;
  display: flex;
  padding: 3px;
  gap: 3px;
  background: rgba(9, 18, 32, .82);
  border: 1px solid var(--line);
  border-radius: 8px;
  backdrop-filter: blur(3px);
}
.map-view {
  border: 0;
  background: none;
  color: var(--ink-dim);
  font: inherit;
  font-size: 13px;
  padding: 4px 12px;
  border-radius: 6px;
  cursor: pointer;
}
/* Full words where there is room for them, two letters where there is not.
   This control floats over the drawing, and at the width of "Romania | Europa"
   it covered Ukraine's initials and half its price on a phone. */
.view-short { display: none; }
.map-view:hover { color: var(--ink); }
.map-view.is-on { background: var(--panel-2); color: var(--ink); }

/* The European frame is nearly square where the Romanian one is landscape, so
   letting it letterbox inside the same box wastes a third of the panel. */
.panel--map.is-eu .map { min-height: 420px; }

/* Countries outside the day-ahead coupling -- Britain, Belarus, Russia, Turkey,
   Bosnia. Their own tone rather than --land, which is what the Romanian map
   paints its neighbours: at 1:1 with the sea underneath they read as water, and
   Europe appeared to have holes in it. Light enough to be unmistakably land,
   flat enough that the eye never mistakes them for a price. */
.eu-land {
  fill: #29405f;
  stroke: #101d30;
  stroke-width: 0.8;
  stroke-linejoin: round;
}
/* Priced but unpainted -- the fetch missed it, or its currency was refused.
   A step above the countries that are not in the coupling at all, and still
   far duller than any point on the scale so it never reads as cheap. */
.eu-land--priced { fill: #35507a; }
.eu-land.is-priced { stroke: rgba(8, 16, 28, .55); }
.eu-land--priced:hover { stroke: var(--ink); stroke-width: 1.6; }

.eu-land.is-picked { stroke: #ffe6a8; stroke-width: 1.8; }

/* Flow arrows.
   Warm and light so they carry across the cold end of the price scale, with a
   dark edge so they still read over the yellows and oranges at the other end --
   a gold arrow on a 90 EUR country is otherwise invisible. Neutral on purpose:
   the fill already carries a colour scale, and a second one competing with it
   would make the map harder to read, not richer. */
.eu-flow { pointer-events: none; }
/* One filter on the group rather than one per arrow: eighty filtered elements
   is a measurable cost on a phone, and the shadow only has to lift the layer
   off the fill, not each arrow off its neighbour. */
#euFlows { filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, .5)); }
.eu-arrow {
  fill: #ffd267;
  stroke: rgba(6, 13, 24, .7);
  stroke-width: 1.1;
  stroke-linejoin: round;
}
.eu-flow-value {
  fill: #ffe6b0;
  font-weight: 700;
  text-anchor: middle;
  dominant-baseline: central;
}

.eu-cur { font-size: .66em; font-weight: 600; opacity: .8; }

.eu-value {
  font-weight: 700;
  text-anchor: middle;
  dominant-baseline: central;
  pointer-events: none;
}
.eu-leader { stroke: rgba(190, 212, 245, .42); stroke-width: 1; }
/* Quieter than an inline number in every respect -- smaller, dimmer ink, a
   softer plate. A country that needs a leader is a footnote, not a headline. */
.eu-plate  { fill: rgba(11, 21, 36, .82); stroke: rgba(150, 180, 220, .3); stroke-width: 1; }
.eu-value--aside { font-weight: 600; }

.legend--eu { width: 216px; }
.eu-scale-head { color: var(--ink); font-size: 12.5px; }
.legend .eu-scale { position: relative; display: block; }
.eu-bar { display: block; height: 9px; border-radius: 3px; }
.eu-ticks { position: relative; display: block; height: 14px; }
.eu-ticks em {
  position: absolute;
  transform: translateX(-50%);
  font-style: normal;
  font-size: 11px;
  color: var(--ink-faint);
}
.eu-note { color: var(--ink-faint); font-size: 11.5px; line-height: 1.35; }
.legend .eu-pick {
  display: flex;
  flex-direction: column;
  /* .legend > span centres its children; in a column that centres them
     horizontally, and the readout was the only thing on the page not aligned
     with everything else. */
  align-items: flex-start;
  gap: 1px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
}
.eu-pick[hidden] { display: none; }
.eu-pick b { color: var(--ink); font-weight: 600; }
/* Two classes deep, because .eu-pick span already sets both and would win. */
.eu-pick .eu-pick-note { color: var(--ink-faint); font-size: 11.5px; }
.eu-pick .eu-pick-net { color: #e3c07a; }
.eu-pick span { color: var(--ink-dim); font-size: 12.5px; }
.legend-wire { width: 26px; height: 2px; flex: none; border-radius: 1px; }
.legend-wire--400 { background: #e0a24a; }
.legend-wire--220 { background: #7fc4e8; }

/* ---------- sources list ---------- */

.sources { list-style: none; margin: 0; padding: 5px 0; }

.sources li {
  display: grid;
  grid-template-columns: 12px 1fr auto auto;
  align-items: center;
  gap: 11px;
  padding: 5px 16px;
}

.swatch { width: 12px; height: 12px; border-radius: 3px; }
.src-name { color: var(--ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.src-val  { white-space: nowrap; }
.src-val b { font-weight: 600; }
.src-val i { font-style: normal; font-size: 11.5px; color: var(--ink-faint); margin-left: 3px; }
.src-pct  { color: var(--ink-dim); width: 54px; text-align: right; white-space: nowrap; }

/* ---------- flow by border ---------- */

.panel-note { color: var(--ink-faint); font-size: 11.5px; white-space: nowrap; }

/* Five overlapping traces in one small box: at 132px the lines spent most of
   their time on top of each other and the shape of any single border was
   guesswork. The extra height is where they separate. */
/* pan-y, not the default: without it the browser claims a horizontal drag for
   page scrolling, fires pointercancel, and the readout never appears on a
   phone. Vertical swipes still scroll, which is what you want on a chart whose
   only meaningful axis is horizontal. */
.brd-chart {
  position: relative;
  height: 186px;
  padding: 8px 12px 0;
  cursor: crosshair;
  touch-action: pan-y;
}
.brd-chart svg { display: block; width: 100%; height: 100%; overflow: visible; }
.brd-line { fill: none; stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.brd-zero { stroke: rgba(176, 206, 244, .6); stroke-width: 1; stroke-dasharray: 3 3; }
.brd-axis-label { fill: var(--ink-faint); font-size: 9.5px; }
.brd-cross { stroke: rgba(160, 190, 230, .55); stroke-width: 1; }
.brd-dot { stroke: #0b1626; stroke-width: 1.2; }

/* ---------- exchange table ---------- */

.table-scroll { overflow-y: auto; flex: 1; min-height: 0; }

.xtable { width: 100%; border-collapse: collapse; font-size: 14px; }
.xtable th {
  position: sticky;
  top: 0;
  background: #0e1a2c;
  color: var(--ink-faint);
  font-weight: 500;
  font-size: 12.5px;
  text-align: left;
  padding: 8px 16px;
  border-bottom: 1px solid var(--line-soft);
  z-index: 1;
}
.xtable td { padding: 6px 16px; border-bottom: 1px solid rgba(30, 51, 85, .4); }
.xtable tr:last-child td { border-bottom: 0; }
.xtable .num { text-align: right; white-space: nowrap; }
.xtable .num b { font-weight: 600; }
.xtable .num i { font-style: normal; font-size: 11.5px; color: var(--ink-faint); margin-left: 3px; }

.dir--import { color: var(--import); }
.dir--export { color: var(--export); }
.dir--idle   { color: var(--ink-faint); }

/* The scheduled figure, directly under the realised one. Deliberately quieter
   than the heading total: it is the reference the big number is read against,
   not a second headline. */
/* The two summary rows under the border chart. Same geometry as .panel-head --
   caption left, figure right -- so the realised and the scheduled number sit
   in one column and can be compared by eye. That comparison is why they are
   adjacent at all. */
.panel-sub {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 0;
  padding: 4px 16px 9px;
  font-size: 12.5px;
  color: var(--ink-faint);
}
.panel-sub .sub-label { min-width: 0; }
.panel-sub em { font-style: normal; font-weight: 600; }

/* The rule separates the pair from the chart legend, not the two rows from
   each other -- they are one block and read as one. */
.panel-sub--now {
  border-top: 1px solid var(--line-soft);
  padding-top: 9px;
  padding-bottom: 2px;
}

.sub-total { white-space: nowrap; }
.sub-total b { font-size: 15px; font-weight: 700; color: var(--ink-dim); }
.sub-total i { font-style: normal; font-size: 12px; color: var(--ink-faint); margin-left: 2px; }

/* What is happening now outranks what was planned; the scheduled row stays
   the dimmer of the two, caption included. */
.panel-sub--now { font-size: 14px; color: var(--ink-dim); }
.panel-sub--now .sub-total b { font-size: 18px; color: var(--ink); }

/* Folded-away interconnections. */
.xtable-more td { padding: 4px 16px; border-bottom: 0; }
.xtable-more button {
  background: none;
  border: 0;
  padding: 2px 0;
  font: inherit;
  font-size: 11.5px;
  color: var(--ink-faint);
  cursor: pointer;
}
.xtable-more button:hover { color: var(--ink); }
.xtable tr.is-idle td { color: var(--ink-faint); }

/* ---------- chart ---------- */

.panel--chart, .panel--mix {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  /* On a short window the plot hits its own minimum and would otherwise spill
     over the legend below it. */
  overflow: hidden;
}

.chart-head { flex-wrap: wrap; align-items: center; row-gap: 7px; }
.chart-head h2 { margin-right: auto; }

.chart-series { display: flex; gap: 5px; flex-wrap: wrap; flex-basis: 100%; }

.ch-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 2px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-faint);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
  transition: color .12s, border-color .12s;
}
.ch-toggle i {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--c);
  opacity: .3;
}
.ch-toggle.is-on { color: var(--ink); border-color: var(--c); }

/* Split down the middle for a series drawn in two colours, dot and border
   both. After `.ch-toggle.is-on`, not before it: same specificity, and written
   above it the plain rule's solid border-color won and the pill kept one
   colour however the gradient was declared. */
.ch-toggle--split i {
  background: linear-gradient(90deg, var(--c) 0 50%, var(--c2) 50% 100%);
}
.ch-toggle--split.is-on {
  border-color: transparent;
  background:
    linear-gradient(var(--panel), var(--panel)) padding-box,
    linear-gradient(90deg, var(--c) 0 50%, var(--c2) 50% 100%) border-box;
}
.ch-toggle.is-on i { opacity: 1; }
.ch-toggle:hover { color: var(--ink); }
.ch-toggle:focus-visible { outline: 2px solid var(--sold); outline-offset: 2px; }

.chart-ranges { display: flex; gap: 2px; }
.chart-ranges button {
  padding: 2px 11px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-faint);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.chart-ranges button:first-child { border-radius: 6px 0 0 6px; }
.chart-ranges button:last-child { border-radius: 0 6px 6px 0; }
.chart-ranges button + button { border-left: 0; }
.chart-ranges button:hover { color: var(--ink); }
.chart-ranges button.is-active { color: var(--ink); background: var(--panel-2); border-color: #2b4a75; }
.chart-ranges button:focus-visible { outline: 2px solid var(--sold); outline-offset: -2px; }

.chart-wrap { position: relative; flex: 1; min-height: 0; padding: 4px 4px 0; }

.chart {
  width: 100%;
  height: 100%;
  min-height: 90px;
  touch-action: pan-y;
  cursor: crosshair;
}

/* ---------- generation mix ---------- */

.mix-area { stroke: none; }
.mix-edge { fill: none; stroke-width: 1; opacity: .55; }

.mix-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 1px 9px;
  padding: 4px 11px 7px;
  font-size: 10.5px;
  color: var(--ink-dim);
}
.mix-legend span { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.mix-legend i { width: 9px; height: 9px; border-radius: 2px; flex: none; }
/* A source sitting at zero is still real information -- it is generating
   nothing right now -- but it should not compete with the ones that are. */
.mix-legend .is-zero { opacity: .4; }

.mix-hover-row { font-size: 11.5px; font-weight: 600; }
.mix-hover-box { fill: rgba(9, 18, 32, .92); stroke: var(--line); stroke-width: 1; }
.chart.is-dragging { cursor: grabbing; }
.chart svg { display: block; width: 100%; height: 100%; }

.ch-grid   { stroke: rgba(46, 74, 116, .35); stroke-width: 1; }
.ch-axis   { stroke: var(--line); stroke-width: 1; }
.ch-cross  { stroke: rgba(160, 190, 230, .5); stroke-width: 1; stroke-dasharray: 3 3; }
.ch-gap    { stroke-dasharray: 4 5; opacity: .28; }
.ch-zero   { stroke: rgba(176, 206, 244, .8); stroke-width: 1.4; stroke-dasharray: 7 5; }
.ch-zero-label { fill: #8ba6c9; font-size: 10.5px; letter-spacing: .3px; }
/* Hangs under the solar line: same indent as the source name, quieter than a
   source of its own, because it is an estimate rather than a reading. */
/* An estimate sitting among measurements. Same grid as the rows above it so
   the figures line up in one column, but hollow swatch and dimmer ink: this
   one is a model, and it should not read as another meter. */
.sources li.src-est { color: var(--ink-dim); }
.src-est .src-val b { font-weight: 600; color: var(--ink-dim); }
.swatch--est {
  background: rgba(242, 168, 58, .22);
  border: 1px dashed rgba(242, 168, 58, .7);
}

/* Under the panel head, above the list: measured generation plus the rooftop
   estimate. Reads as a continuation of the header figure, which is what the
   leading "+" is doing. */
.panel-sub--prosum {
  padding: 6px 16px 2px;
  border-bottom: 1px solid var(--line-soft);
  padding-bottom: 8px;
}
.panel-sub--prosum[hidden] { display: none; }
.src-note em { font-style: normal; opacity: .75; }

.ch-line   { fill: none; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; }
.ch-xlabel, .ch-ylabel { fill: var(--ink-faint); font-size: 11px; }

.chart-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 24px;
  color: var(--ink-faint);
  font-size: 13.5px;
  pointer-events: none;
}
.chart-empty[hidden] { display: none; }

.ch-hover-dot { stroke: #0b1626; stroke-width: 1.5; }
.ch-hover-label { font-size: 12px; font-weight: 700; paint-order: stroke; stroke: rgba(7,13,24,.85); stroke-width: 3.5; stroke-linejoin: round; }
.ch-hover-unit { font-size: 9.5px; font-weight: 600; opacity: .75; }
.ch-hover-time { fill: var(--ink-dim); font-size: 11px; paint-order: stroke; stroke: rgba(7,13,24,.9); stroke-width: 3; stroke-linejoin: round; }

.chart-hint {
  margin: 0;
  padding: 2px 4px 0;
  color: var(--ink-faint);
  font-size: 11px;
}

/* ---------- footer ---------- */

.footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 4px 16px;
  padding: 12px 22px;
  border-top: 1px solid var(--line-soft);
  color: var(--ink-faint);
  font-size: 12.5px;
}

/* Last, and pinned right even when the middle span is empty -- which it is
   until the first payload arrives. */
.foot-contact { white-space: nowrap; }
.foot-contact a { color: inherit; text-decoration: none; }
.foot-contact a:hover { color: var(--ink-dim); text-decoration: underline; }

/* A step lighter than the rest of the footer. It is the one line here that
   changes while you watch, so it earns being read first. */
.foot-viewers {
  margin-left: auto;
  white-space: nowrap;
  color: var(--ink-dim);
}

/* ---------- responsive ---------- */

/* Two columns: the charts drop under the map, which then has the whole width
   of the left column and the figures keep theirs. Held off until the three
   columns genuinely stop fitting -- 1366 is a very common laptop, and putting
   the charts back under everything there brings back both the stretched-out
   line and the scrolling this layout removed. */
@media (max-width: 1279px) {
  .layout { grid-template-columns: minmax(0, 1fr) 380px; grid-template-rows: auto clamp(360px, 52vh, 640px) auto; }
  .panel--map { grid-column: 1; grid-row: 2; }
  .side { grid-column: 2; grid-row: 2 / span 2; }
  .charts { grid-column: 1; grid-row: 3; }
  .panel--chart, .panel--mix { flex: none; min-height: 300px; }
}

/* Single column: the grid no longer has a viewport-sized row to hand out,
   so the map needs its own height or it collapses to nothing. */
@media (max-width: 1180px) {
  /* Stacked, the page scrolls as a whole: release the viewport-height
     lock or the columns shrink under their content and hit the footer. */
  html, body { height: auto; }
  .layout { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto auto auto; min-height: auto; }
  .panel--map { grid-column: 1; grid-row: 2; height: min(64vw, 540px); }
  .charts { grid-column: 1; grid-row: 3; }
  .side { grid-column: 1; grid-row: 4; }
  .panel--flex { flex: none; }
  .table-scroll { overflow: visible; }
}

@media (max-width: 860px) {
  .topbar { grid-template-columns: 1fr auto; row-gap: 8px; padding: 12px 14px; }
  .page-title { grid-column: 1 / -1; grid-row: 2; font-size: 21px; justify-content: center; }
  .brand-text span { display: none; }
  .clock { font-size: 13px; }
  .layout { --pad: 14px; padding: 12px var(--pad) 16px; }
  .kpis { grid-template-columns: repeat(2, 1fr); }
  .kpi:nth-child(3) { border-left: 0; }
  .kpi:nth-child(n+3) { border-top: 1px solid var(--line-soft); }
  /* Two narrow columns. The value and its unit must stay on one line; only
     the direction word ("import"/"export") drops below, and the tile is
     tightened enough that "MW" is never the thing that wraps. */
  .kpi { padding: 10px 14px; }
  .kpi-value { flex-wrap: wrap; column-gap: 5px; }
  .kpi-value b { font-size: 23px; }
  .kpi-value i { font-size: 14px; }
  .kpi-value em { flex-basis: 100%; margin-top: -1px; }
  .panel--map { height: auto; }
  /* Matching the viewBox aspect exactly means "meet" has nothing to letterbox,
     so the panel is all map instead of a band of dead space above and below. */
  .map { flex: none; height: auto; aspect-ratio: 842 / 620; min-height: 0; }
  .map svg { inset: 4px; width: calc(100% - 8px); height: calc(100% - 8px); }
  .legend {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 5px 10px;
    margin: 0 10px 10px;
    padding: 7px 10px;
    font-size: 12px;
    backdrop-filter: none;
  }
  /* Two classes deep: `.legend > span` sets display:flex and outranks a bare
     `.legend-sep`, so the hidden separator stayed a flex item one pixel tall
     and pushed the last pair onto a second row. */
  .map-view {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .04em;
    padding: 3px 9px;
  }
  .view-long { display: none; }
  .view-short { display: inline; }

  .legend > .legend-sep { display: none; }
  /* Small enough that all four pairs fit on one line at 390px. They are
     mnemonics next to their own words, not diagrams to be read on their own. */
  .legend > span { gap: 6px; }
  .legend-arrow { width: 19px; height: 8px; }
  .legend-arrow path { stroke-width: 2.6; }
  .legend-wire { width: 17px; }

  /* The row-and-wrap treatment suits the Romanian legend, which is a list of
     short pairs. The European one is a scale with a caption under it: laid out
     as a row its gradient bar has no intrinsic width and collapses to nothing,
     leaving a stray "200" tick beside the heading. */
  .legend--eu {
    flex-direction: column;
    align-items: stretch;
    width: auto;
  }
  .legend .eu-scale { width: 100%; }

  /* Europe is nearly square; Romania is landscape. Sharing one aspect-ratio
     letterboxes whichever view is not the one it was written for. */
  .panel--map.is-eu .map { aspect-ratio: 1000 / 1030; min-height: 0; }
  .banner { margin: 10px 14px -2px; }
  .footer { flex-direction: column; gap: 4px; }
  /* The column turns margin-left:auto into cross-axis alignment, which leaves
     the count hanging alone on the right below two left-aligned lines. */
  .foot-viewers { margin-left: 0; }
  .panel-head { flex-wrap: wrap; }
}

/* Below this the wordmark and the clock fight over the same strip. The page
   title right underneath already carries the identity, so drop the wordmark
   rather than truncate the timestamp, which is real information. */
@media (max-width: 480px) {
  .brand-text { display: none; }
  .page-title { font-size: 19px; }
  .topbar { padding: 10px 12px; }
  .layout { --pad: 12px; padding: 10px var(--pad) 14px; }
  .banner { margin: 8px 12px -2px; }
}

@media (prefers-reduced-motion: reduce) {
  .live-dot { animation: none; }
  * { transition: none !important; }
}
